Finished refactoring augmentations page UI to use react

This commit is contained in:
danielyxie 2019-05-15 00:15:07 -07:00 committed by danielyxie
parent 9442b348e6
commit 2597b33f81
14 changed files with 561 additions and 508 deletions

@ -10,6 +10,13 @@
padding-top: 10px; padding-top: 10px;
} }
#augmentations-content {
> p {
font-size: $defaultFontSize * 0.875;
width: 70%;
}
}
.augmentations-list { .augmentations-list {
button, button,
div { div {
@ -18,10 +25,7 @@
} }
button { button {
padding: 2px 5px; padding: 4px;
} }
div {
padding: 6px;
}
} }

@ -3,10 +3,11 @@ import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { AugmentationsRoot } from "./ui/Root";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Factions, import { Factions, factionExists } from "../Faction/Factions";
factionExists } from "../Faction/Factions";
import { addWorkerScript } from "../NetscriptWorker"; import { addWorkerScript } from "../NetscriptWorker";
import { Player } from "../Player"; import { Player } from "../Player";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
@ -16,17 +17,23 @@ import { Script } from "../Script/Script";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { Page, routing } from "../ui/navigationTracking";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { createAccordionElement } from "../../utils/uiHelpers/createAccordionElement"; import { createAccordionElement } from "../../utils/uiHelpers/createAccordionElement";
import { Reviver, Generic_toJSON, import {
Generic_fromJSON } from "../../utils/JSONReviver"; Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../../utils/JSONReviver";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { clearObject } from "../../utils/helpers/clearObject"; import { clearObject } from "../../utils/helpers/clearObject";
import { createElement } from "../../utils/uiHelpers/createElement"; import { createElement } from "../../utils/uiHelpers/createElement";
import { isString } from "../../utils/helpers/isString"; import { isString } from "../../utils/helpers/isString";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement"; import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
import React from "react";
import ReactDOM from "react-dom";
function AddToAugmentations(aug) { function AddToAugmentations(aug) {
var name = aug.name; var name = aug.name;
@ -2092,211 +2099,17 @@ function augmentationExists(name) {
return Augmentations.hasOwnProperty(name); return Augmentations.hasOwnProperty(name);
} }
function displayAugmentationsContent(contentEl) { export function displayAugmentationsContent(contentEl) {
removeChildrenFromElement(contentEl); if (!routing.isOn(Page.Augmentations)) { return; }
contentEl.appendChild(createElement("h1", { if (!(contentEl instanceof HTMLElement)) { return; }
innerText:"Purchased Augmentations",
}));
contentEl.appendChild(createElement("pre", { ReactDOM.render(
width:"70%", whiteSpace:"pre-wrap", display:"block", <AugmentationsRoot
innerText:"Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to install them.\n" + exportGameFn={saveObject.exportGame.bind(saveObject)}
"WARNING: Installing your Augmentations resets most of your progress, including:\n\n" + installAugmentationsFn={installAugmentations}
"Stats/Skill levels and Experience\n" + />,
"Money\n" + contentEl
"Scripts on every computer but your home computer\n" + );
"Purchased servers\n" +
"Hacknet Nodes\n" +
"Faction/Company reputation\n" +
"Stocks\n" +
"Installing Augmentations lets you start over with the perks and benefits granted by all " +
"of the Augmentations you have ever installed. Also, you will keep any scripts and RAM/Core upgrades " +
"on your home computer (but you will lose all programs besides NUKE.exe)."
}));
//Install Augmentations button
contentEl.appendChild(createElement("a", {
class:"a-link-button", innerText:"Install Augmentations",
tooltip:"'I never asked for this'",
clickListener:()=>{
installAugmentations();
return false;
}
}));
//Backup button
contentEl.appendChild(createElement("a", {
class:"a-link-button flashing-button", innerText:"Backup Save (Export)",
tooltip:"It's always a good idea to backup/export your save!",
clickListener:()=>{
saveObject.exportGame();
return false;
}
}));
//Purchased/queued augmentations list
var queuedAugmentationsList = createElement("ul", {class:"augmentations-list"});
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
var augName = Player.queuedAugmentations[i].name;
var aug = Augmentations[augName];
var displayName = augName;
if (augName === AugmentationNames.NeuroFluxGovernor) {
displayName += " - Level " + (Player.queuedAugmentations[i].level);
}
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
queuedAugmentationsList.appendChild(accordion[0]);
}
contentEl.appendChild(queuedAugmentationsList);
//Installed augmentations list
contentEl.appendChild(createElement("h1", {
innerText:"Installed Augmentations", marginTop:"8px",
}));
contentEl.appendChild(createElement("p", {
width:"70%", whiteSpace:"pre-wrap",
innerText:"List of all Augmentations (including Source Files) that have been " +
"installed. You have gained the effects of these Augmentations."
}));
var augmentationsList = createElement("ul", {class:"augmentations-list"});
//Expand/Collapse All buttons
contentEl.appendChild(createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Expand All", display:"inline-block",
clickListener:()=>{
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) {
if (!allHeaders[i].classList.contains("active")) {allHeaders[i].click();}
}
}
}));
contentEl.appendChild(createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Collapse All", display:"inline-block",
clickListener:()=>{
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
for (var i = 0; i < allHeaders.length; ++i) {
if (allHeaders[i].classList.contains("active")) {allHeaders[i].click();}
}
}
}));
//Sort Buttons
const sortInOrderButton = createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Sort in Order",
tooltip:"Sorts the Augmentations alphabetically and Source Files in numerical order (1, 2, 3,...)",
clickListener:()=>{
removeChildrenFromElement(augmentationsList);
//Create a copy of Player's Source Files and augs array and sort them
var sourceFiles = Player.sourceFiles.slice();
var augs = Player.augmentations.slice();
sourceFiles.sort((sf1, sf2)=>{
return sf1.n - sf2.n;
});
augs.sort((aug1, aug2)=>{
return aug1.name <= aug2.name ? -1 : 1;
});
displaySourceFiles(augmentationsList, sourceFiles);
displayAugmentations(augmentationsList, augs);
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
}
});
contentEl.appendChild(sortInOrderButton);
const sortByAcquirementTimeButton = createElement("a", {
class:"a-link-button", fontSize:"14px", innerText:"Sort by Acquirement Time",
tooltip:"Sorts the Augmentations and Source Files based on when you acquired them (same as default)",
clickListener:()=>{
removeChildrenFromElement(augmentationsList);
displaySourceFiles(augmentationsList, Player.sourceFiles);
displayAugmentations(augmentationsList, Player.augmentations);
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
}
});
contentEl.appendChild(sortByAcquirementTimeButton);
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sortInOrderButton.click();
} else {
sortByAcquirementTimeButton.click();
}
contentEl.appendChild(augmentationsList);
// Display multiplier information at the bottom
contentEl.appendChild(createElement("p", {
display: "block",
innerHTML:
`<br><br><strong><u>Total Multipliers:</u></strong><br>` +
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
'Hacking Money multiplier: ' + formatNumber(Player.hacking_money_mult * 100, 2) + '%<br>' +
'Hacking Growth multiplier: ' + formatNumber(Player.hacking_grow_mult * 100, 2) + '%<br><br>' +
'Hacking Level multiplier: ' + formatNumber(Player.hacking_mult * 100, 2) + '%<br>' +
'Hacking Experience multiplier: ' + formatNumber(Player.hacking_exp_mult * 100, 2) + '%<br><br>' +
'Strength Level multiplier: ' + formatNumber(Player.strength_mult * 100, 2) + '%<br>' +
'Strength Experience multiplier: ' + formatNumber(Player.strength_exp_mult * 100, 2) + '%<br><br>' +
'Defense Level multiplier: ' + formatNumber(Player.defense_mult * 100, 2) + '%<br>' +
'Defense Experience multiplier: ' + formatNumber(Player.defense_exp_mult * 100, 2) + '%<br><br>' +
'Dexterity Level multiplier: ' + formatNumber(Player.dexterity_mult * 100, 2) + '%<br>' +
'Dexterity Experience multiplier: ' + formatNumber(Player.dexterity_exp_mult * 100, 2) + '%<br><br>' +
'Agility Level multiplier: ' + formatNumber(Player.agility_mult * 100, 2) + '%<br>' +
'Agility Experience multiplier: ' + formatNumber(Player.agility_exp_mult * 100, 2) + '%<br><br>' +
'Charisma Level multiplier: ' + formatNumber(Player.charisma_mult * 100, 2) + '%<br>' +
'Charisma Experience multiplier: ' + formatNumber(Player.charisma_exp_mult * 100, 2) + '%<br><br>' +
'Hacknet Node production multiplier: ' + formatNumber(Player.hacknet_node_money_mult * 100, 2) + '%<br>' +
'Hacknet Node purchase cost multiplier: ' + formatNumber(Player.hacknet_node_purchase_cost_mult * 100, 2) + '%<br>' +
'Hacknet Node RAM upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_ram_cost_mult * 100, 2) + '%<br>' +
'Hacknet Node Core purchase cost multiplier: ' + formatNumber(Player.hacknet_node_core_cost_mult * 100, 2) + '%<br>' +
'Hacknet Node level upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_level_cost_mult * 100, 2) + '%<br><br>' +
'Company reputation gain multiplier: ' + formatNumber(Player.company_rep_mult * 100, 2) + '%<br>' +
'Faction reputation gain multiplier: ' + formatNumber(Player.faction_rep_mult * 100, 2) + '%<br>' +
'Salary multiplier: ' + formatNumber(Player.work_money_mult * 100, 2) + '%<br>' +
'Crime success multiplier: ' + formatNumber(Player.crime_success_mult * 100, 2) + '%<br>' +
'Crime money multiplier: ' + formatNumber(Player.crime_money_mult * 100, 2) + '%<br><br><br>',
}))
}
//Creates the accordion elements to display Augmentations
// @listElement - List DOM element to append accordion elements to
// @augs - Array of Augmentation objects
function displayAugmentations(listElement, augs) {
for (var i = 0; i < augs.length; ++i) {
var augName = augs[i].name;
var aug = Augmentations[augName];
var displayName = augName;
if (augName === AugmentationNames.NeuroFluxGovernor) {
displayName += " - Level " + (augs[i].level);
}
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
listElement.appendChild(accordion[0]);
}
}
//Creates the accordion elements to display Source Files
// @listElement - List DOM element to append accordion elements to
// @sourceFiles - Array of Source File objects
function displaySourceFiles(listElement, sourceFiles) {
for (var i = 0; i < sourceFiles.length; ++i) {
var srcFileKey = "SourceFile" + sourceFiles[i].n;
var sourceFileObject = SourceFiles[srcFileKey];
if (sourceFileObject == null) {
console.log("ERROR: Invalid source file number: " + sourceFiles[i].n);
continue;
}
const maxLevel = sourceFiles[i].n == 12 ? "∞" : "3";
var accordion = createAccordionElement({
hdrText:sourceFileObject.name + "<br>" + "Level " + (sourceFiles[i].lvl) + " / "+maxLevel,
panelText:sourceFileObject.info
});
listElement.appendChild(accordion[0]);
}
} }
export function isRepeatableAug(aug) { export function isRepeatableAug(aug) {
@ -2307,6 +2120,9 @@ export function isRepeatableAug(aug) {
return false; return false;
} }
export {installAugmentations, export {
initAugmentations, applyAugmentation, augmentationExists, installAugmentations,
displayAugmentationsContent}; initAugmentations,
applyAugmentation,
augmentationExists,
};

@ -30,11 +30,13 @@ export function InstalledAugmentations(): React.ReactElement {
} }
return ( return (
<AugmentationAccordion aug={aug} key={e.name} level={level} /> <li key={e.name}>
<AugmentationAccordion aug={aug} level={level} />
</li>
) )
}); });
return ( return (
<ul>{augs}</ul> <>{augs}</>
) )
} }

