Merge pull request #3545 from nickofolas/improvement/purchase-augs-ui

UI: Redesign purchasable Augmentations
This commit is contained in:
hydroflame 2022-04-26 11:16:13 -04:00 committed by GitHub
commit d0d700077a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 433 additions and 402 deletions

@ -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>
);
}

@ -1,9 +1,9 @@
import React from "react";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Faction } from "../Faction";
import { purchaseAugmentation } from "../FactionHelpers";
import { isRepeatableAug } from "../../Augmentation/AugmentationHelpers";
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";
@ -13,21 +13,23 @@ import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
faction: Faction;
aug: Augmentation;
rerender: () => 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) && player.hasAugmentation(props.aug)) {
if (!isRepeatableAug(props.aug as Augmentation) && player.hasAugmentation(props.aug as Augmentation)) {
return;
}
purchaseAugmentation(props.aug, props.faction);
props.rerender();
purchaseAugmentation(props.aug as Augmentation, props.faction as Faction);
props.onClose();
}

@ -54,36 +54,15 @@ export function joinFaction(faction: Faction): void {
//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
export function hasAugmentationPrereqs(aug: Augmentation): boolean {
let hasPrereqs = true;
if (aug.prereqs && aug.prereqs.length > 0) {
for (let i = 0; i < aug.prereqs.length; ++i) {
const prereqAug = Augmentations[aug.prereqs[i]];
if (prereqAug == null) {
console.error(`Invalid prereq Augmentation ${aug.prereqs[i]}`);
continue;
}
if (Player.hasAugmentation(prereqAug, true) === false) {
hasPrereqs = false;
// Check if the aug is purchased
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === prereqAug.name) {
hasPrereqs = true;
break;
}
}
}
}
}
return hasPrereqs;
return aug.prereqs.every((aug) => Player.hasAugmentation(aug));
}
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
const txt = `You must first purchase or install ${aug.prereqs
.filter((req) => !Player.hasAugmentation(req))
.join(",")} before you can purchase this one.`;
if (sing) {
return txt;
} else {

@ -1,30 +1,21 @@
/**
* Root React Component for displaying a faction's "Purchase Augmentations" page
*/
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
import React, { useState } from "react";
import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../Faction";
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { hasAugmentationPrereqs, getFactionAugmentationsFiltered } from "../FactionHelpers";
import { use } from "../../ui/Context";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import { numeralWrapper } from "../../ui/numeralFormat";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import TableBody from "@mui/material/TableBody";
import Table from "@mui/material/Table";
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
import { Favor } from "../../ui/React/Favor";
import { Reputation } from "../../ui/React/Reputation";
import { FactionNames } from "../data/FactionNames";
import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
type IProps = {
faction: Faction;
@ -137,38 +128,12 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
aug === AugmentationNames.NeuroFluxGovernor ||
(!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)),
);
const purchaseableAugmentation = (aug: string, owned = false): React.ReactNode => {
return (
<PurchaseableAugmentation
augName={aug}
faction={props.faction}
key={aug}
p={player}
rerender={rerender}
owned={owned}
/>
);
};
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
let ownedElem = <></>;
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
if (owned.length !== 0) {
ownedElem = (
<>
<br />
<Typography variant="h4">Purchased Augmentations</Typography>
<Typography>This faction also offers these augmentations but you already own them.</Typography>
{owned.map((aug) => purchaseableAugmentation(aug, true))}
</>
);
}
const multiplierComponent =
props.faction.name !== FactionNames.ShadowsOfAnarchy ? (
<Typography>
Price multiplier: x {numeralWrapper.formatMultiplier(getGenericAugmentationPriceMultiplier())}
<b>Price multiplier:</b> x {numeralWrapper.formatReallyBigNumber(getGenericAugmentationPriceMultiplier())}
</Typography>
) : (
<></>
@ -176,42 +141,77 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
return (
<>
<Button onClick={props.routeToMainPage}>Back</Button>
<Typography variant="h4">Faction Augmentations</Typography>
<Typography>
These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
powerful upgrades that will enhance your abilities.
<br />
Reputation: <Reputation reputation={props.faction.playerReputation} /> Favor:{" "}
<Favor favor={Math.floor(props.faction.favor)} />
</Typography>
<Box display="flex">
<Tooltip
title={
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
<Button onClick={props.routeToMainPage}>Back</Button>
<Typography variant="h4">Faction Augmentations</Typography>
<Paper sx={{ p: 1, mb: 1 }}>
<Typography>
These are all of the Augmentations that are available to purchase from <b>{props.faction.name}</b>.
Augmentations are powerful upgrades that will enhance your abilities.
<br />
</Typography>
<Box
sx={{
display: "grid",
gridTemplateColumns: `repeat(${props.faction.name === FactionNames.ShadowsOfAnarchy ? "2" : "3"}, 1fr)`,
justifyItems: "center",
my: 1,
}}
>
<Tooltip
title={
<Typography>
The price of every Augmentation increases for every queued Augmentation and it is reset when you
install them.
</Typography>
}
>
{multiplierComponent}
</Tooltip>
<Typography>
The price of every Augmentation increases for every queued Augmentation and it is reset when you install
them.
<b>Reputation:</b> <Reputation reputation={props.faction.playerReputation} />
</Typography>
<Typography>
<b>Favor:</b> <Favor favor={Math.floor(props.faction.favor)} />
</Typography>
</Box>
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)" }}>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>
Sort by Reputation
</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>
Sort by Default Order
</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Purchasable)}>
Sort by Purchasable
</Button>
</Box>
</Paper>
</Container>
<PurchasableAugmentations
augNames={purchasable}
ownedAugNames={owned}
player={player}
canPurchase={(player, aug) => {
return (
hasAugmentationPrereqs(aug) &&
props.faction.playerReputation >= aug.baseRepRequirement &&
(aug.baseCost === 0 || player.money > aug.baseCost)
);
}}
purchaseAugmentation={(player, aug, showModal) => {
if (!Settings.SuppressBuyAugmentationConfirmation) {
showModal(true);
} else {
purchaseAugmentation(aug, props.faction);
rerender();
}
>
{multiplierComponent}
</Tooltip>
</Box>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>Sort by Reputation</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>Sort by Default Order</Button>
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Purchasable)}>
Sort by Purchasable
</Button>
<br />
<Table size="small" padding="none">
<TableBody>{augListElems}</TableBody>
</Table>
<Table size="small" padding="none">
<TableBody>{ownedElem}</TableBody>
</Table>
}}
rep={props.faction.playerReputation}
faction={props.faction}
/>
</>
);
}

