Merge pull request #811 from danielyxie/dev

v0.48.0
This commit is contained in:
hydroflame 2021-03-07 18:46:03 -05:00 committed by GitHub
commit 802f28082d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1006 additions and 388 deletions

@ -59,10 +59,16 @@
}
#character-hp-wrapper { color: $my-stat-hp-color; }
.character-hp-cell { color: $my-stat-hp-color; }
#character-money-wrapper { color: $my-stat-money-color; }
.character-money-cell { color: $my-stat-money-color; }
#character-hack-wrapper { color: $my-stat-hack-color; }
.character-hack-cell { color: $my-stat-hack-color; }
#character-cha-wrapper { color: $my-stat-cha-color; }
.character-cha-cell { color: $my-stat-cha-color; }
#character-int-wrapper { color: $my-stat-int-color; }
.character-int-cell { color: $my-stat-int-color; }
.character-combat-cell { color: $my-stat-physical; }
.character-overview-btn {
@include borderRadius(12px);

@ -17,7 +17,8 @@ body {
p,
pre,
h2,
.text {
.text,
td {
color: var(--my-font-color);
}
@ -79,6 +80,28 @@ a:visited {
right: 0;
}
#factions-tab {
position: relative;
}
#factions-notification {
font-size: $defaultFontSize * 0.625;
position: absolute; /* Position the badge within the relatively positioned button */
top: 0;
right: 0;
}
#augmentations-tab {
position: relative;
}
#augmentations-notification {
font-size: $defaultFontSize * 0.625;
position: absolute; /* Position the badge within the relatively positioned button */
top: 0;
right: 0;
}
.notification-on {
background-color: #fa3e3e;
color: #fff;

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([362,0]),o()}({305:function(n,t,o){},307:function(n,t,o){},309:function(n,t,o){},311:function(n,t,o){},313:function(n,t,o){},315:function(n,t,o){},317:function(n,t,o){},319:function(n,t,o){},321:function(n,t,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){},362:function(n,t,o){"use strict";o.r(t);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),o(321),o(319),o(317),o(315),o(313),o(311),o(309),o(307),o(305)}});
!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([363,0]),o()}({306:function(n,t,o){},308:function(n,t,o){},310:function(n,t,o){},312:function(n,t,o){},314:function(n,t,o){},316:function(n,t,o){},318:function(n,t,o){},320:function(n,t,o){},322:function(n,t,o){},324:function(n,t,o){},326:function(n,t,o){},328:function(n,t,o){},330:function(n,t,o){},332:function(n,t,o){},334:function(n,t,o){},336:function(n,t,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){},363:function(n,t,o){"use strict";o.r(t);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),o(336),o(334),o(332),o(330),o(328),o(326),o(324),o(322),o(320),o(318),o(316),o(314),o(312),o(310),o(308),o(306)}});
//# sourceMappingURL=engineStyle.bundle.js.map

41
dist/engineStyle.css vendored

