beba7301a1
- PvP: pestaña Campos de batalla (pvpstats) con resultado, stats por partida y fecha/hora. - Logros: rejilla por categoría con anillos de progreso + detalle por categoría (datos de Achievement.db2 + Achievement_Category.db2, iconos vía wowhead). - Bandas: progreso por dificultad (10/25/heroico) con jefes reales de DungeonEncounter.db2 y tooltip de jefes; agrupado por expansión (Clásico/TBC/WotLK). - Mazmorras: mismo sistema, Normal + Heroico, Clásico/TBC/WotLK. - Página de hermandad estilo armería: cabecera (emblema, puntos = unión de logros de miembros sin duplicar, nº miembros, fundada), pestañas Hermandad/Logros, roster con iconos de clase/raza, columna Función (rol por spec), filtros clase/función, orden por rango/puntos/nivel de objeto/nivel/nombre, paginación. - Barra lateral: mensaje diario con login (iron-session, gated por pertenencia), logros recientes y desgloses de clases y funciones. - Cabecera de personaje: enlace a la hermandad + nombre del reino. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
123 lines
4.8 KiB
TypeScript
123 lines
4.8 KiB
TypeScript
import { wowheadIcon, wowheadUrl, wowheadData } from '@/lib/wowhead'
|
|
|
|
export type TalentCell = { tier: number; col: number; rank: number; max: number; spell: number }
|
|
export type TalentTreeData = { index: number; name: string; points: number; talents: TalentCell[] }
|
|
export type GlyphInfo = { name: string; icon: string }
|
|
export type TalentGroupView = {
|
|
spec: string | null
|
|
total: number
|
|
dist: number[]
|
|
trees: TalentTreeData[]
|
|
glyphs: { major: GlyphInfo[]; minor: GlyphInfo[] }
|
|
}
|
|
|
|
/**
|
|
* Renderiza UN grupo de talentos: cabecera de distribución + los 3 árboles (rejilla de
|
|
* iconos) + una 4ª columna con los Glifos (mayores/menores). Componente puro sin estado,
|
|
* usado por ArmoryTalentTab (que conmuta Primaria/Secundaria).
|
|
*/
|
|
export function ArmoryTalents({
|
|
group,
|
|
spellIcons,
|
|
locale,
|
|
labels,
|
|
}: {
|
|
group: TalentGroupView
|
|
spellIcons: Record<number, string>
|
|
locale: string
|
|
labels: { distribution: string; groupLabel: string; glyphs: string; majorGlyphs: string; minorGlyphs: string }
|
|
}) {
|
|
const { trees, glyphs, dist } = group
|
|
return (
|
|
<div className="armory-talents">
|
|
<div className="armory-tdist">
|
|
<div className="armory-tdist-label">
|
|
{labels.distribution} · {labels.groupLabel.toUpperCase()}
|
|
</div>
|
|
<div className="armory-tdist-value">{dist.join(' / ')}</div>
|
|
</div>
|
|
|
|
<div className="armory-talent-cols">
|
|
{trees.map((tree) => {
|
|
const maxTier = tree.talents.reduce((m, t) => Math.max(m, t.tier), 0)
|
|
return (
|
|
<div className="armory-talent-tree" key={tree.index}>
|
|
<div className="armory-talent-tree-head">
|
|
<span>{tree.name}</span>
|
|
<span className="armory-talent-tree-pts">{tree.points}</span>
|
|
</div>
|
|
<div className="armory-talent-grid" style={{ gridTemplateRows: `repeat(${maxTier + 1}, 44px)` }}>
|
|
{tree.talents.map((tal) => {
|
|
const icon = spellIcons[tal.spell]
|
|
const state = tal.rank === 0 ? 'off' : tal.rank >= tal.max ? 'max' : 'on'
|
|
return (
|
|
<a
|
|
key={`${tal.tier}-${tal.col}`}
|
|
className={`armory-talent ${state}`}
|
|
style={{ gridRow: tal.tier + 1, gridColumn: tal.col + 1 }}
|
|
href={wowheadUrl('spell', tal.spell, locale)}
|
|
data-wowhead={wowheadData('spell', tal.spell, locale)}
|
|
data-wh-icon-size="false"
|
|
data-wh-rename-link="false"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{icon ? (
|
|
<span
|
|
className="armory-talent-icon"
|
|
style={{ backgroundImage: `url(${wowheadIcon(icon, 'medium')})` }}
|
|
/>
|
|
) : null}
|
|
{tal.rank > 0 ? (
|
|
<span className="armory-talent-rank">
|
|
{tal.rank}/{tal.max}
|
|
</span>
|
|
) : null}
|
|
</a>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
|
|
<div className="armory-talent-tree armory-glyph-col">
|
|
<div className="armory-talent-tree-head">
|
|
<span>{labels.glyphs}</span>
|
|
<span className="armory-talent-tree-pts">
|
|
{glyphs.major.length} · {glyphs.minor.length}
|
|
</span>
|
|
</div>
|
|
{glyphs.major.length > 0 && (
|
|
<div className="armory-glyph-group">
|
|
<h3 className="armory-prof-grouptitle">{labels.majorGlyphs}</h3>
|
|
{glyphs.major.map((g, i) => (
|
|
<div className="armory-glyph" key={i}>
|
|
<span className="armory-glyph-icon" style={{ backgroundImage: `url(${wowheadIcon(g.icon, 'small')})` }} />
|
|
<span className="armory-glyph-name">{g.name}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
{glyphs.minor.length > 0 && (
|
|
<div className="armory-glyph-group">
|
|
<h3 className="armory-prof-grouptitle">{labels.minorGlyphs}</h3>
|
|
{glyphs.minor.map((g, i) => (
|
|
<div className="armory-glyph" key={i}>
|
|
<span className="armory-glyph-icon" style={{ backgroundImage: `url(${wowheadIcon(g.icon, 'small')})` }} />
|
|
<span className="armory-glyph-name">{g.name}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
{glyphs.major.length === 0 && glyphs.minor.length === 0 && (
|
|
<p className="armory-hint" style={{ padding: '8px 0' }}>
|
|
—
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|