feat: Implement Event Deck, Monster Spawning, and AI Movement

This commit is contained in:
2026-01-05 00:40:12 +01:00
parent 056217437c
commit b619e4cee4
7 changed files with 421 additions and 35 deletions

View File

@@ -1,7 +1,9 @@
import { DungeonGenerator } from '../dungeon/DungeonGenerator.js';
import { TurnManager } from './TurnManager.js';
import { MonsterAI } from './MonsterAI.js';
import { HERO_DEFINITIONS } from '../data/Heroes.js';
import { MONSTER_DEFINITIONS } from '../data/Monsters.js';
import { createEventDeck, EVENT_TYPES } from '../data/Events.js';
/**
* GameEngine for Manual Dungeon Construction with Player Movement
@@ -10,10 +12,12 @@ export class GameEngine {
constructor() {
this.dungeon = new DungeonGenerator();
this.turnManager = new TurnManager();
this.ai = new MonsterAI(this); // Init AI
this.player = null;
this.selectedEntity = null;
this.isRunning = false;
this.plannedPath = []; // Array of {x,y}
this.eventDeck = createEventDeck();
// Callbacks
this.onEntityUpdate = null;
@@ -105,7 +109,12 @@ export class GameEngine {
return;
}
const id = `monster_${monsterKey}_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
// Ensure unique ID even in tight loops
if (!this._monsterIdCounter) this._monsterIdCounter = 0;
this._monsterIdCounter++;
const id = `monster_${monsterKey}_${Date.now()}_${this._monsterIdCounter}`;
console.log(`[GameEngine] Creating monster ${id} at ${x},${y}`);
const monster = {
id: id,
@@ -330,4 +339,98 @@ export class GameEngine {
return points;
}
onRoomRevealed(cells) {
console.log("[GameEngine] Room Revealed!");
// 1. Draw Event Card
if (this.eventDeck.length === 0) {
console.warn("Event deck empty, reshaping...");
this.eventDeck = createEventDeck();
}
const card = this.eventDeck.pop();
console.log(`[GameEngine] Event Drawn: ${card.name}`);
if (card.type === EVENT_TYPES.MONSTER) {
// 2. Determine Count
let count = 0;
if (typeof card.resolve === 'function') {
count = card.resolve(this, { cells });
} else {
count = 1; // Fallback
}
console.log(`[GameEngine] Spawning ${count} ${card.monsterKey}s`);
// 3. Find valid spawn spots
const availableCells = cells.filter(cell => {
const isHero = this.heroes.some(h => h.x === cell.x && h.y === cell.y);
const isMonster = this.monsters.some(m => m.x === cell.x && m.y === cell.y);
return !isHero && !isMonster;
});
console.log(`[GameEngine] Available Spawn Cells: ${availableCells.length}`, availableCells);
// Shuffle
for (let i = availableCells.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[availableCells[i], availableCells[j]] = [availableCells[j], availableCells[i]];
}
// 4. Spawn
let spawnedCount = 0;
for (let i = 0; i < count; i++) {
if (i < availableCells.length) {
const pos = availableCells[i];
console.log(`[GameEngine] Spawning at ${pos.x},${pos.y}`);
this.spawnMonster(card.monsterKey, pos.x, pos.y);
spawnedCount++;
} else {
console.warn("[GameEngine] Not enough space!");
break;
}
}
return {
type: 'MONSTER_SPAWN',
monsterKey: card.monsterKey,
count: spawnedCount,
cardName: card.name
};
}
return null;
}
// =========================================
// MONSTER AI & TURN LOGIC
// =========================================
playMonsterTurn() {
if (this.ai) {
this.ai.executeTurn();
}
}
// AI Helper methods moved to MonsterAI.js
isLeaderAdjacentToDoor(doorCells) {
// ... (Keep this one as it's used by main.js logic for doors)
if (!this.heroes || this.heroes.length === 0) return false;
const leader = this.getLeader();
if (!leader) return false;
const cells = Array.isArray(doorCells) ? doorCells : [doorCells];
for (const cell of cells) {
const dx = Math.abs(leader.x - cell.x);
const dy = Math.abs(leader.y - cell.y);
// Orthogonal adjacency check (Manhattan distance === 1)
if ((dx === 1 && dy === 0) || (dx === 0 && dy === 1)) {
return true;
}
}
return false;
}
}