@ -26,7 +26,8 @@ body {
p,
pre,
h2,
.text {
.text,
td {
color: var(--my-font-color); }
h1 {
@ -78,6 +79,26 @@ a:visited {
top: 0;
right: 0; }
#factions-tab {
position: relative; }
#factions-notification {
font-size: 10px;
position: absolute;
/* Position the badge within the relatively positioned button */
top: 0;
right: 0; }
#augmentations-tab {
position: relative; }
#augmentations-notification {
font-size: 10px;
position: absolute;
/* Position the badge within the relatively positioned button */
top: 0;
right: 0; }
.notification-on {
background-color: #fa3e3e;
color: #fff;
@ -684,18 +705,36 @@ button {
#character-hp-wrapper {
color: #dd3434; }
.character-hp-cell {
color: #dd3434; }
#character-money-wrapper {
color: #ffd700; }
.character-money-cell {
color: #ffd700; }
#character-hack-wrapper {
color: #adff2f; }
.character-hack-cell {
color: #adff2f; }
#character-cha-wrapper {
color: #a671d1; }
.character-cha-cell {
color: #a671d1; }
#character-int-wrapper {
color: #6495ed; }
.character-int-cell {
color: #6495ed; }
.character-combat-cell {
color: #faffdf; }
.character-overview-btn {
-webkit-border-radius: 12px;
-moz-border-radius: 12px;

24
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,49 @@
Changelog
=========
v0.48.0 - ASCII - 2020-03-07
-------
**ASCII**
* Travel Agency now displays a world map
* Cities are now top view of metro station maps
**Netscript**
* 'softReset' is a new netscript function that performs a soft reset
regardless of if the player has bought augmentations or not.
* 'getAugmentationStats' is a new netscript function that returns the stats of
an augmentation.
* getCharacterInformation now additionally returns exp
* pid resets back to 1 when installing or destroying a BitNode.
* New '.ns' scripts start with a main function.
* 'hacknet.maxNumNodes' returns the maximum number of hacknet nodes.
**Bladeburner**
* Current stamina will scale as max stamina increases, this prevents players
from having very high penalty when they gain huge amount of exp at the
start of a reset.
**Misc.**
* Fixed an issue where SF3 was listed as infinitly repeatable and SF12 as
having a limit of 3.
* Fixed an issue where the gang equipment screen would freeze the game if a
script installed augmentations while it is open.
* All BonusTime now displays in the 'H M S' format.
* Donation textbox style updated to match the rest of the game.
* Corporation name style updated to match the rest of the game.
* minor formatting under Hacking>Active Scripts
* typo in BN12 description
* BN12 now reduces contract money
* Character>Stats percentages are aligned, server and hacknet limit are
displayed, if the player has SF5 the reduces stats are shown.
* Character>Augmentations now displays by how much the player stats will
increase.
* Character>Augmentations has a badge indicating how many augs the player
has bought but not installed
* Character>Factions has a badge indicating how many factions have pending
invites.
v0.47.2 - 7/15/2019
-------------------

@ -0,0 +1,8 @@
maxNumNodes() Netscript Function
=============================
.. js:function:: maxNumNodes()
:RAM cost: 0 GB
Returns the maximum number of Hacknet Nodes you can own.

@ -0,0 +1,16 @@
getAugmentationStats() Netscript Function
=========================================
.. js:function:: getAugmentationStats(name)
:RAM cost: 5 GB
:param string name: Name of Augmentation. CASE-SENSITIVE
If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.
ns.getAugmentationStats("Synfibril Muscle")
{
strength_mult: 1.3,
defense_mult: 1.3,
}

@ -49,4 +49,10 @@ getCharacterInformation() Netscript Function
workChaExpGain: Cha experience earned so far from work
workRepGain: Reputation earned so far from work, if applicable
workMoneyGain: Money earned so far from work, if applicable
hackingExp: Total hacking experience
strengthExp: Total strength experience
defenseExp: Total defense experience
dexterityExp: Total dexterity experience
agilityExp: Total agility experience
charismaExp: Total charisma experience
}

@ -0,0 +1,8 @@
softReset() Netscript Function
===================================
.. js:function:: softReset()
If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.
This function will perform a reset even if you don't have any augmentation installed.

@ -58,9 +58,11 @@
</li>
<li id="factions-tab" class="mainmenu-accordion-panel">
<button id="factions-menu-link"> Factions </button>
<span id="factions-notification" class="notification-off"> </span>
</li>
<li id="augmentations-tab" class="mainmenu-accordion-panel">
<button id="augmentations-menu-link" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"> Augmentations </button>
<span id="augmentations-notification" class="notification-off"> </span>
</li>
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel">
<button id="hacknet-nodes-menu-link"> Hacknet Nodes </button>

@ -5,92 +5,104 @@ import * as React from "react";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Augmentations} from "../Augmentations";
function calculateAugmentedStats() {
const augP: any = {};
for(const aug of Player.queuedAugmentations) {
const augObj = Augmentations[aug.name];
for (const mult in augObj.mults) {
const v = augP[mult] ? augP[mult] : 1;
augP[mult] = v * augObj.mults[mult];
}
}
return augP;
}
export function PlayerMultipliers(): React.ReactElement {
const mults = calculateAugmentedStats();
function MultiplierTable(rows: any[]): React.ReactElement {
function improvements(r: number) {
let elems: any[] = [];
if(r) {
elems = [
<td key='2'>&nbsp;=>&nbsp;</td>,
<td key='3'>{numeralWrapper.formatPercentage(r)}</td>
];
}
return elems;
}
return <table>
<tbody>
{rows.map((r: any) => <tr key={r[0]}>
<td key='0'><span>{r[0]} multiplier:&nbsp;</span></td>
<td key='1' style={{textAlign: 'right'}}>{numeralWrapper.formatPercentage(r[1])}</td>
{improvements(r[2])}
</tr>)}
</tbody>
</table>
}
return (
<>
<p><strong><u>Total Multipliers:</u></strong></p>
<p><strong><u>Multipliers:</u></strong></p><br />
{MultiplierTable([
['Hacking Chance ', Player.hacking_chance_mult, Player.hacking_chance_mult*mults.hacking_chance_mult],
['Hacking Speed ', Player.hacking_speed_mult, Player.hacking_speed_mult*mults.hacking_speed_mult],
['Hacking Money ', Player.hacking_money_mult, Player.hacking_money_mult*mults.hacking_money_mult],
['Hacking Growth ', Player.hacking_grow_mult, Player.hacking_grow_mult*mults.hacking_grow_mult]
])}<br />
<pre>
{'Hacking Chance multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_chance_mult)}
</pre>
<pre>
{'Hacking Speed multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_speed_mult)}
</pre>
<pre>
{'Hacking Money multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_money_mult)}
</pre>
<pre>
{'Hacking Growth multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_grow_mult)}
</pre><br />
<pre>
{'Hacking Level multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_mult)}
</pre>
<pre>
{'Hacking Experience multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_exp_mult)}
</pre>
<br />
<pre>
{'Strength Level multiplier: ' + numeralWrapper.formatPercentage(Player.strength_mult)}
</pre>
<pre>
{'Strength Experience multiplier: ' + numeralWrapper.formatPercentage(Player.strength_exp_mult)}
</pre>
<br />
<pre>
{'Defense Level multiplier: ' + numeralWrapper.formatPercentage(Player.defense_mult)}
</pre>
<pre>
{'Defense Experience multiplier: ' + numeralWrapper.formatPercentage(Player.defense_exp_mult)}
</pre><br />
<pre>
{'Dexterity Level multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_mult)}
</pre>
<pre>
{'Dexterity Experience multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_exp_mult)}
</pre><br />
<pre>
{'Agility Level multiplier: ' + numeralWrapper.formatPercentage(Player.agility_mult)}
</pre>
<pre>
{'Agility Experience multiplier: ' + numeralWrapper.formatPercentage(Player.agility_exp_mult)}
</pre><br />
<pre>
{'Charisma Level multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_mult)}
</pre>
<pre>
{'Charisma Experience multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_exp_mult)}
</pre><br />
<pre>
{'Hacknet Node production multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_money_mult)}
</pre>
<pre>
{'Hacknet Node purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_purchase_cost_mult)}
</pre>
<pre>
{'Hacknet Node RAM upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_ram_cost_mult)}
</pre>
<pre>
{'Hacknet Node Core purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_core_cost_mult)}
</pre>
<pre>
{'Hacknet Node level upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_level_cost_mult)}
</pre><br />
<pre>
{'Company reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.company_rep_mult)}
</pre>
<pre>
{'Faction reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.faction_rep_mult)}
</pre>
<pre>
{'Salary multiplier: ' + numeralWrapper.formatPercentage(Player.work_money_mult)}
</pre><br />
<pre>
{'Crime success multiplier: ' + numeralWrapper.formatPercentage(Player.crime_success_mult)}
</pre>
<pre>
{'Crime money multiplier: ' + numeralWrapper.formatPercentage(Player.crime_money_mult)}
</pre>
{MultiplierTable([
['Hacking Level ', Player.hacking_mult, Player.hacking_mult*mults.hacking_mult],
['Hacking Experience ', Player.hacking_exp_mult, Player.hacking_exp_mult*mults.hacking_exp_mult]
])}<br />
{MultiplierTable([
['Strength Level ', Player.strength_mult, Player.strength_mult*mults.strength_mult],
['Strength Experience ', Player.strength_exp_mult, Player.strength_exp_mult*mults.strength_exp_mult]
])}<br />
{MultiplierTable([
['Defense Level ', Player.defense_mult, Player.defense_mult*mults.defense_mult],
['Defense Experience ', Player.defense_exp_mult, Player.defense_exp_mult*mults.defense_exp_mult]
])}<br />
{MultiplierTable([
['Dexterity Level ', Player.dexterity_mult, Player.dexterity_mult*mults.dexterity_mult],
['Dexterity Experience ', Player.dexterity_exp_mult, Player.dexterity_exp_mult*mults.dexterity_exp_mult]
])}<br />
{MultiplierTable([
['Agility Level ', Player.agility_mult, Player.agility_mult*mults.agility_mult],
['Agility Experience ', Player.agility_exp_mult, Player.agility_exp_mult*mults.agility_exp_mult]
])}<br />
{MultiplierTable([
['Charisma Level ', Player.charisma_mult, Player.charisma_mult*mults.charisma_mult],
['Charisma Experience ', Player.charisma_exp_mult, Player.charisma_exp_mult*mults.charisma_exp_mult]
])}<br />
{MultiplierTable([
['Hacknet Node production ', Player.hacknet_node_money_mult, Player.hacknet_node_money_mult*mults.hacknet_node_money_mult],
['Hacknet Node purchase cost ', Player.hacknet_node_purchase_cost_mult, Player.hacknet_node_purchase_cost_mult*mults.hacknet_node_purchase_cost_mult],
['Hacknet Node RAM upgrade cost ', Player.hacknet_node_ram_cost_mult, Player.hacknet_node_ram_cost_mult*mults.hacknet_node_ram_cost_mult],
['Hacknet Node Core purchase cost ', Player.hacknet_node_core_cost_mult, Player.hacknet_node_core_cost_mult*mults.hacknet_node_core_cost_mult],
['Hacknet Node level upgrade cost ', Player.hacknet_node_level_cost_mult, Player.hacknet_node_level_cost_mult*mults.hacknet_node_level_cost_mult]
])}<br />
{MultiplierTable([
['Company reputation gain ', Player.company_rep_mult, Player.company_rep_mult*mults.company_rep_mult],
['Faction reputation gain ', Player.faction_rep_mult, Player.faction_rep_mult*mults.faction_rep_mult],
['Salary ', Player.work_money_mult, Player.work_money_mult*mults.work_money_mult]
])}<br />
{MultiplierTable([
['Crime success ', Player.crime_success_mult, Player.crime_success_mult*mults.crime_success_mult],
['Crime money ', Player.crime_money_mult, Player.crime_money_mult*mults.crime_money_mult],
])}<br />
</>
)
}

@ -234,7 +234,7 @@ BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
"Level 3: 56%");
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
"To iterate is human, to recurse divine.<br><br>" +
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you Souce-File 12, or " +
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
@ -460,11 +460,12 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.PurchasedServerLimit = dec;
BitNodeMultipliers.PurchasedServerMaxRam = dec;
BitNodeMultipliers.ManualHackMoney = dec;
BitNodeMultipliers.ScriptHackMoney = dec;
BitNodeMultipliers.CompanyWorkMoney = dec;
BitNodeMultipliers.CrimeMoney = dec;
BitNodeMultipliers.HacknetNodeMoney = dec;
BitNodeMultipliers.ManualHackMoney = dec;
BitNodeMultipliers.ScriptHackMoney = dec;
BitNodeMultipliers.CompanyWorkMoney = dec;
BitNodeMultipliers.CrimeMoney = dec;
BitNodeMultipliers.HacknetNodeMoney = dec;
BitNodeMultipliers.CodingContractMoney = dec;
BitNodeMultipliers.CompanyWorkExpGain = dec;
BitNodeMultipliers.ClassGymExpGain = dec;

@ -31,6 +31,7 @@ import { KEY } from "../utils/helpers/keyCodes";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { appendLineBreaks } from "../utils/uiHelpers/appendLineBreaks";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElement } from "../utils/uiHelpers/removeElement";
@ -991,12 +992,16 @@ Bladeburner.prototype.process = function() {
}
Bladeburner.prototype.calculateMaxStamina = function() {
var effAgility = Player.agility * this.skillMultipliers.effAgi;
var maxStamina = (Math.pow(effAgility, 0.8) + this.staminaBonus);
maxStamina *= this.skillMultipliers.stamina;
maxStamina *= Player.bladeburner_max_stamina_mult;
const effAgility = Player.agility * this.skillMultipliers.effAgi;
let maxStamina = (Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina *
Player.bladeburner_max_stamina_mult;
if (this.maxStamina !== maxStamina) {
const oldMax = this.maxStamina;
this.maxStamina = maxStamina;
this.stamina = this.maxStamina * this.stamina / oldMax;
}
if (isNaN(maxStamina)) {throw new Error("Max Stamina calculated to be NaN in Bladeburner.calculateMaxStamina()");}
this.maxStamina = maxStamina;
}
Bladeburner.prototype.calculateStaminaGainPerSecond = function() {
@ -2349,7 +2354,7 @@ Bladeburner.prototype.updateOverviewContent = function() {
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);
DomElems.overviewBonusTime.childNodes[0].nodeValue = "Bonus time: " + this.storedCycles/CyclesPerSecond;
DomElems.overviewBonusTime.childNodes[0].nodeValue = "Bonus time: " + convertTimeMsToTimeElapsedString(this.storedCycles/CyclesPerSecond*1000);
DomElems.overviewAugSuccessMult.innerText = "Aug. Success Chance Mult: " + formatNumber(Player.bladeburner_success_chance_mult*100, 1) + "%";
DomElems.overviewAugMaxStaminaMult.innerText = "Aug. Max Stamina Mult: " + formatNumber(Player.bladeburner_max_stamina_mult*100, 1) + "%";
DomElems.overviewAugStaminaGainMult.innerText = "Aug. Stamina Gain Mult: " + formatNumber(Player.bladeburner_stamina_gain_mult*100, 1) + "%";

@ -74,7 +74,6 @@ for (const md of codingContractTypesMetadata) {
// tslint:disable-next-line
CodingContractTypes[md.name] = new CodingContractType(md.name, md.desc, md.gen, md.solver, md.difficulty, md.numTries);
}
console.info(`${Object.keys(CodingContractTypes).length} Coding Contract Types loaded`);
/**
* Enum representing the different types of rewards a Coding Contract can give

@ -6,7 +6,7 @@
import { IMap } from "./types";
export let CONSTANTS: IMap<any> = {
Version: "0.47.3",
Version: "0.48.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,11 +228,46 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate:
`
v0.47.3
v0.48.0 - ASCII
-------
ASCII
* Travel Agency now displays a world map
* Cities are now top view of metro station maps
Netscript
* 'softReset' is a new netscript function that performs a soft reset
regardless of if the player has bought augmentations or not.
* 'getAugmentationStats' is a new netscript function that returns the stats of
an augmentation.
* getCharacterInformation now additionally returns exp
* pid resets back to 1 when installing or destroying a BitNode.
* New '.ns' scripts start with a main function.
* 'hacknet.maxNumNodes' returns the maximum number of hacknet nodes.
Bladeburner
* Current stamina will scale as max stamina increases, this prevents players
from having very high penalty when they gain huge amount of exp at the
start of a reset.
Misc.
* missing ram cost in many function now added to documentation
* typos
* Fixed an issue where SF3 was listed as infinitly repeatable and SF12 as
having a limit of 3.
* Fixed an issue where the gang equipment screen would freeze the game if a
script installed augmentations while it is open.
* All BonusTime now displays in the 'H M S' format.
* Donation textbox style updated to match the rest of the game.
* Corporation name style updated to match the rest of the game.
* minor formatting under Hacking>Active Scripts
* typo in BN12 description
* BN12 now reduces contract money
* Character>Stats percentages are aligned, server and hacknet limit are
displayed, if the player has SF5 the reduces stats are shown.
* Character>Augmentations now displays by how much the player stats will
increase.
* Character>Augmentations has a badge indicating how many augs the player
has bought but not installed
* Character>Factions has a badge indicating how many factions have pending
invites.
`
}

@ -10,6 +10,7 @@ import { CorporationUpgrades } from "../data/CorporationUpgrades";
import { CONSTANTS } from "../../Constants";
import { numeralWrapper } from "../../ui/numeralFormat";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
export class Overview extends BaseReactComponent {
// Generic Function for Creating a button
@ -71,9 +72,9 @@ export class Overview extends BaseReactComponent {
`Private Shares: ${numeralWrapper.format(this.corp().totalShares - this.corp().issuedShares - this.corp().numShares, "0.000a")}` +
"</span></p><br><br>";
const storedTime = this.corp().storedCycles * CONSTANTS.MilliPerCycle / 1000;
if (storedTime > 15) {
txt += `Bonus Time: ${storedTime} seconds<br><br>`;
const storedTime = this.corp().storedCycles * CONSTANTS.MilliPerCycle;
if (storedTime > 15000) {
txt += `Bonus time: ${convertTimeMsToTimeElapsedString(storedTime)}<br><br>`;
}
let prodMult = this.corp().getProductionMultiplier(),

@ -88,7 +88,7 @@ export class DonateOption extends React.Component<IProps, IState> {
return (
<div className={"faction-work-div"}>
<div className={"faction-work-div-wrapper"}>
<input onChange={this.handleChange} placeholder={"Donation amount"} style={inputStyleMarkup} />
<input className='text-input' onChange={this.handleChange} placeholder={"Donation amount"} style={inputStyleMarkup} />
<StdButton
onClick={this.donate}
text={"Donate Money"}

@ -33,6 +33,7 @@ import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
// Constants
@ -1570,7 +1571,9 @@ Gang.prototype.updateGangContent = function() {
// Update territory information
UIElems.gangTerritoryInfoText.innerHTML = "";
const playerPower = AllGangs[this.facName].power;
for (const gangname in AllGangs) {
let gangNames = Object.keys(AllGangs).filter(g => g != this.facName);
gangNames.unshift(this.facName);
for (const gangname of gangNames) {
if (AllGangs.hasOwnProperty(gangname)) {
const gangTerritoryInfo = AllGangs[gangname];
let territory = gangTerritoryInfo.territory * 100;
@ -1670,7 +1673,7 @@ Gang.prototype.updateGangContent = function() {
const CyclesPerSecond = 1000 / Engine._idleSpeed;
UIElems.gangInfo.appendChild(createElement("p", { // Stored Cycles
innerText: `Bonus time(s): ${this.storedCycles / CyclesPerSecond}`,
innerText: `Bonus time: ${convertTimeMsToTimeElapsedString(this.storedCycles / CyclesPerSecond*1000)}`,
display: "inline-block",
tooltip: "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the browser). " +
"Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed",
@ -1942,6 +1945,8 @@ Gang.prototype.setGangMemberTaskDescription = function(memberObj, taskName) {
Gang.prototype.clearUI = function() {
if (UIElems.gangContainer instanceof Element) { removeElement(UIElems.gangContainer); }
if (UIElems.gangMemberUpgradeBox instanceof Element) { removeElement(UIElems.gangMemberUpgradeBox); }
for (const prop in UIElems) {
UIElems[prop] = null;
}

@ -15,9 +15,15 @@ export class City {
*/
name: CityName;
constructor(name: CityName, locations: LocationName[]=[]) {
/**
* Metro map ascii art
*/
asciiArt: string;
constructor(name: CityName, locations: LocationName[]=[], asciiArt: string='') {
this.name = name;
this.locations = locations;
this.asciiArt = asciiArt;
}
addLocation(loc: LocationName): void {

@ -40,6 +40,181 @@ Cities[CityName.NewTokyo] = new City(CityName.NewTokyo);
Cities[CityName.Sector12] = new City(CityName.Sector12);
Cities[CityName.Volhaven] = new City(CityName.Volhaven);
Cities[CityName.Aevum].asciiArt = `
[aevum police headquarters] 26
o
I \\ [bachman & associates]
\\ 56 B
x \\ [summit university]
\\ \\ 28
\\ [snap fitness gym] x o--L------------
K \\ /
\\ \\ P
x 58 \\ / [travel agency]
\\ 94 95 o
90 x 59 o------o |
\\ / \\ | 98 102 103
o--------N------x----o 93 96 o-----+------------o o----o
\\ | \\ /
[hospital] \\ 61 [ecorp] x 31 99 o-F-o 101
o |
| o---E-- | [fulcrum tech.]
x 62 / A [aerocorp]
[crush fitness gym] | / |
| / |
o--------D------+--o o
| |\\ [rho construction]
H [netlink tech.] | J
| | \\
| 34 x \\
[clarke inc.] C | \\ [world stock exchange]
| | \\
| | o-M-------Q--------o
[galactic cybersystems] G 35 x
| [watchdog security]
|
67 o
[the slums] O `
Cities[CityName.Chongqing].asciiArt = `
|
75 o
\\
o 76
7 | |
| + 77
[world stock exchange] F |
\\ o 78 [kuaigong international]
\\ /
38 o----x--x------x------A---------
/ 39 | 41
37 o + 79 o--x--x-C-0
/ | /
/ x-----+-----x-----0 [hospital]
[solaris space system] B |
| + 80
| |
34 o E [travel agency]
|
|
x 82
[the slums] D `
Cities[CityName.Ishima].asciiArt = `
o 59
o o |
[storm tech.] | | G [world stock exchange]
| | 28 |
23 o--C------o--------+----x----o |
/ / 25 | 27 \\ x 57
/ / | \\ |
/ / | \\ |
o 22 o | \\| 29/56
| | o
| [hospital] D / \\ 3 2 1
o | / \\ o-------x------o
/ o / \\ /
48 o / 55 x \\ /
\\ / / x
\\ / [nova medical] / 4/30 \\
49 x A \\
/ \\ / \\
/ \\ [travel agency] F o 31
/ \\ 51 /
/ o----B------x-----o
o 50 52
[omega soft.]
[the slums] E `
Cities[CityName.NewTokyo].asciiArt = `
o
\\
\\ [defcomm]
\\
o--x---A--x--o [travel agency]
7 8 10 G
[vitalife] o 12 [global pharmaceuticals]
|
o--D-x----x-------x-C-+--------x--x-B-x---x-o
21 22 23 \\ 24 25 26 27
\\
[noodle bar] x 14
\\
\\
[hospital] o 15 [world stock exchange]
|
o--x--E--x-----x-----x---+---x----x--H--x-o
|
|
o 17
F [the slums]
`
Cities[CityName.Sector12].asciiArt = `
78 o 97
o [icarus microsystems] /
N [powerhouse gym] o I
1 | | /
o-----+---x----o 4 A [alpha ent.] o-------o /
| 3 \\ | \\ /
| \\ | [iron gym] x 95
(79) x \\ | / \\
| o-E----+----x----J--o 10 / o----T--o
| | 8 \\ 94 x
80 x [city hall] | x 11 / [world stock exchange]
| | \\ /
| C [cia] \\ /
Q [hospital] | F P [universal energy]
| o [deltaone] \\ /
| 35 o---------x 13/92/36
L [megacorp] 33 / / \\
| o------------o 34 / \\
(29) | / [carmichael sec.] D \\
o-----+-----x------o / O [rothman university]
| 31 32 [nsa] M
| /
B [blade industries] H
| / [four sigma]
| [joe's guns] /
| /
85 o--G--------K--------S-------o 88 [the slums] R
[foodnstuff] [travel agency] `
Cities[CityName.Volhaven].asciiArt = `
[omnia cybersystems]
17 66 68
o o------G-------o
\\ / \\
\\ o 65 o 69
[syscore sec.] H | |
\\ | | [millenium fitness gym]
\\ | 21 22 23 24 | 26
o----+--x--x----x---x---+-----x-------D-----o
19 | | 28
| F [omnitek inc.]
[hospital] J 63 o
| / 72
3 | 5 6 / 9
o--------+----x-----x----+----------M-------o
/ | |
/ 61 x [helios labs] B [world stock exchange]
[travel agency] L | |
/ | o
/ E [nwo] / 75
/ [computek] | /
/ A-------o------I-----o
1 o | |
| [zb] o 77
[lexocorp] C
|
o
57
[the slums] K `
// Then construct all locations, and add them to the cities as we go.
for (const metadata of LocationsMetadata) {
const loc = constructLocation(metadata);

@ -127,6 +127,7 @@ export function createStartCorporationPopup(p: IPlayer) {
});
const nameInput = createElement("input", {
class: 'text-input',
placeholder: "Corporation Name",
}) as HTMLInputElement;

@ -17,18 +17,59 @@ type IProps = {
export class LocationCity extends React.Component<IProps, any> {
render() {
const locationButtons = this.props.city.locations.map((locName) => {
return (
<li key={locName}>
<StdButton onClick={this.props.enterLocation.bind(this, locName)} text={locName} />
</li>
)
});
const thiscity = this;
const topprop = this.props
function LocationLetter(location: string) {
if (location)
return <span key={location} className='tooltip' style={{color: 'blue', whiteSpace: 'nowrap', margin: '0px', padding: '0px', cursor: 'pointer'}} onClick={topprop.enterLocation.bind(thiscity, location)}>
X
</span>
return <span>*</span>
}
const locationLettersRegex = /[A-Z]/g;
const letterMap: any = {'A': 0,'B': 1,'C': 2,'D': 3,'E': 4,'F': 5,'G': 6,
'H': 7,'I': 8,'J': 9,'K': 10,'L': 11,'M': 12,'N': 13,'O': 14,
'P': 15,'Q': 16,'R': 17,'S': 18,'T': 19,'U': 20,'V': 21,'W': 22,
'X': 23,'Y': 24,'Z': 25}
let locI = 0;
function lineElems(s: string) {
let elems: any[] = [];
let matches: any[] = [];
let match: any;
while ((match = locationLettersRegex.exec(s)) !== null) {
matches.push(match);
}
if (matches.length === 0) {
elems.push(s);
return elems;
}
let parts: any[] = [];
for(let i = 0; i < matches.length; i++) {
const startI = i === 0 ? 0 : matches[i-1].index+1;
const endI = matches[i].index;
elems.push(s.slice(startI, endI))
const locationI = letterMap[s[matches[i].index]];
elems.push(LocationLetter(thiscity.props.city.locations[locationI]))
locI++;
}
elems.push(s.slice(matches[matches.length-1].index+1))
return elems;
}
let elems: any[] = [];
const lines = this.props.city.asciiArt.split('\n');
for(const i in lines) {
elems.push(<pre key={i}>{lineElems(lines[i])}</pre>)
}
return (
<ul>
{locationButtons}
</ul>
<>
{elems}
</>
)
}
}
}

@ -32,21 +32,16 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
}
render() {
const travelBtns: React.ReactNode[] = [];
for (const key in CityName) {
const city = CityName[key];
const thisTravelAgencyLocation = this;
// Skip current city
if (city === this.props.p.city) { continue; }
travelBtns.push(
<StdButton
key={city}
onClick={createTravelPopup.bind(null, city, this.props.travel)}
style={this.btnStyle}
text={`Travel to ${city}`}
/>
)
function LocationLetter(props: any) {
if(props.city !== thisTravelAgencyLocation.props.p.city) {
return <span className='tooltip' style={{color: 'blue', whiteSpace: 'nowrap', margin: '0px', padding: '0px'}} onClick={createTravelPopup.bind(null, props.city, thisTravelAgencyLocation.props.travel)}>
<span className='tooltiptext'>{props.city}</span>
{props.city[0]}
</span>
}
return <span>{props.city[0]}</span>
}
return (
@ -55,7 +50,28 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
From here, you can travel to any other city! A ticket
costs {numeralWrapper.formatMoney(CONSTANTS.TravelCost)}
</p>
{travelBtns}
<pre> ,_ . ._. _. .</pre>
<pre> , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~-</pre>
<pre> /~~-\_/-'~'--' \~~| ', ,' / / ~|-_\_/~/~ ~~--~~~~'--_</pre>
<pre> / ,/'-/~ '\ ,' _ , '<LocationLetter city='Volhaven' />,'|~ ._/-, /~</pre>
<pre> ~/-'~\_, '-,| '|. ' ~ ,\ /'~ / /_ /~</pre>
<pre>.-~ '| '',\~|\ _\~ ,_ , <LocationLetter city='Chongqing' /> /,</pre>
<pre> '\ <LocationLetter city='Sector-12' /> /'~ |_/~\\,-,~ \ " ,_,/ |</pre>
<pre> | / ._-~'\_ _~| \ ) <LocationLetter city='New Tokyo' /></pre>
<pre> \ __-\ '/ ~ |\ \_ / ~</pre>
<pre> ., '\ |, ~-_ - | \\_' ~| /\ \~ ,</pre>
<pre> ~-_' _; '\ '-, \,' /\/ |</pre>
<pre> '\_,~'\_ \_ _, /' ' |, /|'</pre>
<pre> / \_ ~ | / \ ~'; -,_.</pre>
<pre> | ~\ | | , '-_, ,; ~ ~\</pre>
<pre> \, <LocationLetter city='Aevum' /> / \ / /| ,-, , -,</pre>
<pre> | ,/ | |' |/ ,- ~ \ '.</pre>
<pre> ,| ,/ \ ,/ \ <LocationLetter city='Ishima' /> |</pre>
<pre> / | ~ -~~-, / _</pre>
<pre> | ,-' ~ /</pre>
<pre> / ,' ~</pre>
<pre> ',| ~</pre>
<pre> ~'</pre>
</div>
)
}

42
src/Netscript/Pid.ts Normal file

@ -0,0 +1,42 @@
import { workerScripts } from "./WorkerScripts";
let pidCounter = 1;
/**
* Find and return the next availble PID for a script
*/
export function generateNextPid(): number {
let tempCounter = pidCounter;
// Cap the number of search iterations at some arbitrary value to avoid
// infinite loops. We'll assume that players wont have 1mil+ running scripts
let found = false;
for (let i = 0; i < 1e6;) {
if (!workerScripts.has(tempCounter + i)) {
found = true;
tempCounter = tempCounter + i;
break;
}
if (i === Number.MAX_SAFE_INTEGER - 1) {
i = 1;
} else {
++i;
}
}
if (found) {
pidCounter = tempCounter + 1;
if (pidCounter >= Number.MAX_SAFE_INTEGER) {
pidCounter = 1;
}
return tempCounter;
} else {
return -1;
}
}
export function resetPidCounter(): void {
pidCounter = 1;
}

@ -202,7 +202,9 @@ export const RamCosts: IMap<any> = {
getAugmentationsFromFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,
getAugmentationPrereq: () => RamCostConstants.ScriptSingularityFn3RamCost,
getAugmentationCost: () => RamCostConstants.ScriptSingularityFn3RamCost,
getAugmentationStats: () => RamCostConstants.ScriptSingularityFn3RamCost,
purchaseAugmentation: () => RamCostConstants.ScriptSingularityFn3RamCost,
softReset: () => RamCostConstants.ScriptSingularityFn3RamCost,
installAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,
// Gang API

@ -10,6 +10,7 @@ import {
augmentationExists,
installAugmentations
} from "./Augmentation/AugmentationHelpers";
import { prestigeAugmentation } from "./Prestige";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { findCrime } from "./Crime/CrimeHelpers";
@ -51,7 +52,7 @@ import {
purchaseHashUpgrade,
updateHashManagerCapacity,
} from "./Hacknet/HacknetHelpers";
import { HacknetServer } from "./Hacknet/HacknetServer";
import { HacknetServer, MaxNumberHacknetServers } from "./Hacknet/HacknetServer";
import { CityName } from "./Locations/data/CityNames";
import { LocationName } from "./Locations/data/LocationNames";
@ -391,6 +392,9 @@ function NetscriptFunctions(workerScript) {
numNodes : function() {
return Player.hacknetNodes.length;
},
maxNumNodes : function() {
return MaxNumberHacknetServers;
},
purchaseNode : function() {
return purchaseHacknet();
},
@ -2742,6 +2746,12 @@ function NetscriptFunctions(workerScript) {
workChaExpGain: Player.workChaExpGained,
workRepGain: Player.workRepGained,
workMoneyGain: Player.workMoneyGained,
hackingExp: Player.hacking_exp,
strengthExp: Player.strength_exp,
defenseExp: Player.defense_exp,
dexterityExp: Player.dexterity_exp,
agilityExp: Player.agility_exp,
charismaExp: Player.charisma_exp,
};
},
isBusy: function() {
@ -3415,6 +3425,24 @@ function NetscriptFunctions(workerScript) {
var aug = Augmentations[name];
return [aug.baseRepRequirement, aug.baseCost];
},
getAugmentationStats: function(name) {
updateDynamicRam("getAugmentationStats", getRamCost("getAugmentationStats"));
if (Player.bitNodeN !== 4) {
if (SourceFileFlags[4] <= 2) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getAugmentationStats(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return false;
}
}
if (!augmentationExists(name)) {
workerScript.scriptRef.log("ERROR: getAugmentationStats() failed. Invalid Augmentation name passed in (note: this is case-sensitive): " + name);
return {};
}
var aug = Augmentations[name];
return Object.assign({}, aug.mults);
},
purchaseAugmentation: function(faction, name) {
updateDynamicRam("purchaseAugmentation", getRamCost("purchaseAugmentation"));
if (Player.bitNodeN !== 4) {
@ -3483,6 +3511,24 @@ function NetscriptFunctions(workerScript) {
return false;
}
},
softReset: function() {
updateDynamicRam("softReset", getRamCost("softReset"));
if (Player.bitNodeN !== 4) {
if (SourceFileFlags[4] <= 2) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run softReset(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return false;
}
}
workerScript.log("Soft resetting. This will cause this script to be killed");
setTimeoutRef(() => {
prestigeAugmentation();
}, 0);
// Prevent workerScript from "finishing execution naturally"
workerScript.running = false;
killWorkerScript(workerScript);
},
installAugmentations: function(cbScript) {
updateDynamicRam("installAugmentations", getRamCost("installAugmentations"));
if (Player.bitNodeN !== 4) {

@ -6,6 +6,7 @@ import { killWorkerScript } from "./Netscript/killWorkerScript";
import { WorkerScript } from "./Netscript/WorkerScript";
import { workerScripts } from "./Netscript/WorkerScripts";
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
import { generateNextPid } from "./Netscript/Pid";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
@ -412,42 +413,6 @@ function processNetscript1Imports(code, workerScript) {
return res;
}
/**
* Find and return the next availble PID for a script
*/
let pidCounter = 1;
function generateNextPid() {
let tempCounter = pidCounter;
// Cap the number of search iterations at some arbitrary value to avoid
// infinite loops. We'll assume that players wont have 1mil+ running scripts
let found = false;
for (let i = 0; i < 1e6;) {
if (!workerScripts.has(tempCounter + i)) {
found = true;
tempCounter = tempCounter + i;
break;
}
if (i === Number.MAX_SAFE_INTEGER - 1) {
i = 1;
} else {
++i;
}
}
if (found) {
pidCounter = tempCounter + 1;
if (pidCounter >= Number.MAX_SAFE_INTEGER) {
pidCounter = 1;
}
return tempCounter;
} else {
return -1;
}
}
/**
* Used to start a RunningScript (by creating and starting its
* corresponding WorkerScript), and add the RunningScript to the server on which
@ -535,7 +500,6 @@ export function createAndAddWorkerScript(runningScriptObj, server) {
// the script from being cleaned up twice
if (!w.running) { return; }
console.log("Stopping script " + w.name + " because it finished running naturally");
killWorkerScript(s);
w.log("Script finished running");
}).catch(function(w) {
@ -547,8 +511,8 @@ export function createAndAddWorkerScript(runningScriptObj, server) {
if (isScriptErrorMessage(w.errorMessage)) {
var errorTextArray = w.errorMessage.split("|");
if (errorTextArray.length != 4) {
console.log("ERROR: Something wrong with Error text in evaluator...");
console.log("Error text: " + errorText);
console.error("ERROR: Something wrong with Error text in evaluator...");
console.error("Error text: " + errorText);
return;
}
var serverIp = errorTextArray[1];
@ -567,11 +531,11 @@ export function createAndAddWorkerScript(runningScriptObj, server) {
w.env.stopFlag = true;
} else if (isScriptErrorMessage(w)) {
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
console.log("ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " + w.toString());
console.error("ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " + w.toString());
return;
} else {
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
console.log(w);
console.error(w);
}
killWorkerScript(s);

@ -4,7 +4,7 @@ import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
export function canAccessBladeburner() {
if (this.bitNodeN === 8) { return false; }
return (this.bitNodeN === 6) || (this.bitNodeN === 7) || (SourceFileFlags[6] > 0);
return (this.bitNodeN === 6) || (this.bitNodeN === 7) || (SourceFileFlags[6] > 0) || (SourceFileFlags[7] > 0);
}
export function inBladeburner() {

@ -411,7 +411,7 @@ export function recordMoneySource(amt, source) {
export function gainHackingExp(exp) {
if (isNaN(exp)) {
console.log("ERR: NaN passed into Player.gainHackingExp()"); return;
console.error("ERR: NaN passed into Player.gainHackingExp()"); return;
}
this.hacking_exp += exp;
if(this.hacking_exp < 0) {
@ -421,7 +421,7 @@ export function gainHackingExp(exp) {
export function gainStrengthExp(exp) {
if (isNaN(exp)) {
console.log("ERR: NaN passed into Player.gainStrengthExp()"); return;
console.error("ERR: NaN passed into Player.gainStrengthExp()"); return;
}
this.strength_exp += exp;
if(this.strength_exp < 0) {
@ -431,7 +431,7 @@ export function gainStrengthExp(exp) {
export function gainDefenseExp(exp) {
if (isNaN(exp)) {
console.log("ERR: NaN passed into player.gainDefenseExp()"); return;
console.error("ERR: NaN passed into player.gainDefenseExp()"); return;
}
this.defense_exp += exp;
if(this.defense_exp < 0) {
@ -441,7 +441,7 @@ export function gainDefenseExp(exp) {
export function gainDexterityExp(exp) {
if (isNaN(exp)) {
console.log("ERR: NaN passed into Player.gainDexterityExp()"); return;
console.error("ERR: NaN passed into Player.gainDexterityExp()"); return;
}
this.dexterity_exp += exp;
if(this.dexterity_exp < 0) {
@ -451,7 +451,7 @@ export function gainDexterityExp(exp) {
export function gainAgilityExp(exp) {
if (isNaN(exp)) {
console.log("ERR: NaN passed into Player.gainAgilityExp()"); return;
console.error("ERR: NaN passed into Player.gainAgilityExp()"); return;
}
this.agility_exp += exp;
if(this.agility_exp < 0) {
@ -461,7 +461,7 @@ export function gainAgilityExp(exp) {
export function gainCharismaExp(exp) {
if (isNaN(exp)) {
console.log("ERR: NaN passed into Player.gainCharismaExp()"); return;
console.error("ERR: NaN passed into Player.gainCharismaExp()"); return;
}
this.charisma_exp += exp;
if(this.charisma_exp < 0) {
@ -471,7 +471,7 @@ export function gainCharismaExp(exp) {
export function gainIntelligenceExp(exp) {
if (isNaN(exp)) {
console.log("ERROR: NaN passed into Player.gainIntelligenceExp()"); return;
console.error("ERROR: NaN passed into Player.gainIntelligenceExp()"); return;
}
if (SourceFileFlags[5] > 0 || this.intelligence > 0) {
this.intelligence_exp += exp;
@ -1827,7 +1827,6 @@ export function isQualified(company, position) {
/********** Reapplying Augmentations and Source File ***********/
export function reapplyAllAugmentations(resetMultipliers=true) {
console.log("Re-applying augmentations");
if (resetMultipliers) {
this.resetMultipliers();
}
@ -1856,7 +1855,6 @@ export function reapplyAllAugmentations(resetMultipliers=true) {
}
export function reapplyAllSourceFiles() {
console.log("Re-applying source files");
//Will always be called after reapplyAllAugmentations() so multipliers do not have to be reset
//this.resetMultipliers();

@ -20,6 +20,7 @@ import { Message } from "./Message/Message";
import { initMessages, Messages } from "./Message/MessageHelpers";
import { prestigeWorkerScripts } from "./NetscriptWorker";
import { Player } from "./Player";
import { resetPidCounter } from "./Netscript/Pid";
import {
AllServers,
@ -172,6 +173,8 @@ function prestigeAugmentation() {
DaedalusServer.serversOnNetwork.push(WorldDaemon.ip);
}
}
resetPidCounter();
}
@ -346,6 +349,8 @@ function prestigeSourceFile() {
// Gain int exp
Player.gainIntelligenceExp(5);
resetPidCounter();
}
export {prestigeAugmentation, prestigeSourceFile};

@ -112,7 +112,7 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
var request = objectStore.put(saveString, "save");
request.onerror = function(e) {
console.log("Error saving game to IndexedDB: " + e);
console.error("Error saving game to IndexedDB: " + e);
}
request.onsuccess = function(e) {
@ -124,7 +124,7 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
} catch(e) {
if (e.code == 22) {
createStatusText("Save failed for localStorage! Check console(F12)");
console.log("Failed to save game to localStorage because the size of the save file " +
console.error("Failed to save game to localStorage because the size of the save file " +
"is too large. However, the game will still be saved to IndexedDb if your browser " +
"supports it. If you would like to save to localStorage as well, then " +
"consider killing several of your scripts to " +
@ -247,7 +247,7 @@ function loadGame(saveString) {
try {
Settings.load(saveObj.SettingsSave);
} catch(e) {
console.log("ERROR: Failed to parse Settings. Re-initing default values");
console.error("ERROR: Failed to parse Settings. Re-initing default values");
Settings.init();
}
} else {
@ -257,7 +257,7 @@ function loadGame(saveString) {
try {
loadFconf(saveObj.FconfSettingsSave);
} catch(e) {
console.log("ERROR: Failed to parse .fconf Settings.");
console.error("ERROR: Failed to parse .fconf Settings.");
}
}
if (saveObj.hasOwnProperty("VersionSave")) {
@ -281,7 +281,7 @@ function loadGame(saveString) {
try {
loadAllGangs(saveObj.AllGangsSave);
} catch(e) {
console.log("ERROR: Failed to parse AllGangsSave: " + e);
console.error("ERROR: Failed to parse AllGangsSave: " + e);
}
}
@ -439,7 +439,7 @@ function loadImportedGame(saveObj, saveString) {
try {
loadFconf(saveObj.FconfSettingsSave);
} catch(e) {
console.log("ERROR: Failed to load .fconf settings when importing");
console.error("ERROR: Failed to load .fconf settings when importing");
}
}
if (saveObj.hasOwnProperty("VersionSave")) {
@ -460,7 +460,7 @@ function loadImportedGame(saveObj, saveString) {
try {
loadAllGangs(saveObj.AllGangsSave);
} catch(e) {
console.log("ERROR: Failed to parse AllGangsSave: " + e);
console.error("ERROR: Failed to parse AllGangsSave: " + e);
}
}

@ -91,7 +91,7 @@ let NetscriptFunctions =
"createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" +
"getOwnedSourceFiles|getAugmentationsFromFaction|" +
"getAugmentationPrereq|getAugmentationCost|purchaseAugmentation|" +
"installAugmentations|" +
"softReset|installAugmentations|getAugmentationStats|" +
// TIX API
"getStockPrice|getStockPosition|getStockSymbols|getStockMaxShares|" +
@ -103,7 +103,7 @@ let NetscriptFunctions =
// Hacknet Node API
"hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" +
"upgradeLevel|upgradeRam|upgradeCore|upgradeCache|getLevelUpgradeCost|" +
"getRamUpgradeCost|getCoreUpgradeCost|getCacheUpgradeCost|" +
"getRamUpgradeCost|getCoreUpgradeCost|getCacheUpgradeCost|maxNumNodes|" +
// Gang API
"gang|" +

@ -1760,7 +1760,14 @@ let Terminal = {
const filepath = Terminal.getFilepath(filename);
const script = Terminal.getScript(filename);
if (script == null) {
Engine.loadScriptEditorContent(filepath);
let code = ""
if(filename.endsWith(".ns")) {
code = `export async function main(ns) {
}`;
}
console.log('default code');
Engine.loadScriptEditorContent(filepath, code);
} else {
Engine.loadScriptEditorContent(filepath, script.code);
}

@ -86,7 +86,7 @@ import {
} from "./PersonObjects/Resleeving/ResleevingUI";
import { createStatusText } from "./ui/createStatusText";
import { displayCharacterInfo } from "./ui/displayCharacterInfo";
import { CharacterInfo } from "./ui/CharacterInfo";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { setSettingsLabels } from "./ui/setSettingsLabels";
@ -567,7 +567,7 @@ const Engine = {
/// Display character info
updateCharacterInfo: function() {
displayCharacterInfo(Engine.Display.characterInfo, Player);
ReactDOM.render(CharacterInfo(Player), Engine.Display.characterInfo)
},
// TODO Refactor this into Faction implementation
@ -592,7 +592,7 @@ const Engine = {
factionsList.appendChild(createElement("a", {
class:"a-link-button", innerText:factionName, padding:"4px", margin:"4px",
display:"inline-block",
clickListener:()=>{
clickListener: () => {
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
@ -774,6 +774,7 @@ const Engine = {
updateDisplaysLong: 15,
updateActiveScriptsDisplay: 5,
createProgramNotifications: 10,
augmentationsNotifications: 10,
checkFactionInvitations: 100,
passiveFactionGrowth: 600,
messages: 150,
@ -868,6 +869,19 @@ const Engine = {
Engine.Counters.createProgramNotifications = 10;
}
if (Engine.Counters.augmentationsNotifications <= 0) {
var num = Player.queuedAugmentations.length;
var elem = document.getElementById("augmentations-notification");
if (num > 0) {
elem.innerHTML = num;
elem.setAttribute("class", "notification-on");
} else {
elem.innerHTML = "";
elem.setAttribute("class", "notification-off");
}
Engine.Counters.augmentationsNotifications = 10;
}
if (Engine.Counters.checkFactionInvitations <= 0) {
var invitedFactions = Player.checkForFactionInvitations();
if (invitedFactions.length > 0) {
@ -881,6 +895,17 @@ const Engine = {
var randFaction = invitedFactions[Math.floor(Math.random() * invitedFactions.length)];
inviteToFaction(randFaction);
}
const num = Player.factionInvitations.length;
const elem = document.getElementById("factions-notification");
if (num > 0) {
elem.innerHTML = num;
elem.setAttribute("class", "notification-on");
} else {
elem.innerHTML = "";
elem.setAttribute("class", "notification-off");
}
Engine.Counters.checkFactionInvitations = 100;
}
@ -1536,19 +1561,18 @@ window.onload = function() {
indexedDbRequest = window.indexedDB.open("bitburnerSave", 1);
indexedDbRequest.onerror = function(e) {
console.log("Error opening indexedDB: ");
console.log(e);
console.error("Error opening indexedDB: ");
console.error(e);
return Engine.load(null); // Try to load from localstorage
};
indexedDbRequest.onsuccess = function(e) {
console.log("Opening bitburnerSave database successful!");
indexedDb = e.target.result;
var transaction = indexedDb.transaction(["savestring"]);
var objectStore = transaction.objectStore("savestring");
var request = objectStore.get("save");
request.onerror = function(e) {
console.log("Error in Database request to get savestring: " + e);
console.error("Error in Database request to get savestring: " + e);
return Engine.load(null); // Try to load from localstorage
}

@ -60,9 +60,11 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
</li>
<li id="factions-tab" class="mainmenu-accordion-panel">
<button id="factions-menu-link"> Factions </button>
<span id="factions-notification" class="notification-off"> </span>
</li>
<li id="augmentations-tab" class="mainmenu-accordion-panel">
<button id="augmentations-menu-link" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"> Augmentations </button>
<span id="augmentations-notification" class="notification-off"> </span>
</li>
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel">
<button id="hacknet-nodes-menu-link"> Hacknet Nodes </button>

@ -24,14 +24,14 @@ export function ScriptProduction(props: IProps): React.ReactElement {
return (
<p id="active-scripts-total-prod">
Total online production of Active scripts:
Total online production of Active scripts:&nbsp;
<span className="money-gold">
<span id="active-scripts-total-production-active">
{numeralWrapper.formatMoney(onlineProduction)}
</span> / sec
</span><br />
Total online production since last Aug installation:
Total online production since last Aug installation:&nbsp;
<span id="active-scripts-total-prod-aug-total" className="money-gold">
{numeralWrapper.formatMoney(props.p.scriptProdSinceLastAug)}
</span>

238
src/ui/CharacterInfo.tsx Normal file

@ -0,0 +1,238 @@
import * as React from "react";
import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodes } from "../BitNode/BitNode";
import { IPlayer } from "../PersonObjects/IPlayer";
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { getPurchaseServerLimit } from "../Server/ServerPurchases";
import { MaxNumberHacknetServers } from "../Hacknet/HacknetServer";
export function CharacterInfo(p: IPlayer): React.ReactElement {
function LastEmployer(): React.ReactElement {
if (p.companyName) {
return <><span>Employer at which you last worked: {p.companyName}</span><br /></>;
}
return <></>;
}
function LastJob(): React.ReactElement {
if (p.companyName !== "") {
return <><span>Job you last worked: {p.jobs[p.companyName]}</span><br /></>;
}
return <></>;
}
function Employers(): React.ReactElement {
if (p.jobs && Object.keys(p.jobs).length !== 0)
return <>
<span>All Employers:</span><br />
<ul>
{Object.keys(p.jobs).map(j => <li key='j'> * {j}</li>)}
</ul><br /><br />
</>
return <></>;
}
function convertMoneySourceTrackerToString(src: MoneySourceTracker): string {
let parts: string[] = [`Total: ${numeralWrapper.formatMoney(src.total)}`];
if (src.bladeburner) { parts.push(`Bladeburner: ${numeralWrapper.formatMoney(src.bladeburner)}`) };
if (src.codingcontract) { parts.push(`Coding Contracts: ${numeralWrapper.formatMoney(src.codingcontract)}`) };
if (src.work) { parts.push(`Company Work: ${numeralWrapper.formatMoney(src.work)}`) };
if (src.corporation) { parts.push(`Corporation: ${numeralWrapper.formatMoney(src.corporation)}`) };
if (src.crime) { parts.push(`Crimes: ${numeralWrapper.formatMoney(src.crime)}`) };
if (src.gang) { parts.push(`Gang: ${numeralWrapper.formatMoney(src.gang)}`) };
if (src.hacking) { parts.push(`Hacking: ${numeralWrapper.formatMoney(src.hacking)}`) };
if (src.hacknetnode) { parts.push(`Hacknet Nodes: ${numeralWrapper.formatMoney(src.hacknetnode)}`) };
if (src.hospitalization) { parts.push(`Hospitalization: ${numeralWrapper.formatMoney(src.hospitalization)}`) };
if (src.infiltration) { parts.push(`Infiltration: ${numeralWrapper.formatMoney(src.infiltration)}`) };
if (src.stock) { parts.push(`Stock Market: ${numeralWrapper.formatMoney(src.stock)}`) };
return parts.join("<br>");
}
function openMoneyModal() {
let txt: string = "<u>Money earned since you last installed Augmentations:</u><br>" +
convertMoneySourceTrackerToString(p.moneySourceA);
if (p.sourceFiles.length !== 0) {
txt += "<br><br><u>Money earned in this BitNode:</u><br>" +
convertMoneySourceTrackerToString(p.moneySourceB);
}
dialogBoxCreate(txt, false);
}
function Intelligence(): React.ReactElement {
if (p.intelligence > 0) {
return <tr key='5'>
<td>Intelligence:</td>
<td style={{textAlign: 'right'}}>{(p.intelligence).toLocaleString()}</td>
</tr>;
}
return <></>;
}
function MultiplierTable(props: any): React.ReactElement {
function bn5Stat(r: any) {
if(SourceFileFlags[5] > 0 && r.length > 2 && r[1] != r[2]) {
return <td key='2' style={{textAlign: 'right'}}> ({numeralWrapper.formatPercentage(r[2])})</td>
}
return undefined;
}
return <>
<table>
<tbody>
{props.rows.map((r: any) => <tr key={r[0]}>
<td key='0'>{r[0]} multiplier: </td>
<td key='1' style={{textAlign: 'right'}}>{numeralWrapper.formatPercentage(r[1])}</td>
{bn5Stat(r)}
</tr>)}
</tbody>
</table>
</>
}
function BitNodeTimeText(): React.ReactElement {
if(p.sourceFiles.length > 0) {
return <>
<span>Time played since last Bitnode destroyed: {convertTimeMsToTimeElapsedString(p.playtimeSinceLastBitnode)}</span>
<br />
</>
}
return <></>
}
function CurrentBitNode(): React.ReactElement {
if(p.sourceFiles.length > 0) {
const index = "BitNode" + p.bitNodeN;
return <>
<span>Current BitNode: {p.bitNodeN} ({BitNodes[index].name})</span><br /><br />
<div style={{width:"60%", fontSize: "13px", marginLeft:"4%"}}>
{BitNodes[index].info.split("<br>").map((t, i) => <div key={i}>
<span style={{whiteSpace: 'pre-wrap', overflowWrap: 'break-word'}}>{t}</span><br />
</div>)}
</div>
</>
}
return <></>
}
return (
<pre>
<b>General</b>
<br /><br />
<span>Current City: {p.city}</span><br />
<LastEmployer />
<LastJob />
<Employers />
<span>Money: {numeralWrapper.formatMoney(p.money.toNumber())}</span>
<button className="popup-box-button" style={{display: 'inline-block', float: 'none'}} onClick={openMoneyModal}>Money Statistics & Breakdown</button><br /><br />
<b>Stats</b>
<table>
<tbody>
<tr key='0'>
<td key='0'>Hacking:</td>
<td key='1' style={{textAlign: 'right'}}>{p.hacking_skill.toLocaleString()}</td>
<td key='2' style={{textAlign: 'right'}}>({numeralWrapper.format(p.hacking_exp, '0.000a')} exp)</td>
</tr>
<tr key='1'>
<td key='0'>Strength:</td>
<td key='1' style={{textAlign: 'right'}}>{p.strength.toLocaleString()}</td>
<td key='2' style={{textAlign: 'right'}}>({numeralWrapper.format(p.strength_exp, '0.000a')} exp)</td>
</tr>
<tr key='2'>
<td key='0'>Defense:</td>
<td key='1' style={{textAlign: 'right'}}>{p.defense.toLocaleString()}</td>
<td key='2' style={{textAlign: 'right'}}>({numeralWrapper.format(p.defense_exp, '0.000a')} exp)</td>
</tr>
<tr key='3'>
<td key='0'>Dexterity:</td>
<td key='1' style={{textAlign: 'right'}}>{p.dexterity.toLocaleString()}</td>
<td key='2' style={{textAlign: 'right'}}>({numeralWrapper.format(p.dexterity_exp, '0.000a')} exp)</td>
</tr>
<tr key='4'>
<td key='0'>Agility:</td>
<td key='1' style={{textAlign: 'right'}}>{p.agility.toLocaleString()}</td>
<td key='2' style={{textAlign: 'right'}}>({numeralWrapper.format(p.agility_exp, '0.000a')} exp)</td>
</tr>
<tr key='5'>
<td key='0'>Charisma:</td>
<td key='1' style={{textAlign: 'right'}}>{p.charisma.toLocaleString()}</td>
<td key='2' style={{textAlign: 'right'}}>({numeralWrapper.format(p.charisma_exp, '0.000a')} exp)</td>
</tr>
<Intelligence />
</tbody>
</table>
<br />
<MultiplierTable rows={[
['Hacking Chance', p.hacking_chance_mult],
['Hacking Speed', p.hacking_speed_mult],
['Hacking Money', p.hacking_money_mult, p.hacking_speed_mult*BitNodeMultipliers.ScriptHackMoney],
['Hacking Growth', p.hacking_grow_mult, p.hacking_money_mult*BitNodeMultipliers.ServerGrowthRate]
]} /><br />
<MultiplierTable rows={[
['Hacking Level', p.hacking_mult, p.hacking_mult*BitNodeMultipliers.HackingLevelMultiplier],
['Hacking Experience', p.hacking_exp_mult, p.hacking_exp_mult*BitNodeMultipliers.HackExpGain],
]} /><br />
<MultiplierTable rows={[
['Strength Level', p.strength_mult, p.strength_mult*BitNodeMultipliers.StrengthLevelMultiplier],
['Strength Experience', p.strength_exp_mult]
]} /><br />
<MultiplierTable rows={[
['Defense Level', p.defense_mult, p.defense_mult*BitNodeMultipliers.DefenseLevelMultiplier],
['Defense Experience', p.defense_exp_mult]
]} /><br />
<MultiplierTable rows={[
['Dexterity Level', p.dexterity_mult, p.dexterity_mult*BitNodeMultipliers.DexterityLevelMultiplier],
['Dexterity Experience', p.dexterity_exp_mult]
]} /><br />
<MultiplierTable rows={[
['Agility Level', p.agility_mult, p.agility_mult*BitNodeMultipliers.AgilityLevelMultiplier],
['Agility Experience', p.agility_exp_mult]
]} /><br />
<MultiplierTable rows={[
['Charisma Level', p.charisma_mult, p.charisma_mult*BitNodeMultipliers.CharismaLevelMultiplier],
['Charisma Experience', p.charisma_exp_mult]
]} /><br />
<MultiplierTable rows={[
['Hacknet Node production', p.hacknet_node_money_mult, p.hacknet_node_money_mult*BitNodeMultipliers.HacknetNodeMoney],
['Hacknet Node purchase cost', p.hacknet_node_purchase_cost_mult],
['Hacknet Node RAM upgrade cost', p.hacknet_node_ram_cost_mult],
['Hacknet Node Core purchase cost', p.hacknet_node_core_cost_mult],
['Hacknet Node level upgrade cost', p.hacknet_node_level_cost_mult]
]} /><br />
<MultiplierTable rows={[
['Company reputation gain', p.company_rep_mult],
['Faction reputation gain', p.faction_rep_mult, p.faction_rep_mult*BitNodeMultipliers.FactionWorkRepGain],
['Salary', p.work_money_mult, p.work_money_mult*BitNodeMultipliers.CompanyWorkMoney]
]} /><br />
<MultiplierTable rows={[
['Crime success', p.crime_success_mult],
['Crime money', p.crime_money_mult, p.crime_money_mult*BitNodeMultipliers.CrimeMoney]
]} /><br /><br />
<b>Misc.</b><br /><br />
<span>Servers owned: {p.purchasedServers.length} / {getPurchaseServerLimit()}</span><br />
<span>Hacknet Nodes owned: {p.hacknetNodes.length} / {MaxNumberHacknetServers}</span><br />
<span>Augmentations installed: {p.augmentations.length}</span><br />
<span>Time played since last Augmentation: {convertTimeMsToTimeElapsedString(p.playtimeSinceLastAug)}</span><br />
<BitNodeTimeText />
<span>Time played: {convertTimeMsToTimeElapsedString(p.totalPlaytime)}</span><br />
<br />
<CurrentBitNode />
</pre>
)
}

@ -10,7 +10,7 @@ export class CharacterOverviewComponent extends Component {
render() {
const intelligence = (
<tr id="character-int-wrapper">
<td>Int:&nbsp;</td><td id="character-int-text" className="character-stat-cell">{(Player.intelligence).toLocaleString()}</td>
<td className="character-int-cell">Int:&nbsp;</td><td id="character-int-text" className="character-int-cell character-stat-cell">{(Player.intelligence).toLocaleString()}</td>
</tr>
);
@ -19,28 +19,28 @@ export class CharacterOverviewComponent extends Component {
<table>
<tbody>
<tr id="character-hp-wrapper">
<td>Hp:</td><td id="character-hp-text" className="character-stat-cell">{Player.hp + " / " + Player.max_hp}</td>
<td className="character-hp-cell">Hp:</td><td id="character-hp-text" className="character-hp-cell character-stat-cell">{Player.hp + " / " + Player.max_hp}</td>
</tr>
<tr id="character-money-wrapper">
<td>Money:&nbsp;</td><td id="character-money-text" className="character-stat-cell">{numeralWrapper.format(Player.money.toNumber(), '$0.000a')}</td>
<td className="character-money-cell">Money:&nbsp;</td><td id="character-money-text" className="character-money-cell character-stat-cell">{numeralWrapper.format(Player.money.toNumber(), '$0.000a')}</td>
</tr>
<tr id="character-hack-wrapper">
<td>Hack:&nbsp;</td><td id="character-hack-text" className="character-stat-cell">{(Player.hacking_skill).toLocaleString()}</td>
<td className="character-hack-cell">Hack:&nbsp;</td><td id="character-hack-text" className="character-hack-cell character-stat-cell">{(Player.hacking_skill).toLocaleString()}</td>
</tr>
<tr id="character-str-wrapper">
<td>Str:&nbsp;</td><td id="character-str-text" className="character-stat-cell">{(Player.strength).toLocaleString()}</td>
<td className="character-combat-cell">Str:&nbsp;</td><td id="character-str-text" className="character-combat-cell character-stat-cell">{(Player.strength).toLocaleString()}</td>
</tr>
<tr id="character-def-wrapper">
<td>Def:&nbsp;</td><td id="character-def-text" className="character-stat-cell">{(Player.defense).toLocaleString()}</td>
<td className="character-combat-cell">Def:&nbsp;</td><td id="character-def-text" className="character-combat-cell character-stat-cell">{(Player.defense).toLocaleString()}</td>
</tr>
<tr id="character-dex-wrapper">
<td>Dex:&nbsp;</td><td id="character-dex-text" className="character-stat-cell">{(Player.dexterity).toLocaleString()}</td>
<td className="character-combat-cell">Dex:&nbsp;</td><td id="character-dex-text" className="character-combat-cell character-stat-cell">{(Player.dexterity).toLocaleString()}</td>
</tr>
<tr id="character-agi-wrapper">
<td>Agi:&nbsp;</td><td id="character-agi-text" className="character-stat-cell">{(Player.agility).toLocaleString()}</td>
<td className="character-combat-cell">Agi:&nbsp;</td><td id="character-agi-text" className="character-combat-cell character-stat-cell">{(Player.agility).toLocaleString()}</td>
</tr>
<tr id="character-cha-wrapper">
<td>Cha:&nbsp;</td><td id="character-cha-text" className="character-stat-cell">{(Player.charisma).toLocaleString()}</td>
<td className="character-cha-cell">Cha:&nbsp;</td><td id="character-cha-text" className="character-cha-cell character-stat-cell">{(Player.charisma).toLocaleString()}</td>
</tr>
{
Player.intelligence >= 1 &&
@ -51,4 +51,4 @@ export class CharacterOverviewComponent extends Component {
</div>
)
}
}
}

@ -16,7 +16,7 @@ type IProps = {
}
export function SourceFileAccordion(props: IProps): React.ReactElement {
const maxLevel = props.sf.n === 3 ? "∞" : "3";
const maxLevel = props.sf.n === 12 ? "∞" : "3";
return (
<Accordion

@ -1,157 +0,0 @@
// Displays character info on a given element. This is used to create & update
// the 'Stats' page from the main menu
import { BitNodes } from "../BitNode/BitNode";
import { IPlayer } from "../PersonObjects/IPlayer";
import { numeralWrapper } from "../ui/numeralFormat";
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { createElement } from "../../utils/uiHelpers/createElement";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
export function displayCharacterInfo(elem: HTMLElement, p: IPlayer) {
removeChildrenFromElement(elem);
let companyPosition = "";
if (p.companyName !== "") {
companyPosition = p.jobs[p.companyName];
}
var intText = "";
if (p.intelligence > 0) {
intText = 'Intelligence: ' + (p.intelligence).toLocaleString() + '<br>';
}
let bitNodeTimeText = "";
if(p.sourceFiles.length > 0) {
bitNodeTimeText = 'Time played since last Bitnode destroyed: ' + convertTimeMsToTimeElapsedString(p.playtimeSinceLastBitnode) + '<br>';
}
const unlockedBitnodes: boolean = p.sourceFiles.length !== 0;
// General info
elem.appendChild(createElement("pre", {
display: "block",
innerHTML:
'<b>General</b><br><br>' +
'Current City: ' + p.city + '<br><br>' +
`Employer at which you last worked: ${p.companyName}<br>` +
`Job you last worked: ${companyPosition}<br>` +
`All Employers: ${Object.keys(p.jobs).join(", ")}<br><br>`
}));
// Money, and a button to show money breakdown
elem.appendChild(createElement("pre", {
display: "inline-block",
innerHTML: 'Money: ' + numeralWrapper.formatMoney(p.money.toNumber()) + '<br><br><br>',
margin: "6px",
}));
function convertMoneySourceTrackerToString(src: MoneySourceTracker): string {
let parts: string[] = [`Total: ${numeralWrapper.formatMoney(src.total)}`];
if (src.bladeburner) { parts.push(`Bladeburner: ${numeralWrapper.formatMoney(src.bladeburner)}`) };
if (src.codingcontract) { parts.push(`Coding Contracts: ${numeralWrapper.formatMoney(src.codingcontract)}`) };
if (src.work) { parts.push(`Company Work: ${numeralWrapper.formatMoney(src.work)}`) };
if (src.corporation) { parts.push(`Corporation: ${numeralWrapper.formatMoney(src.corporation)}`) };
if (src.crime) { parts.push(`Crimes: ${numeralWrapper.formatMoney(src.crime)}`) };
if (src.gang) { parts.push(`Gang: ${numeralWrapper.formatMoney(src.gang)}`) };
if (src.hacking) { parts.push(`Hacking: ${numeralWrapper.formatMoney(src.hacking)}`) };
if (src.hacknetnode) { parts.push(`Hacknet Nodes: ${numeralWrapper.formatMoney(src.hacknetnode)}`) };
if (src.hospitalization) { parts.push(`Hospitalization: ${numeralWrapper.formatMoney(src.hospitalization)}`) };
if (src.infiltration) { parts.push(`Infiltration: ${numeralWrapper.formatMoney(src.infiltration)}`) };
if (src.stock) { parts.push(`Stock Market: ${numeralWrapper.formatMoney(src.stock)}`) };
return parts.join("<br>");
}
elem.appendChild(createElement("button", {
class: "popup-box-button",
display: "inline-block",
float: "none",
innerText: "Money Statistics & Breakdown",
clickListener: () => {
let txt: string = "<u>Money earned since you last installed Augmentations:</u><br>" +
convertMoneySourceTrackerToString(p.moneySourceA);
if (unlockedBitnodes) {
txt += "<br><br><u>Money earned in this BitNode:</u><br>" +
convertMoneySourceTrackerToString(p.moneySourceB);
}
dialogBoxCreate(txt, false);
}
}));
// Stats and multiplier
elem.appendChild(createElement("pre", {
display: "block",
innerHTML:
'<b>Stats</b><br><br>' +
'Hacking Level: ' + (p.hacking_skill).toLocaleString() +
' (' + numeralWrapper.format(p.hacking_exp, '(0.000a)') + ' experience)<br>' +
'Strength: ' + (p.strength).toLocaleString() +
' (' + numeralWrapper.format(p.strength_exp, '(0.000a)') + ' experience)<br>' +
'Defense: ' + (p.defense).toLocaleString() +
' (' + numeralWrapper.format(p.defense_exp, '(0.000a)') + ' experience)<br>' +
'Dexterity: ' + (p.dexterity).toLocaleString() +
' (' + numeralWrapper.format(p.dexterity_exp, '(0.000a)') + ' experience)<br>' +
'Agility: ' + (p.agility).toLocaleString() +
' (' + numeralWrapper.format(p.agility_exp, '(0.000a)') + ' experience)<br>' +
'Charisma: ' + (p.charisma).toLocaleString() +
' (' + numeralWrapper.format(p.charisma_exp, '(0.000a)') + ' experience)<br>' +
intText + '<br><br>' +
'<b>Multipliers</b><br><br>' +
'Hacking Chance multiplier: ' + numeralWrapper.formatPercentage(p.hacking_chance_mult) + '<br>' +
'Hacking Speed multiplier: ' + numeralWrapper.formatPercentage(p.hacking_speed_mult) + '<br>' +
'Hacking Money multiplier: ' + numeralWrapper.formatPercentage(p.hacking_money_mult) + '<br>' +
'Hacking Growth multiplier: ' + numeralWrapper.formatPercentage(p.hacking_grow_mult) + '<br><br>' +
'Hacking Level multiplier: ' + numeralWrapper.formatPercentage(p.hacking_mult) + '<br>' +
'Hacking Experience multiplier: ' + numeralWrapper.formatPercentage(p.hacking_exp_mult) + '<br><br>' +
'Strength Level multiplier: ' + numeralWrapper.formatPercentage(p.strength_mult) + '<br>' +
'Strength Experience multiplier: ' + numeralWrapper.formatPercentage(p.strength_exp_mult) + '<br><br>' +
'Defense Level multiplier: ' + numeralWrapper.formatPercentage(p.defense_mult) + '<br>' +
'Defense Experience multiplier: ' + numeralWrapper.formatPercentage(p.defense_exp_mult) + '<br><br>' +
'Dexterity Level multiplier: ' + numeralWrapper.formatPercentage(p.dexterity_mult) + '<br>' +
'Dexterity Experience multiplier: ' + numeralWrapper.formatPercentage(p.dexterity_exp_mult) + '<br><br>' +
'Agility Level multiplier: ' + numeralWrapper.formatPercentage(p.agility_mult) + '<br>' +
'Agility Experience multiplier: ' + numeralWrapper.formatPercentage(p.agility_exp_mult) + '<br><br>' +
'Charisma Level multiplier: ' + numeralWrapper.formatPercentage(p.charisma_mult) + '<br>' +
'Charisma Experience multiplier: ' + numeralWrapper.formatPercentage(p.charisma_exp_mult) + '<br><br>' +
'Hacknet Node production multiplier: ' + numeralWrapper.formatPercentage(p.hacknet_node_money_mult) + '<br>' +
'Hacknet Node purchase cost multiplier: ' + numeralWrapper.formatPercentage(p.hacknet_node_purchase_cost_mult) + '<br>' +
'Hacknet Node RAM upgrade cost multiplier: ' + numeralWrapper.formatPercentage(p.hacknet_node_ram_cost_mult) + '<br>' +
'Hacknet Node Core purchase cost multiplier: ' + numeralWrapper.formatPercentage(p.hacknet_node_core_cost_mult) + '<br>' +
'Hacknet Node level upgrade cost multiplier: ' + numeralWrapper.formatPercentage(p.hacknet_node_level_cost_mult) + '<br><br>' +
'Company reputation gain multiplier: ' + numeralWrapper.formatPercentage(p.company_rep_mult) + '<br>' +
'Faction reputation gain multiplier: ' + numeralWrapper.formatPercentage(p.faction_rep_mult) + '<br>' +
'Salary multiplier: ' + numeralWrapper.formatPercentage(p.work_money_mult) + '<br>' +
'Crime success multiplier: ' + numeralWrapper.formatPercentage(p.crime_success_mult) + '<br>' +
'Crime money multiplier: ' + numeralWrapper.formatPercentage(p.crime_money_mult) + '<br><br><br>' +
'<b>Misc</b><br><br>' +
'Servers owned: ' + p.purchasedServers.length + '<br>' +
'Hacknet Nodes owned: ' + p.hacknetNodes.length + '<br>' +
'Augmentations installed: ' + p.augmentations.length + '<br>' +
'Time played since last Augmentation: ' + convertTimeMsToTimeElapsedString(p.playtimeSinceLastAug) + '<br>' +
bitNodeTimeText +
'Time played: ' + convertTimeMsToTimeElapsedString(p.totalPlaytime),
}));
// BitNode information, if player has gotten that far
if (unlockedBitnodes) {
var index = "BitNode" + p.bitNodeN;
elem.appendChild(createElement("p", {
width:"60%",
innerHTML:
"<br>Current BitNode: " + p.bitNodeN + " (" + BitNodes[index].name + ")<br><br>",
}));
elem.appendChild(createElement("p", {
width:"60%", fontSize: "13px", marginLeft:"4%",
innerHTML:BitNodes[index].info,
}))
}
}

@ -42,7 +42,6 @@ document.addEventListener("keydown", function (event) {
let dialogBoxOpened = false;
function dialogBoxCreate(txt, preformatted=false) {
console.log(`dialogBoxCreate() called`)
var container = document.createElement("div");
container.setAttribute("class", "dialog-box-container");