@ -1,170 +0,0 @@
/**
* React component for displaying a single augmentation for purchase through
* the faction UI
*/
import React, { useState } from "react";
import { hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
import { TableCell } from "../../ui/React/Table";
import TableRow from "@mui/material/TableRow";
import { getNextNeuroFluxLevel } from "../../Augmentation/AugmentationHelpers";
interface IReqProps {
augName: string;
p: IPlayer;
hasReq: boolean;
rep: number;
hasRep: boolean;
cost: number;
hasCost: boolean;
}
function Requirements(props: IReqProps): React.ReactElement {
const aug = Augmentations[props.augName];
if (!props.hasReq) {
return (
<TableCell key={1} colSpan={2}>
<Typography color="error">
Requires{" "}
{aug.prereqs.map((aug, i) => (
<AugFormat key={i} name={aug} />
))}
</Typography>
</TableCell>
);
}
return (
<React.Fragment key="f">
<TableCell key={1}>
<Typography>
<Money money={props.cost} player={props.p} />
</Typography>
</TableCell>
<TableCell key={2}>
<Typography color={props.hasRep ? "primary" : "error"}>
Requires <Reputation reputation={props.rep} /> faction reputation
</Typography>
</TableCell>
</React.Fragment>
);
}
interface IProps {
augName: string;
faction: Faction;
p: IPlayer;
rerender: () => void;
owned?: boolean;
}
export function PurchaseableAugmentation(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false);
const aug = Augmentations[props.augName];
if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
if (aug == null) {
console.error(
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
);
return <></>;
}
const moneyCost = aug.baseCost;
const repCost = aug.baseRepRequirement;
const hasReq = hasAugmentationPrereqs(aug);
const hasRep = props.faction.playerReputation >= repCost;
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost;
// Determine UI properties
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
// Determine button txt
let btnTxt = aug.name;
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
btnTxt += ` - Level ${getNextNeuroFluxLevel()}`;
}
let tooltip = <></>;
if (typeof aug.info === "string") {
tooltip = (
<>
<span>{aug.info}</span>
<br />
<br />
{aug.stats}
</>
);
} else
tooltip = (
<>
{aug.info}
<br />
<br />
{aug.stats}
</>
);
function handleClick(): void {
if (color === "error") return;
if (!Settings.SuppressBuyAugmentationConfirmation) {
setOpen(true);
} else {
purchaseAugmentation(aug, props.faction);
props.rerender();
}
}
return (
<TableRow>
{!props.owned && (
<TableCell key={0}>
<Button onClick={handleClick} color={color}>
Buy
</Button>
<PurchaseAugmentationModal
open={open}
onClose={() => setOpen(false)}
aug={aug}
faction={props.faction}
rerender={props.rerender}
/>
</TableCell>
)}
<TableCell key={1}>
<Box display="flex">
<Tooltip title={<Typography>{tooltip}</Typography>} placement="top">
<Typography>{btnTxt}</Typography>
</Tooltip>
</Box>
</TableCell>
{!props.owned && (
<Requirements
key={2}
augName={props.augName}
p={props.p}
cost={moneyCost}
rep={repCost}
hasReq={hasReq}
hasRep={hasRep}
hasCost={hasCost}
/>
)}
</TableRow>
);
}

