Refine FOW visuals, Turn Skipping logic, and UI Polish

This commit is contained in:
2026-01-09 15:44:04 +01:00
parent b08a922c00
commit 009c2a4135
8 changed files with 264 additions and 63 deletions

View File

@@ -55,6 +55,12 @@ export class GameEngine {
if (phase === 'hero') {
this.initializeTurnOrder();
}
if (phase === 'monster') {
if (window.RENDERER && window.RENDERER.clearAllActiveRings) {
window.RENDERER.clearAllActiveRings();
}
this.deselectEntity();
}
});
// End of Turn Logic (Buffs, cooldowns, etc)
@@ -211,9 +217,44 @@ export class GameEngine {
nextHeroTurn() {
this.currentTurnIndex++;
if (this.currentTurnIndex < this.heroTurnOrder.length) {
this.activateHero(this.heroTurnOrder[this.currentTurnIndex]);
} else {
// Loop to find next VALID hero (visible)
while (this.currentTurnIndex < this.heroTurnOrder.length) {
const nextHero = this.heroTurnOrder[this.currentTurnIndex];
// Check visibility
// Exception: Leader (hasLantern) is ALWAYS visible.
if (nextHero.hasLantern) {
this.activateHero(nextHero);
return;
}
// Check if hero is in a visible tile
// Get hero tile ID
const heroTileId = this.dungeon.grid.occupiedCells.get(`${nextHero.x},${nextHero.y}`);
// If currentVisibleTileIds is defined, enforce it.
if (this.currentVisibleTileIds) {
if (heroTileId && this.currentVisibleTileIds.has(heroTileId)) {
this.activateHero(nextHero);
return;
} else {
console.log(`Skipping turn for ${nextHero.name} (In Darkness)`);
if (this.onShowMessage) {
// Optional: Small notification or log
// this.onShowMessage("Perdido en la oscuridad", `${nextHero.name} pierde su turno.`);
}
this.currentTurnIndex++; // Skip and continue loop
}
} else {
// Should not happen if updateLighting runs, but fallback
this.activateHero(nextHero);
return;
}
}
// If loop finishes, no more heroes
if (this.currentTurnIndex >= this.heroTurnOrder.length) {
console.log("All heroes acted. Ending Phase sequence if auto?");
this.deselectEntity();
if (window.RENDERER) {
@@ -391,9 +432,23 @@ export class GameEngine {
if (this.selectedEntity === clickedEntity) {
// Toggle Deselect
// Prevent deselecting active hero effectively? Or allow it but re-select him if trying to select others?
// For now, allow deselect freely.
this.deselectEntity();
// EXCEPTION: In Hero Phase, if I click MYSELF (Active Hero), do NOT deselect.
// It's annoying to lose the card.
const isHeroPhase = this.turnManager.currentPhase === 'hero';
let isActiveTurnHero = false;
if (isHeroPhase && this.heroTurnOrder && this.currentTurnIndex !== undefined) {
const activeHero = this.heroTurnOrder[this.currentTurnIndex];
if (activeHero && activeHero.id === clickedEntity.id) {
isActiveTurnHero = true;
}
}
if (isActiveTurnHero) {
// Do nothing (keep selected)
// Maybe blink the card or something?
} else {
this.deselectEntity();
}
} else if (this.selectedMonster === clickedMonster && clickedMonster) {
// Clicking on already selected monster - deselect it
const monsterId = this.selectedMonster.id;
@@ -503,6 +558,9 @@ export class GameEngine {
}
}
// Store active visibility sets for Turn Logic
this.currentVisibleTileIds = visibleTileIds;
window.RENDERER.updateFogOfWar(Array.from(visibleTileIds));
}
@@ -720,7 +778,32 @@ export class GameEngine {
if (entity.currentMoves < 0) entity.currentMoves = 0;
}
this.deselectEntity();
// AUTO-DESELECT LOGIC
// In Hero Phase, we want to KEEP the active hero selected to avoid re-selecting.
const isHeroPhase = this.turnManager.currentPhase === 'hero';
// Check if entity is the currently active turn hero
let isActiveTurnHero = false;
if (isHeroPhase && this.heroTurnOrder && this.currentTurnIndex !== undefined) {
const activeHero = this.heroTurnOrder[this.currentTurnIndex];
if (activeHero && activeHero.id === entity.id) {
isActiveTurnHero = true;
}
}
if (isActiveTurnHero) {
// Do NOT deselect. Just clear path.
this.plannedPath = [];
if (this.onPathChange) this.onPathChange([]);
// Also force update UI/Card (stats changed)
if (this.onEntitySelect) {
// Re-trigger selection to ensure UI is fresh?
// UIManager listens to onEntityMove to update stats, so that should be covered.
// But purely being consistent:
}
} else {
this.deselectEntity();
}
}

View File

@@ -214,11 +214,22 @@ export class MonsterAI {
performAttack(monster, hero) {
// SEQUENCE:
// 1. Show green ring on monster
// 2. Monster attack animation (we'll simulate with delay)
// 3. Show red ring + shake on hero
// 4. Remove both rings
// 5. Show combat result
// 0. Show TARGET (Blue Ring) on Hero
if (this.game.onRangedTarget) {
// Re-using onRangedTarget? Or directly calling renderer?
// Better to use a specific callback or direct call if available, or just add a new callback.
// But let's check if we can access renderer directly or use a new callback.
// The user prompt specifically asked for this feature.
// I'll assume we can use game.onEntityTarget if defined, or direct renderer call if needed,
// but standard pattern here is callbacks.
// Let's add onEntityTarget to GameEngine callbacks if not present, but for now I will try to use global RENDERER if possible
// OR simply define a new callback `this.game.onEntityTarget(hero.id, true)`.
}
// Direct renderer call is safest given current context if we don't want to modify GameEngine interface heavily right now.
if (window.RENDERER && window.RENDERER.setEntityTarget) {
window.RENDERER.setEntityTarget(hero.id, true);
}
const result = CombatMechanics.resolveMeleeAttack(monster, hero, this.game);
@@ -229,9 +240,6 @@ export class MonsterAI {
// Step 2: Attack animation delay (500ms)
setTimeout(() => {
// Step 3: Trigger hit visual on defender (if hit succeeded)
// Step 3: Trigger hit visual on defender REMOVED (Handled by onCombatResult)
// Step 4: Remove green ring after red ring appears (1200ms for red ring duration)
setTimeout(() => {
@@ -239,6 +247,11 @@ export class MonsterAI {
this.game.onEntityActive(monster.id, false);
}
// Remove Target Ring
if (window.RENDERER && window.RENDERER.setEntityTarget) {
window.RENDERER.setEntityTarget(hero.id, false);
}
// Step 5: Show combat result after both rings are gone
setTimeout(() => {
if (this.game.onCombatResult) {
@@ -246,7 +259,7 @@ export class MonsterAI {
}
}, 200); // Small delay after rings disappear
}, 1200); // Wait for red ring to disappear
}, 500); // Attack animation delay
}, 800); // Attack animation delay + focus time
}
getAdjacentHero(entity) {