Skip to content

Commit

Permalink
update combo editor
Browse files Browse the repository at this point in the history
  • Loading branch information
frzyc committed Nov 27, 2024
1 parent 640e0d9 commit 0d4a07e
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 73 deletions.
26 changes: 23 additions & 3 deletions libs/sr/db/src/Database/DataManagers/TeamDataManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IConditionalData } from '@genshin-optimizer/common/formula'
import {
notEmpty,
objKeyMap,
pruneOrPadArray,
range,
Expand Down Expand Up @@ -37,14 +38,19 @@ export type TeammateDatum = {

optConfigId?: string
}
export type Frame = {
tag: Tag
multiplier: number
description?: string
}
export interface Team {
name: string
description: string

lastEdit: number

// frames, store data as a "sparse 2d array"
frames: Array<Tag>
frames: Array<Frame>
conditionals: Array<{
sheet: Sheet
src: Member
Expand All @@ -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<TeammateDatum | undefined>
Expand Down Expand Up @@ -170,7 +180,17 @@ export class TeamDataManager extends DataManager<string, 'teams', Team, Team> {
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 = []
Expand Down
143 changes: 99 additions & 44 deletions libs/sr/page-team/src/ComboEditor.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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'
Expand All @@ -36,13 +44,21 @@ export function ComboEditor() {
>
{team.frames.map((frame, i) => (
<Combo
key={i + JSON.stringify(frame ?? {})}
tag={frame}
key={i}
frame={frame}
index={i}
setTarget={(read) =>
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)
})
}
/>
Expand All @@ -51,7 +67,13 @@ export function ComboEditor() {
<OptimizationTargetSelector
setOptTarget={(tag) =>
database.teams.set(teamId, (team) => {
team.frames = [...team.frames, tag]
team.frames = [
...team.frames,
{
multiplier: 1,
tag,
},
]
})
}
/>
Expand All @@ -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<Frame>): 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(() => {
Expand All @@ -94,55 +122,46 @@ function Combo({
: undefined,
})}
>
<ComboEditorModal index={index} show={open} onClose={onClose} />
<ComboEditorModal
frame={frame}
setFrame={setFrame}
index={index}
show={open}
onClose={onClose}
removeFrame={removeFrame}
/>
<CardActionArea onClick={handleClick}>
<Box sx={{ display: 'flex', gap: 1, alignItems: 'stretch', p: 1 }}>
<Typography>{index + 1}</Typography>
<Divider orientation="vertical" />
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<OptimizationTargetDisplay tag={tag} />
<OptimizationTargetDisplay tag={frame.tag} />
</Box>
</Box>

<Divider />
<Box sx={{ p: 1, display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography>X x {valueString(value, unit)}</Typography>
<Typography>
{frame.multiplier} x {valueString(value, unit)}
</Typography>
</Box>
</CardActionArea>
</CardThemed>
)
}

function RelicConditionals() {
const [show, onShow, onHide] = useBoolState()
return (
<>
{/* TODO: translation */}
<Button onClick={onShow}>Relic Conditionals</Button>
<ModalWrapper open={show} onClose={onHide}>
<RelicSheetsDisplay />
</ModalWrapper>
</>
)
}
function LightConeConditionals() {
const [show, onShow, onHide] = useBoolState()
return (
<>
{/* TODO: translation */}
<Button onClick={onShow}>Light Cone Conditionals</Button>
<ModalWrapper open={show} onClose={onHide}>
<LightConeSheetsDisplay />
</ModalWrapper>
</>
)
}
function ComboEditorModal({
index,
frame,
setFrame,
removeFrame,
show,
onClose,
}: {
frame: Frame
index: number
setFrame(frame: Partial<Frame>): void
removeFrame(): void
show: boolean
onClose: () => void
}) {
Expand All @@ -152,7 +171,7 @@ function ComboEditorModal({
onClose={onClose}
containerProps={{ maxWidth: 'xl' }}
>
<CardThemed>
<CardThemed bgt="dark">
{/* TODO: translation */}
<CardHeader
title={`Edit Combo ${index + 1}`}
Expand All @@ -164,8 +183,44 @@ function ComboEditorModal({
/>
<Divider />
<CardContent>
<RelicConditionals />
<LightConeConditionals />
<Stack spacing={1}>
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<NumberInputLazy
value={frame.multiplier}
onChange={(v) => setFrame({ multiplier: v })}
size="small"
InputProps={{
startAdornment: (
<InputAdornment position="start">Multi x </InputAdornment>
),
}}
/>
<OptimizationTargetSelector
optTarget={frame.tag}
setOptTarget={(tag) =>
setFrame({
tag,
})
}
/>
<Button
size="small"
color="error"
onClick={removeFrame}
startIcon={<Delete />}
>
Remove
</Button>
</Box>
<Box>
<Typography variant="h6">Relic Conditionals</Typography>
<RelicSheetsDisplay />
</Box>
<Box>
<Typography variant="h6">Lightcone Conditionals</Typography>
<LightConeSheetsDisplay />
</Box>
</Stack>
</CardContent>
</CardThemed>
</ModalWrapper>
Expand Down
19 changes: 7 additions & 12 deletions libs/sr/page-team/src/LightConeSheetsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<CardThemed bgt="dark">
<CardContent>
<Grid container columns={3} spacing={1}>
{cones.map((lcKey) => (
<Grid item xs={1} key={lcKey}>
<LightConeSheetDisplay lcKey={lcKey} />
</Grid>
))}
<Grid container columns={3} spacing={1}>
{cones.map((lcKey) => (
<Grid item xs={1} key={lcKey}>
<LightConeSheetDisplay lcKey={lcKey} />
</Grid>
</CardContent>
</CardThemed>
))}
</Grid>
)
}
19 changes: 7 additions & 12 deletions libs/sr/page-team/src/RelicSheetsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<CardThemed bgt="dark">
<CardContent>
<Grid container columns={3} spacing={1}>
{sets.map((setKey) => (
<Grid item xs={1} key={setKey}>
<RelicSheetDisplay setKey={setKey} />
</Grid>
))}
<Grid container columns={3} spacing={1}>
{sets.map((setKey) => (
<Grid item xs={1} key={setKey}>
<RelicSheetDisplay setKey={setKey} />
</Grid>
</CardContent>
</CardThemed>
))}
</Grid>
)
}
7 changes: 5 additions & 2 deletions libs/sr/solver/src/solver.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0d4a07e

Please sign in to comment.