Armería: muñeco de papel, talentos, actividad y caché del visor 3D
La ficha de personaje pasa a mostrar el equipo sobre el paperdoll (con encantamientos y gemas en los tooltips de wowhead), el árbol de talentos y la actividad reciente del personaje. El proxy de /modelviewer/* deja de ser un rewrite de Next y pasa a un route handler que cachea en disco los assets de zamimg, para no repetir la descarga en cada visita.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { wowheadUrl, wowheadData, wowheadIcon } 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[] }
|
||||
|
||||
/**
|
||||
* Árbol de talentos estilo calculador: 3 árboles, cada uno una rejilla de casillas
|
||||
* posicionadas por fila (tier) y columna. Cada casilla muestra el icono del talento
|
||||
* (del hechizo de rango 1) y su rango actual/máximo; las no aprendidas van atenuadas.
|
||||
* El icono va en un <span> hijo porque wowhead sobrescribe el background del <a>.
|
||||
*/
|
||||
export function ArmoryTalents({
|
||||
trees,
|
||||
spec,
|
||||
total,
|
||||
maxPoints,
|
||||
locale,
|
||||
spellIcons,
|
||||
}: {
|
||||
trees: TalentTreeData[]
|
||||
spec: string | null
|
||||
total: number
|
||||
maxPoints: number
|
||||
locale: string
|
||||
spellIcons: Record<number, string>
|
||||
}) {
|
||||
return (
|
||||
<div className="armory-talents">
|
||||
<div className="armory-spec">
|
||||
<span className="armory-spec-name">{spec}</span>
|
||||
<span className="armory-spec-dist">{trees.map((tr) => tr.points).join(' / ')}</span>
|
||||
<span className="armory-spec-total">
|
||||
{total} / {maxPoints}
|
||||
</span>
|
||||
</div>
|
||||
<div className="armory-talent-trees">
|
||||
{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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user