First iteration of sleeves UI improvements

This commit is contained in:
nickofolas 2022-03-07 20:03:23 -06:00
parent 372776c94e
commit 5d6952dbac
5 changed files with 289 additions and 178 deletions

@ -50,74 +50,62 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<> <>
<Typography> <Box sx={{ mx: 1 }}>
You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they <Typography>
would for you. You can only purchase Augmentations that you have unlocked through Factions. You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they
<br /> 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 <br />
Duplicate Sleeve will immediately lose all of its stat experience. When purchasing an Augmentation for a Duplicate Sleeve, they are immediately installed. This means that the
</Typography> Duplicate Sleeve will immediately lose all of its stat experience.
<Table size="small" padding="none"> </Typography>
<TableBody> <Box component={Paper} sx={{ my: 1, p: 1 }}>
{availableAugs.map((aug) => { <Table size="small" padding="none">
return ( <TableBody>
<TableRow key={aug.name}> {availableAugs.map((aug) => {
<TableCell> return (
<Button onClick={() => purchaseAugmentation(aug)} disabled={player.money < aug.startingCost}> <TableRow key={aug.name}>
Buy <TableCell>
</Button> <Button onClick={() => purchaseAugmentation(aug)} disabled={player.money < aug.startingCost}>
</TableCell> Buy
<TableCell> </Button>
<Box display="flex"> </TableCell>
<Tooltip title={aug.stats || ""}> <TableCell>
<Typography>{aug.name}</Typography> <Box display="flex">
</Tooltip> <Tooltip title={aug.stats || ""}>
</Box> <Typography>{aug.name}</Typography>
</TableCell> </Tooltip>
<TableCell> </Box>
<Money money={aug.startingCost} player={player} /> </TableCell>
</TableCell> <TableCell>
</TableRow> <Money money={aug.startingCost} player={player} />
); </TableCell>
})} </TableRow>
</TableBody> );
</Table> })}
</TableBody>
</Table>
</Box>
</Box>
{ownedAugNames.length > 0 && ( {ownedAugNames.length > 0 && (
<> <>
<Typography>Owned Augmentations:</Typography> <Typography sx={{ mx: 1 }}>Owned Augmentations:</Typography>
{ownedAugNames.map((augName) => { <Box display="grid" sx={{ gridTemplateColumns: 'repeat(5, 1fr)', m: 1 }}>
const aug = Augmentations[augName]; {ownedAugNames.map((augName) => {
let tooltip = <></>; const aug = Augmentations[augName];
if (typeof aug.info === "string") { const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info
tooltip = ( const tooltip = (<>{info}<br /><br />{aug.stats}</>);
<>
<span>{aug.info}</span>
<br />
<br />
{aug.stats}
</>
);
} else {
tooltip = (
<>
{aug.info}
<br />
<br />
{aug.stats}
</>
);
}
return ( return (
<Tooltip key={augName} title={<Typography>{tooltip}</Typography>}> <Tooltip key={augName} title={<Typography>{tooltip}</Typography>}>
<Paper> <Paper sx={{ p: 1 }}>
<Typography>{augName}</Typography> <Typography>{augName}</Typography>
</Paper> </Paper>
</Tooltip> </Tooltip>
); );
})} })}
</Box>
</> </>
)} )}
</> </>

