From 0d4a07ef2689121d9df952fbf8cf64ce047378f0 Mon Sep 17 00:00:00 2001 From: frzyc Date: Tue, 26 Nov 2024 21:52:25 -0500 Subject: [PATCH] update combo editor --- .../Database/DataManagers/TeamDataManager.ts | 26 +++- libs/sr/page-team/src/ComboEditor.tsx | 143 ++++++++++++------ .../page-team/src/LightConeSheetsDisplay.tsx | 19 +-- libs/sr/page-team/src/RelicSheetsDisplay.tsx | 19 +-- libs/sr/solver/src/solver.ts | 7 +- 5 files changed, 141 insertions(+), 73 deletions(-) diff --git a/libs/sr/db/src/Database/DataManagers/TeamDataManager.ts b/libs/sr/db/src/Database/DataManagers/TeamDataManager.ts index e3f62086c3..07443797e1 100644 --- a/libs/sr/db/src/Database/DataManagers/TeamDataManager.ts +++ b/libs/sr/db/src/Database/DataManagers/TeamDataManager.ts @@ -1,5 +1,6 @@ import type { IConditionalData } from '@genshin-optimizer/common/formula' import { + notEmpty, objKeyMap, pruneOrPadArray, range, @@ -37,6 +38,11 @@ export type TeammateDatum = { optConfigId?: string } +export type Frame = { + tag: Tag + multiplier: number + description?: string +} export interface Team { name: string description: string @@ -44,7 +50,7 @@ export interface Team { lastEdit: number // frames, store data as a "sparse 2d array" - frames: Array + frames: Array conditionals: Array<{ sheet: Sheet src: Member @@ -56,7 +62,11 @@ export interface Team { tag: Tag values: number[] // should be the same length as `frames` }> - statConstraints: Array<{ tag: Tag; values: number[]; isMaxs: boolean[] }> + statConstraints: Array<{ + tag: Tag + values: number[] // should be the same length as `frames` + isMaxs: boolean[] // should be the same length as `frames` + }> // TODO enemy base stats teamMetadata: Array @@ -170,7 +180,17 @@ export class TeamDataManager extends DataManager { if (typeof lastEdit !== 'number') lastEdit = Date.now() if (!Array.isArray(frames)) frames = [] - frames = frames.filter(validateTag) + frames = frames + .map((f) => { + const { tag } = f + let { multiplier, description } = f + if (!validateTag(tag)) return undefined + if (typeof multiplier !== 'number' || multiplier === 0) multiplier = 1 + if (typeof description !== 'string') description = undefined + + return { tag, multiplier, description } + }) + .filter(notEmpty) const framesLength = frames.length if (!framesLength) { conditionals = [] diff --git a/libs/sr/page-team/src/ComboEditor.tsx b/libs/sr/page-team/src/ComboEditor.tsx index c96578e781..797e7b9e24 100644 --- a/libs/sr/page-team/src/ComboEditor.tsx +++ b/libs/sr/page-team/src/ComboEditor.tsx @@ -1,11 +1,17 @@ import { useBoolState } from '@genshin-optimizer/common/react-util' -import { CardThemed, ModalWrapper } from '@genshin-optimizer/common/ui' +import { + CardThemed, + ModalWrapper, + NumberInputLazy, +} from '@genshin-optimizer/common/ui' import { getUnitStr, valueString } from '@genshin-optimizer/common/util' import { TagContext } from '@genshin-optimizer/pando/ui-sheet' +import type { Frame } from '@genshin-optimizer/sr/db' import { useDatabaseContext } from '@genshin-optimizer/sr/db-ui' -import { Read, type Tag } from '@genshin-optimizer/sr/formula' +import { Read } from '@genshin-optimizer/sr/formula' import { useSrCalcContext } from '@genshin-optimizer/sr/ui' import CloseIcon from '@mui/icons-material/Close' +import Delete from '@mui/icons-material/Delete' import { Box, Button, @@ -14,9 +20,11 @@ import { CardHeader, Divider, IconButton, + InputAdornment, + Stack, Typography, } from '@mui/material' -import { useCallback, useContext } from 'react' +import { useCallback, useContext, useMemo } from 'react' import { PresetContext, useTeamContext } from './context' import { LightConeSheetsDisplay } from './LightConeSheetsDisplay' import { OptimizationTargetDisplay } from './Optimize/OptimizationTargetDisplay' @@ -36,13 +44,21 @@ export function ComboEditor() { > {team.frames.map((frame, i) => ( + setFrame={(frame) => database.teams.set(teamId, (team) => { team.frames = [...team.frames] - team.frames[i] = read + team.frames[i] = { + ...team.frames[i], + ...frame, + } + }) + } + removeFrame={() => + database.teams.set(teamId, (team) => { + team.frames = team.frames.filter((_, index) => index !== i) }) } /> @@ -51,7 +67,13 @@ export function ComboEditor() { database.teams.set(teamId, (team) => { - team.frames = [...team.frames, tag] + team.frames = [ + ...team.frames, + { + multiplier: 1, + tag, + }, + ] }) } /> @@ -60,19 +82,25 @@ export function ComboEditor() { ) } function Combo({ - tag, + frame, index, - setTarget, + setFrame, + removeFrame, }: { - tag: Tag + frame: Frame index: number - setTarget(tag: Tag): void + setFrame(frame: Partial): void + removeFrame(): void }) { const { presetIndex, setPresetIndex } = useContext(PresetContext) const calc = useSrCalcContext() const tagcontext = useContext(TagContext) - const value = calc?.withTag(tagcontext).compute(new Read(tag, 'sum')).val ?? 0 - const unit = getUnitStr(tag.q ?? '') + const value = useMemo( + () => + calc?.withTag(tagcontext).compute(new Read(frame.tag, 'sum')).val ?? 0, + [calc, frame.tag, tagcontext] + ) + const unit = getUnitStr(frame.tag.q ?? '') const [open, onOpen, onClose] = useBoolState() const handleClick = useCallback(() => { @@ -94,55 +122,46 @@ function Combo({ : undefined, })} > - + {index + 1} - + - X x {valueString(value, unit)} + + {frame.multiplier} x {valueString(value, unit)} + ) } -function RelicConditionals() { - const [show, onShow, onHide] = useBoolState() - return ( - <> - {/* TODO: translation */} - - - - - - ) -} -function LightConeConditionals() { - const [show, onShow, onHide] = useBoolState() - return ( - <> - {/* TODO: translation */} - - - - - - ) -} function ComboEditorModal({ index, + frame, + setFrame, + removeFrame, show, onClose, }: { + frame: Frame index: number + setFrame(frame: Partial): void + removeFrame(): void show: boolean onClose: () => void }) { @@ -152,7 +171,7 @@ function ComboEditorModal({ onClose={onClose} containerProps={{ maxWidth: 'xl' }} > - + {/* TODO: translation */} - - + + + setFrame({ multiplier: v })} + size="small" + InputProps={{ + startAdornment: ( + Multi x + ), + }} + /> + + setFrame({ + tag, + }) + } + /> + + + + Relic Conditionals + + + + Lightcone Conditionals + + + diff --git a/libs/sr/page-team/src/LightConeSheetsDisplay.tsx b/libs/sr/page-team/src/LightConeSheetsDisplay.tsx index d3be76a4c5..8cd2978a4a 100644 --- a/libs/sr/page-team/src/LightConeSheetsDisplay.tsx +++ b/libs/sr/page-team/src/LightConeSheetsDisplay.tsx @@ -1,22 +1,17 @@ -import { CardThemed } from '@genshin-optimizer/common/ui' import type { LightConeKey } from '@genshin-optimizer/sr/consts' -import { CardContent, Grid } from '@mui/material' +import { Grid } from '@mui/material' import { LightConeSheetDisplay } from './LightConeSheetDisplay' // TODO: hardcoded to LightConeSheetsDisplay.tsx for now. Can be expanded to all lightcones with sheets const cones: LightConeKey[] = ['PastSelfInMirror'] as const export function LightConeSheetsDisplay() { return ( - - - - {cones.map((lcKey) => ( - - - - ))} + + {cones.map((lcKey) => ( + + - - + ))} + ) } diff --git a/libs/sr/page-team/src/RelicSheetsDisplay.tsx b/libs/sr/page-team/src/RelicSheetsDisplay.tsx index 4346a81698..78816d2c19 100644 --- a/libs/sr/page-team/src/RelicSheetsDisplay.tsx +++ b/libs/sr/page-team/src/RelicSheetsDisplay.tsx @@ -1,22 +1,17 @@ -import { CardThemed } from '@genshin-optimizer/common/ui' import type { RelicSetKey } from '@genshin-optimizer/sr/consts' -import { CardContent, Grid } from '@mui/material' +import { Grid } from '@mui/material' import { RelicSheetDisplay } from './RelicSheetDisplay' // TODO: hardcoded to RelicSheetsDisplay.tsx for now. Can be expanded to all relic sets with sheets const sets: RelicSetKey[] = ['WatchmakerMasterOfDreamMachinations'] as const export function RelicSheetsDisplay() { return ( - - - - {sets.map((setKey) => ( - - - - ))} + + {sets.map((setKey) => ( + + - - + ))} + ) } diff --git a/libs/sr/solver/src/solver.ts b/libs/sr/solver/src/solver.ts index dac0a10c84..c16b1dedcb 100644 --- a/libs/sr/solver/src/solver.ts +++ b/libs/sr/solver/src/solver.ts @@ -1,4 +1,4 @@ -import { detach, sum } from '@genshin-optimizer/pando/engine' +import { detach, prod, sum } from '@genshin-optimizer/pando/engine' import type { CharacterKey, RelicSlotKey } from '@genshin-optimizer/sr/consts' import { allLightConeKeys, allRelicSetKeys } from '@genshin-optimizer/sr/consts' import type { @@ -134,7 +134,10 @@ export class Solver { // team sum( ...this.frames.map((frame, i) => - new Read(frame, 'sum').with('preset', `preset${i}` as Preset) + prod( + frame.multiplier, + new Read(frame.tag, 'sum').with('preset', `preset${i}` as Preset) + ) ) ), // stat filters