Apariencia
Arquitectura del Sistema - Aureum
Esta documentación está dirigida a desarrolladores. Para información sobre el juego dirigida a jugadores, ver Primeros pasos, Combate, Items y Hechizos.
Sistema de Atributos y Modificadores
Arquitectura Base vs. Calculado
El sistema de atributos sigue una arquitectura robusta que separa claramente los valores base de los valores finales calculados:
┌─────────────────────────────────────────┐
│ BaseAttributes │
│ (Valores base del personaje) │
│ - hp: 100 │
│ - maxHp: 100 │
│ - mana: 50 │
│ - maxMana: 50 │
│ (NUNCA se modifican directamente) │
└─────────────────────────────────────────┘
│
│ + modificadores
▼
┌─────────────────────────────────────────┐
│ EquippedItems (Map) │
│ - Slot1: Item con +20 maxHp │
│ - Slot2: Item con +10 maxMana │
└─────────────────────────────────────────┘
│
│ getAllModifiers()
▼
┌─────────────────────────────────────────┐
│ ActiveBuffs (Map) │
│ - Buff1: +5 damage (expira en 30s) │
│ - Buff2: +10% crit (expira en 60s) │
└─────────────────────────────────────────┘
│
│ calculateSetBonuses()
▼
┌─────────────────────────────────────────┐
│ ItemSets │
│ - warrior_basic: 3 piezas → bonos │
└─────────────────────────────────────────┘
│
│ getFinalAttributes()
▼
┌─────────────────────────────────────────┐
│ Atributos Finales (Cacheado) │
│ - maxHp: 100 + 20 + 30 = 150 │
│ - maxMana: 50 + 10 = 60 │
│ - hp: min(100, 150) = 100 │
└─────────────────────────────────────────┘Sistema de Cache Inteligente
Los atributos finales se calculan dinámicamente, pero se cachean para optimizar el rendimiento:
- Cache automático: Los atributos finales se cachean después del primer cálculo.
- Invalidación inteligente: El cache se invalida automáticamente cuando:
- Se equipa o desequipa un item.
- Se aplica o remueve un buff.
- Se registran nuevos sets de items.
- Versión de modificadores: Cada cambio incrementa un contador de versión que permite verificar si el cache es válido.
Mantenimiento de Porcentaje de Recursos
Cuando se equipa o desequipa un item que cambia los máximos de HP/Mana/Stamina, el sistema mantiene el porcentaje actual:
Ejemplo:
- Estado inicial: 50/150 HP (33%)
- Equipas item que aumenta maxHp a 200
- Resultado: 66/200 HP (mantiene 33%)
Implementación:
typescript
const oldMaxHp = player.getFinalAttributes().maxHp;
// ... equipar/desequipar item ...
const newMaxHp = player.getFinalAttributes().maxHp;
const hpPercentage = player.baseAttributes.hp / oldMaxHp;
player.baseAttributes.hp = Math.min(newMaxHp, Math.floor(oldMaxHp * hpPercentage));Persistencia de Datos
Regla crítica: En la base de datos solo se guardan valores base, nunca valores con modificadores.
- Al guardar: Se guarda
player.baseAttributes.hp(sin modificadores). - Al cargar: Se carga como HP base y luego se calculan los atributos finales con los items equipados.
- Razón: Evita que los bonos de items se acumulen incorrectamente al recargar el personaje.
Sistema de Buffs Temporales
Los buffs son aplicados por consumibles y hechizos (ver efectos
buff/debuff/status_effect), y también pueden venir de auras toggle que tiquean cada N ms. Los modificadores que aplican son del mismo tipo que los de items.
Características
- Duración: Cada buff tiene un timestamp de expiración (
expiresAt). - Auto-limpieza: Los buffs expirados se eliminan automáticamente en cada tick del servidor.
- Modificadores: Los buffs aplican modificadores igual que los items.
- Fuente: Cada buff puede tener una fuente identificada (
item,spell,consumable, etc.).
Ciclo de Vida
- Aplicación:
player.applyBuff(buff)- Agrega el buff y marca el cache como inválido. - Cálculo: Los buffs activos se incluyen en
getAllModifiers(). - Expiración: En cada tick,
cleanupExpiredBuffs()remueve los buffs expirados. - Remoción manual:
player.removeBuff(buffId)- Remueve un buff específico.
Ejemplo de Uso
typescript
// Aplicar buff de poción de fuerza (30 segundos)
player.applyBuff({
id: 'potion_strength',
name: 'Poción de Fuerza',
modifiers: { minDamage: 5, maxDamage: 10 },
expiresAt: Date.now() + 30000,
source: 'consumable'
});
// El buff se aplica automáticamente en getFinalAttributes()
// Después de 30 segundos, se remueve automáticamenteSistema de Sets de Items
Funcionamiento
- Definición: Los sets se definen en
shared/data/itemSets.ts. - Agrupación: Los items tienen un campo
setIdque los agrupa. - Conteo: El servidor cuenta automáticamente cuántas piezas de cada set están equipadas.
- Bonos: Se aplican todos los bonos que cumplan el requisito de piezas.
Ejemplo
Si tienes 3 piezas del Set del Guerrero:
- Obtienes el bono de 2 piezas: +30 HP, +5 Defensa Física
- Obtienes el bono de 3 piezas: +50 HP, +10 Defensa Física, +3-5 Daño
- Total: +80 HP, +15 Defensa Física, +3-5 Daño
Acceso desde Cliente
Los sets están disponibles tanto en el servidor como en el cliente:
typescript
import { getAllItemSets, getItemSet } from '@shared/data/itemSets';
// Obtener todos los sets
const allSets = getAllItemSets();
// Obtener un set específico
const warriorSet = getItemSet('warrior_basic');
// Verificar cuántas piezas están equipadas
function getEquippedSetPieces(equippedItems: Map<string, Item>, setId: string): number {
let count = 0;
for (const item of equippedItems.values()) {
if (item.setId === setId) {
count++;
}
}
return count;
}Sistema de Renderizado de Cascos
Separación Head vs. Helm
headId: ID de la cabeza del personaje (determinada por raza/sexo, siempre visible).helmId: ID del casco equipado (se dibuja encima de la cabeza).
Gráficos
Los cascos usan sprites en /assets/head/headX.png donde X es el helmId:
head1.png- Casco de Guerrerohead2.png- Casco de Hierro
Renderizado
- Se dibuja primero la cabeza base (
headId). - Si hay un casco equipado (
helmId > 0), se dibuja encima en la misma posición. - Ambos sprites se actualizan cuando cambia la dirección del personaje.
Optimizaciones Implementadas
Cache de Atributos Finales
- Problema: Calcular atributos finales en cada acceso es costoso.
- Solución: Cache con invalidación automática basada en versión de modificadores.
- Beneficio: Mejora significativa del rendimiento en cálculos frecuentes.
Cálculo Dinámico de Modificadores
- Ventaja: No hay que "deshacer" modificadores manualmente.
- Funcionamiento: Los modificadores se calculan desde cero cada vez, sumando solo los items/buffs/sets activos.
- Resultado: Imposible que los modificadores se acumulen incorrectamente.
Limpieza Automática de Buffs
- Problema: Buffs expirados ocuparían memoria indefinidamente.
- Solución: Auto-limpieza en cada tick del servidor.
- Beneficio: Gestión automática de recursos sin intervención manual.
Mejores Prácticas
Para Desarrolladores
- Nunca modificar
baseAttributesdirectamente con valores calculados. - Siempre usar
getFinalAttributes()para obtener valores con modificadores. - Guardar solo valores base en la base de datos.
- Invalidar el cache cuando se modifiquen items o buffs (automático en los métodos públicos).
Para Diseñadores de Items
- Los modificadores se suman: Si dos items dan +10 HP cada uno, el total es +20 HP.
- Los sets otorgan bonos adicionales: Se suman a los modificadores individuales de los items.
- Los buffs temporales se suman: Se combinan con modificadores de items y sets.
Flujo Completo de Cálculo
1. Player tiene baseAttributes: { hp: 100, maxHp: 100 }
2. Equipa item con +20 maxHp
→ getAllModifiers() suma: { hp: 20 }
→ getFinalAttributes() calcula: { hp: 100, maxHp: 120 }
→ Cache se guarda
3. Aplica buff con +10 maxHp (30 segundos)
→ getAllModifiers() suma: { hp: 20 + 10 }
→ getFinalAttributes() calcula: { hp: 100, maxHp: 130 }
→ Cache se invalida y recalcula
4. Equipa 3 piezas del Set del Guerrero
→ calculateSetBonuses() suma bonos de 2 y 3 piezas
→ getAllModifiers() incluye bonos del set
→ getFinalAttributes() calcula con todos los modificadores
5. Buff expira (30 segundos pasaron)
→ cleanupExpiredBuffs() remueve el buff
→ Cache se invalida
→ getFinalAttributes() recalcula sin el buff