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,6 +50,7 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<> <>
<Box sx={{ mx: 1 }}>
<Typography> <Typography>
You can purchase Augmentations for your Duplicate Sleeves. These Augmentations have the same effect as they 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. would for you. You can only purchase Augmentations that you have unlocked through Factions.
@ -58,6 +59,7 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
When purchasing an Augmentation for a Duplicate Sleeve, they are immediately installed. This means that the 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. Duplicate Sleeve will immediately lose all of its stat experience.
</Typography> </Typography>
<Box component={Paper} sx={{ my: 1, p: 1 }}>
<Table size="small" padding="none"> <Table size="small" padding="none">
<TableBody> <TableBody>
{availableAugs.map((aug) => { {availableAugs.map((aug) => {
@ -83,41 +85,27 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
})} })}
</TableBody> </TableBody>
</Table> </Table>
</Box>
</Box>
{ownedAugNames.length > 0 && ( {ownedAugNames.length > 0 && (
<> <>
<Typography>Owned Augmentations:</Typography> <Typography sx={{ mx: 1 }}>Owned Augmentations:</Typography>
<Box display="grid" sx={{ gridTemplateColumns: 'repeat(5, 1fr)', m: 1 }}>
{ownedAugNames.map((augName) => { {ownedAugNames.map((augName) => {
const aug = Augmentations[augName]; const aug = Augmentations[augName];
let tooltip = <></>; const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info
if (typeof aug.info === "string") { const tooltip = (<>{info}<br /><br />{aug.stats}</>);
tooltip = (
<>
<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,42 +137,22 @@ 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 }}>
<Box>
<StatsElement sleeve={props.sleeve} /> <StatsElement sleeve={props.sleeve} />
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: '100%' }}>
<Button onClick={() => setStatsOpen(true)}>More Stats</Button> <Button onClick={() => setStatsOpen(true)}>More Stats</Button>
<Button onClick={() => setEarningsOpen(true)}>More Earnings Info</Button>
<Tooltip title={player.money < CONSTANTS.TravelCost ? <Typography>Insufficient funds</Typography> : ""}> <Tooltip title={player.money < CONSTANTS.TravelCost ? <Typography>Insufficient funds</Typography> : ""}>
<span> <span>
<Button onClick={() => setTravelOpen(true)} disabled={player.money < CONSTANTS.TravelCost}> <Button
onClick={() => setTravelOpen(true)}
disabled={player.money < CONSTANTS.TravelCost}
sx={{ width: '100%', height: '100%' }}
>
Travel Travel
</Button> </Button>
</span> </span>
@ -185,14 +161,22 @@ export function SleeveElem(props: IProps): React.ReactElement {
title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""} title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""}
> >
<span> <span>
<Button onClick={() => setAugmentationsOpen(true)} disabled={props.sleeve.shock < 100}> <Button
onClick={() => setAugmentationsOpen(true)}
disabled={props.sleeve.shock < 100}
sx={{ width: '100%', height: '100%' }}
>
Manage Augmentations Manage Augmentations
</Button> </Button>
</span> </span>
</Tooltip> </Tooltip>
</Grid> </Box>
<Grid item xs={5}> </Box>
<Box>
<EarningsElement sleeve={props.sleeve} />
<Box>
<TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} /> <TaskSelector player={player} sleeve={props.sleeve} setABC={setABC} />
<Button onClick={setTask} sx={{ width: '100%' }}>Set Task</Button>
<Typography>{desc}</Typography> <Typography>{desc}</Typography>
<Typography> <Typography>
{props.sleeve.currentTask === SleeveTaskType.Crime && {props.sleeve.currentTask === SleeveTaskType.Crime &&
@ -201,13 +185,8 @@ export function SleeveElem(props: IProps): React.ReactElement {
totalTicks: 25, totalTicks: 25,
})} })}
</Typography> </Typography>
<Button onClick={setTask}>Set Task</Button> </Box>
</Grid> </Box>
<Grid item xs={4}>
<StatsTable title="Earnings (Pre-Synchronization)" rows={data} />
<Button onClick={() => setEarningsOpen(true)}>More Earnings Info</Button>
</Grid>
</Grid>
<MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} /> <MoreStatsModal open={statsOpen} onClose={() => setStatsOpen(false)} sleeve={props.sleeve} />
<MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} /> <MoreEarningsModal open={earningsOpen} onClose={() => setEarningsOpen(false)} sleeve={props.sleeve} />
<TravelModal <TravelModal
@ -221,6 +200,8 @@ export function SleeveElem(props: IProps): React.ReactElement {
onClose={() => setAugmentationsOpen(false)} onClose={() => setAugmentationsOpen(false)}
sleeve={props.sleeve} 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>
<Box display="grid" sx={{ gridTemplateColumns: 'repeat(2, 1fr)' }}>
{player.sleeves.map((sleeve, i) => ( {player.sleeves.map((sleeve, i) => (
<SleeveElem key={i} rerender={rerender} sleeve={sleeve} /> <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}