feat(game-loop): implement strict phase rules, exploration stops, and hero attacks
This commit is contained in:
100
src/main.js
100
src/main.js
@@ -37,15 +37,15 @@ generator.grid.placeTile = (instance, variant, card) => {
|
||||
setTimeout(() => {
|
||||
renderer.renderExits(generator.availableExits);
|
||||
|
||||
// Check if new tile is a ROOM to trigger events
|
||||
// Note: 'room_dungeon' includes standard room card types
|
||||
if (card.type === 'room' || card.id.startsWith('room')) {
|
||||
const eventResult = game.onRoomRevealed(cells);
|
||||
if (eventResult && eventResult.count > 0) {
|
||||
// Show notification?
|
||||
ui.showModal('¡Emboscada!', `Al entrar en la estancia, aparecen <b>${eventResult.count} Orcos</b>!`);
|
||||
// NEW RULE: Exploration ends turn immediately. No monsters yet.
|
||||
// Monsters appear when a hero ENTERS the new room in the next turn.
|
||||
ui.showModal('Exploración Completada',
|
||||
'Has colocado una nueva sección de mazmorra.<br>El turno termina aquí.',
|
||||
() => {
|
||||
game.turnManager.endTurn();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}, 50);
|
||||
};
|
||||
|
||||
@@ -63,12 +63,37 @@ game.onEntityUpdate = (entity) => {
|
||||
|
||||
game.turnManager.on('phase_changed', (phase) => {
|
||||
if (phase === 'monster') {
|
||||
setTimeout(() => {
|
||||
game.playMonsterTurn();
|
||||
setTimeout(async () => {
|
||||
await game.playMonsterTurn();
|
||||
|
||||
// Logic: Skip Exploration if monsters are alive
|
||||
const hasActiveMonsters = game.monsters && game.monsters.some(m => !m.isDead);
|
||||
|
||||
if (hasActiveMonsters) {
|
||||
ui.showModal('¡Combate en curso!',
|
||||
'Aún quedan monstruos vivos. Se salta la Fase de Exploración.<br>Preparaos para la <b>Fase de Poder</b> del siguiente turno.',
|
||||
() => {
|
||||
// Combat Loop: Power -> Hero -> Monster -> (Skip Exp) -> Power...
|
||||
game.turnManager.endTurn();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
ui.showModal('Zona Despejada',
|
||||
'Fase de Monstruos Finalizada.<br>Pulsa para continuar a la Fase de Exploración.',
|
||||
() => {
|
||||
game.turnManager.nextPhase(); // Go to Exploration
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}, 500); // Slight delay for visual impact
|
||||
}
|
||||
});
|
||||
|
||||
game.onCombatResult = (log) => {
|
||||
ui.showCombatLog(log);
|
||||
};
|
||||
|
||||
game.onEntityMove = (entity, path) => {
|
||||
renderer.moveEntityAlongPath(entity, path);
|
||||
};
|
||||
@@ -109,9 +134,11 @@ game.onPathChange = (path) => {
|
||||
|
||||
// 6. Handle Clicks
|
||||
const handleClick = (x, y, doorMesh) => {
|
||||
const currentPhase = game.turnManager.currentPhase;
|
||||
const hasActiveMonsters = game.monsters && game.monsters.some(m => !m.isDead);
|
||||
|
||||
// PRIORITY 1: Tile Placement Mode - ignore all clicks
|
||||
if (generator.state === 'PLACING_TILE') {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,6 +151,18 @@ const handleClick = (x, y, doorMesh) => {
|
||||
|
||||
if (!doorMesh.userData.isOpen) {
|
||||
|
||||
// CHECK PHASE: Exploration Only
|
||||
if (currentPhase !== 'exploration') {
|
||||
ui.showModal('Fase Incorrecta', 'Solo puedes explorar (abrir puertas) durante la <b>Fase de Exploración</b>.');
|
||||
return;
|
||||
}
|
||||
|
||||
// CHECK MONSTERS: Must be clear
|
||||
if (hasActiveMonsters) {
|
||||
ui.showModal('¡Peligro!', 'No puedes explorar mientras hay <b>Monstruos</b> cerca. ¡Acaba con ellos primero!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Check Selection and Leadership (STRICT)
|
||||
const selectedHero = game.selectedEntity;
|
||||
|
||||
@@ -138,8 +177,6 @@ const handleClick = (x, y, doorMesh) => {
|
||||
}
|
||||
|
||||
// 2. Check Adjacency
|
||||
// Since we know selectedHero IS the leader, we can just check if *this* hero is adjacent.
|
||||
// game.isLeaderAdjacentToDoor checks the 'getLeader()' position, which aligns with selectedHero here.
|
||||
if (game.isLeaderAdjacentToDoor(doorMesh.userData.cells)) {
|
||||
|
||||
// Open door visually
|
||||
@@ -149,11 +186,6 @@ const handleClick = (x, y, doorMesh) => {
|
||||
const exitData = doorMesh.userData.exitData;
|
||||
if (exitData) {
|
||||
generator.selectDoor(exitData);
|
||||
|
||||
// Allow UI to update phase if not already
|
||||
// if (game.turnManager.currentPhase !== 'exploration') {
|
||||
// game.turnManager.setPhase('exploration');
|
||||
// }
|
||||
} else {
|
||||
console.error('[Main] Door missing exitData');
|
||||
}
|
||||
@@ -166,6 +198,17 @@ const handleClick = (x, y, doorMesh) => {
|
||||
|
||||
// PRIORITY 3: Normal cell click (player selection/movement)
|
||||
if (x !== null && y !== null) {
|
||||
// Restrict Hero Selection/Movement to Hero Phase (and verify logic in GameEngine handle selection)
|
||||
// Actually, we might want to select heroes in other phases to see stats, but MOVE only in Hero Phase.
|
||||
// GameEngine.planStep handles planning.
|
||||
|
||||
// We let GameEngine handle selection. But for movement planning...
|
||||
// Let's modify onCellClick inside GameEngine or just block here?
|
||||
// Blocking execution is safer.
|
||||
|
||||
// Wait, onCellClick handles Selection AND Planning.
|
||||
// We'll let it select. But we hook executeMovePath separately.
|
||||
|
||||
game.onCellClick(x, y);
|
||||
}
|
||||
};
|
||||
@@ -193,10 +236,31 @@ window.addEventListener('keydown', (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
game.onEventTriggered = (eventResult) => {
|
||||
if (eventResult) {
|
||||
if (eventResult.type === 'MONSTER_SPAWN') {
|
||||
const count = eventResult.count || 0;
|
||||
ui.showModal('¡Emboscada!', `Al entrar en la estancia, aparecen <b>${count} Enemigos</b>!<br>Tu movimiento se detiene.`);
|
||||
} else if (eventResult.message) {
|
||||
ui.showModal('Zona Explorada', `${eventResult.message}<br>Tu movimiento se detiene.`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 7. Start
|
||||
|
||||
game.startMission(mission);
|
||||
|
||||
// Mark initial tile as visited to prevent immediate trigger
|
||||
if (game.heroes && game.heroes.length > 0) {
|
||||
const h = game.heroes[0];
|
||||
const initialTileId = game.dungeon.grid.occupiedCells.get(`${h.x},${h.y}`);
|
||||
if (initialTileId) {
|
||||
game.visitedRoomIds.add(initialTileId);
|
||||
console.log(`[Main] Initial tile ${initialTileId} marked as visited.`);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Render Loop
|
||||
const animate = (time) => {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
Reference in New Issue
Block a user