mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-18 22:58:32 +02:00
Fix logic and start cleanup refactor
This commit is contained in:
@@ -15,6 +15,8 @@ The game is built using Node.js for the backend, with Express and Socket.io for
|
||||
- **Monster AI**: Evade monster as you navigate the dungeon
|
||||
- **Treasure hunting**: Find treasure for bonus rewards
|
||||
- **Wownero integration**: Players pay an entrance fee (10-100 WOW) and can win rewards
|
||||
- **Chat system**: Players can communicate in real-time during gameplay
|
||||
- **View other players' game states**: Spectate other players' dungeons in real-time
|
||||
|
||||
### Technology Stack:
|
||||
- Backend: Node.js with Express and Socket.io
|
||||
|
||||
@@ -157,7 +157,9 @@ class Game {
|
||||
}
|
||||
if (this.dungeon.treasure && newX === this.dungeon.treasure[0] && newY === this.dungeon.treasure[1] && !this.player.hasTreasure) {
|
||||
this.player.hasTreasure = true;
|
||||
// Remove treasure from map or mark as collected
|
||||
// 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, visibleTiles: this.visibleTiles };
|
||||
}
|
||||
|
||||
@@ -214,19 +216,20 @@ class Game {
|
||||
// So, no conversion to relative coordinates is needed here for visibleTiles.
|
||||
// Just ensure this.visibleTiles is correctly populated by updateFOV.
|
||||
|
||||
// console.log("getState called. Player:", this.player, "Visible tiles keys:", Object.keys(this.visibleTiles || {}).length);
|
||||
|
||||
return {
|
||||
const state = {
|
||||
gameState: this.gameState,
|
||||
player: { ...this.player }, // Send a copy of player object
|
||||
monster: this.monster ? { ...this.monster } : null, // Send a copy if monster exists
|
||||
// items: { ...this.items }, // If you have items, send them too
|
||||
visibleTiles: { ...this.visibleTiles }, // Send a copy of visible tiles
|
||||
// You might also want to send entrance/exit if they are always known or become known
|
||||
// entrance: this.dungeon ? this.dungeon.entrance : null,
|
||||
// exit: this.dungeon ? this.dungeon.exit : null,
|
||||
// treasure: this.dungeon ? this.dungeon.treasure : null
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,4 +252,5 @@ function checkMonsterKill(player, monster) {
|
||||
return false; // Player is safe
|
||||
}
|
||||
|
||||
module.exports = Game;
|
||||
module.exports = Game;
|
||||
module.exports.checkMonsterKill = checkMonsterKill;
|
||||
+64
-56
@@ -19,7 +19,16 @@
|
||||
<script src="js/rot.min.js"></script>
|
||||
<script src="js/options.js"></script>
|
||||
<script src="js/teletype.js"></script> <!-- Load teletype.js -->
|
||||
|
||||
<!-- Use the original game.js with fixes -->
|
||||
<script src="js/game.js"></script>
|
||||
|
||||
<!-- Modular game files - keeping as backup for now -->
|
||||
<!-- <script src="js/displayManager.js"></script>
|
||||
<script src="js/screenManager.js"></script>
|
||||
<script src="js/gameState.js"></script>
|
||||
<script src="js/inputHandler.js"></script>
|
||||
<script src="js/game_refactored.js"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -110,6 +119,21 @@
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
// Handle waiting status updates
|
||||
socket.on('waiting_status', function(data) {
|
||||
console.log("🕒 WAITING STATUS received:", data);
|
||||
|
||||
if (data.status === 'waiting') {
|
||||
$('#messages').append($('<li style="color:#ff0;">').text(data.message));
|
||||
|
||||
// Show waiting screen
|
||||
if (typeof Game !== 'undefined' && Game.drawWaitingScreen) {
|
||||
Game.drawWaitingScreen();
|
||||
}
|
||||
}
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
socket.on('message', function(msg) {
|
||||
$('#messages').append($('<li>').text(msg));
|
||||
scrollChat();
|
||||
@@ -120,31 +144,28 @@
|
||||
console.log("🎮 Game start received with data:", data);
|
||||
$('#messages').append($('<li class="game-start">').text("Starting game..."));
|
||||
|
||||
if (typeof Game !== 'undefined') {
|
||||
if (typeof Game !== 'undefined' && Game.stopWaitingScreen) {
|
||||
Game.stopWaitingScreen();
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
$('#messages').append($('<li class="error">').text("Error: No game data received from server."));
|
||||
if (typeof Game !== 'undefined') Game._drawWelcomeScreen();
|
||||
if (typeof Game !== 'undefined' && Game._drawWelcomeScreen) Game._drawWelcomeScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Pass player, map, monsters, items, and initialVisibleTiles if available
|
||||
var success = Game.startGame(
|
||||
data.player,
|
||||
data.map,
|
||||
data.monsters,
|
||||
data.items,
|
||||
data.visibleTiles // Pass initial visible tiles from server
|
||||
);
|
||||
// Use the original game.js structure
|
||||
var success = Game.startGame(data.player, data.map, data.monster, data.items, data.visibleTiles);
|
||||
console.log("Game start result:", success ? "SUCCESS" : "FAILED");
|
||||
|
||||
if (!success) {
|
||||
$('#messages').append($('<li class="error">').text("Game start failed. Check console for details."));
|
||||
if (typeof Game !== 'undefined') Game._drawWelcomeScreen();
|
||||
if (typeof Game !== 'undefined' && Game._drawWelcomeScreen) Game._drawWelcomeScreen();
|
||||
} else {
|
||||
// Draw the initial game screen - the game draws itself after startGame()
|
||||
console.log("Game started successfully, initial screen should be drawn");
|
||||
|
||||
// Shift focus to the game display area after successful game start
|
||||
$('#game-display').focus();
|
||||
updateFocusIndicator();
|
||||
@@ -160,20 +181,8 @@
|
||||
socket.on('game_update', function(data) {
|
||||
// console.log("🎮 GAME UPDATE received:", data); // Reduced verbosity
|
||||
|
||||
if (typeof Game !== 'undefined' && Game._gameActive) { // Ensure game is active
|
||||
if (data.player) Game._player = data.player;
|
||||
if (data.visibleTiles) {
|
||||
Game._visibleTiles = data.visibleTiles;
|
||||
Game._updateExploredTiles(); // Update explored based on new visibility
|
||||
}
|
||||
if (data.monster) Game._monster = data.monster; // Update monster data
|
||||
if (data.items) Game._items = data.items; // Update items data
|
||||
// Potentially update _entrance, _exit, _treasure if they are part of game_update
|
||||
// Game._entrance = data.entrance;
|
||||
// Game._exit = data.exit;
|
||||
// Game._treasure = data.treasure;
|
||||
|
||||
Game._drawGameScreen();
|
||||
if (typeof Game !== 'undefined' && Game._gameActive) { // Use Game._gameActive instead of gameState
|
||||
Game.updateGameState(data); // Call updateGameState directly on Game object
|
||||
} else {
|
||||
// console.log("Game not active or Game undefined, skipping game_update draw.");
|
||||
}
|
||||
@@ -198,7 +207,7 @@
|
||||
console.log("🔑 'ENTER' COMMAND DETECTED - expecting game_start response");
|
||||
|
||||
// Show waiting screen
|
||||
if (typeof Game !== 'undefined') {
|
||||
if (typeof Game !== 'undefined' && Game.drawWaitingScreen) {
|
||||
Game.drawWaitingScreen();
|
||||
}
|
||||
// Focus will be shifted in the 'game_start' handler after success
|
||||
@@ -221,11 +230,11 @@
|
||||
$('#game-display').attr('tabindex', '-1'); // Make it focusable
|
||||
|
||||
$(document).on('keydown', function(e) {
|
||||
// console.log(`Keydown event: ${e.key}`, "Active element:", document.activeElement, "Game active?:", Game ? Game._gameActive : "Game undefined");
|
||||
// console.log(`Keydown event: ${e.key}`, "Active element:", document.activeElement, "Game active?:", Game && Game.gameState ? Game.gameState.gameActive : "Game undefined");
|
||||
|
||||
if (document.activeElement === $('#game-display')[0]) {
|
||||
// console.log("Game display has focus.");
|
||||
if (Game && Game._gameActive) {
|
||||
if (Game && Game._gameActive) { // Use Game._gameActive
|
||||
// console.log("Game is active. Processing key:", e.key);
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
@@ -248,7 +257,7 @@
|
||||
// console.log("Key pressed on game display, but not a movement key:", e.key);
|
||||
}
|
||||
} else {
|
||||
console.log("Game display has focus, but Game is not active. Game object:", Game, "Game._gameActive:", Game ? Game._gameActive : "N/A");
|
||||
console.log("Game display has focus, but Game is not active. Game object:", Game, "Game._gameActive:", Game && Game.gameState ? Game.gameState.gameActive : "N/A");
|
||||
}
|
||||
} else if (e.key === 'Enter' && document.activeElement !== $('#chatInput')[0]) {
|
||||
// console.log("Enter pressed, focusing chat input.");
|
||||
@@ -374,22 +383,10 @@ $(function() {
|
||||
document.getElementById('toggle-mode').addEventListener('click', function() {
|
||||
try {
|
||||
var button = document.getElementById('toggle-mode');
|
||||
// Check current mode and toggle accordingly
|
||||
if (Game && Game._isTileMode) { // If currently in Tile Mode
|
||||
console.log("Switching to ASCII mode...");
|
||||
if (typeof Game.switchToAsciiMode === 'function') {
|
||||
Game.switchToAsciiMode();
|
||||
button.textContent = 'Switch to Tile Mode';
|
||||
} else { console.error("Game.switchToAsciiMode is not defined"); }
|
||||
} else if (Game) { // If currently in ASCII mode (or Game object exists and not in tile mode)
|
||||
console.log("Switching to TILE mode...");
|
||||
if (typeof Game.switchToTileMode === 'function') {
|
||||
Game.switchToTileMode();
|
||||
button.textContent = 'Switch to ASCII Mode';
|
||||
} else { console.error("Game.switchToTileMode is not defined"); }
|
||||
} else {
|
||||
console.error("Game object not available to toggle mode.");
|
||||
}
|
||||
// Display mode switching has been removed - ASCII and tile modes are now equivalent
|
||||
console.log("Display mode switching is no longer needed - modes are equivalent");
|
||||
button.textContent = 'Mode switching disabled';
|
||||
button.disabled = true;
|
||||
} catch (e) {
|
||||
console.error("Error switching display mode:", e);
|
||||
}
|
||||
@@ -402,15 +399,10 @@ document.getElementById('toggle-mode').addEventListener('click', function() {
|
||||
socket.on('queue_cancelled', function() {
|
||||
console.log("Queue cancelled, returning to welcome screen");
|
||||
|
||||
if (typeof Game !== 'undefined') {
|
||||
// Try different naming patterns for compatibility
|
||||
if (typeof Game._drawWelcomeScreen === 'function') {
|
||||
Game._drawWelcomeScreen();
|
||||
} else if (typeof Game.drawWelcomeScreen === 'function') {
|
||||
Game.drawWelcomeScreen();
|
||||
} else {
|
||||
console.error("Welcome screen function not found");
|
||||
}
|
||||
if (typeof Game !== 'undefined' && Game._drawWelcomeScreen) {
|
||||
Game._drawWelcomeScreen();
|
||||
} else {
|
||||
console.error("Game or _drawWelcomeScreen not available");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -447,16 +439,32 @@ socket.on('game_over', function(data) {
|
||||
$('#messages').append($('<li style="color:#f00; font-weight:bold;">').text(message));
|
||||
|
||||
// Draw lose screen
|
||||
Game.drawLoseScreen(reason);
|
||||
if (Game && Game.drawLoseScreen) {
|
||||
Game.drawLoseScreen(reason);
|
||||
}
|
||||
}
|
||||
|
||||
// Set game as inactive to prevent further movement
|
||||
Game._gameActive = false;
|
||||
if (Game) {
|
||||
Game._gameActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
scrollChat();
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Initialize the Game object -->
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
console.log("Initializing Game...");
|
||||
if (typeof Game !== 'undefined') {
|
||||
Game.init();
|
||||
} else {
|
||||
console.error("Game object not found!");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* DisplayManager - Handles all ROT.js display operations
|
||||
*/
|
||||
var DisplayManager = {
|
||||
_display: null,
|
||||
_screenWidth: 0,
|
||||
_screenHeight: 0,
|
||||
_displayReady: false,
|
||||
_isTileMode: true,
|
||||
|
||||
// Visual representation constants
|
||||
symbols: {
|
||||
player: "@",
|
||||
wall: "#",
|
||||
floor: "'",
|
||||
monster: "~",
|
||||
exit: ">",
|
||||
entrance: "<",
|
||||
treasure: "$",
|
||||
border: "+"
|
||||
},
|
||||
|
||||
init: function(screenWidth, screenHeight, containerId) {
|
||||
console.log("DisplayManager.init called");
|
||||
this._screenWidth = screenWidth;
|
||||
this._screenHeight = screenHeight;
|
||||
|
||||
var displayOptions = {
|
||||
width: this._screenWidth,
|
||||
height: this._screenHeight,
|
||||
forceSquareRatio: true,
|
||||
layout: "tile",
|
||||
bg: window.options.bg,
|
||||
fg: window.options.fg || "#FFF",
|
||||
tileWidth: window.options.tileWidth,
|
||||
tileHeight: window.options.tileHeight,
|
||||
tileSet: window.options.tileSet,
|
||||
tileMap: window.options.tileMap,
|
||||
tileColorize: false
|
||||
};
|
||||
|
||||
try {
|
||||
var gameDisplayContainer = document.getElementById(containerId);
|
||||
if (!gameDisplayContainer) {
|
||||
console.error("Game display container not found:", containerId);
|
||||
return false;
|
||||
}
|
||||
|
||||
this._display = new ROT.Display(displayOptions);
|
||||
gameDisplayContainer.innerHTML = '';
|
||||
gameDisplayContainer.appendChild(this._display.getContainer());
|
||||
|
||||
console.log("ROT.Display initialized successfully");
|
||||
this._displayReady = true;
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Error initializing ROT.Display:", e);
|
||||
this._displayReady = false;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
isReady: function() {
|
||||
return this._display && this._displayReady;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
if (!this.isReady()) return;
|
||||
|
||||
const display = this._display;
|
||||
display._data = {};
|
||||
display._dirty = {};
|
||||
|
||||
var canvas = display.getContainer();
|
||||
if (canvas && typeof canvas.getContext === 'function') {
|
||||
var ctx = canvas.getContext("2d");
|
||||
if (ctx) {
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
draw: function(x, y, char, fg, bg) {
|
||||
if (!this.isReady()) return;
|
||||
this._display.draw(x, y, char, fg, bg);
|
||||
},
|
||||
|
||||
drawText: function(x, y, text, fg) {
|
||||
if (!this.isReady()) return;
|
||||
if (fg) {
|
||||
this._display.drawText(x, y, `%c{${fg}}${text}`);
|
||||
} else {
|
||||
this._display.drawText(x, y, text);
|
||||
}
|
||||
},
|
||||
|
||||
drawCenteredText: function(y, text, fg) {
|
||||
if (!this.isReady()) return y;
|
||||
const x = Math.floor((this._screenWidth - text.length) / 2);
|
||||
this.drawText(x, y, text, fg);
|
||||
return y + 1;
|
||||
},
|
||||
|
||||
drawBorder: function(fg) {
|
||||
if (!this.isReady()) return;
|
||||
const borderChar = this.symbols.border;
|
||||
const color = fg || "#FFF";
|
||||
|
||||
for (let x = 0; x < this._screenWidth; x++) {
|
||||
for (let y = 0; y < this._screenHeight; y++) {
|
||||
if (x === 0 || y === 0 || x === this._screenWidth-1 || y === this._screenHeight-1) {
|
||||
this.draw(x, y, borderChar, color, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getScreenWidth: function() {
|
||||
return this._screenWidth;
|
||||
},
|
||||
|
||||
getScreenHeight: function() {
|
||||
return this._screenHeight;
|
||||
}
|
||||
};
|
||||
+58
-79
@@ -11,7 +11,6 @@ var Game = {
|
||||
_display: null,
|
||||
_screenWidth: options.width,
|
||||
_screenHeight: options.height,
|
||||
_isTileMode: false, // Determined after checking tileset
|
||||
_displayReady: false, // Flag to indicate if _display is initialized
|
||||
_player: null,
|
||||
defaultFg: "#FFF", // Define defaultFg here
|
||||
@@ -28,15 +27,6 @@ var Game = {
|
||||
console.log("Game.init called. 'this' is Game:", this === Game);
|
||||
console.log("Is this.checkTileset a function?", typeof this.checkTileset);
|
||||
|
||||
if (typeof this.checkTileset !== 'function') {
|
||||
console.error("CRITICAL: Game.checkTileset is not a function. Halting initialization.");
|
||||
alert("Error: Game initialization failed (checkTileset not found). Please check game.js.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkTileset(function(tilesetAvailable) {
|
||||
console.log("Game.checkTileset callback. Tileset available: " + tilesetAvailable);
|
||||
this._isTileMode = tilesetAvailable;
|
||||
|
||||
var displayOptions = {
|
||||
width: this._screenWidth,
|
||||
@@ -44,8 +34,6 @@ var Game = {
|
||||
forceSquareRatio: true, // Good for both modes generally
|
||||
};
|
||||
|
||||
if (this._isTileMode) {
|
||||
console.log("Initializing display in TILE mode.");
|
||||
displayOptions.layout = "tile";
|
||||
displayOptions.bg = window.options.bg; // Should be "transparent"
|
||||
displayOptions.fg = window.options.fg || "#FFF"; // Default foreground for text not in tileMap
|
||||
@@ -54,18 +42,6 @@ var Game = {
|
||||
displayOptions.tileSet = window.options.tileSet;
|
||||
displayOptions.tileMap = window.options.tileMap;
|
||||
displayOptions.tileColorize = false; // Render tiles as-is from the tileset
|
||||
} else {
|
||||
console.log("Initializing display in ASCII mode.");
|
||||
displayOptions.layout = "rect";
|
||||
displayOptions.bg = "#000000"; // Black background for ASCII
|
||||
displayOptions.fg = window.options.fg || "#FFF"; // Default foreground
|
||||
displayOptions.fontSize = 18; // Adjust as needed for your font/preference
|
||||
// Ensure no tile-specific options are passed if they might cause errors
|
||||
delete displayOptions.tileWidth;
|
||||
delete displayOptions.tileHeight;
|
||||
delete displayOptions.tileSet;
|
||||
delete displayOptions.tileMap;
|
||||
}
|
||||
|
||||
try {
|
||||
var gameDisplayContainer = document.getElementById("game-display");
|
||||
@@ -90,47 +66,8 @@ var Game = {
|
||||
alert("Failed to initialize game display. Error: " + e.message + ". Check console for details.");
|
||||
this._displayReady = false; // Explicitly false on error
|
||||
}
|
||||
}.bind(this)); // CRUCIAL: bind 'this' for the callback to refer to the Game object
|
||||
},
|
||||
|
||||
checkTileset: function(callback) {
|
||||
console.log("Game.checkTileset called to determine mode.");
|
||||
// This function relies on options.js to have attempted loading the tileset
|
||||
// and set window.tileSet, window.tileMap, and window.globalTileSetStatus.
|
||||
|
||||
// Give a very short moment for options.js to potentially finish initial load attempts.
|
||||
setTimeout(function() {
|
||||
if (window.globalTileSetStatus === "error") {
|
||||
console.warn("Tileset loading previously failed (window.globalTileSetStatus is 'error'). Using ASCII mode.");
|
||||
callback(false); // Tileset not available
|
||||
} else if (window.tileSet && window.tileSet.complete && window.tileSet.naturalHeight !== 0 && window.tileMap && Object.keys(window.tileMap).length > 0) {
|
||||
console.log("Tileset and tileMap appear to be loaded and valid. Using TILE mode.");
|
||||
callback(true); // Tileset available
|
||||
} else {
|
||||
// If not immediately ready, and no definitive error, try waiting a bit longer.
|
||||
// options.js has its own timeouts. This is a secondary check.
|
||||
console.log("Tileset not immediately confirmed. Waiting up to 3 seconds for options.js to complete loading...");
|
||||
let attempts = 0;
|
||||
const maxAttempts = 6; // Try for 3 seconds (6 * 500ms)
|
||||
const interval = setInterval(function() {
|
||||
attempts++;
|
||||
if (window.globalTileSetStatus === "error") {
|
||||
clearInterval(interval);
|
||||
console.warn("Tileset loading failed during interval check in Game.checkTileset. Using ASCII mode.");
|
||||
callback(false);
|
||||
} else if (window.tileSet && window.tileSet.complete && window.tileSet.naturalHeight !== 0 && window.tileMap && Object.keys(window.tileMap).length > 0) {
|
||||
clearInterval(interval);
|
||||
console.log("Tileset and tileMap confirmed after delay in Game.checkTileset. Using TILE mode.");
|
||||
callback(true);
|
||||
} else if (attempts >= maxAttempts) {
|
||||
clearInterval(interval);
|
||||
console.error("Tileset did not become available after extended wait in Game.checkTileset. Assuming not available. Using ASCII mode.");
|
||||
callback(false); // Assume not available
|
||||
}
|
||||
}.bind(this), 500); // Check every 500ms
|
||||
}
|
||||
}.bind(this), 50); // Small initial delay for options.js to run
|
||||
},
|
||||
}, // Removed .bind(this) - not needed for object literal methods
|
||||
|
||||
|
||||
_ensureDisplay: function() {
|
||||
if (!this._display || !this._displayReady) {
|
||||
@@ -228,7 +165,12 @@ var Game = {
|
||||
console.log("Player initialized to:", this._player);
|
||||
|
||||
this._monster = monsterData; // Assuming monsterData is structured correctly or null
|
||||
this._item = itemData; // Assuming itemData is structured correctly or null
|
||||
this._items = itemData; // Fixed: Use _items instead of _item
|
||||
|
||||
// Initialize entrance, exit, treasure from the game data
|
||||
this._entrance = null;
|
||||
this._exit = null;
|
||||
this._treasure = null;
|
||||
|
||||
this._map = {};
|
||||
this._visibleTiles = {}; // Clear/initialize visible tiles
|
||||
@@ -416,6 +358,20 @@ var Game = {
|
||||
this._items = data.items; // Ensure this is _items, not _item
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update entrance, exit, treasure
|
||||
if (data.entrance !== undefined) {
|
||||
this._entrance = data.entrance;
|
||||
needsRedraw = true;
|
||||
}
|
||||
if (data.exit !== undefined) {
|
||||
this._exit = data.exit;
|
||||
needsRedraw = true;
|
||||
}
|
||||
if (data.treasure !== undefined) {
|
||||
this._treasure = data.treasure;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update visible tiles (most critical for movement feedback)
|
||||
if (data.visibleTiles && typeof data.visibleTiles === 'object' && Object.keys(data.visibleTiles).length > 0) {
|
||||
@@ -484,40 +440,63 @@ var Game = {
|
||||
if (this._visibleTiles[wy] && this._visibleTiles[wy][wx] !== undefined) {
|
||||
tileType = this._visibleTiles[wy][wx];
|
||||
baseFg = this.defaultFg; // Use this.defaultFg
|
||||
baseChar = (tileType === 1) ? '#' : (this._isTileMode ? "'" : ".");
|
||||
baseChar = (tileType === 1) ? '#' : "'"; // Fixed: Always use ' for floor
|
||||
} else if (this._exploredTiles[wy] && this._exploredTiles[wy][wx] !== undefined) {
|
||||
tileType = this._exploredTiles[wy][wx];
|
||||
baseFg = this.defaultFg; // Use this.defaultFg
|
||||
baseChar = (tileType === 1) ? '#' : (this._isTileMode ? "'" : ".");
|
||||
baseChar = (tileType === 1) ? '#' : "'"; // Fixed: Always use ' for floor
|
||||
}
|
||||
charStack.push(baseChar);
|
||||
fgStack.push(baseFg);
|
||||
bgStack.push(null);
|
||||
|
||||
// Items, Monsters, Player drawing logic
|
||||
// Example for items (ensure _item and _isVisible are defined and work correctly)
|
||||
if (this._item) { // Check if _item exists
|
||||
for (const itemKey in this._item) {
|
||||
if (this._item.hasOwnProperty(itemKey)) {
|
||||
const currentItem = this._item[itemKey];
|
||||
// Render items (fix property name from _item to _items)
|
||||
if (this._items) {
|
||||
for (const itemKey in this._items) {
|
||||
if (this._items.hasOwnProperty(itemKey)) {
|
||||
const currentItem = this._items[itemKey];
|
||||
if (currentItem && currentItem.x === wx && currentItem.y === wy && this._isVisible(wx, wy)) {
|
||||
charStack.push('$'); // Assuming '$' for items
|
||||
fgStack.push(this.defaultFg); // Use this.defaultFg
|
||||
charStack.push('$');
|
||||
fgStack.push(this.defaultFg);
|
||||
bgStack.push(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._monster && this._monster.x === wx && this._monster.y === wy && this._isVisible(wx, wy)) {
|
||||
charStack.push('~');
|
||||
fgStack.push(this.defaultFg); // Use this.defaultFg
|
||||
// Render entrance
|
||||
if (this._entrance && this._entrance[0] === wx && this._entrance[1] === wy && this._isVisible(wx, wy)) {
|
||||
charStack.push('<');
|
||||
fgStack.push('#0f0'); // Green for entrance
|
||||
bgStack.push(null);
|
||||
}
|
||||
|
||||
// Render exit
|
||||
if (this._exit && this._exit[0] === wx && this._exit[1] === wy && this._isVisible(wx, wy)) {
|
||||
charStack.push('>');
|
||||
fgStack.push('#f0f'); // Magenta for exit
|
||||
bgStack.push(null);
|
||||
}
|
||||
|
||||
// Render treasure
|
||||
if (this._treasure && this._treasure[0] === wx && this._treasure[1] === wy && this._isVisible(wx, wy)) {
|
||||
charStack.push('$');
|
||||
fgStack.push('#ff0'); // Yellow for treasure
|
||||
bgStack.push(null);
|
||||
}
|
||||
|
||||
// Render monster
|
||||
if (this._monster && this._monster.x === wx && this._monster.y === wy && this._isVisible(wx, wy)) {
|
||||
charStack.push('~');
|
||||
fgStack.push('#f00'); // Red for monster
|
||||
bgStack.push(null);
|
||||
}
|
||||
|
||||
// Render player (always on top)
|
||||
if (wx === playerWX && wy === playerWY) {
|
||||
charStack.push('@');
|
||||
fgStack.push(this.defaultFg); // Use this.defaultFg
|
||||
fgStack.push(this.defaultFg);
|
||||
bgStack.push(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* GameState - Manages game state and data
|
||||
*/
|
||||
var GameState = {
|
||||
player: null,
|
||||
monster: null,
|
||||
entrance: null,
|
||||
exit: null,
|
||||
treasure: null,
|
||||
visibleTiles: {},
|
||||
exploredTiles: {},
|
||||
gameActive: false,
|
||||
map: {},
|
||||
items: {},
|
||||
|
||||
init: function() {
|
||||
this.reset();
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.player = null;
|
||||
this.monster = null;
|
||||
this.entrance = null;
|
||||
this.exit = null;
|
||||
this.treasure = null;
|
||||
this.visibleTiles = {};
|
||||
this.exploredTiles = {};
|
||||
this.gameActive = false;
|
||||
this.map = {};
|
||||
this.items = {};
|
||||
},
|
||||
|
||||
startGame: function(data) {
|
||||
console.log("Starting game with data:", data);
|
||||
|
||||
if (!data) {
|
||||
console.error("No game data received!");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.gameActive = true;
|
||||
|
||||
// Set player position
|
||||
this.player = data.player || { x: 12, y: 12 };
|
||||
this.monster = data.monster;
|
||||
this.entrance = data.entrance;
|
||||
this.exit = data.exit;
|
||||
this.treasure = data.treasure;
|
||||
|
||||
// Set visible tiles
|
||||
if (data.visibleTiles && typeof data.visibleTiles === 'object') {
|
||||
this.visibleTiles = data.visibleTiles;
|
||||
console.log("Visible tiles:", Object.keys(this.visibleTiles).length, "rows");
|
||||
} else {
|
||||
// Create a basic visible area around player
|
||||
console.log("No visible tiles provided, creating basic view");
|
||||
this.visibleTiles = {};
|
||||
const px = this.player.x;
|
||||
const py = this.player.y;
|
||||
|
||||
for (let y = py-2; y <= py+2; y++) {
|
||||
if (!this.visibleTiles[y]) this.visibleTiles[y] = {};
|
||||
for (let x = px-2; x <= px+2; x++) {
|
||||
this.visibleTiles[y][x] = 0; // Floor tiles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update explored tiles
|
||||
this.updateExploredTiles();
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("Error starting game:", err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
updateGameState: function(data) {
|
||||
console.log("🎲 Updating game state with:", data);
|
||||
|
||||
if (!data) {
|
||||
console.error("No update data received!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Update player position
|
||||
if (data.player) {
|
||||
console.log("Player position updated:", data.player);
|
||||
this.player = data.player;
|
||||
}
|
||||
|
||||
// Update monster position
|
||||
if (data.monster !== undefined) {
|
||||
this.monster = data.monster;
|
||||
}
|
||||
|
||||
// Update visible tiles
|
||||
if (data.visibleTiles && Object.keys(data.visibleTiles).length > 0) {
|
||||
console.log("Visible tiles updated:", Object.keys(data.visibleTiles).length, "rows");
|
||||
this.visibleTiles = data.visibleTiles;
|
||||
this.updateExploredTiles();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("Error updating game state:", err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
updateExploredTiles: function() {
|
||||
// Add all currently visible tiles to explored tiles
|
||||
for (var y in this.visibleTiles) {
|
||||
if (!this.exploredTiles[y]) this.exploredTiles[y] = {};
|
||||
|
||||
for (var x in this.visibleTiles[y]) {
|
||||
this.exploredTiles[y][x] = this.visibleTiles[y][x];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setGameActive: function(active) {
|
||||
this.gameActive = active;
|
||||
},
|
||||
|
||||
isGameActive: function() {
|
||||
return this.gameActive;
|
||||
},
|
||||
|
||||
getGameData: function() {
|
||||
return {
|
||||
player: this.player,
|
||||
monster: this.monster,
|
||||
entrance: this.entrance,
|
||||
exit: this.exit,
|
||||
treasure: this.treasure,
|
||||
visibleTiles: this.visibleTiles,
|
||||
exploredTiles: this.exploredTiles,
|
||||
gameActive: this.gameActive,
|
||||
map: this.map,
|
||||
items: this.items
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
// This provides a mock socket when the real one isn't available
|
||||
if (typeof window !== 'undefined' && !window.socket) {
|
||||
window.socket = {
|
||||
emit: function(event, data) {
|
||||
console.log("[Mock Socket] Would emit:", event, data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Game object - coordinates all the managers
|
||||
*/
|
||||
var Game = {
|
||||
// Managers
|
||||
displayManager: DisplayManager,
|
||||
screenManager: ScreenManager,
|
||||
inputHandler: InputHandler,
|
||||
gameState: GameState,
|
||||
|
||||
// Config
|
||||
_screenWidth: options.width,
|
||||
_screenHeight: options.height,
|
||||
|
||||
init: function() {
|
||||
console.log("Game.init called");
|
||||
|
||||
// Initialize all managers
|
||||
if (!this.displayManager.init(this._screenWidth, this._screenHeight, "game-display")) {
|
||||
console.error("Failed to initialize display manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.screenManager.init(this.displayManager);
|
||||
this.gameState.init();
|
||||
this.inputHandler.init();
|
||||
|
||||
// Set up input handler events
|
||||
const self = this;
|
||||
this.inputHandler.on('movement', function(direction) {
|
||||
console.log("Movement input received:", direction);
|
||||
});
|
||||
|
||||
// Draw welcome screen
|
||||
this.screenManager.drawWelcomeScreen();
|
||||
|
||||
console.log("Game initialization complete");
|
||||
return true;
|
||||
},
|
||||
|
||||
startGame: function(data) {
|
||||
console.log("🎮 Game start received with data:", data);
|
||||
|
||||
if (!this.gameState.startGame(data)) {
|
||||
console.error("Failed to start game");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Activate input handling
|
||||
this.inputHandler.setGameActive(true);
|
||||
this.gameState.setGameActive(true);
|
||||
|
||||
// Draw the game screen
|
||||
this.screenManager.drawGameScreen(this.gameState.getGameData());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
updateGameState: function(data) {
|
||||
if (!this.gameState.updateGameState(data)) {
|
||||
console.error("Failed to update game state");
|
||||
return;
|
||||
}
|
||||
|
||||
// Redraw the screen if game is active
|
||||
if (this.gameState.isGameActive()) {
|
||||
this.screenManager.drawGameScreen(this.gameState.getGameData());
|
||||
}
|
||||
},
|
||||
|
||||
showWinScreen: function(hasTreasure) {
|
||||
this.inputHandler.setGameActive(false);
|
||||
this.gameState.setGameActive(false);
|
||||
this.screenManager.drawWinScreen(hasTreasure);
|
||||
},
|
||||
|
||||
showLoseScreen: function(reason) {
|
||||
this.inputHandler.setGameActive(false);
|
||||
this.gameState.setGameActive(false);
|
||||
this.screenManager.drawLoseScreen(reason);
|
||||
},
|
||||
|
||||
showWaitingScreen: function() {
|
||||
this.inputHandler.setGameActive(false);
|
||||
this.gameState.setGameActive(false);
|
||||
this.screenManager.drawWaitingScreen();
|
||||
},
|
||||
|
||||
showWelcomeScreen: function() {
|
||||
this.inputHandler.setGameActive(false);
|
||||
this.gameState.setGameActive(false);
|
||||
this.screenManager.drawWelcomeScreen();
|
||||
},
|
||||
|
||||
clearDisplay: function() {
|
||||
this.displayManager.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// Socket event handlers
|
||||
if (typeof window !== 'undefined' && window.socket) {
|
||||
socket.on('game_start', function(data) {
|
||||
console.log("🎮 Game start received with data:", data);
|
||||
Game.startGame(data);
|
||||
});
|
||||
|
||||
socket.on('game_update', function(data) {
|
||||
Game.updateGameState(data);
|
||||
});
|
||||
|
||||
socket.on('waiting_status', function(data) {
|
||||
console.log("Waiting status received:", data);
|
||||
Game.showWaitingScreen();
|
||||
});
|
||||
|
||||
socket.on('queue_cancelled', function() {
|
||||
console.log("Queue cancelled, returning to welcome screen");
|
||||
Game.showWelcomeScreen();
|
||||
});
|
||||
|
||||
socket.on('game_over', function(data) {
|
||||
console.log("Game over received:", data);
|
||||
|
||||
if (data.status === 'won') {
|
||||
Game.showWinScreen(data.hasTreasure);
|
||||
} else {
|
||||
Game.showLoseScreen(data.reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* InputHandler - Manages keyboard input and translates to game actions
|
||||
*/
|
||||
var InputHandler = {
|
||||
gameActive: false,
|
||||
eventCallbacks: {},
|
||||
|
||||
init: function() {
|
||||
console.log("Initializing keyboard handler");
|
||||
|
||||
const self = this;
|
||||
window.addEventListener('keydown', function(e) {
|
||||
self.handleKeyDown(e);
|
||||
});
|
||||
|
||||
console.log("Keyboard handler initialized");
|
||||
},
|
||||
|
||||
setGameActive: function(active) {
|
||||
this.gameActive = active;
|
||||
},
|
||||
|
||||
on: function(eventName, callback) {
|
||||
if (!this.eventCallbacks[eventName]) {
|
||||
this.eventCallbacks[eventName] = [];
|
||||
}
|
||||
this.eventCallbacks[eventName].push(callback);
|
||||
},
|
||||
|
||||
emit: function(eventName, data) {
|
||||
if (this.eventCallbacks[eventName]) {
|
||||
this.eventCallbacks[eventName].forEach(function(callback) {
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
handleKeyDown: function(e) {
|
||||
if (!this.gameActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
let direction = null;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowUp':
|
||||
case 'w':
|
||||
case 'W':
|
||||
direction = 'up';
|
||||
break;
|
||||
|
||||
case 'ArrowDown':
|
||||
case 's':
|
||||
case 'S':
|
||||
direction = 'down';
|
||||
break;
|
||||
|
||||
case 'ArrowLeft':
|
||||
case 'a':
|
||||
case 'A':
|
||||
direction = 'left';
|
||||
break;
|
||||
|
||||
case 'ArrowRight':
|
||||
case 'd':
|
||||
case 'D':
|
||||
direction = 'right';
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
console.log(`Key pressed: ${e.key}, sending direction: ${direction}`);
|
||||
|
||||
// Emit movement event
|
||||
this.emit('movement', direction);
|
||||
|
||||
// Send to server via socket
|
||||
if (window.socket && direction) {
|
||||
window.socket.emit('move', direction);
|
||||
}
|
||||
}
|
||||
};
|
||||
+1
-32
@@ -23,46 +23,15 @@ tileSet.onload = function() {
|
||||
|
||||
tileSet.onerror = function() {
|
||||
console.error("❌ Failed to load tileset image!");
|
||||
alert("Tileset image failed to load. Game will attempt to use ASCII mode.");
|
||||
window.globalTileSetStatus = "error"; // Set a status for Game.js to check if needed
|
||||
|
||||
// Force ASCII mode when tileset fails to load
|
||||
// REMOVED: if (typeof Game !== 'undefined' && Game._display) {
|
||||
// REMOVED: Game.switchToAsciiMode();
|
||||
// REMOVED: }
|
||||
};
|
||||
|
||||
// Try different possible paths (to handle serving from different directories)
|
||||
tileSet.src = "tiles.png"; // Try default path first
|
||||
|
||||
// Add fallback function if main path fails
|
||||
setTimeout(function() {
|
||||
if (!tileSetLoaded) {
|
||||
console.warn("Tileset not loaded yet, trying alternate paths...");
|
||||
tileSet.src = "./tiles.png"; // Try with explicit relative path
|
||||
|
||||
setTimeout(function() {
|
||||
if (!tileSetLoaded) {
|
||||
tileSet.src = "/tiles.png"; // Try from root
|
||||
|
||||
setTimeout(function() {
|
||||
if (!tileSetLoaded && window.globalTileSetStatus !== "error") { // Check if error already handled
|
||||
console.error("Failed to load tileset after trying multiple paths");
|
||||
alert("Could not load tileset. Game will attempt to use ASCII mode.");
|
||||
window.globalTileSetStatus = "error"; // Set a status
|
||||
// REMOVED: if (typeof Game !== 'undefined' && Game._display) {
|
||||
// REMOVED: Game.switchToAsciiMode();
|
||||
// REMOVED: }
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
var options = {
|
||||
layout: "tile",
|
||||
bg: "transparent", // Black background instead of transparent for better visibility
|
||||
bg: "transparent",
|
||||
// Adjusted dimensions for ~800x600 display
|
||||
width: 25, // 25 tiles * 32px/tile = 800px
|
||||
height: 19, // 19 tiles * 32px/tile = 608px
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* ScreenManager - Handles drawing different game screens
|
||||
*/
|
||||
var ScreenManager = {
|
||||
displayManager: null,
|
||||
defaultFg: "#FFF",
|
||||
|
||||
init: function(displayManager) {
|
||||
this.displayManager = displayManager;
|
||||
},
|
||||
|
||||
drawWelcomeScreen: function() {
|
||||
if (!this.displayManager.isReady()) return;
|
||||
|
||||
console.log("Drawing welcome screen");
|
||||
this.displayManager.clear();
|
||||
this.displayManager.drawBorder(this.defaultFg);
|
||||
|
||||
// Draw content with centered text
|
||||
let y = 3;
|
||||
y = this.displayManager.drawCenteredText(y, "WOWGUE", this.defaultFg);
|
||||
y += 4;
|
||||
|
||||
// Draw instructions
|
||||
y = this.displayManager.drawCenteredText(y, "Type enter to start!", this.defaultFg);
|
||||
y = this.displayManager.drawCenteredText(y, "Or type help...", this.defaultFg);
|
||||
y += 2;
|
||||
|
||||
// Draw legend
|
||||
const centerX = Math.floor(this.displayManager.getScreenWidth() / 2) - 11;
|
||||
const symbols = this.displayManager.symbols;
|
||||
|
||||
this.displayManager.draw(centerX, y, symbols.player, this.defaultFg, null);
|
||||
this.displayManager.drawText(centerX + 2, y, "- This is you", this.defaultFg);
|
||||
y++;
|
||||
|
||||
this.displayManager.draw(centerX, y, symbols.exit, this.defaultFg, null);
|
||||
this.displayManager.drawText(centerX + 2, y, "- Escape the dungeon", this.defaultFg);
|
||||
y++;
|
||||
|
||||
this.displayManager.draw(centerX, y, symbols.monster, this.defaultFg, null);
|
||||
this.displayManager.drawText(centerX + 2, y, "- Avoid the monster", this.defaultFg);
|
||||
y++;
|
||||
|
||||
this.displayManager.draw(centerX, y, symbols.treasure, this.defaultFg, null);
|
||||
this.displayManager.drawText(centerX + 2, y, "- Secure the bag", this.defaultFg);
|
||||
y++;
|
||||
|
||||
// Draw footer
|
||||
y += 1;
|
||||
this.displayManager.drawCenteredText(17, "Type enter in chat =", this.defaultFg);
|
||||
},
|
||||
|
||||
drawWinScreen: function(hasTreasure) {
|
||||
if (!this.displayManager.isReady()) return;
|
||||
|
||||
console.log("Drawing win screen, hasTreasure:", hasTreasure);
|
||||
this.displayManager.clear();
|
||||
this.displayManager.drawBorder(this.defaultFg);
|
||||
|
||||
let y = 5;
|
||||
y = this.displayManager.drawCenteredText(y, "YOU ESCAPED!", this.defaultFg);
|
||||
y += 1;
|
||||
|
||||
const subtitle = hasTreasure ? "WITH THE TREASURE!" : "ALIVE!";
|
||||
y = this.displayManager.drawCenteredText(y, subtitle, this.defaultFg);
|
||||
y += 2;
|
||||
|
||||
if (hasTreasure) {
|
||||
y = this.displayManager.drawCenteredText(y, "You won extra Wownero!", this.defaultFg);
|
||||
y += 1;
|
||||
}
|
||||
|
||||
y += 1;
|
||||
this.displayManager.drawCenteredText(y, "Type ENTER to retry", this.defaultFg);
|
||||
},
|
||||
|
||||
drawLoseScreen: function(reason) {
|
||||
if (!this.displayManager.isReady()) return;
|
||||
|
||||
console.log("Drawing lose screen, reason:", reason);
|
||||
this.displayManager.clear();
|
||||
this.displayManager.drawBorder(this.defaultFg);
|
||||
|
||||
let y = 5;
|
||||
y = this.displayManager.drawCenteredText(y, "YOU DIED!", this.defaultFg);
|
||||
y += 1;
|
||||
|
||||
let subtitle = "";
|
||||
if (reason === 'monster') {
|
||||
subtitle = "KILLED BY THE MONSTER";
|
||||
} else if (reason === 'timeout') {
|
||||
subtitle = "YOU RAN OUT OF TIME";
|
||||
}
|
||||
|
||||
if (subtitle) {
|
||||
y = this.displayManager.drawCenteredText(y, subtitle, this.defaultFg);
|
||||
y += 1;
|
||||
}
|
||||
|
||||
y += 2;
|
||||
this.displayManager.drawCenteredText(y, "Type ENTER to retry", this.defaultFg);
|
||||
},
|
||||
|
||||
drawWaitingScreen: function() {
|
||||
if (!this.displayManager.isReady()) return;
|
||||
|
||||
console.log("Drawing waiting screen");
|
||||
this.displayManager.clear();
|
||||
this.displayManager.drawBorder(this.defaultFg);
|
||||
|
||||
const centerY = Math.floor(this.displayManager.getScreenHeight() / 2);
|
||||
this.displayManager.drawCenteredText(centerY, "Waiting for next block...", this.defaultFg);
|
||||
},
|
||||
|
||||
drawGameScreen: function(gameState) {
|
||||
if (!this.displayManager.isReady()) return;
|
||||
|
||||
console.log("Drawing game screen...");
|
||||
this.displayManager.clear();
|
||||
|
||||
if (!gameState.player) {
|
||||
console.error("No player data in game state!");
|
||||
return;
|
||||
}
|
||||
|
||||
const centerX = Math.floor(this.displayManager.getScreenWidth() / 2);
|
||||
const centerY = Math.floor(this.displayManager.getScreenHeight() / 2);
|
||||
const symbols = this.displayManager.symbols;
|
||||
const playerX = gameState.player.x;
|
||||
const playerY = gameState.player.y;
|
||||
|
||||
// Draw visible tiles centered on player (absolute to relative coordinate conversion)
|
||||
for (let worldY in gameState.visibleTiles) {
|
||||
worldY = parseInt(worldY);
|
||||
for (let worldX in gameState.visibleTiles[worldY]) {
|
||||
worldX = parseInt(worldX);
|
||||
|
||||
// Convert world coordinates to screen coordinates relative to player
|
||||
const screenX = centerX + (worldX - playerX);
|
||||
const screenY = centerY + (worldY - playerY);
|
||||
|
||||
if (screenX >= 0 && screenY >= 0 &&
|
||||
screenX < this.displayManager.getScreenWidth() &&
|
||||
screenY < this.displayManager.getScreenHeight()) {
|
||||
|
||||
const tileType = gameState.visibleTiles[worldY][worldX];
|
||||
|
||||
if (tileType === 1) { // Wall
|
||||
this.displayManager.draw(screenX, screenY, symbols.wall, this.defaultFg, null);
|
||||
} else { // Floor
|
||||
this.displayManager.draw(screenX, screenY, symbols.floor, this.defaultFg, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw entities with stacking (now with correct coordinate conversion)
|
||||
this._drawEntityStacked(centerX, centerY, playerX, playerY, gameState.entrance, symbols.entrance, gameState.visibleTiles);
|
||||
this._drawEntityStacked(centerX, centerY, playerX, playerY, gameState.exit, symbols.exit, gameState.visibleTiles);
|
||||
this._drawEntityStacked(centerX, centerY, playerX, playerY, gameState.treasure, symbols.treasure, gameState.visibleTiles);
|
||||
this._drawEntityStacked(centerX, centerY, playerX, playerY, gameState.monster, symbols.monster, gameState.visibleTiles);
|
||||
|
||||
// Always draw player at center
|
||||
const baseTile = gameState.visibleTiles[playerY] && gameState.visibleTiles[playerY][playerX] === 1 ?
|
||||
symbols.wall : symbols.floor;
|
||||
this.displayManager.draw(centerX, centerY, [baseTile, symbols.player],
|
||||
[this.defaultFg, this.defaultFg], [null, null]);
|
||||
},
|
||||
|
||||
_drawEntityStacked: function(centerX, centerY, playerX, playerY, entity, symbol, visibleTiles) {
|
||||
if (!entity) return;
|
||||
|
||||
const entityX = Array.isArray(entity) ? entity[0] : entity.x;
|
||||
const entityY = Array.isArray(entity) ? entity[1] : entity.y;
|
||||
|
||||
// Convert world coordinates to screen coordinates relative to player
|
||||
const screenX = centerX + (entityX - playerX);
|
||||
const screenY = centerY + (entityY - playerY);
|
||||
|
||||
if (screenX >= 0 && screenY >= 0 &&
|
||||
screenX < this.displayManager.getScreenWidth() &&
|
||||
screenY < this.displayManager.getScreenHeight()) {
|
||||
|
||||
// Check if entity is visible
|
||||
if (visibleTiles[entityY] && visibleTiles[entityY][entityX] !== undefined) {
|
||||
const baseTile = visibleTiles[entityY][entityX] === 1 ?
|
||||
this.displayManager.symbols.wall : this.displayManager.symbols.floor;
|
||||
|
||||
this.displayManager.draw(screenX, screenY, [baseTile, symbol],
|
||||
[this.defaultFg, this.defaultFg], [null, null]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -131,7 +131,11 @@ function checkGamesTimeout(currentHeight) {
|
||||
activeGames.forEach((game, socketId) => {
|
||||
const user = getUserBySocket(socketId);
|
||||
|
||||
if (user && user.blockRec && currentHeight > user.blockRec + 1) {
|
||||
// Player must escape before the next block after their start block
|
||||
// If they started on block 2, they must escape before block 3 is found
|
||||
if (user && user.blockRec && currentHeight > user.blockRec) {
|
||||
console.log(`Game timeout for player ${socketId}: started on block ${user.blockRec}, current block ${currentHeight}`);
|
||||
|
||||
// Game has timed out - player didn't escape in time
|
||||
game.gameState = 'lost';
|
||||
io.to(socketId).emit('game_over', {
|
||||
@@ -241,8 +245,41 @@ io.on('connection', function(socket) {
|
||||
const moveResult = game.movePlayer(moveData.dx, moveData.dy); // This should update player pos and FOV
|
||||
|
||||
if (moveResult && moveResult.status === 'moved') {
|
||||
// Optional: Monster moves after player
|
||||
// game.moveMonster();
|
||||
// Move monster after player moves
|
||||
game.moveMonster();
|
||||
|
||||
// Check if monster caught player
|
||||
const GameModule = require('./game');
|
||||
const checkMonsterKill = GameModule.checkMonsterKill ||
|
||||
((player, monster) => monster.x === player.x && monster.y === player.y);
|
||||
|
||||
if (checkMonsterKill(game.player, game.monster)) {
|
||||
game.gameState = 'lost';
|
||||
io.to(socket.id).emit('game_over', {
|
||||
status: 'lost',
|
||||
reason: 'monster',
|
||||
message: 'The monster caught you!'
|
||||
});
|
||||
activeGames.delete(socket.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for treasure pickup
|
||||
if (moveResult.event === 'treasure_found') {
|
||||
io.to(socket.id).emit('message', 'You found the treasure!');
|
||||
}
|
||||
|
||||
// Check for escape
|
||||
if (moveResult.event === 'escaped') {
|
||||
game.gameState = 'won';
|
||||
io.to(socket.id).emit('game_over', {
|
||||
status: 'won',
|
||||
reason: 'escaped',
|
||||
message: 'Congratulations! You escaped the dungeon!'
|
||||
});
|
||||
activeGames.delete(socket.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedGameState = game.getState(); // Get the new state
|
||||
|
||||
|
||||
Reference in New Issue
Block a user