* 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
This commit is contained in:
hydroflame 2021-03-31 00:45:21 -04:00 committed by GitHub
parent ff097db1e2
commit e572c6dad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
127 changed files with 2772 additions and 1125 deletions

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

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

File diff suppressed because one or more lines are too long

@ -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([395,0]),o()}({338:function(n,t,o){},340:function(n,t,o){},342:function(n,t,o){},344:function(n,t,o){},346:function(n,t,o){},348:function(n,t,o){},350:function(n,t,o){},352:function(n,t,o){},354:function(n,t,o){},356:function(n,t,o){},358:function(n,t,o){},360:function(n,t,o){},362:function(n,t,o){},364:function(n,t,o){},366:function(n,t,o){},368:function(n,t,o){},370:function(n,t,o){},372:function(n,t,o){},374:function(n,t,o){},376:function(n,t,o){},378:function(n,t,o){},380:function(n,t,o){},382:function(n,t,o){},384:function(n,t,o){},386:function(n,t,o){},388:function(n,t,o){},390:function(n,t,o){},392:function(n,t,o){},395:function(n,t,o){"use strict";o.r(t);o(394),o(392),o(390),o(388),o(386),o(384),o(382),o(380),o(378),o(376),o(374),o(372),o(370),o(368),o(366),o(364),o(362),o(360),o(358),o(356),o(354),o(352),o(350),o(348),o(346),o(344),o(342),o(340),o(338)}});
//# sourceMappingURL=engineStyle.bundle.js.map //# sourceMappingURL=engineStyle.bundle.js.map

59
dist/engineStyle.css vendored

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

34
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,31 @@
Changelog Changelog
========= =========
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) v0.50.2 - 2021-03-25 Everyone asked for this one. (hydroflame)
-------------------------------------------------------------- --------------------------------------------------------------

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

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

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

@ -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.
: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");

@ -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.
: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);

@ -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()))

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

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

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

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

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

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

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

@ -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

@ -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

@ -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

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

@ -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

@ -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

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

@ -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

@ -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

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

@ -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

@ -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

@ -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

@ -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.

@ -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

@ -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

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

@ -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>

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

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

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

@ -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.

@ -0,0 +1,101 @@
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
}
Example::
player = getPlayer();
print('My charisma level is: ' + player.charisma);

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

@ -30,7 +30,7 @@
<div id="entire-game-container" style="visibility:hidden;"> <div id="entire-game-container" style="visibility:hidden;">
<div id="mainmenu-container"> <div id="mainmenu-container">
<!-- Main menu --> <!-- Main menu -->
<ul id="mainmenu" class="mainmenu"> <ul id="mainmenu" class="mainmenu noscrollbar">
<!-- Hacking dropdown --> <!-- Hacking dropdown -->
<li id="hacking-menu-header-li"> <li id="hacking-menu-header-li">
<button id="hacking-menu-header" class="mainmenu-accordion-header"> Hacking </button> <button id="hacking-menu-header" class="mainmenu-accordion-header"> Hacking </button>

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

@ -6,7 +6,7 @@
import { IMap } from "./types"; import { IMap } from "./types";
export let CONSTANTS: IMap<any> = { export let CONSTANTS: IMap<any> = {
Version: "0.50.2", Version: "0.51.0",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -228,18 +228,25 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate: LatestUpdate:
` `
v0.50.2 - 2021-03-25 Everyone asked for this one. (hydroflame) v0.51.0 - 2021-03-31 Formulas (hydroflame)
------- -------
BitNodeMultipliers
* 'GangKarmaRequirements': a new multipler that influences how much karma is required to make a gang different bitnodes. 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 Netscript
* 'connect': a new singularity function that connects you to a server. (like the terminal command) * 'getServer' is a new function meant to be used with the formulas API.
* 'manualHack': a new singularity function that performs a manual hack on the players current server. * 'getPlayer' is a new function meant to be used with the formulas API.
* ns2 stack trace works on Firefox now. * 'getStats' and 'getCharacterInformation' are deprecated in favor of 'getPlayer'
* 'getCurrentServer' is a new function that returns the server the player is currently connected.
Misc. Display
* New shortcut, Alt + b, brings you to bladeburner * All money should now consistently be orange.
* New shortcut, Alt + g, brings you to gang * 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'
` `
} }

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

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

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