@ -13,20 +13,16 @@ import { createProgressBarText } from "../../../utils/helpers/createProgressBarT
import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal"; import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
import { TravelModal } from "./TravelModal"; import { TravelModal } from "./TravelModal";
import { Money } from "../../../ui/React/Money";
import { MoneyRate } from "../../../ui/React/MoneyRate";
import { use } from "../../../ui/Context"; import { use } from "../../../ui/Context";
import { ReputationRate } from "../../../ui/React/ReputationRate"; import { StatsElement, EarningsElement } from "./StatsElement";
import { StatsElement } from "./StatsElement";
import { MoreStatsModal } from "./MoreStatsModal"; import { MoreStatsModal } from "./MoreStatsModal";
import { MoreEarningsModal } from "./MoreEarningsModal"; import { MoreEarningsModal } from "./MoreEarningsModal";
import { TaskSelector } from "./TaskSelector"; import { TaskSelector } from "./TaskSelector";
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum"; import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
import { StatsTable } from "../../../ui/React/StatsTable";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid"; import { Box } from "@mui/material";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
@ -141,86 +137,71 @@ export function SleeveElem(props: IProps): React.ReactElement {
console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`); console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`);
} }
let data: any[][] = [];
if (props.sleeve.currentTask === SleeveTaskType.Crime) {
data = [
[`Money`, <Money money={parseFloat(props.sleeve.currentTaskLocation)} />, `(on success)`],
[`Hacking Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.hack), `(2x on success)`],
[`Strength Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.str), `(2x on success)`],
[`Defense Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.def), `(2x on success)`],
[`Dexterity Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.dex), `(2x on success)`],
[`Agility Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.agi), `(2x on success)`],
[`Charisma Exp`, numeralWrapper.formatExp(props.sleeve.gainRatesForTask.cha), `(2x on success)`],
];
} else {
data = [
[`Money:`, <MoneyRate money={5 * props.sleeve.gainRatesForTask.money} />],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.hack)} / s`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.str)} / s`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.def)} / s`],
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.dex)} / s`],
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.agi)} / s`],
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.cha)} / s`],
];
if (props.sleeve.currentTask === SleeveTaskType.Company || props.sleeve.currentTask === SleeveTaskType.Faction) {
const repGain: number = props.sleeve.getRepGain(player);
data.push([`Reputation:`, <ReputationRate reputation={5 * repGain} />]);
}
}
return ( return (
<> <Box component={Paper} sx={{ width: 'auto' }}>
<Grid container component={Paper}> <Box sx={{ m: 1 }}>
<Grid item xs={3}> <Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: '100%', gap: 1 }}>
<StatsElement sleeve={props.sleeve} /> <Box>
<Button onClick={() => setStatsOpen(true)}>More Stats</Button> <StatsElement sleeve={props.sleeve} />
<Tooltip title={player.money < CONSTANTS.TravelCost ? <Typography>Insufficient funds</Typography> : ""}> <Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: '100%' }}>
<span> <Button onClick={() => setStatsOpen(true)}>More Stats</Button>
<Button onClick={() => setTravelOpen(true)} disabled={player.money < CONSTANTS.TravelCost}> <Button onClick={() => setEarningsOpen(true)}>More Earnings Info</Button>
Travel <Tooltip title={player.money < CONSTANTS.TravelCost ? <Typography>Insufficient funds</Typography> : ""}>
</Button> <span>
</span> <Button
</Tooltip> onClick={() => setTravelOpen(true)}
<Tooltip disabled={player.money < CONSTANTS.TravelCost}
title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""} sx={{ width: '100%', height: '100%' }}
> >
<span> Travel
<Button onClick={() => setAugmentationsOpen(true)} disabled={props.sleeve.shock < 100}> </Button>
Manage Augmentations </span>
</Button> </Tooltip>
</span> <Tooltip
</Tooltip> title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""}
</Grid> >
<Grid item xs={5}> <span>
<TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} /> <Button
<Typography>{desc}</Typography> onClick={() => setAugmentationsOpen(true)}
<Typography> disabled={props.sleeve.shock < 100}
{props.sleeve.currentTask === SleeveTaskType.Crime && sx={{ width: '100%', height: '100%' }}
createProgressBarText({ >
progress: props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime, Manage Augmentations
totalTicks: 25, </Button>
})} </span>
</Typography> </Tooltip>
<Button onClick={setTask}>Set Task</Button> </Box>
</Grid> </Box>
<Grid item xs={4}> <Box>
<StatsTable title="Earnings (Pre-Synchronization)" rows={data} /> <EarningsElement sleeve={props.sleeve} />
<Button onClick={() => setEarningsOpen(true)}>More Earnings Info</Button> <Box>
</Grid> <TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} />
</Grid> <Button onClick={setTask} sx={{ width: '100%' }}>Set Task</Button>
<MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} /> <Typography>{desc}</Typography>
<MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} /> <Typography>
<TravelModal {props.sleeve.currentTask === SleeveTaskType.Crime &&
open={travelOpen} createProgressBarText({
onClose={() => setTravelOpen(false)} progress: props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime,
sleeve={props.sleeve} totalTicks: 25,
rerender={props.rerender} })}
/> </Typography>
<SleeveAugmentationsModal </Box>
open={augmentationsOpen} </Box>
onClose={() => setAugmentationsOpen(false)} <MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} />
sleeve={props.sleeve} <MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} />
/> <TravelModal
</> open={travelOpen}
onClose={() => setTravelOpen(false)}
sleeve={props.sleeve}
rerender={props.rerender}
/>
<SleeveAugmentationsModal
open={augmentationsOpen}
onClose={() => setAugmentationsOpen(false)}
sleeve={props.sleeve}
/>
</Box>
</Box>
</Box >
); );
} }

@ -7,6 +7,7 @@ import { use } from "../../../ui/Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Link from "@mui/material/Link"; import Link from "@mui/material/Link";
import { Box } from "@mui/material";
export function SleeveRoot(): React.ReactElement { export function SleeveRoot(): React.ReactElement {
const player = use.Player(); const player = use.Player();
@ -41,9 +42,11 @@ export function SleeveRoot(): React.ReactElement {
> >
<Typography> Documentation</Typography> <Typography> Documentation</Typography>
</Link> </Link>
{player.sleeves.map((sleeve, i) => ( <Box display="grid" sx={{ gridTemplateColumns: 'repeat(2, 1fr)' }}>
<SleeveElem key={i} rerender={rerender} sleeve={sleeve} /> {player.sleeves.map((sleeve, i) => (
))} <SleeveElem key={i} rerender={rerender} sleeve={sleeve} />
))}
</Box>
<FAQModal open={FAQOpen} onClose={() => setFAQOpen(false)} /> <FAQModal open={FAQOpen} onClose={() => setFAQOpen(false)} />
</> </>
); );

@ -1,31 +1,172 @@
import { Sleeve } from "../Sleeve"; import { Sleeve } from "../Sleeve";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import React from "react"; import React from "react";
import Typography from "@mui/material/Typography";
import {
Table,
TableBody,
TableCell,
TableRow,
} from "@mui/material";
import { StatsTable } from "../../../ui/React/StatsTable"; import { Settings } from "../../../Settings/Settings";
import { formatNumber } from "../../../utils/StringHelperFunctions";
import { characterOverviewStyles as useStyles } from "../../../ui/React/CharacterOverview";
import { Money } from "../../../ui/React/Money";
import { MoneyRate } from "../../../ui/React/MoneyRate";
import { ReputationRate } from "../../../ui/React/ReputationRate";
import { SleeveTaskType } from "../SleeveTaskTypesEnum";
import { use } from "../../../ui/Context";
interface ITableRowData {
content?: string;
level?: number;
exp?: number;
}
export const generateTableRow = (
name: string,
color: string,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
classes: any,
data: ITableRowData
): React.ReactElement => {
let content;
if (data.content !== undefined) {
content = data.content;
} else if (data.level !== undefined && data.exp !== undefined) {
content = `${formatNumber(data.level, 0)} (${numeralWrapper.formatExp(data.exp)} exp)`;
} else if (data.level !== undefined && data.exp === undefined) {
content = `${formatNumber(data.level, 0)}`;
}
return (
<TableRow>
<TableCell classes={{ root: classes.cellNone }}>
<Typography style={{ color: color }}>{name}</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cellNone }}>
<Typography style={{ color: color }}>
{content}
</Typography>
</TableCell>
</TableRow>
)
}
interface IProps { interface IProps {
sleeve: Sleeve; sleeve: Sleeve;
} }
export function StatsElement(props: IProps): React.ReactElement { export function StatsElement(props: IProps): React.ReactElement {
const rows = [ const classes = useStyles();
[
"HP: ", return (
<> <Table sx={{ display: 'table', mb: 1, width: '100%' }}>
{numeralWrapper.formatHp(props.sleeve.hp)} / {numeralWrapper.formatHp(props.sleeve.max_hp)} <TableBody>
</>, {generateTableRow("City", Settings.theme.primary, classes, {
], content: props.sleeve.city
["City: ", <>{props.sleeve.city}</>], })}
["Hacking: ", <>{numeralWrapper.formatSkill(props.sleeve.hacking)}</>], {generateTableRow("HP", Settings.theme.hp, classes, {
["Strength: ", <>{numeralWrapper.formatSkill(props.sleeve.strength)}</>], content: `${numeralWrapper.formatHp(props.sleeve.hp)} / ${numeralWrapper.formatHp(props.sleeve.max_hp)}`
["Defense: ", <>{numeralWrapper.formatSkill(props.sleeve.defense)}</>], })}
["Dexterity: ", <>{numeralWrapper.formatSkill(props.sleeve.dexterity)}</>], {generateTableRow("Hacking", Settings.theme.hack, classes, {
["Agility: ", <>{numeralWrapper.formatSkill(props.sleeve.agility)}</>], level: props.sleeve.hacking,
["Charisma: ", <>{numeralWrapper.formatSkill(props.sleeve.charisma)}</>], exp: props.sleeve.hacking_exp
["Shock: ", <>{numeralWrapper.formatSleeveShock(100 - props.sleeve.shock)}</>], })}
["Sync: ", <>{numeralWrapper.formatSleeveSynchro(props.sleeve.sync)}</>], {generateTableRow("Strength", Settings.theme.combat, classes, {
["Memory: ", <>{numeralWrapper.formatSleeveMemory(props.sleeve.memory)}</>], level: props.sleeve.strength,
]; exp: props.sleeve.strength_exp
return <StatsTable rows={rows} />; })}
{generateTableRow("Defense", Settings.theme.combat, classes, {
level: props.sleeve.defense,
exp: props.sleeve.defense_exp
})}
{generateTableRow("Dexterity", Settings.theme.combat, classes, {
level: props.sleeve.dexterity,
exp: props.sleeve.dexterity_exp
})}
{generateTableRow("Agility", Settings.theme.combat, classes, {
level: props.sleeve.agility,
exp: props.sleeve.agility_exp
})}
{generateTableRow("Charisma", Settings.theme.cha, classes, {
level: props.sleeve.charisma,
exp: props.sleeve.charisma_exp
})}
<TableRow>
<TableCell classes={{ root: classes.cellNone }}>
<br />
</TableCell>
</TableRow>
{generateTableRow("Shock", Settings.theme.primary, classes, {
content: numeralWrapper.formatSleeveShock(100 - props.sleeve.shock)
})}
{generateTableRow("Sync", Settings.theme.primary, classes, {
content: numeralWrapper.formatSleeveSynchro(props.sleeve.sync)
})}
{generateTableRow("Memory", Settings.theme.primary, classes, {
content: numeralWrapper.formatSleeveMemory(props.sleeve.memory)
})}
</TableBody>
</Table>
)
} }
export function EarningsElement(props: IProps): React.ReactElement {
const classes = useStyles();
const player = use.Player();
let data: any[][] = [];
if (props.sleeve.currentTask === SleeveTaskType.Crime) {
data = [
[`Money`, <><Money money={parseFloat(props.sleeve.currentTaskLocation)} /> (on success)</>],
[`Hacking Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.hack)} (2x on success)`],
[`Strength Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.str)} (2x on success)`],
[`Defense Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.def)} (2x on success)`],
[`Dexterity Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.dex)} (2x on success)`],
[`Agility Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.agi)} (2x on success)`],
[`Charisma Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.cha)} (2x on success)`],
];
} else {
data = [
[`Money:`, <MoneyRate money={5 * props.sleeve.gainRatesForTask.money} />],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.hack)} / sec`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.str)} / sec`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.def)} / sec`],
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.dex)} / sec`],
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.agi)} / sec`],
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.cha)} / sec`],
];
if (props.sleeve.currentTask === SleeveTaskType.Company || props.sleeve.currentTask === SleeveTaskType.Faction) {
const repGain: number = props.sleeve.getRepGain(player);
data.push([`Reputation:`, <ReputationRate reputation={5 * repGain} />]);
}
}
return (
<Table sx={{ display: 'table', mb: 1, width: '100%', lineHeight: 0 }}>
<TableBody>
<TableRow>
<TableCell classes={{ root: classes.cellNone }}>
<Typography variant='h6'>
Earnings
</Typography>
</TableCell>
</TableRow>
{data.map(([a, b]) => (
<TableRow key={a.toString() + b.toString()}>
<TableCell classes={{ root: classes.cellNone }}>
<Typography>{a}</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cellNone }}>
<Typography>{b}</Typography>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}

@ -278,7 +278,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
return ( return (
<> <>
<Select onChange={onS0Change} value={s0}> <Select onChange={onS0Change} value={s0} sx={{ width: '100%' }}>
{validActions.map((task) => ( {validActions.map((task) => (
<MenuItem key={task} value={task}> <MenuItem key={task} value={task}>
{task} {task}
@ -287,8 +287,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
</Select> </Select>
{!(details.first.length === 1 && details.first[0] === "------") && ( {!(details.first.length === 1 && details.first[0] === "------") && (
<> <>
<br /> <Select onChange={onS1Change} value={s1} sx={{ width: '100%' }}>
<Select onChange={onS1Change} value={s1}>
{details.first.map((detail) => ( {details.first.map((detail) => (
<MenuItem key={detail} value={detail}> <MenuItem key={detail} value={detail}>
{detail} {detail}
@ -299,8 +298,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
)} )}
{!(details2.length === 1 && details2[0] === "------") && ( {!(details2.length === 1 && details2[0] === "------") && (
<> <>
<br /> <Select onChange={onS2Change} value={s2} sx={{ width: '100%' }}>
<Select onChange={onS2Change} value={s2}>
{details2.map((detail) => ( {details2.map((detail) => (
<MenuItem key={detail} value={detail}> <MenuItem key={detail} value={detail}>
{detail} {detail}