mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-26 11:10:58 +02:00
Merge pull request #3545 from nickofolas/improvement/purchase-augs-ui
UI: Redesign purchasable Augmentations
This commit is contained in:
@@ -251,7 +251,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1.15e8,
|
||||
repCost: 2.75e4,
|
||||
info: "The latest version of the 'Augmented Targeting' implant adds the ability to lock-on and track threats.",
|
||||
prereqs: [AugmentationNames.Targeting2],
|
||||
prereqs: [AugmentationNames.Targeting2, AugmentationNames.Targeting1],
|
||||
dexterity_mult: 1.3,
|
||||
factions: [
|
||||
FactionNames.TheDarkArmy,
|
||||
@@ -348,7 +348,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
info:
|
||||
"The latest version of the 'Combat Rib' augmentation releases advanced anabolic steroids that " +
|
||||
"improve muscle mass and physical performance while being safe and free of side effects.",
|
||||
prereqs: [AugmentationNames.CombatRib2],
|
||||
prereqs: [AugmentationNames.CombatRib2, AugmentationNames.CombatRib1],
|
||||
strength_mult: 1.18,
|
||||
defense_mult: 1.18,
|
||||
factions: [
|
||||
@@ -682,7 +682,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"This upgraded firmware allows the Embedded Netburner Module to control information on " +
|
||||
"a network by re-routing traffic, spoofing IP addresses, and altering the data inside network " +
|
||||
"packets.",
|
||||
prereqs: [AugmentationNames.ENMCore],
|
||||
prereqs: [AugmentationNames.ENMCore, AugmentationNames.ENM],
|
||||
hacking_speed_mult: 1.05,
|
||||
hacking_money_mult: 1.3,
|
||||
hacking_chance_mult: 1.05,
|
||||
@@ -707,7 +707,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"The Core V3 library is an implant that upgrades the firmware of the Embedded Netburner Module. " +
|
||||
"This upgraded firmware allows the Embedded Netburner Module to seamlessly inject code into " +
|
||||
"any device on a network.",
|
||||
prereqs: [AugmentationNames.ENMCoreV2],
|
||||
prereqs: [AugmentationNames.ENMCoreV2, AugmentationNames.ENMCore, AugmentationNames.ENM],
|
||||
hacking_speed_mult: 1.05,
|
||||
hacking_money_mult: 1.4,
|
||||
hacking_chance_mult: 1.1,
|
||||
@@ -835,7 +835,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"are a set of specialized microprocessors that are attached to " +
|
||||
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
|
||||
"so that the brain doesn't have to.",
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG2],
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG2, AugmentationNames.CranialSignalProcessorsG1],
|
||||
hacking_speed_mult: 1.02,
|
||||
hacking_money_mult: 1.15,
|
||||
hacking_mult: 1.09,
|
||||
@@ -850,7 +850,11 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"are a set of specialized microprocessors that are attached to " +
|
||||
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
|
||||
"so that the brain doesn't have to.",
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG3],
|
||||
prereqs: [
|
||||
AugmentationNames.CranialSignalProcessorsG3,
|
||||
AugmentationNames.CranialSignalProcessorsG2,
|
||||
AugmentationNames.CranialSignalProcessorsG1,
|
||||
],
|
||||
hacking_speed_mult: 1.02,
|
||||
hacking_money_mult: 1.2,
|
||||
hacking_grow_mult: 1.25,
|
||||
@@ -865,7 +869,12 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"are a set of specialized microprocessors that are attached to " +
|
||||
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
|
||||
"so that the brain doesn't have to.",
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG4],
|
||||
prereqs: [
|
||||
AugmentationNames.CranialSignalProcessorsG4,
|
||||
AugmentationNames.CranialSignalProcessorsG3,
|
||||
AugmentationNames.CranialSignalProcessorsG2,
|
||||
AugmentationNames.CranialSignalProcessorsG1,
|
||||
],
|
||||
hacking_mult: 1.3,
|
||||
hacking_money_mult: 1.25,
|
||||
hacking_grow_mult: 1.75,
|
||||
@@ -1962,7 +1971,7 @@ export const initChurchOfTheMachineGodAugmentations = (): Augmentation[] => [
|
||||
"You will become greater than the sum of our parts. As One. Embrace your gift " +
|
||||
"fully and wholly free of it's accursed toll. Serenity brings tranquility the form " +
|
||||
"of no longer suffering a stat penalty. ",
|
||||
prereqs: [AugmentationNames.StaneksGift2],
|
||||
prereqs: [AugmentationNames.StaneksGift2, AugmentationNames.StaneksGift1],
|
||||
isSpecial: true,
|
||||
hacking_chance_mult: 1 / 0.95,
|
||||
hacking_speed_mult: 1 / 0.95,
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* React component for displaying a single augmentation for purchase through
|
||||
* the faction UI
|
||||
*/
|
||||
import { CheckBox, CheckBoxOutlineBlank, CheckCircle, Info, NewReleases, Report } from "@mui/icons-material";
|
||||
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { getNextNeuroFluxLevel } from "../AugmentationHelpers";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Augmentation } from "../Augmentation";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
|
||||
|
||||
interface IPreReqsProps {
|
||||
player: IPlayer;
|
||||
aug: Augmentation;
|
||||
}
|
||||
|
||||
const PreReqs = (props: IPreReqsProps): React.ReactElement => {
|
||||
const ownedPreReqs = props.aug.prereqs.filter((aug) => props.player.hasAugmentation(aug));
|
||||
const hasPreReqs = props.aug.prereqs.length > 0 && ownedPreReqs.length === props.aug.prereqs.length;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
This Augmentation has the following pre-requisite(s):
|
||||
</Typography>
|
||||
{props.aug.prereqs.map((preAug) => (
|
||||
<Requirement
|
||||
fulfilled={props.player.hasAugmentation(preAug)}
|
||||
value={preAug}
|
||||
color={Settings.theme.money}
|
||||
key={preAug}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
color: hasPreReqs ? Settings.theme.successlight : Settings.theme.error,
|
||||
}}
|
||||
>
|
||||
{hasPreReqs ? (
|
||||
<>
|
||||
<CheckCircle fontSize="small" sx={{ mr: 1 }} />
|
||||
Pre-requisites Owned
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Report fontSize="small" sx={{ mr: 1 }} />
|
||||
Missing {props.aug.prereqs.length - ownedPreReqs.length} pre-requisite(s)
|
||||
</>
|
||||
)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface IExclusiveProps {
|
||||
player: IPlayer;
|
||||
aug: Augmentation;
|
||||
}
|
||||
|
||||
const Exclusive = (props: IExclusiveProps): React.ReactElement => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
This Augmentation can only be acquired from the following source(s):
|
||||
</Typography>
|
||||
<ul>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
<li>
|
||||
<b>{props.aug.factions[0]}</b> faction
|
||||
</li>
|
||||
{props.player.canAccessGang() && !props.aug.isSpecial && (
|
||||
<li>
|
||||
Certain <b>gangs</b>
|
||||
</li>
|
||||
)}
|
||||
{props.player.canAccessGrafting() &&
|
||||
!props.aug.isSpecial &&
|
||||
props.aug.name !== AugmentationNames.TheRedPill && (
|
||||
<li>
|
||||
<b>Grafting</b>
|
||||
</li>
|
||||
)}
|
||||
</Typography>
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<NewReleases sx={{ ml: 1, color: Settings.theme.money, transform: "rotate(180deg)" }} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface IReqProps {
|
||||
value: string;
|
||||
color: string;
|
||||
fulfilled: boolean;
|
||||
}
|
||||
|
||||
const Requirement = (props: IReqProps): React.ReactElement => {
|
||||
return (
|
||||
<Typography sx={{ display: "flex", alignItems: "center", color: props.color }}>
|
||||
{props.fulfilled ? <CheckBox sx={{ mr: 1 }} /> : <CheckBoxOutlineBlank sx={{ mr: 1 }} />}
|
||||
{props.value}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
interface IPurchasableAugsProps {
|
||||
augNames: string[];
|
||||
ownedAugNames: string[];
|
||||
player: IPlayer;
|
||||
|
||||
canPurchase: (player: IPlayer, aug: Augmentation) => boolean;
|
||||
purchaseAugmentation: (player: IPlayer, aug: Augmentation, showModal: (open: boolean) => void) => void;
|
||||
|
||||
rep?: number;
|
||||
sleeveAugs?: boolean;
|
||||
faction?: Faction;
|
||||
}
|
||||
|
||||
export const PurchasableAugmentations = (props: IPurchasableAugsProps): React.ReactElement => {
|
||||
return (
|
||||
<Container
|
||||
maxWidth="lg"
|
||||
disableGutters
|
||||
sx={{ mx: 0, display: "grid", gridTemplateColumns: "repeat(1, 1fr)", gap: 1 }}
|
||||
>
|
||||
{props.augNames.map((augName: string) => (
|
||||
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={false} />
|
||||
))}
|
||||
{props.ownedAugNames.map((augName: string) => (
|
||||
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={true} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
interface IPurchasableAugProps {
|
||||
parent: IPurchasableAugsProps;
|
||||
augName: string;
|
||||
owned: boolean;
|
||||
}
|
||||
|
||||
export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const aug = Augmentations[props.augName];
|
||||
|
||||
const cost = props.parent.sleeveAugs ? aug.startingCost : aug.baseCost;
|
||||
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const description = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
p: 1,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "minmax(0, 4fr) 1fr",
|
||||
gap: 1,
|
||||
opacity: props.owned ? 0.75 : 1,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
props.parent.purchaseAugmentation(props.parent.player, aug, (open): void => {
|
||||
setOpen(open);
|
||||
})
|
||||
}
|
||||
disabled={!props.parent.canPurchase(props.parent.player, aug) || props.owned}
|
||||
sx={{ width: "48px", height: "48px", float: "left", clear: "none", mr: 1 }}
|
||||
>
|
||||
{props.owned ? "Owned" : "Buy"}
|
||||
</Button>
|
||||
|
||||
<Box sx={{ maxWidth: props.owned ? "100%" : "85%" }}>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography variant="h5">
|
||||
{props.augName}
|
||||
{props.augName === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
|
||||
</Typography>
|
||||
<Typography>{description}</Typography>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Info sx={{ mr: 1 }} color="info" />
|
||||
</Tooltip>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{aug.name}
|
||||
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
|
||||
</Typography>
|
||||
{aug.factions.length === 1 && !props.parent.sleeveAugs && (
|
||||
<Exclusive player={props.parent.player} aug={aug} />
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs player={props.parent.player} aug={aug} />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{props.owned || (
|
||||
<Box sx={{ display: "grid", alignItems: "center", justifyItems: "left" }}>
|
||||
<Requirement
|
||||
fulfilled={aug.baseCost === 0 || props.parent.player.money > cost}
|
||||
value={numeralWrapper.formatMoney(cost)}
|
||||
color={Settings.theme.money}
|
||||
/>
|
||||
{props.parent.rep !== undefined && (
|
||||
<Requirement
|
||||
fulfilled={props.parent.rep >= aug.baseRepRequirement}
|
||||
value={`${numeralWrapper.formatReputation(aug.baseRepRequirement)} rep`}
|
||||
color={Settings.theme.rep}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{Settings.SuppressBuyAugmentationConfirmation || (
|
||||
<PurchaseAugmentationModal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
faction={props.parent.faction}
|
||||
aug={aug}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
|
||||
import { Augmentation } from "../Augmentation";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { purchaseAugmentation } from "../../Faction/FactionHelpers";
|
||||
import { isRepeatableAug } from "../AugmentationHelpers";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { use } from "../../ui/Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
faction?: Faction;
|
||||
aug?: Augmentation;
|
||||
}
|
||||
|
||||
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
||||
if (typeof props.aug === "undefined" || typeof props.faction === "undefined") {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const player = use.Player();
|
||||
|
||||
function buy(): void {
|
||||
if (!isRepeatableAug(props.aug as Augmentation) && player.hasAugmentation(props.aug as Augmentation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
purchaseAugmentation(props.aug as Augmentation, props.faction as Faction);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography variant="h4">{props.aug.name}</Typography>
|
||||
<Typography>
|
||||
{props.aug.info}
|
||||
<br />
|
||||
<br />
|
||||
{props.aug.stats}
|
||||
<br />
|
||||
<br />
|
||||
Would you like to purchase the {props.aug.name} Augmentation for
|
||||
<Money money={props.aug.baseCost} />?
|
||||
<br />
|
||||
<br />
|
||||
</Typography>
|
||||
<Button autoFocus onClick={buy}>
|
||||
Purchase
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user