Skip to content
Merged
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
19 changes: 9 additions & 10 deletions server/src/filters/builder/pokemon.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-check
const { state } = require('../../services/state')
const { BaseFilter } = require('../Base')
const { getWildFilterKey } = require('../pokemon/getWildFilterKey')

/**
*
Expand Down Expand Up @@ -36,20 +37,18 @@ function buildPokemon(defaults, base, custom) {
Object.entries(state.event.masterfile.pokemon).forEach(([id, pkmn]) => {
pokemon.quests[`${id}`] = new BaseFilter(defaults.pokestops.pokemon)
Object.keys(pkmn.forms).forEach((form) => {
pokemon.full[`${id}-${form}`] = base
pokemon.raids[`${id}-${form}`] = new BaseFilter(defaults.gyms.pokemon)
pokemon.stations[`${id}-${form}`] = new BaseFilter(
defaults.stations.pokemon,
)
pokemon.quests[`${id}-${form}`] = new BaseFilter(
defaults.pokestops.pokemon,
)
const filterKey = getWildFilterKey(id, form)
const rawKey = `${id}-${form}`
pokemon.full[filterKey] = base
pokemon.raids[rawKey] = new BaseFilter(defaults.gyms.pokemon)
pokemon.stations[rawKey] = new BaseFilter(defaults.stations.pokemon)
pokemon.quests[rawKey] = new BaseFilter(defaults.pokestops.pokemon)
if (state.db.filterContext.Pokestop.hasConfirmedInvasions) {
pokemon.rocket[`a${id}-${form}`] = new BaseFilter(
pokemon.rocket[`a${rawKey}`] = new BaseFilter(
defaults.pokestops.invasionPokemon,
)
}
pokemon.nests[`${id}-${form}`] = new BaseFilter(defaults.nests.allPokemon)
pokemon.nests[rawKey] = new BaseFilter(defaults.nests.allPokemon)
})
if ('family' in pkmn) {
if (pkmn.family === +id) {
Expand Down
3 changes: 2 additions & 1 deletion server/src/filters/pokemon/Backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const config = require('@rm/config')
const { log, TAGS } = require('@rm/logger')
const { AND_KEYS, BASE_KEYS } = require('./constants')
const { getWildFilterKey } = require('./getWildFilterKey')
const {
deepCompare,
between,
Expand Down Expand Up @@ -372,7 +373,7 @@ class PkmnBackend {
return true
if (
!this.mods.onlyLinkGlobal ||
(this.pokemon === pokemon.pokemon_id && this.form === pokemon.form)
this.id === getWildFilterKey(pokemon.pokemon_id, pokemon.form)
) {
if (!this.expertFilter || !this.expertGlobal) return true
if (this.expertFilter(pokemon)) {
Expand Down
25 changes: 25 additions & 0 deletions server/src/filters/pokemon/getWildFilterKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const DITTO_ID = 132

const normalizePokemonId = (pokemonId) => {
const parsedPokemonId = Number.parseInt(`${pokemonId}`, 10)
return Number.isNaN(parsedPokemonId) ? 0 : parsedPokemonId
}

const normalizePokemonForm = (pokemonId, formId = 0) => {
if (normalizePokemonId(pokemonId) === DITTO_ID) {
// Confirmed wild Ditto is already treated as species-based upstream.
// Golbat/MEM keeps the scanner lookup on `pokemon_id = 132, form = 0`,
// while the raw form field may still carry the disguise form for display.
return 0
}
const parsedFormId = Number.parseInt(`${formId ?? 0}`, 10)
return Number.isNaN(parsedFormId) ? 0 : parsedFormId
}

const getWildFilterKey = (pokemonId, formId = 0) =>
`${normalizePokemonId(pokemonId)}-${normalizePokemonForm(pokemonId, formId)}`

module.exports = {
DITTO_ID,
getWildFilterKey,
}
55 changes: 35 additions & 20 deletions server/src/models/Pokemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,12 @@ const {
BASE_KEYS,
} = require('../filters/pokemon/constants')
const { PkmnBackend } = require('../filters/pokemon/Backend')
const {
DITTO_ID,
getWildFilterKey,
} = require('../filters/pokemon/getWildFilterKey')
const { state } = require('../services/state')

const DITTO_ID = 132

const getPokemonFilterKey = (pokemonId, form) =>
pokemonId === DITTO_ID ? `${DITTO_ID}-0` : `${pokemonId}-${form}`

class Pokemon extends Model {
static get tableName() {
return 'pokemon'
Expand Down Expand Up @@ -169,7 +168,12 @@ class Pokemon extends Model {
const pokemonForms = []
Object.values(filterMap).forEach((filter) => {
pokemonIds.push(filter.pokemon)
pokemonForms.push(filter.form)
if (!(filter.pokemon === DITTO_ID && filter.form === 0)) {
// Wild Ditto uses a synthetic canonical filter form. Keep the SQL
// prefilter species-only here so disguise form ids don't get turned
// into `form = 0` broad matches.
pokemonForms.push(filter.form)
}
if (
!queryPvp &&
config
Expand Down Expand Up @@ -260,8 +264,12 @@ class Pokemon extends Model {
} else {
ivOr.whereNull('pokemon_id')
}
ivOr.orWhereIn('pokemon_id', pokemonIds)
ivOr.orWhereIn('pokemon.form', pokemonForms)
if (pokemonIds.length) {
ivOr.orWhereIn('pokemon_id', pokemonIds)
}
if (pokemonForms.length) {
ivOr.orWhereIn('pokemon.form', pokemonForms)
}
}
if (onlyZeroIv && ivs) {
ivOr.orWhere(isMad ? raw(IV_CALC) : 'iv', 0)
Expand Down Expand Up @@ -359,7 +367,7 @@ class Pokemon extends Model {
// form checker
for (let i = 0; i < results.length; i += 1) {
const pkmn = results[i]
const id = getPokemonFilterKey(pkmn.pokemon_id, pkmn.form)
const id = getWildFilterKey(pkmn.pokemon_id, pkmn.form)
const filter = filterMap[id] || globalFilter
let noPvp = true

Expand Down Expand Up @@ -439,8 +447,7 @@ class Pokemon extends Model {
for (let i = 0; i < pvpResults.length; i += 1) {
const pkmn = pvpResults[i]
const filter =
filterMap[getPokemonFilterKey(pkmn.pokemon_id, pkmn.form)] ||
globalFilter
filterMap[getWildFilterKey(pkmn.pokemon_id, pkmn.form)] || globalFilter
const result = filter.build(pkmn)
if (filter.valid(result)) {
finalResults.push(result)
Expand Down Expand Up @@ -803,13 +810,13 @@ class Pokemon extends Model {
const built = filtered
.map((item) => {
const filter =
filterMap[getPokemonFilterKey(item.pokemon_id, item.form)] ||
filterMap[getWildFilterKey(item.pokemon_id, item.form)] ||
globalFilter
return filter.build(item)
})
.filter((pkmn) => {
const filter =
filterMap[getPokemonFilterKey(pkmn.pokemon_id, pkmn.form)] ||
filterMap[getWildFilterKey(pkmn.pokemon_id, pkmn.form)] ||
globalFilter
return filter.valid(pkmn)
})
Expand Down Expand Up @@ -886,14 +893,22 @@ class Pokemon extends Model {
secret,
httpAuth,
)
available.forEach((pkmn) => {
if (pkmn.id === DITTO_ID) pkmn.form = 0
})
const normalizedAvailable = available.reduce(
(acc, pkmn) => {
// Wild Ditto reports disguise form ids here, not true Ditto form ids.
// Normalize them to a single wild-filter key for the Pokémon drawer.
const key = getWildFilterKey(pkmn.id, pkmn.form)
acc.available.add(key)
const current = Number(acc.rarity.get(key) ?? 0)
const count = Number(pkmn.count ?? 0)
acc.rarity.set(key, current + count)
return acc
},
{ available: new Set(), rarity: new Map() },
)
return {
available: available.map((pkmn) => `${pkmn.id}-${pkmn.form}`),
rarity: Object.fromEntries(
available.map((pkmn) => [`${pkmn.id}-${pkmn.form}`, pkmn.count]),
),
available: [...normalizedAvailable.available],
rarity: Object.fromEntries(normalizedAvailable.rarity),
}
}

Expand Down
5 changes: 5 additions & 0 deletions server/src/services/EventManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ class EventManager extends Logger {
if (!Number.isNaN(parseInt(item.charAt(0)))) {
const [id, form] = item.split('-')
const formId = form || '0'
if (category === 'pokemon' && id === '132' && formId === '0') {
// Wild Ditto uses a synthetic filter key here. Do not backfill it
// into the masterfile as a real form entry.
return
}
if (!this.masterfile.pokemon[id]) {
this.masterfile.pokemon[id] = {
name: '',
Expand Down
10 changes: 4 additions & 6 deletions src/features/pokemon/PokemonPopup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { GET_TAPPABLE_BY_ID } from '@services/queries/tappable'
import { usePokemonBackgroundVisual } from '@hooks/usePokemonBackgroundVisuals'
import { BackgroundCard } from '@components/popups/BackgroundCard'
import { getFormDisplay } from '@utils/getFormDisplay'
import { getWildFilterId } from '@utils/getWildFilterId'

const rowClass = { width: 30, fontWeight: 'bold' }

Expand Down Expand Up @@ -346,6 +347,7 @@ const Header = ({ pokemon, metaData, iconUrl, userSettings, isTutorial }) => {

const [anchorEl, setAnchorEl] = React.useState(null)
const { id, pokemon_id, form, display_pokemon_id } = pokemon
const filterKey = getWildFilterId(pokemon_id, form)

const handleClick = (event) => {
setAnchorEl(event.currentTarget)
Expand All @@ -363,8 +365,7 @@ const Header = ({ pokemon, metaData, iconUrl, userSettings, isTutorial }) => {
const handleExclude = () => {
setAnchorEl(null)
if (filters?.pokemon?.filter) {
const key = `${pokemon_id}-${form}`
setDeepStore(`filters.pokemon.filter.${key}.enabled`, false)
setDeepStore(`filters.pokemon.filter.${filterKey}.enabled`, false)
}
}

Expand All @@ -382,10 +383,7 @@ const Header = ({ pokemon, metaData, iconUrl, userSettings, isTutorial }) => {
{ name: 'timer', action: handleTimer },
{ name: 'hide', action: handleHide },
]
if (
isTutorial ||
filters?.pokemon?.filter?.[`${pokemon_id}-${form}`]?.enabled
) {
if (isTutorial || filters?.pokemon?.filter?.[filterKey]?.enabled) {
options.push({ name: 'exclude', action: handleExclude })
}
const pokeName = t(`poke_${metaData.pokedexId}`)
Expand Down
3 changes: 2 additions & 1 deletion src/features/pokemon/PokemonTile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getTimeUntil } from '@utils/getTimeUntil'
import { normalizeCategory } from '@utils/normalizeCategory'
import { getS2Polygon } from '@utils/getS2Polygon'
import { getFormDisplay } from '@utils/getFormDisplay'
import { getWildFilterId } from '@utils/getWildFilterId'

import { PokemonPopup } from './PokemonPopup'
import { basicPokemonMarker, fancyPokemonMarker } from './pokemonMarker'
Expand All @@ -46,7 +47,7 @@ const getGlowStatus = (pkmn, userSettings) => {
* @returns
*/
const BasePokemonTile = (pkmn) => {
const internalId = `${pkmn.pokemon_id}-${pkmn.form}`
const internalId = getWildFilterId(pkmn.pokemon_id, pkmn.form)

const [markerRef, setMarkerRef] = React.useState(null)

Expand Down
6 changes: 5 additions & 1 deletion src/pages/map/hooks/useGenPokemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'

import { useMemory } from '@store/useMemory'
import { getWildFilterId } from '@utils/getWildFilterId'

export function useGenPokemon() {
const { t } = useTranslation()
Expand All @@ -29,7 +30,10 @@ export function useGenPokemon() {
const pokeName = t(`poke_${i}`)
Object.entries(pkmn.forms).forEach(([j, form]) => {
const formName = t(`form_${j}`)
const id = `${i}-${j}`
const id = getWildFilterId(i, j)
if (tempObj.pokemon[id]) {
return
}
const formTypes = (form.types || pkmn.types || []).map(
(x) => `poke_type_${x}`,
)
Expand Down
12 changes: 12 additions & 0 deletions src/utils/getWildFilterId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const DITTO_ID = 132

export const getWildFilterId = (pokemonId, formId = 0) => {
const normalizedPokemonId = Number.parseInt(`${pokemonId}`, 10)
const normalizedFormId = Number.parseInt(`${formId ?? 0}`, 10)
if (normalizedPokemonId === DITTO_ID) {
return `${DITTO_ID}-0`
}
return `${Number.isNaN(normalizedPokemonId) ? 0 : normalizedPokemonId}-${
Number.isNaN(normalizedFormId) ? 0 : normalizedFormId
}`
}
Loading