feat: Sistema de combate completo con tarjetas de personajes y animaciones

- Tarjetas de héroes y monstruos con tokens circulares
- Sistema de selección: héroe + monstruo para atacar
- Botón de ATACAR en tarjeta de monstruo
- Animación de muerte: fade-out + hundimiento (1.5s)
- Visualización de estadísticas completas (WS, BS, S, T, W, I, A, Mov)
- Placeholder cuando no hay héroe seleccionado
- Tokens de héroes y monstruos en formato circular
- Deselección correcta de monstruos
- Fix: paso de gameEngine a CombatMechanics para callbacks de muerte
This commit is contained in:
2026-01-06 18:43:09 +01:00
parent 3efbf8d5fb
commit 7b28fcf1b0
22 changed files with 1513 additions and 25 deletions

View File

@@ -290,6 +290,32 @@ export class GameRenderer {
};
}
triggerDeathAnimation(entityId) {
const mesh = this.entities.get(entityId);
if (!mesh) return;
console.log(`[GameRenderer] Triggering death animation for ${entityId}`);
// Start fade-out animation
const startTime = performance.now();
const duration = 1500; // 1.5 seconds fade out
mesh.userData.death = {
startTime: startTime,
duration: duration,
initialOpacity: 1.0
};
// Remove entity from map after animation completes
setTimeout(() => {
if (mesh && mesh.parent) {
mesh.parent.remove(mesh);
}
this.entities.delete(entityId);
console.log(`[GameRenderer] Removed entity ${entityId} from scene`);
}, duration);
}
moveEntityAlongPath(entity, path) {
const mesh = this.entities.get(entity.id);
if (mesh) {
@@ -370,6 +396,38 @@ export class GameRenderer {
mesh.position.copy(data.shake.originalPos);
delete data.shake;
}
} else if (data.death) {
// HANDLE DEATH FADE-OUT
const elapsed = time - data.death.startTime;
const progress = Math.min(elapsed / data.death.duration, 1);
// Fade out opacity
const opacity = data.death.initialOpacity * (1 - progress);
// Apply opacity to all materials in the mesh
mesh.traverse((child) => {
if (child.material) {
if (Array.isArray(child.material)) {
child.material.forEach(mat => {
mat.transparent = true;
mat.opacity = opacity;
});
} else {
child.material.transparent = true;
child.material.opacity = opacity;
}
}
});
// Also fade down (sink into ground)
if (data.death.initialY === undefined) {
data.death.initialY = mesh.position.y;
}
mesh.position.y = data.death.initialY - (progress * 0.5);
if (progress >= 1) {
delete data.death;
}
}