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

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

File diff suppressed because one or more lines are too long

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

59
dist/engineStyle.css vendored

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

34
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,31 @@
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)
--------------------------------------------------------------

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

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

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

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

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

@ -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()
.. warning:: This function is deprecated.
:RAM cost: 0.5 GB
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function.

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

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

@ -6,7 +6,7 @@
import { IMap } from "./types";
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
* 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:
`
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
* 'connect': a new singularity function that connects you to a server. (like the terminal command)
* 'manualHack': a new singularity function that performs a manual hack on the players current server.
* ns2 stack trace works on Firefox now.
* '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.
Misc.
* New shortcut, Alt + b, brings you to bladeburner
* New shortcut, Alt + g, brings you to gang
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'
`
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -5,6 +5,8 @@ import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentatio
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CityName } from "../Locations/data/CityNames";
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
// earnings for tasks
@ -127,7 +129,7 @@ export abstract class Person {
* stat level. Stat-agnostic (same formula for every stat)
*/
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 {
// 15 => +1.4% when you initially acquire int
// 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);
return calculateIntelligenceBonus(this.intelligence, weight);
}
}

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

@ -11,6 +11,7 @@ import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { Page,
routing } from "../../ui/navigationTracking";
@ -24,6 +25,9 @@ import { getSelectValue } from "../../../utils/uiHelpers/getSelectData";
import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../../../utils/uiHelpers/removeElement";
import * as React from "react";
import { renderToStaticMarkup } from "react-dom/server"
interface IResleeveUIElems {
container: 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 " +
"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 " +
"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 " +
"all of your currently-installed Augmentations, and replace " +
"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 " +
"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 " +
"Source-File.",
width: "75%",
@ -225,7 +229,7 @@ export function clearResleevesPage() {
}
for (const prop in UIElems) {
(<any>UIElems)[prop] = null;
(UIElems as any)[prop] = null;
}
playerRef = null;
@ -256,12 +260,12 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
elems.stats = createElement("p", {
class: "resleeve-stats-text",
innerHTML:
`Hacking: ${numeralWrapper.format(resleeve.hacking_skill, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.hacking_exp)} exp)<br>` +
`Strength: ${numeralWrapper.format(resleeve.strength, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.strength_exp)} exp)<br>` +
`Defense: ${numeralWrapper.format(resleeve.defense, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.defense_exp)} exp)<br>` +
`Dexterity: ${numeralWrapper.format(resleeve.dexterity, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.dexterity_exp)} exp)<br>` +
`Agility: ${numeralWrapper.format(resleeve.agility, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.agility_exp)} exp)<br>` +
`Charisma: ${numeralWrapper.format(resleeve.charisma, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.charisma_exp)} exp)<br>` +
`Hacking: ${numeralWrapper.formatSkill(resleeve.hacking_skill)} (${numeralWrapper.formatExp(resleeve.hacking_exp)} exp)<br />` +
`Strength: ${numeralWrapper.formatSkill(resleeve.strength)} (${numeralWrapper.formatExp(resleeve.strength_exp)} exp)<br />` +
`Defense: ${numeralWrapper.formatSkill(resleeve.defense)} (${numeralWrapper.formatExp(resleeve.defense_exp)} exp)<br />` +
`Dexterity: ${numeralWrapper.formatSkill(resleeve.dexterity)} (${numeralWrapper.formatExp(resleeve.dexterity_exp)} exp)<br />` +
`Agility: ${numeralWrapper.formatSkill(resleeve.agility)} (${numeralWrapper.formatExp(resleeve.agility_exp)} exp)<br />` +
`Charisma: ${numeralWrapper.formatSkill(resleeve.charisma)} (${numeralWrapper.formatExp(resleeve.charisma_exp)} exp)<br />` +
`# Augmentations: ${resleeve.augmentations.length}`,
});
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 Field Analysis multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_analysis_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();
elems.costPanel = createElement("div", { class: "resleeve-panel", width: "20%" });
elems.costText = createElement("p", {
innerText: `It costs ${numeralWrapper.formatMoney(cost)} ` +
innerHTML: `It costs ${renderToStaticMarkup(Money(cost))} ` +
`to purchase this Sleeve.`,
});
elems.buyButton = createElement("button", {
@ -332,7 +336,7 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
innerText: "Purchase",
clickListener: () => {
if (purchaseResleeve(resleeve, playerRef!)) {
dialogBoxCreate(`You re-sleeved for ${numeralWrapper.formatMoney(cost)}!`, false);
dialogBoxCreate((<>You re-sleeved for {Money(cost)}!</>), false);
} else {
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 { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -19,6 +20,8 @@ import { createPopup } from "../../../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
import { renderToStaticMarkup } from "react-dom/server"
export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) {
// Array of all owned Augmentations. Names only
const ownedAugNames: string[] = sleeve.augmentations.map((e) => {return e.name});
@ -86,7 +89,7 @@ export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) {
innerHTML:
[
`<h2>${aug.name}</h2><br>`,
`Cost: ${numeralWrapper.formatMoney(aug.startingCost)}<br><br>`,
`Cost: ${renderToStaticMarkup(Money(aug.startingCost))}<br><br>`,
`${aug.info}`
].join(" "),
padding: "2px",

@ -40,11 +40,15 @@ import { removeElement } from "../../../utils/uiHelpers/removeElement";
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
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 { MoreStatsContent } from "./ui/MoreStatsContent";
import { MoreEarningsContent } from "./ui/MoreEarningsContent";
import * as React from "react";
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
interface ISleeveUIElems {
@ -218,9 +222,9 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
const popupArguments: HTMLElement[] = [];
popupArguments.push(createPopupCloseButton(popupId, { class: "std-button" }));
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. " +
`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)",
}));
for (const cityName in Cities) {
@ -331,13 +335,13 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
if (sleeve.currentTask === SleeveTaskType.Crime) {
const data = [
[`Money`, numeralWrapper.formatMoney(parseFloat(sleeve.currentTaskLocation)), `(on success)`],
[`Hacking Exp`, numeralWrapper.format(sleeve.gainRatesForTask.hack, "0.00"), `(2x on success)`],
[`Strength Exp`, numeralWrapper.format(sleeve.gainRatesForTask.str, "0.00"), `(2x on success)`],
[`Defense Exp`, numeralWrapper.format(sleeve.gainRatesForTask.def, "0.00"), `(2x on success)`],
[`Dexterity Exp`, numeralWrapper.format(sleeve.gainRatesForTask.dex, "0.00"), `(2x on success)`],
[`Agility Exp`, numeralWrapper.format(sleeve.gainRatesForTask.agi, "0.00"), `(2x on success)`],
[`Charisma Exp`, numeralWrapper.format(sleeve.gainRatesForTask.cha, "0.00"), `(2x on success)`]
[`Money`, Money(parseFloat(sleeve.currentTaskLocation)), `(on success)`],
[`Hacking Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.hack), `(2x on success)`],
[`Strength Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.str), `(2x on success)`],
[`Defense Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.def), `(2x on success)`],
[`Dexterity Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.dex), `(2x on success)`],
[`Agility Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.agi), `(2x on success)`],
[`Charisma Exp`, numeralWrapper.formatExp(sleeve.gainRatesForTask.cha), `(2x on success)`]
];
ReactDOM.render(EarningsTableElement('Earnings (Pre-Synchronization)', data), elems.currentEarningsInfo!)
@ -347,18 +351,18 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
});
} else {
const data = [
[`Money:`, `${numeralWrapper.formatMoney(5 * sleeve.gainRatesForTask.money)} / s`],
[`Hacking Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.hack, "0.00")} / s`],
[`Strength Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.str, "0.00")} / s`],
[`Defense Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.def, "0.00")} / s`],
[`Dexterity Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.dex, "0.00")} / s`],
[`Agility Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.agi, "0.00")} / s`],
[`Charisma Exp:`, `${numeralWrapper.format(5 * sleeve.gainRatesForTask.cha, "0.00")} / s`]
[`Money:`, MoneyRate(5 * sleeve.gainRatesForTask.money)],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.hack)} / s`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.str)} / s`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.def)} / s`],
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.dex)} / s`],
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.agi)} / s`],
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * sleeve.gainRatesForTask.cha)} / s`]
];
let repGainText: string = "";
if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) {
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!)

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

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

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

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

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

