Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: optimiser relic exclusion filter #481

Open
wants to merge 18 commits into
base: beta
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/components/RelicFilterBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ export default function RelicFilterBar(props) {
})
}

function generateRestrictedTags(arr) {
return arr.map((x) => {
return {
key: x,
display: Renderer.renderFilter(x),
}
})
}

function generateEquippedTags(arr) {
return arr.map((x) => {
return {
Expand All @@ -114,6 +123,7 @@ export default function RelicFilterBar(props) {

const gradeData = generateGradeTags([2, 3, 4, 5])
const verifiedData = generateVerifiedTags([true, false])
const restrictedData = generateRestrictedTags([false, true])
const setsData = generateImageTags(Object.values(Constants.SetsRelics).concat(Object.values(Constants.SetsOrnaments)).filter((x) => !UnreleasedSets[x]),
(x) => Assets.getSetImage(x, Constants.Parts.PlanarSphere), true)
const partsData = generateImageTags(Object.values(Constants.Parts), (x) => Assets.getPart(x), false)
Expand Down Expand Up @@ -238,6 +248,10 @@ export default function RelicFilterBar(props) {
<HeaderText>Verified</HeaderText>
<FilterRow name="verified" tags={verifiedData} flexBasis="15%" />
</Flex>
<Flex vertical flex={0.25}>
<HeaderText>Restricted</HeaderText>
<FilterRow name="restricted" tags={restrictedData} flexBasis="15%" />
</Flex>
<Flex vertical flex={0.25}>
<HeaderText>Equipped</HeaderText>
<FilterRow name="equipped" tags={equippedData} flexBasis="15%" />
Expand Down
36 changes: 35 additions & 1 deletion src/components/RelicModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from 'styled-components'
import { Button, Flex, Form, Image, InputNumber, Modal, Radio, Select, theme } from 'antd'
import { Button, Flex, Form, Image, InputNumber, Modal, Radio, Select, Switch, theme } from 'antd'
import React, { ReactElement, useEffect, useMemo, useState } from 'react'
import { Constants } from 'lib/constants'
import { HeaderText } from './HeaderText'
Expand All @@ -15,6 +15,7 @@ import { calculateUpgradeValues, RelicForm, RelicUpgradeValues, validateRelic }
import { CaretRightOutlined } from '@ant-design/icons'
import { FormInstance } from 'antd/es/form/hooks/useForm'
import { generateCharacterList } from 'lib/displayUtils'
import CharacterSelect from './optimizerTab/optimizerForm/CharacterSelect'

const { useToken } = theme

Expand Down Expand Up @@ -92,6 +93,8 @@ export default function RelicModal(props: {
const equippedBy: string = Form.useWatch('equippedBy', relicForm)
const [upgradeValues, setUpgradeValues] = useState<RelicUpgradeValues[]>([])

const [restrictionList, setRestrictionList] = useState<string[]>([])

useEffect(() => {
let defaultValues = {
grade: 5,
Expand All @@ -103,6 +106,8 @@ export default function RelicModal(props: {

const relic = props.selectedRelic
if (!relic || props.type != 'edit') {
relicForm.setFieldValue('restrictionEnabled', false)
relicForm.setFieldValue('restrictionList', [])
// Ignore
} else {
defaultValues = {
Expand All @@ -121,7 +126,10 @@ export default function RelicModal(props: {
substatValue2: renderSubstat(relic, 2).value,
substatType3: renderSubstat(relic, 3).stat,
substatValue3: renderSubstat(relic, 3).value,
restrictionEnabled: relic.restriction.enabled,
restrictionList: relic.restriction.list,
}
setRestrictionList(relic.restriction.list)
}
onValuesChange(defaultValues)
relicForm.setFieldsValue(defaultValues)
Expand Down Expand Up @@ -363,6 +371,32 @@ export default function RelicModal(props: {
<SubstatInput index={3} upgrades={upgradeValues} relicForm={relicForm} resetUpgradeValues={resetUpgradeValues} plusThree={plusThree} />
</Flex>
</Flex>
<Flex vertical>
<HeaderText>Restrict in Optimiser</HeaderText>
<Flex gap={10}>
<Form.Item name="restrictionList">
<CharacterSelect
selectStyle={{ width: 200 }}
value={restrictionList}
onChange={(x) => {
const excludedCharacterIds = Array.from(x || new Map())
.filter((entry) => entry[1] == true)
.map((entry) => entry[0])
relicForm.setFieldValue('restrictionList', excludedCharacterIds)
setRestrictionList(excludedCharacterIds)
}}
multipleSelect={true}
/>
</Form.Item>
<Form.Item name="restrictionEnabled">
<Switch
style={{ width: 90 }}
checkedChildren="Restricted"
unCheckedChildren="Free"
/>
</Form.Item>
</Flex>
</Flex>
</Flex>
</Modal>
</Form>
Expand Down
1 change: 1 addition & 0 deletions src/components/RelicPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const RelicPreview = ({
</Flex>
</Flex>
</Flex>
{relic.restriction && Renderer.renderFilter(relic.restriction.enabled)}
<img
style={{ height: 50, width: 50 }}
src={equippedBySrc}
Expand Down
48 changes: 48 additions & 0 deletions src/components/RelicsTab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,44 @@ EquippedFilter.displayName = 'EquippedFilter'
EquippedFilter.propTypes = {
filterChangedCallback: PropTypes.func,
}

const RestrictedFilter = forwardRef((props, ref) => {
const [model, setModel] = useState(null)

const isFilterActive = useCallback(() => {
return model != null && (model.restricted.length > 0)
}, [model])

// expose AG Grid Filter Lifecycle callbacks
useImperativeHandle(ref, () => {
return {
doesFilterPass(params) {
if ([0, 2].includes(model.restricted.length)) return true
if ((model.restricted[0] == params.data.restriction.enabled)) return true
return false
},

isFilterActive,

getModel() {
return model
},

setModel(model) {
setModel(model)
},
}
})

useEffect(() => {
props.filterChangedCallback()
}, [model, props])
})

RestrictedFilter.displayName = 'RestrictedFilter'
RestrictedFilter.propTypes = {
filterChangedCallback: PropTypes.func,
}

const PLOT_ALL = 'PLOT_ALL'
const PLOT_CUSTOM = 'PLOT_CUSTOM'
Expand Down Expand Up @@ -175,6 +213,10 @@ export default function RelicsTab() {
operator: 'OR',
}

filterModel.restricted = {
restricted: relicTabFilters.restricted,
}

filterModel.part = {
conditions: relicTabFilters.part.map((x) => ({
filterType: 'text',
Expand Down Expand Up @@ -295,6 +337,12 @@ export default function RelicsTab() {
}
},
},
{
field: 'restricted',
width: 55,
cellRenderer: Renderer.renderFilterCell,
filter: RestrictedFilter,
},
{ field: 'part', valueFormatter: Renderer.readablePart, width: 55, filter: 'agTextColumnFilter' },
{ field: 'enhance', width: 55, filter: 'agNumberColumnFilter' },
{ field: 'main.stat', valueFormatter: Renderer.readableStat, headerName: 'Main\nStat', width: 70, filter: 'agTextColumnFilter' },
Expand Down
5 changes: 2 additions & 3 deletions src/components/optimizerTab/optimizerForm/CharacterSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const CharacterSelect: React.FC<CharacterSelectProps> = ({ value, onChange, sele
const [currentFilters, setCurrentFilters] = useState(Utils.clone(defaultFilters))
const characterOptions = useMemo(() => Utils.generateCharacterOptions(), [])
const [selected, setSelected] = useState<Map<string, boolean>>(new Map())
const excludedRelicPotentialCharacters = window.store((s) => s.excludedRelicPotentialCharacters)

const labelledOptions: { value: string; label }[] = []
for (const option of characterOptions) {
Expand All @@ -58,7 +57,7 @@ const CharacterSelect: React.FC<CharacterSelectProps> = ({ value, onChange, sele
setTimeout(() => inputRef?.current?.focus(), 100)

if (multipleSelect) {
const newSelected = new Map<string, boolean>(excludedRelicPotentialCharacters.map((characterId: string) => [characterId, true]))
const newSelected = new Map<string, boolean>(value.map((characterId: string) => [characterId, true]))
setSelected(newSelected)
}
}
Expand Down Expand Up @@ -114,7 +113,7 @@ const CharacterSelect: React.FC<CharacterSelectProps> = ({ value, onChange, sele
allowClear
maxTagCount={0}
maxTagPlaceholder={() => (
<span>{excludedRelicPotentialCharacters.length ? `${excludedRelicPotentialCharacters.length} characters excluded` : 'All characters enabled'}</span>
<span>{value.length ? `${value.length} characters excluded` : 'All characters enabled'}</span>
)}
onClear={() => {
if (onChange) onChange(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ const OptimizerOptionsDisplay = (): JSX.Element => {
<Text>Keep current relics</Text>
</Flex>

<Flex align="center">
<Form.Item name="ignoreRestrictions" valuePropName="checked">
<Switch
checkedChildren={<CheckOutlined />}
unCheckedChildren={<CloseOutlined />}
style={{ width: 45, marginRight: 5 }}
/>
</Form.Item>
<Text>Ignore wearer restrictions</Text>
</Flex>

<Flex gap={optimizerTabDefaultGap} style={{ marginTop: 10 }}>
<Flex vertical gap={2}>
<HeaderText>
Expand Down
13 changes: 12 additions & 1 deletion src/lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ window.store = create((set) => ({
subStats: [],
grade: [],
verified: [],
restricted: [],
equipped: [],
},
characterTabFilters: {
Expand Down Expand Up @@ -410,6 +411,12 @@ export const DB = {
} else {
relic.equippedBy = undefined
}
if (!relic.restriction) {
relic.restriction = {
enabled: false,
list: [],
}
}
}

if (x.scoringMetadataOverrides) {
Expand Down Expand Up @@ -733,14 +740,17 @@ export const DB = {
found.equippedBy = newRelic.equippedBy
newRelic = found
}
// Fix metadata if field not present
if (!found.restriction) found.restriction = { enabled: false, list: [] }

// Save the old relic because it may have edited speed values, delete the hash to prevent duplicates
replacementRelics.push(found)
stableRelicId = found.id
delete oldRelicHashes[hash]
} else {
// No match found - save the new relic
// No match found - add the restriction field - save the new relic
stableRelicId = newRelic.id
newRelic.restriction = { enabled: false, list: [] }
replacementRelics.push(newRelic)
}

Expand Down Expand Up @@ -884,6 +894,7 @@ export const DB = {
match.verified = true
updatedOldRelics.push(match)
} else {
newRelic.restriction = { enabled: false, list: [] }
oldRelics.push(newRelic)

newRelic.verified = true
Expand Down
19 changes: 12 additions & 7 deletions src/lib/hint.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,24 @@ export const Hint = {
content: (
<Flex vertical gap={10}>
<p>
<strong>Character priority filter</strong>
<strong>Allow equipped relics</strong>
{' '}
- When this option is enabled, the character may only steal relics from lower priority characters. The optimizer will ignore relics equipped by higher priority characters on the list. Change character ranks from the priority selector or by dragging them on the Characters page.
- When enabled, the optimizer will allow using currently equipped by a character for the search. Otherwise equipped relics are excluded
</p>
<p>
<strong>Boost main stat</strong>
<strong>Character priority filter</strong>
{' '}
- Calculates relic mains stats as if they were this level (or their max if they can't reach this level) if they are currently below it. Substats are not changed accordingly, so builds with lower level relics may be stronger once you level them.
- When this option is enabled, the character may only steal relics from lower priority characters. The optimizer will ignore relics equipped by higher priority characters on the list. Change character ranks from the priority selector or by dragging them on the Characters page.
</p>
<p>
<strong>Keep current relics</strong>
{' '}
- The character must use its currently equipped items, and the optimizer will try to fill in empty slots
</p>
<p>
<strong>Include equipped relics</strong>
<strong>Ignore wearer restrictions</strong>
{' '}
- When enabled, the optimizer will allow using currently equipped by a character for the search. Otherwise equipped relics are excluded
- When this option is enabled, the character will ignore the wearer restrictions of relics
</p>
<p>
<strong>Priority</strong>
Expand All @@ -158,10 +158,15 @@ export const Hint = {
- Select specific characters' equipped relics to exclude for the search. This setting overrides the priority filter
</p>
<p>
<strong>Enhance / grade</strong>
<strong>Enhance / Rarity</strong>
{' '}
- Select the minimum enhance to search for and minimum stars for relics to include
</p>
<p>
<strong>Boost main stat</strong>
{' '}
- Calculates relic mains stats as if they were this level (or their max if they can't reach this level) if they are currently below it. Substats are not changed accordingly, so builds with lower level relics may be stronger once you level them.
</p>
</Flex>
),
}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/optimizer/optimizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export const Optimizer = {
relics = RelicFilters.applyRankFilter(request, relics)
relics = RelicFilters.applyExcludeFilter(request, relics)

relics = RelicFilters.applyRestrictionFilter(request, relics)

// Pre-split filters
const preFilteredRelicsByPart = RelicFilters.splitRelicsByPart(relics)

Expand All @@ -62,7 +64,7 @@ export const Optimizer = {
return [relics, preFilteredRelicsByPart]
},

optimize: function(request) {
optimize: function (request) {
CANCEL = false

window.store.getState().setPermutationsSearched(0)
Expand Down
6 changes: 6 additions & 0 deletions src/lib/relicFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ export const RelicFilters = {
return ret
},

applyRestrictionFilter: (request, relics) => {
if (request.ignoreRestrictions) return relics
const ret = relics.filter((x) => !(x.restriction.list).includes(request.characterId) || !x.restriction.enabled)
return ret
},

applyGradeFilter: (request, relics) => {
return relics.filter((x) => x.grade ? x.grade >= request.grade : true)
},
Expand Down
6 changes: 6 additions & 0 deletions src/lib/relicModalController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export type RelicForm = {
substatValue2: number
substatValue3: number
equippedBy: string
restrictionEnabled: boolean
restrictionList: string[]
}

export function validateRelic(relicForm: RelicForm): Relic | void {
Expand Down Expand Up @@ -170,6 +172,10 @@ export function validateRelic(relicForm: RelicForm): Relic | void {
}
relic.substats = substats
RelicAugmenter.augment(relic)
relic.restriction = {
enabled: relicForm.restrictionEnabled,
list: relicForm.restrictionList,
}

return relic
}
Expand Down
Loading