// ============================================================================= // SHARED COMPONENTS // ============================================================================= const { useState, useEffect, useMemo, useRef, useCallback } = React; const { CardArt } = window; const { PROVIDERS, RARITIES, STATS, CARDS, MYTHIC_CARDS, CHAMPIONS, COLLECTIONS, getCard } = window.GAME_DATA; // Format helpers function fmtStat(key, val) { if (key === 'rtp') return val.toFixed(1) + '%'; if (key === 'maxwin') return val >= 1000 ? (val / 1000).toFixed(val >= 10000 ? 0 : 1) + 'k' : val; if (key === 'hits') return val + '%'; return val; } function statForCompare(key, val) { // RTP is in 90-99 range — normalize by subtracting 90 if (key === 'rtp') return (val - 90) * 10; if (key === 'maxwin') return Math.log10(val) * 12; // log scale → 0–60 return val; } function normalizedPercent(key, val) { if (key === 'rtp') return Math.min(100, (val - 90) * 10); if (key === 'maxwin') return Math.min(100, Math.log10(val) * 12); if (key === 'volatility') return val; if (key === 'hits') return val; if (key === 'fame') return val; return val; } // ============================================================================= // PROVIDER CHIP // ============================================================================= function ProvChip({ providerId }) { const p = PROVIDERS[providerId]; const accentClass = { cyan: 'cyan', lime: 'lime', orange: 'orange', violet: 'violet', red: 'red', }[p.accent] || 'cyan'; return {p.code}; } // ============================================================================= // TOP TRUMPS CARD // ============================================================================= function TopTrumpsCard({ card, locked = false, selected = false, inDeck = false, onClick, highlightStat, size = 'md', onStatClick }) { if (!card) return null; const rarity = RARITIES[card.rarity]; const provider = PROVIDERS[card.provider]; const cls = `tt-card rarity-${card.rarity}${locked?' locked':''}${selected?' selected':''}${inDeck?' in-deck':''} size-${size}`; return (
{provider.code} {rarity.label}
{!locked && }
{card.name} {card.mech} · {card.year}
{Object.values(STATS).map(stat => { const v = card.stats[stat.key]; const pct = normalizedPercent(stat.key, v); const active = highlightStat === stat.key; return (
{ e.stopPropagation(); onStatClick(stat.key); } : undefined} style={onStatClick ? {cursor:'pointer'} : undefined}> {stat.short} {fmtStat(stat.key, v)}
); })}
); } // ============================================================================= // CHAMPION PORTRAIT — abstract sigil // ============================================================================= function ChampionSigil({ champion, size = 110 }) { const c = champion; // Use one of the champion's deck arts as the sigil base const baseCard = getCard(c.deck[0]); return ( ); } // ============================================================================= // PACK MINI VISUAL // ============================================================================= function PackMini() { return (
SOBRE
×5
); } // ============================================================================= // CORNER PANEL DECORATION // ============================================================================= function PanelCorners({ color }) { const style = color ? { borderColor: color } : {}; return ( ); } Object.assign(window, { TopTrumpsCard, ChampionSigil, PackMini, ProvChip, PanelCorners, fmtStat, statForCompare, normalizedPercent, });