@ -191,7 +191,7 @@ export async function updateScriptEditorContent() {
var codeCopy = code.repeat(1);
var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts);
if (ramUsage > 0) {
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB";
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.formatRAM(ramUsage);
} else {
switch (ramUsage) {
case RamCalculationErrorCode.ImportError:
@ -334,7 +334,7 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
if (serv == null) {continue;}
var timesGrown = Math.round(0.5 * runningScriptObj.dataMap[ip][2] / runningScriptObj.onlineRunningTime * timePassed);
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");
}
}

@ -4,6 +4,7 @@ import {
ipExists,
} from "./AllServers";
import { Server, IConstructorParams } from "./Server";
import { calculateServerGrowth } from "./formulas/grow";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
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
export function processSingleServerGrowth(server: Server, numCycles: number, p: IPlayer) {
//Server growth processed once every 450 game cycles
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);
export function processSingleServerGrowth(server: Server, threads: number, p: IPlayer) {
let serverGrowth = calculateServerGrowth(server, threads, p);
if (serverGrowth < 1) {
console.warn("serverGrowth calculated to be less than 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 { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
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
*/
@ -58,7 +63,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
if (tixApi) {
workerScript!.log("buyStock", `You do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}.`);
} 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;
@ -69,7 +74,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
if (tixApi) {
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) {
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;
@ -85,12 +90,12 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
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) {
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)
} 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;
@ -143,12 +148,13 @@ export function sellStock(stock: Stock, shares: number, workerScript: WorkerScri
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) {
const resultTxt = `Sold ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`;
workerScript!.log("sellStock", resultTxt)
} 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;
@ -187,8 +193,7 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr
"money to purchase this short position. You need " +
numeralWrapper.formatMoney(totalPrice));
} else if (opts.suppressDialog !== true) {
dialogBoxCreate("You do not have enough money to purchase this short position. You need " +
numeralWrapper.formatMoney(totalPrice));
dialogBoxCreate(<>You do not have enough money to purchase this short position. You need {Money(totalPrice)}</>);
}
return false;
@ -216,13 +221,13 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr
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) {
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);
} 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;
@ -283,12 +288,12 @@ export function sellShort(stock: Stock, shares: number, workerScript: WorkerScri
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) {
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);
} 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;