@ -7,18 +7,22 @@
*/ */
import * as React from "react"; import * as React from "react";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { ListConfiguration } from "./ListConfiguration";
import { OwnedSourceFiles } from "./OwnedSourceFiles";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
type IProps = { type IProps = {}
}
type IState = { type IState = {
rerenderFlag: boolean; rerenderFlag: boolean;
} }
export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> { export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> {
listRef: React.RefObject<HTMLUListElement>;
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -26,8 +30,44 @@ export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps
rerenderFlag: false, rerenderFlag: false,
} }
this.collapseAllHeaders = this.collapseAllHeaders.bind(this);
this.expandAllHeaders = this.expandAllHeaders.bind(this);
this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this); this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this);
this.sortInOrder = this.sortInOrder.bind(this); this.sortInOrder = this.sortInOrder.bind(this);
this.listRef = React.createRef();
}
collapseAllHeaders() {
const ul = this.listRef.current;
if (ul == null) { return; }
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (ticker.classList.contains("active")) {
ticker.click();
}
}
}
expandAllHeaders() {
const ul = this.listRef.current;
if (ul == null) { return; }
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (!ticker.classList.contains("active")) {
ticker.click();
}
}
} }
rerender() { rerender() {
@ -50,7 +90,18 @@ export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps
render() { render() {
return ( return (
<>
<ListConfiguration
collapseAllButtonsFn={this.collapseAllHeaders}
expandAllButtonsFn={this.expandAllHeaders}
sortByAcquirementTimeFn={this.sortByAcquirementTime}
sortInOrderFn={this.sortInOrder}
/>
<ul className="augmentations-list" ref={this.listRef}>
<OwnedSourceFiles />
<InstalledAugmentations />
</ul>
</>
) )
} }
} }

