DEVLOG update: Documenting reversion of GameRenderer refactoring to stable state

This commit is contained in:
2026-01-09 17:04:53 +01:00
parent 009c2a4135
commit 5888c59ba4
3 changed files with 94 additions and 0 deletions

View File

@@ -1,5 +1,26 @@
# Devlog - Warhammer Quest (Versión Web 3D) # Devlog - Warhammer Quest (Versión Web 3D)
## Sesión 12: Refactorización y Renderizado (Intento I)
**Fecha:** 9 de Enero de 2026
### Objetivos
- Refactorizar visualmente el `GameRenderer.js` para dividirlo en submódulos ordenados (`Dungeon`, `Entity`, `Effect`, `Interaction`).
- Mejorar la escalabilidad del código de renderizado y separar responsabilidades.
### Ocurrido
- Se realizó un intento completo de refactorización que, aunque arquitectónicamente sólido, introdujo regresiones visuales críticas:
- **Pantalla Negra**: Debido a una inicialización del canvas basada en un contenedor de altura 0, corregida con `window.innerHeight`.
- **Pérdida de Estilo Píxel**: Las miniaturas de los héroes se veían borrosas/blanquecinas por olvidar `THREE.SRGBColorSpace` y `minFilter/magFilter: Nearest`.
- **Cambio de Estilo**: La visualización de rutas de movimiento era diferente a la original.
### Acción Táctica
- **Reversión (Rollback)**: Se decidió revertir todos los cambios de renderizado (`git restore`) para volver a un estado visualmente perfecto y comenzar la refactorización (v2) de forma segura y controlada, aplicando las lecciones aprendidas (altura del canvas, filtros de textura) desde el principio.
### Próximos Pasos (Inmediato)
- Re-implementar la refactorización de `GameRenderer` de forma "Quirúrgica", copiando la lógica visual original línea por línea para no alterar la estética pixel-art ni el comportamiento del juego.
---
## Sesión 11: Sistema de Iniciativa y Niebla de Guerra (Fog of War) ## Sesión 11: Sistema de Iniciativa y Niebla de Guerra (Fog of War)
**Fecha:** 9 de Enero de 2026 **Fecha:** 9 de Enero de 2026
@@ -37,6 +58,10 @@
- **Ocultación de Entidades en Niebla**: Los héroes y monstruos que quedan fuera del alcance de la lámpara (losetas no visibles) ahora desaparecen completamente de la vista, aumentando la inmersión. - **Ocultación de Entidades en Niebla**: Los héroes y monstruos que quedan fuera del alcance de la lámpara (losetas no visibles) ahora desaparecen completamente de la vista, aumentando la inmersión.
- **Salto de Turno Automático**: Si un héroe comienza su turno en una zona oscura (oculta por la Niebla de Guerra), pierde automáticamente su turno hasta que sea "rescatado" (iluminado de nuevo) por el Portador de la Lámpara. - **Salto de Turno Automático**: Si un héroe comienza su turno en una zona oscura (oculta por la Niebla de Guerra), pierde automáticamente su turno hasta que sea "rescatado" (iluminado de nuevo) por el Portador de la Lámpara.
- **Botones de Fase**: Se ha reorganizado la barra superior. El botón de "Acabar Fase" ahora comparte espacio con un nuevo botón "Acabar Turno" específico para cada héroe, facilitando el flujo de juego sin tener que buscar en la ficha del personaje. - **Botones de Fase**: Se ha reorganizado la barra superior. El botón de "Acabar Fase" ahora comparte espacio con un nuevo botón "Acabar Turno" específico para cada héroe, facilitando el flujo de juego sin tener que buscar en la ficha del personaje.
- **Sistema de Destrabado (Break Away)**: Implementada la mecánica para escapar del combate cuerpo a cuerpo ("Trabado").
- Los héroes trabados verán un botón de "Destrabarse" en su ficha.
- Al pulsarlo, el sistema lanza 1D6 contra el valor `pin_target` del héroe.
- Éxito: El héroe recupera su libertad de movimiento. Fallo: El héroe pierde su movimiento y debe luchar.
### Estado Actual ### Estado Actual
El juego ahora respeta las reglas de visión y turno del juego de mesa original con una fidelidad visual alta. La sensación de exploración es más tensa al ocultarse las zonas lejanas ("se perderán en la oscuridad"), y el orden táctico es crucial. La UI es más intuitiva y limpia durante el combate. El juego ahora respeta las reglas de visión y turno del juego de mesa original con una fidelidad visual alta. La sensación de exploración es más tensa al ocultarse las zonas lejanas ("se perderán en la oscuridad"), y el orden táctico es crucial. La UI es más intuitiva y limpia durante el combate.

