Compare commits

...

16 Commits

Author SHA1 Message Date
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
151 changed files with 3934 additions and 1229 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 {
display: inline;
width: 25%;
}

View File

@@ -356,10 +356,27 @@ a:visited {
color: $my-stat-cha-color;
}
.reputation {
color: $light-yellow;
}
.smallfont {
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] {
filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10);
}
@@ -395,4 +412,12 @@ input[type=checkbox] {
height: 10px;
background: var(--my-font-color);
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 {
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([398,0]),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){},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){},398:function(n,t,o){"use strict";o.r(t);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),o(341)}});
//# sourceMappingURL=engineStyle.bundle.js.map

62
dist/engineStyle.css vendored
View File

@@ -353,9 +353,24 @@ a:visited {
.charisma-purple {
color: #a671d1; }
.reputation {
color: #faffdf; }
.smallfont {
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] {
filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10); }
@@ -387,6 +402,13 @@ input[type=checkbox] {
background: var(--my-font-color);
cursor: pointer; }
.noselect {
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none; }
/* COLORS */
/* Attributes */
/* Styling for tooltip-style elements */
@@ -460,6 +482,43 @@ input[type=checkbox] {
.tooltip:hover .tooltiptextlow {
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 */
/* Attributes */
/**
@@ -1315,8 +1374,7 @@ button {
margin-top: 20px; }
#infiltration-buttons .a-link-button {
display: inline;
width: 25%; }
display: inline; }
/**
* 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,94 @@
Changelog
=========
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)
---------------------------------
**Netscript**

View File

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

View File

@@ -30,4 +30,5 @@ to reach out to the developer!
Gang API <netscript/netscriptgangapi>
Coding Contract API <netscript/netscriptcodingcontractapi>
Sleeve API <netscript/netscriptsleeveapi>
Formulas API <netscript/netscriptformulasapi>
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

@@ -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::
{
agility: Agility stat
agilityEquipMult: Agility multiplier from equipment. Decimal form
agilityAscensionMult: Agility multiplier from ascension. Decimal form
augmentations: Array of names of all owned Augmentations
charisma: Charisma stat
charismaEquipMult: Charisma multiplier from equipment. Decimal form
charismaAscensionMult: Charisma multiplier from ascension. Decimal form
defense: Defense stat
defenseEquipMult: Defense multiplier from equipment. Decimal form
defenseAscensionMult: Defense multiplier from ascension. Decimal form
dexterity: Dexterity stat
dexterityEquipMult: Dexterity multiplier from equipment. Decimal form
dexterityAscensionMult: Dexterity multiplier from ascension. Decimal form
equipment: Array of names of all owned Non-Augmentation Equipment
hacking: Hacking stat
hackingEquipMult: Hacking multiplier from equipment. Decimal form
hackingAscensionMult: Hacking multiplier from ascension. Decimal form
strength: Strength stat
strengthEquipMult: Strength multiplier from equipment. Decimal form
strengthAscensionMult: Strength multiplier from ascension. Decimal form
task: Name of currently assigned task
name: Name of this member.
task: Name of currently assigned task.
earnedRespect: Total amount of respect earned by this member.
hack: Hacking stat
str: Strength stat
def: Defense stat
dex: Dexterity stat
agi: Agility stat
cha: Charisma stat
hack_exp: Hacking experience
str_exp: Strength experience
def_exp: Defense experience
dex_exp: Dexterity experience
agi_exp: Agility experience
cha_exp: Charisma experience
hack_mult: Hacking multiplier from equipment. Decimal form
str_mult: Strength multiplier from equipment. Decimal form
def_mult: Defense multiplier from equipment. Decimal form
dex_mult: Dexterity multiplier from equipment. Decimal form
agi_mult: Agility multiplier from equipment. Decimal form
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

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

View File

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

View File

@@ -3,6 +3,8 @@ getCharacterInformation() Netscript Function
.. js:function:: getCharacterInformation()
.. warning:: This function is deprecated.
: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.

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()
.. warning:: This function is deprecated.
: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.

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="mainmenu-container">
<!-- Main menu -->
<ul id="mainmenu" class="mainmenu">
<ul id="mainmenu" class="mainmenu noscrollbar">
<!-- Hacking dropdown -->
<li id="hacking-menu-header-li">
<button id="hacking-menu-header" class="mainmenu-accordion-header"> Hacking </button>
@@ -261,16 +261,16 @@
<div id="infiltration-left-panel">
<p id="infiltration-level-text"> </p>
<div id="infiltration-buttons">
<a class="a-link-button tooltip" id="infiltration-kill"> </a>
<a class="a-link-button tooltip" id="infiltration-knockout"> </a>
<a class="a-link-button tooltip" id="infiltration-stealthknockout"> </a>
<a class="a-link-button tooltip" id="infiltration-assassinate"> </a>
<a class="a-link-button tooltip" id="infiltration-hacksecurity"> </a>
<a class="a-link-button tooltip" id="infiltration-destroysecurity"> </a>
<a class="a-link-button tooltip" id="infiltration-sneak"> </a>
<a class="a-link-button tooltip" id="infiltration-pickdoor"> </a>
<a class="a-link-button tooltip" id="infiltration-bribe"> </a>
<a class="a-link-button tooltip" id="infiltration-escape"> </a>
<button class="a-link-button tooltip" id="infiltration-kill"> </button>
<button class="a-link-button tooltip" id="infiltration-knockout"> </button>
<button class="a-link-button tooltip" id="infiltration-stealthknockout"> </button>
<button class="a-link-button tooltip" id="infiltration-assassinate"> </button>
<button class="a-link-button tooltip" id="infiltration-hacksecurity"> </button>
<button class="a-link-button tooltip" id="infiltration-destroysecurity"> </button>
<button class="a-link-button tooltip" id="infiltration-sneak"> </button>
<button class="a-link-button tooltip" id="infiltration-pickdoor"> </button>
<button class="a-link-button tooltip" id="infiltration-bribe"> </button>
<button class="a-link-button tooltip" id="infiltration-escape"> </button>
</div>
</div>
<div id="infiltration-right-panel">
@@ -387,7 +387,7 @@
<!-- Game Options -->
<div id="game-options-container" class="popup-box-container">
<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>
<br/>
<div id="game-options-left-panel">
@@ -511,6 +511,16 @@
<input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys">
</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 -->
<fieldset>
<label for="settingsLocale" class="tooltip">Locale:
@@ -549,6 +559,7 @@
<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/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>
<button id="save-game-link" class="a-link-button"> Save 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:dev": "webpack --watch --mode development"
},
"version": "0.50.1"
"version": "0.51.1"
}

View File

@@ -8,6 +8,7 @@ import { Factions, factionExists } from "./Faction/Factions";
import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player";
import { hackWorldDaemon, redPillFlag } from "./RedPill";
import { calculateHospitalizationCost } from "./Hospital/Hospital";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
@@ -51,6 +52,9 @@ import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
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";
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) {
this.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank");
} 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);
@@ -725,9 +729,10 @@ Bladeburner.prototype.completeAction = function() {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage;
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp);
this.moneyLost += cost;
}
}
var logLossText = "";
@@ -797,9 +802,10 @@ Bladeburner.prototype.completeAction = function() {
if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp);
this.moneyLost += cost;
}
}
teamLossMax = Math.floor(teamCount);
@@ -902,7 +908,7 @@ Bladeburner.prototype.completeAction = function() {
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
this.startAction(this.action);
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;
}
@@ -1256,7 +1262,7 @@ Bladeburner.prototype.createContent = function() {
// Console
DomElems.consoleDiv = createElement("div", {
class:"bladeburner-console-div",
clickListener:()=>{
clickListener:() => {
if (DomElems.consoleInput instanceof Element) {
DomElems.consoleInput.focus();
}
@@ -1264,12 +1270,12 @@ Bladeburner.prototype.createContent = function() {
}
});
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.consoleInputHeader = createElement("pre", {innerText:"> "});
DomElems.consoleInput = createElement("input", {
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);
@@ -1330,7 +1336,7 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewStaminaHelpTip = createElement("div", {
class:"help-tip",
innerText:"?",
clickListener: ()=> {
clickListener: () => {
dialogBoxCreate("Performing actions will use up your stamina.<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 " +
@@ -1358,7 +1364,7 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewEstPopHelpTip = createElement("div", {
innerText:"?", class:"help-tip",
clickListener:()=>{
clickListener:() => {
dialogBoxCreate("The success rate of your contracts/operations depends on " +
"the population of Synthoids in your current city. " +
"The success rate that is shown to you is only an estimate, " +
@@ -1421,12 +1427,12 @@ Bladeburner.prototype.createOverviewContent = function() {
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(createElement("a", {
innerHTML:"Travel", class:"a-link-button", display:"inline-block",
clickListener:()=>{
clickListener:() => {
var popupId = "bladeburner-travel-popup-cancel-btn";
var popupArguments = [];
popupArguments.push(createElement("a", { // Cancel Button
innerText:"Cancel", class:"a-link-button",
clickListener:()=>{
clickListener:() => {
removeElementById(popupId); return false;
}
}))
@@ -1445,7 +1451,7 @@ Bladeburner.prototype.createOverviewContent = function() {
*/
class:"cmpy-mgmt-find-employee-option",
innerText:BladeburnerConstants.CityNames[i],
clickListener:()=>{
clickListener:() => {
inst.city = BladeburnerConstants.CityNames[i];
removeElementById(popupId);
inst.updateOverviewContent();
@@ -1468,7 +1474,7 @@ Bladeburner.prototype.createOverviewContent = function() {
DomElems.overviewDiv.appendChild(createElement("a", {
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",
clickListener:()=>{
clickListener:() => {
if (bladeburnerFac.isMember) {
Engine.loadFactionContent();
displayFactionContent(bladeburnersFactionName);
@@ -1512,7 +1518,7 @@ Bladeburner.prototype.createActionAndSkillsContent = function() {
DomElems.actionAndSkillsDiv.appendChild(createElement("a", {
innerText:buttons[i],
class:currTab === buttons[i].toLowerCase() ? "bladeburner-nav-button-inactive" : "bladeburner-nav-button",
clickListener:()=>{
clickListener:() => {
DomElems.currentTab = buttons[i].toLowerCase();
inst.createActionAndSkillsContent();
return false;
@@ -1764,15 +1770,16 @@ Bladeburner.prototype.updateOverviewContent = function() {
if (!routing.isOn(Page.Bladeburner)) {return;}
DomElems.overviewRank.childNodes[0].nodeValue = "Rank: " + formatNumber(this.rank, 2);
DomElems.overviewStamina.innerText = "Stamina: " + formatNumber(this.stamina, 3) + " / " + formatNumber(this.maxStamina, 3);
DomElems.overviewGen1.innerHTML =
"Stamina Penalty: " + formatNumber((1-this.calculateStaminaPenalty())*100, 1) + "%<br><br>" +
"Team Size: " + formatNumber(this.teamSize, 0) + "<br>" +
"Team Members Lost: " + formatNumber(this.teamLost, 0) + "<br><br>" +
"Num Times Hospitalized: " + this.numHosp + "<br>" +
"Money Lost From Hospitalizations: " + numeralWrapper.format(this.moneyLost, "$0.000a") + "<br><br>" +
"Current City: " + this.city + "<br>";
ReactDOM.render(<>
Stamina Penalty: {formatNumber((1-this.calculateStaminaPenalty())*100, 1)}%<br /><br />
Team Size: {formatNumber(this.teamSize, 0)}<br />
Team Members Lost: {formatNumber(this.teamLost, 0)}<br /><br />
Num Times Hospitalized: {this.numHosp}<br />
Money Lost From Hospitalizations: {Money(this.moneyLost)}<br /><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.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos);
DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0);
@@ -1898,7 +1905,7 @@ Bladeburner.prototype.updateGeneralActionsUIElement = function(el, action) {
el.appendChild(createElement("a", {
innerText:"Start", class: "a-link-button",
margin:"3px", padding:"3px",
clickListener:()=>{
clickListener:() => {
this.action.type = ActionTypes[action.name];
this.action.name = action.name;
this.startAction(this.action);
@@ -1939,7 +1946,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
el.appendChild(createElement("a", {
innerText:"Start", class: "a-link-button",
padding:"3px", margin:"3px",
clickListener:()=>{
clickListener:() => {
this.action.type = ActionTypes.Contract;
this.action.name = action.name;
this.startAction(this.action);
@@ -1963,7 +1970,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the contract" : "",
display:"inline",
clickListener:()=>{
clickListener:() => {
++action.level;
if (isActive) {this.startAction(this.action);} // Restart Action
this.updateContractsUIElement(el, action);
@@ -1975,7 +1982,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the contract" : "",
display:"inline",
clickListener:()=>{
clickListener:() => {
--action.level;
if (isActive) {this.startAction(this.action);} // Restart Action
this.updateContractsUIElement(el, action);
@@ -1989,8 +1996,7 @@ Bladeburner.prototype.updateContractsUIElement = function(el, action) {
display:"inline-block",
innerHTML:action.desc + "\n\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" +
"Contracts remaining: " + Math.floor(action.count) + "\n" +
"Successes: " + action.successes + "\n" +
"Failures: " + action.failures,
@@ -2038,7 +2044,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
el.appendChild(createElement("a", {
innerText:"Start", class: "a-link-button",
margin:"3px", padding:"3px",
clickListener:()=>{
clickListener:() => {
this.action.type = ActionTypes.Operation;
this.action.name = action.name;
this.startAction(this.action);
@@ -2049,7 +2055,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
el.appendChild(createElement("a", {
innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button",
margin:"3px", padding:"3px",
clickListener:()=>{
clickListener:() => {
var popupId = "bladeburner-operation-set-team-size-popup";
var txt = createElement("p", {
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", {
innerText:"Confirm", class:"a-link-button",
clickListener:()=>{
clickListener:() => {
var num = Math.round(parseFloat(input.value));
if (isNaN(num)) {
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", {
innerText:"Cancel", class:"a-link-button",
clickListener:()=>{
clickListener:() => {
removeElementById(popupId);
return false;
}
@@ -2101,7 +2107,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "",
display:"inline",
clickListener:()=>{
clickListener:() => {
++action.level;
if (isActive) {this.startAction(this.action);} // Restart Action
this.updateOperationsUIElement(el, action);
@@ -2113,7 +2119,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
padding:"2px", margin:"2px",
tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "",
display:"inline",
clickListener:()=>{
clickListener:() => {
--action.level;
if (isActive) {this.startAction(this.action);} // Restart Action
this.updateOperationsUIElement(el, action);
@@ -2129,7 +2135,7 @@ Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
display:"inline-block",
innerHTML:action.desc + "\n\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" +
"Successes: " + action.successes + "\n" +
"Failures: " + action.failures,
@@ -2190,7 +2196,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
el.appendChild(createElement("a", { // Start button
innerText:"Start", margin:"3px", padding:"3px",
class:hasReqdRank ? "a-link-button" : "a-link-button-inactive",
clickListener:()=>{
clickListener:() => {
this.action.type = ActionTypes.BlackOperation;
this.action.name = action.name;
this.startAction(this.action);
@@ -2201,7 +2207,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
el.appendChild(createElement("a", { // Set Team Size Button
innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button",
margin:"3px", padding:"3px",
clickListener:()=>{
clickListener:() => {
var popupId = "bladeburner-operation-set-team-size-popup";
var txt = createElement("p", {
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", {
innerText:"Confirm", class:"a-link-button",
clickListener:()=>{
clickListener:() => {
var num = Math.round(parseFloat(input.value));
if (isNaN(num)) {
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", {
innerText:"Cancel", class:"a-link-button",
clickListener:()=>{
clickListener:() => {
removeElementById(popupId);
return false;
}
@@ -2252,7 +2258,7 @@ Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
el.appendChild(createElement("p", {
display:"inline-block",
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);
el.appendChild(createElement("h2", { // Header
innerText:skill.name + " (Lvl " + currentLevel + ")", display:"inline-block"
}));
const nameDiv = createElement("div");
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 maxLvl = skill.maxLvl ? currentLevel >= skill.maxLvl : false;
@@ -2275,7 +2287,7 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
innerText:"Level", display:"inline-block",
class: canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive",
margin:"3px", padding:"3px",
clickListener:()=>{
clickListener:() => {
if (this.skillPoints < pointCost) {return;}
this.skillPoints -= pointCost;
this.upgradeSkill(skill);
@@ -2284,6 +2296,10 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
}
}));
appendLineBreaks(el, 2);
el.appendChild(createElement("p", {
display:"block",
innerText:`Level: ${currentLevel}`,
}));
if (maxLvl) {
el.appendChild(createElement("p", {
color:"red", display:"block",
@@ -2311,7 +2327,7 @@ Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
}
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) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);

89
src/Casino/CoinFlip.tsx Normal file
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 { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { BadRNG } from "./RNG";
import { Game } from "./Game";
type IProps = {
p: IPlayer;
}
type IState = {
investment: number;
result: any;
status: string;
}
const maxPlay = 10e3;
export class CoinFlip extends Game<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
investment: 1000,
result: <span> </span>,
status: '',
};
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 = 1000;
}
if (investment > maxPlay) {
investment = maxPlay;
}
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!",
});
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={() => this.play('H')} text={"Head!"} />
<StdButton onClick={() => this.play('T')} text={"Tail!"} />
<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;
}
}

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

@@ -0,0 +1,290 @@
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";
type IProps = {
p: IPlayer;
}
type IState = {
investment: number;
canPlay: boolean;
status: string | JSX.Element;
n: number;
lock: boolean;
strategy: Strategy;
}
const maxPlay = 1e6;
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 = 1000;
}
if (investment > maxPlay) {
investment = maxPlay
}
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={()=>this.play(Single(3))} /></td>
<td><StdButton text={"6"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(6))} /></td>
<td><StdButton text={"9"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(9))} /></td>
<td><StdButton text={"12"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(12))} /></td>
<td><StdButton text={"15"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(15))} /></td>
<td><StdButton text={"18"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(18))} /></td>
<td><StdButton text={"21"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(21))} /></td>
<td><StdButton text={"24"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(24))} /></td>
<td><StdButton text={"27"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(27))} /></td>
<td><StdButton text={"30"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(30))} /></td>
<td><StdButton text={"33"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(33))} /></td>
<td><StdButton text={"36"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(36))} /></td>
</tr>
<tr>
<td><StdButton text={"2"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(2))} /></td>
<td><StdButton text={"5"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(5))} /></td>
<td><StdButton text={"8"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(8))} /></td>
<td><StdButton text={"11"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(11))} /></td>
<td><StdButton text={"14"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(14))} /></td>
<td><StdButton text={"17"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(17))} /></td>
<td><StdButton text={"20"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(20))} /></td>
<td><StdButton text={"23"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(23))} /></td>
<td><StdButton text={"26"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(26))} /></td>
<td><StdButton text={"29"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(29))} /></td>
<td><StdButton text={"32"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(32))} /></td>
<td><StdButton text={"35"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(35))} /></td>
</tr>
<tr>
<td><StdButton text={"1"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(1))} /></td>
<td><StdButton text={"4"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(4))} /></td>
<td><StdButton text={"7"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(7))} /></td>
<td><StdButton text={"10"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(10))} /></td>
<td><StdButton text={"13"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(13))} /></td>
<td><StdButton text={"16"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(16))} /></td>
<td><StdButton text={"19"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(19))} /></td>
<td><StdButton text={"22"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(22))} /></td>
<td><StdButton text={"25"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(25))} /></td>
<td><StdButton text={"28"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(28))} /></td>
<td><StdButton text={"31"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(31))} /></td>
<td><StdButton text={"34"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(34))} /></td>
</tr>
<tr>
<td colSpan={4}><StdButton text={"1 to 12"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Third1)} /></td>
<td colSpan={4}><StdButton text={"13 to 24"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Third2)} /></td>
<td colSpan={4}><StdButton text={"25 to 36"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Third3)} /></td>
</tr>
<tr>
<td colSpan={2}><StdButton text={"Red"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Red)} /></td>
<td colSpan={2}><StdButton text={"Black"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Black)} /></td>
<td colSpan={2}><StdButton text={"Odd"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Odd)} /></td>
<td colSpan={2}><StdButton text={"Even"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Even)} /></td>
<td colSpan={2}><StdButton text={"High"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.High)} /></td>
<td colSpan={2}><StdButton text={"Low"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Low)} /></td>
</tr>
<tr>
<td><StdButton text={"0"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(0))} /></td>
</tr>
</tbody>
</table>
</>
}
}

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

@@ -0,0 +1,234 @@
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";
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 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 = 1000;
}
if (investment > maxPlay) {
investment = maxPlay;
}
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={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/

View File

@@ -6,7 +6,7 @@
import { IMap } from "./types";
export let CONSTANTS: IMap<any> = {
Version: "0.50.2",
Version: "0.51.2",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@@ -228,18 +228,16 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate:
`
v0.50.2 - 2021-03-25 Everyone asked for this one. (hydroflame)
v0.51.2 - 2021-04-09 Vegas, Baby! (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.
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.
* New shortcut, Alt + b, brings you to bladeburner
* New shortcut, Alt + g, brings you to gang
* Link to discord added under options
* 'getMemberInformation' doc updated, oops
* tech vendor now handle max ram and cores.
`
}

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 { Player } from "../Player";
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 { formatNumber } from "../../utils/StringHelperFunctions";
import { numeralWrapper } from "../ui/numeralFormat";
//Posts a "help" message if connected to DarkWeb
export function checkIfConnectedToDarkweb(): void {
@@ -49,7 +52,7 @@ export function executeDarkwebTerminalCommand(commandArray: string[]): void {
function listAllDarkwebItems() {
for(const key in DarkWebItems) {
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 {
program: string;
@@ -10,9 +10,4 @@ export class DarkWebItem {
this.price = price;
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 { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { Money } from "./ui/React/Money";
import React from "react";
import ReactDOM from "react-dom";
@@ -611,12 +612,16 @@ class DevMenuComponent extends Component {
}
viewStockCaps() {
let text = "<table><tbody><tr><th>Stock</th><th>Price cap</th></tr>";
let stocks = [];
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(text);
dialogBoxCreate(<table><tbody><tr><th>Stock</th><th>Price cap</th></tr>
{stocks}
</tbody></table>);
}
sleeveMaxAllShock() {
@@ -703,8 +708,11 @@ class DevMenuComponent extends Component {
<h2>Generic</h2>
</div>
<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(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>
</div>
<div className="row">

View File

@@ -25,6 +25,8 @@ import {
Generic_fromJSON
} from "../../utils/JSONReviver";
import { formatNumber } from "../../utils/StringHelperFunctions";
import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import {
yesNoBoxCreate,
yesNoBoxGetYesButton,
@@ -108,10 +110,12 @@ export function purchaseAugmentationBoxCreate(aug, fac) {
yesNoBoxClose();
});
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
aug.info + "<br><br>" +
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
yesNoBoxCreate(<>
<h2>{aug.name}</h2><br />
<div dangerouslySetInnerHTML={{__html: aug.info}}></div><br /><br />
<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

View File

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

View File

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

View File

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

View File

@@ -35,6 +35,14 @@ import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
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
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) {
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:`,
`Hacking: ${numeralWrapper.format(res.hack, "0.000%")}`,
`Strength: ${numeralWrapper.format(res.str, "0.000%")}`,
`Defense: ${numeralWrapper.format(res.def, "0.000%")}`,
`Dexterity: ${numeralWrapper.format(res.dex, "0.000%")}`,
`Agility: ${numeralWrapper.format(res.agi, "0.000%")}`,
`Charisma: ${numeralWrapper.format(res.cha, "0.000%")}`].join("<br>"));
`Hacking: ${numeralWrapper.formatPercentage(res.hack, 3)}`,
`Strength: ${numeralWrapper.formatPercentage(res.str, 3)}`,
`Defense: ${numeralWrapper.formatPercentage(res.def, 3)}`,
`Dexterity: ${numeralWrapper.formatPercentage(res.dex, 3)}`,
`Agility: ${numeralWrapper.formatPercentage(res.agi, 3)}`,
`Charisma: ${numeralWrapper.formatPercentage(res.cha, 3)}`].join("<br>"));
} else {
workerScript.log(`Ascended Gang member ${memberObj.name}`);
}
@@ -1044,14 +1052,14 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
type:"text", placeholder:"Filter gang members",
class: "text-input",
value:initialFilter,
onkeyup:()=>{
onkeyup:() => {
var filterValue = UIElems.gangMemberUpgradeBoxFilter.value.toString();
this.createGangMemberUpgradeBox(player, filterValue);
}
});
UIElems.gangMemberUpgradeBoxDiscount = createElement("p", {
innerText: "Discount: -" + numeralWrapper.format(1 - 1 / this.getDiscount(), "0.00%"),
innerText: "Discount: -" + numeralWrapper.formatPercentage(1 - 1 / this.getDiscount()),
marginLeft: "6px",
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."
@@ -1178,10 +1186,10 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
let upg = upgradeArray[j];
(function (upg, div, memberObj, i, gang) {
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",
fontSize:"11px",
clickListener:()=>{
clickListener:() => {
memberObj.buyUpgrade(upg, player, gangObj);
return false;
}
@@ -1266,7 +1274,7 @@ Gang.prototype.displayGangContent = function(player) {
// Back button
UIElems.gangContainer.appendChild(createElement("a", {
class:"a-link-button", display:"inline-block", innerText:"Back",
clickListener:()=>{
clickListener:() => {
Engine.loadFactionContent();
displayFactionContent(facName);
return false;
@@ -1277,7 +1285,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.managementButton = createElement("a", {
id:"gang-management-subpage-button", class:"a-link-button-inactive",
display:"inline-block", innerHTML: "Gang Management (Alt+1)",
clickListener:()=>{
clickListener:() => {
UIElems.gangManagementSubpage.style.display = "block";
UIElems.gangTerritorySubpage.style.display = "none";
UIElems.managementButton.classList.toggle("a-link-button-inactive");
@@ -1340,7 +1348,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangRecruitMemberButton = createElement("a", {
id: "gang-management-recruit-member-btn", class:"a-link-button-inactive",
innerHTML:"Recruit Gang Member", display:"inline-block", margin:"10px",
clickListener:()=>{
clickListener:() => {
const popupId = "recruit-gang-member-popup";
let yesBtn;
@@ -1408,7 +1416,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangExpandAllButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerHTML:"Expand All",
clickListener:()=>{
clickListener:() => {
var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) {
var hdr = allHeaders[i];
@@ -1422,7 +1430,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangCollapseAllButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerHTML:"Collapse All",
clickListener:()=>{
clickListener:() => {
var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) {
var hdr = allHeaders[i];
@@ -1436,7 +1444,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangMemberFilter = createElement("input", {
type:"text", placeholder:"Filter gang members", margin:"5px", padding:"5px",
class:"text-input",
onkeyup:()=>{
onkeyup:() => {
this.displayGangMemberList();
}
});
@@ -1584,13 +1592,13 @@ Gang.prototype.updateGangContent = function() {
if (UIElems.gangMemberUpgradeBoxOpened) {
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") {
// Territory Warfare Clash Chance
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
UIElems.gangTerritoryWarfareCheckbox.checked = this.territoryWarfareEngaged;
@@ -1623,7 +1631,7 @@ Gang.prototype.updateGangContent = function() {
const clashVictoryChance = playerPower / (gangTerritoryInfo.power + playerPower);
let newHTML = `<u>${gangname}</u><br>Power: ${formatNumber(gangTerritoryInfo.power, 6)}<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;
}
}
@@ -1641,8 +1649,8 @@ Gang.prototype.updateGangContent = function() {
removeChildrenFromElement(UIElems.gangInfo);
UIElems.gangInfo.appendChild(createElement("p", { // Respect
display: "inline-block",
innerText: "Respect: " + numeralWrapper.format(this.respect, '0.00000a') +
" (" + numeralWrapper.format(5*this.respectGainRate, '0.00000a') + " / sec)",
innerText: "Respect: " + numeralWrapper.formatRespect(this.respect) +
" (" + numeralWrapper.formatRespect(5*this.respectGainRate) + " / sec)",
tooltip: "Represents the amount of respect your gang has from other gangs and criminal " +
"organizations. Your respect affects the amount of money " +
"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
display: "inline-block",
innerText: "Wanted Level: " + numeralWrapper.format(this.wanted, '0.00000a') +
" (" + numeralWrapper.format(5*this.wantedGainRate, '0.00000a') + " / sec)",
innerText: "Wanted Level: " + numeralWrapper.formatWanted(this.wanted) +
" (" + numeralWrapper.formatWanted(5*this.wantedGainRate) + " / sec)",
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 " +
"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("p", { // Money gain rate
display: "inline-block",
innerText: `Money gain rate: ${numeralWrapper.format(5 * this.moneyGainRate, "$0.000a")} / sec`,
}));
const d0 = createElement("div");
ReactDOM.render(<p style={{'display': 'inline-block'}}>Money gain rate: {MoneyRate(5 * this.moneyGainRate)}</p>, d0);
UIElems.gangInfo.appendChild(d0);
UIElems.gangInfo.appendChild(createElement("br"));
// Fix some rounding issues graphically
@@ -1692,10 +1699,9 @@ Gang.prototype.updateGangContent = function() {
}));
UIElems.gangInfo.appendChild(createElement("br"));
UIElems.gangInfo.appendChild(createElement("p", { // Faction reputation
display:"inline-block",
innerText:"Faction reputation: " + numeralWrapper.format(rep, '0.000a')
}));
const d1 = createElement("div");
ReactDOM.render(<p style={{'display': 'inline-block'}}>Faction reputation: {Reputation(rep)}</p>, d1);
UIElems.gangInfo.appendChild(d1);
UIElems.gangInfo.appendChild(createElement("br"));
const CyclesPerSecond = 1000 / Engine._idleSpeed;
@@ -1761,12 +1767,12 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
const statsDiv = createElement("div", {
class: "gang-member-info-div",
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)`,
`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)`,
`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)`,
`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)`,
`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)`,
`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>"),
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.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.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.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.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.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;
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",
"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",
`Hacking: +${numeralWrapper.format(ascendBenefits.hack, "0.00%")}`,
`Strength: +${numeralWrapper.format(ascendBenefits.str, "0.00%")}`,
`Defense: +${numeralWrapper.format(ascendBenefits.def, "0.00%")}`,
`Dexterity: +${numeralWrapper.format(ascendBenefits.dex, "0.00%")}`,
`Agility: +${numeralWrapper.format(ascendBenefits.agi, "0.00%")}`,
`Charisma: +${numeralWrapper.format(ascendBenefits.cha, "0.00%")}`].join("\n"),
`Hacking: +${numeralWrapper.formatPercentage(ascendBenefits.hack)}`,
`Strength: +${numeralWrapper.formatPercentage(ascendBenefits.str)}`,
`Defense: +${numeralWrapper.formatPercentage(ascendBenefits.def)}`,
`Dexterity: +${numeralWrapper.formatPercentage(ascendBenefits.dex)}`,
`Agility: +${numeralWrapper.formatPercentage(ascendBenefits.agi)}`,
`Charisma: +${numeralWrapper.formatPercentage(ascendBenefits.cha)}`].join("\n"),
});
const confirmBtn = createElement("button", {
class: "std-button",
@@ -1874,7 +1880,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
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(gainInfo);
@@ -1907,12 +1913,12 @@ Gang.prototype.updateGangMemberDisplayElement = function(memberObj) {
var stats = document.getElementById(name + "gang-member-stats-text");
if (stats) {
stats.innerText =
[`Hacking: ${formatNumber(memberObj.hack, 0)} (${numeralWrapper.format(memberObj.hack_exp, '(0.00a)')} exp)`,
`Strength: ${formatNumber(memberObj.str, 0)} (${numeralWrapper.format(memberObj.str_exp, '(0.00a)')} exp)`,
`Defense: ${formatNumber(memberObj.def, 0)} (${numeralWrapper.format(memberObj.def_exp, '(0.00a)')} exp)`,
`Dexterity: ${formatNumber(memberObj.dex, 0)} (${numeralWrapper.format(memberObj.dex_exp, '(0.00a)')} exp)`,
`Agility: ${formatNumber(memberObj.agi, 0)} (${numeralWrapper.format(memberObj.agi_exp, '(0.00a)')} exp)`,
`Charisma: ${formatNumber(memberObj.cha, 0)} (${numeralWrapper.format(memberObj.cha_exp, '(0.00a)')} exp)`].join("\n");
[`Hacking: ${formatNumber(memberObj.hack, 0)} (${numeralWrapper.formatExp(memberObj.hack_exp)} exp)`,
`Strength: ${formatNumber(memberObj.str, 0)} (${numeralWrapper.formatExp(memberObj.str_exp)} exp)`,
`Defense: ${formatNumber(memberObj.def, 0)} (${numeralWrapper.formatExp(memberObj.def_exp)} exp)`,
`Dexterity: ${formatNumber(memberObj.dex, 0)} (${numeralWrapper.formatExp(memberObj.dex_exp)} exp)`,
`Agility: ${formatNumber(memberObj.agi, 0)} (${numeralWrapper.formatExp(memberObj.agi_exp)} exp)`,
`Charisma: ${formatNumber(memberObj.cha, 0)} (${numeralWrapper.formatExp(memberObj.cha_exp)} exp)`].join("\n");
}
// Update tooltip for stat multipliers
@@ -1921,23 +1927,25 @@ Gang.prototype.updateGangMemberDisplayElement = function(memberObj) {
const statsDiv = panel["statsDiv"];
if (statsDiv) {
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)`,
`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)`,
`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)`,
`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)`,
`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)`,
`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>");
[`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.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.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.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.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.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
var gainInfo = document.getElementById(name + "gang-member-gain-info");
if (gainInfo) {
gainInfo.innerHTML =
[`Money: ${numeralWrapper.format(5*memberObj.calculateMoneyGain(this), '$0.000a')} / sec`,
`Respect: ${numeralWrapper.format(5*memberObj.calculateRespectGain(this), '0.00000a')} / sec`,
`Wanted Level: ${numeralWrapper.format(5*memberObj.calculateWantedLevelGain(this), '0.00000a')} / sec`,
`Total Respect Earned: ${numeralWrapper.format(memberObj.earnedRespect, '0.00000a')}`].join("<br>");
const data = [
[`Money:`, MoneyRate(5*memberObj.calculateMoneyGain(this))],
[`Respect:`, `${numeralWrapper.formatRespect(5*memberObj.calculateRespectGain(this))} / sec`],
[`Wanted Level:`, `${numeralWrapper.formatWanted(5*memberObj.calculateWantedLevelGain(this))} / sec`],
[`Total Respect:`, `${numeralWrapper.formatRespect(memberObj.earnedRespect)}`],
];
ReactDOM.render(StatsTable(data), gainInfo);
}
// Update selector to have the correct task

View File

@@ -1,17 +1,19 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { Player } from "./Player";
import { IPlayer } from "./PersonObjects/IPlayer";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
import { Server } from "./Server/Server";
import { HacknetServer } from "./Hacknet/HacknetServer";
/**
* 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 difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = hackFactor * Player.hacking_skill;
const skillMult = hackFactor * player.hacking_skill;
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 < 0) { return 0; }
@@ -22,14 +24,14 @@ export function calculateHackingChance(server: Server): number {
* Returns the amount of hacking experience the player will gain upon
* successfully hacking a server
*/
export function calculateHackingExpGain(server: Server): number {
export function calculateHackingExpGain(server: Server, player: IPlayer): number {
const baseExpGain = 3;
const diffFactor = 0.3;
if (server.baseDifficulty == null) {
server.baseDifficulty = server.hackDifficulty;
}
let expGain = baseExpGain;
expGain += (server.baseDifficulty * Player.hacking_exp_mult * diffFactor);
expGain += (server.baseDifficulty * player.hacking_exp_mult * diffFactor);
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
* 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
const balanceFactor = 240;
const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = (Player.hacking_skill - (server.requiredHackingSkill - 1)) / Player.hacking_skill;
const percentMoneyHacked = difficultyMult * skillMult * Player.hacking_money_mult / balanceFactor;
const skillMult = (player.hacking_skill - (server.requiredHackingSkill - 1)) / player.hacking_skill;
const percentMoneyHacked = difficultyMult * skillMult * player.hacking_money_mult / balanceFactor;
if (percentMoneyHacked < 0) { return 0; }
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
*/
export function calculateHackingTime(server: Server, hack: number): number {
export function calculateHackingTime(server: Server, player: IPlayer): number {
const difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
const baseDiff = 500;
const baseSkill = 50;
const diffFactor = 2.5;
if (hack == null) {hack = Player.hacking_skill;}
let skillFactor = (diffFactor * difficultyMult + baseDiff);
// tslint:disable-next-line
skillFactor /= (hack + baseSkill);
skillFactor /= (player.hacking_skill + baseSkill);
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;
}
@@ -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
*/
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
return growTimeMultiplier * calculateHackingTime(server, hack);
return growTimeMultiplier * calculateHackingTime(server, player);
}
/**
* 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
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
*/
import {
HacknetNode,
BaseCostForHacknetNode,
HacknetNodePurchaseNextMult,
HacknetNodeMaxLevel,
HacknetNodeMaxRam,
HacknetNodeMaxCores
} from "./HacknetNode";
import {
HacknetServer,
BaseCostForHacknetServer,
HacknetServerPurchaseMult,
HacknetServerMaxLevel,
HacknetServerMaxRam,
HacknetServerMaxCores,
HacknetServerMaxCache,
MaxNumberHacknetServers
} from "./HacknetServer";
import { HacknetNode } from "./HacknetNode";
import { calculateNodeCost } from "./formulas/HacknetNodes";
import { calculateServerCost } from "./formulas/HacknetServers";
import { HacknetNodeConstants, HacknetServerConstants } from "./data/Constants";
import { HacknetServer } from "./HacknetServer";
import { HashManager } from "./HashManager";
import { HashUpgrades } from "./HashUpgrades";
@@ -105,24 +92,15 @@ export function purchaseHacknet() {
}
export function hasMaxNumberHacknetServers() {
return hasHacknetServers() && Player.hacknetNodes.length >= MaxNumberHacknetServers;
return hasHacknetServers() && Player.hacknetNodes.length >= HacknetServerConstants.MaxServers;
}
export function getCostOfNextHacknetNode() {
// Cost increases exponentially based on how many you own
const numOwned = Player.hacknetNodes.length;
const mult = HacknetNodePurchaseNextMult;
return BaseCostForHacknetNode * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
return calculateNodeCost(Player.hacknetNodes.length+1, Player.hacknet_node_purchase_cost_mult);
}
export function getCostOfNextHacknetServer() {
const numOwned = Player.hacknetNodes.length;
const mult = HacknetServerPurchaseMult;
if (numOwned >= MaxNumberHacknetServers) { return Infinity; }
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
return calculateServerCost(Player.hacknetNodes.length+1, Player.hacknet_node_purchase_cost_mult);
}
// 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);
// If we're at max level, return false
if (node.level >= (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) {
if (node.level >= (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) {
return false;
}
// If the number of specified upgrades would exceed the max level, calculate
// the maximum number of upgrades and use that
if (node.level + sanitizedLevels > (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel)) {
const diff = Math.max(0, (isServer ? HacknetServerMaxLevel : HacknetNodeMaxLevel) - node.level);
if (node.level + sanitizedLevels > (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) {
const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel) - node.level);
return purchaseLevelUpgrade(node, diff);
}
@@ -301,20 +279,20 @@ export function purchaseRamUpgrade(node, levels=1) {
const isServer = (node instanceof HacknetServer);
// Fail if we're already at max
if (node.ram >= (isServer ? HacknetServerMaxRam : HacknetNodeMaxRam)) {
if (node.ram >= (isServer ? HacknetServerConstants.MaxRam : HacknetNodeConstants.MaxRam)) {
return false;
}
// If the number of specified upgrades would exceed the max RAM, calculate the
// max possible number of upgrades and use that
if (isServer) {
if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerMaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetServerMaxRam / node.maxRam)));
if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerConstants.MaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetServerConstants.MaxRam / node.maxRam)));
return purchaseRamUpgrade(node, diff);
}
} else {
if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeMaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetNodeMaxRam / node.ram)));
if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeConstants.MaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetNodeConstants.MaxRam / node.ram)));
return purchaseRamUpgrade(node, diff);
}
}
@@ -340,14 +318,14 @@ export function purchaseCoreUpgrade(node, levels=1) {
const isServer = (node instanceof HacknetServer);
// Fail if we're already at max
if (node.cores >= (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) {
if (node.cores >= (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) {
return false;
}
// If the specified number of upgrades would exceed the max Cores, calculate
// the max possible number of upgrades and use that
if (node.cores + sanitizedLevels > (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores)) {
const diff = Math.max(0, (isServer ? HacknetServerMaxCores : HacknetNodeMaxCores) - node.cores);
if (node.cores + sanitizedLevels > (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) {
const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores) - node.cores);
return purchaseCoreUpgrade(node, diff);
}
@@ -374,8 +352,8 @@ export function purchaseCacheUpgrade(node, levels=1) {
}
// Fail if we're already at max
if (node.cache + sanitizedLevels > HacknetServerMaxCache) {
const diff = Math.max(0, HacknetServerMaxCache - node.cache);
if (node.cache + sanitizedLevels > HacknetServerConstants.MaxCache) {
const diff = Math.max(0, HacknetServerConstants.MaxCache - node.cache);
return purchaseCacheUpgrade(node, diff);
}

View File

@@ -9,29 +9,19 @@ import { IHacknetNode } from "./IHacknetNode";
import { CONSTANTS } from "../Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import {
calculateMoneyGainRate,
calculateLevelUpgradeCost,
calculateCoreUpgradeCost,
calculateRamUpgradeCost,
} from "./formulas/HacknetNodes";
import { HacknetNodeConstants } from "./data/Constants";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { Generic_fromJSON,
Generic_toJSON,
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 {
/**
* 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
calculateCoreUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels);
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;
return calculateCoreUpgradeCost(this.cores, levels, costMult);
}
// Get the cost to upgrade this Node's level
calculateLevelUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels);
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;
return calculateLevelUpgradeCost(this.level, levels, costMult);
}
// Get the cost to upgrade this Node's RAM
calculateRamUpgradeCost(levels: number=1, costMult: number): number {
const sanitizedLevels = Math.round(levels);
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;
return calculateRamUpgradeCost(this.ram, levels, costMult);
}
// 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
// Returns a boolean indicating whether new cores were successfully bought
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);
}
// Upgrade this Node's level, if possible
// Returns a boolean indicating whether the level was successfully updated
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);
}
@@ -186,14 +114,7 @@ export class HacknetNode implements IHacknetNode {
// Re-calculate this Node's production and update the moneyGainRatePerSecond prop
updateMoneyGainRate(prodMult: number): void {
//How much extra $/s is gained per level
var gainPerLevel = HacknetNodeMoneyGainPerLevel;
this.moneyGainRatePerSecond = (this.level * gainPerLevel) *
Math.pow(1.035, this.ram - 1) *
((this.cores + 5) / 6) *
prodMult *
BitNodeMultipliers.HacknetNodeMoney;
this.moneyGainRatePerSecond = calculateMoneyGainRate(this.level, this.ram, this.cores, prodMult);
if (isNaN(this.moneyGainRatePerSecond)) {
this.moneyGainRatePerSecond = 0;
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 { BaseServer } from "../Server/BaseServer";
import { RunningScript } from "../Script/RunningScript";
import { HacknetServerConstants } from "./data/Constants";
import {
calculateHashGainRate,
calculateLevelUpgradeCost,
calculateRamUpgradeCost,
calculateCoreUpgradeCost,
calculateCacheUpgradeCost,
} from "./formulas/HacknetServers";
import { createRandomIp } from "../../utils/IPAddress";
@@ -17,29 +25,6 @@ import {
Reviver
} 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 {
adminRights?: boolean;
hostname: string;
@@ -73,7 +58,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
// How long this HacknetServer has existed, in seconds
onlineTimeSeconds: number = 0;
// Total number of hashes earned by this
// Total number of hashes earned by this server
totalHashesGenerated: number = 0;
constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) {
@@ -84,96 +69,19 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
}
calculateCacheUpgradeCost(levels: number): number {
const sanitizedLevels = Math.round(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;
return calculateCacheUpgradeCost(this.cache, levels);
}
calculateCoreUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels);
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;
return calculateCoreUpgradeCost(this.cores, levels, costMult);
}
calculateLevelUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels);
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;
return calculateLevelUpgradeCost(this.level, levels, costMult);
}
calculateRamUpgradeCost(levels: number, costMult: number): number {
const sanitizedLevels = Math.round(levels);
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;
return calculateRamUpgradeCost(this.maxRam, levels, costMult);
}
// 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 {
this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels));
this.cache = Math.min(HacknetServerConstants.MaxCache, Math.round(this.cache + levels));
this.updateHashCapacity();
}
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);
}
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);
}
@@ -202,7 +110,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
for (let i = 0; i < levels; ++i) {
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);
return true;
@@ -221,14 +129,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
}
updateHashRate(prodMult: number): void {
const baseGain = HacknetServerHashesPerLevel * this.level;
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;
this.hashRate = calculateHashGainRate(this.level, this.ramUsed, this.maxRam, this.cores, prodMult)
if (isNaN(this.hashRate)) {
this.hashRate = 0;

View File

@@ -7,6 +7,7 @@
* those upgrades
*/
import { HashUpgrades } from "./HashUpgrades";
import { HashUpgrade } from "./HashUpgrade";
import { IMap } from "../types";
import { Generic_fromJSON,
@@ -67,11 +68,20 @@ export class HashManager {
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
*/
getUpgradeCost(upgName: string): number {
const upg = HashUpgrades[upgName];
const upg = this.getUpgrade(upgName);
const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null) {
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
perform distributed cyberattacks without the fear of being traced.
</p>
<br />
<p className={"hacknet-general-info"}>
{this.getSecondParagraph()}
</p>
<br />
<p className={"hacknet-general-info"}>
{this.getThirdParagraph()}
</p>

View File

@@ -4,11 +4,7 @@
*/
import React from "react";
import {
HacknetNodeMaxLevel,
HacknetNodeMaxRam,
HacknetNodeMaxCores
} from "../HacknetNode";
import { HacknetNodeConstants } from "../data/Constants";
import {
getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
@@ -21,6 +17,8 @@ import {
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
export class HacknetNode extends React.Component {
render() {
@@ -29,21 +27,21 @@ export class HacknetNode extends React.Component {
const recalculate = this.props.recalculate;
// Upgrade Level Button
let upgradeLevelText, upgradeLevelClass;
if (node.level >= HacknetNodeMaxLevel) {
upgradeLevelText = "MAX LEVEL";
let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetNodeConstants.MaxLevel) {
upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
} else {
const levelsToMax = HacknetNodeMaxLevel - node.level;
const levelsToMax = HacknetNodeConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult);
}
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)) {
upgradeLevelClass = "std-button-disabled";
} else {
@@ -53,28 +51,28 @@ export class HacknetNode extends React.Component {
const upgradeLevelOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeMaxLevel);
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
}
purchaseLevelUpgrade(node, numUpgrades);
recalculate();
return false;
}
let upgradeRamText, upgradeRamClass;
if (node.ram >= HacknetNodeMaxRam) {
upgradeRamText = "MAX RAM";
let upgradeRamContent, upgradeRamClass;
if (node.ram >= HacknetNodeConstants.MaxRam) {
upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
multiplier = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
} 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);
}
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)) {
upgradeRamClass = "std-button-disabled";
} else {
@@ -84,28 +82,28 @@ export class HacknetNode extends React.Component {
const upgradeRamOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeMaxRam);
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
}
purchaseRamUpgrade(node, numUpgrades);
recalculate();
return false;
}
let upgradeCoresText, upgradeCoresClass;
if (node.cores >= HacknetNodeMaxCores) {
upgradeCoresText = "MAX CORES";
let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetNodeConstants.MaxCores) {
upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
} else {
const levelsToMax = HacknetNodeMaxCores - node.cores;
const levelsToMax = HacknetNodeConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult);
}
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)) {
upgradeCoresClass = "std-button-disabled";
} else {
@@ -115,7 +113,7 @@ export class HacknetNode extends React.Component {
const upgradeCoresOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeMaxCores);
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
}
purchaseCoreUpgrade(node, numUpgrades);
recalculate();
@@ -132,25 +130,28 @@ export class HacknetNode extends React.Component {
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
{numeralWrapper.formatMoney(node.totalMoneyGenerated)} ({numeralWrapper.formatMoney(node.moneyGainRatePerSecond)} / sec)
{Money(node.totalMoneyGenerated)} ({MoneyRate(node.moneyGainRatePerSecond)})
</span>
</div>
<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}>
{upgradeLevelText}
{upgradeLevelContent}
</button>
</div>
<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}>
{upgradeRamText}
{upgradeRamContent}
</button>
</div>
<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}>
{upgradeCoresText}
{upgradeCoresContent}
</button>
</div>
</div>

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,14 +47,14 @@ Cities[CityName.Aevum].asciiArt = `
\\ 56 B
x \\ [summit university]
\\ \\ 28
\\ [snap fitness gym] x o--L------------
\\ [snap fitness gym] x o--L-----------N
K \\ /
\\ \\ P
\\ \\ Q [casino]
x 58 \\ / [travel agency]
\\ 94 95 o
90 x 59 o------o |
\\ / \\ | 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
o |
@@ -69,13 +69,13 @@ Cities[CityName.Aevum].asciiArt = `
| 34 x \\
[clarke inc.] C | \\ [world stock exchange]
| | \\
| | o-M-------Q--------o
| | o-M-------R--------o
[galactic cybersystems] G 35 x
| [watchdog security]
|
67 o
[the slums] O `
[the slums] P `
Cities[CityName.Chongqing].asciiArt = `
|
75 o

View File

@@ -21,6 +21,7 @@ import { SpecialServerIps } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings";
import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox";
import {
@@ -38,6 +39,7 @@ import { createElement } from "../../utils/uiHelpers/createElement";
import { createPopup } from "../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
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.
@@ -49,7 +51,7 @@ import { removeElementById } from "../../utils/uiHelpers/removeElementById";
*/
type TravelFunction = (to: CityName) => void;
export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
const cost = CONSTANTS.TravelCost;
const cost: number = CONSTANTS.TravelCost;
if (Settings.SuppressTravelConfirmation) {
travelFn(destination);
@@ -76,8 +78,8 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio
return false;
});
yesNoBoxCreate(`Would you like to travel to ${destination}? The trip will ` +
`cost ${numeralWrapper.formatMoney(cost)}`);
yesNoBoxCreate(<span>Would you like to travel to ${destination}? The trip will
cost {Money(cost)}.</span>);
}
/**
@@ -105,10 +107,9 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
yesNoTxtInpBoxClose();
});
yesNoTxtInpBoxCreate(
`Would you like to purchase a new server with ${ram} GB of RAM for ` +
`${numeralWrapper.formatMoney(cost)}?<br><br>Please enter the server hostname below:<br>`
);
yesNoTxtInpBoxCreate(<>Would you like to purchase a new server with {numeralWrapper.formatRAM(ram)} of RAM for {Money(cost)}?
<br /><br />Please enter the server hostname below:<br />
</>);
}
/**
@@ -197,7 +198,12 @@ export function createStartCorporationPopup(p: IPlayer) {
*/
export function createUpgradeHomeCoresPopup(p: IPlayer) {
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
const allCosts = [
@@ -236,12 +242,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
yesNoBoxClose();
});
yesNoBoxCreate(
"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>" +
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " +
"cost " + numeralWrapper.formatMoney(cost)
);
yesNoBoxCreate(<>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 />
Purchasing an additional core (for a total of {p.getHomeComputer().cpuCores + 1}) will
cost {Money(cost)}</>);
}
/**
@@ -256,6 +260,14 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
const noBtn = yesNoBoxGetNoButton();
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.addEventListener("click", ()=>{
purchaseRamForHomeComputer(cost, p);
@@ -267,11 +279,11 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
yesNoBoxClose();
});
yesNoBoxCreate(
"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 cost " + numeralWrapper.format(cost, '$0.000a')
);
yesNoBoxCreate(<>
Would you like to purchase additional RAM for your home computer? <br /><br />
This will upgrade your RAM from {numeralWrapper.formatRAM(ram)} to {numeralWrapper.formatRAM(ram*2)}. <br /><br />
This will cost {Money(cost)}
</>);
}

View File

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

View File

@@ -145,6 +145,11 @@ export const LocationsMetadata: IConstructorParams[] = [
name: LocationName.AevumWatchdogSecurity,
types: [LocationType.Company],
},
{
city: CityName.Aevum,
name: LocationName.AevumCasino,
types: [LocationType.Casino],
},
{
city: CityName.Chongqing,
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 { LocationName } from "../data/LocationNames";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
@@ -16,7 +17,7 @@ type IProps = {
}
export class LocationCity extends React.Component<IProps, any> {
render() {
asciiCity() {
const thiscity = this;
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>)
}
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 (
<>
{elems}
{Settings.DisableASCIIArt ? this.listCity() : this.asciiCity()}
</>
)
}

View File

@@ -23,6 +23,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
type IProps = {
engine: IEngine;
@@ -34,6 +36,10 @@ type IState = {
employedHere: boolean;
}
const blockStyleMarkup = {
display: "block",
}
export class CompanyLocation extends React.Component<IProps, IState> {
/**
* 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 &&
<div>
<p>Job Title: {this.jobTitle}</p>
<p>--------------------</p>
<br /><p style={blockStyleMarkup}>-------------------------</p><br />
<p className={"tooltip"}>
Company reputation: {numeralWrapper.format(this.company.playerReputation, "0,0.000")}
Company reputation: {Reputation(this.company.playerReputation)}
<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
</span>
</p>
<p>--------------------</p>
</p><br />
<br /><p style={blockStyleMarkup}>-------------------------</p><br />
<p className={"tooltip"}>
Company Favor: {numeralWrapper.format(this.company.favor, "0,0")}
Company Favor: {Favor(this.company.favor)}
<span className={"tooltiptext"}>
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
of favor you gain depends on how much reputation you have with the comapny.
</span>
</p>
</p><br />
<br /><p style={blockStyleMarkup}>-------------------------</p><br />
<StdButton
id={"foo-work-button-id"}
onClick={this.work}

View File

@@ -14,6 +14,7 @@ import { SpecialLocation } from "./SpecialLocation";
import { TechVendorLocation } from "./TechVendorLocation";
import { TravelAgencyLocation } from "./TravelAgencyLocation";
import { UniversityLocation } from "./UniversityLocation";
import { CasinoLocation } from "./CasinoLocation";
import { Location } from "../Location";
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;
}

View File

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

View File

@@ -7,9 +7,11 @@ import * as React from "react";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getHospitalizationCost } from "../../Hospital/Hospital";
import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox";
@@ -41,7 +43,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
}
getCost(): number {
return (this.props.p.max_hp - this.props.p.hp) * CONSTANTS.HospitalCostPerHp;
return getHospitalizationCost(this.props.p);
}
getHealed(e: React.MouseEvent<HTMLElement>): void {
@@ -59,7 +61,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
currHp: this.props.p.hp,
});
dialogBoxCreate(`You were healed to full health! The hospital billed you for ${numeralWrapper.formatMoney(cost)}`);
dialogBoxCreate(<>You were healed to full health! The hospital billed you for {Money(cost)}</>);
}
render() {
@@ -69,7 +71,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
<AutoupdatingStdButton
onClick={this.getHealed}
style={this.btnStyle}
text={`Get treatment for wounds - ${numeralWrapper.formatMoney(cost)}`}
text={<>Get treatment for wounds - {Money(cost)}</>}
/>
)
}

View File

@@ -18,6 +18,7 @@ import { getPurchaseServerCost } from "../../Server/ServerPurchases";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
type IProps = {
loc: Location;
@@ -70,7 +71,7 @@ export class TechVendorLocation extends React.Component<IProps, any> {
key={i}
onClick={() => createPurchaseServerPopup(i, this.props.p)}
style={this.btnStyle}
text={`Purchase ${i}GB Server - ${numeralWrapper.formatMoney(cost)}`}
text={<>Purchase {i}GB Server - {Money(cost)}</>}
/>
)
}
@@ -88,7 +89,7 @@ export class TechVendorLocation extends React.Component<IProps, any> {
<StdButton
onClick={this.purchaseTorRouter}
style={this.btnStyle}
text={`Purchase TOR Router - ${numeralWrapper.formatMoney(CONSTANTS.TorRouterCost)}`}
text={<>Purchase TOR Router - {Money(CONSTANTS.TorRouterCost)}</>}
/>
)

View File

@@ -1,7 +1,7 @@
/**
* React Subcomponent for displaying a location's UI, when that location is a Travel Agency
*
* This subcomponent renders all of the buttons for traveling to different cities
* TThis subcomponent renders all of the buttons for traveling to different cities
*/
import * as React from "react";
@@ -10,9 +10,11 @@ import { createTravelPopup } from "../LocationsHelpers";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
type IProps = {
p: IPlayer;
@@ -31,7 +33,7 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
this.btnStyle = { display: "block" };
}
render() {
asciiWorldMap() {
const thisTravelAgencyLocation = this;
function LocationLetter(props: any) {
@@ -48,7 +50,7 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
<div>
<p>
From here, you can travel to any other city! A ticket
costs {numeralWrapper.formatMoney(CONSTANTS.TravelCost)}
costs {Money(CONSTANTS.TravelCost)}.
</p>
<pre> ,_ . ._. _. .</pre>
<pre> , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~-</pre>
@@ -75,4 +77,42 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
</div>
)
}
listWorldMap() {
const travelBtns: React.ReactNode[] = [];
for (const key in CityName) {
const city = CityName[key];
// Skip current city
if (city === this.props.p.city) { continue; }
travelBtns.push(
<StdButton
key={city}
onClick={createTravelPopup.bind(null, city, this.props.travel)}
style={this.btnStyle}
text={`Travel to ${city}`}
/>
)
}
return (
<div>
<p>
From here, you can travel to any other city! A ticket
costs {Money(CONSTANTS.TravelCost)}.
</p>
{travelBtns}
</div>
)
}
render() {
if (Settings.DisableASCIIArt) {
return this.listWorldMap();
} else {
return this.asciiWorldMap();
}
}
}

View File

@@ -12,6 +12,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
type IProps = {
loc: Location;
@@ -90,31 +91,31 @@ export class UniversityLocation extends React.Component<IProps, any> {
<StdButton
onClick={this.dataStructures}
style={this.btnStyle}
text={`Take Data Structures course (${numeralWrapper.formatMoney(dataStructuresCost)} / sec)`}
text={<>Take Data Structures course ({Money(dataStructuresCost)} / sec)</>}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.networks}
style={this.btnStyle}
text={`Take Networks course (${numeralWrapper.formatMoney(networksCost)} / sec)`}
text={<>Take Networks course ({Money(networksCost)} / sec)</>}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.algorithms}
style={this.btnStyle}
text={`Take Algorithms course (${numeralWrapper.formatMoney(algorithmsCost)} / sec)`}
text={<>Take Algorithms course ({Money(algorithmsCost)} / sec)</>}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.management}
style={this.btnStyle}
text={`Take Management course (${numeralWrapper.formatMoney(managementCost)} / sec)`}
text={<>Take Management course ({Money(managementCost)} / sec)</>}
tooltip={earnCharismaExpTooltip}
/>
<StdButton
onClick={this.leadership}
style={this.btnStyle}
text={`Take Leadership course (${numeralWrapper.formatMoney(leadershipCost)} / sec)`}
text={<>Take Leadership course ({Money(leadershipCost)} / sec)</>}
tooltip={earnCharismaExpTooltip}
/>
</div>

View File

@@ -6,6 +6,7 @@ import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox";
import { formatNumber } from "../utils/StringHelperFunctions";
import { numeralWrapper } from "./ui/numeralFormat";
import { Reputation } from "./ui/React/Reputation";
import { addOffset } from "../utils/helpers/addOffset";
import { getRandomInt } from "../utils/helpers/getRandomInt";
@@ -15,6 +16,9 @@ import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import jsplumb from "jsplumb";
import React from "react";
import ReactDOM from "react-dom";
let inMission = false; // Flag to denote whether a mission is running
let currMission = null;
@@ -286,9 +290,7 @@ HackingMission.prototype.createPageDom = function() {
var favorMult = 1 + (this.faction.favor / 100);
var gain = this.reward * Player.faction_rep_mult * favorMult;
var headerText = document.createElement("p");
headerText.innerHTML = "You are about to start a hacking mission! You will gain " +
numeralWrapper.format(gain, '0.000a') + " faction reputation with " + this.faction.name +
" if you win. Click the 'Start' button to begin.";
ReactDOM.render(<>You are about to start a hacking mission! You will gain {Reputation(gain)} faction reputation with {this.faction.name} if you win. Click the 'Start' button to begin.</>, headerText);
headerText.style.display = "block";
headerText.classList.add("hack-mission-header-element");
headerText.style.width = "80%";
@@ -310,7 +312,7 @@ HackingMission.prototype.createPageDom = function() {
startBtn.classList.add("a-link-button");
startBtn.classList.add("hack-mission-header-element");
startBtn.style.display = "inline-block";
startBtn.addEventListener("click", ()=>{
startBtn.addEventListener("click", () => {
this.start();
return false;
});
@@ -320,7 +322,7 @@ HackingMission.prototype.createPageDom = function() {
forfeitMission.classList.add("a-link-button");
forfeitMission.classList.add("hack-mission-header-element");
forfeitMission.style.display = "inline-block";
forfeitMission.addEventListener("click", ()=> {
forfeitMission.addEventListener("click", () => {
this.finishMission(false);
return false;
});
@@ -394,7 +396,7 @@ HackingMission.prototype.createPageDom = function() {
actionsContainer.appendChild(enemyStats);
// Set Action Button event listeners
this.actionButtons[0].addEventListener("click", ()=>{
this.actionButtons[0].addEventListener("click", () => {
if (!(this.selectedNode.length > 0)) {
console.error("Pressing Action button without selected node");
return;
@@ -407,7 +409,7 @@ HackingMission.prototype.createPageDom = function() {
});
});
this.actionButtons[1].addEventListener("click", ()=>{
this.actionButtons[1].addEventListener("click", () => {
if (!(this.selectedNode.length > 0)) {
console.error("Pressing Action button without selected node");
return;
@@ -421,7 +423,7 @@ HackingMission.prototype.createPageDom = function() {
});
});
this.actionButtons[2].addEventListener("click", ()=>{
this.actionButtons[2].addEventListener("click", () => {
if (!(this.selectedNode.length > 0)) {
console.error("Pressing Action button without selected node");
return;
@@ -435,7 +437,7 @@ HackingMission.prototype.createPageDom = function() {
});
});
this.actionButtons[3].addEventListener("click", ()=>{
this.actionButtons[3].addEventListener("click", () => {
if (!(this.selectedNode.length > 0)) {
console.error("Pressing Action button without selected node");
return;
@@ -447,7 +449,7 @@ HackingMission.prototype.createPageDom = function() {
});
});
this.actionButtons[4].addEventListener("click", ()=>{
this.actionButtons[4].addEventListener("click", () => {
if (!(this.selectedNode.length > 0)) {
console.error("Pressing Action button without selected node");
return;
@@ -461,7 +463,7 @@ HackingMission.prototype.createPageDom = function() {
});
});
this.actionButtons[5].addEventListener("click", ()=>{
this.actionButtons[5].addEventListener("click", () => {
if (!(this.selectedNode.length > 0)) {
console.error("Pressing Action button without selected node");
return;
@@ -1049,7 +1051,7 @@ HackingMission.prototype.initJsPlumb = function() {
});
// Detach Connection events
instance.bind("connectionDetached", (info, originalEvent)=>{
instance.bind("connectionDetached", (info, originalEvent) => {
var sourceNode = this.getNodeFromElement(info.source);
sourceNode.conn = null;
var targetNode = this.getNodeFromElement(info.target);
@@ -1087,11 +1089,11 @@ HackingMission.prototype.process = function(numCycles=1) {
var res = false;
// Process actions of all player nodes
this.playerCores.forEach((node)=>{
this.playerCores.forEach((node) => {
res |= this.processNode(node, storedCycles);
});
this.playerNodes.forEach((node)=>{
this.playerNodes.forEach((node) => {
if (node.type === NodeTypes.Transfer ||
node.type === NodeTypes.Shield ||
node.type === NodeTypes.Firewall) {
@@ -1100,12 +1102,12 @@ HackingMission.prototype.process = function(numCycles=1) {
});
// Process actions of all enemy nodes
this.enemyCores.forEach((node)=>{
this.enemyCores.forEach((node) => {
this.enemyAISelectAction(node);
res |= this.processNode(node, storedCycles);
});
this.enemyNodes.forEach((node)=>{
this.enemyNodes.forEach((node) => {
if (node.type === NodeTypes.Transfer ||
node.type === NodeTypes.Shield ||
node.type === NodeTypes.Firewall) {
@@ -1115,7 +1117,7 @@ HackingMission.prototype.process = function(numCycles=1) {
});
// The hp of enemy databases increases slowly
this.enemyDatabases.forEach((node)=>{
this.enemyDatabases.forEach((node) => {
node.maxhp += (0.1 * storedCycles);
node.hp += (0.1 * storedCycles);
});
@@ -1138,7 +1140,7 @@ HackingMission.prototype.process = function(numCycles=1) {
}
// Defense/hp of misc nodes increases slowly over time
this.miscNodes.forEach((node)=>{
this.miscNodes.forEach((node) => {
node.def += (0.1 * storedCycles);
node.maxhp += (0.05 * storedCycles);
node.hp += (0.1 * storedCycles);
@@ -1353,7 +1355,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
// If a misc node was conquered, the defense for all misc nodes increases by some fixed amount
if (isMiscNode) { //&& conqueredByPlayer) {
this.miscNodes.forEach((node)=>{
this.miscNodes.forEach((node) => {
if (node.targetedCount === 0) {
node.def *= CONSTANTS.HackingMissionMiscDefenseIncrease;
}
@@ -1514,8 +1516,7 @@ HackingMission.prototype.finishMission = function(win) {
if (win) {
var favorMult = 1 + (this.faction.favor / 100);
var gain = this.reward * Player.faction_rep_mult * favorMult;
dialogBoxCreate("Mission won! You earned " +
numeralWrapper.format(gain, '0.000a') + " reputation with " + this.faction.name);
dialogBoxCreate(<>Mission won! You earned {Reputation(gain)} reputation with {this.faction.name}</>);
Player.gainIntelligenceExp(this.difficulty * CONSTANTS.IntelligenceHackingMissionBaseExpGain);
this.faction.playerReputation += gain;
} else {

View File

@@ -111,6 +111,7 @@ export const RamCosts: IMap<any> = {
getHackingMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
getHacknetMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
getBitNodeMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,
getServer: () => RamCostConstants.ScriptGetMultipliersRamCost / 2,
getServerMoneyAvailable: () => RamCostConstants.ScriptGetServerRamCost,
getServerSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
getServerBaseSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,
@@ -176,10 +177,13 @@ export const RamCosts: IMap<any> = {
travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost,
purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost,
purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost,
getCurrentServer: () => RamCostConstants.ScriptSingularityFn1RamCost,
connect: () => RamCostConstants.ScriptSingularityFn1RamCost,
manualHack: () => RamCostConstants.ScriptSingularityFn1RamCost,
getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getPlayer: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
hospitalize: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2,
upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost,

View File

@@ -29,6 +29,7 @@ import {
calculateGrowTime,
calculateWeakenTime
} from "./Hacking";
import { calculateServerGrowth } from "./Server/formulas/grow";
import {
AllGangs,
GangMemberUpgrades,
@@ -56,10 +57,30 @@ import {
purchaseHashUpgrade,
updateHashManagerCapacity,
} from "./Hacknet/HacknetHelpers";
import {
calculateMoneyGainRate,
calculateLevelUpgradeCost,
calculateRamUpgradeCost,
calculateCoreUpgradeCost,
calculateNodeCost,
} from "./Hacknet/formulas/HacknetNodes";
import {
calculateHashGainRate as HScalculateHashGainRate,
calculateLevelUpgradeCost as HScalculateLevelUpgradeCost,
calculateRamUpgradeCost as HScalculateRamUpgradeCost,
calculateCoreUpgradeCost as HScalculateCoreUpgradeCost,
calculateCacheUpgradeCost as HScalculateCacheUpgradeCost,
calculateServerCost as HScalculateServerCost,
} from "./Hacknet/formulas/HacknetServers";
import { HacknetNodeConstants, HacknetServerConstants } from "./Hacknet/data/Constants";
import { HacknetServer, MaxNumberHacknetServers } from "./Hacknet/HacknetServer";
import { CityName } from "./Locations/data/CityNames";
import { LocationName } from "./Locations/data/LocationNames";
import { Terminal } from "./Terminal";
import {
calculateSkill,
calculateExp,
} from "./PersonObjects/formulas/skill";
import { Message } from "./Message/Message";
import { Messages } from "./Message/MessageHelpers";
@@ -449,6 +470,16 @@ function NetscriptFunctions(workerScript) {
return makeRuntimeRejectMsg(workerScript, rejectMsg);
}
const checkFormulasAccess = function(func, n) {
if (SourceFileFlags[5] < 1 || SourceFileFlags[n] < 1) {
let extra = '';
if (n !== 5) {
extra = ` and Source-File ${n}-1`;
}
throw makeRuntimeErrorMsg(`formulas.${func}`, `Requires Source-File 5-1${extra} to run.`);
}
}
const checkSingularityAccess = function(func, n) {
if (Player.bitNodeN !== 4) {
if (SourceFileFlags[4] < n) {
@@ -592,7 +623,7 @@ function NetscriptFunctions(workerScript) {
}
// Calculate the hacking time
var hackingTime = calculateHackingTime(server); // This is in seconds
var hackingTime = calculateHackingTime(server, Player); // This is in seconds
// No root access or skill level too low
const canHack = netscriptCanHack(server, Player);
@@ -604,12 +635,12 @@ function NetscriptFunctions(workerScript) {
return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
var hackChance = calculateHackingChance(server);
var hackChance = calculateHackingChance(server, Player);
var rand = Math.random();
var expGainedOnSuccess = calculateHackingExpGain(server) * threads;
var expGainedOnSuccess = calculateHackingExpGain(server, Player) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { // Success!
const percentHacked = calculatePercentMoneyHacked(server);
const percentHacked = calculatePercentMoneyHacked(server, Player);
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) {
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
@@ -636,7 +667,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.recordHack(server.ip, moneyGained, threads);
Player.gainHackingExp(expGainedOnSuccess);
workerScript.scriptRef.onlineExpGained += expGainedOnSuccess;
workerScript.log("hack", `Successfully hacked '${server.hostname}' for ${numeralWrapper.format(moneyGained, '$0.000a')} and ${numeralWrapper.format(expGainedOnSuccess, '0.000a')} exp (t=${threads})`);
workerScript.log("hack", `Successfully hacked '${server.hostname}' for ${numeralWrapper.formatMoney(moneyGained)} and ${numeralWrapper.formatExp(expGainedOnSuccess)} exp (t=${threads})`);
server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
if (stock) {
influenceStockThroughServerHack(server, moneyGained);
@@ -649,7 +680,7 @@ function NetscriptFunctions(workerScript) {
// Player only gains 25% exp for failure?
Player.gainHackingExp(expGainedOnFailure);
workerScript.scriptRef.onlineExpGained += expGainedOnFailure;
workerScript.log("hack", `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.format(expGainedOnFailure, '0.000a')} exp (t=${threads})`);
workerScript.log("hack", `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.formatExp(expGainedOnFailure)} exp (t=${threads})`);
return Promise.resolve(0);
}
});
@@ -735,6 +766,10 @@ function NetscriptFunctions(workerScript) {
if (!hasHacknetServers()) { return 0; }
return Player.hashManager.hashes;
},
hashCapacity: function() {
if (!hasHacknetServers()) { return 0; }
return Player.hashManager.capacity;
},
hashCost : function(upgName) {
if (!hasHacknetServers()) { return Infinity; }
@@ -743,7 +778,22 @@ function NetscriptFunctions(workerScript) {
spendHashes : function(upgName, upgTarget) {
if (!hasHacknetServers()) { return false; }
return purchaseHashUpgrade(upgName, upgTarget);
}
},
getHashUpgradeLevel : function(upgName) {
const level = Player.hashManager.upgrades[upgName];
if(level === undefined) {
throw makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`);
}
return level;
},
getStudyMult : function() {
if (!hasHacknetServers()) { return false; }
return Player.hashManager.getStudyMult();
},
getTrainingMult : function() {
if (!hasHacknetServers()) { return false; }
return Player.hashManager.getTrainingMult();
},
},
sprintf : sprintf,
vsprintf: vsprintf,
@@ -786,7 +836,7 @@ function NetscriptFunctions(workerScript) {
return -1;
}
const percentHacked = calculatePercentMoneyHacked(server);
const percentHacked = calculatePercentMoneyHacked(server, Player);
return hackAmount / Math.floor(server.moneyAvailable * percentHacked);
},
@@ -795,14 +845,14 @@ function NetscriptFunctions(workerScript) {
const server = safeGetServer(ip, 'hackAnalyzePercent');
return calculatePercentMoneyHacked(server) * 100;
return calculatePercentMoneyHacked(server, Player) * 100;
},
hackChance : function(ip) {
updateDynamicRam("hackChance", getRamCost("hackChance"));
const server = safeGetServer(ip, 'hackChance');
return calculateHackingChance(server);
return calculateHackingChance(server, Player);
},
sleep : function(time){
if (time === undefined) {
@@ -830,21 +880,21 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeErrorMsg("grow", canHack.msg);
}
var growTime = calculateGrowTime(server);
var growTime = calculateGrowTime(server, Player);
workerScript.log("grow", `Executing on '${server.hostname}' in ${formatNumber(growTime, 3)} seconds (t=${threads}).`);
return netscriptDelay(growTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
server.moneyAvailable += (1 * threads); // It can be grown even if it has no money
var growthPercentage = processSingleServerGrowth(server, 450 * threads, Player);
var growthPercentage = processSingleServerGrowth(server, threads, Player);
const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads);
var expGain = calculateHackingExpGain(server) * threads;
var expGain = calculateHackingExpGain(server, Player) * threads;
if (growthPercentage == 1) {
expGain = 0;
}
const logGrowPercent = (moneyAfter/moneyBefore)*100 - 100;
workerScript.log("grow", `Available money on '${server.hostname}' grown by ${formatNumber(logGrowPercent, 6)}%. Gained ${numeralWrapper.format(expGain, '0.000a')} hacking exp (t=${threads}).`);
workerScript.log("grow", `Available money on '${server.hostname}' grown by ${formatNumber(logGrowPercent, 6)}%. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${threads}).`);
workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain);
if (stock) {
@@ -881,14 +931,14 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeErrorMsg("weaken", canHack.msg);
}
var weakenTime = calculateWeakenTime(server);
var weakenTime = calculateWeakenTime(server, Player);
workerScript.log("weaken", `Executing on '${server.hostname}' in ${formatNumber(weakenTime, 3)} seconds (t=${threads})`);
return netscriptDelay(weakenTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
server.weaken(CONSTANTS.ServerWeakenAmount * threads);
workerScript.scriptRef.recordWeaken(server.ip, threads);
var expGain = calculateHackingExpGain(server) * threads;
workerScript.log("weaken", `'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.format(expGain, '0.000a')} hacking exp (t=${threads})`);
var expGain = calculateHackingExpGain(server, Player) * threads;
workerScript.log("weaken", `'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${threads})`);
workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain);
return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads);
@@ -1508,16 +1558,33 @@ function NetscriptFunctions(workerScript) {
let copy = Object.assign({}, BitNodeMultipliers);
return copy;
},
getServer: function(ip) {
updateDynamicRam("getServer", getRamCost("getServer"));
if (SourceFileFlags[5] <= 0) {
throw makeRuntimeErrorMsg("getServer", "Requires Source-File 5 to run.");
}
const server = safeGetServer(ip, "getServer");
const copy = Object.assign({}, server);
// These fields should be hidden.
copy.contracts = undefined;
copy.messages = undefined;
copy.runningScripts = undefined;
copy.scripts = undefined;
copy.textFiles = undefined;
copy.programs = undefined;
copy.serversOnNetwork = undefined;
return copy;
},
getServerMoneyAvailable: function(ip) {
updateDynamicRam("getServerMoneyAvailable", getRamCost("getServerMoneyAvailable"));
const server = safeGetServer(ip, "getServerMoneyAvailable");
if (failOnHacknetServer(server, "getServerMoneyAvailable")) { return 0; }
if (server.hostname == "home") {
// Return player's money
workerScript.log("getServerMoneyAvailable", `returned player's money: ${numeralWrapper.format(Player.money.toNumber(), '$0.000a')}`);
workerScript.log("getServerMoneyAvailable", `returned player's money: ${numeralWrapper.formatMoney(Player.money.toNumber())}`);
return Player.money.toNumber();
}
workerScript.log("getServerMoneyAvailable", `returned ${numeralWrapper.format(server.moneyAvailable, '$0.000a')} for '${server.hostname}`);
workerScript.log("getServerMoneyAvailable", `returned ${numeralWrapper.formatMoney(server.moneyAvailable)} for '${server.hostname}`);
return server.moneyAvailable;
},
getServerSecurityLevel: function(ip) {
@@ -1552,7 +1619,7 @@ function NetscriptFunctions(workerScript) {
updateDynamicRam("getServerMaxMoney", getRamCost("getServerMaxMoney"));
const server = safeGetServer(ip, "getServerMaxMoney");
if (failOnHacknetServer(server, "getServerMaxMoney")) { return 0; }
workerScript.log("getServerMaxMoney", `returned ${numeralWrapper.format(server.moneyMax, '$0.000a')} for '${server.hostname}'`);
workerScript.log("getServerMaxMoney", `returned ${numeralWrapper.formatMoney(server.moneyMax)} for '${server.hostname}'`);
return server.moneyMax;
},
getServerGrowth: function(ip) {
@@ -1959,7 +2026,7 @@ function NetscriptFunctions(workerScript) {
}
if (Player.money.lt(cost)) {
workerScript.log("purchaseServer", `Not enough money to purchase server. Need ${numeralWrapper.format(cost, '$0.000a')}`);
workerScript.log("purchaseServer", `Not enough money to purchase server. Need ${numeralWrapper.formatMoney(cost)}`);
return "";
}
var newServ = safetlyCreateUniqueServer({
@@ -1978,7 +2045,7 @@ function NetscriptFunctions(workerScript) {
homeComputer.serversOnNetwork.push(newServ.ip);
newServ.serversOnNetwork.push(homeComputer.ip);
Player.loseMoney(cost);
workerScript.log("purchaseServer", `Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.format(cost, '$0.000a')}`);
workerScript.log("purchaseServer", `Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.formatMoney(cost)}`);
return newServ.hostname;
},
deleteServer: function(hostname) {
@@ -2300,21 +2367,21 @@ function NetscriptFunctions(workerScript) {
const server = safeGetServer(ip, "getHackTime");
if (failOnHacknetServer(server, "getHackTime")) { return Infinity; }
return calculateHackingTime(server, hack, int); // Returns seconds
return calculateHackingTime(server, Player); // Returns seconds
},
getGrowTime: function(ip, hack, int) {
updateDynamicRam("getGrowTime", getRamCost("getGrowTime"));
const server = safeGetServer(ip, "getGrowTime");
if (failOnHacknetServer(server, "getGrowTime")) { return Infinity; }
return calculateGrowTime(server, hack, int); // Returns seconds
return calculateGrowTime(server, Player); // Returns seconds
},
getWeakenTime: function(ip, hack, int) {
updateDynamicRam("getWeakenTime", getRamCost("getWeakenTime"));
const server = safeGetServer(ip, "getWeakenTime");
if (failOnHacknetServer(server, "getWeakenTime")) { return Infinity; }
return calculateWeakenTime(server, hack, int); // Returns seconds
return calculateWeakenTime(server, Player); // Returns seconds
},
getScriptIncome: function(scriptname, ip) {
updateDynamicRam("getScriptIncome", getRamCost("getScriptIncome"));
@@ -2695,7 +2762,7 @@ function NetscriptFunctions(workerScript) {
}
if(Player.money.lt(item.price)) {
workerScript.log("purchaseProgram", `Not enough money to purchase '${item.program}'. Need ${numeralWrapper.format(item.price, '$0.000a')}`);
workerScript.log("purchaseProgram", `Not enough money to purchase '${item.program}'. Need ${numeralWrapper.formatMoney(item.price)}`);
return false;
}
@@ -2710,7 +2777,14 @@ function NetscriptFunctions(workerScript) {
workerScript.log("purchaseProgram", `You have purchased the '${item.program}' program. The new program can be found on your home computer.`);
return true;
},
getCurrentServer: function() {
updateDynamicRam("getCurrentServer", getRamCost("getCurrentServer"));
checkSingularityAccess("getCurrentServer", 1);
return Player.getCurrentServer().hostname;
},
connect: function(hostname) {
updateDynamicRam("connect", getRamCost("connect"));
checkSingularityAccess("connect", 1);
if (!hostname) {
throw makeRuntimeErrorMsg("connect", `Invalid hostname: '${hostname}'`);
}
@@ -2754,6 +2828,7 @@ function NetscriptFunctions(workerScript) {
getStats: function() {
updateDynamicRam("getStats", getRamCost("getStats"));
checkSingularityAccess("getStats", 1);
workerScript.log("getStats", `getStats is deprecated, please use getPlayer`);
return {
hacking: Player.hacking_skill,
@@ -2768,6 +2843,7 @@ function NetscriptFunctions(workerScript) {
getCharacterInformation: function() {
updateDynamicRam("getCharacterInformation", getRamCost("getCharacterInformation"));
checkSingularityAccess("getCharacterInformation", 1);
workerScript.log("getCharacterInformation", `getCharacterInformation is deprecated, please use getPlayer`);
return {
bitnode: Player.bitNodeN,
@@ -2812,6 +2888,103 @@ function NetscriptFunctions(workerScript) {
charismaExp: Player.charisma_exp,
};
},
getPlayer: function() {
updateDynamicRam("getPlayer", getRamCost("getPlayer"));
checkSingularityAccess("getPlayer", 1);
const data = {
hacking_skill: Player.hacking_skill,
hp: Player.hp,
max_hp: Player.max_hp,
strength: Player.strength,
defense: Player.defense,
dexterity: Player.dexterity,
agility: Player.agility,
charisma: Player.charisma,
intelligence: Player.intelligence,
hacking_chance_mult: Player.hacking_chance_mult,
hacking_speed_mult: Player.hacking_speed_mult,
hacking_money_mult: Player.hacking_money_mult,
hacking_grow_mult: Player.hacking_grow_mult,
hacking_exp: Player.hacking_exp,
strength_exp: Player.strength_exp,
defense_exp: Player.defense_exp,
dexterity_exp: Player.dexterity_exp,
agility_exp: Player.agility_exp,
charisma_exp: Player.charisma_exp,
hacking_mult: Player.hacking_mult,
strength_mult: Player.strength_mult,
defense_mult: Player.defense_mult,
dexterity_mult: Player.dexterity_mult,
agility_mult: Player.agility_mult,
charisma_mult: Player.charisma_mult,
hacking_exp_mult: Player.hacking_exp_mult,
strength_exp_mult: Player.strength_exp_mult,
defense_exp_mult: Player.defense_exp_mult,
dexterity_exp_mult: Player.dexterity_exp_mult,
agility_exp_mult: Player.agility_exp_mult,
charisma_exp_mult: Player.charisma_exp_mult,
company_rep_mult: Player.company_rep_mult,
faction_rep_mult: Player.faction_rep_mult,
money: Player.money.toNumber(),
city: Player.city,
location: Player.location,
crime_money_mult: Player.crime_money_mult,
crime_success_mult: Player.crime_success_mult,
isWorking: Player.isWorking,
workType: Player.workType,
currentWorkFactionName: Player.currentWorkFactionName,
currentWorkFactionDescription: Player.currentWorkFactionDescription,
workHackExpGainRate: Player.workHackExpGainRate,
workStrExpGainRate: Player.workStrExpGainRate,
workDefExpGainRate: Player.workDefExpGainRate,
workDexExpGainRate: Player.workDexExpGainRate,
workAgiExpGainRate: Player.workAgiExpGainRate,
workChaExpGainRate: Player.workChaExpGainRate,
workRepGainRate: Player.workRepGainRate,
workMoneyGainRate: Player.workMoneyGainRate,
workMoneyLossRate: Player.workMoneyLossRate,
workHackExpGained: Player.workHackExpGained,
workStrExpGained: Player.workStrExpGained,
workDefExpGained: Player.workDefExpGained,
workDexExpGained: Player.workDexExpGained,
workAgiExpGained: Player.workAgiExpGained,
workChaExpGained: Player.workChaExpGained,
workRepGained: Player.workRepGained,
workMoneyGained: Player.workMoneyGained,
createProgramName: Player.createProgramName,
createProgramReqLvl: Player.createProgramReqLvl,
className: Player.className,
crimeType: Player.crimeType,
work_money_mult: Player.work_money_mult,
hacknet_node_money_mult: Player.hacknet_node_money_mult,
hacknet_node_purchase_cost_mult: Player.hacknet_node_purchase_cost_mult,
hacknet_node_ram_cost_mult: Player.hacknet_node_ram_cost_mult,
hacknet_node_core_cost_mult: Player.hacknet_node_core_cost_mult,
hacknet_node_level_cost_mult: Player.hacknet_node_level_cost_mult,
hasWseAccount: Player.hasWseAccount,
hasTixApiAccess: Player.hasTixApiAccess,
has4SData: Player.has4SData,
has4SDataTixApi: Player.has4SDataTixApi,
bladeburner_max_stamina_mult: Player.bladeburner_max_stamina_mult,
bladeburner_stamina_gain_mult: Player.bladeburner_stamina_gain_mult,
bladeburner_success_chance_mult: Player.bladeburner_success_chance_mult,
bitNodeN: Player.bitNodeN,
totalPlaytime: Player.totalPlaytime,
playtimeSinceLastAug: Player.playtimeSinceLastAug,
playtimeSinceLastBitnode: Player.playtimeSinceLastBitnode,
jobs: {},
factions: Player.factions.slice(),
tor: SpecialServerIps.hasOwnProperty("Darkweb Server"),
};
Object.assign(data.jobs, Player.jobs);
return data;
},
hospitalize: function() {
updateDynamicRam("hospitalize", getRamCost("hospitalize"));
checkSingularityAccess("hospitalize", 1);
return Player.hospitalize();
},
isBusy: function() {
updateDynamicRam("isBusy", getRamCost("isBusy"));
checkSingularityAccess("isBusy", 1);
@@ -2840,7 +3013,7 @@ function NetscriptFunctions(workerScript) {
const cost = Player.getUpgradeHomeRamCost();
if (Player.money.lt(cost)) {
workerScript.log("upgradeHomeRam", `You don't have enough money. Need ${numeralWrapper.format(cost, '$0.000a')}`);
workerScript.log("upgradeHomeRam", `You don't have enough money. Need ${numeralWrapper.formatMoney(cost)}`);
return false;
}
@@ -2995,7 +3168,7 @@ function NetscriptFunctions(workerScript) {
joinFaction: function(name) {
updateDynamicRam("joinFaction", getRamCost("joinFaction"));
checkSingularityAccess("joinFaction", 2);
getFaction("workForFaction", name);
getFaction("joinFaction", name);
if (!Player.factionInvitations.includes(name)) {
workerScript.log("joinFaction", `You have not been invited by faction '${name}'`);
@@ -3127,7 +3300,7 @@ function NetscriptFunctions(workerScript) {
return false;
}
if (Player.money.lt(amt)) {
workerScript.log("donateToFaction", `You do not have enough money to donate ${numeralWrapper.format(amt, '$0.000a')} to '${name}'`);
workerScript.log("donateToFaction", `You do not have enough money to donate ${numeralWrapper.formatMoney(amt)} to '${name}'`);
return false;
}
const repNeededToDonate = Math.round(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
@@ -3138,7 +3311,7 @@ function NetscriptFunctions(workerScript) {
const repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
faction.playerReputation += repGain;
Player.loseMoney(amt);
workerScript.log("donateToFaction", `${numeralWrapper.format(amt, '$0.000a')} donated to '${name}' for ${numeralWrapper.format(repGain, '0.000a')} reputation`);
workerScript.log("donateToFaction", `${numeralWrapper.formatMoney(amt)} donated to '${name}' for ${numeralWrapper.formatReputation(repGain)} reputation`);
return true;
},
createProgram: function(name) {
@@ -3412,27 +3585,35 @@ function NetscriptFunctions(workerScript) {
checkGangApiAccess("getMemberInformation");
const member = getGangMember("getMemberInformation", name);
return {
agility: member.agi,
agilityEquipMult: member.agi_mult,
agilityAscensionMult: member.agi_asc_mult,
augmentations: member.augmentations.slice(),
charisma: member.cha,
charismaEquipMult: member.cha_mult,
charismaAscensionMult: member.cha_asc_mult,
defense: member.def,
defenseEquipMult: member.def_mult,
defenseAscensionMult: member.def_asc_mult,
dexterity: member.dex,
dexterityEquipMult: member.dex_mult,
dexterityAscensionMult: member.dex_asc_mult,
equipment: member.upgrades.slice(),
hacking: member.hack,
hackingEquipMult: member.hack_mult,
hackingAscensionMult: member.hack_asc_mult,
strength: member.str,
strengthEquipMult: member.str_mult,
strengthAscensionMult: member.str_asc_mult,
task: member.task,
name: member.name,
task: member.task,
earnedRespect: member.earnedRespect,
hack: member.hack,
str: member.str,
def: member.def,
dex: member.dex,
agi: member.agi,
cha: member.cha,
hack_exp: member.hack_exp,
str_exp: member.str_exp,
def_exp: member.def_exp,
dex_exp: member.dex_exp,
agi_exp: member.agi_exp,
cha_exp: member.cha_exp,
hack_mult: member.hack_mult,
str_mult: member.str_mult,
def_mult: member.def_mult,
dex_mult: member.dex_mult,
agi_mult: member.agi_mult,
cha_mult: member.cha_mult,
hack_asc_mult: member.hack_asc_mult,
str_asc_mult: member.str_asc_mult,
def_asc_mult: member.def_asc_mult,
dex_asc_mult: member.dex_asc_mult,
agi_asc_mult: member.agi_asc_mult,
cha_asc_mult: member.cha_asc_mult,
upgrades: member.upgrades.slice(),
augmentations: member.augmentations.slice(),
}
},
canRecruitMember: function() {
@@ -4088,6 +4269,110 @@ function NetscriptFunctions(workerScript) {
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
}
}, // End sleeve
formulas: {
basic: {
calculateSkill: function(exp, mult = 1) {
checkFormulasAccess("basic.calculateSkill", 5);
return calculateSkill(exp, mult);
},
calculateExp: function(skill, mult = 1) {
checkFormulasAccess("basic.calculateExp", 5);
return calculateExp(skill, mult);
},
hackChance: function(server, player) {
checkFormulasAccess("basic.hackChance", 5);
return calculateHackingChance(server, player);
},
hackExp: function(server, player) {
checkFormulasAccess("basic.hackExp", 5);
return calculateHackingExpGain(server, player);
},
hackPercent: function(server, player) {
checkFormulasAccess("basic.hackPercent", 5);
return calculatePercentMoneyHacked(server, player);
},
growPercent: function(server, threads, player) {
checkFormulasAccess("basic.growPercent", 5);
return calculateServerGrowth(server, threads, player);
},
hackTime: function(server, player) {
checkFormulasAccess("basic.hackTime", 5);
return calculateHackingTime(server, player);
},
growTime: function(server, player) {
checkFormulasAccess("basic.growTime", 5);
return calculateGrowTime(server, player);
},
weakenTime: function(server, player) {
checkFormulasAccess("basic.weakenTime", 5);
return calculateWeakenTime(server, player);
},
},
hacknetNodes: {
moneyGainRate: function(level, ram, cores, mult=1) {
checkFormulasAccess("hacknetNodes.moneyGainRate", 5);
return calculateMoneyGainRate(level, ram, cores, mult);
},
levelUpgradeCost: function(startingLevel, extraLevels=1, costMult=1) {
checkFormulasAccess("hacknetNodes.levelUpgradeCost", 5);
return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
},
ramUpgradeCost: function(startingRam, extraLevels=1, costMult=1) {
checkFormulasAccess("hacknetNodes.ramUpgradeCost", 5);
return calculateRamUpgradeCost(startingRam, extraLevels, costMult);
},
coreUpgradeCost: function(startingCore, extraCores=1, costMult=1) {
checkFormulasAccess("hacknetNodes.coreUpgradeCost", 5);
return calculateCoreUpgradeCost(startingCore, extraCores, costMult);
},
hacknetNodeCost: function(n, mult) {
checkFormulasAccess("hacknetNodes.hacknetNodeCost", 5);
return calculateNodeCost(n, mult);
},
constants: function() {
checkFormulasAccess("hacknetNodes.constants", 5);
return Object.assign({}, HacknetNodeConstants, HacknetServerConstants);
}
},
hacknetServers: {
hashGainRate: function(level, ram, cores, mult=1) {
checkFormulasAccess("hacknetServers.hashGainRate", 9);
return HScalculateHashGainRate(level, ram, cores, mult);
},
levelUpgradeCost: function(startingLevel, extraLevels=1, costMult=1) {
checkFormulasAccess("hacknetServers.levelUpgradeCost", 9);
return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
},
ramUpgradeCost: function(startingRam, extraLevels=1, costMult=1) {
checkFormulasAccess("hacknetServers.ramUpgradeCost", 9);
return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult);
},
coreUpgradeCost: function(startingCore, extraCores=1, costMult=1) {
checkFormulasAccess("hacknetServers.coreUpgradeCost", 9);
return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult);
},
cacheUpgradeCost: function(startingCache, extraCache=1, costMult=1) {
checkFormulasAccess("hacknetServers.cacheUpgradeCost", 9);
return HScalculateCacheUpgradeCost(startingCache, extraCache, costMult);
},
hashUpgradeCost: function(upgName, level) {
checkFormulasAccess("hacknetServers.hashUpgradeCost", 9);
const upg = Player.hashManager.getUpgrade(upgName);
if(!upg) {
throw makeRuntimeErrorMsg("formulas.hacknetServers.calculateHashUpgradeCost", `Invalid Hash Upgrade: ${upgName}`);
}
return upg.getCost(level);
},
hacknetServerCost: function(n, mult) {
checkFormulasAccess("hacknetServers.hacknetServerCost", 9);
return HScalculateServerCost(n, mult);
},
constants: function() {
checkFormulasAccess("hacknetServers.constants", 9);
return Object.assign({}, HacknetServerConstants);
}
},
}, // end formulas
heart: {
// Easter egg function
break: function() {

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