@ -139,34 +139,37 @@ export const GraftingRoot = (): React.ReactElement => {
</>
}
/>
<Typography color={Settings.theme.info}>
<b>Time to Graft:</b>{" "}
{convertTimeMsToTimeElapsedString(
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
<Box sx={{ maxHeight: 330, overflowY: "scroll" }}>
<Typography color={Settings.theme.info}>
<b>Time to Graft:</b>{" "}
{convertTimeMsToTimeElapsedString(
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
)}
{/* Use formula so the displayed creation time is accurate to player bonus */}
</Typography>
{Augmentations[selectedAug].prereqs.length > 0 && (
<AugPreReqsChecklist player={player} aug={Augmentations[selectedAug]} />
)}
{/* Use formula so the displayed creation time is accurate to player bonus */}
</Typography>
{Augmentations[selectedAug].prereqs.length > 0 && (
<AugPreReqsChecklist player={player} aug={Augmentations[selectedAug]} />
)}
<br />
<br />
<Typography sx={{ maxHeight: 305, overflowY: "scroll" }}>
{(() => {
const aug = Augmentations[selectedAug];
<Typography>
{(() => {
const aug = Augmentations[selectedAug];
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
const tooltip = (
<>
{info}
<br />
<br />
{aug.stats}
</>
);
return tooltip;
})()}
</Typography>
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
const tooltip = (
<>
{info}
<br />
<br />
{aug.stats}
</>
);
return tooltip;
})()}
</Typography>
</Box>
</Box>
</Paper>
) : (

@ -1,20 +1,10 @@
import React, { useState, useEffect } from "react";
import { Container, Typography, Paper } from "@mui/material";
import React, { useEffect, useState } from "react";
import { PurchasableAugmentations } from "../../../Augmentation/ui/PurchasableAugmentations";
import { use } from "../../../ui/Context";
import { Modal } from "../../../ui/React/Modal";
import { Sleeve } from "../Sleeve";
import { findSleevePurchasableAugs } from "../SleeveHelpers";
import { Augmentations } from "../../../Augmentation/Augmentations";
import { Augmentation } from "../../../Augmentation/Augmentation";
import { Money } from "../../../ui/React/Money";
import { Modal } from "../../../ui/React/Modal";
import { use } from "../../../ui/Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import TableBody from "@mui/material/TableBody";
import Table from "@mui/material/Table";
import { TableCell } from "../../../ui/React/Table";
import TableRow from "@mui/material/TableRow";
interface IProps {
open: boolean;
@ -42,80 +32,34 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
// and you must also have enough rep in that faction in order to purchase it.
const availableAugs = findSleevePurchasableAugs(props.sleeve, player);
function purchaseAugmentation(aug: Augmentation): void {
props.sleeve.tryBuyAugmentation(player, aug);
rerender();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<>
<Box sx={{ mx: 1 }}>
<Typography>
You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they
would for you. You can only purchase Augmentations that you have unlocked through Factions.
<br />
<br />
When purchasing an Augmentation for a Duplicate Sleeve, they are immediately installed. This means that the
Duplicate Sleeve will immediately lose all of its stat experience.
</Typography>
<Box component={Paper} sx={{ my: 1, p: 1 }}>
<Table size="small" padding="none">
<TableBody>
{availableAugs.map((aug) => {
return (
<TableRow key={aug.name}>
<TableCell>
<Button onClick={() => purchaseAugmentation(aug)} disabled={player.money < aug.startingCost}>
Buy
</Button>
</TableCell>
<TableCell>
<Box display="flex">
<Tooltip title={aug.stats || ""}>
<Typography>{aug.name}</Typography>
</Tooltip>
</Box>
</TableCell>
<TableCell>
<Money money={aug.startingCost} player={player} />
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</Box>
</Box>
{ownedAugNames.length > 0 && (
<>
<Typography sx={{ mx: 1 }}>Owned Augmentations:</Typography>
<Box display="grid" sx={{ gridTemplateColumns: "repeat(5, 1fr)", m: 1 }}>
{ownedAugNames.map((augName) => {
const aug = Augmentations[augName];
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
const tooltip = (
<>
{info}
<br />
<br />
{aug.stats}
</>
);
return (
<Tooltip key={augName} title={<Typography>{tooltip}</Typography>}>
<Paper sx={{ p: 1 }}>
<Typography>{augName}</Typography>
</Paper>
</Tooltip>
);
})}
</Box>
</>
)}
</>
<Container component={Paper} disableGutters maxWidth="lg" sx={{ mx: 0, mb: 1, p: 1 }}>
<Typography>
You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they
would for you. You can only purchase Augmentations that you have unlocked through Factions.
<br />
<br />
When purchasing an Augmentation for a Duplicate Sleeve, they are immediately installed. This means that the
Duplicate Sleeve will immediately lose all of its stat experience.
<br />
<br />
Augmentations will appear below as they become available.
</Typography>
</Container>
<PurchasableAugmentations
augNames={availableAugs.map((aug) => aug.name)}
ownedAugNames={ownedAugNames}
player={player}
canPurchase={(player, aug) => {
return player.money > aug.startingCost;
}}
purchaseAugmentation={(player, aug, _showModal) => {
props.sleeve.tryBuyAugmentation(player, aug);
rerender();
}}
sleeveAugs
/>
</Modal>
);
}