Compare commits

...

19 Commits

Author SHA1 Message Date
hydroflame
4e5ebcfe6f V0.51.3 (#845)
v0.51.3 - 2021-04-16 Y'all broke it on the first day (hydroflame)
-------

Passive faction reputation
* Reworked, from 1 rep / 2 minute. Now is a complicated percentage of the
  reputation you'd gain working for them. It's not op but it feels a bit
  more useful.

Netscript
* print/tprint now take any number of arguments.
* print/tprint will now print object as json.
* print/tprint now handle passing in an undefined argument properly.

Casino
* Cannot bet negative money anymore.
* Roulette max bet is a bit higher.
* Coin Flip has a small cooldown.
* All buttons reject unstrusted mouse events.

Documentation
* Changed a message that said nsjs only works on Chrome.

Bugfix
* hacknet.maxNumNodes now works for both nodes and servers.
* Fixed a bug where the popup boxes would contain data from previous popup boxes.
* .js files will also have the export async function boilerplate.

Misc.
* turned off autocomplete for the terminal text input.
* Fixed an issue on Windows+Firefox where pressing up on the terminal would
  bring the cursor to the begining of the line. (Issue #836)
* Hacknet node names is easier to handle for screen readers.
* Money spent on classes is now tracked independently of work money.
* running coding contract from the terminal will display its name.
2021-04-18 11:18:56 -04:00
hydroflame
80b703639e Small hotfix (#840)
* yesno box now correctly clean up before new content is loaded in.

* formatHp doesnt display decimal, duh

* character overview uses numeralWrapper formatHp

* minor formatting stuff

* Class spending is tracked indepedently of work money

* Made an augmentation named after myself.

* hotfix a bunch of small stuff
2021-04-12 20:03:32 -04:00
hydroflame
0afdba8f38 fixed ns imports requiring semicolon (#839) 2021-04-10 02:26:13 -04:00
hydroflame
925e96345d v0.51.2 (#838)
* infiltration use buttons instead of a links

* minor accessibility patch

* Hospitalization will not cost more than 10% of the players money.

* Adde hospitalization netscript function

* Removed the suggestion that the combat path will lead to Daedalus, it still will. But new players should not be told that this is a viable path to completing a BitNode.

* getMemberInformation now returns everything about the member.

* New netscript function to get the players hacknet server hash capacity

* yesno dialog box will not keep older messages anymore

* v0.51.1

* Casino part 1

* Discord link in options, documentation for getMemberInformation updated, dev menu has more money options, tech vendors now handle max cores or max ram better

* Removed text under Factiosn referencing rejected factions.

* Removed html element forgotten in plain text

* Casino implementation

* v0.51.2
2021-04-09 18:12:31 -04:00
hydroflame
db2bf79e3b v0.51.1 (#835)
* infiltration use buttons instead of a links

* minor accessibility patch

* Hospitalization will not cost more than 10% of the players money.

* Adde hospitalization netscript function

* Removed the suggestion that the combat path will lead to Daedalus, it still will. But new players should not be told that this is a viable path to completing a BitNode.

* getMemberInformation now returns everything about the member.

* New netscript function to get the players hacknet server hash capacity

* yesno dialog box will not keep older messages anymore

* v0.51.1
2021-04-06 03:50:09 -04:00
hydroflame
6f330efc44 Added Disable ASCII art to options (#832)
* hotfix getPlayer missing factions

* Added ability to disable ascii art in options. ASCII art is impossible to deal with for screenreaders.
2021-04-02 20:14:35 -04:00
hydroflame
708c73fa0f hotfix getPlayer missing factions (#831) 2021-04-01 12:55:43 -04:00
hydroflame
c7febd5551 Failed crime no longer pretend you gained money. (#830) 2021-04-01 00:06:00 -04:00
hydroflame
ddbdf66d00 hotfix analyze (#828)
* hotfix analyze
2021-03-31 11:45:55 -04:00
hydroflame
e572c6dad8 v0.51.0 (#826)
* Hash upgrades and Bladeburner skills can now be clicked to copy to clipboard

* Aug purchase confirmation popup displays money in 0.000a format

* Character now displays hacknet server info properly

* Character,Info now displays hacknet server info correctly.

* Formulas (#825)

Formulas API v0.1

* Make all money the same color, same for reputation, format all numbers consistently.

* rename a lot of the formulas function to no longer contain calculate

* added hacking related formulas

* removed unused variable

* v0.51.0
2021-03-31 00:45:21 -04:00
hydroflame
ff097db1e2 Merge pull request #824 from danielyxie/dev
Update sphinx changelog
2021-03-25 21:21:11 -04:00
Olivier Gagnon
ad12f0e551 Update sphinx changelog 2021-03-25 21:20:37 -04:00
hydroflame
93f8785ec6 Merge pull request #823 from danielyxie/dev
v0.50.2
2021-03-25 21:16:22 -04:00
hydroflame
8e79658e67 Merge pull request #821 from danielyxie/dev
Fix typo in patch notes.
2021-03-22 18:57:10 -04:00
hydroflame
9840e1f4eb Merge pull request #820 from danielyxie/dev
0.50.1 hotfix version number
2021-03-22 18:51:52 -04:00
hydroflame
d170693da4 Merge pull request #819 from danielyxie/dev
v0.50.1
2021-03-22 18:44:49 -04:00
hydroflame
8f30e60d08 Merge pull request #818 from danielyxie/dev
v0.50.0
2021-03-20 05:35:16 -04:00
hydroflame
101834fcaf Merge pull request #817 from danielyxie/dev
Hotfix 0.49.2, link tFormat in documentation and update version so it…
2021-03-13 15:30:50 -05:00
hydroflame
63da8d709a Merge pull request #816 from danielyxie/dev
0.49.3
2021-03-13 15:19:56 -05:00
160 changed files with 4422 additions and 1532 deletions

10
README_contribution.md Normal file
View File

@@ -0,0 +1,10 @@
Deploying a new version
-----------------------
Update the following
- `src/Constants.ts` `Version` and `LatestUpdate`
- `package.json` `version`
- `doc/source/conf.py` `version` and `release`
- `doc/source/changelog.rst`
- post to discord
- post to reddit.com/r/Bitburner

View File

@@ -184,5 +184,4 @@
#infiltration-buttons .a-link-button { #infiltration-buttons .a-link-button {
display: inline; display: inline;
width: 25%;
} }

View File

@@ -356,10 +356,27 @@ a:visited {
color: $my-stat-cha-color; color: $my-stat-cha-color;
} }
.reputation {
color: $light-yellow;
}
.smallfont { .smallfont {
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
} }
.samefont {
font-size: inherit;
}
.noscrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.noscrollbar::-webkit-scrollbar {
display: none;
}
input[type=checkbox] { input[type=checkbox] {
filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10); filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10);
} }
@@ -395,4 +412,12 @@ input[type=checkbox] {
height: 10px; height: 10px;
background: var(--my-font-color); background: var(--my-font-color);
cursor: pointer; cursor: pointer;
}
.noselect {
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
} }

View File

@@ -85,3 +85,45 @@
.tooltip:hover .tooltiptextlow { .tooltip:hover .tooltiptextlow {
visibility: visible; visibility: visible;
} }
.copy_tooltip {
position: relative;
display: inline-block;
}
.copy_tooltip_copied {
color: #fff;
transition: color 0.3s;
}
.copy_tooltip .copy_tooltip_text {
visibility: hidden;
font-size: 15px;
padding: 5px;
background-color: var(--my-background-color);
color: #fff;
text-align: center;
position: absolute;
z-index: 1;
top: 120%;
left: 5%;
opacity: 0;
border: 2px solid var(--my-highlight-color);
}
.copy_tooltip .copy_tooltip_text::after {
content: "";
position: absolute;
bottom: 100%;
left: 50%;
margin-left: -6px;
border-width: 8px;
border-style: solid;
border-color: transparent transparent white transparent;
}
.copy_tooltip .copy_tooltip_text_visible {
visibility: visible;
opacity: 1;
transition: opacity 0.3s;
}

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([380,0]),o()}({323:function(n,t,o){},325:function(n,t,o){},327:function(n,t,o){},329:function(n,t,o){},331:function(n,t,o){},333:function(n,t,o){},335:function(n,t,o){},337:function(n,t,o){},339:function(n,t,o){},341:function(n,t,o){},343:function(n,t,o){},345:function(n,t,o){},347:function(n,t,o){},349:function(n,t,o){},351:function(n,t,o){},353:function(n,t,o){},355:function(n,t,o){},357:function(n,t,o){},359:function(n,t,o){},361:function(n,t,o){},363:function(n,t,o){},365:function(n,t,o){},367:function(n,t,o){},369:function(n,t,o){},371:function(n,t,o){},373:function(n,t,o){},375:function(n,t,o){},377:function(n,t,o){},380:function(n,t,o){"use strict";o.r(t);o(379),o(377),o(375),o(373),o(371),o(369),o(367),o(365),o(363),o(361),o(359),o(357),o(355),o(353),o(351),o(349),o(347),o(345),o(343),o(341),o(339),o(337),o(335),o(333),o(331),o(329),o(327),o(325),o(323)}}); !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([400,0]),o()}({343:function(n,t,o){},345:function(n,t,o){},347:function(n,t,o){},349:function(n,t,o){},351:function(n,t,o){},353:function(n,t,o){},355:function(n,t,o){},357:function(n,t,o){},359:function(n,t,o){},361:function(n,t,o){},363:function(n,t,o){},365:function(n,t,o){},367:function(n,t,o){},369:function(n,t,o){},371:function(n,t,o){},373:function(n,t,o){},375:function(n,t,o){},377:function(n,t,o){},379:function(n,t,o){},381:function(n,t,o){},383:function(n,t,o){},385:function(n,t,o){},387:function(n,t,o){},389:function(n,t,o){},391:function(n,t,o){},393:function(n,t,o){},395:function(n,t,o){},397:function(n,t,o){},400:function(n,t,o){"use strict";o.r(t);o(399),o(397),o(395),o(393),o(391),o(389),o(387),o(385),o(383),o(381),o(379),o(377),o(375),o(373),o(371),o(369),o(367),o(365),o(363),o(361),o(359),o(357),o(355),o(353),o(351),o(349),o(347),o(345),o(343)}});
//# sourceMappingURL=engineStyle.bundle.js.map //# sourceMappingURL=engineStyle.bundle.js.map

62
dist/engineStyle.css vendored
View File

@@ -353,9 +353,24 @@ a:visited {
.charisma-purple { .charisma-purple {
color: #a671d1; } color: #a671d1; }
.reputation {
color: #faffdf; }
.smallfont { .smallfont {
font-size: 13px; } font-size: 13px; }
.samefont {
font-size: inherit; }
.noscrollbar {
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */ }
.noscrollbar::-webkit-scrollbar {
display: none; }
input[type=checkbox] { input[type=checkbox] {
filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10); } filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10); }
@@ -387,6 +402,13 @@ input[type=checkbox] {
background: var(--my-font-color); background: var(--my-font-color);
cursor: pointer; } cursor: pointer; }
.noselect {
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/* Styling for tooltip-style elements */ /* Styling for tooltip-style elements */
@@ -460,6 +482,43 @@ input[type=checkbox] {
.tooltip:hover .tooltiptextlow { .tooltip:hover .tooltiptextlow {
visibility: visible; } visibility: visible; }
.copy_tooltip {
position: relative;
display: inline-block; }
.copy_tooltip_copied {
color: #fff;
transition: color 0.3s; }
.copy_tooltip .copy_tooltip_text {
visibility: hidden;
font-size: 15px;
padding: 5px;
background-color: var(--my-background-color);
color: #fff;
text-align: center;
position: absolute;
z-index: 1;
top: 120%;
left: 5%;
opacity: 0;
border: 2px solid var(--my-highlight-color); }
.copy_tooltip .copy_tooltip_text::after {
content: "";
position: absolute;
bottom: 100%;
left: 50%;
margin-left: -6px;
border-width: 8px;
border-style: solid;
border-color: transparent transparent white transparent; }
.copy_tooltip .copy_tooltip_text_visible {
visibility: visible;
opacity: 1;
transition: opacity 0.3s; }
/* COLORS */ /* COLORS */
/* Attributes */ /* Attributes */
/** /**
@@ -1315,8 +1374,7 @@ button {
margin-top: 20px; } margin-top: 20px; }
#infiltration-buttons .a-link-button { #infiltration-buttons .a-link-button {
display: inline; display: inline; }
width: 25%; }
/** /**
* Styling for the Augmentations UI. This is the page that displays all of the * Styling for the Augmentations UI. This is the page that displays all of the

34
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,135 @@
Changelog Changelog
========= =========
v0.51.3 - 2021-04-16 Y'all broke it on the first day (hydroflame)
-----------------------------------------------------------------
**Passive faction reputation**
* Reworked, from 1 rep / 2 minute. Now is a complicated percentage of the
reputation you'd gain working for them. It's not op but it feels a bit
more useful.
**Netscript**
* print/tprint now take any number of arguments.
* print/tprint will now print object as json.
* print/tprint now handle passing in an undefined argument properly.
**Casino**
* Cannot bet negative money anymore.
* Roulette max bet is a bit higher.
* Coin Flip has a small cooldown.
* All buttons reject unstrusted mouse events.
**Documentation**
* Changed a message that said nsjs only works on Chrome.
**Bugfix**
* hacknet.maxNumNodes now works for both nodes and servers.
* Fixed a bug where the popup boxes would contain data from previous popup boxes.
* .js files will also have the 'export async function' boilerplate.
**Misc.**
* turned off web form autocomplete for the terminal text input.
* Fixed an issue on Windows+Firefox where pressing up on the terminal would
bring the cursor to the begining of the line. (Issue #836)
* Hacknet node names is easier to handle for screen readers.
* Money spent on classes is now tracked independently of work money.
* running coding contract from the terminal will display its name.
v0.51.2 - 2021-04-09 Vegas, Baby! (hydroflame)
----------------------------------------------
**New location: The Iker Molina Casino**
* A casino opened in Aevum. However the house is rumored to cheat. If only
we could give them a taste of their own medicine.
**Misc.**
* Link to discord added under options
* 'getMemberInformation' doc updated, oops
* tech vendor now handle max ram and cores.
v0.51.1 - 2021-04-06 Bugfixes because the author of the last patch sucks (it's hydroflame)
------------------------------------------------------------------------------------------
**Netscript**
* 'getPlayer' returns players faction and tor
* 'hospitalization' is a new singularity function.
* 'gang.getMemberInformation' now returns more information.
* 'hacknet.hashCapacity' is a new hacknet function that returns the maximum hash capacity.
**Hospitalization**
* Now only cost at most 10% of your money.
**Bugfix**
* confirmation dialog box no longer use previous text
**Accessibility**
* The game is a little easier to handle for screen readers (yes, there's an
absolute legend playing this game with a screen reader)
* Infiltration use buttons instead of a-links
* New option to disable ASCII art. This will make the metro map and world
map display as a list of buttons.
**Misc.**
* 'fl1ght.exe' will no longer suggest the combat path. Related faction
requirements unchanged.
v0.51.0 - 2021-03-31 Formulas (hydroflame)
------------------------------------------
**Formulas API**
* A new API is introduced, this gives players access to various formulas used in the game.
It'll help you make more informed decisions.
**Netscript**
* 'getServer' is a new function meant to be used with the formulas API.
* 'getPlayer' is a new function meant to be used with the formulas API.
* 'getStats' and 'getCharacterInformation' are deprecated in favor of 'getPlayer'
* 'getCurrentServer' is a new function that returns the server the player is currently connected.
**Display**
* All money should now consistently be orange.
* All rep should now consistently be light-yellow.
* Most numbers should display consistently now (aka all money is formatted the same).
**Click to copy**
* Certain UI elements are now 'click-to-copy'
v0.50.2 - 2021-03-25 Everyone asked for this one. (hydroflame)
--------------------------------------------------------------
**BitNodeMultipliers**
* 'GangKarmaRequirements': a new multipler that influences how much karma is required to make a gang different bitnodes.
**Netscript**
* 'connect': a new singularity function that connects you to a server. (like the terminal command)
* 'manualHack': a new singularity function that performs a manual hack on the players current server.
* ns2 stack trace works on Firefox now.
**Misc.**
* New shortcut, Alt + b, brings you to bladeburner
* New shortcut, Alt + g, brings you to gang
v0.50.1 - 2021-03-22 (hydroflame) v0.50.1 - 2021-03-22 (hydroflame)
--------------------------------- ---------------------------------
**Netscript** **Netscript**

View File

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

View File

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

View File

@@ -0,0 +1,39 @@
getServer() Netscript Function
==========================================
.. js:function:: getServer()
:RAM cost: 4 GB
If you are not in BitNode-5, then you must have Source-File 5-1 in order to run this function.
This function is meant to be used in conjunction with the :doc:`formulas API<../netscriptformulasapi>`.
Returns an object with the Server's stats. The object has the following properties::
{
cpuCores
ftpPortOpen
hasAdminRights
hostname
httpPortOpen
ip
isConnectedTo
maxRam
organizationName
ramUsed
smtpPortOpen
sqlPortOpen
sshPortOpen
baseDifficulty
hackDifficulty
manuallyHacked
minDifficulty
moneyAvailable
moneyMax
numOpenPortsRequired
openPortCount
purchasedByPlayer
requiredHackingSkill
serverGrowth
}

View File

@@ -1,15 +1,16 @@
print() Netscript Function print() Netscript Function
=========================== ===========================
.. js:function:: print(x) .. js:function:: print(args...)
:RAM cost: 0 GB :RAM cost: 0 GB
:param x: Value to be printed. :param args: Values to be printed.
Prints a value or a variable to the script's logs. Prints any number of values to the script's logs.
Example: Example:
.. code-block:: javascript .. code-block:: javascript
print("Hello world!"); // Prints "Hello world!" in the logs. print("Hello world!"); // Prints "Hello world!" in the logs.
print({a:5}); // Prints '{"a":5}' in the logs.

View File

@@ -1,15 +1,16 @@
tprint() Netscript Function tprint() Netscript Function
=========================== ===========================
.. js:function:: tprint(x) .. js:function:: tprint(args...)
:RAM cost: 0 GB :RAM cost: 0 GB
:param x: Value to be printed :param args: Values to be printed
Prints a value or a variable to the Terminal. Prints any number of values to the Terminal.
Example: Example:
.. code-block:: javascript .. code-block:: javascript
tprint("Hello world!"); // Prints "Hello world!" to the terminal. tprint("Hello world!"); // Prints "Hello world!" to the terminal.
tprint({a:5}); // Prints '{"a":5}' to the terminal.

View File

@@ -0,0 +1,22 @@
calculateExp() Netscript Function
=================================
.. js:function:: calculateExp(skillLevel[, mult])
:RAM cost: 0 GB
:param number skillLevel: ``skillLevel`` to convert to exp.
:param number mult: Assume a specific skill multipler (not exp multiplier).
:returns: number of exp required to reach given ``skillLevel`` with that multiplier.
You must have Source-File 5-1 in order to use this function.
This function calculates the amount of experience needed to reach level the given ``skillLevel``.
Examples:
.. code-block:: javascript
nextHacking = getStats().hacking+1;
nextExp = formulas.basic.calculateExp(nextHacking);
missingExp = nextExp - getCharacterInformation().hackingExp;
tprint("Missing " + missingExp + " to reach next hacking level");

View File

@@ -0,0 +1,20 @@
calculateSkill() Netscript Function
===================================
.. js:function:: calculateSkill(exp[, mult])
:RAM cost: 0 GB
:param number exp: ``exp`` to convert to skillLevel.
:param number mult: Assume a specific skill multipler (not exp multiplier).
:returns: skillLevel that ``exp`` would reach with that multiplier.
You must have Source-File 5-1 in order to use this function.
This function calculates the skillLevel that the given amount of ``exp`` would reach.
Examples:
.. code-block:: javascript
skillLevel = formulas.basic.calculateSkill(1000);
tprint("1000 exp would reach level " + skillLevel);

View File

@@ -0,0 +1,23 @@
growPercent() Netscript Function
=================================
.. js:function:: growPercent(server, threads, player)
:RAM cost: 0 GB
:param server server: The server that receives the growth.
:param number threads: The number of thread that would be used.
:param player player: The player.
:returns: The percentage growth this server would receive with these parameters.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates percentage of growth a server would receive with these parameters.
Examples:
.. code-block:: javascript
tprint(growPercent(getServer(), 50, getPlayer()))

View File

@@ -0,0 +1,24 @@
growTime() Netscript Function
=================================
.. js:function:: growTime(server, player)
:RAM cost: 0 GB
:param server server: The server to grow.
:param player player: The player.
:returns: The time it takes to grow this server. In seconds.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates the amount of time it takes to grow a server.
Examples:
.. code-block:: javascript
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(growTime(server, getPlayer()));

View File

@@ -0,0 +1,24 @@
hackChance() Netscript Function
=================================
.. js:function:: hackChance(server, player)
:RAM cost: 0 GB
:param server server: The server to hack.
:param player player: The player.
:returns: The change to hack that server. between 0 and 1.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates percentage chance to hack a server.
Examples:
.. code-block:: javascript
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(hackChance(server, getPlayer()));

View File

@@ -0,0 +1,24 @@
hackExp() Netscript Function
=================================
.. js:function:: hackExp(server, player)
:RAM cost: 0 GB
:param server server: The server to hack.
:param player player: The player.
:returns: The amount of exp that would be acquired if this server were to be hacked.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates the amount of exp obtained by hacking a server.
Examples:
.. code-block:: javascript
server = getServer();
server.hackDifficulty = 99.9;
tprint(hackExp(server, getPlayer()));

View File

@@ -0,0 +1,25 @@
hackPercent() Netscript Function
=================================
.. js:function:: hackPercent(server, player)
:RAM cost: 0 GB
:param server server: The server to hack.
:param player player: The player.
:returns: The percentage of money hacked from a servers maximum money.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates the percentage of maximum money hacked from a server.
Multiply this by thread count to know calculate the percentage for more than 1 thread.
Examples:
.. code-block:: javascript
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(hackPercent(server, getPlayer()));

View File

@@ -0,0 +1,24 @@
hackTime() Netscript Function
=================================
.. js:function:: hackTime(server, player)
:RAM cost: 0 GB
:param server server: The server to hack.
:param player player: The player.
:returns: The time it takes to hack this server. In seconds.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates the amount of time it takes to hack a server.
Examples:
.. code-block:: javascript
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(hackTime(server, getPlayer()));

View File

@@ -0,0 +1,24 @@
weakenTime() Netscript Function
=================================
.. js:function:: weakenTime(server, player)
:RAM cost: 0 GB
:param server server: The server to weaken.
:param player player: The player.
:returns: The time it takes to weaken this server. In seconds.
You must have Source-File 5-1 in order to use this function.
Server can be acquired with the :doc:`getServer<../../advancedfunctions/getServer>` function.
Player can be acquired with the :doc:`getPlayer<../../singularityfunctions/getPlayer>` function.
This function calculates the amount of time it takes to weaken a server.
Examples:
.. code-block:: javascript
server = getServer();
server.hackDifficulty = server.minDifficulty;
tprint(weakenTime(server, getPlayer()));

View File

@@ -0,0 +1,26 @@
constants() Netscript Function
==============================
.. js:function:: constants()
:RAM cost: 0 GB
:returns: A structure with various constants related to hacknet nodes.
Examples:
.. code-block:: javascript
{
MoneyGainPerLevel
BaseCost
LevelBaseCost
RamBaseCost
CoreBaseCost
PurchaseNextMult
UpgradeLevelMult
UpgradeRamMult
UpgradeCoreMult
MaxLevel
MaxRam
MaxCores
}

View File

@@ -0,0 +1,20 @@
coreUpgradeCost() Netscript Function
=============================================
.. js:function:: coreUpgradeCost(startingCores[, extraCores[, costMult]])
:RAM cost: 0 GB
:param number startingCores: Number of core at the start the calculation.
:param number extraCores: Extra number of cores you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingCores`` to ``startingCores+extraCores``.
You must have Source-File 5-1 in order to use this function.
This function calculates the cost of upgrading cores from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetNodes.coreUpgradeCost(1, 5); // returns: 6355000

View File

@@ -0,0 +1,19 @@
hacknetNodeCost() Netscript Function
=============================================
.. js:function:: hacknetNodeCost(nodeN[, costMult]])
:RAM cost: 0 GB
:param number nodeN: Number of the new node.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to buy your ``nodeN`` th node.
You must have Source-File 5-1 in order to use this function.
This function calculates the cost purchasing a hacknet node.
Examples:
.. code-block:: javascript
formulas.hacknetNodes.hacknetNodeCost(2); // returns: 1800

View File

@@ -0,0 +1,20 @@
levelUpgradeCost() Netscript Function
==============================================
.. js:function:: levelUpgradeCost(startingLevel[, extraLevels[, costMult]])
:RAM cost: 0 GB
:param number startingLevel: Number of level at the start the calculation.
:param number extraLevels: Extra number of levels you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingLevel`` to ``startingLevel+extraLevels``.
You must have Source-File 5-1 in order to use this function.
This function calculates the cost of upgrading levels from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetNodes.levelUpgradeCost(1, 5); // returns: 2816

View File

@@ -0,0 +1,24 @@
moneyGainRate() Netscript Function
===========================================
.. js:function:: moneyGainRate(level, ram, core[, mult])
:RAM cost: 0 GB
:param number level: level of the node.
:param number ram: ram of the node.
:param number core: cores of the node.
:returns: Money per second that a node with those stats would gain per second.
You must have Source-File 5-1 in order to use this function.
This function calculates the money rate of a node with the given stats.
Examples:
.. code-block:: javascript
node = hacknet.getNodeStats(1);
currentRate = formulas.hacknetNodes.moneyGainRate(node.level, node.ram, node.cores);
levelRate = formulas.hacknetNodes.moneyGainRate(node.level+1, node.ram, node.cores);
ramRate = formulas.hacknetNodes.moneyGainRate(node.level, node.ram*2, node.cores);
coresRate = formulas.hacknetNodes.moneyGainRate(node.level, node.ram, node.cores+1);

View File

@@ -0,0 +1,22 @@
ramUpgradeCost() Netscript Function
============================================
.. js:function:: ramUpgradeCost(startingRam[, extraRamLevels[, costMult]])
:RAM cost: 0 GB
:param number startingRam: Amount of RAM at the start the calculation.
:param number extraRamLevels: Extra number of levels you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingRam`` to ``startingRam+extraRamLevels``.
..note:: ``startingRam`` is the actual amount of ram, not the amount of levels of ram.
You must have Source-File 5-1 in order to use this function.
This function calculates the cost of upgrading levels from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetNodes.ramUpgradeCost(1, 5); // returns: 2095000

View File

@@ -0,0 +1,20 @@
cacheUpgradeCost() Netscript Function
==============================================
.. js:function:: cacheUpgradeCost(startingCache[, extraCacheLevels[, costMult]])
:RAM cost: 0 GB
:param number startingCache: Cache level at the start the calculation.
:param number extraCacheLevels: Extra number of cache level you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingLevel`` to ``startingLevel+extraLevels``.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates the cost of upgrading cache from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetServers.cacheUpgradeCost(1, 5); // returns: 243170000

View File

@@ -0,0 +1,29 @@
constants() Netscript Function
=============================================
.. js:function:: constants()
:RAM cost: 0 GB
:returns: A structure with various constants related to hacknet servers.
Examples:
.. code-block:: javascript
{
HashesPerLevel
BaseCost
RamBaseCost
CoreBaseCost
CacheBaseCost
PurchaseMult
UpgradeLevelMult
UpgradeRamMult
UpgradeCoreMult
UpgradeCacheMult
MaxServers
MaxLevel
MaxRam
MaxCores
MaxCache
}

View File

@@ -0,0 +1,20 @@
coreUpgradeCost() Netscript Function
=============================================
.. js:function:: coreUpgradeCost(startingCores[, extraCores[, costMult]])
:RAM cost: 0 GB
:param number startingCores: Number of core at the start the calculation.
:param number extraCores: Extra number of cores you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingCores`` to ``startingCores+extraCores``.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates the cost of upgrading cores from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetServers.coreUpgradeCost(1, 5); // returns: 12015000

View File

@@ -0,0 +1,19 @@
hacknetServerCost() Netscript Function
===============================================
.. js:function:: hacknetServerCost(serverN[, costMult]])
:RAM cost: 0 GB
:param number serverN: Number of the new node.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to buy your ``serverN`` th node.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates the cost purchasing a hacknet node.
Examples:
.. code-block:: javascript
formulas.hacknetNodes.hacknetServerCost(2); // returns: 1800000

View File

@@ -0,0 +1,24 @@
hashGainRate() Netscript Function
==========================================
.. js:function:: hashGainRate(level, ram, core[, mult])
:RAM cost: 0 GB
:param number level: level of the server.
:param number ram: 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.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates the hash rate of a server with the given stats.
Examples:
.. 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);

View File

@@ -0,0 +1,19 @@
hashUpgradeCost() Netscript Function
=============================================
.. js:function:: hashUpgradeCost(upgName, level)
:RAM cost: 0 GB
:param string upgName: Name of the Hash upgrade.
:param number level: Level of the upgrade.
:returns: Amount of Hash.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates amount of Hash require to buy level ``level`` of upgrade ``upgName``.
Examples:
.. code-block:: javascript
formulas.hacknetServers.hashUpgradeCost("Increase Maximum Money", 5); // returns: 250

View File

@@ -0,0 +1,20 @@
levelUpgradeCost() Netscript Function
=============================================
.. js:function:: levelUpgradeCost(startingLevel[, extraLevels[, costMult]])
:RAM cost: 0 GB
:param number startingLevel: Number of level at the start the calculation.
:param number extraLevels: Extra number of levels you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingLevel`` to ``startingLevel+extraLevels``.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates the cost of upgrading levels from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetServers.levelUpgradeCost(1, 5); // returns: 2792000

View File

@@ -0,0 +1,22 @@
ramUpgradeCost() Netscript Function
=============================================
.. js:function:: ramUpgradeCost(startingRam[, extraRamLevels[, costMult]])
:RAM cost: 0 GB
:param number startingRam: Amount of RAM at the start the calculation.
:param number extraRamLevels: Extra number of levels you want to buy. Default to ``1``.
:param number costMult: Aug multiplier that reduces cost. Defaults to ``1``.
:returns: Money required to go from ``startingRam`` to ``startingRam+extraRamLevels``.
..note:: ``startingRam`` is the actual amount of ram, not the amount of levels of ram.
You must have Source-File 5-1 and Source-File 9-1 in order to use this function.
This function calculates the cost of upgrading levels from any level to any level.
Examples:
.. code-block:: javascript
formulas.hacknetServers.ramUpgradeCost(1, 5); // returns: 15810000

View File

@@ -10,27 +10,35 @@ getMemberInformation() Netscript Function
The object has the following structure:: The object has the following structure::
{ {
agility: Agility stat name: Name of this member.
agilityEquipMult: Agility multiplier from equipment. Decimal form task: Name of currently assigned task.
agilityAscensionMult: Agility multiplier from ascension. Decimal form earnedRespect: Total amount of respect earned by this member.
augmentations: Array of names of all owned Augmentations hack: Hacking stat
charisma: Charisma stat str: Strength stat
charismaEquipMult: Charisma multiplier from equipment. Decimal form def: Defense stat
charismaAscensionMult: Charisma multiplier from ascension. Decimal form dex: Dexterity stat
defense: Defense stat agi: Agility stat
defenseEquipMult: Defense multiplier from equipment. Decimal form cha: Charisma stat
defenseAscensionMult: Defense multiplier from ascension. Decimal form hack_exp: Hacking experience
dexterity: Dexterity stat str_exp: Strength experience
dexterityEquipMult: Dexterity multiplier from equipment. Decimal form def_exp: Defense experience
dexterityAscensionMult: Dexterity multiplier from ascension. Decimal form dex_exp: Dexterity experience
equipment: Array of names of all owned Non-Augmentation Equipment agi_exp: Agility experience
hacking: Hacking stat cha_exp: Charisma experience
hackingEquipMult: Hacking multiplier from equipment. Decimal form hack_mult: Hacking multiplier from equipment. Decimal form
hackingAscensionMult: Hacking multiplier from ascension. Decimal form str_mult: Strength multiplier from equipment. Decimal form
strength: Strength stat def_mult: Defense multiplier from equipment. Decimal form
strengthEquipMult: Strength multiplier from equipment. Decimal form dex_mult: Dexterity multiplier from equipment. Decimal form
strengthAscensionMult: Strength multiplier from ascension. Decimal form agi_mult: Agility multiplier from equipment. Decimal form
task: Name of currently assigned task cha_mult: Charisma multiplier from equipment. Decimal form
hack_asc_mult: Hacking multiplier from ascension. Decimal form
str_asc_mult: Strength multiplier from ascension. Decimal form
def_asc_mult: Defense multiplier from ascension. Decimal form
dex_asc_mult: Dexterity multiplier from ascension. Decimal form
agi_asc_mult: Agility multiplier from ascension. Decimal form
cha_asc_mult: Charisma multiplier from ascension. Decimal form
upgrades: Array of names of all owned Non-Augmentation Equipment
augmentations: Array of names of all owned Augmentations
} }
Get stat and equipment-related information about a Gang Member Get stat and equipment-related information about a Gang Member

View File

@@ -0,0 +1,18 @@
getHashUpgradeLevel() Netscript Function
========================================
.. js:function:: getHashUpgradeLevel(upgName)
:RAM cost: 0 GB
:param string upgName: Name of upgrade to spend hashes on. Must be an exact match.
:returns: level of the upgrade.
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
of a Hacknet Node).
Example:
.. code:: javascript
hacknet.getHashUpgradeLevel("Sell for Money"); // returns: 5
// "Sell for Money" was bought 5 times.

View File

@@ -0,0 +1,13 @@
getStudyMult() Netscript Function
=================================
.. js:function:: getStudyMult()
:RAM cost: 0 GB
:returns: The multiplier to studying that hash upgrades provide to the player.
Example:
.. code:: javascript
hacknet.getStudyMult(); // return: 1.4

View File

@@ -0,0 +1,13 @@
getTrainingMul() Netscript Function
===================================
.. js:function:: getTrainingMul()
:RAM cost: 0 GB
:returns: The multiplier to training that hash upgrades provide to the player.
Example:
.. code:: javascript
hacknet.getTrainingMult(); // return: 1.4

View File

@@ -0,0 +1,12 @@
hashCapacity() Netscript Function
=================================
.. warning:: This page contains spoilers for the game
.. js:function:: hashCapacity()
:RAM cost: 0 GB
:returns: The players maximum hash capacity.
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
a Hacknet Node).

View File

@@ -9,3 +9,4 @@ they contain spoilers for the game.
.. toctree:: .. toctree::
getBitNodeMultipliers() <advancedfunctions/getBitNodeMultipliers> getBitNodeMultipliers() <advancedfunctions/getBitNodeMultipliers>
getServer() <advancedfunctions/getServer>

View File

@@ -0,0 +1,61 @@
.. _netscriptformulas:
Netscript Formulas Functions
============================
.. warning:: This page contains spoilers for the game.
The formulas API allow you to gain insight into the inner workings of the game.
These functions will allow you to make more informed decision.
All of these function cost 0 GB of ram to use. All these function require
Source-File 5-1 but some additionally need another source file level 1 to use.
basic formulas
--------------
These functions are under the ``formulas.basic.`` name space and available as
soon as you acquire Source-File 5-1
.. toctree::
calculateSkill() <formulasapi/basic/calculateSkill>
calculateExp() <formulasapi/basic/calculateExp>
growTime() <formulasapi/basic/growTime>
hackTime() <formulasapi/basic/hackTime>
weakenTime() <formulasapi/basic/weakenTime>
growPercent() <formulasapi/basic/growPercent>
hackPercent() <formulasapi/basic/hackPercent>
hackChance() <formulasapi/basic/hackChance>
hackExp() <formulasapi/basic/hackExp>
hacknetNodes formulas
---------------------
These functions are under the ``formulas.hacknetNodes.`` namespace and available as
soon as you acquire Source-File 5-1.
.. toctree::
hacknetNodeCost() <formulasapi/hacknetNodes/hacknetNodeCost>
moneyGainRate() <formulasapi/hacknetNodes/moneyGainRate>
levelUpgradeCost() <formulasapi/hacknetNodes/levelUpgradeCost>
ramUpgradeCost() <formulasapi/hacknetNodes/ramUpgradeCost>
coreUpgradeCost() <formulasapi/hacknetNodes/coreUpgradeCost>
constants() <formulasapi/hacknetNodes/constants>
hacknetServers formulas
-----------------------
These functions are under the ``formulas.hacknetServers.`` namespace.
These functions require Source-File 5-1 and Source-File 9-1 to be invoked.
.. toctree::
hacknetServerCost() <formulasapi/hacknetServers/hacknetServerCost>
hashGainRate() <formulasapi/hacknetServers/hashGainRate>
levelUpgradeCost() <formulasapi/hacknetServers/levelUpgradeCost>
ramUpgradeCost() <formulasapi/hacknetServers/ramUpgradeCost>
coreUpgradeCost() <formulasapi/hacknetServers/coreUpgradeCost>
cacheUpgradeCost() <formulasapi/hacknetServers/cacheUpgradeCost>
hashUpgradeCost() <formulasapi/hacknetServers/hashUpgradeCost>
constants() <formulasapi/hacknetServers/constants>

View File

@@ -26,7 +26,7 @@ In :ref:`netscriptjs`::
ns.hacknet.getNodeStats(3).level; ns.hacknet.getNodeStats(3).level;
.. toctree:: .. toctree::
:caption: API Functions: :caption: Hacknet Nodes API Functions:
numNodes() <hacknetnodeapi/numNodes> numNodes() <hacknetnodeapi/numNodes>
maxNumNodes() <hacknetnodeapi/maxNumNodes> maxNumNodes() <hacknetnodeapi/maxNumNodes>
@@ -36,14 +36,22 @@ In :ref:`netscriptjs`::
upgradeLevel() <hacknetnodeapi/upgradeLevel> upgradeLevel() <hacknetnodeapi/upgradeLevel>
upgradeRam() <hacknetnodeapi/upgradeRam> upgradeRam() <hacknetnodeapi/upgradeRam>
upgradeCore() <hacknetnodeapi/upgradeCore> upgradeCore() <hacknetnodeapi/upgradeCore>
upgradeCache() <hacknetnodeapi/upgradeCache>
getLevelUpgradeCost() <hacknetnodeapi/getLevelUpgradeCost> getLevelUpgradeCost() <hacknetnodeapi/getLevelUpgradeCost>
getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost> getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost>
getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost> getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost>
.. toctree::
:caption: Hacknet Servers API Functions:
upgradeCache() <hacknetnodeapi/upgradeCache>
getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost> getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost>
numHashes() <hacknetnodeapi/numHashes> numHashes() <hacknetnodeapi/numHashes>
hashCapacity() <hacknetnodeapi/hashCapacity>
hashCost() <hacknetnodeapi/hashCost> hashCost() <hacknetnodeapi/hashCost>
spendHashes() <hacknetnodeapi/spendHashes> spendHashes() <hacknetnodeapi/spendHashes>
getHashUpgradeLevel() <hacknetnodeapi/getHashUpgradeLevel>
getTrainingMult() <hacknetnodeapi/getTrainingMult>
getStudyMult() <hacknetnodeapi/getStudyMult>
.. _netscript_hacknetnodeapi_referencingahacknetnode: .. _netscript_hacknetnodeapi_referencingahacknetnode:

View File

@@ -65,6 +65,6 @@ Here is a short summary of the differences between Netscript 1.0 and Netscript 2
* Supports (almost) all features of modern JavaScript * Supports (almost) all features of modern JavaScript
* Extremely fast - code is executed as an Async Function * Extremely fast - code is executed as an Async Function
* Currently only works with Google Chrome browser * Works on most modern browsers.
* Each script becomes a module and therefore all instances of that script can easily * Each script becomes a module and therefore all instances of that script can easily
share data between each other (essentially global/static variables) share data between each other (essentially global/static variables)

View File

@@ -24,10 +24,11 @@ level 3, then you will be able to access all of the Singularity Functions.
travelToCity() <singularityfunctions/travelToCity> travelToCity() <singularityfunctions/travelToCity>
purchaseTor() <singularityfunctions/purchaseTor> purchaseTor() <singularityfunctions/purchaseTor>
purchaseProgram() <singularityfunctions/purchaseProgram> purchaseProgram() <singularityfunctions/purchaseProgram>
getCurrentServer() <singularityfunctions/getCurrentServer>
connect() <singularityfunctions/connect> connect() <singularityfunctions/connect>
manualHack() <singularityfunctions/manualHack> manualHack() <singularityfunctions/manualHack>
getStats() <singularityfunctions/getStats> getPlayer() <singularityfunctions/getPlayer>
getCharacterInformation() <singularityfunctions/getCharacterInformation> hospitalize() <singularityfunctions/hospitalize>
isBusy() <singularityfunctions/isBusy> isBusy() <singularityfunctions/isBusy>
stopAction() <singularityfunctions/stopAction> stopAction() <singularityfunctions/stopAction>
upgradeHomeRam() <singularityfunctions/upgradeHomeRam> upgradeHomeRam() <singularityfunctions/upgradeHomeRam>
@@ -57,3 +58,10 @@ level 3, then you will be able to access all of the Singularity Functions.
purchaseAugmentation() <singularityfunctions/purchaseAugmentation> purchaseAugmentation() <singularityfunctions/purchaseAugmentation>
installAugmentations() <singularityfunctions/installAugmentations> installAugmentations() <singularityfunctions/installAugmentations>
softReset() <singularityfunctions/softReset> softReset() <singularityfunctions/softReset>
.. toctree::
:caption: Deprecated:
getStats() <singularityfunctions/getStats>
getCharacterInformation() <singularityfunctions/getCharacterInformation>

View File

@@ -3,6 +3,8 @@ getCharacterInformation() Netscript Function
.. js:function:: getCharacterInformation() .. js:function:: getCharacterInformation()
.. warning:: This function is deprecated.
:RAM cost: 0.5 GB :RAM cost: 0.5 GB
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function. If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function.

View File

@@ -0,0 +1,10 @@
getCurrentServer() Netscript Function
=====================================
.. js:function:: getCurrentServer()
:RAM cost: 2 GB
:returns: The hostname of the server the player is currently connected to.
If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.

View File

@@ -0,0 +1,103 @@
getPlayer() Netscript Function
==============================
.. js:function:: getPlayer()
:RAM cost: 0.5 GB
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function.
The result of this function can be passed to the :doc:`formulas API<../netscriptformulasapi>`.
Returns an object with the Player's stats. The object has the following properties::
{
hacking_skill
hp
max_hp
strength
defense
dexterity
agility
charisma
intelligence
hacking_chance_mult
hacking_speed_mult
hacking_money_mult
hacking_grow_mult
hacking_exp
strength_exp
defense_exp
dexterity_exp
agility_exp
charisma_exp
hacking_mult
strength_mult
defense_mult
dexterity_mult
agility_mult
charisma_mult
hacking_exp_mult
strength_exp_mult
defense_exp_mult
dexterity_exp_mult
agility_exp_mult
charisma_exp_mult
company_rep_mult
faction_rep_mult
money
city
location
crime_money_mult
crime_success_mult
isWorking
workType
currentWorkFactionName
currentWorkFactionDescription
workHackExpGainRate
workStrExpGainRate
workDefExpGainRate
workDexExpGainRate
workAgiExpGainRate
workChaExpGainRate
workRepGainRate
workMoneyGainRate
workMoneyLossRate
workHackExpGained
workStrExpGained
workDefExpGained
workDexExpGained
workAgiExpGained
workChaExpGained
workRepGained
workMoneyGained
createProgramName
createProgramReqLvl
className
crimeType
work_money_mult
hacknet_node_money_mult
hacknet_node_purchase_cost_mult
hacknet_node_ram_cost_mult
hacknet_node_core_cost_mult
hacknet_node_level_cost_mult
hasWseAccount
hasTixApiAccess
has4SData
has4SDataTixApi
bladeburner_max_stamina_mult
bladeburner_stamina_gain_mult
bladeburner_success_chance_mult
bitNodeN
totalPlaytime
playtimeSinceLastAug
playtimeSinceLastBitnode
jobs
factions
tor
}
Example::
player = getPlayer();
print('My charisma level is: ' + player.charisma);

View File

@@ -3,6 +3,8 @@ getStats() Netscript Function
.. js:function:: getStats() .. js:function:: getStats()
.. warning:: This function is deprecated.
:RAM cost: 0.5 GB :RAM cost: 0.5 GB
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function. If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function.

View File

@@ -0,0 +1,11 @@
hospitalize() Netscript Function
===================================
.. js:function:: hospitalize()
:RAM cost: 1 GB
:returns: The cost of your visit to the hospital.
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.
Hospitalize yourself. Recovering all lost hp.

View File

@@ -30,7 +30,7 @@
<div id="entire-game-container" style="visibility:hidden;"> <div id="entire-game-container" style="visibility:hidden;">
<div id="mainmenu-container"> <div id="mainmenu-container">
<!-- Main menu --> <!-- Main menu -->
<ul id="mainmenu" class="mainmenu"> <ul id="mainmenu" class="mainmenu noscrollbar">
<!-- Hacking dropdown --> <!-- Hacking dropdown -->
<li id="hacking-menu-header-li"> <li id="hacking-menu-header-li">
<button id="hacking-menu-header" class="mainmenu-accordion-header"> Hacking </button> <button id="hacking-menu-header" class="mainmenu-accordion-header"> Hacking </button>
@@ -177,7 +177,7 @@
<table id="terminal"> <table id="terminal">
<tr id="terminal-input"> <tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$ <td id="terminal-input-td" tabindex="2">$
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/> <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;" autocomplete="off"/>
</td> </td>
</tr> </tr>
</table> </table>
@@ -261,16 +261,16 @@
<div id="infiltration-left-panel"> <div id="infiltration-left-panel">
<p id="infiltration-level-text"> </p> <p id="infiltration-level-text"> </p>
<div id="infiltration-buttons"> <div id="infiltration-buttons">
<a class="a-link-button tooltip" id="infiltration-kill"> </a> <button class="a-link-button tooltip" id="infiltration-kill"> </button>
<a class="a-link-button tooltip" id="infiltration-knockout"> </a> <button class="a-link-button tooltip" id="infiltration-knockout"> </button>
<a class="a-link-button tooltip" id="infiltration-stealthknockout"> </a> <button class="a-link-button tooltip" id="infiltration-stealthknockout"> </button>
<a class="a-link-button tooltip" id="infiltration-assassinate"> </a> <button class="a-link-button tooltip" id="infiltration-assassinate"> </button>
<a class="a-link-button tooltip" id="infiltration-hacksecurity"> </a> <button class="a-link-button tooltip" id="infiltration-hacksecurity"> </button>
<a class="a-link-button tooltip" id="infiltration-destroysecurity"> </a> <button class="a-link-button tooltip" id="infiltration-destroysecurity"> </button>
<a class="a-link-button tooltip" id="infiltration-sneak"> </a> <button class="a-link-button tooltip" id="infiltration-sneak"> </button>
<a class="a-link-button tooltip" id="infiltration-pickdoor"> </a> <button class="a-link-button tooltip" id="infiltration-pickdoor"> </button>
<a class="a-link-button tooltip" id="infiltration-bribe"> </a> <button class="a-link-button tooltip" id="infiltration-bribe"> </button>
<a class="a-link-button tooltip" id="infiltration-escape"> </a> <button class="a-link-button tooltip" id="infiltration-escape"> </button>
</div> </div>
</div> </div>
<div id="infiltration-right-panel"> <div id="infiltration-right-panel">
@@ -387,7 +387,7 @@
<!-- Game Options --> <!-- Game Options -->
<div id="game-options-container" class="popup-box-container"> <div id="game-options-container" class="popup-box-container">
<div id="game-options-content" class="game-options-box"> <div id="game-options-content" class="game-options-box">
<button id="game-options-close-button">&times;</button> <button id="game-options-close-button" aria-label="close options dialog">&times;</button>
<h1> Game Options </h1> <h1> Game Options </h1>
<br/> <br/>
<div id="game-options-left-panel"> <div id="game-options-left-panel">
@@ -511,6 +511,16 @@
<input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys"> <input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys">
</fieldset> </fieldset>
<!-- View city as list of buttons instead of ASCII art. -->
<fieldset>
<label for="settingsDisableASCIIArt" class="tooltip">Disable ASCII art:
<span class="tooltiptexthigh">
If this is set all ASCII art will be disabled.
</span>
</label>
<input class="optionCheckbox" type="checkbox" name="settingsDisableASCIIArt" id="settingsDisableASCIIArt">
</fieldset>
<!-- Locale for displaying numbers --> <!-- Locale for displaying numbers -->
<fieldset> <fieldset>
<label for="settingsLocale" class="tooltip">Locale: <label for="settingsLocale" class="tooltip">Locale:
@@ -549,6 +559,7 @@
<div id="game-options-right-panel"> <div id="game-options-right-panel">
<a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank"> Changelog </a> <a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank"> Changelog </a>
<a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">Documentation</a> <a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">Documentation</a>
<a class="a-link-button" href="https://discord.gg/TFc3hKD" target="_blank">Discord</a>
<a class="a-link-button" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a> <a class="a-link-button" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a>
<button id="save-game-link" class="a-link-button"> Save Game </button> <button id="save-game-link" class="a-link-button"> Save Game </button>
<button id="delete-game-link" class="a-link-button"> Delete Game </button> <button id="delete-game-link" class="a-link-button"> Delete Game </button>

View File

@@ -121,5 +121,5 @@
"watch": "webpack --watch --mode production", "watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development" "watch:dev": "webpack --watch --mode development"
}, },
"version": "0.50.1" "version": "0.51.3"
} }

View File

@@ -1395,6 +1395,23 @@ function initAugmentations() {
} }
AddToAugmentations(Xanipher); AddToAugmentations(Xanipher);
const HydroflameLeftArm = new Augmentation({
name:AugmentationNames.HydroflameLeftArm, repCost:500e3, moneyCost:500e9,
info:"The left arm of a legendary BitRunner who ascended beyond this world. " +
"It projects a light blue energy shield that protects the exposed inner parts. " +
"Even though it contains no weapons, the advance tungsten titanium " +
"alloy increases the users strength to unbelievable levels.<br><br>" +
"This augmentation increases the player's strength by 300%.",
strength_mult: 3,
});
HydroflameLeftArm.addToFactions(["NWO"]);
if (augmentationExists(AugmentationNames.HydroflameLeftArm)) {
delete Augmentations[AugmentationNames.HydroflameLeftArm];
}
AddToAugmentations(HydroflameLeftArm);
// ClarkeIncorporated // ClarkeIncorporated
const nextSENS = new Augmentation({ const nextSENS = new Augmentation({
name:AugmentationNames.nextSENS, repCost:175e3, moneyCost:385e6, name:AugmentationNames.nextSENS, repCost:175e3, moneyCost:385e6,

View File

@@ -89,6 +89,7 @@ export let AugmentationNames: IMap<string> = {
BrachiBlades: "BrachiBlades", BrachiBlades: "BrachiBlades",
BionicArms: "Bionic Arms", BionicArms: "Bionic Arms",
SNA: "Social Negotiation Assistant (S.N.A)", SNA: "Social Negotiation Assistant (S.N.A)",
HydroflameLeftArm: "Hydroflame left arm",
EsperEyewear: "EsperTech Bladeburner Eyewear", EsperEyewear: "EsperTech Bladeburner Eyewear",
EMS4Recombination: "EMS-4 Recombination", EMS4Recombination: "EMS-4 Recombination",
OrionShoulder: "ORION-MKIV Shoulder", OrionShoulder: "ORION-MKIV Shoulder",

View File

@@ -8,6 +8,7 @@ import { Factions, factionExists } from "./Faction/Factions";
import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers"; import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player"; import { Player } from "./Player";
import { hackWorldDaemon, redPillFlag } from "./RedPill"; import { hackWorldDaemon, redPillFlag } from "./RedPill";
import { calculateHospitalizationCost } from "./Hospital/Hospital";
import { Page, routing } from "./ui/navigationTracking"; import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat"; import { numeralWrapper } from "./ui/numeralFormat";
@@ -51,6 +52,9 @@ import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { StatsTable } from "./ui/React/StatsTable"; import { StatsTable } from "./ui/React/StatsTable";
import { CopyableText } from "./ui/React/CopyableText";
import { Money } from "./ui/React/Money";
import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
const stealthIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 166 132" style="fill:#adff2f;"><g><path d="M132.658-0.18l-24.321,24.321c-7.915-2.71-16.342-4.392-25.087-4.392c-45.84,0-83,46-83,46 s14.1,17.44,35.635,30.844L12.32,120.158l12.021,12.021L144.68,11.841L132.658-0.18z M52.033,80.445 c-2.104-4.458-3.283-9.438-3.283-14.695c0-19.054,15.446-34.5,34.5-34.5c5.258,0,10.237,1.179,14.695,3.284L52.033,80.445z"/><path d="M134.865,37.656l-18.482,18.482c0.884,3.052,1.367,6.275,1.367,9.612c0,19.055-15.446,34.5-34.5,34.5 c-3.337,0-6.56-0.483-9.611-1.367l-10.124,10.124c6.326,1.725,12.934,2.743,19.735,2.743c45.84,0,83-46,83-46 S153.987,50.575,134.865,37.656z"/></g></svg>&nbsp;` const stealthIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 166 132" style="fill:#adff2f;"><g><path d="M132.658-0.18l-24.321,24.321c-7.915-2.71-16.342-4.392-25.087-4.392c-45.84,0-83,46-83,46 s14.1,17.44,35.635,30.844L12.32,120.158l12.021,12.021L144.68,11.841L132.658-0.18z M52.033,80.445 c-2.104-4.458-3.283-9.438-3.283-14.695c0-19.054,15.446-34.5,34.5-34.5c5.258,0,10.237,1.179,14.695,3.284L52.033,80.445z"/><path d="M134.865,37.656l-18.482,18.482c0.884,3.052,1.367,6.275,1.367,9.612c0,19.055-15.446,34.5-34.5,34.5 c-3.337,0-6.56-0.483-9.611-1.367l-10.124,10.124c6.326,1.725,12.934,2.743,19.735,2.743c45.84,0,83-46,83-46 S153.987,50.575,134.865,37.656z"/></g></svg>&nbsp;`
@@ -709,7 +713,7 @@ Bladeburner.prototype.completeAction = function() {
if (isOperation && this.logging.ops) { if (isOperation && this.logging.ops) {
this.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank"); this.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank");
} else if (!isOperation && this.logging.contracts) { } else if (!isOperation && this.logging.contracts) {
this.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeralWrapper.format(moneyGain, "$0.000a")); this.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeralWrapper.formatMoney(moneyGain));
} }
} }
isOperation ? this.completeOperation(true) : this.completeContract(true); isOperation ? this.completeOperation(true) : this.completeContract(true);
@@ -725,9 +729,10 @@ Bladeburner.prototype.completeAction = function() {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage; this.hpLost += damage;
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) { if (Player.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp); this.moneyLost += cost;
} }
} }
var logLossText = ""; var logLossText = "";
@@ -797,9 +802,10 @@ Bladeburner.prototype.completeAction = function() {
if (action.hpLoss) { if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) { if (Player.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp); this.moneyLost += cost;
} }
} }
teamLossMax = Math.floor(teamCount); teamLossMax = Math.floor(teamCount);
@@ -902,7 +908,7 @@ Bladeburner.prototype.completeAction = function() {
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain); this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
this.startAction(this.action); this.startAction(this.action);
if (this.logging.general) { if (this.logging.general) {
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${BladeburnerConstants.HrcHpGain} HP and gained ${numeralWrapper.format(staminaGain, "0.0")} stamina`); this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${BladeburnerConstants.HrcHpGain} HP and gained ${numeralWrapper.formatStamina(staminaGain)} stamina`);
} }
break; break;
} }
@@ -1256,7 +1262,7 @@ Bladeburner.prototype.createContent = function() {
// Console // Console
DomElems.consoleDiv = createElement("div", { DomElems.consoleDiv = createElement("div", {
class:"bladeburner-console-div", class:"bladeburner-console-div",
clickListener:()=>{ clickListener:() => {
if (DomElems.consoleInput instanceof Element) { if (DomElems.consoleInput instanceof Element) {
DomElems.consoleInput.focus(); DomElems.consoleInput.focus();
} }
@@ -1264,12 +1270,12 @@ Bladeburner.prototype.createContent = function() {
} }
}); });
DomElems.consoleTable = createElement("table", {class:"bladeburner-console-table"}); DomElems.consoleTable = createElement("table", {class:"bladeburner-console-table"});
DomElems.consoleInputRow = createElement("tr", {class:"bladeburner-console-input-row", id:"bladeubrner-console-input-row"}); DomElems.consoleInputRow = createElement("tr", {class:"bladeburner-console-input-row", id:"bladeburner-console-input-row"});
DomElems.consoleInputCell = createElement("td", {class:"bladeburner-console-input-cell"}); DomElems.consoleInputCell = createElement("td", {class:"bladeburner-console-input-cell"});
DomElems.consoleInputHeader = createElement("pre", {innerText:"> "}); DomElems.consoleInputHeader = createElement("pre", {innerText:"> "});
DomElems.consoleInput = createElement("input", { DomElems.consoleInput = createElement("input", {
type:"text", class:"bladeburner-console-input", tabIndex:1, type:"text", class:"bladeburner-console-input", tabIndex:1,
onfocus:()=>{DomElems.consoleInput.value = DomElems.consoleInput.value} onfocus:() => {DomElems.consoleInput.value = DomElems.consoleInput.value}
}); });
DomElems.consoleInputCell.appendChild(DomElems.consoleInputHeader); DomElems.consoleInputCell.appendChild(DomElems.consoleInputHeader);
@@ -1330,7 +1336,7 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewStaminaHelpTip = createElement("div", { DomElems.overviewStaminaHelpTip = createElement("div", {
class:"help-tip", class:"help-tip",
innerText:"?", innerText:"?",
clickListener: ()=> { clickListener: () => {
dialogBoxCreate("Performing actions will use up your stamina.<br><br>" + dialogBoxCreate("Performing actions will use up your stamina.<br><br>" +
"Your max stamina is determined primarily by your agility stat.<br><br>" + "Your max stamina is determined primarily by your agility stat.<br><br>" +
"Your stamina gain rate is determined by both your agility and your " + "Your stamina gain rate is determined by both your agility and your " +
@@ -1358,7 +1364,7 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewEstPopHelpTip = createElement("div", { DomElems.overviewEstPopHelpTip = createElement("div", {
innerText:"?", class:"help-tip", innerText:"?", class:"help-tip",
clickListener:()=>{ clickListener:() => {
dialogBoxCreate("The success rate of your contracts/operations depends on " + dialogBoxCreate("The success rate of your contracts/operations depends on " +
"the population of Synthoids in your current city. " + "the population of Synthoids in your current city. " +
"The success rate that is shown to you is only an estimate, " + "The success rate that is shown to you is only an estimate, " +
@@ -1421,12 +1427,12 @@ Bladeburner.prototype.createOverviewContent = function() {
appendLineBreaks(DomElems.overviewDiv, 1); appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(createElement("a", { DomElems.overviewDiv.appendChild(createElement("a", {
innerHTML:"Travel", class:"a-link-button", display:"inline-block", innerHTML:"Travel", class:"a-link-button", display:"inline-block",
clickListener:()=>{ clickListener:() => {
var popupId = "bladeburner-travel-popup-cancel-btn"; var popupId = "bladeburner-travel-popup-cancel-btn";
var popupArguments = []; var popupArguments = [];
popupArguments.push(createElement("a", { // Cancel Button popupArguments.push(createElement("a", { // Cancel Button
innerText:"Cancel", class:"a-link-button", innerText:"Cancel", class:"a-link-button",
clickListener:()=>{ clickListener:() => {
removeElementById(popupId); return false; removeElementById(popupId); return false;
} }
})) }))
@@ -1445,7 +1451,7 @@ Bladeburner.prototype.createOverviewContent = function() {
*/ */
class:"cmpy-mgmt-find-employee-option", class:"cmpy-mgmt-find-employee-option",
innerText:BladeburnerConstants.CityNames[i], innerText:BladeburnerConstants.CityNames[i],
clickListener:()=>{ clickListener:() => {
inst.city = BladeburnerConstants.CityNames[i]; inst.city = BladeburnerConstants.CityNames[i];
removeElementById(popupId); removeElementById(popupId);
inst.updateOverviewContent(); inst.updateOverviewContent();
@@ -1468,7 +1474,7 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewDiv.appendChild(createElement("a", { DomElems.overviewDiv.appendChild(createElement("a", {
innerText:"Faction", class:"a-link-button", display:"inline-block", innerText:"Faction", class:"a-link-button", display:"inline-block",
tooltip:"Apply to the Bladeburner Faction, or go to the faction page if you are already a member", tooltip:"Apply to the Bladeburner Faction, or go to the faction page if you are already a member",
clickListener:()=>{ clickListener:() => {
if (bladeburnerFac.isMember) { if (bladeburnerFac.isMember) {
Engine.loadFactionContent(); Engine.loadFactionContent();
displayFactionContent(bladeburnersFactionName); displayFactionContent(bladeburnersFactionName);
@@ -1512,7 +1518,7 @@ Bladeburner.prototype.createActionAndSkillsContent = function() {
DomElems.actionAndSkillsDiv.appendChild(createElement("a", { DomElems.actionAndSkillsDiv.appendChild(createElement("a", {
innerText:buttons[i], innerText:buttons[i],
class:currTab === buttons[i].toLowerCase() ? "bladeburner-nav-button-inactive" : "bladeburner-nav-button", class:currTab === buttons[i].toLowerCase() ? "bladeburner-nav-button-inactive" : "bladeburner-nav-button",
clickListener:()=>{ clickListener:() => {
DomElems.currentTab = buttons[i].toLowerCase(); DomElems.currentTab = buttons[i].toLowerCase();
inst.createActionAndSkillsContent(); inst.createActionAndSkillsContent();
return false; return false;
@@ -1764,15 +1770,16 @@ Bladeburner.prototype.updateOverviewContent = function() {
if (!routing.isOn(Page.Bladeburner)) {return;} if (!routing.isOn(Page.Bladeburner)) {return;}
DomElems.overviewRank.childNodes[0].nodeValue = "Rank: " + formatNumber(this.rank, 2); DomElems.overviewRank.childNodes[0].nodeValue = "Rank: " + formatNumber(this.rank, 2);
DomElems.overviewStamina.innerText = "Stamina: " + formatNumber(this.stamina, 3) + " / " + formatNumber(this.maxStamina, 3); DomElems.overviewStamina.innerText = "Stamina: " + formatNumber(this.stamina, 3) + " / " + formatNumber(this.maxStamina, 3);
DomElems.overviewGen1.innerHTML = ReactDOM.render(<>
"Stamina Penalty: " + formatNumber((1-this.calculateStaminaPenalty())*100, 1) + "%<br><br>" + Stamina Penalty: {formatNumber((1-this.calculateStaminaPenalty())*100, 1)}%<br /><br />
"Team Size: " + formatNumber(this.teamSize, 0) + "<br>" + Team Size: {formatNumber(this.teamSize, 0)}<br />
"Team Members Lost: " + formatNumber(this.teamLost, 0) + "<br><br>" + Team Members Lost: {formatNumber(this.teamLost, 0)}<br /><br />
"Num Times Hospitalized: " + this.numHosp + "<br>" + Num Times Hospitalized: {this.numHosp}<br />
"Money Lost From Hospitalizations: " + numeralWrapper.format(this.moneyLost, "$0.000a") + "<br><br>" + Money Lost From Hospitalizations: {Money(this.moneyLost)}<br /><br />
"Current City: " + this.city + "<br>"; Current City: {this.city}<br />
</>, DomElems.overviewGen1);
DomElems.overviewEstPop.childNodes[0].nodeValue = "Est. Synthoid Population: " + numeralWrapper.format(this.getCurrentCity().popEst, "0.000a"); DomElems.overviewEstPop.childNodes[0].nodeValue = "Est. Synthoid Population: " + numeralWrapper.formatPopulation(this.getCurrentCity().popEst);
DomElems.overviewEstComms.childNodes[0].nodeValue = "Est. Synthoid Communities: " + formatNumber(this.getCurrentCity().comms, 0); DomElems.overviewEstComms.childNodes[0].nodeValue = "Est. Synthoid Communities: " + formatNumber(this.getCurrentCity().comms, 0);
DomElems.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos); DomElems.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos);
DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0); DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0);
@@ -1898,7 +1905,7 @@ Bladeburner.prototype.updateGeneralActionsUIElement = function(el, action) {
el.appendChild(createElement("a", { el.appendChild(createElement("a", {
innerText:"Start", class: "a-link-button", innerText:"Start", class: "a-link-button",
margin:"3px", padding:"3px", margin:"3px", padding:"3px",
clickListener:()=>{ clickListener:() => {
this.action.type = ActionTypes[action.name]; this.action.type = ActionTypes[action.name];
this.action.name = action.name; this.action.name = action.name;
this.startAction(this.action); this.startAction(this.action);
@@ -1939,7 +1946,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
el.appendChild(createElement("a", { el.appendChild(createElement("a", {
innerText:"Start", class: "a-link-button", innerText:"Start", class: "a-link-button",
padding:"3px", margin:"3px", padding:"3px", margin:"3px",
clickListener:()=>{ clickListener:() => {
this.action.type = ActionTypes.Contract; this.action.type = ActionTypes.Contract;
this.action.name = action.name; this.action.name = action.name;
this.startAction(this.action); this.startAction(this.action);
@@ -1963,7 +1970,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
padding:"2px", margin:"2px", padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the contract" : "", tooltip: isActive ? "WARNING: changing the level will restart the contract" : "",
display:"inline", display:"inline",
clickListener:()=>{ clickListener:() => {
++action.level; ++action.level;
if (isActive) {this.startAction(this.action);} // Restart Action if (isActive) {this.startAction(this.action);} // Restart Action
this.updateContractsUIElement(el, action); this.updateContractsUIElement(el, action);
@@ -1975,7 +1982,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
padding:"2px", margin:"2px", padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the contract" : "", tooltip: isActive ? "WARNING: changing the level will restart the contract" : "",
display:"inline", display:"inline",
clickListener:()=>{ clickListener:() => {
--action.level; --action.level;
if (isActive) {this.startAction(this.action);} // Restart Action if (isActive) {this.startAction(this.action);} // Restart Action
this.updateContractsUIElement(el, action); this.updateContractsUIElement(el, action);
@@ -1989,8 +1996,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
display:"inline-block", display:"inline-block",
innerHTML:action.desc + "\n\n" + innerHTML:action.desc + "\n\n" +
`Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` + `Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
"Time Required: " + convertTimeMsToTimeElapsedString(actionTime*1000) + "\n" +
"Time Required (s): " + formatNumber(actionTime, 0) + "\n" +
"Contracts remaining: " + Math.floor(action.count) + "\n" + "Contracts remaining: " + Math.floor(action.count) + "\n" +
"Successes: " + action.successes + "\n" + "Successes: " + action.successes + "\n" +
"Failures: " + action.failures, "Failures: " + action.failures,
@@ -2038,7 +2044,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
el.appendChild(createElement("a", { el.appendChild(createElement("a", {
innerText:"Start", class: "a-link-button", innerText:"Start", class: "a-link-button",
margin:"3px", padding:"3px", margin:"3px", padding:"3px",
clickListener:()=>{ clickListener:() => {
this.action.type = ActionTypes.Operation; this.action.type = ActionTypes.Operation;
this.action.name = action.name; this.action.name = action.name;
this.startAction(this.action); this.startAction(this.action);
@@ -2049,7 +2055,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
el.appendChild(createElement("a", { el.appendChild(createElement("a", {
innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button", innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button",
margin:"3px", padding:"3px", margin:"3px", padding:"3px",
clickListener:()=>{ clickListener:() => {
var popupId = "bladeburner-operation-set-team-size-popup"; var popupId = "bladeburner-operation-set-team-size-popup";
var txt = createElement("p", { var txt = createElement("p", {
innerText:"Enter the amount of team members you would like to take on these " + innerText:"Enter the amount of team members you would like to take on these " +
@@ -2063,7 +2069,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
}); });
var setBtn = createElement("a", { var setBtn = createElement("a", {
innerText:"Confirm", class:"a-link-button", innerText:"Confirm", class:"a-link-button",
clickListener:()=>{ clickListener:() => {
var num = Math.round(parseFloat(input.value)); var num = Math.round(parseFloat(input.value));
if (isNaN(num)) { if (isNaN(num)) {
dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric)") dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric)")
@@ -2077,7 +2083,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
}); });
var cancelBtn = createElement("a", { var cancelBtn = createElement("a", {
innerText:"Cancel", class:"a-link-button", innerText:"Cancel", class:"a-link-button",
clickListener:()=>{ clickListener:() => {
removeElementById(popupId); removeElementById(popupId);
return false; return false;
} }
@@ -2101,7 +2107,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
padding:"2px", margin:"2px", padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "", tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "",
display:"inline", display:"inline",
clickListener:()=>{ clickListener:() => {
++action.level; ++action.level;
if (isActive) {this.startAction(this.action);} // Restart Action if (isActive) {this.startAction(this.action);} // Restart Action
this.updateOperationsUIElement(el, action); this.updateOperationsUIElement(el, action);
@@ -2113,7 +2119,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
padding:"2px", margin:"2px", padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "", tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "",
display:"inline", display:"inline",
clickListener:()=>{ clickListener:() => {
--action.level; --action.level;
if (isActive) {this.startAction(this.action);} // Restart Action if (isActive) {this.startAction(this.action);} // Restart Action
this.updateOperationsUIElement(el, action); this.updateOperationsUIElement(el, action);
@@ -2129,7 +2135,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
display:"inline-block", display:"inline-block",
innerHTML:action.desc + "\n\n" + innerHTML:action.desc + "\n\n" +
`Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` + `Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
"Time Required(s): " + formatNumber(actionTime, 0) + "\n" + "Time Required: " + convertTimeMsToTimeElapsedString(actionTime*1000) + "\n" +
"Operations remaining: " + Math.floor(action.count) + "\n" + "Operations remaining: " + Math.floor(action.count) + "\n" +
"Successes: " + action.successes + "\n" + "Successes: " + action.successes + "\n" +
"Failures: " + action.failures, "Failures: " + action.failures,
@@ -2190,7 +2196,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
el.appendChild(createElement("a", { // Start button el.appendChild(createElement("a", { // Start button
innerText:"Start", margin:"3px", padding:"3px", innerText:"Start", margin:"3px", padding:"3px",
class:hasReqdRank ? "a-link-button" : "a-link-button-inactive", class:hasReqdRank ? "a-link-button" : "a-link-button-inactive",
clickListener:()=>{ clickListener:() => {
this.action.type = ActionTypes.BlackOperation; this.action.type = ActionTypes.BlackOperation;
this.action.name = action.name; this.action.name = action.name;
this.startAction(this.action); this.startAction(this.action);
@@ -2201,7 +2207,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
el.appendChild(createElement("a", { // Set Team Size Button el.appendChild(createElement("a", { // Set Team Size Button
innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button", innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button",
margin:"3px", padding:"3px", margin:"3px", padding:"3px",
clickListener:()=>{ clickListener:() => {
var popupId = "bladeburner-operation-set-team-size-popup"; var popupId = "bladeburner-operation-set-team-size-popup";
var txt = createElement("p", { var txt = createElement("p", {
innerText:"Enter the amount of team members you would like to take on this " + innerText:"Enter the amount of team members you would like to take on this " +
@@ -2215,7 +2221,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
}); });
var setBtn = createElement("a", { var setBtn = createElement("a", {
innerText:"Confirm", class:"a-link-button", innerText:"Confirm", class:"a-link-button",
clickListener:()=>{ clickListener:() => {
var num = Math.round(parseFloat(input.value)); var num = Math.round(parseFloat(input.value));
if (isNaN(num)) { if (isNaN(num)) {
dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric)") dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric)")
@@ -2229,7 +2235,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
}); });
var cancelBtn = createElement("a", { var cancelBtn = createElement("a", {
innerText:"Cancel", class:"a-link-button", innerText:"Cancel", class:"a-link-button",
clickListener:()=>{ clickListener:() => {
removeElementById(popupId); removeElementById(popupId);
return false; return false;
} }
@@ -2252,7 +2258,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
el.appendChild(createElement("p", { el.appendChild(createElement("p", {
display:"inline-block", display:"inline-block",
innerHTML:`Estimated Success Chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` + innerHTML:`Estimated Success Chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
"Time Required(s): " + formatNumber(actionTime, 0), "Time Required: " + convertTimeMsToTimeElapsedString(actionTime*1000),
})) }))
} }
@@ -2265,9 +2271,15 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
} }
var pointCost = skill.calculateCost(currentLevel); var pointCost = skill.calculateCost(currentLevel);
el.appendChild(createElement("h2", { // Header const nameDiv = createElement("div");
innerText:skill.name + " (Lvl " + currentLevel + ")", display:"inline-block" ReactDOM.render(React.createElement(CopyableText, {value: skill.name}, null), nameDiv);
})); el.appendChild(nameDiv)
const h2 = createElement("h2", { // Header
display:"inline-block",
});
h2.appendChild(nameDiv);
el.appendChild(h2);
var canLevel = this.skillPoints >= pointCost; var canLevel = this.skillPoints >= pointCost;
var maxLvl = skill.maxLvl ? currentLevel >= skill.maxLvl : false; var maxLvl = skill.maxLvl ? currentLevel >= skill.maxLvl : false;
@@ -2275,7 +2287,7 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
innerText:"Level", display:"inline-block", innerText:"Level", display:"inline-block",
class: canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive", class: canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive",
margin:"3px", padding:"3px", margin:"3px", padding:"3px",
clickListener:()=>{ clickListener:() => {
if (this.skillPoints < pointCost) {return;} if (this.skillPoints < pointCost) {return;}
this.skillPoints -= pointCost; this.skillPoints -= pointCost;
this.upgradeSkill(skill); this.upgradeSkill(skill);
@@ -2284,6 +2296,10 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
} }
})); }));
appendLineBreaks(el, 2); appendLineBreaks(el, 2);
el.appendChild(createElement("p", {
display:"block",
innerText:`Level: ${currentLevel}`,
}));
if (maxLvl) { if (maxLvl) {
el.appendChild(createElement("p", { el.appendChild(createElement("p", {
color:"red", display:"block", color:"red", display:"block",
@@ -2311,7 +2327,7 @@ Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
} }
if (input == null || DomElems.consoleDiv == null) {return;} if (input == null || DomElems.consoleDiv == null) {return;}
$("#bladeubrner-console-input-row").before('<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>'); $("#bladeburner-console-input-row").before('<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>');
if (DomElems.consoleTable.childNodes.length > MaxConsoleEntries) { if (DomElems.consoleTable.childNodes.length > MaxConsoleEntries) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild); DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);

98
src/Casino/CoinFlip.tsx Normal file
View File

@@ -0,0 +1,98 @@
/**
* React Subcomponent for displaying a location's UI, when that location is a gym
*
* This subcomponent renders all of the buttons for training at the gym
*/
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { BadRNG } from "./RNG";
import { Game } from "./Game";
import { trusted } from "./utils";
type IProps = {
p: IPlayer;
}
type IState = {
investment: number;
result: any;
status: string;
playLock: boolean;
}
const minPlay = 0;
const maxPlay = 10e3;
export class CoinFlip extends Game<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
investment: 1000,
result: <span> </span>,
status: '',
playLock: false,
};
this.play = this.play.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>) {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = minPlay;
}
if (investment > maxPlay) {
investment = maxPlay;
}
if (investment < minPlay) {
investment = minPlay;
}
this.setState({investment: investment});
}
play(guess: string) {
if(this.reachedLimit(this.props.p)) return;
const v = BadRNG.random();
let letter: string;
if (v < 0.5) {
letter = 'H';
} else {
letter = 'T';
}
const correct: boolean = guess===letter;
this.setState({
result: <span className={correct ? "text" : "failure"}>{letter}</span>,
status: correct ? " win!" : "lose!",
playLock: true,
});
setTimeout(()=>this.setState({playLock: false}), 250);
if (correct) {
this.win(this.props.p, this.state.investment);
} else {
this.win(this.props.p, -this.state.investment);
}
if(this.reachedLimit(this.props.p)) return;
}
render() {
return <>
<pre>
++<br />
| | | |<br />
| | {this.state.result} | |<br />
| | | |<br />
++<br />
</pre>
<span className="text">Play for: </span><input type="number" className='text-input' onChange={this.updateInvestment} value={this.state.investment} /><br />
<StdButton onClick={trusted(() => this.play('H'))} text={"Head!"} disabled={this.state.playLock} />
<StdButton onClick={trusted(() => this.play('T'))} text={"Tail!"} disabled={this.state.playLock} />
<h1>{this.state.status}</h1>
</>
}
}

20
src/Casino/Game.tsx Normal file
View File

@@ -0,0 +1,20 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../../utils/DialogBox";
const gainLimit = 10e9;
export class Game<T,U> extends React.Component<T, U> {
win(p: IPlayer, n: number) {
p.gainMoney(n);
p.recordMoneySource(n, "casino");
}
reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if(reached) {
dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.</>);
}
return reached;
}
}

64
src/Casino/RNG.ts Normal file
View File

@@ -0,0 +1,64 @@
export interface RNG {
random(): number
}
/*
* very bad RNG, meant to be used as introduction to RNG manipulation. It has a
* period of 1024.
*/
class RNG0 implements RNG {
x: number;
m: number = 1024;
a: number = 341;
c: number = 1;
constructor() {
this.x = 0;
this.reset();
}
step() {
this.x = (this.a*this.x+this.c) % this.m;
}
random(): number {
this.step();
return this.x/this.m;
}
reset() {
this.x = (new Date()).getTime() % this.m;
}
}
export const BadRNG: RNG0 = new RNG0();
/*
* WichmannHill PRNG
* The period is 6e12.
*/
export class WHRNG implements RNG {
s1: number = 0;
s2: number = 0;
s3: number = 0;
constructor(totalPlaytime: number) {
// This one is seeded by the players total play time.
const v: number = (totalPlaytime/1000)%30000;
this.s1 = v;
this.s2 = v;
this.s3 = v;
}
step() {
this.s1 = (171 * this.s1) % 30269;
this.s2 = (172 * this.s2) % 30307;
this.s3 = (170 * this.s3) % 30323;
}
random(): number {
this.step();
return (this.s1/30269.0 + this.s2/30307.0 + this.s3/30323.0)%1.0;
}
}

295
src/Casino/Roulette.tsx Normal file
View File

@@ -0,0 +1,295 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { Money } from "../ui/React/Money";
import { Game } from "./Game";
import { WHRNG } from "./RNG";
import { trusted } from "./utils";
type IProps = {
p: IPlayer;
}
type IState = {
investment: number;
canPlay: boolean;
status: string | JSX.Element;
n: number;
lock: boolean;
strategy: Strategy;
}
const minPlay = 0;
const maxPlay = 1e7;
function isRed(n: number): boolean {
return [1, 3, 5, 7, 9, 12, 14, 16, 18, 19,
21, 23, 25, 27, 30, 32, 34, 36].includes(n);
}
function isBlack(n: number): boolean {
return !isRed(n);
}
type Strategy = {
match: (n: number) => boolean;
payout: number;
}
const redNumbers: number[] = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19,
21, 23, 25, 27, 30, 32, 34, 36];
const strategies: {
Red: Strategy;
Black: Strategy;
Odd: Strategy;
Even: Strategy;
High: Strategy;
Low: Strategy;
Third1: Strategy;
Third2: Strategy;
Third3: Strategy;
} = {
Red: {
match: (n: number): boolean => {
if (n === 0) return false;
return redNumbers.includes(n);
},
payout: 1,
},
Black: {
match: (n: number): boolean => {
return !redNumbers.includes(n);
},
payout: 1,
},
Odd: {
match: (n: number): boolean => {
if (n === 0) return false;
return n%2 === 1;
},
payout: 1,
},
Even: {
match: (n: number): boolean => {
if (n === 0) return false;
return n%2 === 0;
},
payout: 1,
},
High: {
match: (n: number): boolean => {
if (n === 0) return false;
return n>18
},
payout: 1,
},
Low: {
match: (n: number): boolean => {
if (n === 0) return false;
return n<19;
},
payout: 1,
},
Third1: {
match: (n: number): boolean => {
if (n === 0) return false;
return n <= 12;
},
payout: 2,
},
Third2: {
match: (n: number): boolean => {
if (n === 0) return false;
return 13 <= n && n <= 24;
},
payout: 2,
},
Third3: {
match: (n: number): boolean => {
if (n === 0) return false;
return 25 <= n;
},
payout: 2,
},
}
function Single(s: number): Strategy {
return {
match: (n: number): boolean => {
return s === n;
},
payout: 36,
}
}
export class Roulette extends Game<IProps, IState> {
interval: number = -1;
rng: WHRNG;
constructor(props: IProps) {
super(props);
this.rng = new WHRNG((new Date()).getTime());
this.state = {
investment: 1000,
canPlay: true,
status: 'waiting',
n: 0,
lock: true,
strategy: {
payout: 0,
match: (n: number): boolean => { return false },
},
}
this.step = this.step.bind(this);
this.currentNumber = this.currentNumber.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
componentDidMount() {
this.interval = setInterval(this.step, 50);
}
step() {
if (!this.state.lock) {
this.setState({n: Math.floor(Math.random()*37)});
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>) {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = minPlay;
}
if (investment > maxPlay) {
investment = maxPlay;
}
if (investment < minPlay) {
investment = minPlay;
}
this.setState({investment: investment});
}
currentNumber() {
if (this.state.n === 0) return '0';
const color = isRed(this.state.n) ? 'R' : 'B';
return `${this.state.n}${color}`;
}
play(s: Strategy) {
if(this.reachedLimit(this.props.p)) return;
this.setState({
canPlay: false,
lock: false,
status: 'playing',
strategy: s,
})
setTimeout(() => {
let n = Math.floor(this.rng.random()*37);
let status = <></>;
let gain = 0;
let playerWin = this.state.strategy.match(n)
// oh yeah, the house straight up cheats. Try finding the seed now!
if(playerWin && Math.random() > 0.9) {
playerWin = false;
while(this.state.strategy.match(n)) {
n++;
}
}
if(playerWin) {
gain = this.state.investment*this.state.strategy.payout;
status = <>won {Money(gain)}</>;
} else {
gain = -this.state.investment;
status = <>lost {Money(-gain)}</>;
}
this.win(this.props.p, gain);
this.setState({
canPlay: true,
lock: true,
status: status,
n: n,
});
this.reachedLimit(this.props.p);
}, 1600);
}
render() {
return <>
<h1>{this.currentNumber()}</h1>
<input type="number" className='text-input' onChange={this.updateInvestment} placeholder={"Amount to play"} value={this.state.investment} disabled={!this.state.canPlay} />
<h1>{this.state.status}</h1>
<table>
<tbody>
<tr>
<td><StdButton text={"3"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(3)))} /></td>
<td><StdButton text={"6"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(6)))} /></td>
<td><StdButton text={"9"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(9)))} /></td>
<td><StdButton text={"12"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(12)))} /></td>
<td><StdButton text={"15"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(15)))} /></td>
<td><StdButton text={"18"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(18)))} /></td>
<td><StdButton text={"21"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(21)))} /></td>
<td><StdButton text={"24"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(24)))} /></td>
<td><StdButton text={"27"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(27)))} /></td>
<td><StdButton text={"30"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(30)))} /></td>
<td><StdButton text={"33"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(33)))} /></td>
<td><StdButton text={"36"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(36)))} /></td>
</tr>
<tr>
<td><StdButton text={"2"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(2)))} /></td>
<td><StdButton text={"5"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(5)))} /></td>
<td><StdButton text={"8"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(8)))} /></td>
<td><StdButton text={"11"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(11)))} /></td>
<td><StdButton text={"14"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(14)))} /></td>
<td><StdButton text={"17"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(17)))} /></td>
<td><StdButton text={"20"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(20)))} /></td>
<td><StdButton text={"23"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(23)))} /></td>
<td><StdButton text={"26"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(26)))} /></td>
<td><StdButton text={"29"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(29)))} /></td>
<td><StdButton text={"32"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(32)))} /></td>
<td><StdButton text={"35"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(35)))} /></td>
</tr>
<tr>
<td><StdButton text={"1"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(1)))} /></td>
<td><StdButton text={"4"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(4)))} /></td>
<td><StdButton text={"7"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(7)))} /></td>
<td><StdButton text={"10"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(10)))} /></td>
<td><StdButton text={"13"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(13)))} /></td>
<td><StdButton text={"16"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(16)))} /></td>
<td><StdButton text={"19"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(19)))} /></td>
<td><StdButton text={"22"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(22)))} /></td>
<td><StdButton text={"25"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(25)))} /></td>
<td><StdButton text={"28"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(28)))} /></td>
<td><StdButton text={"31"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(31)))} /></td>
<td><StdButton text={"34"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(34)))} /></td>
</tr>
<tr>
<td colSpan={4}><StdButton text={"1 to 12"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Third1))} /></td>
<td colSpan={4}><StdButton text={"13 to 24"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Third2))} /></td>
<td colSpan={4}><StdButton text={"25 to 36"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Third3))} /></td>
</tr>
<tr>
<td colSpan={2}><StdButton text={"Red"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Red))} /></td>
<td colSpan={2}><StdButton text={"Black"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Black))} /></td>
<td colSpan={2}><StdButton text={"Odd"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Odd))} /></td>
<td colSpan={2}><StdButton text={"Even"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Even))} /></td>
<td colSpan={2}><StdButton text={"High"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.High))} /></td>
<td colSpan={2}><StdButton text={"Low"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(strategies.Low))} /></td>
</tr>
<tr>
<td><StdButton text={"0"} disabled={!this.state.canPlay} onClick={trusted(()=>this.play(Single(0)))} /></td>
</tr>
</tbody>
</table>
</>
}
}

239
src/Casino/SlotMachine.tsx Normal file
View File

@@ -0,0 +1,239 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { Money } from "../ui/React/Money";
import { WHRNG } from "./RNG";
import { Game } from "./Game";
import { trusted } from "./utils";
type IProps = {
p: IPlayer;
}
type IState = {
index: number[];
locks: number[];
investment: number;
canPlay: boolean;
status: string | JSX.Element;
}
// statically shuffled array of symbols.
let symbols = ["D", "C", "$", "?", "♥", "A", "C", "B", "C", "E", "B", "E", "C",
"*", "D", "♥", "B", "A", "A", "A", "C", "A", "D", "B", "E", "?", "D", "*",
"@", "♥", "B", "E", "?"];
function getPayout(s: string, n: number): number {
switch (s) {
case "$":
return [20, 200, 1000][n];
case "@":
return [8, 80, 400][n];
case "♥":
case "?":
return [6, 20, 150][n];
case "D":
case "E":
return [1, 8, 30][n];
default:
return [1, 5, 20][n];
}
}
const payLines = [
// lines
[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]],
[[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]],
[[2, 0], [2, 1], [2, 2], [2, 3], [2, 4]],
// Vs
[[2, 0], [1, 1], [0, 2], [1, 3], [2, 4]],
[[0, 0], [1, 1], [2, 2], [1, 3], [0, 4]],
// rest
[[0, 0], [1, 1], [1, 2], [1, 3], [0, 4]],
[[2, 0], [1, 1], [1, 2], [1, 3], [2, 4]],
[[1, 0], [0, 1], [0, 2], [0, 3], [1, 4]],
[[1, 0], [2, 1], [2, 2], [2, 3], [1, 4]],
];
const minPlay = 0;
const maxPlay = 1e6;
export class SlotMachine extends Game<IProps, IState> {
rng: WHRNG;
interval: number = -1;
constructor(props: IProps) {
super(props);
this.rng = new WHRNG(this.props.p.totalPlaytime);
this.state = {
index: [0, 0, 0, 0, 0],
investment: 1000,
locks: [0, 0, 0, 0, 0],
canPlay: true,
status: 'waiting',
};
this.play = this.play.bind(this);
this.lock = this.lock.bind(this);
this.unlock = this.unlock.bind(this);
this.step = this.step.bind(this);
this.checkWinnings = this.checkWinnings.bind(this);
this.getTable = this.getTable.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
componentDidMount() {
this.interval = setInterval(this.step, 50);
}
step() {
let stoppedOne = false;
const index = this.state.index.slice();
for(const i in index) {
if (index[i] === this.state.locks[i] && !stoppedOne) continue;
index[i] = (index[i] + 1) % symbols.length;
stoppedOne = true;
}
this.setState({index: index});
if(stoppedOne && index.every((e, i) => e === this.state.locks[i])) {
this.checkWinnings();
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
getTable(): string[][] {
return [
[symbols[(this.state.index[0]+symbols.length-1)%symbols.length], symbols[(this.state.index[1]+symbols.length-1)%symbols.length], symbols[(this.state.index[2]+symbols.length-1)%symbols.length], symbols[(this.state.index[3]+symbols.length-1)%symbols.length], symbols[(this.state.index[4]+symbols.length-1)%symbols.length]],
[symbols[this.state.index[0]], symbols[this.state.index[1]], symbols[this.state.index[2]], symbols[this.state.index[3]], symbols[this.state.index[4]]],
[symbols[(this.state.index[0]+1)%symbols.length], symbols[(this.state.index[1]+1)%symbols.length], symbols[(this.state.index[2]+1)%symbols.length], symbols[(this.state.index[3]+1)%symbols.length], symbols[(this.state.index[4]+1)%symbols.length]],
];
}
play() {
if(this.reachedLimit(this.props.p)) return;
this.setState({status: 'playing'});
this.win(this.props.p, -this.state.investment);
if(!this.state.canPlay) return;
this.unlock();
setTimeout(this.lock, this.rng.random()*2000+1000);
}
lock() {
this.setState({
locks: [
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
],
})
}
checkWinnings() {
const t = this.getTable();
const getPaylineData = function(payline: number[][]): string[] {
let data = [];
for(const point of payline) {
data.push(t[point[0]][point[1]]);
}
return data;
}
const countSequence = function(data: string[]): number {
let count = 1;
for(let i = 1; i < data.length; i++) {
if (data[i]!==data[i-1]) break;
count++;
}
return count;
}
let gains = -this.state.investment;
for (const payline of payLines) {
const data = getPaylineData(payline);
const count = countSequence(data);
if (count < 3) continue;
const payout = getPayout(data[0], count-3);
gains += this.state.investment*payout;
this.win(this.props.p, this.state.investment*payout);
}
this.setState({
status: <>{gains>0?"gained":"lost"} {Money(Math.abs(gains))}</>,
canPlay: true,
})
if(this.reachedLimit(this.props.p)) return;
}
unlock() {
this.setState({
locks: [-1, -1, -1, -1, -1],
canPlay: false,
})
}
updateInvestment(e: React.FormEvent<HTMLInputElement>) {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = minPlay;
}
if (investment > maxPlay) {
investment = maxPlay;
}
if (investment < minPlay) {
investment = minPlay;
}
this.setState({investment: investment});
}
render() {
const t = this.getTable();
return <>
<pre>
++<br />
| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |<br />
| | | | | | | |<br />
| | {symbols[this.state.index[0]]} | {symbols[this.state.index[1]]} | {symbols[this.state.index[2]]} | {symbols[this.state.index[3]]} | {symbols[this.state.index[4]]} | |<br />
| | | | | | | |<br />
| | {symbols[(this.state.index[0]+1)%symbols.length]} | {symbols[(this.state.index[1]+1)%symbols.length]} | {symbols[(this.state.index[2]+1)%symbols.length]} | {symbols[(this.state.index[3]+1)%symbols.length]} | {symbols[(this.state.index[4]+1)%symbols.length]} | |<br />
++<br />
</pre>
<input type="number" className='text-input' onChange={this.updateInvestment} placeholder={"Amount to play"} value={this.state.investment} disabled={!this.state.canPlay} />
<StdButton onClick={trusted(this.play)} text={"Spin!"} disabled={!this.state.canPlay} />
<h1>{this.state.status}</h1>
<h2>Pay lines</h2>
<pre>
----- ····· ····· <br />
····· ----- ····· <br />
····· ····· ----- <br />
</pre>
<br />
<pre>
··^·· \···/ \···/<br />
·/·\· ·\·/· ·---·<br />
/···\ ··v·· ·····<br />
</pre>
<br />
<pre>
····· ·---· ·····<br />
·---· /···\ \···/<br />
/···\ ····· ·---·<br />
</pre>
</>
}
}
// https://felgo.com/doc/how-to-make-a-slot-game-tutorial/

8
src/Casino/utils.ts Normal file
View File

@@ -0,0 +1,8 @@
import * as React from "react";
export function trusted(f: () => void): (event: React.MouseEvent<HTMLElement, MouseEvent>) => any {
return function(event: React.MouseEvent<HTMLElement, MouseEvent>): any {
if(!event.isTrusted) return;
f();
};
}

View File

@@ -181,6 +181,9 @@ export class CodingContract {
return new Promise<CodingContractResult>((resolve: Function, reject: Function) => { return new Promise<CodingContractResult>((resolve: Function, reject: Function) => {
const contractType: CodingContractType = CodingContractTypes[this.type]; const contractType: CodingContractType = CodingContractTypes[this.type];
const popupId: string = `coding-contract-prompt-popup-${this.fn}`; const popupId: string = `coding-contract-prompt-popup-${this.fn}`;
const title: HTMLElement = createElement("h1", {
innerHTML: this.type,
});
const txt: HTMLElement = createElement("p", { const txt: HTMLElement = createElement("p", {
innerHTML: ["You are attempting to solve a Coding Contract. You have", innerHTML: ["You are attempting to solve a Coding Contract. You have",
`${this.getMaxNumTries() - this.tries} tries remaining,`, `${this.getMaxNumTries() - this.tries} tries remaining,`,
@@ -225,7 +228,7 @@ export class CodingContract {
innerText: "Cancel", innerText: "Cancel",
}); });
const lineBreak: HTMLElement = createElement("br"); const lineBreak: HTMLElement = createElement("br");
createPopup(popupId, [txt, lineBreak, lineBreak, answerInput, solveBtn, cancelBtn]); createPopup(popupId, [title, lineBreak, txt, lineBreak, lineBreak, answerInput, solveBtn, cancelBtn]);
answerInput.focus(); answerInput.focus();
}); });
} }

View File

@@ -6,13 +6,13 @@
import { IMap } from "./types"; import { IMap } from "./types";
export let CONSTANTS: IMap<any> = { export let CONSTANTS: IMap<any> = {
Version: "0.50.2", Version: "0.51.3",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience /** 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 * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
* the player will have this level assuming no multipliers. Multipliers can cause skills to go above this. * the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
*/ */
MaxSkillLevel: 975, MaxSkillLevel: 975,
// Milliseconds per game cycle // Milliseconds per game cycle
MilliPerCycle: 200, MilliPerCycle: 200,
@@ -218,7 +218,7 @@ export let CONSTANTS: IMap<any> = {
CrimeHeist: "pull off the ultimate heist", CrimeHeist: "pull off the ultimate heist",
// Coding Contract // Coding Contract
// TODO Move this into Coding contract impelmentation? // TODO: Move this into Coding contract implementation?
CodingContractBaseFactionRepGain: 2500, CodingContractBaseFactionRepGain: 2500,
CodingContractBaseCompanyRepGain: 4000, CodingContractBaseCompanyRepGain: 4000,
CodingContractBaseMoneyGain: 75e6, CodingContractBaseMoneyGain: 75e6,
@@ -228,18 +228,39 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate: LatestUpdate:
` `
v0.50.2 - 2021-03-25 Everyone asked for this one. (hydroflame) v0.51.3 - 2021-04-16 Y'all broke it on the first day (hydroflame)
------- -------
BitNodeMultipliers
* 'GangKarmaRequirements': a new multipler that influences how much karma is required to make a gang different bitnodes. Passive faction reputation
* Reworked, from 1 rep / 2 minute. Now is a complicated percentage of the
reputation you'd gain working for them. It's not op but it feels a bit
more useful.
Netscript Netscript
* 'connect': a new singularity function that connects you to a server. (like the terminal command) * print/tprint now take any number of arguments.
* 'manualHack': a new singularity function that performs a manual hack on the players current server. * print/tprint will now print object as json.
* ns2 stack trace works on Firefox now. * print/tprint now handle passing in an undefined argument properly.
Casino
* Cannot bet negative money anymore.
* Roulette max bet is a bit higher.
* Coin Flip has a small cooldown.
* All buttons reject unstrusted mouse events.
Documentation
* Changed a message that said nsjs only works on Chrome.
Bugfix
* hacknet.maxNumNodes now works for both nodes and servers.
* Fixed a bug where the popup boxes would contain data from previous popup boxes.
* .js files will also have the export async function boilerplate.
Misc. Misc.
* New shortcut, Alt + b, brings you to bladeburner * turned off autocomplete for the terminal text input.
* New shortcut, Alt + g, brings you to gang * Fixed an issue on Windows+Firefox where pressing up on the terminal would
bring the cursor to the begining of the line. (Issue #836)
* Hacknet node names is easier to handle for screen readers.
* Money spent on classes is now tracked independently of work money.
* running coding contract from the terminal will display its name.
` `
} }

View File

@@ -0,0 +1,39 @@
import { calculateIntelligenceBonus } from "../../PersonObjects/formulas/intelligence";
import { CONSTANTS } from "../../Constants";
export interface ICrime {
hacking_success_weight: number;
strength_success_weight: number;
defense_success_weight: number;
dexterity_success_weight: number;
agility_success_weight: number;
charisma_success_weight: number;
difficulty: number;
}
export interface IPerson {
hacking_skill: number;
strength: number;
defense: number;
dexterity: number;
agility: number;
charisma: number;
intelligence: number;
crime_success_mult: number;
}
export function calculateCrimeSuccessChance(crime: ICrime, person: IPerson) {
let chance: number = (crime.hacking_success_weight * person.hacking_skill +
crime.strength_success_weight * person.strength +
crime.defense_success_weight * person.defense +
crime.dexterity_success_weight * person.dexterity +
crime.agility_success_weight * person.agility +
crime.charisma_success_weight * person.charisma +
CONSTANTS.IntelligenceCrimeWeight * person.intelligence);
chance /= CONSTANTS.MaxSkillLevel;
chance /= crime.difficulty;
chance *= person.crime_success_mult;
chance *= calculateIntelligenceBonus(person.intelligence);
return Math.min(chance, 1);
}

View File

@@ -1,11 +1,14 @@
import * as React from "react";
import { DarkWebItems } from "./DarkWebItems"; import { DarkWebItems } from "./DarkWebItems";
import { Player } from "../Player"; import { Player } from "../Player";
import { SpecialServerIps } from "../Server/SpecialServerIps"; import { SpecialServerIps } from "../Server/SpecialServerIps";
import { post } from "../ui/postToTerminal"; import { post, postElement } from "../ui/postToTerminal";
import { Money } from "../ui/React/Money";
import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress"; import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { numeralWrapper } from "../ui/numeralFormat";
//Posts a "help" message if connected to DarkWeb //Posts a "help" message if connected to DarkWeb
export function checkIfConnectedToDarkweb(): void { export function checkIfConnectedToDarkweb(): void {
@@ -49,7 +52,7 @@ export function executeDarkwebTerminalCommand(commandArray: string[]): void {
function listAllDarkwebItems() { function listAllDarkwebItems() {
for(const key in DarkWebItems) { for(const key in DarkWebItems) {
const item = DarkWebItems[key]; const item = DarkWebItems[key];
post(item.toString()); postElement(<>{item.program} - {Money(item.price)} - {item.description}</>);
} }
} }

View File

@@ -1,4 +1,4 @@
import { formatNumber } from "../../utils/StringHelperFunctions"; import { numeralWrapper } from "../ui/numeralFormat";
export class DarkWebItem { export class DarkWebItem {
program: string; program: string;
@@ -10,9 +10,4 @@ export class DarkWebItem {
this.price = price; this.price = price;
this.description = description; this.description = description;
} }
// Formats the item to print out to terminal (e.g. BruteSSH.exe -$500,000 - Opens up SSH Ports)
toString(): string {
return [this.program, "$" + formatNumber(this.price, 0), this.description].join(' - ');
}
} }

View File

@@ -26,6 +26,7 @@ import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement"; import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData"; import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { Money } from "./ui/React/Money";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
@@ -611,12 +612,16 @@ class DevMenuComponent extends Component {
} }
viewStockCaps() { viewStockCaps() {
let text = "<table><tbody><tr><th>Stock</th><th>Price cap</th></tr>"; let stocks = [];
this.processStocks((stock) => { this.processStocks((stock) => {
text += `<tr><td>${stock.symbol}</td><td style="text-align:right;">${numeralWrapper.format(stock.cap, '$0.000a')}</td></tr>`; stocks.push(<tr key={stock.symbol}>
<td>{stock.symbol}</td>
<td style={{'textAlign':'right'}}>{Money(stock.cap)}</td>
</tr>);
}); });
text += "</tbody></table>"; dialogBoxCreate(<table><tbody><tr><th>Stock</th><th>Price cap</th></tr>
dialogBoxCreate(text); {stocks}
</tbody></table>);
} }
sleeveMaxAllShock() { sleeveMaxAllShock() {
@@ -703,8 +708,11 @@ class DevMenuComponent extends Component {
<h2>Generic</h2> <h2>Generic</h2>
</div> </div>
<div className="row"> <div className="row">
<button className="std-button" onClick={this.addMoney(1e6)}>Add $1m</button>
<button className="std-button" onClick={this.addMoney(1e9)}>Add $1b</button>
<button className="std-button" onClick={this.addMoney(1e12)}>Add $1t</button> <button className="std-button" onClick={this.addMoney(1e12)}>Add $1t</button>
<button className="std-button" onClick={this.addMoney(1e15)}>Add $1000t</button> <button className="std-button" onClick={this.addMoney(1e15)}>Add $1000t</button>
<button className="std-button" onClick={this.addMoney(1e27)}>Add $1e27</button>
<button className="std-button" onClick={this.upgradeRam}>Upgrade Home Computer's RAM</button> <button className="std-button" onClick={this.upgradeRam}>Upgrade Home Computer's RAM</button>
</div> </div>
<div className="row"> <div className="row">

View File

@@ -15,6 +15,11 @@ import { Factions } from "./Factions";
import { HackingMission, setInMission } from "../Missions"; import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player"; import { Player } from "../Player";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import {
getHackingWorkRepGain,
getFactionSecurityWorkRepGain,
getFactionFieldWorkRepGain,
} from "../PersonObjects/formulas/reputation";
import { Page, routing } from "../ui/navigationTracking"; import { Page, routing } from "../ui/navigationTracking";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
@@ -25,6 +30,8 @@ import {
Generic_fromJSON Generic_fromJSON
} from "../../utils/JSONReviver"; } from "../../utils/JSONReviver";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { import {
yesNoBoxCreate, yesNoBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetYesButton,
@@ -108,10 +115,12 @@ export function purchaseAugmentationBoxCreate(aug, fac) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" + yesNoBoxCreate(<>
aug.info + "<br><br>" + <h2>{aug.name}</h2><br />
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" + <div dangerouslySetInnerHTML={{__html: aug.info}}></div><br /><br />
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?"); <br />Would you like to purchase the {aug.name} Augmentation for&nbsp;
{Money(aug.baseCost * factionInfo.augmentationPriceMult)}?
</>);
} }
//Returns a boolean indicating whether the player has the prerequisites for the //Returns a boolean indicating whether the player has the prerequisites for the
@@ -231,15 +240,24 @@ export function getNextNeurofluxLevel() {
} }
export function processPassiveFactionRepGain(numCycles) { export function processPassiveFactionRepGain(numCycles) {
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult; for (const name in Factions) {
for (var name in Factions) { if (name === Player.currentWorkFactionName) continue;
if (Factions.hasOwnProperty(name)) { if (!Factions.hasOwnProperty(name)) continue;
var faction = Factions[name]; const faction = Factions[name];
if (!faction.isMember) continue;
// 0 favor = 1%/s
// 50 favor = 6%/s
// 100 favor = 11%/s
const favorMult = Math.min(0.1, (faction.favor / 1000) + 0.01);
// Find the best of all possible favor gain, minimum 1 rep / 2 minute.
const hRep = getHackingWorkRepGain(Player, faction);
const sRep = getFactionSecurityWorkRepGain(Player, faction);
const fRep = getFactionFieldWorkRepGain(Player, faction);
const rate = Math.max(hRep * favorMult, sRep * favorMult, fRep * favorMult, 1/120);
//TODO Get hard value of 1 rep per "rep gain cycle"" for now.. faction.playerReputation += rate *
//maybe later make this based on (numCycles) *
//a player's 'status' like how powerful they are and how much money they have Player.faction_rep_mult *
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);} BitNodeMultipliers.FactionPassiveRepGain;
} }
}
} }

View File

@@ -8,6 +8,8 @@ import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
@@ -21,7 +23,7 @@ type IProps = {
type IState = { type IState = {
donateAmt: number; donateAmt: number;
statusTxt: string; status: JSX.Element;
} }
const inputStyleMarkup = { const inputStyleMarkup = {
@@ -37,7 +39,7 @@ export class DonateOption extends React.Component<IProps, IState> {
this.state = { this.state = {
donateAmt: 0, donateAmt: 0,
statusTxt: "", status: <></>,
} }
this.calculateRepGain = this.calculateRepGain.bind(this); this.calculateRepGain = this.calculateRepGain.bind(this);
@@ -61,8 +63,9 @@ export class DonateOption extends React.Component<IProps, IState> {
this.props.p.loseMoney(amt); this.props.p.loseMoney(amt);
const repGain = this.calculateRepGain(amt); const repGain = this.calculateRepGain(amt);
this.props.faction.playerReputation += repGain; this.props.faction.playerReputation += repGain;
dialogBoxCreate(`You just donated ${numeralWrapper.formatMoney(amt)} to ${fac.name} to gain ` + dialogBoxCreate(<>
`${numeralWrapper.format(repGain, "0,0.000")} reputation`); You just donated {Money(amt)} to {fac.name} to gain {Reputation(repGain)} reputation
</>);
this.props.rerender(); this.props.rerender();
} }
} }
@@ -73,13 +76,13 @@ export class DonateOption extends React.Component<IProps, IState> {
if (isNaN(amt)) { if (isNaN(amt)) {
this.setState({ this.setState({
donateAmt: 0, donateAmt: 0,
statusTxt: "Invalid donate amount entered!", status: <>Invalid donate amount entered!</>,
}); });
} else { } else {
const repGain = this.calculateRepGain(amt); const repGain = this.calculateRepGain(amt);
this.setState({ this.setState({
donateAmt: amt, donateAmt: amt,
statusTxt: `This donation will result in ${numeralWrapper.format(repGain, "0,0.000")} reputation gain`, status: <>This donation will result in {Reputation(repGain)} reputation gain</>,
}); });
} }
} }
@@ -93,7 +96,7 @@ export class DonateOption extends React.Component<IProps, IState> {
onClick={this.donate} onClick={this.donate}
text={"Donate Money"} text={"Donate Money"}
/> />
<p style={this.blockStyle}>{this.state.statusTxt}</p> <p style={this.blockStyle}>{this.state.status}</p>
</div> </div>
</div> </div>
) )

View File

@@ -10,6 +10,8 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingParagraph } from "../../ui/React/AutoupdatingParagraph"; import { AutoupdatingParagraph } from "../../ui/React/AutoupdatingParagraph";
import { ParagraphWithTooltip } from "../../ui/React/ParagraphWithTooltip"; import { ParagraphWithTooltip } from "../../ui/React/ParagraphWithTooltip";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
type IProps = { type IProps = {
faction: Faction; faction: Faction;
@@ -33,18 +35,17 @@ export class Info extends React.Component<IProps, any> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
this.getFavorGainText = this.getFavorGainText.bind(this); this.getFavorGainContent = this.getFavorGainContent.bind(this);
this.getReputationText = this.getReputationText.bind(this); this.getReputationContent = this.getReputationContent.bind(this);
} }
getFavorGainText(): string { getFavorGainContent(): JSX.Element {
const favorGain = this.props.faction.getFavorGain()[0]; const favorGain = this.props.faction.getFavorGain()[0];
return `You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation` return <>You will earn {Favor(favorGain)} faction favor upon resetting after installing an Augmentation</>
} }
getReputationText(): string { getReputationContent(): JSX.Element {
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a"); return <>Reputation: {Reputation(this.props.faction.playerReputation)}</>
return `Reputation: ${formattedRep}`
} }
render() { render() {
@@ -65,12 +66,12 @@ export class Info extends React.Component<IProps, any> {
<p style={blockStyleMarkup}>-------------------------</p> <p style={blockStyleMarkup}>-------------------------</p>
<AutoupdatingParagraph <AutoupdatingParagraph
intervalTime={5e3} intervalTime={5e3}
getText={this.getReputationText} getContent={this.getReputationContent}
getTooltip={this.getFavorGainText} getTooltip={this.getFavorGainContent}
/> />
<p style={blockStyleMarkup}>-------------------------</p> <p style={blockStyleMarkup}>-------------------------</p>
<ParagraphWithTooltip <ParagraphWithTooltip
text={`Faction Favor: ${numeralWrapper.format(this.props.faction.favor, "0,0")}`} content={<>Faction Favor: {Favor(this.props.faction.favor)}</>}
tooltip={favorTooltip} tooltip={favorTooltip}
/> />
<p style={blockStyleMarkup}>-------------------------</p> <p style={blockStyleMarkup}>-------------------------</p>

View File

@@ -18,9 +18,12 @@ import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { IMap } from "../../types"; import { IMap } from "../../types";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
type IProps = { type IProps = {
augName: string; augName: string;
@@ -106,19 +109,19 @@ export class PurchaseableAugmentation extends React.Component<IProps, any> {
// Determine UI properties // Determine UI properties
let disabled: boolean = false; let disabled: boolean = false;
let statusTxt: string = ""; let status: JSX.Element = <></>;
let color: string = ""; let color: string = "";
if (!this.hasPrereqs()) { if (!this.hasPrereqs()) {
disabled = true; disabled = true;
statusTxt = `LOCKED (Requires ${this.aug.prereqs.join(",")} as prerequisite(s))`; status = <>LOCKED (Requires {this.aug.prereqs.map(aug => AugFormat(aug))} as prerequisite)</>;
color = "red"; color = "red";
} else if (this.aug.name !== AugmentationNames.NeuroFluxGovernor && (this.aug.owned || this.owned())) { } else if (this.aug.name !== AugmentationNames.NeuroFluxGovernor && (this.aug.owned || this.owned())) {
disabled = true; disabled = true;
} else if (this.hasReputation()) { } else if (this.hasReputation()) {
statusTxt = `UNLOCKED - ${numeralWrapper.formatMoney(moneyCost)}`; status = <>UNLOCKED - {Money(moneyCost)}</>;
} else { } else {
disabled = true; disabled = true;
statusTxt = `LOCKED (Requires ${numeralWrapper.format(repCost, "0.000a")} faction reputation - ${numeralWrapper.formatMoney(moneyCost)})`; status = <>LOCKED (Requires {Reputation(repCost)} faction reputation - {Money(moneyCost)})</>;
color = "red"; color = "red";
} }
@@ -143,7 +146,7 @@ export class PurchaseableAugmentation extends React.Component<IProps, any> {
text={btnTxt} text={btnTxt}
tooltip={this.aug.info} tooltip={this.aug.info}
/> />
<p style={txtStyle}>{statusTxt}</p> <p style={txtStyle}>{status}</p>
</span> </span>
</li> </li>
) )

View File

@@ -35,6 +35,14 @@ import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { StatsTable } from "./ui/React/StatsTable";
import { Money } from "./ui/React/Money";
import { MoneyRate } from "./ui/React/MoneyRate";
import { Reputation } from "./ui/React/Reputation";
import React from "react";
import ReactDOM from "react-dom";
import { renderToStaticMarkup } from "react-dom/server"
// Constants // Constants
const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain
@@ -492,15 +500,15 @@ Gang.prototype.ascendMember = function(memberObj, workerScript) {
if (workerScript == null) { if (workerScript == null) {
dialogBoxCreate([`You ascended ${memberObj.name}!`, dialogBoxCreate([`You ascended ${memberObj.name}!`,
"", "",
`Your gang lost ${numeralWrapper.format(res.respect, "0.000a")} respect`, `Your gang lost ${numeralWrapper.formatRespect(res.respect)} respect`,
"", "",
`${memberObj.name} gained the following stat multipliers for ascending:`, `${memberObj.name} gained the following stat multipliers for ascending:`,
`Hacking: ${numeralWrapper.format(res.hack, "0.000%")}`, `Hacking: ${numeralWrapper.formatPercentage(res.hack, 3)}`,
`Strength: ${numeralWrapper.format(res.str, "0.000%")}`, `Strength: ${numeralWrapper.formatPercentage(res.str, 3)}`,
`Defense: ${numeralWrapper.format(res.def, "0.000%")}`, `Defense: ${numeralWrapper.formatPercentage(res.def, 3)}`,
`Dexterity: ${numeralWrapper.format(res.dex, "0.000%")}`, `Dexterity: ${numeralWrapper.formatPercentage(res.dex, 3)}`,
`Agility: ${numeralWrapper.format(res.agi, "0.000%")}`, `Agility: ${numeralWrapper.formatPercentage(res.agi, 3)}`,
`Charisma: ${numeralWrapper.format(res.cha, "0.000%")}`].join("<br>")); `Charisma: ${numeralWrapper.formatPercentage(res.cha, 3)}`].join("<br>"));
} else { } else {
workerScript.log(`Ascended Gang member ${memberObj.name}`); workerScript.log(`Ascended Gang member ${memberObj.name}`);
} }
@@ -1044,14 +1052,14 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
type:"text", placeholder:"Filter gang members", type:"text", placeholder:"Filter gang members",
class: "text-input", class: "text-input",
value:initialFilter, value:initialFilter,
onkeyup:()=>{ onkeyup:() => {
var filterValue = UIElems.gangMemberUpgradeBoxFilter.value.toString(); var filterValue = UIElems.gangMemberUpgradeBoxFilter.value.toString();
this.createGangMemberUpgradeBox(player, filterValue); this.createGangMemberUpgradeBox(player, filterValue);
} }
}); });
UIElems.gangMemberUpgradeBoxDiscount = createElement("p", { UIElems.gangMemberUpgradeBoxDiscount = createElement("p", {
innerText: "Discount: -" + numeralWrapper.format(1 - 1 / this.getDiscount(), "0.00%"), innerText: "Discount: -" + numeralWrapper.formatPercentage(1 - 1 / this.getDiscount()),
marginLeft: "6px", marginLeft: "6px",
tooltip: "You get a discount on equipment and upgrades based on your gang's " + tooltip: "You get a discount on equipment and upgrades based on your gang's " +
"respect and power. More respect and power leads to more discounts." "respect and power. More respect and power leads to more discounts."
@@ -1178,10 +1186,10 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
let upg = upgradeArray[j]; let upg = upgradeArray[j];
(function (upg, div, memberObj, i, gang) { (function (upg, div, memberObj, i, gang) {
let createElementParams = { let createElementParams = {
innerText: upg.name + " - " + numeralWrapper.format(upg.getCost(gang), "$0.000a"), innerHTML: `${upg.name} - ${renderToStaticMarkup(Money(upg.getCost(gang)))}`,
class: "a-link-button", margin:"2px", padding:"2px", display:"block", class: "a-link-button", margin:"2px", padding:"2px", display:"block",
fontSize:"11px", fontSize:"11px",
clickListener:()=>{ clickListener:() => {
memberObj.buyUpgrade(upg, player, gangObj); memberObj.buyUpgrade(upg, player, gangObj);
return false; return false;
} }
@@ -1266,7 +1274,7 @@ Gang.prototype.displayGangContent = function(player) {
// Back button // Back button
UIElems.gangContainer.appendChild(createElement("a", { UIElems.gangContainer.appendChild(createElement("a", {
class:"a-link-button", display:"inline-block", innerText:"Back", class:"a-link-button", display:"inline-block", innerText:"Back",
clickListener:()=>{ clickListener:() => {
Engine.loadFactionContent(); Engine.loadFactionContent();
displayFactionContent(facName); displayFactionContent(facName);
return false; return false;
@@ -1277,7 +1285,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.managementButton = createElement("a", { UIElems.managementButton = createElement("a", {
id:"gang-management-subpage-button", class:"a-link-button-inactive", id:"gang-management-subpage-button", class:"a-link-button-inactive",
display:"inline-block", innerHTML: "Gang Management (Alt+1)", display:"inline-block", innerHTML: "Gang Management (Alt+1)",
clickListener:()=>{ clickListener:() => {
UIElems.gangManagementSubpage.style.display = "block"; UIElems.gangManagementSubpage.style.display = "block";
UIElems.gangTerritorySubpage.style.display = "none"; UIElems.gangTerritorySubpage.style.display = "none";
UIElems.managementButton.classList.toggle("a-link-button-inactive"); UIElems.managementButton.classList.toggle("a-link-button-inactive");
@@ -1340,7 +1348,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangRecruitMemberButton = createElement("a", { UIElems.gangRecruitMemberButton = createElement("a", {
id: "gang-management-recruit-member-btn", class:"a-link-button-inactive", id: "gang-management-recruit-member-btn", class:"a-link-button-inactive",
innerHTML:"Recruit Gang Member", display:"inline-block", margin:"10px", innerHTML:"Recruit Gang Member", display:"inline-block", margin:"10px",
clickListener:()=>{ clickListener:() => {
const popupId = "recruit-gang-member-popup"; const popupId = "recruit-gang-member-popup";
let yesBtn; let yesBtn;
@@ -1408,7 +1416,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangExpandAllButton = createElement("a", { UIElems.gangExpandAllButton = createElement("a", {
class:"a-link-button", display:"inline-block", class:"a-link-button", display:"inline-block",
innerHTML:"Expand All", innerHTML:"Expand All",
clickListener:()=>{ clickListener:() => {
var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header"); var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) { for (var i = 0; i < allHeaders.length; ++i) {
var hdr = allHeaders[i]; var hdr = allHeaders[i];
@@ -1422,7 +1430,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangCollapseAllButton = createElement("a", { UIElems.gangCollapseAllButton = createElement("a", {
class:"a-link-button", display:"inline-block", class:"a-link-button", display:"inline-block",
innerHTML:"Collapse All", innerHTML:"Collapse All",
clickListener:()=>{ clickListener:() => {
var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header"); var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) { for (var i = 0; i < allHeaders.length; ++i) {
var hdr = allHeaders[i]; var hdr = allHeaders[i];
@@ -1436,7 +1444,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangMemberFilter = createElement("input", { UIElems.gangMemberFilter = createElement("input", {
type:"text", placeholder:"Filter gang members", margin:"5px", padding:"5px", type:"text", placeholder:"Filter gang members", margin:"5px", padding:"5px",
class:"text-input", class:"text-input",
onkeyup:()=>{ onkeyup:() => {
this.displayGangMemberList(); this.displayGangMemberList();
} }
}); });
@@ -1584,13 +1592,13 @@ Gang.prototype.updateGangContent = function() {
if (UIElems.gangMemberUpgradeBoxOpened) { if (UIElems.gangMemberUpgradeBoxOpened) {
UIElems.gangMemberUpgradeBoxDiscount.childNodes[0].nodeValue = UIElems.gangMemberUpgradeBoxDiscount.childNodes[0].nodeValue =
"Discount: -" + numeralWrapper.format(1 - 1 / this.getDiscount(), "0.00%"); "Discount: -" + numeralWrapper.formatPercentage(1 - 1 / this.getDiscount());
} }
if (UIElems.gangTerritorySubpage.style.display === "block") { if (UIElems.gangTerritorySubpage.style.display === "block") {
// Territory Warfare Clash Chance // Territory Warfare Clash Chance
UIElems.gangTerritoryWarfareClashChance.innerText = UIElems.gangTerritoryWarfareClashChance.innerText =
`Territory Clash Chance: ${numeralWrapper.format(this.territoryClashChance, '0.000%')}`; `Territory Clash Chance: ${numeralWrapper.formatPercentage(this.territoryClashChance, 3)}`;
// Engaged in Territory Warfare checkbox // Engaged in Territory Warfare checkbox
UIElems.gangTerritoryWarfareCheckbox.checked = this.territoryWarfareEngaged; UIElems.gangTerritoryWarfareCheckbox.checked = this.territoryWarfareEngaged;
@@ -1623,7 +1631,7 @@ Gang.prototype.updateGangContent = function() {
const clashVictoryChance = playerPower / (gangTerritoryInfo.power + playerPower); const clashVictoryChance = playerPower / (gangTerritoryInfo.power + playerPower);
let newHTML = `<u>${gangname}</u><br>Power: ${formatNumber(gangTerritoryInfo.power, 6)}<br>`; let newHTML = `<u>${gangname}</u><br>Power: ${formatNumber(gangTerritoryInfo.power, 6)}<br>`;
newHTML += `Territory: ${displayNumber}%<br>`; newHTML += `Territory: ${displayNumber}%<br>`;
newHTML += `Chance to win clash with this gang: ${numeralWrapper.format(clashVictoryChance, "0.000%")}<br><br>`; newHTML += `Chance to win clash with this gang: ${numeralWrapper.formatPercentage(clashVictoryChance, 3)}<br><br>`;
UIElems.gangTerritoryInfoText.innerHTML += newHTML; UIElems.gangTerritoryInfoText.innerHTML += newHTML;
} }
} }
@@ -1641,8 +1649,8 @@ Gang.prototype.updateGangContent = function() {
removeChildrenFromElement(UIElems.gangInfo); removeChildrenFromElement(UIElems.gangInfo);
UIElems.gangInfo.appendChild(createElement("p", { // Respect UIElems.gangInfo.appendChild(createElement("p", { // Respect
display: "inline-block", display: "inline-block",
innerText: "Respect: " + numeralWrapper.format(this.respect, '0.00000a') + innerText: "Respect: " + numeralWrapper.formatRespect(this.respect) +
" (" + numeralWrapper.format(5*this.respectGainRate, '0.00000a') + " / sec)", " (" + numeralWrapper.formatRespect(5*this.respectGainRate) + " / sec)",
tooltip: "Represents the amount of respect your gang has from other gangs and criminal " + tooltip: "Represents the amount of respect your gang has from other gangs and criminal " +
"organizations. Your respect affects the amount of money " + "organizations. Your respect affects the amount of money " +
"your gang members will earn, and also determines how much " + "your gang members will earn, and also determines how much " +
@@ -1652,8 +1660,8 @@ Gang.prototype.updateGangContent = function() {
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
display: "inline-block", display: "inline-block",
innerText: "Wanted Level: " + numeralWrapper.format(this.wanted, '0.00000a') + innerText: "Wanted Level: " + numeralWrapper.formatWanted(this.wanted) +
" (" + numeralWrapper.format(5*this.wantedGainRate, '0.00000a') + " / sec)", " (" + numeralWrapper.formatWanted(5*this.wantedGainRate) + " / sec)",
tooltip: "Represents how much the gang is wanted by law enforcement. The higher " + tooltip: "Represents how much the gang is wanted by law enforcement. The higher " +
"your gang's wanted level, the harder it will be for your gang members " + "your gang's wanted level, the harder it will be for your gang members " +
"to make money and earn respect. Note that the minimum wanted level is 1." "to make money and earn respect. Note that the minimum wanted level is 1."
@@ -1669,10 +1677,9 @@ Gang.prototype.updateGangContent = function() {
})); }));
UIElems.gangInfo.appendChild(createElement("br")); UIElems.gangInfo.appendChild(createElement("br"));
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate const d0 = createElement("div");
display: "inline-block", ReactDOM.render(<p style={{'display': 'inline-block'}}>Money gain rate: {MoneyRate(5 * this.moneyGainRate)}</p>, d0);
innerText: `Money gain rate: ${numeralWrapper.format(5 * this.moneyGainRate, "$0.000a")} / sec`, UIElems.gangInfo.appendChild(d0);
}));
UIElems.gangInfo.appendChild(createElement("br")); UIElems.gangInfo.appendChild(createElement("br"));
// Fix some rounding issues graphically // Fix some rounding issues graphically
@@ -1692,10 +1699,9 @@ Gang.prototype.updateGangContent = function() {
})); }));
UIElems.gangInfo.appendChild(createElement("br")); UIElems.gangInfo.appendChild(createElement("br"));
UIElems.gangInfo.appendChild(createElement("p", { // Faction reputation const d1 = createElement("div");
display:"inline-block", ReactDOM.render(<p style={{'display': 'inline-block'}}>Faction reputation: {Reputation(rep)}</p>, d1);
innerText:"Faction reputation: " + numeralWrapper.format(rep, '0.000a') UIElems.gangInfo.appendChild(d1);
}));
UIElems.gangInfo.appendChild(createElement("br")); UIElems.gangInfo.appendChild(createElement("br"));
const CyclesPerSecond = 1000 / Engine._idleSpeed; const CyclesPerSecond = 1000 / Engine._idleSpeed;
@@ -1761,12 +1767,12 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
const statsDiv = createElement("div", { const statsDiv = createElement("div", {
class: "gang-member-info-div", class: "gang-member-info-div",
id: name + "gang-member-stats", id: name + "gang-member-stats",
tooltipsmall: [`Hk: x${numeralWrapper.format(memberObj.hack_mult * memberObj.hack_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.hack_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.hack_asc_mult, "0,0.00")} Asc)`, tooltipsmall: [`Hk: x${numeralWrapper.formatMultiplier(memberObj.hack_mult * memberObj.hack_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.hack_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.hack_asc_mult)} Asc)`,
`St: x${numeralWrapper.format(memberObj.str_mult * memberObj.str_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.str_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.str_asc_mult, "0,0.00")} Asc)`, `St: x${numeralWrapper.formatMultiplier(memberObj.str_mult * memberObj.str_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.str_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.str_asc_mult)} Asc)`,
`Df: x${numeralWrapper.format(memberObj.def_mult * memberObj.def_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.def_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.def_asc_mult, "0,0.00")} Asc)`, `Df: x${numeralWrapper.formatMultiplier(memberObj.def_mult * memberObj.def_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.def_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.def_asc_mult)} Asc)`,
`Dx: x${numeralWrapper.format(memberObj.dex_mult * memberObj.dex_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.dex_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.dex_asc_mult, "0,0.00")} Asc)`, `Dx: x${numeralWrapper.formatMultiplier(memberObj.dex_mult * memberObj.dex_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.dex_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.dex_asc_mult)} Asc)`,
`Ag: x${numeralWrapper.format(memberObj.agi_mult * memberObj.agi_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.agi_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.agi_asc_mult, "0,0.00")} Asc)`, `Ag: x${numeralWrapper.formatMultiplier(memberObj.agi_mult * memberObj.agi_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.agi_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.agi_asc_mult)} Asc)`,
`Ch: x${numeralWrapper.format(memberObj.cha_mult * memberObj.cha_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.cha_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.cha_asc_mult, "0,0.00")} Asc)`].join("<br>"), `Ch: x${numeralWrapper.formatMultiplier(memberObj.cha_mult * memberObj.cha_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.cha_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.cha_asc_mult)} Asc)`].join("<br>"),
}); });
UIElems.gangMemberPanels[name]["statsDiv"] = statsDiv; UIElems.gangMemberPanels[name]["statsDiv"] = statsDiv;
const statsP = createElement("pre", { const statsP = createElement("pre", {
@@ -1784,15 +1790,15 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
innerText: ["Are you sure you want to ascend this member? They will lose all of", innerText: ["Are you sure you want to ascend this member? They will lose all of",
"their non-Augmentation upgrades and their stats will reset back to 1.", "their non-Augmentation upgrades and their stats will reset back to 1.",
"", "",
`Furthermore, your gang will lose ${numeralWrapper.format(memberObj.earnedRespect, "0.000a")} respect`, `Furthermore, your gang will lose ${numeralWrapper.formatRespect(memberObj.earnedRespect)} respect`,
"", "",
"In return, they will gain the following permanent boost to stat multipliers:\n", "In return, they will gain the following permanent boost to stat multipliers:\n",
`Hacking: +${numeralWrapper.format(ascendBenefits.hack, "0.00%")}`, `Hacking: +${numeralWrapper.formatPercentage(ascendBenefits.hack)}`,
`Strength: +${numeralWrapper.format(ascendBenefits.str, "0.00%")}`, `Strength: +${numeralWrapper.formatPercentage(ascendBenefits.str)}`,
`Defense: +${numeralWrapper.format(ascendBenefits.def, "0.00%")}`, `Defense: +${numeralWrapper.formatPercentage(ascendBenefits.def)}`,
`Dexterity: +${numeralWrapper.format(ascendBenefits.dex, "0.00%")}`, `Dexterity: +${numeralWrapper.formatPercentage(ascendBenefits.dex)}`,
`Agility: +${numeralWrapper.format(ascendBenefits.agi, "0.00%")}`, `Agility: +${numeralWrapper.formatPercentage(ascendBenefits.agi)}`,
`Charisma: +${numeralWrapper.format(ascendBenefits.cha, "0.00%")}`].join("\n"), `Charisma: +${numeralWrapper.formatPercentage(ascendBenefits.cha)}`].join("\n"),
}); });
const confirmBtn = createElement("button", { const confirmBtn = createElement("button", {
class: "std-button", class: "std-button",
@@ -1874,7 +1880,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
taskSelector.selectedIndex = taskIndex; taskSelector.selectedIndex = taskIndex;
} }
var gainInfo = createElement("p", {id:name + "gang-member-gain-info"}); var gainInfo = createElement("div", {id:name + "gang-member-gain-info"});
taskDiv.appendChild(taskSelector); taskDiv.appendChild(taskSelector);
taskDiv.appendChild(gainInfo); taskDiv.appendChild(gainInfo);
@@ -1907,12 +1913,12 @@ Gang.prototype.updateGangMemberDisplayElement = function(memberObj) {
var stats = document.getElementById(name + "gang-member-stats-text"); var stats = document.getElementById(name + "gang-member-stats-text");
if (stats) { if (stats) {
stats.innerText = stats.innerText =
[`Hacking: ${formatNumber(memberObj.hack, 0)} (${numeralWrapper.format(memberObj.hack_exp, '(0.00a)')} exp)`, [`Hacking: ${formatNumber(memberObj.hack, 0)} (${numeralWrapper.formatExp(memberObj.hack_exp)} exp)`,
`Strength: ${formatNumber(memberObj.str, 0)} (${numeralWrapper.format(memberObj.str_exp, '(0.00a)')} exp)`, `Strength: ${formatNumber(memberObj.str, 0)} (${numeralWrapper.formatExp(memberObj.str_exp)} exp)`,
`Defense: ${formatNumber(memberObj.def, 0)} (${numeralWrapper.format(memberObj.def_exp, '(0.00a)')} exp)`, `Defense: ${formatNumber(memberObj.def, 0)} (${numeralWrapper.formatExp(memberObj.def_exp)} exp)`,
`Dexterity: ${formatNumber(memberObj.dex, 0)} (${numeralWrapper.format(memberObj.dex_exp, '(0.00a)')} exp)`, `Dexterity: ${formatNumber(memberObj.dex, 0)} (${numeralWrapper.formatExp(memberObj.dex_exp)} exp)`,
`Agility: ${formatNumber(memberObj.agi, 0)} (${numeralWrapper.format(memberObj.agi_exp, '(0.00a)')} exp)`, `Agility: ${formatNumber(memberObj.agi, 0)} (${numeralWrapper.formatExp(memberObj.agi_exp)} exp)`,
`Charisma: ${formatNumber(memberObj.cha, 0)} (${numeralWrapper.format(memberObj.cha_exp, '(0.00a)')} exp)`].join("\n"); `Charisma: ${formatNumber(memberObj.cha, 0)} (${numeralWrapper.formatExp(memberObj.cha_exp)} exp)`].join("\n");
} }
// Update tooltip for stat multipliers // Update tooltip for stat multipliers
@@ -1921,23 +1927,25 @@ Gang.prototype.updateGangMemberDisplayElement = function(memberObj) {
const statsDiv = panel["statsDiv"]; const statsDiv = panel["statsDiv"];
if (statsDiv) { if (statsDiv) {
statsDiv.firstChild.innerHTML = statsDiv.firstChild.innerHTML =
[`Hk: x${numeralWrapper.format(memberObj.hack_mult * memberObj.hack_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.hack_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.hack_asc_mult, "0,0.00")} Asc)`, [`Hk: x${numeralWrapper.formatMultiplier(memberObj.hack_mult * memberObj.hack_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.hack_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.hack_asc_mult)} Asc)`,
`St: x${numeralWrapper.format(memberObj.str_mult * memberObj.str_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.str_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.str_asc_mult, "0,0.00")} Asc)`, `St: x${numeralWrapper.formatMultiplier(memberObj.str_mult * memberObj.str_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.str_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.str_asc_mult)} Asc)`,
`Df: x${numeralWrapper.format(memberObj.def_mult * memberObj.def_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.def_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.def_asc_mult, "0,0.00")} Asc)`, `Df: x${numeralWrapper.formatMultiplier(memberObj.def_mult * memberObj.def_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.def_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.def_asc_mult)} Asc)`,
`Dx: x${numeralWrapper.format(memberObj.dex_mult * memberObj.dex_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.dex_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.dex_asc_mult, "0,0.00")} Asc)`, `Dx: x${numeralWrapper.formatMultiplier(memberObj.dex_mult * memberObj.dex_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.dex_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.dex_asc_mult)} Asc)`,
`Ag: x${numeralWrapper.format(memberObj.agi_mult * memberObj.agi_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.agi_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.agi_asc_mult, "0,0.00")} Asc)`, `Ag: x${numeralWrapper.formatMultiplier(memberObj.agi_mult * memberObj.agi_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.agi_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.agi_asc_mult)} Asc)`,
`Ch: x${numeralWrapper.format(memberObj.cha_mult * memberObj.cha_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.cha_mult, "0,0.00")} Eq, x${numeralWrapper.format(memberObj.cha_asc_mult, "0,0.00")} Asc)`].join("<br>"); `Ch: x${numeralWrapper.formatMultiplier(memberObj.cha_mult * memberObj.cha_asc_mult)}(x${numeralWrapper.formatMultiplier(memberObj.cha_mult)} Eq, x${numeralWrapper.formatMultiplier(memberObj.cha_asc_mult)} Asc)`].join("<br>");
} }
} }
// Update info about gang member's earnings/gains // Update info about gang member's earnings/gains
var gainInfo = document.getElementById(name + "gang-member-gain-info"); var gainInfo = document.getElementById(name + "gang-member-gain-info");
if (gainInfo) { if (gainInfo) {
gainInfo.innerHTML = const data = [
[`Money: ${numeralWrapper.format(5*memberObj.calculateMoneyGain(this), '$0.000a')} / sec`, [`Money:`, MoneyRate(5*memberObj.calculateMoneyGain(this))],
`Respect: ${numeralWrapper.format(5*memberObj.calculateRespectGain(this), '0.00000a')} / sec`, [`Respect:`, `${numeralWrapper.formatRespect(5*memberObj.calculateRespectGain(this))} / sec`],
`Wanted Level: ${numeralWrapper.format(5*memberObj.calculateWantedLevelGain(this), '0.00000a')} / sec`, [`Wanted Level:`, `${numeralWrapper.formatWanted(5*memberObj.calculateWantedLevelGain(this))} / sec`],
`Total Respect Earned: ${numeralWrapper.format(memberObj.earnedRespect, '0.00000a')}`].join("<br>"); [`Total Respect:`, `${numeralWrapper.formatRespect(memberObj.earnedRespect)}`],
];
ReactDOM.render(StatsTable(data), gainInfo);
} }
// Update selector to have the correct task // Update selector to have the correct task

View File

@@ -1,17 +1,19 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { Player } from "./Player"; import { Player } from "./Player";
import { IPlayer } from "./PersonObjects/IPlayer";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
import { Server } from "./Server/Server"; import { Server } from "./Server/Server";
import { HacknetServer } from "./Hacknet/HacknetServer"; import { HacknetServer } from "./Hacknet/HacknetServer";
/** /**
* Returns the chance the player has to successfully hack a server * Returns the chance the player has to successfully hack a server
*/ */
export function calculateHackingChance(server: Server): number { export function calculateHackingChance(server: Server, player: IPlayer): number {
const hackFactor = 1.75; const hackFactor = 1.75;
const difficultyMult = (100 - server.hackDifficulty) / 100; const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = hackFactor * Player.hacking_skill; const skillMult = hackFactor * player.hacking_skill;
const skillChance = (skillMult - server.requiredHackingSkill) / skillMult; const skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
const chance = skillChance * difficultyMult * Player.hacking_chance_mult * Player.getIntelligenceBonus(1); const chance = skillChance * difficultyMult * player.hacking_chance_mult * calculateIntelligenceBonus(player.intelligence, 1);
if (chance > 1) { return 1; } if (chance > 1) { return 1; }
if (chance < 0) { return 0; } if (chance < 0) { return 0; }
@@ -22,14 +24,14 @@ export function calculateHackingChance(server: Server): number {
* Returns the amount of hacking experience the player will gain upon * Returns the amount of hacking experience the player will gain upon
* successfully hacking a server * successfully hacking a server
*/ */
export function calculateHackingExpGain(server: Server): number { export function calculateHackingExpGain(server: Server, player: IPlayer): number {
const baseExpGain = 3; const baseExpGain = 3;
const diffFactor = 0.3; const diffFactor = 0.3;
if (server.baseDifficulty == null) { if (server.baseDifficulty == null) {
server.baseDifficulty = server.hackDifficulty; server.baseDifficulty = server.hackDifficulty;
} }
let expGain = baseExpGain; let expGain = baseExpGain;
expGain += (server.baseDifficulty * Player.hacking_exp_mult * diffFactor); expGain += (server.baseDifficulty * player.hacking_exp_mult * diffFactor);
return expGain * BitNodeMultipliers.HackExpGain; return expGain * BitNodeMultipliers.HackExpGain;
} }
@@ -38,13 +40,13 @@ export function calculateHackingExpGain(server: Server): number {
* Returns the percentage of money that will be stolen from a server if * Returns the percentage of money that will be stolen from a server if
* it is successfully hacked (returns the decimal form, not the actual percent value) * it is successfully hacked (returns the decimal form, not the actual percent value)
*/ */
export function calculatePercentMoneyHacked(server: Server): number { export function calculatePercentMoneyHacked(server: Server, player: IPlayer): number {
// Adjust if needed for balancing. This is the divisor for the final calculation // Adjust if needed for balancing. This is the divisor for the final calculation
const balanceFactor = 240; const balanceFactor = 240;
const difficultyMult = (100 - server.hackDifficulty) / 100; const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = (Player.hacking_skill - (server.requiredHackingSkill - 1)) / Player.hacking_skill; const skillMult = (player.hacking_skill - (server.requiredHackingSkill - 1)) / player.hacking_skill;
const percentMoneyHacked = difficultyMult * skillMult * Player.hacking_money_mult / balanceFactor; const percentMoneyHacked = difficultyMult * skillMult * player.hacking_money_mult / balanceFactor;
if (percentMoneyHacked < 0) { return 0; } if (percentMoneyHacked < 0) { return 0; }
if (percentMoneyHacked > 1) { return 1; } if (percentMoneyHacked > 1) { return 1; }
@@ -54,19 +56,18 @@ export function calculatePercentMoneyHacked(server: Server): number {
/** /**
* Returns time it takes to complete a hack on a server, in seconds * Returns time it takes to complete a hack on a server, in seconds
*/ */
export function calculateHackingTime(server: Server, hack: number): number { export function calculateHackingTime(server: Server, player: IPlayer): number {
const difficultyMult = server.requiredHackingSkill * server.hackDifficulty; const difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
const baseDiff = 500; const baseDiff = 500;
const baseSkill = 50; const baseSkill = 50;
const diffFactor = 2.5; const diffFactor = 2.5;
if (hack == null) {hack = Player.hacking_skill;}
let skillFactor = (diffFactor * difficultyMult + baseDiff); let skillFactor = (diffFactor * difficultyMult + baseDiff);
// tslint:disable-next-line // tslint:disable-next-line
skillFactor /= (hack + baseSkill); skillFactor /= (player.hacking_skill + baseSkill);
const hackTimeMultiplier = 5; const hackTimeMultiplier = 5;
const hackingTime = hackTimeMultiplier * skillFactor / (Player.hacking_speed_mult * Player.getIntelligenceBonus(1)); const hackingTime = hackTimeMultiplier * skillFactor / (player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1));
return hackingTime; return hackingTime;
} }
@@ -74,17 +75,17 @@ export function calculateHackingTime(server: Server, hack: number): number {
/** /**
* Returns time it takes to complete a grow operation on a server, in seconds * Returns time it takes to complete a grow operation on a server, in seconds
*/ */
export function calculateGrowTime(server: Server, hack: number): number { export function calculateGrowTime(server: Server, player: IPlayer): number {
const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2 const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * calculateHackingTime(server, hack); return growTimeMultiplier * calculateHackingTime(server, player);
} }
/** /**
* Returns time it takes to complete a weaken operation on a server, in seconds * Returns time it takes to complete a weaken operation on a server, in seconds
*/ */
export function calculateWeakenTime(server: Server, hack: number): number { export function calculateWeakenTime(server: Server, player: IPlayer): number {
const weakenTimeMultiplier = 4; // Relative to hacking time const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * calculateHackingTime(server, hack); return weakenTimeMultiplier * calculateHackingTime(server, player);
} }

View File

@@ -1,4 +0,0 @@
/**
* Utility functions for calculating the maximum number of Hacknet upgrades the player
* can purchase for a Node with his/her current money
*/

View File

@@ -8,24 +8,11 @@
* *
* TODO Should probably split the different types of functions into their own modules * TODO Should probably split the different types of functions into their own modules
*/ */
import { import { HacknetNode } from "./HacknetNode";
HacknetNode, import { calculateNodeCost } from "./formulas/HacknetNodes";
BaseCostForHacknetNode, import { calculateServerCost } from "./formulas/HacknetServers";
HacknetNodePurchaseNextMult, import { HacknetNodeConstants, HacknetServerConstants } from "./data/Constants";
HacknetNodeMaxLevel, import { HacknetServer } from "./HacknetServer";
HacknetNodeMaxRam,
HacknetNodeMaxCores
} from "./HacknetNode";
import {
HacknetServer,
BaseCostForHacknetServer,
HacknetServerPurchaseMult,
HacknetServerMaxLevel,
HacknetServerMaxRam,
HacknetServerMaxCores,
HacknetServerMaxCache,
MaxNumberHacknetServers
} from "./HacknetServer";
import { HashManager } from "./HashManager"; import { HashManager } from "./HashManager";
import { HashUpgrades } from "./HashUpgrades"; import { HashUpgrades } from "./HashUpgrades";
@@ -105,24 +92,15 @@ export function purchaseHacknet() {
} }
export function hasMaxNumberHacknetServers() { export function hasMaxNumberHacknetServers() {
return hasHacknetServers() && Player.hacknetNodes.length >= MaxNumberHacknetServers; return hasHacknetServers() && Player.hacknetNodes.length >= HacknetServerConstants.MaxServers;
} }
export function getCostOfNextHacknetNode() { export function getCostOfNextHacknetNode() {
// Cost increases exponentially based on how many you own return calculateNodeCost(Player.hacknetNodes.length+1, Player.hacknet_node_purchase_cost_mult);
const numOwned = Player.hacknetNodes.length;
const mult = HacknetNodePurchaseNextMult;
return BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
} }
export function getCostOfNextHacknetServer() { export function getCostOfNextHacknetServer() {
const numOwned = Player.hacknetNodes.length; return calculateServerCost(Player.hacknetNodes.length+1, Player.hacknet_node_purchase_cost_mult);
const mult = HacknetServerPurchaseMult;
if (numOwned >= MaxNumberHacknetServers) { return Infinity; }
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level
@@ -270,14 +248,14 @@ export function purchaseLevelUpgrade(node, levels=1) {
const isServer = (node instanceof HacknetServer); const isServer = (node instanceof HacknetServer);
// If we're at max level, return false // If we're at max level, return false
if (node.level >= (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) { if (node.level >= (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) {
return false; return false;
} }
// If the number of specified upgrades would exceed the max level, calculate // If the number of specified upgrades would exceed the max level, calculate
// the maximum number of upgrades and use that // the maximum number of upgrades and use that
if (node.level + sanitizedLevels > (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) { if (node.level + sanitizedLevels > (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) {
const diff = Math.max(0, (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel) - node.level); const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel) - node.level);
return purchaseLevelUpgrade(node, diff); return purchaseLevelUpgrade(node, diff);
} }
@@ -301,20 +279,20 @@ export function purchaseRamUpgrade(node, levels=1) {
const isServer = (node instanceof HacknetServer); const isServer = (node instanceof HacknetServer);
// Fail if we're already at max // Fail if we're already at max
if (node.ram >= (isServer ? HacknetServerMaxRam : HacknetNodeMaxRam)) { if (node.ram >= (isServer ? HacknetServerConstants.MaxRam : HacknetNodeConstants.MaxRam)) {
return false; return false;
} }
// If the number of specified upgrades would exceed the max RAM, calculate the // If the number of specified upgrades would exceed the max RAM, calculate the
// max possible number of upgrades and use that // max possible number of upgrades and use that
if (isServer) { if (isServer) {
if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) { if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerConstants.MaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / node.maxRam))); const diff = Math.max(0, Math.log2(Math.round(HacknetServerConstants.MaxRam / node.maxRam)));
return purchaseRamUpgrade(node, diff); return purchaseRamUpgrade(node, diff);
} }
} else { } else {
if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) { if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeConstants.MaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / node.ram))); const diff = Math.max(0, Math.log2(Math.round(HacknetNodeConstants.MaxRam / node.ram)));
return purchaseRamUpgrade(node, diff); return purchaseRamUpgrade(node, diff);
} }
} }
@@ -340,14 +318,14 @@ export function purchaseCoreUpgrade(node, levels=1) {
const isServer = (node instanceof HacknetServer); const isServer = (node instanceof HacknetServer);
// Fail if we're already at max // Fail if we're already at max
if (node.cores >= (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) { if (node.cores >= (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) {
return false; return false;
} }
// If the specified number of upgrades would exceed the max Cores, calculate // If the specified number of upgrades would exceed the max Cores, calculate
// the max possible number of upgrades and use that // the max possible number of upgrades and use that
if (node.cores + sanitizedLevels > (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) { if (node.cores + sanitizedLevels > (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) {
const diff = Math.max(0, (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores) - node.cores); const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores) - node.cores);
return purchaseCoreUpgrade(node, diff); return purchaseCoreUpgrade(node, diff);
} }
@@ -374,8 +352,8 @@ export function purchaseCacheUpgrade(node, levels=1) {
} }
// Fail if we're already at max // Fail if we're already at max
if (node.cache + sanitizedLevels > HacknetServerMaxCache) { if (node.cache + sanitizedLevels > HacknetServerConstants.MaxCache) {
const diff = Math.max(0, HacknetServerMaxCache - node.cache); const diff = Math.max(0, HacknetServerConstants.MaxCache - node.cache);
return purchaseCacheUpgrade(node, diff); return purchaseCacheUpgrade(node, diff);
} }

View File

@@ -9,29 +9,19 @@ import { IHacknetNode } from "./IHacknetNode";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import {
calculateMoneyGainRate,
calculateLevelUpgradeCost,
calculateCoreUpgradeCost,
calculateRamUpgradeCost,
} from "./formulas/HacknetNodes";
import { HacknetNodeConstants } from "./data/Constants";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { Generic_fromJSON, import { Generic_fromJSON,
Generic_toJSON, Generic_toJSON,
Reviver } from "../../utils/JSONReviver"; Reviver } from "../../utils/JSONReviver";
// Constants for Hacknet Node production
export const HacknetNodeMoneyGainPerLevel: number = 1.6; // Base production per level
// Constants for Hacknet Node purchase/upgrade costs
export const BaseCostForHacknetNode: number = 1000;
export const BaseCostFor1GBOfRamHacknetNode: number = 30e3;
export const BaseCostForHacknetNodeCore: number = 500e3;
export const HacknetNodePurchaseNextMult: number = 1.85; // Multiplier when purchasing an additional hacknet node
export const HacknetNodeUpgradeLevelMult: number = 1.04; // Multiplier for cost when upgrading level
export const HacknetNodeUpgradeRamMult: number = 1.28; // Multiplier for cost when upgrading RAM
export const HacknetNodeUpgradeCoreMult: number = 1.48; // Multiplier for cost when buying another core
// Constants for max upgrade levels for Hacknet Nodes
export const HacknetNodeMaxLevel: number = 200;
export const HacknetNodeMaxRam: number = 64;
export const HacknetNodeMaxCores: number = 16;
export class HacknetNode implements IHacknetNode { export class HacknetNode implements IHacknetNode {
/** /**
* Initiatizes a HacknetNode object from a JSON save state. * Initiatizes a HacknetNode object from a JSON save state.
@@ -69,79 +59,17 @@ export class HacknetNode implements IHacknetNode {
// Get the cost to upgrade this Node's number of cores // Get the cost to upgrade this Node's number of cores
calculateCoreUpgradeCost(levels: number=1, costMult: number): number { calculateCoreUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels); return calculateCoreUpgradeCost(this.cores, levels, costMult);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.cores >= HacknetNodeMaxCores) {
return Infinity;
}
const coreBaseCost = BaseCostForHacknetNodeCore;
const mult = HacknetNodeUpgradeCoreMult;
let totalCost = 0;
let currentCores = this.cores;
for (let i = 0; i < sanitizedLevels; ++i) {
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
++currentCores;
}
totalCost *= costMult;
return totalCost;
} }
// Get the cost to upgrade this Node's level // Get the cost to upgrade this Node's level
calculateLevelUpgradeCost(levels: number=1, costMult: number): number { calculateLevelUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels); return calculateLevelUpgradeCost(this.level, levels, costMult);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.level >= HacknetNodeMaxLevel) {
return Infinity;
}
const mult = HacknetNodeUpgradeLevelMult;
let totalMultiplier = 0;
let currLevel = this.level;
for (let i = 0; i < sanitizedLevels; ++i) {
totalMultiplier += Math.pow(mult, currLevel);
++currLevel;
}
return BaseCostForHacknetNode / 2 * totalMultiplier * costMult;
} }
// Get the cost to upgrade this Node's RAM // Get the cost to upgrade this Node's RAM
calculateRamUpgradeCost(levels: number=1, costMult: number): number { calculateRamUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels); return calculateRamUpgradeCost(this.ram, levels, costMult);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.ram >= HacknetNodeMaxRam) {
return Infinity;
}
let totalCost = 0;
let numUpgrades = Math.round(Math.log2(this.ram));
let currentRam = this.ram;
for (let i = 0; i < sanitizedLevels; ++i) {
let baseCost = currentRam * BaseCostFor1GBOfRamHacknetNode;
let mult = Math.pow(HacknetNodeUpgradeRamMult, numUpgrades);
totalCost += (baseCost * mult);
currentRam *= 2;
++numUpgrades;
}
totalCost *= costMult;
return totalCost;
} }
// Process this Hacknet Node in the game loop. // Process this Hacknet Node in the game loop.
@@ -163,14 +91,14 @@ export class HacknetNode implements IHacknetNode {
// Upgrade this Node's number of cores, if possible // Upgrade this Node's number of cores, if possible
// Returns a boolean indicating whether new cores were successfully bought // Returns a boolean indicating whether new cores were successfully bought
upgradeCore(levels: number=1, prodMult: number): void { upgradeCore(levels: number=1, prodMult: number): void {
this.cores = Math.min(HacknetNodeMaxCores, Math.round(this.cores + levels)); this.cores = Math.min(HacknetNodeConstants.MaxCores, Math.round(this.cores + levels));
this.updateMoneyGainRate(prodMult); this.updateMoneyGainRate(prodMult);
} }
// Upgrade this Node's level, if possible // Upgrade this Node's level, if possible
// Returns a boolean indicating whether the level was successfully updated // Returns a boolean indicating whether the level was successfully updated
upgradeLevel(levels: number=1, prodMult: number): void { upgradeLevel(levels: number=1, prodMult: number): void {
this.level = Math.min(HacknetNodeMaxLevel, Math.round(this.level + levels)); this.level = Math.min(HacknetNodeConstants.MaxLevel, Math.round(this.level + levels));
this.updateMoneyGainRate(prodMult); this.updateMoneyGainRate(prodMult);
} }
@@ -186,14 +114,7 @@ export class HacknetNode implements IHacknetNode {
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop // Re-calculate this Node's production and update the moneyGainRatePerSecond prop
updateMoneyGainRate(prodMult: number): void { updateMoneyGainRate(prodMult: number): void {
//How much extra $/s is gained per level this.moneyGainRatePerSecond = calculateMoneyGainRate(this.level, this.ram, this.cores, prodMult);
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
Math.pow(1.035, this.ram - 1) *
((this.cores + 5) / 6) *
prodMult *
BitNodeMultipliers.HacknetNodeMoney;
if (isNaN(this.moneyGainRatePerSecond)) { if (isNaN(this.moneyGainRatePerSecond)) {
this.moneyGainRatePerSecond = 0; this.moneyGainRatePerSecond = 0;
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer", false); dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer", false);

View File

@@ -8,6 +8,14 @@ import { IHacknetNode } from "./IHacknetNode";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { BaseServer } from "../Server/BaseServer"; import { BaseServer } from "../Server/BaseServer";
import { RunningScript } from "../Script/RunningScript"; import { RunningScript } from "../Script/RunningScript";
import { HacknetServerConstants } from "./data/Constants";
import {
calculateHashGainRate,
calculateLevelUpgradeCost,
calculateRamUpgradeCost,
calculateCoreUpgradeCost,
calculateCacheUpgradeCost,
} from "./formulas/HacknetServers";
import { createRandomIp } from "../../utils/IPAddress"; import { createRandomIp } from "../../utils/IPAddress";
@@ -17,29 +25,6 @@ import {
Reviver Reviver
} from "../../utils/JSONReviver"; } from "../../utils/JSONReviver";
// Constants for Hacknet Server stats/production
export const HacknetServerHashesPerLevel: number = 0.001;
// Constants for Hacknet Server purchase/upgrade costs
export const BaseCostForHacknetServer: number = 50e3;
export const BaseCostFor1GBHacknetServerRam: number = 200e3;
export const BaseCostForHacknetServerCore: number = 1e6;
export const BaseCostForHacknetServerCache: number = 10e6;
export const HacknetServerPurchaseMult: number = 3.2; // Multiplier for puchasing an additional Hacknet Server
export const HacknetServerUpgradeLevelMult: number = 1.1; // Multiplier for cost when upgrading level
export const HacknetServerUpgradeRamMult: number = 1.4; // Multiplier for cost when upgrading RAM
export const HacknetServerUpgradeCoreMult: number = 1.55; // Multiplier for cost when buying another core
export const HacknetServerUpgradeCacheMult: number = 1.85; // Multiplier for cost when upgrading cache
export const MaxNumberHacknetServers: number = 20; // Max number of Hacknet Servers you can own
// Constants for max upgrade levels for Hacknet Server
export const HacknetServerMaxLevel: number = 300;
export const HacknetServerMaxRam: number = 8192;
export const HacknetServerMaxCores: number = 128;
export const HacknetServerMaxCache: number = 15;
interface IConstructorParams { interface IConstructorParams {
adminRights?: boolean; adminRights?: boolean;
hostname: string; hostname: string;
@@ -73,7 +58,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
// How long this HacknetServer has existed, in seconds // How long this HacknetServer has existed, in seconds
onlineTimeSeconds: number = 0; onlineTimeSeconds: number = 0;
// Total number of hashes earned by this // Total number of hashes earned by this server
totalHashesGenerated: number = 0; totalHashesGenerated: number = 0;
constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) { constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) {
@@ -84,96 +69,19 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
} }
calculateCacheUpgradeCost(levels: number): number { calculateCacheUpgradeCost(levels: number): number {
const sanitizedLevels = Math.round(levels); return calculateCacheUpgradeCost(this.cache, levels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.cache >= HacknetServerMaxCache) {
return Infinity;
}
const mult = HacknetServerUpgradeCacheMult;
let totalCost = 0;
let currentCache = this.cache;
for (let i = 0; i < sanitizedLevels; ++i) {
totalCost += Math.pow(mult, currentCache - 1);
++currentCache;
}
totalCost *= BaseCostForHacknetServerCache;
return totalCost;
} }
calculateCoreUpgradeCost(levels: number, costMult: number): number { calculateCoreUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels); return calculateCoreUpgradeCost(this.cores, levels, costMult);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.cores >= HacknetServerMaxCores) {
return Infinity;
}
const mult = HacknetServerUpgradeCoreMult;
let totalCost = 0;
let currentCores = this.cores;
for (let i = 0; i < sanitizedLevels; ++i) {
totalCost += Math.pow(mult, currentCores-1);
++currentCores;
}
totalCost *= BaseCostForHacknetServerCore;
totalCost *= costMult;
return totalCost;
} }
calculateLevelUpgradeCost(levels: number, costMult: number): number { calculateLevelUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels); return calculateLevelUpgradeCost(this.level, levels, costMult);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.level >= HacknetServerMaxLevel) {
return Infinity;
}
const mult = HacknetServerUpgradeLevelMult;
let totalMultiplier = 0;
let currLevel = this.level;
for (let i = 0; i < sanitizedLevels; ++i) {
totalMultiplier += Math.pow(mult, currLevel);
++currLevel;
}
return 10 * BaseCostForHacknetServer * totalMultiplier * costMult;
} }
calculateRamUpgradeCost(levels: number, costMult: number): number { calculateRamUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels); return calculateRamUpgradeCost(this.maxRam, levels, costMult);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (this.maxRam >= HacknetServerMaxRam) {
return Infinity;
}
let totalCost = 0;
let numUpgrades = Math.round(Math.log2(this.maxRam));
let currentRam = this.maxRam;
for (let i = 0; i < sanitizedLevels; ++i) {
let baseCost = currentRam * BaseCostFor1GBHacknetServerRam;
let mult = Math.pow(HacknetServerUpgradeRamMult, numUpgrades);
totalCost += (baseCost * mult);
currentRam *= 2;
++numUpgrades;
}
totalCost *= costMult;
return totalCost;
} }
// Process this Hacknet Server in the game loop. Returns the number of hashes generated // Process this Hacknet Server in the game loop. Returns the number of hashes generated
@@ -184,17 +92,17 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
} }
upgradeCache(levels: number): void { upgradeCache(levels: number): void {
this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels)); this.cache = Math.min(HacknetServerConstants.MaxCache, Math.round(this.cache + levels));
this.updateHashCapacity(); this.updateHashCapacity();
} }
upgradeCore(levels: number, prodMult: number): void { upgradeCore(levels: number, prodMult: number): void {
this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels)); this.cores = Math.min(HacknetServerConstants.MaxCores, Math.round(this.cores + levels));
this.updateHashRate(prodMult); this.updateHashRate(prodMult);
} }
upgradeLevel(levels: number, prodMult: number): void { upgradeLevel(levels: number, prodMult: number): void {
this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels)); this.level = Math.min(HacknetServerConstants.MaxLevel, Math.round(this.level + levels));
this.updateHashRate(prodMult); this.updateHashRate(prodMult);
} }
@@ -202,7 +110,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
for (let i = 0; i < levels; ++i) { for (let i = 0; i < levels; ++i) {
this.maxRam *= 2; this.maxRam *= 2;
} }
this.maxRam = Math.min(HacknetServerMaxRam, Math.round(this.maxRam)); this.maxRam = Math.min(HacknetServerConstants.MaxRam, Math.round(this.maxRam));
this.updateHashRate(prodMult); this.updateHashRate(prodMult);
return true; return true;
@@ -221,14 +129,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
} }
updateHashRate(prodMult: number): void { updateHashRate(prodMult: number): void {
const baseGain = HacknetServerHashesPerLevel * this.level; this.hashRate = calculateHashGainRate(this.level, this.ramUsed, this.maxRam, this.cores, prodMult)
const ramMultiplier = Math.pow(1.07, Math.log2(this.maxRam));
const coreMultiplier = 1 + (this.cores - 1) / 5;
const ramRatio = (1 - this.ramUsed / this.maxRam);
const hashRate = baseGain * ramMultiplier * coreMultiplier * ramRatio;
this.hashRate = hashRate * prodMult * BitNodeMultipliers.HacknetNodeMoney;
if (isNaN(this.hashRate)) { if (isNaN(this.hashRate)) {
this.hashRate = 0; this.hashRate = 0;

View File

@@ -7,6 +7,7 @@
* those upgrades * those upgrades
*/ */
import { HashUpgrades } from "./HashUpgrades"; import { HashUpgrades } from "./HashUpgrades";
import { HashUpgrade } from "./HashUpgrade";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, import { Generic_fromJSON,
@@ -67,11 +68,20 @@ export class HashManager {
return this.getMult(upgName); return this.getMult(upgName);
} }
getUpgrade(upgName: string): HashUpgrade | null {
const upg = HashUpgrades[upgName];
if (!upg) {
console.error(`Invalid Upgrade Name given to HashManager.getUpgrade(): ${upgName}`);
return null;
}
return upg;
}
/** /**
* Get the cost (in hashes) of an upgrade * Get the cost (in hashes) of an upgrade
*/ */
getUpgradeCost(upgName: string): number { getUpgradeCost(upgName: string): number {
const upg = HashUpgrades[upgName]; const upg = this.getUpgrade(upgName);
const currLevel = this.upgrades[upgName]; const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null) { if (upg == null || currLevel == null) {
console.error(`Invalid Upgrade Name given to HashManager.getUpgradeCost(): ${upgName}`); console.error(`Invalid Upgrade Name given to HashManager.getUpgradeCost(): ${upgName}`);

View File

@@ -0,0 +1,80 @@
export const HacknetNodeConstants: {
// Constants for Hacknet Node production
MoneyGainPerLevel: number;
// Constants for Hacknet Node purchase/upgrade costs
BaseCost: number;
LevelBaseCost: number;
RamBaseCost: number;
CoreBaseCost: number;
PurchaseNextMult: number;
UpgradeLevelMult: number;
UpgradeRamMult: number;
UpgradeCoreMult: number;
// Constants for max upgrade levels for Hacknet Nodes
MaxLevel: number;
MaxRam: number;
MaxCores: number;
} = {
MoneyGainPerLevel: 1.6,
BaseCost: 1000,
LevelBaseCost: 1,
RamBaseCost: 30e3,
CoreBaseCost: 500e3,
PurchaseNextMult: 1.85,
UpgradeLevelMult: 1.04,
UpgradeRamMult: 1.28,
UpgradeCoreMult: 1.48,
MaxLevel: 200,
MaxRam: 64,
MaxCores: 16,
}
export const HacknetServerConstants: {
// Constants for Hacknet Server stats/production
HashesPerLevel: number;
// Constants for Hacknet Server purchase/upgrade costs
BaseCost: number;
RamBaseCost: number;
CoreBaseCost: number;
CacheBaseCost: number;
PurchaseMult: number; // Multiplier for puchasing an additional Hacknet Server
UpgradeLevelMult: number; // Multiplier for cost when upgrading level
UpgradeRamMult: number; // Multiplier for cost when upgrading RAM
UpgradeCoreMult: number; // Multiplier for cost when buying another core
UpgradeCacheMult: number; // Multiplier for cost when upgrading cache
MaxServers: number; // Max number of Hacknet Servers you can own
// Constants for max upgrade levels for Hacknet Server
MaxLevel: number;
MaxRam: number;
MaxCores: number;
MaxCache: number;
} = {
HashesPerLevel: 0.001,
BaseCost: 50e3,
RamBaseCost: 200e3,
CoreBaseCost: 1e6,
CacheBaseCost: 10e6,
PurchaseMult: 3.20,
UpgradeLevelMult: 1.10,
UpgradeRamMult: 1.40,
UpgradeCoreMult: 1.55,
UpgradeCacheMult: 1.85,
MaxServers: 20,
MaxLevel: 300,
MaxRam: 8192,
MaxCores: 128,
MaxCache: 15,
}

View File

@@ -0,0 +1,96 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { HacknetNodeConstants } from "../data/Constants";
export function calculateMoneyGainRate(level: number, ram: number, cores: number, mult: number): number {
const gainPerLevel = HacknetNodeConstants.MoneyGainPerLevel;
const levelMult = (level * gainPerLevel);
const ramMult = Math.pow(1.035, ram - 1);
const coresMult = ((cores + 5) / 6);
return levelMult *
ramMult *
coresMult *
mult *
BitNodeMultipliers.HacknetNodeMoney;
}
export function calculateLevelUpgradeCost(startingLevel: number, extraLevels: number=1, costMult: number=1): number {
const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (startingLevel >= HacknetNodeConstants.MaxLevel) {
return Infinity;
}
const mult = HacknetNodeConstants.UpgradeLevelMult;
let totalMultiplier = 0;
let currLevel = startingLevel;
for (let i = 0; i < sanitizedLevels; ++i) {
totalMultiplier += (HacknetNodeConstants.LevelBaseCost * Math.pow(mult, currLevel));
++currLevel;
}
return HacknetNodeConstants.BaseCost / 2 * totalMultiplier * costMult;
}
export function calculateRamUpgradeCost(startingRam: number, extraLevels: number=1, costMult: number=1): number {
const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (startingRam >= HacknetNodeConstants.MaxRam) {
return Infinity;
}
let totalCost = 0;
let numUpgrades = Math.round(Math.log2(startingRam));
let currentRam = startingRam;
for (let i = 0; i < sanitizedLevels; ++i) {
const baseCost = currentRam * HacknetNodeConstants.RamBaseCost;
const mult = Math.pow(HacknetNodeConstants.UpgradeRamMult, numUpgrades);
totalCost += (baseCost * mult);
currentRam *= 2;
++numUpgrades;
}
totalCost *= costMult;
return totalCost;
}
export function calculateCoreUpgradeCost(startingCore: number, extraLevels: number=1, costMult: number=1): number {
const sanitizedCores = Math.round(extraLevels);
if (isNaN(sanitizedCores) || sanitizedCores < 1) {
return 0;
}
if (startingCore >= HacknetNodeConstants.MaxCores) {
return Infinity;
}
const coreBaseCost = HacknetNodeConstants.CoreBaseCost;
const mult = HacknetNodeConstants.UpgradeCoreMult;
let totalCost = 0;
let currentCores = startingCore;
for (let i = 0; i < sanitizedCores; ++i) {
totalCost += (coreBaseCost * Math.pow(mult, currentCores-1));
++currentCores;
}
totalCost *= costMult;
return totalCost;
}
export function calculateNodeCost(n: number, mult: number=1): number {
if(n <= 0) {
return 0;
}
return HacknetNodeConstants.BaseCost * Math.pow(HacknetNodeConstants.PurchaseNextMult, n-1) * mult;
}

View File

@@ -0,0 +1,115 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { HacknetServerConstants } from "../data/Constants";
export function calculateHashGainRate(level: number, ramUsed: number, maxRam: number, cores: number, mult: number): number {
const baseGain = HacknetServerConstants.HashesPerLevel * level;
const ramMultiplier = Math.pow(1.07, Math.log2(maxRam));
const coreMultiplier = 1 + (cores - 1) / 5;
const ramRatio = (1 - ramUsed / maxRam);
return baseGain *
ramMultiplier *
coreMultiplier *
ramRatio *
mult *
BitNodeMultipliers.HacknetNodeMoney;
}
export function calculateLevelUpgradeCost(startingLevel: number, extraLevels: number=1, costMult: number=1): number {
const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (startingLevel >= HacknetServerConstants.MaxLevel) {
return Infinity;
}
const mult = HacknetServerConstants.UpgradeLevelMult;
let totalMultiplier = 0;
let currLevel = startingLevel;
for (let i = 0; i < sanitizedLevels; ++i) {
totalMultiplier += Math.pow(mult, currLevel);
++currLevel;
}
return 10 * HacknetServerConstants.BaseCost * totalMultiplier * costMult;
}
export function calculateRamUpgradeCost(startingRam: number, extraLevels: number=1, costMult: number=1): number {
const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (startingRam >= HacknetServerConstants.MaxRam) {
return Infinity;
}
let totalCost = 0;
let numUpgrades = Math.round(Math.log2(startingRam));
let currentRam = startingRam;
for (let i = 0; i < sanitizedLevels; ++i) {
let baseCost = currentRam * HacknetServerConstants.RamBaseCost;
let mult = Math.pow(HacknetServerConstants.UpgradeRamMult, numUpgrades);
totalCost += (baseCost * mult);
currentRam *= 2;
++numUpgrades;
}
totalCost *= costMult;
return totalCost;
}
export function calculateCoreUpgradeCost(startingCores: number, extraLevels: number=1, costMult: number=1): number {
const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (startingCores >= HacknetServerConstants.MaxCores) {
return Infinity;
}
const mult = HacknetServerConstants.UpgradeCoreMult;
let totalCost = 0;
let currentCores = startingCores;
for (let i = 0; i < sanitizedLevels; ++i) {
totalCost += Math.pow(mult, currentCores-1);
++currentCores;
}
totalCost *= HacknetServerConstants.CoreBaseCost;
totalCost *= costMult;
return totalCost;
}
export function calculateCacheUpgradeCost(startingCache: number, extraLevels: number=1): number {
const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0;
}
if (startingCache >= HacknetServerConstants.MaxCache) {
return Infinity;
}
const mult = HacknetServerConstants.UpgradeCacheMult;
let totalCost = 0;
let currentCache = startingCache;
for (let i = 0; i < sanitizedLevels; ++i) {
totalCost += Math.pow(mult, currentCache - 1);
++currentCache;
}
totalCost *= HacknetServerConstants.CacheBaseCost;
return totalCost;
}
export function calculateServerCost(n: number, mult: number=1): number {
if (n-1 >= HacknetServerConstants.MaxServers) { return Infinity; }
return HacknetServerConstants.BaseCost * Math.pow(HacknetServerConstants.PurchaseMult, n-1) * mult;
}

View File

@@ -42,11 +42,9 @@ export class GeneralInfo extends React.Component {
hackers all around the world to anonymously share computing power and hackers all around the world to anonymously share computing power and
perform distributed cyberattacks without the fear of being traced. perform distributed cyberattacks without the fear of being traced.
</p> </p>
<br />
<p className={"hacknet-general-info"}> <p className={"hacknet-general-info"}>
{this.getSecondParagraph()} {this.getSecondParagraph()}
</p> </p>
<br />
<p className={"hacknet-general-info"}> <p className={"hacknet-general-info"}>
{this.getThirdParagraph()} {this.getThirdParagraph()}
</p> </p>

View File

@@ -4,11 +4,7 @@
*/ */
import React from "react"; import React from "react";
import { import { HacknetNodeConstants } from "../data/Constants";
HacknetNodeMaxLevel,
HacknetNodeMaxRam,
HacknetNodeMaxCores
} from "../HacknetNode";
import { import {
getMaxNumberLevelUpgrades, getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades, getMaxNumberRamUpgrades,
@@ -21,6 +17,8 @@ import {
import { Player } from "../../Player"; import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
export class HacknetNode extends React.Component { export class HacknetNode extends React.Component {
render() { render() {
@@ -29,21 +27,21 @@ export class HacknetNode extends React.Component {
const recalculate = this.props.recalculate; const recalculate = this.props.recalculate;
// Upgrade Level Button // Upgrade Level Button
let upgradeLevelText, upgradeLevelClass; let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetNodeMaxLevel) { if (node.level >= HacknetNodeConstants.MaxLevel) {
upgradeLevelText = "MAX LEVEL"; upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled"; upgradeLevelClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel); multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
} else { } else {
const levelsToMax = HacknetNodeMaxLevel - node.level; const levelsToMax = HacknetNodeConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`; upgradeLevelContent = <>Upgrade x{multiplier} - {Money(upgradeLevelCost)}</>;
if (Player.money.lt(upgradeLevelCost)) { if (Player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled"; upgradeLevelClass = "std-button-disabled";
} else { } else {
@@ -53,28 +51,28 @@ export class HacknetNode extends React.Component {
const upgradeLevelOnClick = () => { const upgradeLevelOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel); numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
} }
purchaseLevelUpgrade(node, numUpgrades); purchaseLevelUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
let upgradeRamText, upgradeRamClass; let upgradeRamContent, upgradeRamClass;
if (node.ram >= HacknetNodeMaxRam) { if (node.ram >= HacknetNodeConstants.MaxRam) {
upgradeRamText = "MAX RAM"; upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled"; upgradeRamClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam); multiplier = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
} else { } else {
const levelsToMax = Math.round(Math.log2(HacknetNodeMaxRam / node.ram)); const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram));
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`; upgradeRamContent = <>Upgrade x{multiplier} - {Money(upgradeRamCost)}</>;
if (Player.money.lt(upgradeRamCost)) { if (Player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled"; upgradeRamClass = "std-button-disabled";
} else { } else {
@@ -84,28 +82,28 @@ export class HacknetNode extends React.Component {
const upgradeRamOnClick = () => { const upgradeRamOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam); numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
} }
purchaseRamUpgrade(node, numUpgrades); purchaseRamUpgrade(node, numUpgrades);
recalculate(); recalculate();
return false; return false;
} }
let upgradeCoresText, upgradeCoresClass; let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetNodeMaxCores) { if (node.cores >= HacknetNodeConstants.MaxCores) {
upgradeCoresText = "MAX CORES"; upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled"; upgradeCoresClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores); multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
} else { } else {
const levelsToMax = HacknetNodeMaxCores - node.cores; const levelsToMax = HacknetNodeConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`; upgradeCoresContent = <>Upgrade x{multiplier} - {Money(upgradeCoreCost)}</>;
if (Player.money.lt(upgradeCoreCost)) { if (Player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled"; upgradeCoresClass = "std-button-disabled";
} else { } else {
@@ -115,7 +113,7 @@ export class HacknetNode extends React.Component {
const upgradeCoresOnClick = () => { const upgradeCoresOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores); numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
} }
purchaseCoreUpgrade(node, numUpgrades); purchaseCoreUpgrade(node, numUpgrades);
recalculate(); recalculate();
@@ -126,31 +124,33 @@ export class HacknetNode extends React.Component {
<li className={"hacknet-node"}> <li className={"hacknet-node"}>
<div className={"hacknet-node-container"}> <div className={"hacknet-node-container"}>
<div className={"row"}> <div className={"row"}>
<p>Node name:</p> <h1 style={{"fontSize":"1em"}}>{node.name}</h1>
<span className={"text"}>{node.name}</span>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Production:</p> <p>Production:</p>
<span className={"text money-gold"}> <span className={"text money-gold"}>
{numeralWrapper.formatMoney(node.totalMoneyGenerated)} ({numeralWrapper.formatMoney(node.moneyGainRatePerSecond)} / sec) {Money(node.totalMoneyGenerated)} ({MoneyRate(node.moneyGainRatePerSecond)})
</span> </span>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span> <p>Level:</p>
<span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}> <button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelText} {upgradeLevelContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>RAM:</p><span className={"text upgradable-info"}>{node.ram}GB</span> <p>RAM:</p>
<span className={"text upgradable-info"}>{node.ram}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}> <button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamText} {upgradeRamContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span> <p>Cores:</p>
<span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}> <button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresText} {upgradeCoresContent}
</button> </button>
</div> </div>
</div> </div>

View File

@@ -4,12 +4,7 @@
*/ */
import React from "react"; import React from "react";
import { import { HacknetServerConstants } from "../data/Constants";
HacknetServerMaxLevel,
HacknetServerMaxRam,
HacknetServerMaxCores,
HacknetServerMaxCache
} from "../HacknetServer";
import { import {
getMaxNumberLevelUpgrades, getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades, getMaxNumberRamUpgrades,
@@ -25,6 +20,9 @@ import {
import { Player } from "../../Player"; import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { Hashes } from "../../ui/React/Hashes";
import { HashRate } from "../../ui/React/HashRate";
export class HacknetServer extends React.Component { export class HacknetServer extends React.Component {
render() { render() {
@@ -33,21 +31,21 @@ export class HacknetServer extends React.Component {
const recalculate = this.props.recalculate; const recalculate = this.props.recalculate;
// Upgrade Level Button // Upgrade Level Button
let upgradeLevelText, upgradeLevelClass; let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetServerMaxLevel) { if (node.level >= HacknetServerConstants.MaxLevel) {
upgradeLevelText = "MAX LEVEL"; upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled"; upgradeLevelClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel); multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
} else { } else {
const levelsToMax = HacknetServerMaxLevel - node.level; const levelsToMax = HacknetServerConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
upgradeLevelText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeLevelCost)}`; upgradeLevelContent = <>Upgrade x{multiplier} - {Money(upgradeLevelCost)}</>;
if (Player.money.lt(upgradeLevelCost)) { if (Player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled"; upgradeLevelClass = "std-button-disabled";
} else { } else {
@@ -57,7 +55,7 @@ export class HacknetServer extends React.Component {
const upgradeLevelOnClick = () => { const upgradeLevelOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerMaxLevel); numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
} }
purchaseLevelUpgrade(node, numUpgrades); purchaseLevelUpgrade(node, numUpgrades);
recalculate(); recalculate();
@@ -65,21 +63,21 @@ export class HacknetServer extends React.Component {
} }
// Upgrade RAM Button // Upgrade RAM Button
let upgradeRamText, upgradeRamClass; let upgradeRamContent, upgradeRamClass;
if (node.maxRam >= HacknetServerMaxRam) { if (node.maxRam >= HacknetServerConstants.MaxRam) {
upgradeRamText = "MAX RAM"; upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled"; upgradeRamClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(node, HacknetServerMaxRam); multiplier = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
} else { } else {
const levelsToMax = Math.round(Math.log2(HacknetServerMaxRam / node.maxRam)); const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam));
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
upgradeRamText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeRamCost)}`; upgradeRamContent = <>Upgrade x{multiplier} - {Money(upgradeRamCost)}</>;
if (Player.money.lt(upgradeRamCost)) { if (Player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled"; upgradeRamClass = "std-button-disabled";
} else { } else {
@@ -89,7 +87,7 @@ export class HacknetServer extends React.Component {
const upgradeRamOnClick = () => { const upgradeRamOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerMaxRam); numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
} }
purchaseRamUpgrade(node, numUpgrades); purchaseRamUpgrade(node, numUpgrades);
recalculate(); recalculate();
@@ -97,21 +95,21 @@ export class HacknetServer extends React.Component {
} }
// Upgrade Cores Button // Upgrade Cores Button
let upgradeCoresText, upgradeCoresClass; let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetServerMaxCores) { if (node.cores >= HacknetServerConstants.MaxCores) {
upgradeCoresText = "MAX CORES"; upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled"; upgradeCoresClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores); multiplier = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
} else { } else {
const levelsToMax = HacknetServerMaxCores - node.cores; const levelsToMax = HacknetServerConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
upgradeCoresText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCoreCost)}`; upgradeCoresContent = <>Upgrade x{multiplier} - {Money(upgradeCoreCost)}</>;
if (Player.money.lt(upgradeCoreCost)) { if (Player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled"; upgradeCoresClass = "std-button-disabled";
} else { } else {
@@ -121,7 +119,7 @@ export class HacknetServer extends React.Component {
const upgradeCoresOnClick = () => { const upgradeCoresOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerMaxCores); numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
} }
purchaseCoreUpgrade(node, numUpgrades); purchaseCoreUpgrade(node, numUpgrades);
recalculate(); recalculate();
@@ -129,21 +127,21 @@ export class HacknetServer extends React.Component {
} }
// Upgrade Cache button // Upgrade Cache button
let upgradeCacheText, upgradeCacheClass; let upgradeCacheContent, upgradeCacheClass;
if (node.cache >= HacknetServerMaxCache) { if (node.cache >= HacknetServerConstants.MaxCache) {
upgradeCacheText = "MAX CACHE"; upgradeCacheContent = <>MAX CACHE</>;
upgradeCacheClass = "std-button-disabled"; upgradeCacheClass = "std-button-disabled";
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache); multiplier = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
} else { } else {
const levelsToMax = HacknetServerMaxCache - node.cache; const levelsToMax = HacknetServerConstants.MaxCache - node.cache;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier); const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier);
upgradeCacheText = `Upgrade x${multiplier} - ${numeralWrapper.formatMoney(upgradeCacheCost)}`; upgradeCacheContent = <>Upgrade x{multiplier} - {Money(upgradeCacheCost)}</>;
if (Player.money.lt(upgradeCacheCost)) { if (Player.money.lt(upgradeCacheCost)) {
upgradeCacheClass = "std-button-disabled"; upgradeCacheClass = "std-button-disabled";
} else { } else {
@@ -153,7 +151,7 @@ export class HacknetServer extends React.Component {
const upgradeCacheOnClick = () => { const upgradeCacheOnClick = () => {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerMaxCache); numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
} }
purchaseCacheUpgrade(node, numUpgrades); purchaseCacheUpgrade(node, numUpgrades);
recalculate(); recalculate();
@@ -165,13 +163,12 @@ export class HacknetServer extends React.Component {
<li className={"hacknet-node"}> <li className={"hacknet-node"}>
<div className={"hacknet-node-container"}> <div className={"hacknet-node-container"}>
<div className={"row"}> <div className={"row"}>
<p>Node name:</p> <h1 style={{"fontSize":"1em"}}>{node.hostname}</h1>
<span className={"text"}>{node.hostname}</span>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Production:</p> <p>Production:</p>
<span className={"text money-gold"}> <span className={"text money-gold"}>
{numeralWrapper.formatBigNumber(node.totalHashesGenerated)} ({numeralWrapper.formatBigNumber(node.hashRate)} / sec) {Hashes(node.totalHashesGenerated)} ({HashRate(node.hashRate)})
</span> </span>
</div> </div>
<div className={"row"}> <div className={"row"}>
@@ -181,25 +178,25 @@ export class HacknetServer extends React.Component {
<div className={"row"}> <div className={"row"}>
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span> <p>Level:</p><span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}> <button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelText} {upgradeLevelContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>RAM:</p><span className={"text upgradable-info"}>{node.maxRam}GB</span> <p>RAM:</p><span className={"text upgradable-info"}>{node.maxRam}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}> <button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamText} {upgradeRamContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span> <p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}> <button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresText} {upgradeCoresContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Cache Level:</p><span className={"text upgradable-info"}>{node.cache}</span> <p>Cache Level:</p><span className={"text upgradable-info"}>{node.cache}</span>
<button className={upgradeCacheClass} onClick={upgradeCacheOnClick}> <button className={upgradeCacheClass} onClick={upgradeCacheOnClick}>
{upgradeCacheText} {upgradeCacheContent}
</button> </button>
</div> </div>
</div> </div>

View File

@@ -19,6 +19,8 @@ import { ServerDropdown,
ServerType } from "../../ui/React/ServerDropdown" ServerType } from "../../ui/React/ServerDropdown"
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { CopyableText } from "../../ui/React/CopyableText";
import { Hashes } from "../../ui/React/Hashes";
class HashUpgrade extends React.Component { class HashUpgrade extends React.Component {
constructor(props) { constructor(props) {
@@ -63,8 +65,8 @@ class HashUpgrade extends React.Component {
// We'll reuse a Bladeburner css class // We'll reuse a Bladeburner css class
return ( return (
<div className={"bladeburner-action"}> <div className={"bladeburner-action"}>
<h2>{upg.name}</h2> <CopyableText value={upg.name} />
<p>Cost: {numeralWrapper.format(cost, "0.000a")}</p> <p>Cost: {Hashes(cost)}</p>
<p>{upg.desc}</p> <p>{upg.desc}</p>
<button className={btnClass} onClick={this.purchase}> <button className={btnClass} onClick={this.purchase}>
Purchase Purchase
@@ -122,7 +124,7 @@ export class HashUpgradePopup extends React.Component {
<div> <div>
<PopupCloseButton popup={this.props.popupId} text={"Close"} /> <PopupCloseButton popup={this.props.popupId} text={"Close"} />
<p>Spend your hashes on a variety of different upgrades</p> <p>Spend your hashes on a variety of different upgrades</p>
<p>Hashes: {numeralWrapper.formatBigNumber(this.state.totalHashes)}</p> <p>Hashes: {numeralWrapper.formatHashes(this.state.totalHashes)}</p>
{upgradeElems} {upgradeElems}
</div> </div>
) )

View File

@@ -9,43 +9,33 @@ import React from "react";
import { hasHacknetServers } from "../HacknetHelpers"; import { hasHacknetServers } from "../HacknetHelpers";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { HashRate } from "../../ui/React/HashRate";
import { Hashes } from "../../ui/React/Hashes";
export function PlayerInfo(props) { export function PlayerInfo(props) {
const hasServers = hasHacknetServers(); const hasServers = hasHacknetServers();
let prod; let prod;
if (hasServers) { if (hasServers) {
prod = numeralWrapper.format(props.totalProduction, "0.000a") + " hashes / sec"; prod = HashRate(props.totalProduction);
} else { } else {
prod = numeralWrapper.formatMoney(props.totalProduction) + " / sec"; prod = MoneyRate(props.totalProduction);
}
let hashInfo;
if (hasServers) {
hashInfo = numeralWrapper.format(Player.hashManager.hashes, "0.000a") + " / " +
numeralWrapper.format(Player.hashManager.capacity, "0.000a");
} }
return ( return (
<p id={"hacknet-nodes-money"}> <p id={"hacknet-nodes-money"}>
<span>Money:</span> <span>Money: </span>
<span className={"money-gold"}>{numeralWrapper.formatMoney(Player.money.toNumber())}</span><br /> {Money(Player.money.toNumber())}<br />
{ {
hasServers && hasServers &&
<span>Hashes:</span> <><span>Hashes: {Hashes(Player.hashManager.hashes)} / {Hashes(Player.hashManager.capacity)}</span><br /></>
}
{
hasServers &&
<span className={"money-gold"}>{hashInfo}</span>
}
{
hasServers &&
<br />
} }
<span>Total Hacknet Node Production:</span> <span>Total Hacknet Node Production: </span>
<span className={"money-gold"}>{prod}</span> {prod}
</p> </p>
) )
} }

View File

@@ -7,6 +7,7 @@ import { hasHacknetServers,
hasMaxNumberHacknetServers } from "../HacknetHelpers"; hasMaxNumberHacknetServers } from "../HacknetHelpers";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
export function PurchaseButton(props) { export function PurchaseButton(props) {
if (props.multiplier == null || props.onClick == null) { if (props.multiplier == null || props.onClick == null) {
@@ -20,13 +21,13 @@ export function PurchaseButton(props) {
if (hasHacknetServers()) { if (hasHacknetServers()) {
if (hasMaxNumberHacknetServers()) { if (hasMaxNumberHacknetServers()) {
className = "std-button-disabled"; className = "std-button-disabled";
text = "Hacknet Server limit reached"; text = <>Hacknet Server limit reached</>;
style = {color: "red"}; style = {color: "red"};
} else { } else {
text = `Purchase Hacknet Server - ${numeralWrapper.formatMoney(cost)}`; text = <>Purchase Hacknet Server - {Money(cost)}</>;
} }
} else { } else {
text = `Purchase Hacknet Node - ${numeralWrapper.formatMoney(cost)}`; text = <>Purchase Hacknet Node - {Money(cost)}</>;
} }
return ( return (

26
src/Hospital/Hospital.ts Normal file
View File

@@ -0,0 +1,26 @@
import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"
export function getHospitalizationCost(p: IPlayer): number {
let money;
if (typeof p.money === 'number') {
money = p.money;
} else {
money = p.money.toNumber();
}
if (money < 0) {
return 0;
}
return Math.min(money*0.1, (p.max_hp - p.hp) * CONSTANTS.HospitalCostPerHp);
}
export function calculateHospitalizationCost(p: IPlayer, damage: number): number {
const oldhp = p.hp;
p.hp -= damage
if (p.hp < 0) p.hp = 0;
const cost = getHospitalizationCost(p);
p.hp = oldhp;
return cost;
}

View File

@@ -11,4 +11,5 @@ export enum LocationType {
TechVendor, TechVendor,
TravelAgency, TravelAgency,
University, University,
Casino,
} }

View File

@@ -47,14 +47,14 @@ Cities[CityName.Aevum].asciiArt = `
\\ 56 B \\ 56 B
x \\ [summit university] x \\ [summit university]
\\ \\ 28 \\ \\ 28
\\ [snap fitness gym] x o--L------------ \\ [snap fitness gym] x o--L-----------N
K \\ / K \\ /
\\ \\ P \\ \\ Q [casino]
x 58 \\ / [travel agency] x 58 \\ / [travel agency]
\\ 94 95 o \\ 94 95 o
90 x 59 o------o | 90 x 59 o------o |
\\ / \\ | 98 102 103 \\ / \\ | 98 102 103
o--------N------x----o 93 96 o-----+------------o o----o o--------O------x----o 93 96 o-----+------------o o----o
\\ | \\ / \\ | \\ /
[hospital] \\ 61 [ecorp] x 31 99 o-F-o 101 [hospital] \\ 61 [ecorp] x 31 99 o-F-o 101
o | o |
@@ -69,13 +69,13 @@ Cities[CityName.Aevum].asciiArt = `
| 34 x \\ | 34 x \\
[clarke inc.] C | \\ [world stock exchange] [clarke inc.] C | \\ [world stock exchange]
| | \\ | | \\
| | o-M-------Q--------o | | o-M-------R--------o
[galactic cybersystems] G 35 x [galactic cybersystems] G 35 x
| [watchdog security] | [watchdog security]
| |
67 o 67 o
[the slums] O ` [the slums] P `
Cities[CityName.Chongqing].asciiArt = ` Cities[CityName.Chongqing].asciiArt = `
| |
75 o 75 o

View File

@@ -21,6 +21,7 @@ import { SpecialServerIps } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { import {
@@ -38,6 +39,7 @@ import { createElement } from "../../utils/uiHelpers/createElement";
import { createPopup } from "../../utils/uiHelpers/createPopup"; import { createPopup } from "../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton"; import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../utils/uiHelpers/removeElementById";
import * as React from "react";
/** /**
* Create a pop-up box that lets the player confirm traveling to a different city. * Create a pop-up box that lets the player confirm traveling to a different city.
@@ -49,7 +51,7 @@ import { removeElementById } from "../../utils/uiHelpers/removeElementById";
*/ */
type TravelFunction = (to: CityName) => void; type TravelFunction = (to: CityName) => void;
export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void { export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
const cost = CONSTANTS.TravelCost; const cost: number = CONSTANTS.TravelCost;
if (Settings.SuppressTravelConfirmation) { if (Settings.SuppressTravelConfirmation) {
travelFn(destination); travelFn(destination);
@@ -76,8 +78,8 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio
return false; return false;
}); });
yesNoBoxCreate(`Would you like to travel to ${destination}? The trip will ` + yesNoBoxCreate(<span>Would you like to travel to ${destination}? The trip will
`cost ${numeralWrapper.formatMoney(cost)}`); cost {Money(cost)}.</span>);
} }
/** /**
@@ -105,10 +107,9 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
yesNoTxtInpBoxClose(); yesNoTxtInpBoxClose();
}); });
yesNoTxtInpBoxCreate( yesNoTxtInpBoxCreate(<>Would you like to purchase a new server with {numeralWrapper.formatRAM(ram)} of RAM for {Money(cost)}?
`Would you like to purchase a new server with ${ram} GB of RAM for ` + <br /><br />Please enter the server hostname below:<br />
`${numeralWrapper.formatMoney(cost)}?<br><br>Please enter the server hostname below:<br>` </>);
);
} }
/** /**
@@ -197,7 +198,12 @@ export function createStartCorporationPopup(p: IPlayer) {
*/ */
export function createUpgradeHomeCoresPopup(p: IPlayer) { export function createUpgradeHomeCoresPopup(p: IPlayer) {
const currentCores = p.getHomeComputer().cpuCores; const currentCores = p.getHomeComputer().cpuCores;
if (currentCores >= 8) { return; } // Max of 8 cores if (currentCores >= 8) {
dialogBoxCreate(<>
You've have the maximum amount of CPU cores on your home computer.
</>);
return;
}
// Cost of purchasing another cost is found by indexing this array with number of current cores // Cost of purchasing another cost is found by indexing this array with number of current cores
const allCosts = [ const allCosts = [
@@ -236,12 +242,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate( yesNoBoxCreate(<>Would you like to purchase an additional CPU Core for your home computer? Each CPU Core
"Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " + lets you start with an additional Core Node in Hacking Missions.<br /><br />
"lets you start with an additional Core Node in Hacking Missions.<br><br>" + Purchasing an additional core (for a total of {p.getHomeComputer().cpuCores + 1}) will
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " + cost {Money(cost)}</>);
"cost " + numeralWrapper.formatMoney(cost)
);
} }
/** /**
@@ -256,6 +260,14 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
const noBtn = yesNoBoxGetNoButton(); const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) { return; } if (yesBtn == null || noBtn == null) { return; }
const homeComputer = p.getHomeComputer();
if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) {
dialogBoxCreate(<>
You've have the maximum amount of RAM on your home computer.
</>);
return;
}
yesBtn.innerText = "Purchase"; yesBtn.innerText = "Purchase";
yesBtn.addEventListener("click", ()=>{ yesBtn.addEventListener("click", ()=>{
purchaseRamForHomeComputer(cost, p); purchaseRamForHomeComputer(cost, p);
@@ -267,11 +279,11 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate( yesNoBoxCreate(<>
"Would you like to purchase additional RAM for your home computer? <br><br>" + Would you like to purchase additional RAM for your home computer? <br /><br />
"This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" + This will upgrade your RAM from {numeralWrapper.formatRAM(ram)} to {numeralWrapper.formatRAM(ram*2)}. <br /><br />
"This will cost " + numeralWrapper.format(cost, '$0.000a') This will cost {Money(cost)}
); </>);
} }

View File

@@ -24,6 +24,7 @@ export enum LocationName {
AevumSnapFitnessGym = "Snap Fitness Gym", AevumSnapFitnessGym = "Snap Fitness Gym",
AevumSummitUniversity = "Summit University", AevumSummitUniversity = "Summit University",
AevumWatchdogSecurity = "Watchdog Security", AevumWatchdogSecurity = "Watchdog Security",
AevumCasino = "Iker Molina Casino",
// Chongqing locations // Chongqing locations
ChongqingKuaiGongInternational = "KuaiGong International", ChongqingKuaiGongInternational = "KuaiGong International",

View File

@@ -145,6 +145,11 @@ export const LocationsMetadata: IConstructorParams[] = [
name: LocationName.AevumWatchdogSecurity, name: LocationName.AevumWatchdogSecurity,
types: [LocationType.Company], types: [LocationType.Company],
}, },
{
city: CityName.Aevum,
name: LocationName.AevumCasino,
types: [LocationType.Casino],
},
{ {
city: CityName.Chongqing, city: CityName.Chongqing,
infiltrationData: { infiltrationData: {

View File

@@ -0,0 +1,89 @@
/**
* React Subcomponent for displaying a location's UI, when that location is a gym
*
* This subcomponent renders all of the buttons for training at the gym
*/
import * as React from "react";
import { Location } from "../Location";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
import { SlotMachine } from "../../Casino/SlotMachine";
import { CoinFlip } from "../../Casino/CoinFlip";
import { Roulette } from "../../Casino/Roulette";
type IProps = {
p: IPlayer;
}
type IState = {
game: string;
}
export class CasinoLocation extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
game: '',
}
this.updateGame = this.updateGame.bind(this);
}
updateGame(game: string) {
this.setState({
game: game,
});
}
renderGames() {
return (<>
<StdButton
onClick={() => this.updateGame('coin')}
text={"Play coin flip"}
/><br />
<StdButton
onClick={() => this.updateGame('slots')}
text={"Play slots"}
/><br />
<StdButton
onClick={() => this.updateGame('roulette')}
text={"Play roulette"}
/>
</>)
}
renderGame() {
let elem;
switch(this.state.game) {
case 'coin':
elem = <CoinFlip p={this.props.p} />
break;
case 'slots':
elem = <SlotMachine p={this.props.p} />
break;
case 'roulette':
elem = <Roulette p={this.props.p} />
break;
}
return (<>
<StdButton onClick={() => this.updateGame('')} text={"Stop playing"} />
{elem}
</>)
}
render() {
if(!this.state.game) {
return this.renderGames();
} else {
return this.renderGame();
}
}
}

View File

@@ -7,6 +7,7 @@ import * as React from "react";
import { City } from "../City"; import { City } from "../City";
import { LocationName } from "../data/LocationNames"; import { LocationName } from "../data/LocationNames";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
@@ -16,7 +17,7 @@ type IProps = {
} }
export class LocationCity extends React.Component<IProps, any> { export class LocationCity extends React.Component<IProps, any> {
render() { asciiCity() {
const thiscity = this; const thiscity = this;
const topprop = this.props const topprop = this.props
@@ -66,9 +67,29 @@ export class LocationCity extends React.Component<IProps, any> {
elems.push(<pre key={i}>{lineElems(lines[i])}</pre>) elems.push(<pre key={i}>{lineElems(lines[i])}</pre>)
} }
return elems;
}
listCity() {
const locationButtons = this.props.city.locations.map((locName) => {
return (
<li key={locName}>
<StdButton onClick={this.props.enterLocation.bind(this, locName)} text={locName} />
</li>
)
});
return (
<ul>
{locationButtons}
</ul>
)
}
render() {
return ( return (
<> <>
{elems} {Settings.DisableASCIIArt ? this.listCity() : this.asciiCity()}
</> </>
) )
} }

View File

@@ -23,6 +23,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
type IProps = { type IProps = {
engine: IEngine; engine: IEngine;
@@ -34,6 +36,10 @@ type IState = {
employedHere: boolean; employedHere: boolean;
} }
const blockStyleMarkup = {
display: "block",
}
export class CompanyLocation extends React.Component<IProps, IState> { export class CompanyLocation extends React.Component<IProps, IState> {
/** /**
* We'll keep a reference to the Company that this component is being rendered for, * We'll keep a reference to the Company that this component is being rendered for,
@@ -211,23 +217,24 @@ export class CompanyLocation extends React.Component<IProps, IState> {
isEmployedHere && isEmployedHere &&
<div> <div>
<p>Job Title: {this.jobTitle}</p> <p>Job Title: {this.jobTitle}</p>
<p>--------------------</p> <br /><p style={blockStyleMarkup}>-------------------------</p><br />
<p className={"tooltip"}> <p className={"tooltip"}>
Company reputation: {numeralWrapper.format(this.company.playerReputation, "0,0.000")} Company reputation: {Reputation(this.company.playerReputation)}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
You will earn {numeralWrapper.format(favorGain[0], "0,0")} company You will earn {Favor(favorGain[0])} company
favor upon resetting after installing Augmentations favor upon resetting after installing Augmentations
</span> </span>
</p> </p><br />
<p>--------------------</p> <br /><p style={blockStyleMarkup}>-------------------------</p><br />
<p className={"tooltip"}> <p className={"tooltip"}>
Company Favor: {numeralWrapper.format(this.company.favor, "0,0")} Company Favor: {Favor(this.company.favor)}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
Company favor increases the rate at which you earn reputation for this company by Company favor increases the rate at which you earn reputation for this company by
1% per favor. Company favor is gained whenever you reset after installing Augmentations. The amount 1% per favor. Company favor is gained whenever you reset after installing Augmentations. The amount
of favor you gain depends on how much reputation you have with the comapny. of favor you gain depends on how much reputation you have with the comapny.
</span> </span>
</p> </p><br />
<br /><p style={blockStyleMarkup}>-------------------------</p><br />
<StdButton <StdButton
id={"foo-work-button-id"} id={"foo-work-button-id"}
onClick={this.work} onClick={this.work}

View File

@@ -14,6 +14,7 @@ import { SpecialLocation } from "./SpecialLocation";
import { TechVendorLocation } from "./TechVendorLocation"; import { TechVendorLocation } from "./TechVendorLocation";
import { TravelAgencyLocation } from "./TravelAgencyLocation"; import { TravelAgencyLocation } from "./TravelAgencyLocation";
import { UniversityLocation } from "./UniversityLocation"; import { UniversityLocation } from "./UniversityLocation";
import { CasinoLocation } from "./CasinoLocation";
import { Location } from "../Location"; import { Location } from "../Location";
import { LocationType } from "../LocationTypeEnum"; import { LocationType } from "../LocationTypeEnum";
@@ -131,6 +132,15 @@ export class GenericLocation extends React.Component<IProps, any> {
) )
} }
if (this.props.loc.types.includes(LocationType.Casino)) {
content.push(
<CasinoLocation
key={"casinoLocation"}
p={this.props.p}
/>
)
}
return content; return content;
} }

View File

@@ -12,6 +12,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
type IProps = { type IProps = {
loc: Location; loc: Location;
@@ -66,22 +67,22 @@ export class GymLocation extends React.Component<IProps, any> {
<StdButton <StdButton
onClick={this.trainStrength} onClick={this.trainStrength}
style={this.btnStyle} style={this.btnStyle}
text={`Train Strength (${numeralWrapper.formatMoney(cost)} / sec)`} text={<>Train Strength ({Money(cost)} / sec)</>}
/> />
<StdButton <StdButton
onClick={this.trainDefense} onClick={this.trainDefense}
style={this.btnStyle} style={this.btnStyle}
text={`Train Defense (${numeralWrapper.formatMoney(cost)} / sec)`} text={<>Train Defense ({Money(cost)} / sec)</>}
/> />
<StdButton <StdButton
onClick={this.trainDexterity} onClick={this.trainDexterity}
style={this.btnStyle} style={this.btnStyle}
text={`Train Dexterity (${numeralWrapper.formatMoney(cost)} / sec)`} text={<>Train Dexterity ({Money(cost)} / sec)</>}
/> />
<StdButton <StdButton
onClick={this.trainAgility} onClick={this.trainAgility}
style={this.btnStyle} style={this.btnStyle}
text={`Train Agility (${numeralWrapper.formatMoney(cost)} / sec)`} text={<>Train Agility ({Money(cost)} / sec)</>}
/> />
</div> </div>
) )

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