View File

@@ -77,6 +77,7 @@ export class GameEngine {
this.heroes.forEach(hero => { this.heroes.forEach(hero => {
hero.currentMoves = hero.stats.move; hero.currentMoves = hero.stats.move;
hero.hasAttacked = false; hero.hasAttacked = false;
hero.hasEscapedPin = false; // Reset pin escape status
}); });
console.log("Refilled Hero Moves"); console.log("Refilled Hero Moves");
} }
@@ -598,6 +599,9 @@ export class GameEngine {
isEntityPinned(entity) { isEntityPinned(entity) {
if (!this.monsters || this.monsters.length === 0) return false; if (!this.monsters || this.monsters.length === 0) return false;
// If already escaped this turn, not pinned
if (entity.hasEscapedPin) return false;
return this.monsters.some(m => { return this.monsters.some(m => {
if (m.isDead) return false; if (m.isDead) return false;
const dx = Math.abs(entity.x - m.x); const dx = Math.abs(entity.x - m.x);
@@ -629,6 +633,26 @@ export class GameEngine {
}); });
} }
attemptBreakAway(hero) {
if (!hero || hero.hasEscapedPin) return { success: false, roll: 0 };
const roll = Math.floor(Math.random() * 6) + 1;
const target = hero.stats.pin_target || 6;
const success = roll >= target;
if (success) {
hero.hasEscapedPin = true;
} else {
// Failed to escape: Unit loses movement and ranged attacks?
// "The Adventurer must stay where he is and fight"
// So movement becomes 0.
hero.currentMoves = 0;
}
return { success, roll, target };
}
// Alias for legacy calls if any // Alias for legacy calls if any
deselectPlayer() { deselectPlayer() {
this.deselectEntity(); this.deselectEntity();

View File

@@ -231,6 +231,51 @@ export class UnitCardManager {
card.appendChild(bowBtn); card.appendChild(bowBtn);
} }
// Break Away Button (Destrabarse)
const isPinned = this.game.isEntityPinned(hero) && !hero.hasEscapedPin;
const canTryBreak = isPinned && hero.currentMoves > 0 && !hero.hasAttacked; // Can only try if hasn't acted yet?
// Rules say: "can attempt to escape... if achieved... moves as normal".
// If fails "must stay and fight".
if (isPinned) {
const breakBtn = document.createElement('button');
const target = hero.stats.pin_target || 6;
breakBtn.textContent = `🏃 DESTRABARSE (${target}+)`;
Object.assign(breakBtn.style, {
width: '100%', padding: '8px', marginTop: '8px',
color: '#fff', border: '1px solid #FFA500', borderRadius: '4px',
fontFamily: '"Cinzel", serif',
cursor: canTryBreak ? 'pointer' : 'not-allowed',
backgroundColor: canTryBreak ? '#FF8C00' : '#555'
});
if (!canTryBreak) {
if (hero.hasAttacked) breakBtn.title = "Ya has atacado.";
else if (hero.currentMoves <= 0) breakBtn.title = "No tienes movimiento.";
} else {
breakBtn.onclick = (e) => {
e.stopPropagation();
const result = this.game.attemptBreakAway(hero);
// Show result
const color = result.success ? '#00ff00' : '#ff0000';
const msg = result.success ? "¡Escapada con éxito!" : "¡Fallo! Debes luchar.";
if (this.callbacks.showModal) {
this.callbacks.showModal(
result.success ? '¡Destrabado!' : '¡Atrapado!',
`Resultado del dado: <b style="color:${color}">${result.roll}</b> (Necesitabas ${result.target}+)<br>${msg}`
);
}
// Update UI (Refresh card to show movement unlocked or locked)
this.updateHeroCard(hero.id);
if (this.callbacks.refresh) this.callbacks.refresh(); // Or just let update handle it
};
}
card.appendChild(breakBtn);
}
// Inventory // Inventory
const invBtn = document.createElement('button'); const invBtn = document.createElement('button');
invBtn.textContent = '🎒 INVENTARIO'; invBtn.textContent = '🎒 INVENTARIO';