@ -3,3 +3,37 @@
* Source-Files are displayed in the Augmentations UI * Source-Files are displayed in the Augmentations UI
*/ */
import * as React from "react"; import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
collapseAllButtonsFn: () => void;
expandAllButtonsFn: () => void;
sortByAcquirementTimeFn: () => void;
sortInOrderFn: () => void;
}
export function ListConfiguration(props: IProps): React.ReactElement {
return (
<>
<StdButton
onClick={props.expandAllButtonsFn}
text="Expand All"
/>
<StdButton
onClick={props.collapseAllButtonsFn}
text="Collapse All"
/>
<StdButton
onClick={props.sortInOrderFn}
text="Sort in Order"
tooltip="Sorts the Augmentations alphabetically and Source-Files in numeral order"
/>
<StdButton
onClick={props.sortByAcquirementTimeFn}
text="Sort by Acquirement Time"
tooltip="Sorts the Augmentations and Source-Files based on when you acquired them (same as default)"
/>
</>
)
}

@ -5,36 +5,37 @@
import * as React from "react"; import * as React from "react";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { SourceFiles } from "../../SourceFile/SourceFiles";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion"; import { SourceFileAccordion } from "../../ui/React/SourceFileAccordion";
export function OwnedSourceFiles(): React.ReactElement { export function OwnedSourceFiles(): React.ReactElement {
const sourceAugs = Player.augmentations.slice(); const sourceSfs = Player.sourceFiles.slice();
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) { if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sourceAugs.sort((aug1, aug2) => { sourceSfs.sort((sf1, sf2) => {
return aug1.name <= aug2.name ? -1 : 1; return sf1.n - sf2.n;
}); });
} }
const augs = sourceAugs.map((e) => { const sfs = sourceSfs.map((e) => {
const aug = Augmentations[e.name]; const srcFileKey = "SourceFile" + e.n;
const sfObj = SourceFiles[srcFileKey];
let level = null; if (sfObj == null) {
if (e.name === AugmentationNames.NeuroFluxGovernor) { console.error(`Invalid source file number: ${e.n}`);
level = e.level; return null;
} }
return ( return (
<AugmentationAccordion aug={aug} key={e.name} level={level} /> <li key={e.n}>
<SourceFileAccordion level={e.lvl} sf={sfObj} />
</li>
) )
}); });
return ( return (
<ul>{augs}</ul> <>{sfs}</>
) );
} }

