feat: Implement Event Deck, Monster Spawning, and AI Movement
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user