@ -26,6 +26,7 @@ import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement"; import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData"; import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { Money } from "./ui/React/Money";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
@ -611,12 +612,16 @@ class DevMenuComponent extends Component {
} }
viewStockCaps() { viewStockCaps() {
let text = "<table><tbody><tr><th>Stock</th><th>Price cap</th></tr>"; let stocks = [];
this.processStocks((stock) => { this.processStocks((stock) => {
text += `<tr><td>${stock.symbol}</td><td style="text-align:right;">${numeralWrapper.format(stock.cap, '$0.000a')}</td></tr>`; stocks.push(<tr key={stock.symbol}>
<td>{stock.symbol}</td>
<td style={{'textAlign':'right'}}>{Money(stock.cap)}</td>
</tr>);
}); });
text += "</tbody></table>"; dialogBoxCreate(<table><tbody><tr><th>Stock</th><th>Price cap</th></tr>
dialogBoxCreate(text); {stocks}
</tbody></table>);
} }
sleeveMaxAllShock() { sleeveMaxAllShock() {

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

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

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

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

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

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

@ -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
*/

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

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

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

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

@ -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,
}

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

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

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

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

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

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

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

@ -21,6 +21,7 @@ import { SpecialServerIps } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { import {
@ -38,6 +39,7 @@ import { createElement } from "../../utils/uiHelpers/createElement";
import { createPopup } from "../../utils/uiHelpers/createPopup"; import { createPopup } from "../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton"; import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../utils/uiHelpers/removeElementById";
import * as React from "react";
/** /**
* Create a pop-up box that lets the player confirm traveling to a different city. * Create a pop-up box that lets the player confirm traveling to a different city.
@ -49,7 +51,7 @@ import { removeElementById } from "../../utils/uiHelpers/removeElementById";
*/ */
type TravelFunction = (to: CityName) => void; type TravelFunction = (to: CityName) => void;
export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void { export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
const cost = CONSTANTS.TravelCost; const cost: number = CONSTANTS.TravelCost;
if (Settings.SuppressTravelConfirmation) { if (Settings.SuppressTravelConfirmation) {
travelFn(destination); travelFn(destination);
@ -76,8 +78,8 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio
return false; return false;
}); });
yesNoBoxCreate(`Would you like to travel to ${destination}? The trip will ` + yesNoBoxCreate(<span>Would you like to travel to ${destination}? The trip will
`cost ${numeralWrapper.formatMoney(cost)}`); cost {Money(cost)}.</span>);
} }
/** /**
@ -105,10 +107,9 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
yesNoTxtInpBoxClose(); yesNoTxtInpBoxClose();
}); });
yesNoTxtInpBoxCreate( yesNoTxtInpBoxCreate(<>Would you like to purchase a new server with {numeralWrapper.formatRAM(ram)} of RAM for {Money(cost)}?
`Would you like to purchase a new server with ${ram} GB of RAM for ` + <br /><br />Please enter the server hostname below:<br />
`${numeralWrapper.formatMoney(cost)}?<br><br>Please enter the server hostname below:<br>` </>);
);
} }
/** /**
@ -236,12 +237,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate( yesNoBoxCreate(<>Would you like to purchase an additional CPU Core for your home computer? Each CPU Core
"Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " + lets you start with an additional Core Node in Hacking Missions.<br /><br />
"lets you start with an additional Core Node in Hacking Missions.<br><br>" + Purchasing an additional core (for a total of {p.getHomeComputer().cpuCores + 1}) will
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " + cost {Money(cost)}</>);
"cost " + numeralWrapper.formatMoney(cost)
);
} }
/** /**
@ -267,11 +266,11 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate( yesNoBoxCreate(<>
"Would you like to purchase additional RAM for your home computer? <br><br>" + Would you like to purchase additional RAM for your home computer? <br /><br />
"This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" + This will upgrade your RAM from {numeralWrapper.formatRAM(ram)} to {numeralWrapper.formatRAM(ram*2)}. <br /><br />
"This will cost " + numeralWrapper.format(cost, '$0.000a') This will cost {Money(cost)}
); </>);
} }

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

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

@ -10,6 +10,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton"; import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -59,7 +60,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
currHp: this.props.p.hp, 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() { render() {
@ -69,7 +70,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
<AutoupdatingStdButton <AutoupdatingStdButton
onClick={this.getHealed} onClick={this.getHealed}
style={this.btnStyle} style={this.btnStyle}
text={`Get treatment for wounds - ${numeralWrapper.formatMoney(cost)}`} text={<>Get treatment for wounds - {Money(cost)}</>}
/> />
) )
} }

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

@ -1,7 +1,7 @@
/** /**
* React Subcomponent for displaying a location's UI, when that location is a Travel Agency * 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"; import * as React from "react";
@ -13,6 +13,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
type IProps = { type IProps = {
p: IPlayer; p: IPlayer;
@ -48,7 +49,7 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
<div> <div>
<p> <p>
From here, you can travel to any other city! A ticket From here, you can travel to any other city! A ticket
costs {numeralWrapper.formatMoney(CONSTANTS.TravelCost)} costs {Money(CONSTANTS.TravelCost)}.
</p> </p>
<pre> ,_ . ._. _. .</pre> <pre> ,_ . ._. _. .</pre>
<pre> , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~-</pre> <pre> , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~-</pre>

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

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

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

@ -29,6 +29,7 @@ import {
calculateGrowTime, calculateGrowTime,
calculateWeakenTime calculateWeakenTime
} from "./Hacking"; } from "./Hacking";
import { calculateServerGrowth } from "./Server/formulas/grow";
import { import {
AllGangs, AllGangs,
GangMemberUpgrades, GangMemberUpgrades,
@ -56,10 +57,30 @@ import {
purchaseHashUpgrade, purchaseHashUpgrade,
updateHashManagerCapacity, updateHashManagerCapacity,
} from "./Hacknet/HacknetHelpers"; } 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 { HacknetServer, MaxNumberHacknetServers } from "./Hacknet/HacknetServer";
import { CityName } from "./Locations/data/CityNames"; import { CityName } from "./Locations/data/CityNames";
import { LocationName } from "./Locations/data/LocationNames"; import { LocationName } from "./Locations/data/LocationNames";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import {
calculateSkill,
calculateExp,
} from "./PersonObjects/formulas/skill";
import { Message } from "./Message/Message"; import { Message } from "./Message/Message";
import { Messages } from "./Message/MessageHelpers"; import { Messages } from "./Message/MessageHelpers";
@ -449,6 +470,16 @@ function NetscriptFunctions(workerScript) {
return makeRuntimeRejectMsg(workerScript, rejectMsg); 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) { const checkSingularityAccess = function(func, n) {
if (Player.bitNodeN !== 4) { if (Player.bitNodeN !== 4) {
if (SourceFileFlags[4] < n) { if (SourceFileFlags[4] < n) {
@ -592,7 +623,7 @@ function NetscriptFunctions(workerScript) {
} }
// Calculate the hacking time // 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 // No root access or skill level too low
const canHack = netscriptCanHack(server, Player); const canHack = netscriptCanHack(server, Player);
@ -604,12 +635,12 @@ function NetscriptFunctions(workerScript) {
return netscriptDelay(hackingTime * 1000, workerScript).then(function() { return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
var hackChance = calculateHackingChance(server); var hackChance = calculateHackingChance(server, Player);
var rand = Math.random(); var rand = Math.random();
var expGainedOnSuccess = calculateHackingExpGain(server) * threads; var expGainedOnSuccess = calculateHackingExpGain(server, Player) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4); var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { // Success! if (rand < hackChance) { // Success!
const percentHacked = calculatePercentMoneyHacked(server); const percentHacked = calculatePercentMoneyHacked(server, Player);
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax)); let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) { if (isNaN(maxThreadNeeded)) {
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value // 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); workerScript.scriptRef.recordHack(server.ip, moneyGained, threads);
Player.gainHackingExp(expGainedOnSuccess); Player.gainHackingExp(expGainedOnSuccess);
workerScript.scriptRef.onlineExpGained += 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)); server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
if (stock) { if (stock) {
influenceStockThroughServerHack(server, moneyGained); influenceStockThroughServerHack(server, moneyGained);
@ -649,7 +680,7 @@ function NetscriptFunctions(workerScript) {
// Player only gains 25% exp for failure? // Player only gains 25% exp for failure?
Player.gainHackingExp(expGainedOnFailure); Player.gainHackingExp(expGainedOnFailure);
workerScript.scriptRef.onlineExpGained += 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); return Promise.resolve(0);
} }
}); });
@ -743,7 +774,22 @@ function NetscriptFunctions(workerScript) {
spendHashes : function(upgName, upgTarget) { spendHashes : function(upgName, upgTarget) {
if (!hasHacknetServers()) { return false; } if (!hasHacknetServers()) { return false; }
return purchaseHashUpgrade(upgName, upgTarget); 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, sprintf : sprintf,
vsprintf: vsprintf, vsprintf: vsprintf,
@ -786,7 +832,7 @@ function NetscriptFunctions(workerScript) {
return -1; return -1;
} }
const percentHacked = calculatePercentMoneyHacked(server); const percentHacked = calculatePercentMoneyHacked(server, Player);
return hackAmount / Math.floor(server.moneyAvailable * percentHacked); return hackAmount / Math.floor(server.moneyAvailable * percentHacked);
}, },
@ -795,14 +841,14 @@ function NetscriptFunctions(workerScript) {
const server = safeGetServer(ip, 'hackAnalyzePercent'); const server = safeGetServer(ip, 'hackAnalyzePercent');
return calculatePercentMoneyHacked(server) * 100; return calculatePercentMoneyHacked(server, Player) * 100;
}, },
hackChance : function(ip) { hackChance : function(ip) {
updateDynamicRam("hackChance", getRamCost("hackChance")); updateDynamicRam("hackChance", getRamCost("hackChance"));
const server = safeGetServer(ip, 'hackChance'); const server = safeGetServer(ip, 'hackChance');
return calculateHackingChance(server); return calculateHackingChance(server, Player);
}, },
sleep : function(time){ sleep : function(time){
if (time === undefined) { if (time === undefined) {
@ -830,21 +876,21 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeErrorMsg("grow", canHack.msg); 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}).`); workerScript.log("grow", `Executing on '${server.hostname}' in ${formatNumber(growTime, 3)} seconds (t=${threads}).`);
return netscriptDelay(growTime * 1000, workerScript).then(function() { return netscriptDelay(growTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable; const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
server.moneyAvailable += (1 * threads); // It can be grown even if it has no money 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; const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads); workerScript.scriptRef.recordGrow(server.ip, threads);
var expGain = calculateHackingExpGain(server) * threads; var expGain = calculateHackingExpGain(server, Player) * threads;
if (growthPercentage == 1) { if (growthPercentage == 1) {
expGain = 0; expGain = 0;
} }
const logGrowPercent = (moneyAfter/moneyBefore)*100 - 100; 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; workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain); Player.gainHackingExp(expGain);
if (stock) { if (stock) {
@ -881,14 +927,14 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeErrorMsg("weaken", canHack.msg); 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})`); workerScript.log("weaken", `Executing on '${server.hostname}' in ${formatNumber(weakenTime, 3)} seconds (t=${threads})`);
return netscriptDelay(weakenTime * 1000, workerScript).then(function() { return netscriptDelay(weakenTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);} if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
server.weaken(CONSTANTS.ServerWeakenAmount * threads); server.weaken(CONSTANTS.ServerWeakenAmount * threads);
workerScript.scriptRef.recordWeaken(server.ip, threads); workerScript.scriptRef.recordWeaken(server.ip, threads);
var expGain = calculateHackingExpGain(server) * threads; var expGain = calculateHackingExpGain(server, Player) * threads;
workerScript.log("weaken", `'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.format(expGain, '0.000a')} hacking exp (t=${threads})`); workerScript.log("weaken", `'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${threads})`);
workerScript.scriptRef.onlineExpGained += expGain; workerScript.scriptRef.onlineExpGained += expGain;
Player.gainHackingExp(expGain); Player.gainHackingExp(expGain);
return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads); return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads);
@ -1508,16 +1554,33 @@ function NetscriptFunctions(workerScript) {
let copy = Object.assign({}, BitNodeMultipliers); let copy = Object.assign({}, BitNodeMultipliers);
return copy; 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) { getServerMoneyAvailable: function(ip) {
updateDynamicRam("getServerMoneyAvailable", getRamCost("getServerMoneyAvailable")); updateDynamicRam("getServerMoneyAvailable", getRamCost("getServerMoneyAvailable"));
const server = safeGetServer(ip, "getServerMoneyAvailable"); const server = safeGetServer(ip, "getServerMoneyAvailable");
if (failOnHacknetServer(server, "getServerMoneyAvailable")) { return 0; } if (failOnHacknetServer(server, "getServerMoneyAvailable")) { return 0; }
if (server.hostname == "home") { if (server.hostname == "home") {
// Return player's money // 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(); 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; return server.moneyAvailable;
}, },
getServerSecurityLevel: function(ip) { getServerSecurityLevel: function(ip) {
@ -1552,7 +1615,7 @@ function NetscriptFunctions(workerScript) {
updateDynamicRam("getServerMaxMoney", getRamCost("getServerMaxMoney")); updateDynamicRam("getServerMaxMoney", getRamCost("getServerMaxMoney"));
const server = safeGetServer(ip, "getServerMaxMoney"); const server = safeGetServer(ip, "getServerMaxMoney");
if (failOnHacknetServer(server, "getServerMaxMoney")) { return 0; } 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; return server.moneyMax;
}, },
getServerGrowth: function(ip) { getServerGrowth: function(ip) {
@ -1959,7 +2022,7 @@ function NetscriptFunctions(workerScript) {
} }
if (Player.money.lt(cost)) { 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 ""; return "";
} }
var newServ = safetlyCreateUniqueServer({ var newServ = safetlyCreateUniqueServer({
@ -1978,7 +2041,7 @@ function NetscriptFunctions(workerScript) {
homeComputer.serversOnNetwork.push(newServ.ip); homeComputer.serversOnNetwork.push(newServ.ip);
newServ.serversOnNetwork.push(homeComputer.ip); newServ.serversOnNetwork.push(homeComputer.ip);
Player.loseMoney(cost); 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; return newServ.hostname;
}, },
deleteServer: function(hostname) { deleteServer: function(hostname) {
@ -2300,21 +2363,21 @@ function NetscriptFunctions(workerScript) {
const server = safeGetServer(ip, "getHackTime"); const server = safeGetServer(ip, "getHackTime");
if (failOnHacknetServer(server, "getHackTime")) { return Infinity; } if (failOnHacknetServer(server, "getHackTime")) { return Infinity; }
return calculateHackingTime(server, hack, int); // Returns seconds return calculateHackingTime(server, Player); // Returns seconds
}, },
getGrowTime: function(ip, hack, int) { getGrowTime: function(ip, hack, int) {
updateDynamicRam("getGrowTime", getRamCost("getGrowTime")); updateDynamicRam("getGrowTime", getRamCost("getGrowTime"));
const server = safeGetServer(ip, "getGrowTime"); const server = safeGetServer(ip, "getGrowTime");
if (failOnHacknetServer(server, "getGrowTime")) { return Infinity; } if (failOnHacknetServer(server, "getGrowTime")) { return Infinity; }
return calculateGrowTime(server, hack, int); // Returns seconds return calculateGrowTime(server, Player); // Returns seconds
}, },
getWeakenTime: function(ip, hack, int) { getWeakenTime: function(ip, hack, int) {
updateDynamicRam("getWeakenTime", getRamCost("getWeakenTime")); updateDynamicRam("getWeakenTime", getRamCost("getWeakenTime"));
const server = safeGetServer(ip, "getWeakenTime"); const server = safeGetServer(ip, "getWeakenTime");
if (failOnHacknetServer(server, "getWeakenTime")) { return Infinity; } if (failOnHacknetServer(server, "getWeakenTime")) { return Infinity; }
return calculateWeakenTime(server, hack, int); // Returns seconds return calculateWeakenTime(server, Player); // Returns seconds
}, },
getScriptIncome: function(scriptname, ip) { getScriptIncome: function(scriptname, ip) {
updateDynamicRam("getScriptIncome", getRamCost("getScriptIncome")); updateDynamicRam("getScriptIncome", getRamCost("getScriptIncome"));
@ -2695,7 +2758,7 @@ function NetscriptFunctions(workerScript) {
} }
if(Player.money.lt(item.price)) { 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; return false;
} }
@ -2710,7 +2773,14 @@ function NetscriptFunctions(workerScript) {
workerScript.log("purchaseProgram", `You have purchased the '${item.program}' program. The new program can be found on your home computer.`); workerScript.log("purchaseProgram", `You have purchased the '${item.program}' program. The new program can be found on your home computer.`);
return true; return true;
}, },
getCurrentServer: function() {
updateDynamicRam("getCurrentServer", getRamCost("getCurrentServer"));
checkSingularityAccess("getCurrentServer", 1);
return Player.getCurrentServer().hostname;
},
connect: function(hostname) { connect: function(hostname) {
updateDynamicRam("connect", getRamCost("connect"));
checkSingularityAccess("connect", 1);
if (!hostname) { if (!hostname) {
throw makeRuntimeErrorMsg("connect", `Invalid hostname: '${hostname}'`); throw makeRuntimeErrorMsg("connect", `Invalid hostname: '${hostname}'`);
} }
@ -2754,6 +2824,7 @@ function NetscriptFunctions(workerScript) {
getStats: function() { getStats: function() {
updateDynamicRam("getStats", getRamCost("getStats")); updateDynamicRam("getStats", getRamCost("getStats"));
checkSingularityAccess("getStats", 1); checkSingularityAccess("getStats", 1);
workerScript.log("getStats", `getStats is deprecated, please use getPlayer`);
return { return {
hacking: Player.hacking_skill, hacking: Player.hacking_skill,
@ -2768,6 +2839,7 @@ function NetscriptFunctions(workerScript) {
getCharacterInformation: function() { getCharacterInformation: function() {
updateDynamicRam("getCharacterInformation", getRamCost("getCharacterInformation")); updateDynamicRam("getCharacterInformation", getRamCost("getCharacterInformation"));
checkSingularityAccess("getCharacterInformation", 1); checkSingularityAccess("getCharacterInformation", 1);
workerScript.log("getCharacterInformation", `getCharacterInformation is deprecated, please use getPlayer`);
return { return {
bitnode: Player.bitNodeN, bitnode: Player.bitNodeN,
@ -2812,6 +2884,96 @@ function NetscriptFunctions(workerScript) {
charismaExp: Player.charisma_exp, 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: {},
};
Object.assign(data.jobs, Player.jobs);
return data;
},
isBusy: function() { isBusy: function() {
updateDynamicRam("isBusy", getRamCost("isBusy")); updateDynamicRam("isBusy", getRamCost("isBusy"));
checkSingularityAccess("isBusy", 1); checkSingularityAccess("isBusy", 1);
@ -2840,7 +3002,7 @@ function NetscriptFunctions(workerScript) {
const cost = Player.getUpgradeHomeRamCost(); const cost = Player.getUpgradeHomeRamCost();
if (Player.money.lt(cost)) { 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; return false;
} }
@ -2995,7 +3157,7 @@ function NetscriptFunctions(workerScript) {
joinFaction: function(name) { joinFaction: function(name) {
updateDynamicRam("joinFaction", getRamCost("joinFaction")); updateDynamicRam("joinFaction", getRamCost("joinFaction"));
checkSingularityAccess("joinFaction", 2); checkSingularityAccess("joinFaction", 2);
getFaction("workForFaction", name); getFaction("joinFaction", name);
if (!Player.factionInvitations.includes(name)) { if (!Player.factionInvitations.includes(name)) {
workerScript.log("joinFaction", `You have not been invited by faction '${name}'`); workerScript.log("joinFaction", `You have not been invited by faction '${name}'`);
@ -3127,7 +3289,7 @@ function NetscriptFunctions(workerScript) {
return false; return false;
} }
if (Player.money.lt(amt)) { 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; return false;
} }
const repNeededToDonate = Math.round(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); const repNeededToDonate = Math.round(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
@ -3138,7 +3300,7 @@ function NetscriptFunctions(workerScript) {
const repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult; const repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
faction.playerReputation += repGain; faction.playerReputation += repGain;
Player.loseMoney(amt); 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; return true;
}, },
createProgram: function(name) { createProgram: function(name) {
@ -4088,6 +4250,110 @@ function NetscriptFunctions(workerScript) {
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug); return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
} }
}, // End sleeve }, // 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: { heart: {
// Easter egg function // Easter egg function
break: function() { break: function() {

@ -5,6 +5,8 @@ import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentatio
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { calculateSkill } from "./formulas/skill";
import { calculateIntelligenceBonus } from "./formulas/intelligence";
// Interface that defines a generic object used to track experience/money // Interface that defines a generic object used to track experience/money
// earnings for tasks // earnings for tasks
@ -127,7 +129,7 @@ export abstract class Person {
* stat level. Stat-agnostic (same formula for every stat) * stat level. Stat-agnostic (same formula for every stat)
*/ */
calculateStat(exp: number, mult: number=1): number { calculateStat(exp: number, mult: number=1): number {
return Math.max(Math.floor(mult*(32 * Math.log(exp + 534.5) - 200)), 1); return calculateSkill(exp, mult);
} }
/** /**
@ -212,10 +214,6 @@ export abstract class Person {
getIntelligenceBonus(weight: number): number { getIntelligenceBonus(weight: number): number {
// 15 => +1.4% when you initially acquire int return calculateIntelligenceBonus(this.intelligence, weight);
// 50 => +3.8% mid game
// 100 => +6.6% late game
// 250 => +13.4% realistic best possible
return 1+(weight*Math.pow(this.intelligence, 0.8)/600);
} }
} }

@ -28,6 +28,8 @@ import { Locations } from "../../Locations/Locations";
import { CityName } from "../../Locations/data/CityNames"; import { CityName } from "../../Locations/data/CityNames";
import { LocationName } from "../../Locations/data/LocationNames"; import { LocationName } from "../../Locations/data/LocationNames";
import { Sleeve } from "../../PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "../../PersonObjects/Sleeve/Sleeve";
import { calculateSkill as calculateSkillF } from "../formulas/skill";
import { calculateIntelligenceBonus } from "../formulas/intelligence";
import { import {
AllServers, AllServers,
AddToAllServers, AddToAllServers,
@ -55,6 +57,14 @@ import {
} from "../../../utils/JSONReviver"; } from "../../../utils/JSONReviver";
import {convertTimeMsToTimeElapsedString} from "../../../utils/StringHelperFunctions"; import {convertTimeMsToTimeElapsedString} from "../../../utils/StringHelperFunctions";
import { Reputation } from "../../ui/React/Reputation";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { ReputationRate } from "../../ui/React/ReputationRate";
import React from "react";
import ReactDOM from "react-dom";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
export function init() { export function init() {
@ -293,7 +303,7 @@ export function receiveInvite(factionName) {
//Calculates skill level based on experience. The same formula will be used for every skill //Calculates skill level based on experience. The same formula will be used for every skill
export function calculateSkill(exp, mult=1) { export function calculateSkill(exp, mult=1) {
return Math.max(Math.floor(mult*(32 * Math.log(exp + 534.5) - 200)), 1); return calculateSkillF(exp, mult);
} }
export function updateSkillLevels() { export function updateSkillLevels() {
@ -520,7 +530,7 @@ export function resetWorkStatus() {
this.createProgramName = ""; this.createProgramName = "";
this.className = ""; this.className = "";
document.getElementById("work-in-progress-text").innerHTML = ""; ReactDOM.unmountComponentAtNode(document.getElementById("work-in-progress-text"));
} }
export function processWorkEarnings(numCycles=1) { export function processWorkEarnings(numCycles=1) {
@ -611,22 +621,22 @@ export function work(numCycles) {
const position = this.jobs[this.companyName]; const position = this.jobs[this.companyName];
var txt = document.getElementById("work-in-progress-text"); var elem = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + position + ReactDOM.render(<>
" at " + this.companyName + " (Current Company Reputation: " + You are currently working as a {position} at {this.companyName} (Current Company Reputation: {Reputation(companyRep)})<br /><br />
numeralWrapper.format(companyRep, '0,0') + ")<br><br>" + You have been working for {convertTimeMsToTimeElapsedString(this.timeWorked)}<br /><br />
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + You have earned: <br /><br />
"You have earned: <br><br>" + {Money(this.workMoneyGained)} ({MoneyRate(this.workMoneyGainRate * CYCLES_PER_SEC)}) <br /><br />
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + " ($" + numeralWrapper.format(this.workMoneyGainRate * CYCLES_PER_SEC, '0,0.00') + " / sec) <br><br>" + {Reputation(this.workRepGained)} ({ReputationRate(this.workRepGainRate * CYCLES_PER_SEC)}) reputation for this company <br /><br />
numeralWrapper.format(this.workRepGained, '0,0.0000') + " (" + numeralWrapper.format(this.workRepGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) reputation for this company <br><br>" + {numeralWrapper.formatExp(this.workHackExpGained)} ({`${numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}) hacking exp <br /><br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workHackExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) hacking exp <br><br>" + {numeralWrapper.formatExp(this.workStrExpGained)} ({`${numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}) strength exp <br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workStrExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) strength exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} ({`${numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}) defense exp <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDefExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) defense exp <br>" + {numeralWrapper.formatExp(this.workDexExpGained)} ({`${numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}) dexterity exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDexExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) dexterity exp <br>" + {numeralWrapper.formatExp(this.workAgiExpGained)} ({`${numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}) agility exp <br /><br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workAgiExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) agility exp <br><br> " + {numeralWrapper.formatExp(this.workChaExpGained)} ({`${numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}) charisma exp <br /><br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workChaExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) charisma exp <br><br>" + You will automatically finish after working for 8 hours. You can cancel earlier if you wish,
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish, " + but you will only gain half of the reputation you've earned so far.
"but you will only gain half of the reputation you've earned so far." </>, elem);
} }
export function finishWork(cancelled, sing=false) { export function finishWork(cancelled, sing=false) {
@ -640,23 +650,27 @@ export function finishWork(cancelled, sing=false) {
this.updateSkillLevels(); this.updateSkillLevels();
var txt = "You earned a total of: <br>" + let content = <>
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + "<br>" + You earned a total of: <br />
numeralWrapper.format(this.workRepGained, '0,0.0000') + " reputation for the company <br>" + {Money(this.workMoneyGained)}<br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp <br>" + {Reputation(this.workRepGained)} reputation for the company <br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp <br>" + {numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp <br>" + {numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp <br>" + {numeralWrapper.formatExp(this.workDexExpGained)} dexterity exp <br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp<br>"; {numeralWrapper.formatExp(this.workAgiExpGained)} agility exp <br />
{numeralWrapper.formatExp(this.workChaExpGained)} charisma exp<br />
</>
if (cancelled) { if (cancelled) {
txt = "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " <br><br> " + content = <>
"Since you cancelled your work early, you only gained half of the reputation you earned. <br><br>" + txt; You worked a short shift of {convertTimeMsToTimeElapsedString(this.timeWorked)} <br /><br />
Since you cancelled your work early, you only gained half of the reputation you earned. <br /><br />{content}
</>
} else { } else {
txt = "You worked a full shift of 8 hours! <br><br> " + txt; content = <>You worked a full shift of 8 hours! <br /><br />{content}</>;
} }
if (!sing) {dialogBoxCreate(txt);} if (!sing) {dialogBoxCreate(content);}
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
@ -665,14 +679,14 @@ export function finishWork(cancelled, sing=false) {
if (sing) { if (sing) {
var res = "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " + var res = "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
"earned $" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + ", " + "earned $" + numeralWrapper.formatMoney(this.workMoneyGained) + ", " +
numeralWrapper.format(this.workRepGained, '0,0.0000') + " reputation, " + numeralWrapper.formatReputation(this.workRepGained) + " reputation, " +
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp, " + numeralWrapper.formatExp(this.workHackExpGained) + " hacking exp, " +
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp, " + numeralWrapper.formatExp(this.workStrExpGained) + " strength exp, " +
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp, " + numeralWrapper.formatExp(this.workDefExpGained) + " defense exp, " +
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp, " + numeralWrapper.formatExp(this.workDexExpGained) + " dexterity exp, " +
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp, and " + numeralWrapper.formatExp(this.workAgiExpGained) + " agility exp, and " +
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp."; numeralWrapper.formatExp(this.workChaExpGained) + " charisma exp.";
this.resetWorkStatus(); this.resetWorkStatus();
return res; return res;
} }
@ -734,23 +748,21 @@ export function workPartTime(numCycles) {
const position = this.jobs[this.companyName]; const position = this.jobs[this.companyName];
var txt = document.getElementById("work-in-progress-text"); const elem = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working as a " + position + ReactDOM.render(<>
" at " + this.companyName + " (Current Company Reputation: " + You are currently working as a {position} at {this.companyName} (Current Company Reputation: {Reputation(companyRep)})<br /><br />
numeralWrapper.format(companyRep, '0,0') + ")<br><br>" + You have been working for {convertTimeMsToTimeElapsedString(this.timeWorked)}<br /><br />
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + You have earned: <br /><br />
"You have earned: <br><br>" + {Money(this.workMoneyGained)} ({MoneyRate(this.workMoneyGainRate * CYCLES_PER_SEC)}) <br /><br />
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + " ($" + numeralWrapper.format(this.workMoneyGainRate * CYCLES_PER_SEC, '0,0.00') + " / sec) <br><br>" + {Reputation(this.workRepGained)} ({Reputation(`${numeralWrapper.formatExp(this.workRepGainRate * CYCLES_PER_SEC)} / sec`)}) reputation for this company <br /><br />
numeralWrapper.format(this.workRepGained, '0,0.0000') + " (" + numeralWrapper.format(this.workRepGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) reputation for this company <br><br>" + {numeralWrapper.formatExp(this.workHackExpGained)} ({`${numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}) hacking exp <br /><br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workHackExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) hacking exp <br><br>" + {numeralWrapper.formatExp(this.workStrExpGained)} ({`${numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}) strength exp <br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workStrExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) strength exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} ({`${numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}) defense exp <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDefExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) defense exp <br>" + {numeralWrapper.formatExp(this.workDexExpGained)} ({`${numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}) dexterity exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDexExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) dexterity exp <br>" + {numeralWrapper.formatExp(this.workAgiExpGained)} ({`${numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}) agility exp <br /><br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workAgiExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) agility exp <br><br> " + {numeralWrapper.formatExp(this.workChaExpGained)} ({`${numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}) charisma exp <br /><br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workChaExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) charisma exp <br><br>" + You will automatically finish after working for 8 hours. You can cancel earlier if you wish, and there will be no penalty because this is a part-time job.
"You will automatically finish after working for 8 hours. You can cancel earlier if you wish, <br>" + </>, elem);
"and there will be no penalty because this is a part-time job.";
} }
export function finishWorkPartTime(sing=false) { export function finishWorkPartTime(sing=false) {
@ -759,17 +771,19 @@ export function finishWorkPartTime(sing=false) {
this.updateSkillLevels(); this.updateSkillLevels();
var txt = "You earned a total of: <br>" + const content = <>
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + "<br>" + You worked for {convertTimeMsToTimeElapsedString(this.timeWorked)}<br /><br />
numeralWrapper.format(this.workRepGained, '0,0.0000') + " reputation for the company <br>" + You earned a total of: <br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp <br>" + {Money(this.workMoneyGained)}<br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp <br>" + {Reputation(this.workRepGained)} reputation for the company <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp <br>" + {numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp <br>" + {numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp<br>"; {numeralWrapper.formatExp(this.workDexExpGained)} dexterity exp <br />
txt = "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br> " + txt; {numeralWrapper.formatExp(this.workAgiExpGained)} agility exp <br />
if (!sing) {dialogBoxCreate(txt);} {numeralWrapper.formatExp(this.workChaExpGained)} charisma exp<br />
</>;
if (!sing) {dialogBoxCreate(content);}
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
@ -778,14 +792,14 @@ export function finishWorkPartTime(sing=false) {
if (sing) { if (sing) {
var res = "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " + var res = "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
"earned a total of " + "earned a total of " +
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + ", " + "$" + numeralWrapper.formatMoney(this.workMoneyGained) + ", " +
numeralWrapper.format(this.workRepGained, '0,0.0000') + " reputation, " + numeralWrapper.formatReputation(this.workRepGained) + " reputation, " +
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp, " + numeralWrapper.formatExp(this.workHackExpGained) + " hacking exp, " +
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp, " + numeralWrapper.formatExp(this.workStrExpGained) + " strength exp, " +
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp, " + numeralWrapper.formatExp(this.workDefExpGained) + " defense exp, " +
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp, " + numeralWrapper.formatExp(this.workDexExpGained) + " dexterity exp, " +
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp, and " + numeralWrapper.formatExp(this.workAgiExpGained) + " agility exp, and " +
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp"; numeralWrapper.formatExp(this.workChaExpGained) + " charisma exp";
this.resetWorkStatus(); this.resetWorkStatus();
return res; return res;
} }
@ -902,22 +916,22 @@ export function workForFaction(numCycles) {
return this.finishFactionWork(false); return this.finishFactionWork(false);
} }
var txt = document.getElementById("work-in-progress-text"); const elem = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently " + this.currentWorkFactionDescription + " for your faction " + faction.name + ReactDOM.render(<>You are currently {this.currentWorkFactionDescription} for your faction {faction.name}<br />
" (Current Faction Reputation: " + numeralWrapper.format(faction.playerReputation, '0,0') + "). <br>" + (Current Faction Reputation: {Reputation(faction.playerReputation)}). <br />
"You have been doing this for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + You have been doing this for {convertTimeMsToTimeElapsedString(this.timeWorked)}<br /><br />
"You have earned: <br><br>" + You have earned: <br /><br />
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + " (" + numeralWrapper.format(this.workMoneyGainRate * CYCLES_PER_SEC, '0,0.00') + " / sec) <br><br>" + {Money(this.workMoneyGained)} ({MoneyRate(this.workMoneyGainRate * CYCLES_PER_SEC)}) <br /><br />
numeralWrapper.format(this.workRepGained, '0,0.0000') + " (" + numeralWrapper.format(this.workRepGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) reputation for this faction <br><br>" + {Reputation(this.workRepGained)} ({ReputationRate(this.workRepGainRate * CYCLES_PER_SEC)}) reputation for this faction <br /><br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workHackExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) hacking exp <br><br>" + {numeralWrapper.formatExp(this.workHackExpGained)} ({numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp <br /><br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workStrExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) strength exp <br>" + {numeralWrapper.formatExp(this.workStrExpGained)} ({numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDefExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) defense exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} ({numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDexExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) dexterity exp <br>" + {numeralWrapper.formatExp(this.workDexExpGained)} ({numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp <br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workAgiExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) agility exp <br><br> " + {numeralWrapper.formatExp(this.workAgiExpGained)} ({numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp <br /><br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workChaExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) charisma exp <br><br>" + {numeralWrapper.formatExp(this.workChaExpGained)} ({numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp <br /><br />
"You will automatically finish after working for 20 hours. You can cancel earlier if you wish.<br>" + You will automatically finish after working for 20 hours. You can cancel earlier if you wish.<br />
"There is no penalty for cancelling earlier."; There is no penalty for cancelling earlier.</>, elem)
} }
export function finishFactionWork(cancelled, sing=false) { export function finishFactionWork(cancelled, sing=false) {
@ -926,17 +940,20 @@ export function finishFactionWork(cancelled, sing=false) {
this.updateSkillLevels(); this.updateSkillLevels();
var txt = "You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " <br><br> " + if (!sing) {
"You earned a total of: <br>" + dialogBoxCreate(<>
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + "<br>" + You worked for your faction {faction.name} for a total of {convertTimeMsToTimeElapsedString(this.timeWorked)} <br /><br />
numeralWrapper.format(this.workRepGained, '0,0.0000') + " reputation for the faction <br>" + You earned a total of: <br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp <br>" + {Money(this.workMoneyGained)}<br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp <br>" + {Reputation(this.workRepGained)} reputation for the faction <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp <br>" + {numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp <br>" + {numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp<br>"; {numeralWrapper.formatExp(this.workDexExpGained)} dexterity exp <br />
if (!sing) {dialogBoxCreate(txt);} {numeralWrapper.formatExp(this.workAgiExpGained)} agility exp <br />
{numeralWrapper.formatExp(this.workChaExpGained)} charisma exp<br />
</>);
}
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
@ -948,13 +965,13 @@ export function finishFactionWork(cancelled, sing=false) {
if (sing) { if (sing) {
var res="You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " + var res="You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " +
"You earned " + "You earned " +
numeralWrapper.format(this.workRepGained, '0,0.0000') + " rep, " + numeralWrapper.formatReputation(this.workRepGained) + " rep, " +
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp, " + numeralWrapper.formatExp(this.workHackExpGained) + " hacking exp, " +
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " str exp, " + numeralWrapper.formatExp(this.workStrExpGained) + " str exp, " +
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " def exp, " + numeralWrapper.formatExp(this.workDefExpGained) + " def exp, " +
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dex exp, " + numeralWrapper.formatExp(this.workDexExpGained) + " dex exp, " +
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agi exp, and " + numeralWrapper.formatExp(this.workAgiExpGained) + " agi exp, and " +
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " cha exp."; numeralWrapper.formatExp(this.workChaExpGained) + " cha exp.";
this.resetWorkStatus(); this.resetWorkStatus();
return res; return res;
} }
@ -1169,11 +1186,13 @@ export function createProgramWork(numCycles) {
this.finishCreateProgramWork(false); this.finishCreateProgramWork(false);
} }
var txt = document.getElementById("work-in-progress-text"); const elem = document.getElementById("work-in-progress-text");
txt.innerHTML = "You are currently working on coding " + programName + ".<br><br> " + ReactDOM.render(<>
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + You are currently working on coding {programName}.<br /><br />
"The program is " + (this.timeWorkedCreateProgram / this.timeNeededToCompleteWork * 100).toFixed(2) + "% complete. <br>" + You have been working for {convertTimeMsToTimeElapsedString(this.timeWorked)}<br /><br />
"If you cancel, your work will be saved and you can come back to complete the program later."; The program is {(this.timeWorkedCreateProgram / this.timeNeededToCompleteWork * 100).toFixed(2)}% complete. <br />
If you cancel, your work will be saved and you can come back to complete the program later.
</>, elem);
} }
export function finishCreateProgramWork(cancelled, sing=false) { export function finishCreateProgramWork(cancelled, sing=false) {
@ -1210,9 +1229,7 @@ export function startClass(costMult, expMult, className) {
this.className = className; this.className = className;
var gameCPS = 1000 / Engine._idleSpeed; const gameCPS = 1000 / Engine._idleSpeed;
const baseGymExp = 1;
//Find cost and exp gain per game cycle //Find cost and exp gain per game cycle
var cost = 0; var cost = 0;
@ -1244,19 +1261,19 @@ export function startClass(costMult, expMult, className) {
break; break;
case CONSTANTS.ClassGymStrength: case CONSTANTS.ClassGymStrength:
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS; cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
strExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult(); strExp = expMult / gameCPS * hashManager.getTrainingMult();
break; break;
case CONSTANTS.ClassGymDefense: case CONSTANTS.ClassGymDefense:
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS; cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
defExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult(); defExp = expMult / gameCPS * hashManager.getTrainingMult();
break; break;
case CONSTANTS.ClassGymDexterity: case CONSTANTS.ClassGymDexterity:
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS; cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
dexExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult(); dexExp = expMult / gameCPS * hashManager.getTrainingMult();
break; break;
case CONSTANTS.ClassGymAgility: case CONSTANTS.ClassGymAgility:
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS; cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
agiExp = baseGymExp * expMult / gameCPS * hashManager.getTrainingMult(); agiExp = expMult / gameCPS * hashManager.getTrainingMult();
break; break;
default: default:
throw new Error("ERR: Invalid/unrecognized class name"); throw new Error("ERR: Invalid/unrecognized class name");
@ -1294,19 +1311,21 @@ export function takeClass(numCycles) {
var className = this.className; var className = this.className;
this.processWorkEarnings(numCycles); this.processWorkEarnings(numCycles);
var txt = document.getElementById("work-in-progress-text"); const elem = document.getElementById("work-in-progress-text");
txt.innerHTML = "You have been " + className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" + ReactDOM.render(<>
"This has cost you: <br>" + You have been {className} for {convertTimeMsToTimeElapsedString(this.timeWorked)}<br /><br />
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + " ($" + numeralWrapper.format(this.workMoneyLossRate * CYCLES_PER_SEC, '0,0.00') + " / sec) <br><br>" + This has cost you: <br />
"You have gained: <br>" + {Money(-this.workMoneyGained)} ({MoneyRate(this.workMoneyLossRate * CYCLES_PER_SEC)}) <br /><br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workHackExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) hacking exp <br>" + You have gained: <br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workStrExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) strength exp <br>" + {numeralWrapper.formatExp(this.workHackExpGained)} ({numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDefExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) defense exp <br>" + {numeralWrapper.formatExp(this.workStrExpGained)} ({numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workDexExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) dexterity exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} ({numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp <br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workAgiExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) agility exp <br>" + {numeralWrapper.formatExp(this.workDexExpGained)} ({numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp <br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " (" + numeralWrapper.format(this.workChaExpGainRate * CYCLES_PER_SEC, '0,0.0000') + " / sec) charisma exp <br>" + {numeralWrapper.formatExp(this.workAgiExpGained)} ({numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp <br />
"You may cancel at any time"; {numeralWrapper.formatExp(this.workChaExpGained)} ({numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp <br />
You may cancel at any time
</>, elem);
} }
//The 'sing' argument defines whether or not this function was called //The 'sing' argument defines whether or not this function was called
@ -1319,16 +1338,19 @@ export function finishClass(sing=false) {
} }
this.updateSkillLevels(); this.updateSkillLevels();
var txt = "After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", <br>" + if (!sing) {
"you spent a total of $" + numeralWrapper.format(this.workMoneyGained * -1, '0,0.00') + ". <br><br>" + dialogBoxCreate(<>
"You earned a total of: <br>" + After {this.className} for {convertTimeMsToTimeElapsedString(this.timeWorked)}, <br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp <br>" + you spent a total of {Money(this.workMoneyGained * -1)}. <br /><br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp <br>" + You earned a total of: <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp <br>" + {numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp <br>" + {numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp <br>" + {numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp<br>"; {numeralWrapper.formatExp(this.workDexExpGained)} dexterity exp <br />
if (!sing) {dialogBoxCreate(txt);} {numeralWrapper.formatExp(this.workAgiExpGained)} agility exp <br />
{numeralWrapper.formatExp(this.workChaExpGained)} charisma exp<br />
</>);
}
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
@ -1338,14 +1360,14 @@ export function finishClass(sing=false) {
Engine.loadLocationContent(false); Engine.loadLocationContent(false);
if (sing) { if (sing) {
var res="After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", " + var res="After " + this.className + " for " + convertTimeMsToTimeElapsedString(this.timeWorked) + ", " +
"you spent a total of $" + numeralWrapper.format(this.workMoneyGained * -1, '0,0.00') + ". " + "you spent a total of " + numeralWrapper.formatMoney(this.workMoneyGained * -1) + ". " +
"You earned a total of: " + "You earned a total of: " +
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking exp, " + numeralWrapper.formatExp(this.workHackExpGained) + " hacking exp, " +
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength exp, " + numeralWrapper.formatExp(this.workStrExpGained) + " strength exp, " +
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense exp, " + numeralWrapper.formatExp(this.workDefExpGained) + " defense exp, " +
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity exp, " + numeralWrapper.formatExp(this.workDexExpGained) + " dexterity exp, " +
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility exp, and " + numeralWrapper.formatExp(this.workAgiExpGained) + " agility exp, and " +
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma exp"; numeralWrapper.formatExp(this.workChaExpGained) + " charisma exp";
this.resetWorkStatus(); this.resetWorkStatus();
return res; return res;
} }
@ -1438,24 +1460,26 @@ export function finishCrime(cancelled) {
if (this.committingCrimeThruSingFn) { if (this.committingCrimeThruSingFn) {
if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) { if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) {
this.singFnCrimeWorkerScript.scriptRef.log("Crime successful! Gained " + this.singFnCrimeWorkerScript.scriptRef.log("Crime successful! Gained " +
numeralWrapper.format(this.workMoneyGained, "$0.000a") + ", " + numeralWrapper.formatMoney(this.workMoneyGained) + ", " +
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hack exp, " + numeralWrapper.formatExp(this.workHackExpGained) + " hack exp, " +
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " str exp, " + numeralWrapper.formatExp(this.workStrExpGained) + " str exp, " +
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " def exp, " + numeralWrapper.formatExp(this.workDefExpGained) + " def exp, " +
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dex exp, " + numeralWrapper.formatExp(this.workDexExpGained) + " dex exp, " +
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agi exp, " + numeralWrapper.formatExp(this.workAgiExpGained) + " agi exp, " +
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " cha exp."); numeralWrapper.formatExp(this.workChaExpGained) + " cha exp.");
} }
} else { } else {
dialogBoxCreate("Crime successful! <br><br>" + dialogBoxCreate(<>
"You gained:<br>"+ Crime successful!<br /><br />
"$" + numeralWrapper.format(this.workMoneyGained, '0,0.00') + "<br>" + You gained:<br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking experience <br>" + {Money(this.workMoneyGained)}<br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength experience<br>" + {numeralWrapper.formatExp(this.workHackExpGained)} hacking experience <br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense experience<br>" + {numeralWrapper.formatExp(this.workStrExpGained)} strength experience<br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity experience<br>" + {numeralWrapper.formatExp(this.workDefExpGained)} defense experience<br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility experience<br>" + {numeralWrapper.formatExp(this.workDexExpGained)} dexterity experience<br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma experience"); {numeralWrapper.formatExp(this.workAgiExpGained)} agility experience<br />
{numeralWrapper.formatExp(this.workChaExpGained)} charisma experience
</>);
} }
} else { } else {
@ -1469,22 +1493,25 @@ export function finishCrime(cancelled) {
if (this.committingCrimeThruSingFn) { if (this.committingCrimeThruSingFn) {
if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) { if(this.singFnCrimeWorkerScript.disableLogs.ALL == null && this.singFnCrimeWorkerScript.disableLogs.commitCrime == null) {
this.singFnCrimeWorkerScript.scriptRef.log("Crime failed! Gained " + this.singFnCrimeWorkerScript.scriptRef.log("Crime failed! Gained " +
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hack exp, " + numeralWrapper.formatExp(this.workHackExpGained) + " hack exp, " +
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " str exp, " + numeralWrapper.formatExp(this.workStrExpGained) + " str exp, " +
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " def exp, " + numeralWrapper.formatExp(this.workDefExpGained) + " def exp, " +
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dex exp, " + numeralWrapper.formatExp(this.workDexExpGained) + " dex exp, " +
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agi exp, " + numeralWrapper.formatExp(this.workAgiExpGained) + " agi exp, " +
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " cha exp."); numeralWrapper.formatExp(this.workChaExpGained) + " cha exp.");
} }
} else { } else {
dialogBoxCreate("Crime failed! <br><br>" + dialogBoxCreate(<>
"You gained:<br>"+ Crime failed!<br /><br />
numeralWrapper.format(this.workHackExpGained, '0,0.0000') + " hacking experience <br>" + You gained:<br />
numeralWrapper.format(this.workStrExpGained, '0,0.0000') + " strength experience<br>" + {Money(this.workMoneyGained)}<br />
numeralWrapper.format(this.workDefExpGained, '0,0.0000') + " defense experience<br>" + {numeralWrapper.formatExp(this.workHackExpGained)} hacking experience <br />
numeralWrapper.format(this.workDexExpGained, '0,0.0000') + " dexterity experience<br>" + {numeralWrapper.formatExp(this.workStrExpGained)} strength experience<br />
numeralWrapper.format(this.workAgiExpGained, '0,0.0000') + " agility experience<br>" + {numeralWrapper.formatExp(this.workDefExpGained)} defense experience<br />
numeralWrapper.format(this.workChaExpGained, '0,0.0000') + " charisma experience"); {numeralWrapper.formatExp(this.workDexExpGained)} dexterity experience<br />
{numeralWrapper.formatExp(this.workAgiExpGained)} agility experience<br />
{numeralWrapper.formatExp(this.workChaExpGained)} charisma experience
</>);
} }
} }
@ -1563,11 +1590,11 @@ export function regenerateHp(amt) {
export function hospitalize() { export function hospitalize() {
if (Settings.SuppressHospitalizationPopup === false) { if (Settings.SuppressHospitalizationPopup === false) {
dialogBoxCreate( dialogBoxCreate(<>
"You were in critical condition! You were taken to the hospital where " + You were in critical condition! You were taken to the hospital where
"luckily they were able to save your life. You were charged " + luckily they were able to save your life. You were charged&nbsp;
numeralWrapper.format(this.max_hp * CONSTANTS.HospitalCostPerHp, '$0.000a') {Money(this.max_hp * CONSTANTS.HospitalCostPerHp)}
); </>);
} }
const cost = this.max_hp * CONSTANTS.HospitalCostPerHp const cost = this.max_hp * CONSTANTS.HospitalCostPerHp
@ -2271,7 +2298,7 @@ export function gainCodingContractReward(reward, difficulty=1) {
var moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney; var moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney;
this.gainMoney(moneyGain); this.gainMoney(moneyGain);
this.recordMoneySource(moneyGain, "codingcontract"); this.recordMoneySource(moneyGain, "codingcontract");
return `Gained ${numeralWrapper.format(moneyGain, '$0.000a')}`; return `Gained ${numeralWrapper.formatMoney(moneyGain)}`;
break; break;
} }
/* eslint-enable no-case-declarations */ /* eslint-enable no-case-declarations */
@ -2309,9 +2336,5 @@ export function giveExploit(exploit) {
export function getIntelligenceBonus(weight) { export function getIntelligenceBonus(weight) {
// 15 => +1.4% when you initially acquire int return calculateIntelligenceBonus(this.intelligence, weight);
// 50 => +3.8% mid game
// 100 => +6.6% late game
// 250 => +13.4% realistic best possible
return 1+(weight*Math.pow(this.intelligence, 0.8)/600);
} }

@ -11,6 +11,7 @@ import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { Page, import { Page,
routing } from "../../ui/navigationTracking"; routing } from "../../ui/navigationTracking";
@ -24,6 +25,9 @@ import { getSelectValue } from "../../../utils/uiHelpers/getSelectData";
import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement"; import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../../../utils/uiHelpers/removeElement"; import { removeElement } from "../../../utils/uiHelpers/removeElement";
import * as React from "react";
import { renderToStaticMarkup } from "react-dom/server"
interface IResleeveUIElems { interface IResleeveUIElems {
container: HTMLElement | null; container: HTMLElement | null;
statsPanel: HTMLElement | null; statsPanel: HTMLElement | null;
@ -74,13 +78,13 @@ export function createResleevesPage(p: IPlayer) {
innerHTML: "Re-sleeving is the process of digitizing and transferring your consciousness " + innerHTML: "Re-sleeving is the process of digitizing and transferring your consciousness " +
"into a new human body, or 'sleeve'. Here at VitaLife, you can purchase new " + "into a new human body, or 'sleeve'. Here at VitaLife, you can purchase new " +
"specially-engineered bodies for the re-sleeve process. Many of these bodies " + "specially-engineered bodies for the re-sleeve process. Many of these bodies " +
"even come with genetic and cybernetic Augmentations!<br><br>" + "even come with genetic and cybernetic Augmentations!<br /><br />" +
"Re-sleeving will change your experience for every stat. It will also REMOVE " + "Re-sleeving will change your experience for every stat. It will also REMOVE " +
"all of your currently-installed Augmentations, and replace " + "all of your currently-installed Augmentations, and replace " +
"them with the ones provided by the purchased sleeve. However, Augmentations that you have " + "them with the ones provided by the purchased sleeve. However, Augmentations that you have " +
"purchased but not installed will NOT be removed. If you have purchased an " + "purchased but not installed will NOT be removed. If you have purchased an " +
"Augmentation and then re-sleeve into a body which already has that Augmentation, " + "Augmentation and then re-sleeve into a body which already has that Augmentation, " +
"it will be removed (since you cannot have duplicate Augmentations).<br><br>" + "it will be removed (since you cannot have duplicate Augmentations).<br /><br />" +
"NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from " + "NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from " +
"Source-File.", "Source-File.",
width: "75%", width: "75%",
@ -225,7 +229,7 @@ export function clearResleevesPage() {
} }
for (const prop in UIElems) { for (const prop in UIElems) {
(<any>UIElems)[prop] = null; (UIElems as any)[prop] = null;
} }
playerRef = null; playerRef = null;
@ -256,12 +260,12 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
elems.stats = createElement("p", { elems.stats = createElement("p", {
class: "resleeve-stats-text", class: "resleeve-stats-text",
innerHTML: innerHTML:
`Hacking: ${numeralWrapper.format(resleeve.hacking_skill, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.hacking_exp)} exp)<br>` + `Hacking: ${numeralWrapper.formatSkill(resleeve.hacking_skill)} (${numeralWrapper.formatExp(resleeve.hacking_exp)} exp)<br />` +
`Strength: ${numeralWrapper.format(resleeve.strength, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.strength_exp)} exp)<br>` + `Strength: ${numeralWrapper.formatSkill(resleeve.strength)} (${numeralWrapper.formatExp(resleeve.strength_exp)} exp)<br />` +
`Defense: ${numeralWrapper.format(resleeve.defense, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.defense_exp)} exp)<br>` + `Defense: ${numeralWrapper.formatSkill(resleeve.defense)} (${numeralWrapper.formatExp(resleeve.defense_exp)} exp)<br />` +
`Dexterity: ${numeralWrapper.format(resleeve.dexterity, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.dexterity_exp)} exp)<br>` + `Dexterity: ${numeralWrapper.formatSkill(resleeve.dexterity)} (${numeralWrapper.formatExp(resleeve.dexterity_exp)} exp)<br />` +
`Agility: ${numeralWrapper.format(resleeve.agility, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.agility_exp)} exp)<br>` + `Agility: ${numeralWrapper.formatSkill(resleeve.agility)} (${numeralWrapper.formatExp(resleeve.agility_exp)} exp)<br />` +
`Charisma: ${numeralWrapper.format(resleeve.charisma, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.charisma_exp)} exp)<br>` + `Charisma: ${numeralWrapper.formatSkill(resleeve.charisma)} (${numeralWrapper.formatExp(resleeve.charisma_exp)} exp)<br />` +
`# Augmentations: ${resleeve.augmentations.length}`, `# Augmentations: ${resleeve.augmentations.length}`,
}); });
elems.multipliersButton = createElement("button", { elems.multipliersButton = createElement("button", {
@ -301,7 +305,7 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
`Bladeburner Stamina Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_stamina_gain_mult)}`, `Bladeburner Stamina Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_stamina_gain_mult)}`,
`Bladeburner Field Analysis multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_analysis_mult)}`, `Bladeburner Field Analysis multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_analysis_mult)}`,
`Bladeburner Success Chance multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_success_chance_mult)}` `Bladeburner Success Chance multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_success_chance_mult)}`
].join("<br>"), false ].join("<br />"), false
) )
} }
}); });
@ -324,7 +328,7 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
const cost: number = resleeve.getCost(); const cost: number = resleeve.getCost();
elems.costPanel = createElement("div", { class: "resleeve-panel", width: "20%" }); elems.costPanel = createElement("div", { class: "resleeve-panel", width: "20%" });
elems.costText = createElement("p", { elems.costText = createElement("p", {
innerText: `It costs ${numeralWrapper.formatMoney(cost)} ` + innerHTML: `It costs ${renderToStaticMarkup(Money(cost))} ` +
`to purchase this Sleeve.`, `to purchase this Sleeve.`,
}); });
elems.buyButton = createElement("button", { elems.buyButton = createElement("button", {
@ -332,7 +336,7 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
innerText: "Purchase", innerText: "Purchase",
clickListener: () => { clickListener: () => {
if (purchaseResleeve(resleeve, playerRef!)) { if (purchaseResleeve(resleeve, playerRef!)) {
dialogBoxCreate(`You re-sleeved for ${numeralWrapper.formatMoney(cost)}!`, false); dialogBoxCreate((<>You re-sleeved for {Money(cost)}!</>), false);
} else { } else {
dialogBoxCreate(`You cannot afford to re-sleeve into this body`, false); dialogBoxCreate(`You cannot afford to re-sleeve into this body`, false);
} }

@ -11,6 +11,7 @@ import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -19,6 +20,8 @@ import { createPopup } from "../../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton"; import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
import { renderToStaticMarkup } from "react-dom/server"
export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) { export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) {
// Array of all owned Augmentations. Names only // Array of all owned Augmentations. Names only
const ownedAugNames: string[] = sleeve.augmentations.map((e) => {return e.name}); const ownedAugNames: string[] = sleeve.augmentations.map((e) => {return e.name});
@ -86,7 +89,7 @@ export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) {
innerHTML: innerHTML:
[ [
`<h2>${aug.name}</h2><br>`, `<h2>${aug.name}</h2><br>`,
`Cost: ${numeralWrapper.formatMoney(aug.startingCost)}<br><br>`, `Cost: ${renderToStaticMarkup(Money(aug.startingCost))}<br><br>`,
`${aug.info}` `${aug.info}`
].join(" "), ].join(" "),
padding: "2px", padding: "2px",

@ -40,11 +40,15 @@ import { removeElement } from "../../../utils/uiHelpers/removeElement";
import { removeElementById } from "../../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
import { EarningsTableElement } from "./ui/EarningsTableElement"; import { EarningsTableElement } from "./ui/EarningsTableElement";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { ReputationRate } from "../../ui/React/ReputationRate";
import { StatsElement } from "./ui/StatsElement"; import { StatsElement } from "./ui/StatsElement";
import { MoreStatsContent } from "./ui/MoreStatsContent"; import { MoreStatsContent } from "./ui/MoreStatsContent";
import { MoreEarningsContent } from "./ui/MoreEarningsContent"; import { MoreEarningsContent } from "./ui/MoreEarningsContent";
import * as React from "react"; import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import { renderToStaticMarkup } from "react-dom/server"
// Object that keeps track of all DOM elements for the UI for a single Sleeve // Object that keeps track of all DOM elements for the UI for a single Sleeve
interface ISleeveUIElems { interface ISleeveUIElems {
@ -218,9 +222,9 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
const popupArguments: HTMLElement[] = []; const popupArguments: HTMLElement[] = [];
popupArguments.push(createPopupCloseButton(popupId, { class: "std-button" })); popupArguments.push(createPopupCloseButton(popupId, { class: "std-button" }));
popupArguments.push(createElement("p", { popupArguments.push(createElement("p", {
innerText: "Have this sleeve travel to a different city. This affects " + innerHTML: "Have this sleeve travel to a different city. This affects " +
"the gyms and universities at which this sleeve can study. " + "the gyms and universities at which this sleeve can study. " +
`Traveling to a different city costs ${numeralWrapper.formatMoney(CONSTANTS.TravelCost)}. ` + `Traveling to a different city costs ${renderToStaticMarkup(Money(CONSTANTS.TravelCost))}. ` +
"It will also CANCEL the sleeve's current task (setting it to idle)", "It will also CANCEL the sleeve's current task (setting it to idle)",
})); }));
for (const cityName in Cities) { for (const cityName in Cities) {
@ -331,13 +335,13 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
if (sleeve.currentTask === SleeveTaskType.Crime) { if (sleeve.currentTask === SleeveTaskType.Crime) {
const data = [ const data = [
[`Money`, numeralWrapper.formatMoney(parseFloat(sleeve.currentTaskLocation)), `(on success)`], [`Money`, Money(parseFloat(sleeve.currentTaskLocation)), `(on success)`],
[`Hacking Exp`, numeralWrapper.format(sleeve.gainRatesForTask.hack, "0.00"), `(2x on success)`], [`Hacking Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.hack), `(2x on success)`],
[`Strength Exp`, numeralWrapper.format(sleeve.gainRatesForTask.str, "0.00"), `(2x on success)`], [`Strength Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.str), `(2x on success)`],
[`Defense Exp`, numeralWrapper.format(sleeve.gainRatesForTask.def, "0.00"), `(2x on success)`], [`Defense Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.def), `(2x on success)`],
[`Dexterity Exp`, numeralWrapper.format(sleeve.gainRatesForTask.dex, "0.00"), `(2x on success)`], [`Dexterity Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.dex), `(2x on success)`],
[`Agility Exp`, numeralWrapper.format(sleeve.gainRatesForTask.agi, "0.00"), `(2x on success)`], [`Agility Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.agi), `(2x on success)`],
[`Charisma Exp`, numeralWrapper.format(sleeve.gainRatesForTask.cha, "0.00"), `(2x on success)`] [`Charisma Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.cha), `(2x on success)`]
]; ];
ReactDOM.render(EarningsTableElement('Earnings (Pre-Synchronization)', data), elems.currentEarningsInfo!) ReactDOM.render(EarningsTableElement('Earnings (Pre-Synchronization)', data), elems.currentEarningsInfo!)
@ -347,18 +351,18 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
}); });
} else { } else {
const data = [ const data = [
[`Money:`, `${numeralWrapper.formatMoney(5 * sleeve.gainRatesForTask.money)} / s`], [`Money:`, MoneyRate(5 * sleeve.gainRatesForTask.money)],
[`Hacking Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.hack, "0.00")} / s`], [`Hacking Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.hack)} / s`],
[`Strength Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.str, "0.00")} / s`], [`Strength Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.str)} / s`],
[`Defense Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.def, "0.00")} / s`], [`Defense Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.def)} / s`],
[`Dexterity Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.dex, "0.00")} / s`], [`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.dex)} / s`],
[`Agility Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.agi, "0.00")} / s`], [`Agility Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.agi)} / s`],
[`Charisma Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.cha, "0.00")} / s`] [`Charisma Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.cha)} / s`]
]; ];
let repGainText: string = ""; let repGainText: string = "";
if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) { if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) {
const repGain: number = sleeve.getRepGain(playerRef!); const repGain: number = sleeve.getRepGain(playerRef!);
data.push([`Reputation:`, `${numeralWrapper.format(5 * repGain, "0.00")} / s`]); data.push([`Reputation:`, ReputationRate(5 * repGain)]);
} }
ReactDOM.render(EarningsTableElement('Earnings (Pre-Synchronization)', data), elems.currentEarningsInfo!) ReactDOM.render(EarningsTableElement('Earnings (Pre-Synchronization)', data), elems.currentEarningsInfo!)

@ -16,6 +16,7 @@ import { numeralWrapper } from "../../../ui/numeralFormat";
import { PopupCloseButton } from "../../../ui/React/PopupCloseButton"; import { PopupCloseButton } from "../../../ui/React/PopupCloseButton";
import { StdButton } from "../../../ui/React/StdButton"; import { StdButton } from "../../../ui/React/StdButton";
import { Money } from "../../../ui/React/Money";
import { dialogBoxCreate } from "../../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../../utils/DialogBox";
@ -92,7 +93,7 @@ export class CovenantPurchasesRoot extends React.Component<IProps, IState> {
<PopupCloseButton popup={PopupId} text={"Close"} /> <PopupCloseButton popup={PopupId} text={"Close"} />
<p> <p>
Would you like to purchase an additional Duplicate Sleeve from The Covenant Would you like to purchase an additional Duplicate Sleeve from The Covenant
for {numeralWrapper.formatMoney(this.purchaseCost())}? for {Money(this.purchaseCost())}?
</p> </p>
<br /> <br />
<p> <p>

@ -9,6 +9,7 @@ import { IPlayer } from "../../IPlayer";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { StdButton } from "../../../ui/React/StdButton"; import { StdButton } from "../../../ui/React/StdButton";
import { Money } from "../../../ui/React/Money";
interface IProps { interface IProps {
index: number; index: number;
@ -68,13 +69,13 @@ export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState>
// Purchase button props // Purchase button props
const cost = this.getPurchaseCost(); const cost = this.getPurchaseCost();
const purchaseBtnDisabled = !this.props.p.canAfford(cost); const purchaseBtnDisabled = !this.props.p.canAfford(cost);
let purchaseBtnText; let purchaseBtnContent;
if (isNaN(this.state.amt)) { if (isNaN(this.state.amt)) {
purchaseBtnText = "Invalid value"; purchaseBtnContent = <>Invalid value</>;
} else if (this.state.amt > maxMemory) { } else if (this.state.amt > maxMemory) {
purchaseBtnText = `Memory cannot exceed 100`; purchaseBtnContent = <>Memory cannot exceed 100?</>;
} else { } else {
purchaseBtnText = `Purchase ${this.state.amt} memory - ${numeralWrapper.formatMoney(cost)}`; purchaseBtnContent = <>Purchase {this.state.amt} memory - {Money(cost)}?</>;
} }
return ( return (
@ -82,7 +83,7 @@ export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState>
<h2><u>Upgrade Memory</u></h2> <h2><u>Upgrade Memory</u></h2>
<p> <p>
Purchase a memory upgrade for your sleeve. Note that a sleeve's max memory Purchase a memory upgrade for your sleeve. Note that a sleeve's max memory
is 100 (current: {numeralWrapper.format(this.props.sleeve.memory, "0")}) is 100 (current: {numeralWrapper.formatMemory(this.props.sleeve.memory)})
</p> </p>
<label htmlFor={inputId}> <label htmlFor={inputId}>
@ -90,7 +91,7 @@ export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState>
</label> </label>
<input id={inputId} onChange={this.changePurchaseAmount} type={"number"} value={isNaN(this.state.amt) ? this.state.amt.toString() : this.state.amt} /> <input id={inputId} onChange={this.changePurchaseAmount} type={"number"} value={isNaN(this.state.amt) ? this.state.amt.toString() : this.state.amt} />
<br /> <br />
<StdButton disabled={purchaseBtnDisabled} onClick={this.purchaseMemory} text={purchaseBtnText} /> <StdButton disabled={purchaseBtnDisabled} onClick={this.purchaseMemory} text={purchaseBtnContent} />
</div> </div>
) )
} }

@ -1,5 +1,6 @@
import { Sleeve } from "../Sleeve"; import { Sleeve } from "../Sleeve";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { Money } from "../../../ui/React/Money";
import * as React from "react"; import * as React from "react";
import { StatsTable } from "../../../ui/React/StatsTable"; import { StatsTable } from "../../../ui/React/StatsTable";
@ -8,33 +9,33 @@ export function MoreEarningsContent(sleeve: Sleeve): React.ReactElement {
style = {textAlign: 'right'}; style = {textAlign: 'right'};
return (<> return (<>
{StatsTable([ {StatsTable([
['Money ', numeralWrapper.formatMoney(sleeve.earningsForTask.money)], ['Money ', Money(sleeve.earningsForTask.money)],
['Hacking Exp ', numeralWrapper.formatBigNumber(sleeve.earningsForTask.hack)], ['Hacking Exp ', numeralWrapper.formatExp(sleeve.earningsForTask.hack)],
['Strength Exp ', numeralWrapper.formatBigNumber(sleeve.earningsForTask.str)], ['Strength Exp ', numeralWrapper.formatExp(sleeve.earningsForTask.str)],
['Defense Exp ', numeralWrapper.formatBigNumber(sleeve.earningsForTask.def)], ['Defense Exp ', numeralWrapper.formatExp(sleeve.earningsForTask.def)],
['Dexterity Exp ', numeralWrapper.formatBigNumber(sleeve.earningsForTask.dex)], ['Dexterity Exp ', numeralWrapper.formatExp(sleeve.earningsForTask.dex)],
['Agility Exp ', numeralWrapper.formatBigNumber(sleeve.earningsForTask.agi)], ['Agility Exp ', numeralWrapper.formatExp(sleeve.earningsForTask.agi)],
['Charisma Exp ', numeralWrapper.formatBigNumber(sleeve.earningsForTask.cha)], ['Charisma Exp ', numeralWrapper.formatExp(sleeve.earningsForTask.cha)],
], 'Earnings for Current Task:')} ], 'Earnings for Current Task:')}
<br /> <br />
{StatsTable([ {StatsTable([
['Money: ', numeralWrapper.formatMoney(sleeve.earningsForPlayer.money)], ['Money: ', Money(sleeve.earningsForPlayer.money)],
['Hacking Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.hack)], ['Hacking Exp: ', numeralWrapper.formatExp(sleeve.earningsForPlayer.hack)],
['Strength Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.str)], ['Strength Exp: ', numeralWrapper.formatExp(sleeve.earningsForPlayer.str)],
['Defense Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.def)], ['Defense Exp: ', numeralWrapper.formatExp(sleeve.earningsForPlayer.def)],
['Dexterity Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.dex)], ['Dexterity Exp: ', numeralWrapper.formatExp(sleeve.earningsForPlayer.dex)],
['Agility Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.agi)], ['Agility Exp: ', numeralWrapper.formatExp(sleeve.earningsForPlayer.agi)],
['Charisma Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.cha)], ['Charisma Exp: ', numeralWrapper.formatExp(sleeve.earningsForPlayer.cha)],
], 'Total Earnings for Host Consciousness:')} ], 'Total Earnings for Host Consciousness:')}
<br /> <br />
{StatsTable([ {StatsTable([
['Money: ', numeralWrapper.formatMoney(sleeve.earningsForSleeves.money)], ['Money: ', Money(sleeve.earningsForSleeves.money)],
['Hacking Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.hack)], ['Hacking Exp: ', numeralWrapper.formatExp(sleeve.earningsForSleeves.hack)],
['Strength Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.str)], ['Strength Exp: ', numeralWrapper.formatExp(sleeve.earningsForSleeves.str)],
['Defense Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.def)], ['Defense Exp: ', numeralWrapper.formatExp(sleeve.earningsForSleeves.def)],
['Dexterity Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.dex)], ['Dexterity Exp: ', numeralWrapper.formatExp(sleeve.earningsForSleeves.dex)],
['Agility Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.agi)], ['Agility Exp: ', numeralWrapper.formatExp(sleeve.earningsForSleeves.agi)],
['Charisma Exp: ', numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.cha)], ['Charisma Exp: ', numeralWrapper.formatExp(sleeve.earningsForSleeves.cha)],
], 'Total Earnings for Other Sleeves:')} ], 'Total Earnings for Other Sleeves:')}
<br /> <br />
</>); </>);

@ -8,12 +8,12 @@ export function MoreStatsContent(sleeve: Sleeve): React.ReactElement {
style = {textAlign: 'right'}; style = {textAlign: 'right'};
return (<> return (<>
{StatsTable([ {StatsTable([
['Hacking: ', sleeve.hacking_skill, `(${numeralWrapper.formatBigNumber(sleeve.hacking_exp)} exp)`], ['Hacking: ', sleeve.hacking_skill, `(${numeralWrapper.formatExp(sleeve.hacking_exp)} exp)`],
['Strength: ', sleeve.strength, `(${numeralWrapper.formatBigNumber(sleeve.strength_exp)} exp)`], ['Strength: ', sleeve.strength, `(${numeralWrapper.formatExp(sleeve.strength_exp)} exp)`],
['Defense: ', sleeve.defense, `(${numeralWrapper.formatBigNumber(sleeve.defense_exp)} exp)`], ['Defense: ', sleeve.defense, `(${numeralWrapper.formatExp(sleeve.defense_exp)} exp)`],
['Dexterity: ', sleeve.dexterity, `(${numeralWrapper.formatBigNumber(sleeve.dexterity_exp)} exp)`], ['Dexterity: ', sleeve.dexterity, `(${numeralWrapper.formatExp(sleeve.dexterity_exp)} exp)`],
['Agility: ', sleeve.agility, `(${numeralWrapper.formatBigNumber(sleeve.agility_exp)} exp)`], ['Agility: ', sleeve.agility, `(${numeralWrapper.formatExp(sleeve.agility_exp)} exp)`],
['Charisma: ', sleeve.charisma, `(${numeralWrapper.formatBigNumber(sleeve.charisma_exp)} exp)`], ['Charisma: ', sleeve.charisma, `(${numeralWrapper.formatExp(sleeve.charisma_exp)} exp)`],
], 'Stats:')} ], 'Stats:')}
<br /> <br />
{StatsTable([ {StatsTable([

@ -10,7 +10,7 @@ export function StatsElement(sleeve: Sleeve): React.ReactElement {
<tbody> <tbody>
<tr> <tr>
<td className="character-hp-cell">HP: </td> <td className="character-hp-cell">HP: </td>
<td className="character-hp-cell" style={style}>{numeralWrapper.format(sleeve.hp, "0,0")} / {numeralWrapper.format(sleeve.max_hp, "0,0")}</td> <td className="character-hp-cell" style={style}>{numeralWrapper.formatHp(sleeve.hp)} / {numeralWrapper.formatHp(sleeve.max_hp)}</td>
</tr> </tr>
<tr> <tr>
<td>City: </td> <td>City: </td>
@ -18,39 +18,39 @@ export function StatsElement(sleeve: Sleeve): React.ReactElement {
</tr> </tr>
<tr> <tr>
<td className="character-hack-cell">Hacking: </td> <td className="character-hack-cell">Hacking: </td>
<td className="character-hack-cell" style={style}>{numeralWrapper.format(sleeve.hacking_skill, "0,0")}</td> <td className="character-hack-cell" style={style}>{numeralWrapper.formatSkill(sleeve.hacking_skill)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-combat-cell">Strength: </td> <td className="character-combat-cell">Strength: </td>
<td className="character-combat-cell" style={style}>{numeralWrapper.format(sleeve.strength, "0,0")}</td> <td className="character-combat-cell" style={style}>{numeralWrapper.formatSkill(sleeve.strength)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-combat-cell">Defense: </td> <td className="character-combat-cell">Defense: </td>
<td className="character-combat-cell" style={style}>{numeralWrapper.format(sleeve.defense, "0,0")}</td> <td className="character-combat-cell" style={style}>{numeralWrapper.formatSkill(sleeve.defense)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-combat-cell">Dexterity: </td> <td className="character-combat-cell">Dexterity: </td>
<td className="character-combat-cell" style={style}>{numeralWrapper.format(sleeve.dexterity, "0,0")}</td> <td className="character-combat-cell" style={style}>{numeralWrapper.formatSkill(sleeve.dexterity)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-combat-cell">Agility: </td> <td className="character-combat-cell">Agility: </td>
<td className="character-combat-cell" style={style}>{numeralWrapper.format(sleeve.agility, "0,0")}</td> <td className="character-combat-cell" style={style}>{numeralWrapper.formatSkill(sleeve.agility)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-cha-cell">Charisma: </td> <td className="character-cha-cell">Charisma: </td>
<td className="character-cha-cell" style={style}>{numeralWrapper.format(sleeve.charisma, "0,0")}</td> <td className="character-cha-cell" style={style}>{numeralWrapper.formatSkill(sleeve.charisma)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-int-cell">Shock: </td> <td className="character-int-cell">Shock: </td>
<td className="character-int-cell" style={style}>{numeralWrapper.format(100 - sleeve.shock, "0,0.000")}</td> <td className="character-int-cell" style={style}>{numeralWrapper.formatShock(100 - sleeve.shock)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-int-cell">Sync: </td> <td className="character-int-cell">Sync: </td>
<td className="character-int-cell" style={style}>{numeralWrapper.format(sleeve.sync, "0,0.000")}</td> <td className="character-int-cell" style={style}>{numeralWrapper.formatSync(sleeve.sync)}</td>
</tr> </tr>
<tr> <tr>
<td className="character-int-cell">Memory: </td> <td className="character-int-cell">Memory: </td>
<td className="character-int-cell" style={style}>{numeralWrapper.format(sleeve.memory, "0")}</td> <td className="character-int-cell" style={style}>{numeralWrapper.formatMemory(sleeve.memory)}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

@ -0,0 +1,3 @@
export function calculateIntelligenceBonus(intelligence: number, weight: number = 1): number {
return 1+(weight*Math.pow(intelligence, 0.8)/600);
}

@ -0,0 +1,7 @@
export function calculateSkill(exp: number, mult: number = 1): number {
return Math.max(Math.floor(mult*(32 * Math.log(exp + 534.5) - 200)), 1);
}
export function calculateExp(skill: number, mult: number = 1): number {
return Math.exp((skill / mult + 200) / 32) - 534.6
}

@ -471,7 +471,7 @@ function loadImportedGame(saveObj, saveString) {
}); });
var gotitBtn = createElement("a", { var gotitBtn = createElement("a", {
class:"a-link-button", float:"right", padding:"6px", innerText:"Got it!", class:"a-link-button", float:"right", padding:"6px", innerText:"Got it!",
clickListener:()=>{ clickListener:() => {
removeElementById(popupId); removeElementById(popupId);
} }
}); });
@ -508,8 +508,8 @@ function loadImportedGame(saveObj, saveString) {
// Hacknet Nodes offline progress // Hacknet Nodes offline progress
var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline); var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline);
const hacknetProdInfo = hasHacknetServers() ? const hacknetProdInfo = hasHacknetServers() ?
`${numeralWrapper.format(offlineProductionFromHacknetNodes, "0.000a")} hashes` : <>Hashes(offlineProductionFromHacknetNodes)} hashes</> :
`${numeralWrapper.formatMoney(offlineProductionFromHacknetNodes)}`; Money(offlineProductionFromHacknetNodes);
// Passive faction rep gain offline // Passive faction rep gain offline
processPassiveFactionRepGain(numCyclesOffline); processPassiveFactionRepGain(numCyclesOffline);
@ -532,10 +532,9 @@ function loadImportedGame(saveObj, saveString) {
Player.lastUpdate = Engine._lastUpdate; Player.lastUpdate = Engine._lastUpdate;
Engine.start(); // Run main game loop and Scripts loop Engine.start(); // Run main game loop and Scripts loop
const timeOfflineString = convertTimeMsToTimeElapsedString(time); const timeOfflineString = convertTimeMsToTimeElapsedString(time);
dialogBoxCreate(`Offline for ${timeOfflineString}. While you were offline, your scripts ` + dialogBoxCreate(<>Offline for {timeOfflineString}. While you were offline, your scripts
"generated <span class='money-gold'>" + generated {Money(offlineProductionFromScripts)}
numeralWrapper.formatMoney(offlineProductionFromScripts) + "</span> " + and your Hacknet Nodes generated hacknetProdInfo</>);
"and your Hacknet Nodes generated <span class='money-gold'>" + hacknetProdInfo + "</span>");
return true; return true;
} }

@ -191,7 +191,7 @@ export async function updateScriptEditorContent() {
var codeCopy = code.repeat(1); var codeCopy = code.repeat(1);
var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts); var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts);
if (ramUsage > 0) { if (ramUsage > 0) {
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB"; scriptEditorRamText.innerText = "RAM: " + numeralWrapper.formatRAM(ramUsage);
} else { } else {
switch (ramUsage) { switch (ramUsage) {
case RamCalculationErrorCode.ImportError: case RamCalculationErrorCode.ImportError:
@ -334,7 +334,7 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
if (serv == null) {continue;} if (serv == null) {continue;}
var timesGrown = Math.round(0.5 * runningScriptObj.dataMap[ip][2] / runningScriptObj.onlineRunningTime * timePassed); var timesGrown = Math.round(0.5 * runningScriptObj.dataMap[ip][2] / runningScriptObj.onlineRunningTime * timePassed);
runningScriptObj.log("Called grow() on " + serv.hostname + " " + timesGrown + " times while offline"); runningScriptObj.log("Called grow() on " + serv.hostname + " " + timesGrown + " times while offline");
var growth = processSingleServerGrowth(serv, timesGrown * 450, Player); var growth = processSingleServerGrowth(serv, timesGrown, Player);
runningScriptObj.log(serv.hostname + " grown by " + numeralWrapper.format(growth * 100 - 100, '0.000000%') + " from grow() calls made while offline"); runningScriptObj.log(serv.hostname + " grown by " + numeralWrapper.format(growth * 100 - 100, '0.000000%') + " from grow() calls made while offline");
} }
} }

@ -4,6 +4,7 @@ import {
ipExists, ipExists,
} from "./AllServers"; } from "./AllServers";
import { Server, IConstructorParams } from "./Server"; import { Server, IConstructorParams } from "./Server";
import { calculateServerGrowth } from "./formulas/grow";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
@ -59,21 +60,8 @@ export function numCycleForGrowth(server: Server, growth: number, p: IPlayer) {
} }
//Applied server growth for a single server. Returns the percentage growth //Applied server growth for a single server. Returns the percentage growth
export function processSingleServerGrowth(server: Server, numCycles: number, p: IPlayer) { export function processSingleServerGrowth(server: Server, threads: number, p: IPlayer) {
//Server growth processed once every 450 game cycles let serverGrowth = calculateServerGrowth(server, threads, p);
const numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0);
//Get adjusted growth rate, which accounts for server security
const growthRate = CONSTANTS.ServerBaseGrowthRate;
var adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty;
if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;}
//Calculate adjusted server growth rate based on parameters
const serverGrowthPercentage = server.serverGrowth / 100;
const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles
let serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * p.hacking_grow_mult);
if (serverGrowth < 1) { if (serverGrowth < 1) {
console.warn("serverGrowth calculated to be less than 1"); console.warn("serverGrowth calculated to be less than 1");
serverGrowth = 1; serverGrowth = 1;

@ -0,0 +1,20 @@
import { CONSTANTS } from "../../Constants";
import { Server } from "../Server";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { IPlayer } from "../../PersonObjects/IPlayer";
export function calculateServerGrowth(server: Server, threads: number, p: IPlayer) {
const numServerGrowthCycles = Math.max(Math.floor(threads), 0);
//Get adjusted growth rate, which accounts for server security
const growthRate = CONSTANTS.ServerBaseGrowthRate;
let adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty;
if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;}
//Calculate adjusted server growth rate based on parameters
const serverGrowthPercentage = server.serverGrowth / 100;
const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles
return Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * p.hacking_grow_mult);
}

@ -16,9 +16,14 @@ import { WorkerScript } from "../Netscript/WorkerScript";
import { Player } from "../Player"; import { Player } from "../Player";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { renderToStaticMarkup } from "react-dom/server"
/** /**
* Each function takes an optional config object as its last argument * Each function takes an optional config object as its last argument
*/ */
@ -58,7 +63,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
if (tixApi) { if (tixApi) {
workerScript!.log("buyStock", `You do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}.`); workerScript!.log("buyStock", `You do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}.`);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`); dialogBoxCreate(<>You do not have enough money to purchase this. You need {Money(totalPrice)}</>);
} }
return false; return false;
@ -69,7 +74,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
if (tixApi) { if (tixApi) {
workerScript!.log("buyStock", `Purchasing '${shares + stock.playerShares + stock.playerShortShares}' shares would exceed ${stock.symbol}'s maximum (${stock.maxShares}) number of shares`); workerScript!.log("buyStock", `Purchasing '${shares + stock.playerShares + stock.playerShortShares}' shares would exceed ${stock.symbol}'s maximum (${stock.maxShares}) number of shares`);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${numeralWrapper.formatBigNumber(stock.maxShares)} shares.`); dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${numeralWrapper.formatShares(stock.maxShares)} shares.`);
} }
return false; return false;
@ -85,12 +90,12 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
opts.rerenderFn(); opts.rerenderFn();
} }
const resultTxt = `Bought ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol} for ${numeralWrapper.formatMoney(totalPrice)}. ` +
`Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.`
if (tixApi) { if (tixApi) {
const resultTxt = `Bought ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol} for ${numeralWrapper.formatMoney(totalPrice)}. ` +
`Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.`
workerScript!.log("buyStock", resultTxt) workerScript!.log("buyStock", resultTxt)
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate(resultTxt); dialogBoxCreate(<>Bought {numeralWrapper.formatShares(shares)} shares of {stock.symbol} for {Money(totalPrice)}. Paid {Money(CONSTANTS.StockMarketCommission)} in commission fees.</>);
} }
return true; return true;
@ -143,12 +148,13 @@ export function sellStock(stock: Stock, shares: number, workerScript: WorkerScri
opts.rerenderFn(); opts.rerenderFn();
} }
const resultTxt = `Sold ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`;
if (tixApi) { if (tixApi) {
const resultTxt = `Sold ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`;
workerScript!.log("sellStock", resultTxt) workerScript!.log("sellStock", resultTxt)
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate(resultTxt); dialogBoxCreate(<>Sold {numeralWrapper.formatShares(shares)} shares of {stock.symbol}. After commissions, you gained a total of {Money(gains)}.</>);
} }
return true; return true;
@ -187,8 +193,7 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr
"money to purchase this short position. You need " + "money to purchase this short position. You need " +
numeralWrapper.formatMoney(totalPrice)); numeralWrapper.formatMoney(totalPrice));
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate("You do not have enough money to purchase this short position. You need " + dialogBoxCreate(<>You do not have enough money to purchase this short position. You need {Money(totalPrice)}</>);
numeralWrapper.formatMoney(totalPrice));
} }
return false; return false;
@ -216,13 +221,13 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr
opts.rerenderFn(); opts.rerenderFn();
} }
const resultTxt = `Bought a short position of ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol} ` +
`for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} ` +
`in commission fees.`;
if (tixApi) { if (tixApi) {
const resultTxt = `Bought a short position of ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol} ` +
`for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} ` +
`in commission fees.`;
workerScript!.log("shortStock", resultTxt); workerScript!.log("shortStock", resultTxt);
} else if (!opts.suppressDialog) { } else if (!opts.suppressDialog) {
dialogBoxCreate(resultTxt); dialogBoxCreate(<>Bought a short position of {numeralWrapper.formatShares(shares)} shares of {stock.symbol} for {Money(totalPrice)}. Paid {Money(CONSTANTS.StockMarketCommission)} in commission fees.</>);
} }
return true; return true;
@ -283,12 +288,12 @@ export function sellShort(stock: Stock, shares: number, workerScript: WorkerScri
opts.rerenderFn(); opts.rerenderFn();
} }
const resultTxt = `Sold your short position of ${numeralWrapper.format(shares, '0,0')} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(totalGain)}`;
if (tixApi) { if (tixApi) {
const resultTxt = `Sold your short position of ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(totalGain)}`;
workerScript!.log("sellShort", resultTxt); workerScript!.log("sellShort", resultTxt);
} else if (!opts.suppressDialog) { } else if (!opts.suppressDialog) {
dialogBoxCreate(resultTxt); dialogBoxCreate(<>Sold your short position of {numeralWrapper.formatShares(shares)} shares of {stock.symbol}. After commissions, you gained a total of {Money(totalGain)}</>);
} }
return true; return true;

@ -19,9 +19,12 @@ import { PositionTypes } from "./data/PositionTypes";
import { IMap } from "../types"; import { IMap } from "../types";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import * as React from "react";
export interface IProcessOrderRefs { export interface IProcessOrderRefs {
rerenderFn: () => void; rerenderFn: () => void;
stockMarket: IStockMarket; stockMarket: IStockMarket;
@ -147,8 +150,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs) {
for (let i = 0; i < stockOrders.length; ++i) { for (let i = 0; i < stockOrders.length; ++i) {
if (order == stockOrders[i]) { if (order == stockOrders[i]) {
stockOrders.splice(i, 1); stockOrders.splice(i, 1);
dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` + dialogBoxCreate(<>{order.type} for {stock.symbol} @ {Money(order.price)} ({pos}) was filled ({numeralWrapper.formatShares(Math.round(order.shares))} shares)</>);
`(${numeralWrapper.formatBigNumber(Math.round(order.shares))} shares)`);
refs.rerenderFn(); refs.rerenderFn();
return; return;
} }
@ -158,8 +160,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs) {
console.error(order); console.error(order);
} else { } else {
if (isBuy) { if (isBuy) {
dialogBoxCreate(`Failed to execute ${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + dialogBoxCreate(<>Failed to execute {order.type} for {stock.symbol} @ {Money(order.price)} ({pos}). This is most likely because you do not have enough money or the order would exceed the stock's maximum number of shares</>);
`This is most likely because you do not have enough money or the order would exceed the stock's maximum number of shares`);
} }
} }
} }

@ -15,6 +15,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased"; import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -107,7 +108,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
<StdButton <StdButton
disabled={!this.props.p.canAfford(cost)} disabled={!this.props.p.canAfford(cost)}
onClick={this.purchaseWseAccount} onClick={this.purchaseWseAccount}
text={`Buy WSE Account - ${numeralWrapper.formatMoney(cost)}`} text={<>Buy WSE Account - {Money(cost)}</>}
/> />
) )
} }
@ -125,7 +126,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount} disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount}
onClick={this.purchaseTixApiAccess} onClick={this.purchaseTixApiAccess}
style={blockStyleMarkup} style={blockStyleMarkup}
text={`Buy Trade Information eXchange (TIX) API Access - ${numeralWrapper.formatMoney(cost)}`} text={<>Buy Trade Information eXchange (TIX) API Access - {Money(cost)}</>}
/> />
) )
} }
@ -145,7 +146,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
<StdButton <StdButton
disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount} disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount}
onClick={this.purchase4SMarketData} onClick={this.purchase4SMarketData}
text={`Buy 4S Market Data Access - ${numeralWrapper.formatMoney(cost)}`} text={<>Buy 4S Market Data Access - {Money(cost)}</>}
tooltip={"Lets you view additional pricing and volatility information about stocks"} tooltip={"Lets you view additional pricing and volatility information about stocks"}
/> />
) )
@ -174,7 +175,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
<StdButton <StdButton
disabled={!this.props.p.canAfford(cost)} disabled={!this.props.p.canAfford(cost)}
onClick={this.purchase4SMarketDataTixApiAccess} onClick={this.purchase4SMarketDataTixApiAccess}
text={`Buy 4S Market Data TIX API Access - ${numeralWrapper.formatMoney(cost)}`} text={<>Buy 4S Market Data TIX API Access - {Money(cost)}</>}
tooltip={"Let you access 4S Market Data through Netscript"} tooltip={"Let you access 4S Market Data through Netscript"}
/> />
) )
@ -216,7 +217,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
{this.renderPurchase4SMarketDataTixApiAccessButton()} {this.renderPurchase4SMarketDataTixApiAccessButton()}
<p> <p>
Commission Fees: Every transaction you make has Commission Fees: Every transaction you make has
a {numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} commission fee. a {Money(CONSTANTS.StockMarketCommission)} commission fee.
</p><br /> </p><br />
<p> <p>
WARNING: When you reset after installing Augmentations, the Stock WARNING: When you reset after installing Augmentations, the Stock

