mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-23 09:42:53 +02:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bb0bc5728 | |||
| 80d566f051 |
@@ -0,0 +1,12 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { Money } from "../../../ui/React/Money";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
|
||||||
|
const selectHacknetExpenses = (p: PlayerObject) => -p.moneySourceA.hacknet_expenses || 0;
|
||||||
|
|
||||||
|
export function HacknetExpenses(): React.ReactElement {
|
||||||
|
const spent = usePlayerSelector(selectHacknetExpenses);
|
||||||
|
return <Money key="money" money={spent} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { Money } from "../../../ui/React/Money";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
|
||||||
|
const selectHacknetMoney = (p: PlayerObject) => p.moneySourceA.hacknet;
|
||||||
|
|
||||||
|
export function HacknetProduced(): React.ReactElement {
|
||||||
|
const produced = usePlayerSelector(selectHacknetMoney);
|
||||||
|
return <Money key="money" money={produced} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
import { safeGetHacknetServer } from "../utils";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HacknetServerLevel({ index }: IProps): React.ReactElement {
|
||||||
|
const level = usePlayerSelector(
|
||||||
|
useCallback((p: PlayerObject) => safeGetHacknetServer(p, index)?.level ?? "???", [index]),
|
||||||
|
);
|
||||||
|
return <>{level}</>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { hasHacknetServers } from "../../../Hacknet/HacknetHelpers";
|
||||||
|
import { HacknetNode } from "../../../Hacknet/HacknetNode";
|
||||||
|
import { HacknetServer } from "../../../Hacknet/HacknetServer";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { GetServer } from "../../../Server/AllServers";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
import { HashRate } from "../../../ui/React/HashRate";
|
||||||
|
|
||||||
|
const selectTotalProduction = (p: PlayerObject) => {
|
||||||
|
let totalProduction = 0;
|
||||||
|
for (let i = 0; i < p.hacknetNodes.length; ++i) {
|
||||||
|
const node = p.hacknetNodes[i];
|
||||||
|
if (hasHacknetServers()) {
|
||||||
|
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
|
||||||
|
const hserver = GetServer(node);
|
||||||
|
if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalProduction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function HashTotalProduction(): React.ReactElement {
|
||||||
|
const prod = usePlayerSelector(selectTotalProduction);
|
||||||
|
return <HashRate key="hashRate" hashes={prod} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Button, ButtonProps } from "@mui/material";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
|
||||||
|
type MoneyButtonProps = {
|
||||||
|
cost: number;
|
||||||
|
} & ButtonProps;
|
||||||
|
|
||||||
|
export function MoneyButton(props: MoneyButtonProps): React.ReactElement {
|
||||||
|
const canAfford = usePlayerSelector(React.useCallback((p: PlayerObject) => p.canAfford(props.cost), [props.cost]));
|
||||||
|
return (
|
||||||
|
<Button disabled={!canAfford || props.disabled} {...props}>
|
||||||
|
{props.children}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { formatMoney } from "../../../ui/formatNumber";
|
||||||
|
import type { Theme } from "@mui/material/styles";
|
||||||
|
import { makeStyles } from "tss-react/mui";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
|
||||||
|
const useStyles = makeStyles()((theme: Theme) => ({
|
||||||
|
unbuyable: {
|
||||||
|
color: theme.palette.action.disabled,
|
||||||
|
},
|
||||||
|
money: {
|
||||||
|
color: theme.colors.money,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
cost: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MoneyCost({ cost }: IProps): React.ReactElement {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const canAfford = usePlayerSelector(React.useCallback((p: PlayerObject) => p.canAfford(cost), [cost]));
|
||||||
|
return <span className={canAfford ? classes.money : classes.unbuyable}>{formatMoney(cost)}</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { Hashes } from "../../../ui/React/Hashes";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
|
||||||
|
const selectHashCapacity = (p: PlayerObject) => p.hashManager.capacity;
|
||||||
|
|
||||||
|
export function PlayerHashCapacity(): React.ReactElement {
|
||||||
|
const capacity = usePlayerSelector(selectHashCapacity);
|
||||||
|
return <Hashes hashes={capacity} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { Hashes } from "../../../ui/React/Hashes";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
|
||||||
|
const selectHashes = (p: PlayerObject) => p.hashManager.hashes;
|
||||||
|
|
||||||
|
export function PlayerHashes(): React.ReactElement {
|
||||||
|
const hashes = usePlayerSelector(selectHashes);
|
||||||
|
return <Hashes hashes={hashes} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import React, { useCallback } from "react";
|
||||||
|
import { Button, Tooltip } from "@mui/material";
|
||||||
|
import { HacknetServerConstants } from "../../data/Constants";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
import { getMaxNumberLevelUpgrades, purchaseLevelUpgrade } from "../../HacknetHelpers";
|
||||||
|
import { Money } from "../../../ui/React/Money";
|
||||||
|
import { UpgradeHacknetServerLevelTooltip } from "./UpgradeHacknetServerLevelTooltip";
|
||||||
|
import { HacknetServer } from "../../HacknetServer";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
node: HacknetServer;
|
||||||
|
purchaseMult: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectHacknetNodeLevelCost = (p: PlayerObject) => p.mults.hacknet_node_level_cost;
|
||||||
|
const selectMoney = (p: PlayerObject) => p.money;
|
||||||
|
|
||||||
|
export function UpgradeHacknetServerLevelButton({ purchaseMult, node }: IProps): React.ReactElement {
|
||||||
|
const level = usePlayerSelector(useCallback((): number => node.level, [node]));
|
||||||
|
const hacknet_node_level_cost = usePlayerSelector(selectHacknetNodeLevelCost);
|
||||||
|
usePlayerSelector(selectMoney); // we have to hook on money change for the max level algorithm
|
||||||
|
|
||||||
|
function upgradeLevelOnClick(): void {
|
||||||
|
let numUpgrades = purchaseMult;
|
||||||
|
if (purchaseMult === "MAX") {
|
||||||
|
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
|
||||||
|
}
|
||||||
|
purchaseLevelUpgrade(node, numUpgrades as number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level >= HacknetServerConstants.MaxLevel) {
|
||||||
|
return <Button disabled>MAX LEVEL</Button>;
|
||||||
|
} else {
|
||||||
|
let multiplier = 0;
|
||||||
|
if (purchaseMult === "MAX") {
|
||||||
|
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
|
||||||
|
} else {
|
||||||
|
const levelsToMax = HacknetServerConstants.MaxLevel - level;
|
||||||
|
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
||||||
|
}
|
||||||
|
|
||||||
|
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, hacknet_node_level_cost) ?? 0;
|
||||||
|
return (
|
||||||
|
<Tooltip title={<UpgradeHacknetServerLevelTooltip purchaseMult={purchaseMult} node={node} />}>
|
||||||
|
<Button onClick={upgradeLevelOnClick}>
|
||||||
|
+{multiplier} -
|
||||||
|
<Money money={upgradeLevelCost} forPurchase={true} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Typography } from "@mui/material";
|
||||||
|
import React, { useCallback } from "react";
|
||||||
|
import { calculateHashGainRate } from "../../formulas/HacknetServers";
|
||||||
|
import { PlayerObject } from "../../../PersonObjects/Player/PlayerObject";
|
||||||
|
import { HashRate } from "../../../ui/React/HashRate";
|
||||||
|
import { arrayShallowEquals, usePlayerSelector } from "../../../utils/PlayerExternalStore";
|
||||||
|
import { getMaxNumberLevelUpgrades } from "../../HacknetHelpers";
|
||||||
|
import { HacknetServerConstants } from "../../data/Constants";
|
||||||
|
import { HacknetServer } from "../../HacknetServer";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
node: HacknetServer;
|
||||||
|
purchaseMult: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectHacknetNodeMoney = (p: PlayerObject) => p.mults.hacknet_node_money;
|
||||||
|
|
||||||
|
export function UpgradeHacknetServerLevelTooltip({ purchaseMult, node }: IProps): React.ReactElement {
|
||||||
|
const [level, ramUsed, maxRam, cores] = usePlayerSelector(
|
||||||
|
useCallback((): [number, number, number, number] => [node.level, node.ramUsed, node.maxRam, node.cores], [node]),
|
||||||
|
arrayShallowEquals,
|
||||||
|
);
|
||||||
|
|
||||||
|
const hacknetNodeMoney = usePlayerSelector(selectHacknetNodeMoney);
|
||||||
|
|
||||||
|
let multiplier = 0;
|
||||||
|
if (purchaseMult === "MAX") {
|
||||||
|
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
|
||||||
|
} else {
|
||||||
|
const levelsToMax = HacknetServerConstants.MaxLevel - level;
|
||||||
|
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
||||||
|
}
|
||||||
|
|
||||||
|
const base_increase =
|
||||||
|
calculateHashGainRate(level + multiplier, 0, maxRam, cores, hacknetNodeMoney) -
|
||||||
|
calculateHashGainRate(level, 0, maxRam, cores, hacknetNodeMoney);
|
||||||
|
const modded_increase = (base_increase * (maxRam - ramUsed)) / maxRam;
|
||||||
|
return (
|
||||||
|
<Typography>
|
||||||
|
+<HashRate hashes={modded_increase} /> (effective increase, taking current RAM usage into account)
|
||||||
|
<br />
|
||||||
|
<span style={{ opacity: 0.5 }}>
|
||||||
|
+<HashRate hashes={base_increase} />
|
||||||
|
</span>{" "}
|
||||||
|
(base increase, attained when no script is running)
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -35,13 +35,11 @@ import { calculateMoneyGainRate } from "../formulas/HacknetNodes";
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
node: HacknetNode;
|
node: HacknetNode;
|
||||||
purchaseMultiplier: number | "MAX";
|
purchaseMultiplier: number | "MAX";
|
||||||
rerender: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HacknetNodeElem(props: IProps): React.ReactElement {
|
export function HacknetNodeElem(props: IProps): React.ReactElement {
|
||||||
const node = props.node;
|
const node = props.node;
|
||||||
const purchaseMult = props.purchaseMultiplier;
|
const purchaseMult = props.purchaseMultiplier;
|
||||||
const rerender = props.rerender;
|
|
||||||
|
|
||||||
// Upgrade Level Button
|
// Upgrade Level Button
|
||||||
let upgradeLevelButton;
|
let upgradeLevelButton;
|
||||||
@@ -79,7 +77,6 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
|
|||||||
const numUpgrades =
|
const numUpgrades =
|
||||||
purchaseMult === "MAX" ? getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel) : purchaseMult;
|
purchaseMult === "MAX" ? getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel) : purchaseMult;
|
||||||
purchaseLevelUpgrade(node, numUpgrades);
|
purchaseLevelUpgrade(node, numUpgrades);
|
||||||
rerender();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let upgradeRAMButton;
|
let upgradeRAMButton;
|
||||||
@@ -121,14 +118,12 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
|
|||||||
const numUpgrades =
|
const numUpgrades =
|
||||||
purchaseMult === "MAX" ? getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam) : purchaseMult;
|
purchaseMult === "MAX" ? getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam) : purchaseMult;
|
||||||
purchaseRamUpgrade(node, numUpgrades);
|
purchaseRamUpgrade(node, numUpgrades);
|
||||||
rerender();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function upgradeCoresOnClick(): void {
|
function upgradeCoresOnClick(): void {
|
||||||
const numUpgrades =
|
const numUpgrades =
|
||||||
purchaseMult === "MAX" ? getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores) : purchaseMult;
|
purchaseMult === "MAX" ? getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores) : purchaseMult;
|
||||||
purchaseCoreUpgrade(node, numUpgrades);
|
purchaseCoreUpgrade(node, numUpgrades);
|
||||||
rerender();
|
|
||||||
}
|
}
|
||||||
let upgradeCoresButton;
|
let upgradeCoresButton;
|
||||||
if (node.cores >= HacknetNodeConstants.MaxCores) {
|
if (node.cores >= HacknetNodeConstants.MaxCores) {
|
||||||
|
|||||||
@@ -25,16 +25,19 @@ import Typography from "@mui/material/Typography";
|
|||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import { useCycleRerender } from "../../ui/React/hooks";
|
import { usePlayerSelector } from "../../utils/PlayerExternalStore";
|
||||||
|
import { PlayerObject } from "src/PersonObjects/Player/PlayerObject";
|
||||||
|
|
||||||
|
const selectHacknetNodecount = (p: PlayerObject) => p.hacknetNodes.length;
|
||||||
|
|
||||||
/** Root React Component for the Hacknet Node UI */
|
/** Root React Component for the Hacknet Node UI */
|
||||||
export function HacknetRoot(): React.ReactElement {
|
export function HacknetRoot(): React.ReactElement {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const rerender = useCycleRerender();
|
|
||||||
const [purchaseMultiplier, setPurchaseMultiplier] = useState<number | "MAX">(PurchaseMultipliers.x1);
|
const [purchaseMultiplier, setPurchaseMultiplier] = useState<number | "MAX">(PurchaseMultipliers.x1);
|
||||||
|
const hacknetNodeCount = usePlayerSelector(selectHacknetNodecount);
|
||||||
|
|
||||||
let totalProduction = 0;
|
let totalProduction = 0;
|
||||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
for (let i = 0; i < hacknetNodeCount; ++i) {
|
||||||
const node = Player.hacknetNodes[i];
|
const node = Player.hacknetNodes[i];
|
||||||
if (hasHacknetServers()) {
|
if (hasHacknetServers()) {
|
||||||
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
|
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
|
||||||
@@ -53,7 +56,6 @@ export function HacknetRoot(): React.ReactElement {
|
|||||||
|
|
||||||
function handlePurchaseButtonClick(): void {
|
function handlePurchaseButtonClick(): void {
|
||||||
purchaseHacknet();
|
purchaseHacknet();
|
||||||
rerender();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cost to purchase a new Hacknet Node
|
// Cost to purchase a new Hacknet Node
|
||||||
@@ -73,7 +75,7 @@ export function HacknetRoot(): React.ReactElement {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// HacknetNode components
|
// HacknetNode components
|
||||||
const nodes = Player.hacknetNodes.map((node: string | HacknetNode) => {
|
const nodes = Player.hacknetNodes.map((node: string | HacknetNode, index: number) => {
|
||||||
if (hasHacknetServers()) {
|
if (hasHacknetServers()) {
|
||||||
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
|
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
|
||||||
const hserver = GetServer(node);
|
const hserver = GetServer(node);
|
||||||
@@ -84,16 +86,14 @@ export function HacknetRoot(): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<HacknetServerElem
|
<HacknetServerElem
|
||||||
key={hserver.hostname}
|
key={hserver.hostname}
|
||||||
|
index={index}
|
||||||
node={hserver}
|
node={hserver}
|
||||||
purchaseMultiplier={purchaseMultiplier}
|
purchaseMultiplier={purchaseMultiplier}
|
||||||
rerender={rerender}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (typeof node === "string") throw new Error("node was ip string"); // should never happen
|
if (typeof node === "string") throw new Error("node was ip string"); // should never happen
|
||||||
return (
|
return <HacknetNodeElem key={node.name} node={node} purchaseMultiplier={purchaseMultiplier} />;
|
||||||
<HacknetNodeElem key={node.name} node={node} purchaseMultiplier={purchaseMultiplier} rerender={rerender} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ import React from "react";
|
|||||||
|
|
||||||
import { HacknetServerConstants } from "../data/Constants";
|
import { HacknetServerConstants } from "../data/Constants";
|
||||||
import {
|
import {
|
||||||
getMaxNumberLevelUpgrades,
|
|
||||||
getMaxNumberRamUpgrades,
|
getMaxNumberRamUpgrades,
|
||||||
getMaxNumberCoreUpgrades,
|
getMaxNumberCoreUpgrades,
|
||||||
getMaxNumberCacheUpgrades,
|
getMaxNumberCacheUpgrades,
|
||||||
purchaseLevelUpgrade,
|
|
||||||
purchaseRamUpgrade,
|
purchaseRamUpgrade,
|
||||||
purchaseCoreUpgrade,
|
purchaseCoreUpgrade,
|
||||||
purchaseCacheUpgrade,
|
purchaseCacheUpgrade,
|
||||||
@@ -34,65 +32,19 @@ import TableRow from "@mui/material/TableRow";
|
|||||||
import { formatRam } from "../../ui/formatNumber";
|
import { formatRam } from "../../ui/formatNumber";
|
||||||
import { calculateHashGainRate } from "../formulas/HacknetServers";
|
import { calculateHashGainRate } from "../formulas/HacknetServers";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import { HacknetServerLevel } from "./Components/HacknetServerLevel";
|
||||||
|
import { UpgradeHacknetServerLevelButton } from "./Components/UpgradeHacknetServerLevelButton";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
index: number;
|
||||||
node: HacknetServer;
|
node: HacknetServer;
|
||||||
purchaseMultiplier: number | string;
|
purchaseMultiplier: number | string;
|
||||||
rerender: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HacknetServerElem(props: IProps): React.ReactElement {
|
export function HacknetServerElem(props: IProps): React.ReactElement {
|
||||||
const node = props.node;
|
const node = props.node;
|
||||||
const purchaseMult = props.purchaseMultiplier;
|
const purchaseMult = props.purchaseMultiplier;
|
||||||
const rerender = props.rerender;
|
const index = props.index;
|
||||||
|
|
||||||
// Upgrade Level Button
|
|
||||||
let upgradeLevelButton;
|
|
||||||
if (node.level >= HacknetServerConstants.MaxLevel) {
|
|
||||||
upgradeLevelButton = <Button disabled>MAX LEVEL</Button>;
|
|
||||||
} else {
|
|
||||||
let multiplier = 0;
|
|
||||||
if (purchaseMult === "MAX") {
|
|
||||||
multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
|
|
||||||
} else {
|
|
||||||
const levelsToMax = HacknetServerConstants.MaxLevel - node.level;
|
|
||||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
|
||||||
}
|
|
||||||
|
|
||||||
const base_increase =
|
|
||||||
calculateHashGainRate(node.level + multiplier, 0, node.maxRam, node.cores, Player.mults.hacknet_node_money) -
|
|
||||||
calculateHashGainRate(node.level, 0, node.maxRam, node.cores, Player.mults.hacknet_node_money);
|
|
||||||
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
|
|
||||||
|
|
||||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.mults.hacknet_node_level_cost);
|
|
||||||
upgradeLevelButton = (
|
|
||||||
<Tooltip
|
|
||||||
title={
|
|
||||||
<Typography>
|
|
||||||
+<HashRate hashes={modded_increase} /> (effective increase, taking current RAM usage into account)
|
|
||||||
<br />
|
|
||||||
<span style={{ opacity: 0.5 }}>
|
|
||||||
+<HashRate hashes={base_increase} />
|
|
||||||
</span>{" "}
|
|
||||||
(base increase, attained when no script is running)
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button onClick={upgradeLevelOnClick}>
|
|
||||||
+{multiplier} -
|
|
||||||
<Money money={upgradeLevelCost} forPurchase={true} />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
function upgradeLevelOnClick(): void {
|
|
||||||
let numUpgrades = purchaseMult;
|
|
||||||
if (purchaseMult === "MAX") {
|
|
||||||
numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
|
|
||||||
}
|
|
||||||
purchaseLevelUpgrade(node, numUpgrades as number);
|
|
||||||
rerender();
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgradeRamOnClick(): void {
|
function upgradeRamOnClick(): void {
|
||||||
let numUpgrades = purchaseMult;
|
let numUpgrades = purchaseMult;
|
||||||
@@ -100,7 +52,6 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
|
numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
|
||||||
}
|
}
|
||||||
purchaseRamUpgrade(node, numUpgrades as number);
|
purchaseRamUpgrade(node, numUpgrades as number);
|
||||||
rerender();
|
|
||||||
}
|
}
|
||||||
// Upgrade RAM Button
|
// Upgrade RAM Button
|
||||||
let upgradeRamButton;
|
let upgradeRamButton;
|
||||||
@@ -161,7 +112,6 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
|
numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
|
||||||
}
|
}
|
||||||
purchaseCoreUpgrade(node, numUpgrades as number);
|
purchaseCoreUpgrade(node, numUpgrades as number);
|
||||||
rerender();
|
|
||||||
}
|
}
|
||||||
// Upgrade Cores Button
|
// Upgrade Cores Button
|
||||||
let upgradeCoresButton;
|
let upgradeCoresButton;
|
||||||
@@ -239,7 +189,6 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
|
numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
|
||||||
}
|
}
|
||||||
purchaseCacheUpgrade(node, numUpgrades as number);
|
purchaseCacheUpgrade(node, numUpgrades as number);
|
||||||
rerender();
|
|
||||||
updateHashManagerCapacity();
|
updateHashManagerCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,9 +248,13 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
<Typography>Level:</Typography>
|
<Typography>Level:</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography>{node.level}</Typography>
|
<Typography>
|
||||||
|
<HacknetServerLevel index={index} />
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<UpgradeHacknetServerLevelButton purchaseMult={purchaseMult} node={node} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{upgradeLevelButton}</TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { hasHacknetServers } from "../HacknetHelpers";
|
import { hasHacknetServers } from "../HacknetHelpers";
|
||||||
import { Player } from "@player";
|
|
||||||
import { Money } from "../../ui/React/Money";
|
|
||||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||||
import { HashRate } from "../../ui/React/HashRate";
|
|
||||||
import { Hashes } from "../../ui/React/Hashes";
|
|
||||||
import { Paper, Typography } from "@mui/material";
|
import { Paper, Typography } from "@mui/material";
|
||||||
import { StatsTable } from "../../ui/React/StatsTable";
|
import { StatsTable } from "../../ui/React/StatsTable";
|
||||||
import { Tooltip } from "@mui/material";
|
import { Tooltip } from "@mui/material";
|
||||||
|
import { HashTotalProduction } from "./Components/HashTotalProduction";
|
||||||
|
import { HacknetExpenses } from "./Components/HacknetExpenses";
|
||||||
|
import { HacknetProduced } from "./Components/HacknetProduced";
|
||||||
|
import { PlayerHashes } from "./Components/PlayerHashes";
|
||||||
|
import { PlayerHashCapacity } from "./Components/PlayerHashCapacity";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
totalProduction: number;
|
totalProduction: number;
|
||||||
@@ -24,13 +25,13 @@ export function PlayerInfo(props: IProps): React.ReactElement {
|
|||||||
const hasServers = hasHacknetServers();
|
const hasServers = hasHacknetServers();
|
||||||
|
|
||||||
const rows: React.ReactNode[][] = [];
|
const rows: React.ReactNode[][] = [];
|
||||||
rows.push(["Money Spent:", <Money key="money" money={-Player.moneySourceA.hacknet_expenses || 0} />]);
|
rows.push(["Money Spent:", <HacknetExpenses key="expenses" />]);
|
||||||
rows.push(["Money Produced:", <Money key="money" money={Player.moneySourceA.hacknet} />]);
|
rows.push(["Money Produced:", <HacknetProduced key="money" />]);
|
||||||
if (hasServers) {
|
if (hasServers) {
|
||||||
rows.push([
|
rows.push([
|
||||||
"Hashes:",
|
"Hashes:",
|
||||||
<span key={"hashes"}>
|
<span key={"hashes"}>
|
||||||
<Hashes hashes={Player.hashManager.hashes} /> / <Hashes hashes={Player.hashManager.capacity} />
|
<PlayerHashes /> / <PlayerHashCapacity />
|
||||||
</span>,
|
</span>,
|
||||||
]);
|
]);
|
||||||
rows.push([
|
rows.push([
|
||||||
@@ -44,7 +45,7 @@ export function PlayerInfo(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<HashRate key="hashRate" hashes={props.totalProduction} />
|
<HashTotalProduction />
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>,
|
</Tooltip>,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { hasHacknetServers, hasMaxNumberHacknetServers } from "../HacknetHelpers";
|
import { hasHacknetServers, hasMaxNumberHacknetServers } from "../HacknetHelpers";
|
||||||
import { Player } from "@player";
|
import { MoneyCost } from "./Components/MoneyCost";
|
||||||
import { Money } from "../../ui/React/Money";
|
import { MoneyButton } from "./Components/MoneyButton";
|
||||||
|
|
||||||
import Button from "@mui/material/Button";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
multiplier: number | string;
|
multiplier: number | string;
|
||||||
@@ -23,7 +21,7 @@ export function PurchaseButton(props: IProps): React.ReactElement {
|
|||||||
text = (
|
text = (
|
||||||
<>
|
<>
|
||||||
Purchase Hacknet Server -
|
Purchase Hacknet Server -
|
||||||
<Money money={cost} forPurchase={true} />
|
<MoneyCost cost={cost} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -31,14 +29,14 @@ export function PurchaseButton(props: IProps): React.ReactElement {
|
|||||||
text = (
|
text = (
|
||||||
<>
|
<>
|
||||||
Purchase Hacknet Node -
|
Purchase Hacknet Node -
|
||||||
<Money money={cost} forPurchase={true} />
|
<MoneyCost cost={cost} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button disabled={!Player.canAfford(cost)} onClick={props.onClick}>
|
<MoneyButton cost={cost} onClick={props.onClick}>
|
||||||
{text}
|
{text}
|
||||||
</Button>
|
</MoneyButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { PlayerObject } from "src/PersonObjects/Player/PlayerObject";
|
||||||
|
import { HacknetServer } from "../HacknetServer";
|
||||||
|
import { HacknetNode } from "../HacknetNode";
|
||||||
|
import { GetServer } from "../../Server/AllServers";
|
||||||
|
|
||||||
|
export function safeGetHacknetServer(p: PlayerObject, index: number): HacknetServer | undefined {
|
||||||
|
const node = p.hacknetNodes[index];
|
||||||
|
if (node instanceof HacknetNode) return undefined;
|
||||||
|
const hserver = GetServer(node);
|
||||||
|
if (hserver == null) return undefined;
|
||||||
|
if (!(hserver instanceof HacknetServer)) return undefined;
|
||||||
|
return hserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function safeGetHacknetNode(p: PlayerObject, index: number): HacknetNode | undefined {
|
||||||
|
const node = p.hacknetNodes[index];
|
||||||
|
if (!(node instanceof HacknetNode)) return undefined;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ import { EventEmitter } from "./utils/EventEmitter";
|
|||||||
import { Companies } from "./Company/Companies";
|
import { Companies } from "./Company/Companies";
|
||||||
import { resetGoPromises } from "./Go/boardAnalysis/goAI";
|
import { resetGoPromises } from "./Go/boardAnalysis/goAI";
|
||||||
import { getRecordEntries } from "./Types/Record";
|
import { getRecordEntries } from "./Types/Record";
|
||||||
|
import { runSelectors } from "./utils/PlayerExternalStore";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// This property is only available in the dev build
|
// This property is only available in the dev build
|
||||||
@@ -421,6 +422,7 @@ const Engine = {
|
|||||||
if (GameCycleEvents.hasSubscribers()) {
|
if (GameCycleEvents.hasSubscribers()) {
|
||||||
ReactDOM.unstable_batchedUpdates(() => {
|
ReactDOM.unstable_batchedUpdates(() => {
|
||||||
GameCycleEvents.emit();
|
GameCycleEvents.emit();
|
||||||
|
runSelectors();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Player } from "@player";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { PlayerObject } from "src/PersonObjects/Player/PlayerObject";
|
||||||
|
|
||||||
|
const trieq = <T>(a: T, b: T): boolean => a === b;
|
||||||
|
|
||||||
|
export const arrayShallowEquals = <T>(a: T[], b: T[]): boolean => {
|
||||||
|
if (a.length != b.length) return false;
|
||||||
|
return a.every((v, i) => b[i] === v);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const runSelectors = (): void => {
|
||||||
|
for (const sub of subscriptions) sub(Player);
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscriptions: ((g: PlayerObject) => void)[] = [];
|
||||||
|
|
||||||
|
const subscribe = (check: (g: PlayerObject) => void): void => {
|
||||||
|
subscriptions.push(check);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsubscribe = (check: (g: PlayerObject) => void) => {
|
||||||
|
return (): void => {
|
||||||
|
let found = -1;
|
||||||
|
for (let i = 0; i < subscriptions.length; i++) {
|
||||||
|
if (subscriptions[i] !== check) continue;
|
||||||
|
found = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (found === -1) return;
|
||||||
|
subscriptions[found] = subscriptions[subscriptions.length - 1];
|
||||||
|
subscriptions.length -= 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const incr = (n: number): number => n + 1;
|
||||||
|
|
||||||
|
export const usePlayerSelector = <T>(f: (g: PlayerObject) => T, eq: (a: T, b: T) => boolean = trieq): T => {
|
||||||
|
const [, setValue] = useState(0);
|
||||||
|
const ref = useRef(f(Player));
|
||||||
|
useEffect(() => {
|
||||||
|
const check = (g: PlayerObject): void => {
|
||||||
|
const next = f(g);
|
||||||
|
if (eq(ref.current, next)) return;
|
||||||
|
ref.current = next;
|
||||||
|
setValue(incr);
|
||||||
|
};
|
||||||
|
subscribe(check);
|
||||||
|
return unsubscribe(check);
|
||||||
|
}, [f, eq]);
|
||||||
|
return ref.current;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user