mirror of
https://github.com/Kvan7/Exiled-Exchange-2.git
synced 2026-09-21 12:15:47 +00:00
preset system
This commit is contained in:
@@ -49,6 +49,7 @@ export interface ParsedItem {
|
||||
influences: ItemInfluence[]
|
||||
logbookAreaMods?: ParsedModifier[][]
|
||||
isSynthesised?: boolean
|
||||
isFractured?: boolean
|
||||
isVeiled?: boolean
|
||||
statsByType: StatCalculated[]
|
||||
newMods: ParsedModifier[]
|
||||
|
||||
@@ -64,6 +64,7 @@ const parsers: Array<ParserFn | { virtual: VirtualParserFn }> = [
|
||||
parseModifiers, // implicit
|
||||
parseModifiers, // explicit
|
||||
{ virtual: transformToLegacyModifiers },
|
||||
{ virtual: parseFractured },
|
||||
{ virtual: parseBlightedMap },
|
||||
{ virtual: pickCorrectVariant }
|
||||
]
|
||||
@@ -223,6 +224,12 @@ function parseBlightedMap (item: ParsedItem) {
|
||||
}
|
||||
}
|
||||
|
||||
function parseFractured (item: ParserState) {
|
||||
if (item.newMods.some(mod => mod.info.type === ModifierType.Fractured)) {
|
||||
item.isFractured = true
|
||||
}
|
||||
}
|
||||
|
||||
function pickCorrectVariant (item: ParserState) {
|
||||
if (!item.info.disc) return
|
||||
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<template>
|
||||
<div v-if="show" class="p-4 layout-column min-h-0">
|
||||
<filter-name
|
||||
ref="nameFilter"
|
||||
:filters="itemFilters"
|
||||
:item="item" />
|
||||
<price-prediction v-if="showPredictedPrice" class="mb-4"
|
||||
:item="item" />
|
||||
<price-trend
|
||||
<price-trend v-else
|
||||
:item="item"
|
||||
:filters="itemFilters"
|
||||
@filter-item-base="applyItemBaseFilter" />
|
||||
:filters="itemFilters" />
|
||||
<filters-block
|
||||
ref="filtersBlock"
|
||||
:filters="itemFilters"
|
||||
:stats="itemStats"
|
||||
:item="item"
|
||||
@submit="interactedOnce = true" />
|
||||
:presets="presets"
|
||||
@preset="selectPreset"
|
||||
@submit="doSearch = true" />
|
||||
<trade-listing
|
||||
v-if="tradeAPI === 'trade' && interactedOnce"
|
||||
v-if="tradeAPI === 'trade' && doSearch"
|
||||
ref="tradeService"
|
||||
:filters="itemFilters"
|
||||
:stats="itemStats"
|
||||
:item="item" />
|
||||
<trade-bulk
|
||||
v-if="tradeAPI === 'bulk' && interactedOnce"
|
||||
v-if="tradeAPI === 'bulk' && doSearch"
|
||||
ref="tradeService"
|
||||
:filters="itemFilters"
|
||||
:item="item" />
|
||||
<div v-if="!interactedOnce" @mouseenter="handleSearchMouseenter">
|
||||
<button class="btn" @click="interactedOnce = true">{{ t('Search') }}</button>
|
||||
<div v-if="!doSearch" @mouseenter="handleSearchMouseenter">
|
||||
<button class="btn" @click="doSearch = true">{{ t('Search') }}</button>
|
||||
</div>
|
||||
<stack-value :filters="itemFilters" :item="item"/>
|
||||
<div v-if="showSupportLinks" class="mt-auto border border-dashed p-2">
|
||||
@@ -42,7 +42,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, watch, ref, nextTick, computed, Ref, ComponentPublicInstance } from 'vue'
|
||||
import { defineComponent, PropType, watch, ref, nextTick, computed, ComponentPublicInstance } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ItemRarity, ItemCategory, ParsedItem } from '@/parser'
|
||||
import TradeListing from './trade/TradeListing.vue'
|
||||
@@ -50,14 +50,13 @@ import TradeBulk from './trade/TradeBulk.vue'
|
||||
import { apiToSatisfySearch } from './trade/common'
|
||||
import PriceTrend from './trends/PriceTrend.vue'
|
||||
import FiltersBlock from './filters/FiltersBlock.vue'
|
||||
import { createFilters } from './filters/create-item-filters'
|
||||
import { initUiModFilters } from './filters/create-stat-filters'
|
||||
import { createPresets } from './filters/create-presets'
|
||||
import PricePrediction from './price-prediction/PricePrediction.vue'
|
||||
import StackValue from './stack-value/StackValue.vue'
|
||||
import FilterName from './filters/FilterName.vue'
|
||||
import { CATEGORY_TO_TRADE_ID } from './trade/pathofexile-trade'
|
||||
import { AppConfig } from '@/web/Config'
|
||||
import { FilterTag, ItemFilters, StatFilter } from './filters/interfaces'
|
||||
import { FilterPreset } from './filters/interfaces'
|
||||
import { PriceCheckWidget } from '../overlay/interfaces'
|
||||
import { selected as selectedLeague, isPublic as isPublicLeague } from '@/web/background/Leagues'
|
||||
|
||||
@@ -87,34 +86,31 @@ export default defineComponent({
|
||||
setup (props) {
|
||||
const widget = computed(() => AppConfig<PriceCheckWidget>('price-check')!)
|
||||
|
||||
const itemFilters = ref<ItemFilters>(null!)
|
||||
const itemStats = ref<StatFilter[]>(null!)
|
||||
const interactedOnce = ref(false)
|
||||
const presets = ref<{ active: string, presets: FilterPreset[] }>(null!)
|
||||
const itemFilters = computed(() => presets.value.presets.find(preset => preset.id === presets.value.active)!.filters)
|
||||
const itemStats = computed(() => presets.value.presets.find(preset => preset.id === presets.value.active)!.stats)
|
||||
const doSearch = ref(false)
|
||||
const tradeAPI = ref<'trade' | 'bulk'>('bulk')
|
||||
|
||||
// TradeListing.vue OR TradeBulk.vue
|
||||
const tradeService = ref<{ execSearch(): void } | null>(null)
|
||||
// FilterName.vue
|
||||
const nameFilter = ref<{ toggleAccuracy(): void }>(null!)
|
||||
// FiltersBlock.vue
|
||||
const filtersBlock = ref<ComponentPublicInstance<{}, { showHidden: Ref<boolean> }>>(null!)
|
||||
const filtersBlock = ref<ComponentPublicInstance<{}, {}>>(null!)
|
||||
|
||||
watch(() => props.item, (item) => {
|
||||
itemFilters.value = createFilters(item, {
|
||||
presets.value = createPresets(item, {
|
||||
league: selectedLeague.value!,
|
||||
chaosPriceThreshold: widget.value.chaosPriceThreshold,
|
||||
collapseListings: widget.value.collapseListings,
|
||||
activateStockFilter: widget.value.activateStockFilter
|
||||
})
|
||||
itemStats.value = initUiModFilters(item, {
|
||||
activateStockFilter: widget.value.activateStockFilter,
|
||||
searchStatRange: widget.value.searchStatRange
|
||||
})
|
||||
|
||||
if ((!props.advancedCheck && !widget.value.smartInitialSearch) ||
|
||||
(props.advancedCheck && !widget.value.lockedInitialSearch)) {
|
||||
interactedOnce.value = false
|
||||
doSearch.value = false
|
||||
} else {
|
||||
interactedOnce.value = Boolean(
|
||||
doSearch.value = Boolean(
|
||||
(item.rarity === ItemRarity.Unique) ||
|
||||
(item.category === ItemCategory.Map) ||
|
||||
(item.category === ItemCategory.HeistBlueprint) ||
|
||||
@@ -125,8 +121,8 @@ export default defineComponent({
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => [props.item, interactedOnce.value], () => {
|
||||
if (interactedOnce.value === false) return
|
||||
watch(() => [props.item, doSearch.value], () => {
|
||||
if (doSearch.value === false) return
|
||||
|
||||
tradeAPI.value = apiToSatisfySearch(props.item, itemStats.value, itemFilters.value)
|
||||
|
||||
@@ -138,13 +134,13 @@ export default defineComponent({
|
||||
})
|
||||
}, { deep: false, immediate: true })
|
||||
|
||||
watch(() => [props.item, interactedOnce.value, itemStats.value, itemFilters.value], (curr, prev) => {
|
||||
watch(() => [props.item, doSearch.value, itemStats.value, itemFilters.value], (curr, prev) => {
|
||||
const cItem = curr[0]; const pItem = prev[0]
|
||||
const cIntaracted = curr[1]; const pIntaracted = prev[1]
|
||||
|
||||
if (cItem === pItem && cIntaracted === true && pIntaracted === true) {
|
||||
// force user to press Search button on change
|
||||
interactedOnce.value = false
|
||||
doSearch.value = false
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
@@ -154,7 +150,7 @@ export default defineComponent({
|
||||
|
||||
if (cItem === pItem && cTrade !== pTrade) {
|
||||
nextTick(() => {
|
||||
interactedOnce.value = true
|
||||
doSearch.value = true
|
||||
})
|
||||
}
|
||||
}, { deep: false })
|
||||
@@ -164,12 +160,15 @@ export default defineComponent({
|
||||
AppConfig().language !== 'en' ||
|
||||
!isPublicLeague.value) return false
|
||||
|
||||
if (presets.value.active === 'Base item') return false
|
||||
|
||||
return props.item.rarity === ItemRarity.Rare &&
|
||||
props.item.category !== ItemCategory.Map &&
|
||||
props.item.category !== ItemCategory.CapturedBeast &&
|
||||
props.item.category !== ItemCategory.HeistContract &&
|
||||
props.item.category !== ItemCategory.HeistBlueprint &&
|
||||
props.item.category !== ItemCategory.Invitation &&
|
||||
props.item.info.refName !== 'Expedition Logbook' &&
|
||||
!props.item.isUnidentified
|
||||
})
|
||||
|
||||
@@ -179,28 +178,9 @@ export default defineComponent({
|
||||
props.item.info.unique == null)
|
||||
})
|
||||
|
||||
async function applyItemBaseFilter () {
|
||||
for (const stat of itemStats.value) {
|
||||
// TODO or 'Syntesized'
|
||||
if (stat.tag === FilterTag.Fractured && stat.hidden) {
|
||||
stat.disabled = false
|
||||
filtersBlock.value.showHidden = true
|
||||
} else {
|
||||
stat.disabled = true
|
||||
}
|
||||
}
|
||||
await nextTick()
|
||||
|
||||
itemFilters.value.itemLevel!.disabled = false
|
||||
if (itemFilters.value.influences) {
|
||||
itemFilters.value.influences[0].disabled = false
|
||||
}
|
||||
nameFilter.value.toggleAccuracy()
|
||||
}
|
||||
|
||||
function handleSearchMouseenter (e: MouseEvent) {
|
||||
if ((filtersBlock.value.$el as HTMLElement).contains(e.relatedTarget as HTMLElement)) {
|
||||
interactedOnce.value = true
|
||||
doSearch.value = true
|
||||
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
document.activeElement.blur()
|
||||
@@ -209,7 +189,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const showSupportLinks = ref(false)
|
||||
watch(() => [props.item, interactedOnce.value], ([cItem, cInteracted], [pItem]) => {
|
||||
watch(() => [props.item, doSearch.value], ([cItem, cInteracted], [pItem]) => {
|
||||
if (_showSupportLinksCounter >= 13 && (!cInteracted || tradeAPI.value === 'bulk')) {
|
||||
showSupportLinks.value = true
|
||||
_showSupportLinksCounter = 0
|
||||
@@ -227,16 +207,19 @@ export default defineComponent({
|
||||
t,
|
||||
itemFilters,
|
||||
itemStats,
|
||||
interactedOnce,
|
||||
doSearch,
|
||||
tradeAPI,
|
||||
tradeService,
|
||||
nameFilter,
|
||||
filtersBlock,
|
||||
showPredictedPrice,
|
||||
show,
|
||||
applyItemBaseFilter,
|
||||
handleSearchMouseenter,
|
||||
showSupportLinks
|
||||
showSupportLinks,
|
||||
presets: computed(() => presets.value.presets.map(preset =>
|
||||
({ id: preset.id, active: (preset.id === presets.value.active) }))),
|
||||
selectPreset (id: string) {
|
||||
presets.value.active = id
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
:filter="filters.veiled" text="Veiled" />
|
||||
<filter-btn-logical v-if="filters.mirrored" active
|
||||
:filter="filters.mirrored" :text="filters.mirrored.disabled ? 'Not Mirrored' : 'Mirrored'" />
|
||||
<filter-btn-logical v-if="stats.length"
|
||||
<filter-btn-logical v-if="hasStats"
|
||||
:collapse="statsVisibility.disabled"
|
||||
:filter="statsVisibility"
|
||||
:active="totalSelectedMods > 0"
|
||||
@@ -44,13 +44,25 @@
|
||||
: t('Stats ignored')"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!statsVisibility.disabled && stats.length" class="my-4">
|
||||
<div v-if="!statsVisibility.disabled && hasStats" class="mb-4" :class="(presets.length > 1) ? 'mt-1' : 'mt-4'">
|
||||
<div class="flex" v-if="presets.length > 1">
|
||||
<div class="w-5 border-b border-gray-700" />
|
||||
<div class="flex divide-x border-gray-700 border-t border-l border-r rounded-t overflow-hidden">
|
||||
<button v-for="preset in presets"
|
||||
:class="[$style.presetBtn, { [$style.active]: preset.active }]"
|
||||
@click="selectPreset(preset.id)"
|
||||
>{{ t(preset.id) }}</button>
|
||||
</div>
|
||||
<div class="flex-1 border-b border-gray-700" />
|
||||
</div>
|
||||
<form @submit.prevent="handleStatsSubmit">
|
||||
<filter-modifier v-for="filter of shownStats" :key="filter.tag + '/' + filter.text"
|
||||
<filter-modifier v-for="filter of filteredStats" :key="filter.tag + '/' + filter.text"
|
||||
:filter="filter"
|
||||
:item="item"
|
||||
:show-sources="showFilterSources"
|
||||
@submit="handleStatsSubmit" />
|
||||
<div v-if="!filteredStats.length && !item.unknownModifiers.length"
|
||||
class="border-b border-gray-700 py-2">{{ t('No relevant stats were found') }}</div>
|
||||
<unknown-modifier v-for="stat of item.unknownModifiers" :key="stat.type + '/' + stat.text"
|
||||
:stat="stat" />
|
||||
<input type="submit" class="hidden" />
|
||||
@@ -58,7 +70,7 @@
|
||||
<div class="flex gap-x-4">
|
||||
<button @click="statsVisibility.disabled = !statsVisibility.disabled" class="bg-gray-700 px-2 py-1 text-gray-400 leading-none rounded-b w-40"
|
||||
>{{ t('Collapse') }} <i class="fas fa-chevron-up pl-1 text-xs text-gray-600"></i></button>
|
||||
<ui-toggle v-if="shownStats.length != stats.length"
|
||||
<ui-toggle v-if="filteredStats.length != stats.length"
|
||||
v-model="showHidden" class="text-gray-400 pt-2">{{ t('Hidden') }}</ui-toggle>
|
||||
<!-- <ui-toggle
|
||||
v-model="showFilterSources" class="ml-auto text-gray-400 pt-2">{{ t('Mods') }}</ui-toggle> -->
|
||||
@@ -75,11 +87,11 @@ import FilterBtnNumeric from './FilterBtnNumeric.vue'
|
||||
import FilterBtnLogical from './FilterBtnLogical.vue'
|
||||
import UnknownModifier from './UnknownModifier.vue'
|
||||
import { ItemFilters, StatFilter } from './interfaces'
|
||||
import { ParsedItem } from '@/parser'
|
||||
import { ParsedItem, ItemRarity } from '@/parser'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FiltersBlock',
|
||||
emits: ['submit'],
|
||||
emits: ['submit', 'preset'],
|
||||
components: {
|
||||
FilterModifier,
|
||||
FilterBtnNumeric,
|
||||
@@ -87,6 +99,10 @@ export default defineComponent({
|
||||
UnknownModifier
|
||||
},
|
||||
props: {
|
||||
presets: {
|
||||
type: Array as PropType<Array<{ id: string, active: boolean }>>,
|
||||
required: true
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<ItemFilters>,
|
||||
required: true
|
||||
@@ -120,21 +136,44 @@ export default defineComponent({
|
||||
totalSelectedMods: computed(() => {
|
||||
return props.stats.filter(stat => !stat.disabled).length
|
||||
}),
|
||||
shownStats: computed(() => {
|
||||
filteredStats: computed(() => {
|
||||
if (showHidden.value) {
|
||||
return props.stats.filter(s => s.hidden)
|
||||
} else {
|
||||
return props.stats.filter(s => !s.hidden)
|
||||
}
|
||||
}),
|
||||
hasStats: computed(() =>
|
||||
props.stats.length ||
|
||||
(props.item.unknownModifiers.length && props.item.rarity === ItemRarity.Unique) ||
|
||||
props.presets.length > 1),
|
||||
handleStatsSubmit () {
|
||||
ctx.emit('submit')
|
||||
},
|
||||
selectPreset (id: string) {
|
||||
ctx.emit('preset', id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="postcss" module>
|
||||
.presetBtn {
|
||||
@apply border-gray-700 bg-gray-800;
|
||||
@apply px-2;
|
||||
min-width: 3rem;
|
||||
|
||||
&:hover {
|
||||
@apply bg-gray-700;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(to bottom, theme('colors.gray.900'), theme('colors.gray.800'));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
{
|
||||
"ru": {
|
||||
@@ -142,7 +181,11 @@ export default defineComponent({
|
||||
"Collapse": "Свернуть",
|
||||
"Stats ignored": "Св-ва не важны",
|
||||
"{0} of {1}, stats": "Св-ва: {0} из {1}",
|
||||
"Mods": "Моды"
|
||||
"Mods": "Моды",
|
||||
"No relevant stats were found": "Подходящие свойства не найдены",
|
||||
|
||||
"Pseudo": "Псевдо",
|
||||
"Base item": "База предмета"
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ItemFilters } from './interfaces'
|
||||
import { ParsedItem, ItemCategory, ItemRarity } from '@/parser'
|
||||
import { ParsedItem, ItemCategory, ItemRarity, ItemInfluence } from '@/parser'
|
||||
import { tradeTag, PERMANENT_LEAGUES } from '../trade/common'
|
||||
import { ModifierType } from '@/parser/modifiers'
|
||||
import { ITEM_BY_REF } from '@/assets/data'
|
||||
@@ -14,6 +14,7 @@ export function createFilters (
|
||||
chaosPriceThreshold: number
|
||||
collapseListings: 'app' | 'api'
|
||||
activateStockFilter: boolean
|
||||
exact: boolean
|
||||
}
|
||||
): ItemFilters {
|
||||
const filters: ItemFilters = {
|
||||
@@ -154,7 +155,7 @@ export function createFilters (
|
||||
if (item.category && CATEGORY_TO_TRADE_ID.has(item.category)) {
|
||||
filters.searchRelaxed = {
|
||||
category: item.category,
|
||||
disabled: (item.rarity !== ItemRarity.Rare)
|
||||
disabled: opts.exact
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,11 +199,21 @@ export function createFilters (
|
||||
filters.mirrored = { disabled: false }
|
||||
}
|
||||
|
||||
if (item.influences.length) {
|
||||
filters.influences = item.influences.map(influecne => ({
|
||||
value: influecne,
|
||||
disabled: true
|
||||
}))
|
||||
if (item.influences.length && item.influences.length <= 2) {
|
||||
if (opts.exact) {
|
||||
filters.influences = item.influences.map(influecne => ({
|
||||
value: influecne,
|
||||
disabled: false
|
||||
}))
|
||||
} else if (item.influences.length === 1 && (
|
||||
item.influences[0] === ItemInfluence.Shaper ||
|
||||
item.influences[0] === ItemInfluence.Elder
|
||||
)) {
|
||||
filters.influences = [{
|
||||
value: item.influences[0],
|
||||
disabled: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
if (item.itemLevel) {
|
||||
@@ -216,7 +227,7 @@ export function createFilters (
|
||||
if (item.itemLevel > 86) {
|
||||
filters.itemLevel = {
|
||||
value: 86,
|
||||
disabled: true
|
||||
disabled: !opts.exact
|
||||
}
|
||||
// @TODO limit by item type
|
||||
// If (RegExMatch(subtype, "i)Helmet|Gloves|Boots|Body Armour|Shield|Quiver")) {
|
||||
@@ -232,7 +243,7 @@ export function createFilters (
|
||||
} else {
|
||||
filters.itemLevel = {
|
||||
value: item.itemLevel,
|
||||
disabled: true
|
||||
disabled: !opts.exact
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,18 +279,6 @@ export function createFilters (
|
||||
value: true,
|
||||
disabled: (item.rarity !== ItemRarity.Unique)
|
||||
}
|
||||
|
||||
if (item.rarity !== ItemRarity.Unique) {
|
||||
if (filters.itemLevel) {
|
||||
filters.itemLevel.disabled = false
|
||||
}
|
||||
if (filters.influences) {
|
||||
filters.influences[0].disabled = false
|
||||
}
|
||||
if (filters.searchRelaxed) {
|
||||
filters.searchRelaxed.disabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.isVeiled) {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { createFilters } from './create-item-filters'
|
||||
import { createExactStatFilters, initUiModFilters } from './create-stat-filters'
|
||||
import { ModifierType, sumStatsByModType } from '@/parser/modifiers'
|
||||
import { ItemCategory, ItemRarity, ParsedItem } from '@/parser'
|
||||
import type { FilterPreset } from './interfaces'
|
||||
|
||||
const ROMAN_NUMERALS = ['I', 'II', 'III', 'IV', 'V']
|
||||
|
||||
export function createPresets (
|
||||
item: ParsedItem,
|
||||
opts: {
|
||||
league: string
|
||||
chaosPriceThreshold: number
|
||||
collapseListings: 'app' | 'api'
|
||||
activateStockFilter: boolean
|
||||
searchStatRange: number
|
||||
}
|
||||
): { presets: FilterPreset[], active: string } {
|
||||
if (item.info.refName === 'Expedition Logbook') {
|
||||
return {
|
||||
active: ROMAN_NUMERALS[0],
|
||||
presets: item.logbookAreaMods!.map<FilterPreset>((area, idx) => ({
|
||||
id: ROMAN_NUMERALS[idx],
|
||||
filters: createFilters(item, { ...opts, exact: true }),
|
||||
stats: createExactStatFilters(item, sumStatsByModType(area), opts)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(!item.info.craftable && item.rarity !== ItemRarity.Unique) ||
|
||||
item.isUnidentified ||
|
||||
item.rarity === ItemRarity.Normal ||
|
||||
item.category === ItemCategory.Map ||
|
||||
item.category === ItemCategory.Invitation ||
|
||||
item.category === ItemCategory.HeistContract ||
|
||||
item.category === ItemCategory.HeistBlueprint
|
||||
) {
|
||||
return {
|
||||
active: 'Exact',
|
||||
presets: [{
|
||||
id: 'Exact',
|
||||
filters: createFilters(item, { ...opts, exact: true }),
|
||||
stats: createExactStatFilters(item, item.statsByType, opts)
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
const pseudoPreset: FilterPreset = {
|
||||
id: 'Pseudo',
|
||||
filters: createFilters(item, { ...opts, exact: false }),
|
||||
stats: initUiModFilters(item, opts)
|
||||
}
|
||||
|
||||
const likelyFinishedItem = (
|
||||
item.rarity === ItemRarity.Unique ||
|
||||
item.statsByType.some(calc => calc.type === ModifierType.Crafted) ||
|
||||
item.quality === 20 || // quality > 20 can be used for selling bases, quality < 20 drops sometimes
|
||||
item.isCorrupted ||
|
||||
item.isMirrored
|
||||
)
|
||||
|
||||
const hasCraftingValue = (
|
||||
item.isSynthesised ||
|
||||
item.isFractured ||
|
||||
item.influences.length ||
|
||||
item.category === ItemCategory.ClusterJewel ||
|
||||
(item.category === ItemCategory.Jewel && item.rarity === ItemRarity.Magic) ||
|
||||
(item.category !== ItemCategory.Jewel &&
|
||||
item.category !== ItemCategory.AbyssJewel &&
|
||||
item.itemLevel! >= 82)
|
||||
)
|
||||
|
||||
if (likelyFinishedItem || !hasCraftingValue) {
|
||||
return { active: pseudoPreset.id, presets: [pseudoPreset] }
|
||||
}
|
||||
|
||||
const baseItemPreset: FilterPreset = {
|
||||
id: 'Base item',
|
||||
filters: createFilters(item, { ...opts, exact: true }),
|
||||
stats: createExactStatFilters(item, item.statsByType, opts)
|
||||
}
|
||||
|
||||
return {
|
||||
active: pseudoPreset.id,
|
||||
presets: [pseudoPreset, baseItemPreset]
|
||||
}
|
||||
}
|
||||
@@ -14,21 +14,90 @@ export interface FiltersCreationContext {
|
||||
statsByType: StatCalculated[]
|
||||
}
|
||||
|
||||
export function createExactStatFilters (
|
||||
item: ParsedItem,
|
||||
statsByType: StatCalculated[],
|
||||
_opts: {}
|
||||
): StatFilter[] {
|
||||
if (item.mapBlighted) return []
|
||||
if (
|
||||
item.isUnidentified &&
|
||||
item.rarity === ItemRarity.Unique &&
|
||||
!item.isSynthesised
|
||||
) return []
|
||||
|
||||
const keepByType = [ModifierType.Pseudo, ModifierType.Implicit, ModifierType.Fractured]
|
||||
if (
|
||||
item.category !== ItemCategory.Amulet &&
|
||||
item.category !== ItemCategory.Ring &&
|
||||
item.category !== ItemCategory.Flask
|
||||
) {
|
||||
keepByType.push(ModifierType.Enchant)
|
||||
}
|
||||
|
||||
if (item.rarity === ItemRarity.Magic && (
|
||||
item.category !== ItemCategory.Flask &&
|
||||
item.category !== ItemCategory.ClusterJewel &&
|
||||
item.category !== ItemCategory.Map &&
|
||||
item.category !== ItemCategory.Invitation &&
|
||||
item.category !== ItemCategory.HeistContract &&
|
||||
item.category !== ItemCategory.HeistBlueprint
|
||||
)) {
|
||||
keepByType.push(ModifierType.Explicit)
|
||||
}
|
||||
|
||||
const ctx: FiltersCreationContext = {
|
||||
item,
|
||||
searchInRange: 100,
|
||||
filters: [],
|
||||
statsByType: statsByType.filter(calc => keepByType.includes(calc.type))
|
||||
}
|
||||
|
||||
ctx.filters.push(
|
||||
...ctx.statsByType.map(mod => calculatedStatToFilter(mod, ctx.searchInRange, item))
|
||||
)
|
||||
|
||||
if (item.info.refName === 'Chronicle of Atzoatl') {
|
||||
applyAtzoatlRules(ctx.filters)
|
||||
return ctx.filters
|
||||
}
|
||||
|
||||
for (const filter of ctx.filters) {
|
||||
filter.hidden = undefined
|
||||
|
||||
if (filter.tag === FilterTag.Explicit) {
|
||||
filter.disabled = !filter.sources.some(source =>
|
||||
source.modifier.info.tier != null &&
|
||||
source.modifier.info.tier <= 2
|
||||
)
|
||||
} else {
|
||||
filter.disabled = false
|
||||
}
|
||||
|
||||
if (item.category === ItemCategory.ClusterJewel) {
|
||||
if (filter.statRef === '# Added Passive Skills are Jewel Sockets') {
|
||||
filter.hidden = 'Roll is not variable'
|
||||
filter.disabled = true
|
||||
}
|
||||
if (filter.statRef === 'Adds # Passive Skills') {
|
||||
filter.roll!.min = filter.roll!.default.min
|
||||
// https://www.poewiki.net/wiki/Cluster_Jewel#Optimal_passive_skill_amounts
|
||||
if (filter.roll!.max === 4) {
|
||||
filter.roll!.max = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.filters
|
||||
}
|
||||
|
||||
export function initUiModFilters (
|
||||
item: ParsedItem,
|
||||
opts: {
|
||||
searchStatRange: number
|
||||
}
|
||||
): StatFilter[] {
|
||||
if (
|
||||
(item.rarity === ItemRarity.Unique && item.category === ItemCategory.Map) ||
|
||||
item.category === ItemCategory.Invitation ||
|
||||
item.category === ItemCategory.HeistContract ||
|
||||
item.category === ItemCategory.HeistBlueprint
|
||||
) {
|
||||
return []
|
||||
}
|
||||
|
||||
const ctx: FiltersCreationContext = {
|
||||
item,
|
||||
filters: [],
|
||||
@@ -44,15 +113,8 @@ export function initUiModFilters (
|
||||
})
|
||||
}
|
||||
|
||||
if (!item.isUnidentified) {
|
||||
filterItemProp(ctx)
|
||||
filterPseudo(ctx)
|
||||
}
|
||||
|
||||
if (!item.isCorrupted && !item.isMirrored) {
|
||||
ctx.statsByType = ctx.statsByType.filter(mod => mod.type !== ModifierType.Fractured)
|
||||
ctx.statsByType.push(...item.statsByType.filter(mod => mod.type === ModifierType.Fractured))
|
||||
}
|
||||
filterItemProp(ctx)
|
||||
filterPseudo(ctx)
|
||||
|
||||
if (item.isVeiled) {
|
||||
ctx.statsByType = ctx.statsByType.filter(mod => mod.type !== ModifierType.Veiled)
|
||||
@@ -62,35 +124,10 @@ export function initUiModFilters (
|
||||
...ctx.statsByType.map(mod => calculatedStatToFilter(mod, ctx.searchInRange, item))
|
||||
)
|
||||
|
||||
if (item.info.refName === 'Chronicle of Atzoatl') {
|
||||
applyAtzoatlRules(ctx.filters)
|
||||
return ctx.filters
|
||||
}
|
||||
|
||||
if (!item.isCorrupted && !item.isMirrored && item.isSynthesised) {
|
||||
const transformedImplicits = item.statsByType.filter(mod =>
|
||||
mod.type === ModifierType.Implicit &&
|
||||
!ctx.statsByType.includes(mod))
|
||||
|
||||
const synthImplicitFilters = transformedImplicits.map(mod => calculatedStatToFilter(mod, ctx.searchInRange, item))
|
||||
for (const filter of synthImplicitFilters) {
|
||||
filter.hidden = 'Select only if price-checking as base item for crafting'
|
||||
}
|
||||
ctx.filters.push(...synthImplicitFilters)
|
||||
}
|
||||
|
||||
if (item.isUnidentified && item.rarity === ItemRarity.Unique) {
|
||||
ctx.filters = ctx.filters.filter(f => !f.hidden)
|
||||
}
|
||||
|
||||
if (item.isVeiled) {
|
||||
ctx.filters.forEach(filter => { filter.disabled = true })
|
||||
}
|
||||
|
||||
if (item.category === ItemCategory.Map) {
|
||||
ctx.filters = ctx.filters.filter(f => f.tag !== FilterTag.Explicit)
|
||||
}
|
||||
|
||||
finalFilterTweaks(ctx)
|
||||
|
||||
return ctx.filters
|
||||
@@ -266,11 +303,8 @@ function finalFilterTweaks (ctx: FiltersCreationContext) {
|
||||
filter.hidden = 'Roll is not variable'
|
||||
}
|
||||
if (filter.statRef === 'Adds # Passive Skills') {
|
||||
// https://pathofexile.gamepedia.com/Cluster_Jewel#Optimal_passive_skill_amounts
|
||||
filter.disabled = false
|
||||
if (filter.roll!.max === 4) {
|
||||
filter.roll!.max = 5
|
||||
}
|
||||
filter.disabled = true
|
||||
filter.roll!.max = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ import type { ItemInfluence, ItemCategory } from '@/parser'
|
||||
import type { StatCalculated } from '@/parser/modifiers'
|
||||
import type { ParsedItem } from '@/parser/ParsedItem'
|
||||
|
||||
export interface FilterPreset {
|
||||
id: 'Pseudo' | 'Base' | string
|
||||
filters: ItemFilters
|
||||
stats: StatFilter[]
|
||||
}
|
||||
|
||||
interface SearchFilter {
|
||||
name?: string
|
||||
baseType?: string
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
:item-img="item.info.icon"
|
||||
>
|
||||
<template #item v-if="isValuableBasetype">
|
||||
<button class="text-gray-400 hover:bg-gray-700 rounded px-1 -mx-1"
|
||||
@click="$emit('filter-item-base')">{{ t('Base item') }}</button>
|
||||
<span class="text-gray-400">{{ t('Base item') }}</span>
|
||||
</template>
|
||||
</item-quick-price>
|
||||
<div v-if="trend.changeStr" class="px-2 text-center">
|
||||
|
||||
@@ -78,10 +78,11 @@ function forBasetype (item: ParsedItem) {
|
||||
|
||||
if (item.influences.length === 1) {
|
||||
id += ` // ${item.influences[0]}`
|
||||
return id
|
||||
} else if (item.influences.length > 1) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
function forUniqueItem (item: ParsedItem) {
|
||||
|
||||
Reference in New Issue
Block a user