@ -0,0 +1,96 @@
/**
* React component for displaying the player's multipliers on the Augmentation UI page
*/
import * as React from "react";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
export function PlayerMultipliers(): React.ReactElement {
return (
<>
<p><strong><u>Total Multipliers:</u></strong></p>
<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>
</>
)
}

@ -20,7 +20,9 @@ export function PurchasedAugmentations(): React.ReactElement {
} }
augs.push( augs.push(
<AugmentationAccordion aug={aug} key={ownedAug.name} level={level} /> <li key={`${ownedAug.name}${ownedAug.level}`}>
<AugmentationAccordion aug={aug} level={level} />
</li>
) )
} }

@ -4,9 +4,11 @@
*/ */
import * as React from "react"; import * as React from "react";
import { Augmentations } from "../../Augmentation/Augmentations"; import { InstalledAugmentationsAndSourceFiles } from "./InstalledAugmentationsAndSourceFiles";
import { Player } from "../../Player"; import { PlayerMultipliers } from "./PlayerMultipliers";
import { PurchasedAugmentations } from "./PurchasedAugmentations";
import { Player } from "../../Player";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
type IProps = { type IProps = {
@ -25,7 +27,7 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
render() { render() {
return ( return (
<div> <div id="augmentations-content">
<h1>Purchased Augmentations</h1> <h1>Purchased Augmentations</h1>
<p> <p>
Below is a list of all Augmentations you have purchased but not Below is a list of all Augmentations you have purchased but not
@ -34,14 +36,14 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
<p> <p>
WARNING: Installing your Augmentations resets most of your progress, WARNING: Installing your Augmentations resets most of your progress,
including: including:
</p> </p><br />
<p>- Stats/Skill levels and Experience</p> <p>- Stats/Skill levels and Experience</p>
<p>- Money</p> <p>- Money</p>
<p>- Scripts on every computer but your home computer</p> <p>- Scripts on every computer but your home computer</p>
<p>- Purchased servers</p> <p>- Purchased servers</p>
<p>- Hacknet Nodes</p> <p>- Hacknet Nodes</p>
<p>- Faction/Company reputation</p> <p>- Faction/Company reputation</p>
<p>- Stocks</p> <p>- Stocks</p><br />
<p> <p>
Installing Augmentations lets you start over with the perks and Installing Augmentations lets you start over with the perks and
benefits granted by all of the Augmentations you have ever benefits granted by all of the Augmentations you have ever
@ -62,10 +64,19 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
text="Backup Save (Export)" text="Backup Save (Export)"
tooltip="It's always a good idea to backup/export your save!" tooltip="It's always a good idea to backup/export your save!"
/> />
<PurchasedAugmentations />
<ul className="augmentations-list"> <h1>Installed Augmentations</h1>
<p>
{
`List of all Augmentations ${Player.sourceFiles.length > 0 ? "and Source Files " : ""} ` +
`that have been installed. You have gained the effects of these.`
}
</p>
<InstalledAugmentationsAndSourceFiles />
</ul> <br /> <br />
<PlayerMultipliers />
</div> </div>
) )
} }

@ -25,11 +25,9 @@ class BitNode {
} }
export let BitNodes: IMap<BitNode> = {}; export const BitNodes: IMap<BitNode> = {};
export function initBitNodes() { BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
BitNodes = {};
BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
"The first BitNode created by the Enders to imprison the minds of humans. It became " + "The first BitNode created by the Enders to imprison the minds of humans. It became " +
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" + "the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
"This is the first BitNode that you play through. It has no special " + "This is the first BitNode that you play through. It has no special " +
@ -41,7 +39,7 @@ export function initBitNodes() {
"Level 1: 16%<br>" + "Level 1: 16%<br>" +
"Level 2: 24%<br>" + "Level 2: 24%<br>" +
"Level 3: 28%"); "Level 3: 28%");
BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " + "From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " + "left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " + "people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
@ -63,7 +61,7 @@ export function initBitNodes() {
"Level 1: 24%<br>" + "Level 1: 24%<br>" +
"Level 2: 36%<br>" + "Level 2: 36%<br>" +
"Level 3: 42%"); "Level 3: 42%");
BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization", BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization",
"Our greatest illusion is that a healthy society can revolve around a " + "Our greatest illusion is that a healthy society can revolve around a " +
"single-minded pursuit of wealth.<br><br>" + "single-minded pursuit of wealth.<br><br>" +
"Sometime in the early 21st century economic and political globalization turned " + "Sometime in the early 21st century economic and political globalization turned " +
@ -82,7 +80,7 @@ export function initBitNodes() {
"Level 1: 8%<br>" + "Level 1: 8%<br>" +
"Level 2: 12%<br>" + "Level 2: 12%<br>" +
"Level 3: 14%"); "Level 3: 14%");
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine", BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine",
"The Singularity has arrived. The human race is gone, replaced " + "The Singularity has arrived. The human race is gone, replaced " +
"by artificially superintelligent beings that are more machine than man. <br><br>" + "by artificially superintelligent beings that are more machine than man. <br><br>" +
"In this BitNode, progressing is significantly harder. Experience gain rates " + "In this BitNode, progressing is significantly harder. Experience gain rates " +
@ -94,7 +92,7 @@ export function initBitNodes() {
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " + "upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " + "Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
"that you can use."); "that you can use.");
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman", BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman",
"They said it couldn't be done. They said the human brain, " + "They said it couldn't be done. They said the human brain, " +
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " + "along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " + "of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
@ -118,7 +116,7 @@ export function initBitNodes() {
"Level 1: 8%<br>" + "Level 1: 8%<br>" +
"Level 2: 12%<br>" + "Level 2: 12%<br>" +
"Level 3: 14%"); "Level 3: 14%");
BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain", BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain",
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " + "In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " + "androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " + "of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
@ -138,7 +136,7 @@ export function initBitNodes() {
"Level 1: 8%<br>" + "Level 1: 8%<br>" +
"Level 2: 12%<br>" + "Level 2: 12%<br>" +
"Level 3: 14%"); "Level 3: 14%");
BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans", BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans",
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " + "In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " + "for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " + "breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
@ -161,7 +159,7 @@ export function initBitNodes() {
"Level 1: 8%<br>" + "Level 1: 8%<br>" +
"Level 2: 12%<br>" + "Level 2: 12%<br>" +
"Level 3: 14%"); "Level 3: 14%");
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps", BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" + "You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
"In this BitNode:<br><br>" + "In this BitNode:<br><br>" +
"You start with $250 million<br>" + "You start with $250 million<br>" +
@ -176,7 +174,7 @@ export function initBitNodes() {
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" + "Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
"This Source-File also increases your hacking growth multipliers by: " + "This Source-File also increases your hacking growth multipliers by: " +
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%"); "<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed", BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " + "When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " + "became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " + "powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
@ -194,7 +192,7 @@ export function initBitNodes() {
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" + "Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " + "(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
"when installing Augmentations)"); "when installing Augmentations)");
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are", BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " + "In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " + "to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " + "or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
@ -210,7 +208,7 @@ export function initBitNodes() {
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " + "Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " + "upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
"Each level of this Source-File also grants you a Duplicate Sleeve"); "Each level of this Source-File also grants you a Duplicate Sleeve");
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.", BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " + "The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " + "of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" + "the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
@ -234,26 +232,25 @@ export function initBitNodes() {
"Level 1: 32%<br>" + "Level 1: 32%<br>" +
"Level 2: 48%<br>" + "Level 2: 48%<br>" +
"Level 3: 56%"); "Level 3: 56%");
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.", BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
"To iterate is human, to recurse divine.<br><br>" + "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 your 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 " + "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. " + "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)"); "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)");
//Books: Frontera, Shiner // Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON"); BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON"); BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON"); BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON"); BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON"); BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON"); BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON"); BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON"); BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON"); BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON"); BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON"); BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
}
export function initBitNodeMultipliers(p: IPlayer) { export function initBitNodeMultipliers(p: IPlayer) {
if (p.bitNodeN == null) { if (p.bitNodeN == null) {

@ -15,7 +15,6 @@ import {
import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { import {
BitNodes, BitNodes,
initBitNodes,
initBitNodeMultipliers initBitNodeMultipliers
} from "./BitNode/BitNode"; } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner"; import { Bladeburner } from "./Bladeburner";
@ -310,8 +309,8 @@ const Engine = {
loadAugmentationsContent: function() { loadAugmentationsContent: function() {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.augmentationsContent.style.display = "block"; Engine.Display.augmentationsContent.style.display = "block";
displayAugmentationsContent(Engine.Display.augmentationsContent);
routing.navigateTo(Page.Augmentations); routing.navigateTo(Page.Augmentations);
displayAugmentationsContent(Engine.Display.augmentationsContent);
MainMenuLinks.Augmentations.classList.add("active"); MainMenuLinks.Augmentations.classList.add("active");
}, },
@ -488,13 +487,20 @@ const Engine = {
Engine.Display.activeScriptsContent.style.display = "none"; Engine.Display.activeScriptsContent.style.display = "none";
clearHacknetNodesUI(); clearHacknetNodesUI();
Engine.Display.createProgramContent.style.display = "none"; Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent.style.display = "none"; Engine.Display.factionsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
Engine.Display.factionContent.style.display = "none"; Engine.Display.factionContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
Engine.Display.augmentationsContent.style.display = "none"; Engine.Display.augmentationsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.augmentationsContent);
Engine.Display.tutorialContent.style.display = "none"; Engine.Display.tutorialContent.style.display = "none";
Engine.Display.locationContent.style.display = "none"; Engine.Display.locationContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.locationContent); ReactDOM.unmountComponentAtNode(Engine.Display.locationContent);
Engine.Display.workInProgressContent.style.display = "none"; Engine.Display.workInProgressContent.style.display = "none";
Engine.Display.redPillContent.style.display = "none"; Engine.Display.redPillContent.style.display = "none";
Engine.Display.cinematicTextContent.style.display = "none"; Engine.Display.cinematicTextContent.style.display = "none";
@ -1038,7 +1044,6 @@ const Engine = {
// Load game from save or create new game // Load game from save or create new game
if (loadGame(saveString)) { if (loadGame(saveString)) {
initBitNodes();
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
Engine.setDisplayElements(); // Sets variables for important DOM elements Engine.setDisplayElements(); // Sets variables for important DOM elements
Engine.init(); // Initialize buttons, work, etc. Engine.init(); // Initialize buttons, work, etc.
@ -1160,7 +1165,6 @@ const Engine = {
} else { } else {
// No save found, start new game // No save found, start new game
console.log("Initializing new game"); console.log("Initializing new game");
initBitNodes();
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
initSpecialServerIps(); initSpecialServerIps();
Engine.setDisplayElements(); // Sets variables for important DOM elements Engine.setDisplayElements(); // Sets variables for important DOM elements

@ -45,12 +45,12 @@ export class Accordion extends React.Component<IProps, IState> {
render() { render() {
return ( return (
<div> <>
<button className={"accordion-header"} onClick={this.handleHeaderClick}> <button className={"accordion-header"} onClick={this.handleHeaderClick}>
{this.props.headerContent} {this.props.headerContent}
</button> </button>
<AccordionPanel opened={this.state.panelOpened} panelContent={this.props.panelContent} /> <AccordionPanel opened={this.state.panelOpened} panelContent={this.props.panelContent} />
</div> </>
) )
} }
} }

@ -2,7 +2,7 @@
* React Component for displaying a single Augmentation as an accordion. * React Component for displaying a single Augmentation as an accordion.
* *
* The header of the accordion contains the Augmentation's name (and level, if * The header of the accordion contains the Augmentation's name (and level, if
* applicable), and the accordion's panel contains the Augmentation's level. * applicable), and the accordion's panel contains the Augmentation's description.
*/ */
import * as React from "react"; import * as React from "react";
@ -26,8 +26,8 @@ export function AugmentationAccordion(props: IProps): React.ReactElement {
return ( return (
<Accordion <Accordion
headerContent={<p>{displayName}</p>} headerContent={<>{displayName}</>}
panelContent={<p>{props.aug.info}</p>} panelContent={<p dangerouslySetInnerHTML={{__html: props.aug.info}}></p>}
/> />
) )
} }

@ -0,0 +1,35 @@
/**
* React Component for displaying a single Source-File as an accordion.
*
* The header of the accordion contains the Source-Files's name and level,
* and the accordion's panel contains the Source-File's description.
*/
import * as React from "react";
import { Accordion } from "./Accordion";
import { SourceFile } from "../../SourceFile/SourceFile";
type IProps = {
level: number,
sf: SourceFile,
}
export function SourceFileAccordion(props: IProps): React.ReactElement {
const maxLevel = props.sf.n === 3 ? "∞" : "3";
return (
<Accordion
headerContent={
<>
{props.sf.name}
<br />
{`Level ${props.level} / ${maxLevel}`}
</>
}
panelContent={
<p dangerouslySetInnerHTML={{__html: props.sf.info}}></p>
}
/>
)
}