@ -22,6 +22,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Accordion } from "../../ui/React/Accordion"; import { Accordion } from "../../ui/React/Accordion";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { import {
@ -70,8 +71,8 @@ export class StockTicker extends React.Component<IProps, IState> {
qty: "", qty: "",
} }
this.getBuyTransactionCostText = this.getBuyTransactionCostText.bind(this); this.getBuyTransactionCostContent = this.getBuyTransactionCostContent.bind(this);
this.getSellTransactionCostText = this.getSellTransactionCostText.bind(this); this.getSellTransactionCostContent = this.getSellTransactionCostContent.bind(this);
this.handleBuyButtonClick = this.handleBuyButtonClick.bind(this); this.handleBuyButtonClick = this.handleBuyButtonClick.bind(this);
this.handleBuyMaxButtonClick = this.handleBuyMaxButtonClick.bind(this); this.handleBuyMaxButtonClick = this.handleBuyMaxButtonClick.bind(this);
this.handleHeaderClick = this.handleHeaderClick.bind(this); this.handleHeaderClick = this.handleHeaderClick.bind(this);
@ -106,46 +107,40 @@ export class StockTicker extends React.Component<IProps, IState> {
yesNoTxtInpBoxCreate(popupTxt); yesNoTxtInpBoxCreate(popupTxt);
} }
getBuyTransactionCostText(): string { getBuyTransactionCostContent(): JSX.Element | null {
const stock = this.props.stock; const stock = this.props.stock;
const qty: number = this.getQuantity(); const qty: number = this.getQuantity();
if (isNaN(qty)) { return ""; } if (isNaN(qty)) { return null; }
const cost = getBuyTransactionCost(stock, qty, this.state.position); const cost = getBuyTransactionCost(stock, qty, this.state.position);
if (cost == null) { return ""; } if (cost == null) { return null; }
let costTxt = `Purchasing ${numeralWrapper.formatBigNumber(qty)} shares (${this.state.position === PositionTypes.Long ? "Long" : "Short"}) ` + return <>Purchasing {numeralWrapper.formatShares(qty)} shares ({this.state.position === PositionTypes.Long ? "Long" : "Short"}) will cost {Money(cost)}.</>;
`will cost ${numeralWrapper.formatMoney(cost)}. `;
return costTxt;
} }
getQuantity(): number { getQuantity(): number {
return Math.round(parseFloat(this.state.qty)); return Math.round(parseFloat(this.state.qty));
} }
getSellTransactionCostText(): string { getSellTransactionCostContent(): JSX.Element | null {
const stock = this.props.stock; const stock = this.props.stock;
const qty: number = this.getQuantity(); const qty: number = this.getQuantity();
if (isNaN(qty)) { return ""; } if (isNaN(qty)) { return null; }
if (this.state.position === PositionTypes.Long) { if (this.state.position === PositionTypes.Long) {
if (qty > stock.playerShares) { if (qty > stock.playerShares) {
return `You do not have this many shares in the Long position`; return <>You do not have this many shares in the Long position</>;
} }
} else { } else {
if (qty > stock.playerShortShares) { if (qty > stock.playerShortShares) {
return `You do not have this many shares in the Short position`; return <>You do not have this many shares in the Short position</>;
} }
} }
const cost = getSellTransactionGain(stock, qty, this.state.position); const cost = getSellTransactionGain(stock, qty, this.state.position);
if (cost == null) { return ""; } if (cost == null) { return null; }
let costTxt = `Selling ${numeralWrapper.formatBigNumber(qty)} shares (${this.state.position === PositionTypes.Long ? "Long" : "Short"}) ` + return <>Selling {numeralWrapper.formatShares(qty)} shares ({this.state.position === PositionTypes.Long ? "Long" : "Short"}) will result in a gain of {Money(cost)}.</>;
`will result in a gain of ${numeralWrapper.formatMoney(cost)}. `;
return costTxt;
} }
handleBuyButtonClick() { handleBuyButtonClick() {
@ -380,8 +375,8 @@ export class StockTicker extends React.Component<IProps, IState> {
} }
</select> </select>
<StockTickerTxButton onClick={this.handleBuyButtonClick} text={"Buy"} tooltip={this.getBuyTransactionCostText()} /> <StockTickerTxButton onClick={this.handleBuyButtonClick} text={"Buy"} tooltip={this.getBuyTransactionCostContent()} />
<StockTickerTxButton onClick={this.handleSellButtonClick} text={"Sell"} tooltip={this.getSellTransactionCostText()} /> <StockTickerTxButton onClick={this.handleSellButtonClick} text={"Sell"} tooltip={this.getSellTransactionCostContent()} />
<StockTickerTxButton onClick={this.handleBuyMaxButtonClick} text={"Buy MAX"} /> <StockTickerTxButton onClick={this.handleBuyMaxButtonClick} text={"Buy MAX"} />
<StockTickerTxButton onClick={this.handleSellAllButtonClick} text={"Sell ALL"} /> <StockTickerTxButton onClick={this.handleSellAllButtonClick} text={"Sell ALL"} />
<StockTickerPositionText p={this.props.p} stock={this.props.stock} /> <StockTickerPositionText p={this.props.p} stock={this.props.stock} />

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