Trim typings and matchups to the selected generation
- Detail page resolves a Pokemon's typing from PokeAPI past_types for the selected game's generation (Gen 1 Magnemite is pure Electric, pre-Gen 6 Clefairy is Normal); feeds hero pills, theming and matchups - Type chart excludes Steel/Dark (pre-Gen 2) and Fairy (pre-Gen 6) as attacking types entirely - Gen 1-2 game-era sprites (opaque white bg) shown as a small framed tile on cards and the detail hero Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
9216a53932
commit
404ca7dbbd
@ -9,10 +9,12 @@ import { typeHex } from '../lib/type-color.js';
|
||||
* gradient, a type-coloured spotlight behind an oversized sprite that lifts
|
||||
* above the card, a number chip, and a big ghost number in the corner.
|
||||
*/
|
||||
export function Card(species, number, { spriteStyle = 'official', versionGroup } = {}) {
|
||||
export function Card(species, number, { spriteStyle = 'official', versionGroup, boxed = false } = {}) {
|
||||
const state = entry(species.id);
|
||||
const mainType = (species.types || [])[0] || 'normal';
|
||||
const num = String(number ?? species.id).padStart(3, '0');
|
||||
const data = { type: mainType, sprite: spriteStyle };
|
||||
if (boxed) data.boxed = '1';
|
||||
|
||||
const caughtBtn = el('button', {
|
||||
class: 'card__caught',
|
||||
@ -34,7 +36,7 @@ export function Card(species, number, { spriteStyle = 'official', versionGroup }
|
||||
{
|
||||
class: `card${state.caught ? ' is-caught' : ''}`,
|
||||
href: `#/pokemon/${species.id}`,
|
||||
dataset: { type: mainType, sprite: spriteStyle },
|
||||
dataset: data,
|
||||
style: `--type-main:${typeHex(mainType)}`,
|
||||
},
|
||||
el('span', { class: 'card__ghost', 'aria-hidden': 'true' }, num),
|
||||
|
||||
@ -70,7 +70,10 @@ export function multiplier(attacking, defTypes, gen = 9) {
|
||||
*/
|
||||
export function defensiveMatchups(defTypes, gen = 9) {
|
||||
const buckets = { '4': [], '2': [], '0.5': [], '0.25': [], '0': [] };
|
||||
const atkTypes = gen < 6 ? TYPES.filter((t) => t !== 'fairy') : TYPES;
|
||||
// Types that didn't exist yet can't be attacking: Steel/Dark before Gen 2,
|
||||
// Fairy before Gen 6.
|
||||
const absent = new Set(gen < 2 ? ['steel', 'dark', 'fairy'] : gen < 6 ? ['fairy'] : []);
|
||||
const atkTypes = TYPES.filter((t) => !absent.has(t));
|
||||
for (const atk of atkTypes) {
|
||||
const m = multiplier(atk, defTypes, gen);
|
||||
if (m === 1) continue;
|
||||
|
||||
@ -450,6 +450,23 @@
|
||||
.card[data-sprite="game"] .card__art .sprite {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
.card[data-sprite="game"] .card__art {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
/* Gen 1–2 game sprites ship with an opaque white background — show them as
|
||||
a small framed tile rather than a box that swallows the card. */
|
||||
.card[data-boxed] .card__art {
|
||||
width: 78px;
|
||||
height: 78px;
|
||||
padding: 5px;
|
||||
border-radius: 12px;
|
||||
background: #f7f7f5;
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08), 0 2px 6px rgba(0, 0, 0, 0.14);
|
||||
}
|
||||
.card[data-boxed] .card__art .sprite {
|
||||
filter: none;
|
||||
}
|
||||
.card__body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@ -703,6 +720,18 @@
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.phero__art--boxed {
|
||||
width: min(180px, 46vw);
|
||||
height: min(180px, 46vw);
|
||||
padding: 14px;
|
||||
border-radius: 18px;
|
||||
background: #f7f7f5;
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.22), inset 0 0 0 1px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.phero__art--boxed img {
|
||||
image-rendering: pixelated;
|
||||
filter: none;
|
||||
}
|
||||
@keyframes pop {
|
||||
from { transform: scale(0.8); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
|
||||
@ -113,6 +113,10 @@ export async function DexGrid() {
|
||||
function paintGrid() {
|
||||
const st = settings.get();
|
||||
const pokemonState = selection.get().pokemon;
|
||||
// Gen 1–2 game-era sprites have opaque white backgrounds — frame them.
|
||||
const boxed =
|
||||
st.spriteStyle === 'game' &&
|
||||
(snap.versionGroupByKey.get(st.versionGroup)?.generation ?? 9) <= 2;
|
||||
const filterTest = FILTERS.find((f) => f.key === filterKey).test;
|
||||
const frag = document.createDocumentFragment();
|
||||
let shown = 0;
|
||||
@ -131,6 +135,7 @@ export async function DexGrid() {
|
||||
const card = Card(species, number, {
|
||||
spriteStyle: st.spriteStyle,
|
||||
versionGroup: st.versionGroup,
|
||||
boxed,
|
||||
});
|
||||
if (shown < 30) card.style.setProperty('--i', String(shown));
|
||||
frag.append(card);
|
||||
|
||||
@ -15,6 +15,16 @@ import { typeHex } from '../lib/type-color.js';
|
||||
|
||||
const idFromUrl = (u) => Number(u.replace(/\/$/, '').split('/').pop());
|
||||
|
||||
/** A Pokémon's typing as it was in the selected game's generation. */
|
||||
function typesForGeneration(pokemon, gen) {
|
||||
const past = (pokemon.past_types || [])
|
||||
.map((p) => ({ types: p.types, _gen: idFromUrl(p.generation.url) }))
|
||||
.sort((a, b) => a._gen - b._gen);
|
||||
const era = past.find((p) => p._gen >= gen);
|
||||
const list = era ? era.types : pokemon.types;
|
||||
return list.slice().sort((a, b) => a.slot - b.slot).map((t) => t.type.name);
|
||||
}
|
||||
|
||||
const STAT_NAMES = [
|
||||
'hp',
|
||||
'attack',
|
||||
@ -68,10 +78,7 @@ export async function PokemonDetail(nationalId) {
|
||||
return view;
|
||||
}
|
||||
|
||||
const types = pokemon.types
|
||||
.slice()
|
||||
.sort((a, b) => a.slot - b.slot)
|
||||
.map((t) => t.type.name);
|
||||
const types = typesForGeneration(pokemon, vgGen);
|
||||
const mainType = types[0];
|
||||
view.style.setProperty('--type-main', typeHex(mainType));
|
||||
view.style.setProperty('--type-2', typeHex(types[1] || mainType));
|
||||
@ -81,8 +88,11 @@ export async function PokemonDetail(nationalId) {
|
||||
// Shiny Pokémon were introduced in Gen 2 — no toggle for Gen 1 games.
|
||||
const shinyAvailable = vgGen >= 2;
|
||||
const artStyle = st.spriteStyle === 'default' ? 'official' : st.spriteStyle;
|
||||
const boxedArt = artStyle === 'game' && vgGen <= 2;
|
||||
let shiny = shinyAvailable && st.showShiny;
|
||||
const artHolder = el('div', { class: 'phero__art' });
|
||||
const artHolder = el('div', {
|
||||
class: `phero__art${boxedArt ? ' phero__art--boxed' : ''}`,
|
||||
});
|
||||
const shinyBtn = shinyAvailable
|
||||
? el('button', {
|
||||
class: 'phero__shiny',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user