Foro: avatares por clase del personaje principal
Los avatares salían vacíos (el original usaba getAvatar, que no teníamos). Ahora cada post muestra el avatar de FusionCMS correspondiente a la CLASE del personaje de más nivel de la cuenta que postea, usando datos reales de acore_characters. - Se descargan 10 avatares (uno por clase jugable en WotLK 3.4.3: 1..9 y 11) a public/forum/avatars/class-<id>.gif. - resolvePosters() consulta la clase del personaje top de cada cuenta y mapea a su avatar; sin personaje (o acore_characters vacía) cae a guerrero por defecto. - La página de tema pinta el avatar en el recuadro lateral. Nota: la tabla characters de AzerothCore usa `guid` como clave (no `id`); ordenar por `id` lanzaba error y devolvía siempre el avatar por defecto. Corregido. Verificado en producción: una cuenta con Paladín de nivel máximo muestra class-2.gif. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+37
-1
@@ -85,6 +85,16 @@ export interface PosterInfo {
|
||||
isStaff: boolean
|
||||
joindate: string | null
|
||||
postCount: number
|
||||
avatar: string
|
||||
}
|
||||
|
||||
// Avatar por clase (FusionCMS) del personaje de más nivel de la cuenta. Solo clases
|
||||
// jugables en WotLK 3.4.3 (1..9 y 11). Sin personaje → guerrero por defecto.
|
||||
const AVATAR_DIR = '/forum/avatars'
|
||||
const AVATAR_CLASSES = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 11])
|
||||
function avatarForClass(classId: number | undefined): string {
|
||||
const c = classId && AVATAR_CLASSES.has(classId) ? classId : 1
|
||||
return `${AVATAR_DIR}/class-${c}.gif`
|
||||
}
|
||||
|
||||
/** Nivel GM mínimo para considerarse «staff» en el foro (badge y color). */
|
||||
@@ -362,6 +372,23 @@ export async function resolvePosters(ids: number[]): Promise<Map<number, PosterI
|
||||
if (unique.length === 0) return map
|
||||
|
||||
const placeholders = unique.map(() => '?').join(',')
|
||||
|
||||
// Clase del personaje de más nivel de cada cuenta (para el avatar). Se tolera que
|
||||
// acore_characters no exista o esté vacía: el avatar cae a guerrero por defecto.
|
||||
const classByAccount = new Map<number, number>()
|
||||
try {
|
||||
const [chars] = await db(DB.characters).query<RowDataPacket[]>(
|
||||
`SELECT account, class FROM characters WHERE account IN (${placeholders}) ORDER BY level DESC, guid ASC`,
|
||||
unique,
|
||||
)
|
||||
for (const c of chars) {
|
||||
const acc = Number(c.account)
|
||||
if (!classByAccount.has(acc)) classByAccount.set(acc, Number(c.class))
|
||||
}
|
||||
} catch {
|
||||
/* sin personajes: avatar por defecto */
|
||||
}
|
||||
|
||||
try {
|
||||
const [accs] = await db(DB.auth).query<RowDataPacket[]>(
|
||||
`SELECT id, username, joindate FROM account WHERE id IN (${placeholders})`,
|
||||
@@ -383,6 +410,7 @@ export async function resolvePosters(ids: number[]): Promise<Map<number, PosterI
|
||||
isStaff: gmlevel >= STAFF_GMLEVEL,
|
||||
joindate: a.joindate ? new Date(a.joindate).toISOString() : null,
|
||||
postCount: 0,
|
||||
avatar: avatarForClass(classByAccount.get(Number(a.id))),
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
@@ -391,7 +419,15 @@ export async function resolvePosters(ids: number[]): Promise<Map<number, PosterI
|
||||
// Cualquier id no resuelto (cuenta borrada) recibe un placeholder.
|
||||
for (const id of unique) {
|
||||
if (!map.has(id)) {
|
||||
map.set(id, { id, name: `#${id}`, gmlevel: 0, isStaff: false, joindate: null, postCount: 0 })
|
||||
map.set(id, {
|
||||
id,
|
||||
name: `#${id}`,
|
||||
gmlevel: 0,
|
||||
isStaff: false,
|
||||
joindate: null,
|
||||
postCount: 0,
|
||||
avatar: avatarForClass(classByAccount.get(id)),
|
||||
})
|
||||
}
|
||||
}
|
||||
return map
|
||||
|
||||
Reference in New Issue
Block a user