convert all hacknet to ts

This commit is contained in:
Olivier Gagnon
2021-09-09 03:17:01 -04:00
parent c97fece747
commit b7e07bc7f2
41 changed files with 1947 additions and 1930 deletions
-57
View File
@@ -1,57 +0,0 @@
/**
* React Component for the Hacknet Node UI
*
* Displays general information about Hacknet Nodes
*/
import React from "react";
import { hasHacknetServers } from "../HacknetHelpers";
export class GeneralInfo extends React.Component {
getSecondParagraph() {
if (hasHacknetServers()) {
return (
`Here, you can purchase a Hacknet Server, an upgraded version of the Hacknet Node. ` +
`Hacknet Servers will perform computations and operations on the network, earning ` +
`you hashes. Hashes can be spent on a variety of different upgrades.`
);
} else {
return (
`Here, you can purchase a Hacknet Node, a specialized machine that can connect ` +
`and contribute its resources to the Hacknet network. This allows you to take ` +
`a small percentage of profits from hacks performed on the network. Essentially, ` +
`you are renting out your Node's computing power.`
);
}
}
getThirdParagraph() {
if (hasHacknetServers()) {
return (
`Hacknet Servers can also be used as servers to run scripts. However, running scripts ` +
`on a server will reduce its hash rate (hashes generated per second). A Hacknet Server's hash ` +
`rate will be reduced by the percentage of RAM that is being used by that Server to run ` +
`scripts.`
);
} else {
return (
`Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node ` +
`can be upgraded in order to increase its computing power and thereby increase ` +
`the profit you earn from it.`
);
}
}
render() {
return (
<div>
<p className={"hacknet-general-info"}>
The Hacknet is a global, decentralized network of machines. It is used by hackers all around the world to
anonymously share computing power and perform distributed cyberattacks without the fear of being traced.
</p>
<p className={"hacknet-general-info"}>{this.getSecondParagraph()}</p>
<p className={"hacknet-general-info"}>{this.getThirdParagraph()}</p>
</div>
);
}
}
+50
View File
@@ -0,0 +1,50 @@
/**
* React Component for the Hacknet Node UI
*
* Displays general information about Hacknet Nodes
*/
import React from "react";
interface IProps {
hasHacknetServers: boolean;
}
export function GeneralInfo(props: IProps): React.ReactElement {
return (
<div>
<p className={"hacknet-general-info"}>
The Hacknet is a global, decentralized network of machines. It is used by hackers all around the world to
anonymously share computing power and perform distributed cyberattacks without the fear of being traced.
</p>
{!props.hasHacknetServers ? (
<>
<p className={"hacknet-general-info"}>
{`Here, you can purchase a Hacknet Node, a specialized machine that can connect ` +
`and contribute its resources to the Hacknet network. This allows you to take ` +
`a small percentage of profits from hacks performed on the network. Essentially, ` +
`you are renting out your Node's computing power.`}
</p>
<p className={"hacknet-general-info"}>
{`Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node ` +
`can be upgraded in order to increase its computing power and thereby increase ` +
`the profit you earn from it.`}
</p>
</>
) : (
<>
<p className={"hacknet-general-info"}>
{`Here, you can purchase a Hacknet Server, an upgraded version of the Hacknet Node. ` +
`Hacknet Servers will perform computations and operations on the network, earning ` +
`you hashes. Hashes can be spent on a variety of different upgrades.`}
</p>
<p className={"hacknet-general-info"}>
{`Hacknet Servers can also be used as servers to run scripts. However, running scripts ` +
`on a server will reduce its hash rate (hashes generated per second). A Hacknet Server's hash ` +
`rate will be reduced by the percentage of RAM that is being used by that Server to run ` +
`scripts.`}
</p>
</>
)}
</div>
);
}
-171
View File
@@ -1,171 +0,0 @@
/**
* React Component for the Hacknet Node UI.
* This Component displays the panel for a single Hacknet Node
*/
import React from "react";
import { HacknetNodeConstants } from "../data/Constants";
import {
getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
getMaxNumberCoreUpgrades,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
} from "../HacknetHelpers";
import { Player } from "../../Player";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
export class HacknetNode extends React.Component {
render() {
const node = this.props.node;
const purchaseMult = this.props.purchaseMultiplier;
const recalculate = this.props.recalculate;
// Upgrade Level Button
let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetNodeConstants.MaxLevel) {
upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
} else {
const levelsToMax = HacknetNodeConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
upgradeLevelContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeLevelCost} player={Player} />
</>
);
if (Player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled";
} else {
upgradeLevelClass = "std-button";
}
}
const upgradeLevelOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
}
purchaseLevelUpgrade(node, numUpgrades);
recalculate();
return false;
};
let upgradeRamContent, upgradeRamClass;
if (node.ram >= HacknetNodeConstants.MaxRam) {
upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
} else {
const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram));
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
upgradeRamContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeRamCost} player={Player} />
</>
);
if (Player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled";
} else {
upgradeRamClass = "std-button";
}
}
const upgradeRamOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
}
purchaseRamUpgrade(node, numUpgrades);
recalculate();
return false;
};
let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetNodeConstants.MaxCores) {
upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
} else {
const levelsToMax = HacknetNodeConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
upgradeCoresContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeCoreCost} player={Player} />
</>
);
if (Player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled";
} else {
upgradeCoresClass = "std-button";
}
}
const upgradeCoresOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
}
purchaseCoreUpgrade(node, numUpgrades);
recalculate();
return false;
};
return (
<li className={"hacknet-node"}>
<div className={"hacknet-node-container"}>
<div className={"row"}>
<h1 style={{ fontSize: "1em" }}>{node.name}</h1>
</div>
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
<Money money={node.totalMoneyGenerated} player={Player} /> ({MoneyRate(node.moneyGainRatePerSecond)})
</span>
</div>
<div className={"row"}>
<p>Level:</p>
<span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelContent}
</button>
</div>
<div className={"row"}>
<p>RAM:</p>
<span className={"text upgradable-info"}>{node.ram}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamContent}
</button>
</div>
<div className={"row"}>
<p>Cores:</p>
<span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresContent}
</button>
</div>
</div>
</li>
);
}
}
+174
View File
@@ -0,0 +1,174 @@
/**
* React Component for the Hacknet Node UI.
* This Component displays the panel for a single Hacknet Node
*/
import React from "react";
import { HacknetNodeConstants } from "../data/Constants";
import {
getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
getMaxNumberCoreUpgrades,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
} from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { HacknetNode } from "../HacknetNode";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
interface IProps {
node: HacknetNode;
purchaseMultiplier: number | string;
rerender: () => void;
player: IPlayer;
}
export function HacknetNodeElem(props: IProps): React.ReactElement {
const node = props.node;
const purchaseMult = props.purchaseMultiplier;
const rerender = props.rerender;
// Upgrade Level Button
let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetNodeConstants.MaxLevel) {
upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel);
} else {
const levelsToMax = HacknetNodeConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult);
upgradeLevelContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeLevelCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled";
} else {
upgradeLevelClass = "std-button";
}
}
function upgradeLevelOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel);
}
purchaseLevelUpgrade(props.player, node, numUpgrades as number);
rerender();
}
let upgradeRamContent, upgradeRamClass;
if (node.ram >= HacknetNodeConstants.MaxRam) {
upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam);
} else {
const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram));
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult);
upgradeRamContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeRamCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled";
} else {
upgradeRamClass = "std-button";
}
}
function upgradeRamOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam);
}
purchaseRamUpgrade(props.player, node, numUpgrades as number);
rerender();
}
let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetNodeConstants.MaxCores) {
upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores);
} else {
const levelsToMax = HacknetNodeConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult);
upgradeCoresContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeCoreCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled";
} else {
upgradeCoresClass = "std-button";
}
}
function upgradeCoresOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores);
}
purchaseCoreUpgrade(props.player, node, numUpgrades as number);
rerender();
}
return (
<li className={"hacknet-node"}>
<div className={"hacknet-node-container"}>
<div className={"row"}>
<h1 style={{ fontSize: "1em" }}>{node.name}</h1>
</div>
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
<Money money={node.totalMoneyGenerated} player={props.player} /> ({MoneyRate(node.moneyGainRatePerSecond)})
</span>
</div>
<div className={"row"}>
<p>Level:</p>
<span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelContent}
</button>
</div>
<div className={"row"}>
<p>RAM:</p>
<span className={"text upgradable-info"}>{node.ram}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamContent}
</button>
</div>
<div className={"row"}>
<p>Cores:</p>
<span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresContent}
</button>
</div>
</div>
</li>
);
}
+145
View File
@@ -0,0 +1,145 @@
/**
* Root React Component for the Hacknet Node UI
*/
import React, { useState, useEffect } from "react";
import { GeneralInfo } from "./GeneralInfo";
import { HacknetNodeElem } from "./HacknetNodeElem";
import { HacknetServerElem } from "./HacknetServerElem";
import { HacknetNode } from "../HacknetNode";
import { HashUpgradePopup } from "./HashUpgradePopup";
import { MultiplierButtons } from "./MultiplierButtons";
import { PlayerInfo } from "./PlayerInfo";
import { PurchaseButton } from "./PurchaseButton";
import { PurchaseMultipliers } from "../data/Constants";
import {
getCostOfNextHacknetNode,
getCostOfNextHacknetServer,
hasHacknetServers,
purchaseHacknet,
} from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { AllServers } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { createPopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
}
export function HacknetRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const [purchaseMultiplier, setPurchaseMultiplier] = useState<number | string>(PurchaseMultipliers.x1);
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
function createHashUpgradesPopup(): void {
const id = "hacknet-server-hash-upgrades-popup";
createPopup(id, HashUpgradePopup, {
player: props.player,
});
}
let totalProduction = 0;
for (let i = 0; i < props.player.hacknetNodes.length; ++i) {
const node = props.player.hacknetNodes[i];
if (hasHacknetServers(props.player)) {
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
const hserver = AllServers[node];
if (hserver instanceof Server) throw new Error("node was a normal server"); // should never happen
if (hserver) {
totalProduction += hserver.hashRate;
} else {
console.warn(`Could not find Hacknet Server object in AllServers map (i=${i})`);
}
} else {
if (typeof node === "string") throw new Error("node was ip string"); // should never happen
totalProduction += node.moneyGainRatePerSecond;
}
}
function handlePurchaseButtonClick(): void {
purchaseHacknet(props.player);
rerender();
}
// Cost to purchase a new Hacknet Node
let purchaseCost;
if (hasHacknetServers(props.player)) {
purchaseCost = getCostOfNextHacknetServer(props.player);
} else {
purchaseCost = getCostOfNextHacknetNode(props.player);
}
// onClick event handlers for purchase multiplier buttons
const purchaseMultiplierOnClicks = [
() => setPurchaseMultiplier(PurchaseMultipliers.x1),
() => setPurchaseMultiplier(PurchaseMultipliers.x5),
() => setPurchaseMultiplier(PurchaseMultipliers.x10),
() => setPurchaseMultiplier(PurchaseMultipliers.MAX),
];
// HacknetNode components
const nodes = props.player.hacknetNodes.map((node: string | HacknetNode) => {
if (hasHacknetServers(props.player)) {
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
const hserver = AllServers[node];
if (hserver == null) {
throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`);
}
if (hserver instanceof Server) throw new Error("node was normal server"); // should never happen
return (
<HacknetServerElem
player={props.player}
key={hserver.hostname}
node={hserver}
purchaseMultiplier={purchaseMultiplier}
rerender={rerender}
/>
);
} else {
if (typeof node === "string") throw new Error("node was ip string"); // should never happen
return (
<HacknetNodeElem
player={props.player}
key={node.name}
node={node}
purchaseMultiplier={purchaseMultiplier}
rerender={rerender}
/>
);
}
});
return (
<div>
<h1>Hacknet {hasHacknetServers(props.player) ? "Servers" : "Nodes"}</h1>
<GeneralInfo hasHacknetServers={hasHacknetServers(props.player)} />
<PurchaseButton cost={purchaseCost} multiplier={purchaseMultiplier} onClick={handlePurchaseButtonClick} />
<br />
<div id={"hacknet-nodes-money-multipliers-div"}>
<PlayerInfo totalProduction={totalProduction} player={props.player} />
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={purchaseMultiplier} />
</div>
{hasHacknetServers(props.player) && (
<button className={"std-button"} onClick={createHashUpgradesPopup} style={{ display: "block" }}>
{"Spend Hashes on Upgrades"}
</button>
)}
<ul id={"hacknet-nodes-list"}>{nodes}</ul>
</div>
);
}
-225
View File
@@ -1,225 +0,0 @@
/**
* React Component for the Hacknet Node UI.
* This Component displays the panel for a single Hacknet Node
*/
import React from "react";
import { HacknetServerConstants } from "../data/Constants";
import {
getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
getMaxNumberCoreUpgrades,
getMaxNumberCacheUpgrades,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
purchaseCacheUpgrade,
updateHashManagerCapacity,
} from "../HacknetHelpers";
import { Player } from "../../Player";
import { Money } from "../../ui/React/Money";
import { Hashes } from "../../ui/React/Hashes";
import { HashRate } from "../../ui/React/HashRate";
export class HacknetServer extends React.Component {
render() {
const node = this.props.node;
const purchaseMult = this.props.purchaseMultiplier;
const recalculate = this.props.recalculate;
// Upgrade Level Button
let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetServerConstants.MaxLevel) {
upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
} else {
const levelsToMax = HacknetServerConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult);
upgradeLevelContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeLevelCost} player={Player} />
</>
);
if (Player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled";
} else {
upgradeLevelClass = "std-button";
}
}
const upgradeLevelOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
}
purchaseLevelUpgrade(node, numUpgrades);
recalculate();
return false;
};
// Upgrade RAM Button
let upgradeRamContent, upgradeRamClass;
if (node.maxRam >= HacknetServerConstants.MaxRam) {
upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
} else {
const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam));
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult);
upgradeRamContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeRamCost} player={Player} />
</>
);
if (Player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled";
} else {
upgradeRamClass = "std-button";
}
}
const upgradeRamOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
}
purchaseRamUpgrade(node, numUpgrades);
recalculate();
return false;
};
// Upgrade Cores Button
let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetServerConstants.MaxCores) {
upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
} else {
const levelsToMax = HacknetServerConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult);
upgradeCoresContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeCoreCost} player={Player} />
</>
);
if (Player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled";
} else {
upgradeCoresClass = "std-button";
}
}
const upgradeCoresOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
}
purchaseCoreUpgrade(node, numUpgrades);
recalculate();
return false;
};
// Upgrade Cache button
let upgradeCacheContent, upgradeCacheClass;
if (node.cache >= HacknetServerConstants.MaxCache) {
upgradeCacheContent = <>MAX CACHE</>;
upgradeCacheClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
} else {
const levelsToMax = HacknetServerConstants.MaxCache - node.cache;
multiplier = Math.min(levelsToMax, purchaseMult);
}
const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier);
upgradeCacheContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeCacheCost} player={Player} />
</>
);
if (Player.money.lt(upgradeCacheCost)) {
upgradeCacheClass = "std-button-disabled";
} else {
upgradeCacheClass = "std-button";
}
}
const upgradeCacheOnClick = () => {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
}
purchaseCacheUpgrade(node, numUpgrades);
recalculate();
updateHashManagerCapacity();
return false;
};
return (
<li className={"hacknet-node"}>
<div className={"hacknet-node-container"}>
<div className={"row"}>
<h1 style={{ fontSize: "1em" }}>{node.hostname}</h1>
</div>
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
{Hashes(node.totalHashesGenerated)} ({HashRate(node.hashRate)})
</span>
</div>
<div className={"row"}>
<p>Hash Capacity:</p>
<span className={"text"}>{Hashes(node.hashCapacity)}</span>
</div>
<div className={"row"}>
<p>Level:</p>
<span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelContent}
</button>
</div>
<div className={"row"}>
<p>RAM:</p>
<span className={"text upgradable-info"}>{node.maxRam}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamContent}
</button>
</div>
<div className={"row"}>
<p>Cores:</p>
<span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresContent}
</button>
</div>
<div className={"row"}>
<p>Cache Level:</p>
<span className={"text upgradable-info"}>{node.cache}</span>
<button className={upgradeCacheClass} onClick={upgradeCacheOnClick}>
{upgradeCacheContent}
</button>
</div>
</div>
</li>
);
}
}
+227
View File
@@ -0,0 +1,227 @@
/**
* React Component for the Hacknet Node UI.
* This Component displays the panel for a single Hacknet Node
*/
import React from "react";
import { HacknetServerConstants } from "../data/Constants";
import {
getMaxNumberLevelUpgrades,
getMaxNumberRamUpgrades,
getMaxNumberCoreUpgrades,
getMaxNumberCacheUpgrades,
purchaseLevelUpgrade,
purchaseRamUpgrade,
purchaseCoreUpgrade,
purchaseCacheUpgrade,
updateHashManagerCapacity,
} from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { HacknetServer } from "../HacknetServer";
import { Money } from "../../ui/React/Money";
import { Hashes } from "../../ui/React/Hashes";
import { HashRate } from "../../ui/React/HashRate";
interface IProps {
node: HacknetServer;
purchaseMultiplier: number | string;
rerender: () => void;
player: IPlayer;
}
export function HacknetServerElem(props: IProps): React.ReactElement {
const node = props.node;
const purchaseMult = props.purchaseMultiplier;
const rerender = props.rerender;
// Upgrade Level Button
let upgradeLevelContent, upgradeLevelClass;
if (node.level >= HacknetServerConstants.MaxLevel) {
upgradeLevelContent = <>MAX LEVEL</>;
upgradeLevelClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetServerConstants.MaxLevel);
} else {
const levelsToMax = HacknetServerConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult);
upgradeLevelContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeLevelCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeLevelCost)) {
upgradeLevelClass = "std-button-disabled";
} else {
upgradeLevelClass = "std-button";
}
}
function upgradeLevelOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(props.player, node, HacknetServerConstants.MaxLevel);
}
purchaseLevelUpgrade(props.player, node, numUpgrades as number);
rerender();
}
// Upgrade RAM Button
let upgradeRamContent, upgradeRamClass;
if (node.maxRam >= HacknetServerConstants.MaxRam) {
upgradeRamContent = <>MAX RAM</>;
upgradeRamClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetServerConstants.MaxRam);
} else {
const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam));
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult);
upgradeRamContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeRamCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeRamCost)) {
upgradeRamClass = "std-button-disabled";
} else {
upgradeRamClass = "std-button";
}
}
function upgradeRamOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(props.player, node, HacknetServerConstants.MaxRam);
}
purchaseRamUpgrade(props.player, node, numUpgrades as number);
rerender();
}
// Upgrade Cores Button
let upgradeCoresContent, upgradeCoresClass;
if (node.cores >= HacknetServerConstants.MaxCores) {
upgradeCoresContent = <>MAX CORES</>;
upgradeCoresClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetServerConstants.MaxCores);
} else {
const levelsToMax = HacknetServerConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult);
upgradeCoresContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeCoreCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeCoreCost)) {
upgradeCoresClass = "std-button-disabled";
} else {
upgradeCoresClass = "std-button";
}
}
function upgradeCoresOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(props.player, node, HacknetServerConstants.MaxCores);
}
purchaseCoreUpgrade(props.player, node, numUpgrades as number);
rerender();
}
// Upgrade Cache button
let upgradeCacheContent, upgradeCacheClass;
if (node.cache >= HacknetServerConstants.MaxCache) {
upgradeCacheContent = <>MAX CACHE</>;
upgradeCacheClass = "std-button-disabled";
} else {
let multiplier = 0;
if (purchaseMult === "MAX") {
multiplier = getMaxNumberCacheUpgrades(props.player, node, HacknetServerConstants.MaxCache);
} else {
const levelsToMax = HacknetServerConstants.MaxCache - node.cache;
multiplier = Math.min(levelsToMax, purchaseMult as number);
}
const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier);
upgradeCacheContent = (
<>
Upgrade x{multiplier} - <Money money={upgradeCacheCost} player={props.player} />
</>
);
if (props.player.money.lt(upgradeCacheCost)) {
upgradeCacheClass = "std-button-disabled";
} else {
upgradeCacheClass = "std-button";
}
}
function upgradeCacheOnClick(): void {
let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCacheUpgrades(props.player, node, HacknetServerConstants.MaxCache);
}
purchaseCacheUpgrade(props.player, node, numUpgrades as number);
rerender();
updateHashManagerCapacity(props.player);
}
return (
<li className={"hacknet-node"}>
<div className={"hacknet-node-container"}>
<div className={"row"}>
<h1 style={{ fontSize: "1em" }}>{node.hostname}</h1>
</div>
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
{Hashes(node.totalHashesGenerated)} ({HashRate(node.hashRate)})
</span>
</div>
<div className={"row"}>
<p>Hash Capacity:</p>
<span className={"text"}>{Hashes(node.hashCapacity)}</span>
</div>
<div className={"row"}>
<p>Level:</p>
<span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelContent}
</button>
</div>
<div className={"row"}>
<p>RAM:</p>
<span className={"text upgradable-info"}>{node.maxRam}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamContent}
</button>
</div>
<div className={"row"}>
<p>Cores:</p>
<span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresContent}
</button>
</div>
<div className={"row"}>
<p>Cache Level:</p>
<span className={"text upgradable-info"}>{node.cache}</span>
<button className={upgradeCacheClass} onClick={upgradeCacheOnClick}>
{upgradeCacheContent}
</button>
</div>
</div>
</li>
);
}
+70
View File
@@ -0,0 +1,70 @@
import React, { useState } from "react";
import { purchaseHashUpgrade } from "../HacknetHelpers";
import { HashManager } from "../HashManager";
import { HashUpgrade } from "../HashUpgrade";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { ServerDropdown, ServerType } from "../../ui/React/ServerDropdown";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { CopyableText } from "../../ui/React/CopyableText";
import { Hashes } from "../../ui/React/Hashes";
interface IProps {
player: IPlayer;
hashManager: HashManager;
upg: HashUpgrade;
rerender: () => void;
}
export function HacknetUpgradeElem(props: IProps): React.ReactElement {
const [selectedServer, setSelectedServer] = useState("ecorp");
function changeTargetServer(event: React.ChangeEvent<HTMLSelectElement>): void {
setSelectedServer(event.target.value);
}
function purchase(): void {
const canPurchase = props.hashManager.hashes >= props.hashManager.getUpgradeCost(props.upg.name);
if (canPurchase) {
const res = purchaseHashUpgrade(props.player, props.upg.name, selectedServer);
if (!res) {
dialogBoxCreate(
"Failed to purchase upgrade. This may be because you do not have enough hashes, " +
"or because you do not have access to the feature upgrade affects.",
);
}
props.rerender();
}
}
const hashManager = props.hashManager;
const upg = props.upg;
const cost = hashManager.getUpgradeCost(upg.name);
const level = hashManager.upgrades[upg.name];
const effect = upg.effectText(level);
// Purchase button
const canPurchase = hashManager.hashes >= cost;
const btnClass = canPurchase ? "std-button" : "std-button-disabled";
// We'll reuse a Bladeburner css class
return (
<div className={"bladeburner-action"}>
<CopyableText value={upg.name} />
<p>
Cost: {Hashes(cost)}, Bought: {level} times
</p>
<p>{upg.desc}</p>
<button className={btnClass} onClick={purchase}>
Purchase
</button>
{level > 0 && effect && <p>{effect}</p>}
{upg.hasTargetServer && (
<ServerDropdown serverType={ServerType.Foreign} onChange={changeTargetServer} style={{ margin: "5px" }} />
)}
</div>
);
}
-133
View File
@@ -1,133 +0,0 @@
/**
* Create the pop-up for purchasing upgrades with hashes
*/
import React from "react";
import { purchaseHashUpgrade } from "../HacknetHelpers";
import { HashManager } from "../HashManager";
import { HashUpgrades } from "../HashUpgrades";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
import { ServerDropdown, ServerType } from "../../ui/React/ServerDropdown";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { CopyableText } from "../../ui/React/CopyableText";
import { Hashes } from "../../ui/React/Hashes";
class HashUpgrade extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedServer: "ecorp",
};
this.changeTargetServer = this.changeTargetServer.bind(this);
this.purchase = this.purchase.bind(this, this.props.hashManager, this.props.upg);
}
changeTargetServer(e) {
this.setState({
selectedServer: e.target.value,
});
}
purchase(hashManager, upg) {
const canPurchase = hashManager.hashes >= hashManager.getUpgradeCost(upg.name);
if (canPurchase) {
const res = purchaseHashUpgrade(upg.name, this.state.selectedServer);
if (res) {
this.props.rerender();
} else {
dialogBoxCreate(
"Failed to purchase upgrade. This may be because you do not have enough hashes, " +
"or because you do not have access to the feature this upgrade affects.",
);
}
}
}
render() {
const hashManager = this.props.hashManager;
const upg = this.props.upg;
const cost = hashManager.getUpgradeCost(upg.name);
const level = hashManager.upgrades[upg.name];
const effect = upg.effectText(level);
// Purchase button
const canPurchase = hashManager.hashes >= cost;
const btnClass = canPurchase ? "std-button" : "std-button-disabled";
// We'll reuse a Bladeburner css class
return (
<div className={"bladeburner-action"}>
<CopyableText value={upg.name} />
<p>
Cost: {Hashes(cost)}, Bought: {level} times
</p>
<p>{upg.desc}</p>
<button className={btnClass} onClick={this.purchase}>
Purchase
</button>
{level > 0 && effect && <p>{effect}</p>}
{upg.hasTargetServer && (
<ServerDropdown
serverType={ServerType.Foreign}
onChange={this.changeTargetServer}
style={{ margin: "5px" }}
/>
)}
</div>
);
}
}
export class HashUpgradePopup extends React.Component {
constructor(props) {
super(props);
this.state = {
totalHashes: Player.hashManager.hashes,
};
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1e3);
}
componentWillUnmount() {
clearInterval(this.interval);
}
tick() {
this.setState({
totalHashes: Player.hashManager.hashes,
});
}
render() {
const rerender = this.props.rerender;
const hashManager = Player.hashManager;
if (!(hashManager instanceof HashManager)) {
throw new Error(`Player does not have a HashManager)`);
}
const upgradeElems = Object.keys(HashUpgrades).map((upgName) => {
const upg = HashUpgrades[upgName];
return <HashUpgrade upg={upg} hashManager={hashManager} key={upg.name} rerender={rerender} />;
});
return (
<div>
<p>Spend your hashes on a variety of different upgrades</p>
<p>Hashes: {numeralWrapper.formatHashes(this.state.totalHashes)}</p>
{upgradeElems}
</div>
);
}
}
+54
View File
@@ -0,0 +1,54 @@
/**
* Create the pop-up for purchasing upgrades with hashes
*/
import React, { useState, useEffect } from "react";
import { HashManager } from "../HashManager";
import { HashUpgrades } from "../HashUpgrades";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Hashes } from "../../ui/React/Hashes";
import { HacknetUpgradeElem } from "./HacknetUpgradeElem";
interface IProps {
player: IPlayer;
}
export function HashUpgradePopup(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(() => setRerender((old) => !old), 1000);
return () => clearInterval(id);
}, []);
const hashManager = props.player.hashManager;
if (!(hashManager instanceof HashManager)) {
throw new Error(`Player does not have a HashManager)`);
}
const upgradeElems = Object.keys(HashUpgrades).map((upgName) => {
const upg = HashUpgrades[upgName];
return (
<HacknetUpgradeElem
player={props.player}
upg={upg}
hashManager={hashManager}
key={upg.name}
rerender={rerender}
/>
);
});
return (
<div>
<p>Spend your hashes on a variety of different upgrades</p>
<p>Hashes: {Hashes(props.player.hashManager.hashes)}</p>
{upgradeElems}
</div>
);
}
@@ -5,9 +5,16 @@
*/
import React from "react";
import { PurchaseMultipliers } from "./Root";
import { PurchaseMultipliers } from "../data/Constants";
function MultiplierButton(props) {
interface IMultiplierProps {
className: string;
key: string;
onClick: () => void;
text: string;
}
function MultiplierButton(props: IMultiplierProps): React.ReactElement {
return (
<button className={props.className} onClick={props.onClick}>
{props.text}
@@ -15,7 +22,12 @@ function MultiplierButton(props) {
);
}
export function MultiplierButtons(props) {
interface IProps {
purchaseMultiplier: number | string;
onClicks: (() => void)[];
}
export function MultiplierButtons(props: IProps): React.ReactElement {
if (props.purchaseMultiplier == null) {
throw new Error(`MultiplierButtons constructed without required props`);
}
@@ -7,14 +7,19 @@
import React from "react";
import { hasHacknetServers } from "../HacknetHelpers";
import { Player } from "../../Player";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { HashRate } from "../../ui/React/HashRate";
import { Hashes } from "../../ui/React/Hashes";
export function PlayerInfo(props) {
const hasServers = hasHacknetServers();
interface IProps {
totalProduction: number;
player: IPlayer;
}
export function PlayerInfo(props: IProps): React.ReactElement {
const hasServers = hasHacknetServers(props.player);
let prod;
if (hasServers) {
@@ -26,13 +31,13 @@ export function PlayerInfo(props) {
return (
<p id={"hacknet-nodes-money"}>
<span>Money: </span>
<Money money={Player.money.toNumber()} />
<Money money={props.player.money.toNumber()} />
<br />
{hasServers && (
<>
<span>
Hashes: {Hashes(Player.hashManager.hashes)} / {Hashes(Player.hashManager.capacity)}
Hashes: {Hashes(props.player.hashManager.hashes)} / {Hashes(props.player.hashManager.capacity)}
</span>
<br />
</>
@@ -7,17 +7,19 @@ import { hasHacknetServers, hasMaxNumberHacknetServers } from "../HacknetHelpers
import { Player } from "../../Player";
import { Money } from "../../ui/React/Money";
export function PurchaseButton(props) {
if (props.multiplier == null || props.onClick == null) {
throw new Error(`PurchaseButton constructed without required props`);
}
interface IProps {
multiplier: number | string;
onClick: () => void;
cost: number;
}
export function PurchaseButton(props: IProps): React.ReactElement {
const cost = props.cost;
let className = Player.canAfford(cost) ? "std-button" : "std-button-disabled";
let text;
let style = null;
if (hasHacknetServers()) {
if (hasMaxNumberHacknetServers()) {
let style = {};
if (hasHacknetServers(Player)) {
if (hasMaxNumberHacknetServers(Player)) {
className = "std-button-disabled";
text = <>Hacknet Server limit reached</>;
style = { color: "red" };
-162
View File
@@ -1,162 +0,0 @@
/**
* Root React Component for the Hacknet Node UI
*/
import React from "react";
import { GeneralInfo } from "./GeneralInfo";
import { HacknetNode } from "./HacknetNode";
import { HacknetServer } from "./HacknetServer";
import { HashUpgradePopup } from "./HashUpgradePopup";
import { MultiplierButtons } from "./MultiplierButtons";
import { PlayerInfo } from "./PlayerInfo";
import { PurchaseButton } from "./PurchaseButton";
import {
getCostOfNextHacknetNode,
getCostOfNextHacknetServer,
hasHacknetServers,
purchaseHacknet,
} from "../HacknetHelpers";
import { Player } from "../../Player";
import { AllServers } from "../../Server/AllServers";
import { createPopup } from "../../ui/React/createPopup";
export const PurchaseMultipliers = Object.freeze({
x1: 1,
x5: 5,
x10: 10,
MAX: "MAX",
});
export class HacknetRoot extends React.Component {
constructor(props) {
super(props);
this.state = {
purchaseMultiplier: PurchaseMultipliers.x1,
totalProduction: 0, // Total production ($ / s) of Hacknet Nodes
};
this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this);
this.handlePurchaseButtonClick = this.handlePurchaseButtonClick.bind(this);
this.recalculateTotalProduction = this.recalculateTotalProduction.bind(this);
}
componentDidMount() {
this.recalculateTotalProduction();
}
createHashUpgradesPopup() {
const id = "hacknet-server-hash-upgrades-popup";
createPopup(id, HashUpgradePopup, {
popupId: id,
rerender: this.createHashUpgradesPopup,
});
}
handlePurchaseButtonClick() {
if (purchaseHacknet() >= 0) {
this.recalculateTotalProduction();
}
}
recalculateTotalProduction() {
let total = 0;
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
if (hasHacknetServers()) {
const hserver = AllServers[Player.hacknetNodes[i]];
if (hserver) {
total += hserver.hashRate;
} else {
console.warn(`Could not find Hacknet Server object in AllServers map (i=${i})`);
}
} else {
total += Player.hacknetNodes[i].moneyGainRatePerSecond;
}
}
this.setState({
totalProduction: total,
});
}
setPurchaseMultiplier(mult) {
this.setState({
purchaseMultiplier: mult,
});
}
render() {
// Cost to purchase a new Hacknet Node
let purchaseCost;
if (hasHacknetServers()) {
purchaseCost = getCostOfNextHacknetServer();
} else {
purchaseCost = getCostOfNextHacknetNode();
}
// onClick event handlers for purchase multiplier buttons
const purchaseMultiplierOnClicks = [
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1),
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x5),
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x10),
this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.MAX),
];
// HacknetNode components
const nodes = Player.hacknetNodes.map((node) => {
if (hasHacknetServers()) {
const hserver = AllServers[node];
if (hserver == null) {
throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`);
}
return (
<HacknetServer
key={hserver.hostname}
node={hserver}
purchaseMultiplier={this.state.purchaseMultiplier}
recalculate={this.recalculateTotalProduction}
/>
);
} else {
return (
<HacknetNode
key={node.name}
node={node}
purchaseMultiplier={this.state.purchaseMultiplier}
recalculate={this.recalculateTotalProduction}
/>
);
}
});
return (
<div>
<h1>Hacknet {hasHacknetServers() ? "Servers" : "Nodes"}</h1>
<GeneralInfo />
<PurchaseButton
cost={purchaseCost}
multiplier={this.state.purchaseMultiplier}
onClick={this.handlePurchaseButtonClick}
/>
<br />
<div id={"hacknet-nodes-money-multipliers-div"}>
<PlayerInfo totalProduction={this.state.totalProduction} />
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={this.state.purchaseMultiplier} />
</div>
{hasHacknetServers() && (
<button className={"std-button"} onClick={this.createHashUpgradesPopup} style={{ display: "block" }}>
{"Spend Hashes on Upgrades"}
</button>
)}
<ul id={"hacknet-nodes-list"}>{nodes}</ul>
</div>
);
}
}