@ -19,9 +19,12 @@ import { PositionTypes } from "./data/PositionTypes";
import { IMap } from "../types";
import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../../utils/DialogBox";
import * as React from "react";
export interface IProcessOrderRefs {
rerenderFn: () => void;
stockMarket: IStockMarket;
@ -147,8 +150,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs) {
for (let i = 0; i < stockOrders.length; ++i) {
if (order == stockOrders[i]) {
stockOrders.splice(i, 1);
dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` +
`(${numeralWrapper.formatBigNumber(Math.round(order.shares))} shares)`);
dialogBoxCreate(<>{order.type} for {stock.symbol} @ {Money(order.price)} ({pos}) was filled ({numeralWrapper.formatShares(Math.round(order.shares))} shares)</>);
refs.rerenderFn();
return;
}
@ -158,8 +160,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs) {
console.error(order);
} else {
if (isBuy) {
dialogBoxCreate(`Failed to execute ${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(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`);
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</>);
}
}
}

@ -15,6 +15,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -107,7 +108,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
<StdButton
disabled={!this.props.p.canAfford(cost)}
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}
onClick={this.purchaseTixApiAccess}
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
disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount}
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"}
/>
)
@ -174,7 +175,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
<StdButton
disabled={!this.props.p.canAfford(cost)}
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"}
/>
)
@ -216,7 +217,7 @@ export class InfoAndPurchases extends React.Component<IProps, any> {
{this.renderPurchase4SMarketDataTixApiAccessButton()}
<p>
Commission Fees: Every transaction you make has
a {numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} commission fee.
a {Money(CONSTANTS.StockMarketCommission)} commission fee.
</p><br />
<p>
WARNING: When you reset after installing Augmentations, the Stock

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