Armería: PvP campos de batalla, logros por categoría, bandas/mazmorras y página de hermandad
- 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>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
export type RaidBossState = { name: string; killed: boolean }
|
||||
export type RaidDifficulty = { diff: number; label: string; killed: number; total: number; bosses: RaidBossState[] }
|
||||
export type RaidProgressItem = { map: number; name: string; exp: number; total: number; difficulties: RaidDifficulty[] }
|
||||
|
||||
/** Gradiente de cabecera por banda (sin arte del cliente). */
|
||||
const RAID_THEME: Record<number, string> = {
|
||||
// Classic
|
||||
409: 'linear-gradient(135deg, #8a3a1a, #2e1208)',
|
||||
469: 'linear-gradient(135deg, #7a2a2a, #250d0d)',
|
||||
531: 'linear-gradient(135deg, #8a7a2a, #2a2408)',
|
||||
509: 'linear-gradient(135deg, #6b7a2a, #202808)',
|
||||
309: 'linear-gradient(135deg, #2f7a5a, #0d281c)',
|
||||
// TBC
|
||||
532: 'linear-gradient(135deg, #4a2f6b, #1a1028)',
|
||||
565: 'linear-gradient(135deg, #6b3a2f, #281410)',
|
||||
544: 'linear-gradient(135deg, #7a2f4a, #2a1018)',
|
||||
548: 'linear-gradient(135deg, #2a6b6b, #0d2828)',
|
||||
550: 'linear-gradient(135deg, #3a5c8a, #101f2f)',
|
||||
534: 'linear-gradient(135deg, #2f6b46, #0d281a)',
|
||||
564: 'linear-gradient(135deg, #5a2f6b, #1c1028)',
|
||||
580: 'linear-gradient(135deg, #8a7a2f, #2a2410)',
|
||||
568: 'linear-gradient(135deg, #6b5a2a, #282008)',
|
||||
// WotLK
|
||||
249: 'linear-gradient(135deg, #7a2d1a, #3a1710)',
|
||||
533: 'linear-gradient(135deg, #2f6b46, #10281a)',
|
||||
616: 'linear-gradient(135deg, #2a5c8a, #10202f)',
|
||||
615: 'linear-gradient(135deg, #4a2f6b, #1a1028)',
|
||||
624: 'linear-gradient(135deg, #6b5a2f, #282010)',
|
||||
603: 'linear-gradient(135deg, #35566b, #101c28)',
|
||||
649: 'linear-gradient(135deg, #6b4a2f, #281a10)',
|
||||
631: 'linear-gradient(135deg, #3a6b8a, #0f2230)',
|
||||
724: 'linear-gradient(135deg, #7a1f2f, #2a0f14)',
|
||||
}
|
||||
|
||||
function barClass(killed: number, total: number) {
|
||||
if (total > 0 && killed >= total) return 'full'
|
||||
if (killed > 0) return 'partial'
|
||||
return 'empty'
|
||||
}
|
||||
|
||||
/** Gradiente determinista por mapId cuando no hay tema fijo (mazmorras). */
|
||||
function themeFor(map: number) {
|
||||
if (RAID_THEME[map]) return RAID_THEME[map]
|
||||
const h = (map * 47) % 360
|
||||
return `linear-gradient(135deg, hsl(${h} 42% 30%), hsl(${h} 48% 11%))`
|
||||
}
|
||||
|
||||
export function ArmoryRaids({
|
||||
locale,
|
||||
raids,
|
||||
labels,
|
||||
}: {
|
||||
locale: string
|
||||
raids: RaidProgressItem[]
|
||||
labels: { bosses: string; empty: string; classic: string; tbc: string; wotlk: string }
|
||||
}) {
|
||||
if (raids.length === 0) {
|
||||
return (
|
||||
<div className="armory-panel armory-raids">
|
||||
<p className="armory-hint">{labels.empty}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const groups: { exp: number; title: string; items: RaidProgressItem[] }[] = [
|
||||
{ exp: 2, title: labels.wotlk, items: raids.filter((r) => r.exp === 2) },
|
||||
{ exp: 1, title: labels.tbc, items: raids.filter((r) => r.exp === 1) },
|
||||
{ exp: 0, title: labels.classic, items: raids.filter((r) => r.exp === 0) },
|
||||
].filter((g) => g.items.length > 0)
|
||||
|
||||
return (
|
||||
<div className="armory-panel armory-raids">
|
||||
{groups.map((g) => (
|
||||
<section className="armory-raid-exp" key={g.exp}>
|
||||
<h3 className="armory-raid-exp-title">{g.title}</h3>
|
||||
<div className="armory-raid-grid">
|
||||
{g.items.map((r) => (
|
||||
<div className="armory-raid-card" key={r.map}>
|
||||
<div className="armory-raid-banner" style={{ background: themeFor(r.map) }}>
|
||||
<span className="armory-raid-name">{r.name}</span>
|
||||
<span className="armory-raid-count">
|
||||
{r.total} {labels.bosses}
|
||||
</span>
|
||||
</div>
|
||||
<div className="armory-raid-diffs">
|
||||
{r.difficulties.map((d) => (
|
||||
<div className="armory-raid-row" key={d.diff}>
|
||||
<span className="armory-raid-diff-lbl">{d.label}</span>
|
||||
<div className={`armory-raid-bar ${barClass(d.killed, d.total)}`}>
|
||||
<div
|
||||
className="armory-raid-bar-fill"
|
||||
style={{ width: `${d.total ? Math.round((d.killed / d.total) * 100) : 0}%` }}
|
||||
/>
|
||||
<span className="armory-raid-bar-txt">
|
||||
{d.killed}/{d.total}
|
||||
</span>
|
||||
</div>
|
||||
<div className="armory-raid-tip" role="tooltip">
|
||||
<div className="armory-raid-tip-head">
|
||||
{r.name} · {d.label}
|
||||
</div>
|
||||
<ul>
|
||||
{d.bosses.map((b, i) => (
|
||||
<li key={i} className={b.killed ? 'killed' : 'alive'}>
|
||||
<span className="armory-raid-tip-x">{b.killed ? '1' : '0'} ×</span> {b.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user