mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-18 14:51:26 +02:00
172 lines
6.9 KiB
JavaScript
172 lines
6.9 KiB
JavaScript
const ROT = require('./rot.js');
|
|
const Player = require('./player.js');
|
|
const Monster = require('./monster.js');
|
|
const DungeonGenerator = require('./dungeon.js');
|
|
|
|
class Game {
|
|
constructor(socketId, mapWidth, mapHeight) {
|
|
this.socketId = socketId;
|
|
this.width = mapWidth || 25; // Reduced default size to match client display
|
|
this.height = mapHeight || 19; // Reduced default size to match client display
|
|
this.gameState = 'waiting'; // waiting, active, won, lost
|
|
this.startBlock = null;
|
|
this.dungeon = null;
|
|
this.player = new Player();
|
|
this.monster = new Monster();
|
|
this.fee = 0; // Amount player paid
|
|
this.visibleTiles = {}; // Will store visible tiles
|
|
this.fov = null; // FOV calculator
|
|
this.generateDungeon();
|
|
}
|
|
|
|
generateDungeon() {
|
|
this.dungeon = DungeonGenerator.generate(this.width, this.height);
|
|
|
|
if (this.dungeon.entrance) {
|
|
this.player.moveTo(this.dungeon.entrance[0], this.dungeon.entrance[1]);
|
|
}
|
|
|
|
// Place monster far from entrance
|
|
if (this.dungeon.rooms && this.dungeon.rooms.length > 2) {
|
|
const monsterRoom = this.dungeon.rooms[Math.floor(this.dungeon.rooms.length * 0.7)];
|
|
const center = monsterRoom.getCenter();
|
|
this.monster.moveTo(center[0], center[1]);
|
|
}
|
|
|
|
// Initialize FOV calculator
|
|
this.fov = new ROT.FOV.PreciseShadowcasting(
|
|
(x, y) => {
|
|
// Return true if the tile is transparent (can see through it)
|
|
return this.dungeon.map[y] && this.dungeon.map[y][x] === 0;
|
|
}
|
|
);
|
|
|
|
// Calculate initial FOV
|
|
this.updateFOV();
|
|
|
|
// Set game state to active
|
|
this.gameState = 'active';
|
|
}
|
|
|
|
updateFOV() {
|
|
// Reset visible tiles
|
|
this.visibleTiles = {};
|
|
|
|
// Calculate new visible tiles
|
|
this.fov.compute(this.player.x, this.player.y, 10, (x, y, r, visibility) => { // Added r and visibility params
|
|
if (visibility > 0) { // Only add if tile is actually visible
|
|
if (!this.visibleTiles[y]) {
|
|
this.visibleTiles[y] = {};
|
|
}
|
|
// Store the actual tile type (0 for floor, 1 for wall) from the dungeon map
|
|
if (this.dungeon && this.dungeon.map && this.dungeon.map[y] && this.dungeon.map[y][x] !== undefined) {
|
|
this.visibleTiles[y][x] = this.dungeon.map[y][x];
|
|
} else {
|
|
// Should not happen if FOV is within map bounds, but as a fallback:
|
|
// this.visibleTiles[y][x] = 1; // Default to wall if outside known map
|
|
}
|
|
}
|
|
});
|
|
// console.log("FOV updated. Player:", this.player.x, this.player.y, "Visible tiles count:", Object.keys(this.visibleTiles).reduce((acc, k) => acc + Object.keys(this.visibleTiles[k]).length, 0));
|
|
}
|
|
|
|
movePlayer(dx, dy) {
|
|
const newX = this.player.x + dx;
|
|
const newY = this.player.y + dy;
|
|
|
|
// Debug logging for movement validation
|
|
console.log(`[DEBUG] Move attempt: from (${this.player.x},${this.player.y}) to (${newX},${newY}) with dx=${dx},dy=${dy}`);
|
|
console.log(`[DEBUG] Map bounds: width=${this.width}, height=${this.height}`);
|
|
console.log(`[DEBUG] Dungeon exists: ${!!this.dungeon}`);
|
|
if (this.dungeon && this.dungeon.map) {
|
|
console.log(`[DEBUG] Map row ${newY} exists: ${!!this.dungeon.map[newY]}`);
|
|
if (this.dungeon.map[newY]) {
|
|
console.log(`[DEBUG] Map cell [${newY}][${newX}] value: ${this.dungeon.map[newY][newX]} (0=floor, 1=wall)`);
|
|
}
|
|
}
|
|
|
|
// Check if the move is valid (not into a wall and within map bounds)
|
|
if (this.dungeon &&
|
|
this.dungeon.map[newY] &&
|
|
this.dungeon.map[newY][newX] !== undefined &&
|
|
this.dungeon.map[newY][newX] === 0) { // 0 is floor
|
|
|
|
this.player.moveTo(newX, newY);
|
|
console.log(`Player moved to ${newX},${newY} in game for socket ${this.socketId}`);
|
|
|
|
// Update FOV after moving
|
|
this.updateFOV();
|
|
|
|
// Check for game events like finding treasure or exit
|
|
if (this.dungeon.exit && this.player.isAt(this.dungeon.exit[0], this.dungeon.exit[1])) {
|
|
// Handle win condition (e.g., escaped)
|
|
this.gameState = 'won';
|
|
return { status: 'moved', event: 'escaped', player: this.player.getState(), visibleTiles: this.visibleTiles };
|
|
}
|
|
if (this.dungeon.treasure && this.player.isAt(this.dungeon.treasure[0], this.dungeon.treasure[1]) && !this.player.hasTreasure) {
|
|
this.player.hasTreasure = true;
|
|
// Remove treasure from dungeon so it won't be sent in future updates
|
|
this.dungeon.treasure = null;
|
|
console.log(`Player ${this.socketId} collected treasure! hasTreasure: ${this.player.hasTreasure}`);
|
|
return { status: 'moved', event: 'treasure_found', player: this.player.getState(), visibleTiles: this.visibleTiles };
|
|
}
|
|
|
|
return { status: 'moved', player: this.player.getState(), visibleTiles: this.visibleTiles };
|
|
}
|
|
|
|
|
|
console.log(`Player move to ${newX},${newY} for socket ${this.socketId} is invalid (wall or out of bounds).`);
|
|
|
|
// Debug: Show map around player
|
|
console.log(`[DEBUG] Map around player position:`);
|
|
for (let y = this.player.y - 2; y <= this.player.y + 2; y++) {
|
|
let row = '';
|
|
for (let x = this.player.x - 2; x <= this.player.x + 2; x++) {
|
|
if (x === this.player.x && y === this.player.y) {
|
|
row += '@'; // Player position
|
|
} else if (x === newX && y === newY) {
|
|
row += 'X'; // Attempted move position
|
|
} else if (this.dungeon && this.dungeon.map[y] && this.dungeon.map[y][x] !== undefined) {
|
|
row += this.dungeon.map[y][x] === 0 ? '.' : '#';
|
|
} else {
|
|
row += '?'; // Out of bounds or undefined
|
|
}
|
|
}
|
|
console.log(`[DEBUG] Y=${y}: ${row}`);
|
|
}
|
|
|
|
return { status: 'invalid' };
|
|
}
|
|
|
|
moveMonster() {
|
|
this.monster.moveTowardPlayer(this.player, this.dungeon);
|
|
}
|
|
|
|
// Update the getState() method to include all entities with relative positions
|
|
getState() {
|
|
const state = {
|
|
gameState: this.gameState,
|
|
player: this.player.getState(), // Use Player's getState method
|
|
monster: this.monster ? this.monster.getState() : null, // Use Monster's getState method
|
|
visibleTiles: { ...this.visibleTiles }, // Send a copy of visible tiles
|
|
entrance: this.dungeon ? this.dungeon.entrance : null,
|
|
exit: this.dungeon ? this.dungeon.exit : null,
|
|
treasure: this.dungeon ? this.dungeon.treasure : null,
|
|
};
|
|
|
|
// Debug logging to verify entity data
|
|
console.log("🎮 getState() sending entities - Monster:", state.monster, "Entrance:", state.entrance, "Exit:", state.exit, "Treasure:", state.treasure);
|
|
|
|
return state;
|
|
}
|
|
}
|
|
|
|
// Check if monster killed player
|
|
function checkMonsterKill(player, monster) {
|
|
// Use Monster's hasCaughtPlayer method
|
|
const monsterInstance = new Monster(monster.x, monster.y);
|
|
return monsterInstance.hasCaughtPlayer(player);
|
|
}
|
|
|
|
module.exports = Game;
|
|
module.exports.checkMonsterKill = checkMonsterKill; |