diff --git a/game.js b/game.js
index 0a53a8f..a564041 100644
--- a/game.js
+++ b/game.js
@@ -152,7 +152,7 @@ class Game {
this.moveMonster();
// Check if monster caught player
- if (this.monster.x === this.player.x && this.monster.y === this.player.y) {
+ if (checkMonsterKill(this.player, this.monster)) {
this.gameState = 'lost';
return { status: 'lost', reason: 'caught' };
}
@@ -199,84 +199,100 @@ class Game {
}
}
- // Replace getState() method with this relative-coordinates version
+ // Update the getState() method to include all entities with relative positions
getState() {
- // Only include visible information and translate to player-relative coordinates
+ // Calculate relative position of all entities from player's perspective
const playerX = this.player.x;
const playerY = this.player.y;
- // Create relative FOV map
+ // Convert all visible tiles to relative coordinates
const relativeVisibleTiles = {};
for (const y in this.visibleTiles) {
for (const x in this.visibleTiles[y]) {
const relX = parseInt(x) - playerX;
const relY = parseInt(y) - playerY;
- // Initialize row if needed
+ // Create row if needed
if (!relativeVisibleTiles[relY]) {
relativeVisibleTiles[relY] = {};
}
- // Add relative position tile
+ // Store tile type at relative position
relativeVisibleTiles[relY][relX] = this.visibleTiles[y][x];
}
}
- // Create relative entity positions only if visible in FOV
- const entityInfo = {};
-
- // Check if monster is visible
- if (this.visibleTiles[this.monster.y] &&
- this.visibleTiles[this.monster.y][this.monster.x] !== undefined) {
- entityInfo.monster = {
- x: this.monster.x - playerX,
- y: this.monster.y - playerY
- };
- }
-
- // Check if entrance is visible
- if (this.dungeon.entrance &&
- this.visibleTiles[this.dungeon.entrance[1]] &&
- this.visibleTiles[this.dungeon.entrance[1]][this.dungeon.entrance[0]] !== undefined) {
- entityInfo.entrance = [
- this.dungeon.entrance[0] - playerX,
- this.dungeon.entrance[1] - playerY
- ];
- }
-
- // Check if exit is visible
- if (this.dungeon.exit &&
- this.visibleTiles[this.dungeon.exit[1]] &&
- this.visibleTiles[this.dungeon.exit[1]][this.dungeon.exit[0]] !== undefined) {
- entityInfo.exit = [
- this.dungeon.exit[0] - playerX,
- this.dungeon.exit[1] - playerY
- ];
- }
-
- // Check if treasure is visible
- if (this.dungeon.treasure &&
- this.visibleTiles[this.dungeon.treasure[1]] &&
- this.visibleTiles[this.dungeon.treasure[1]][this.dungeon.treasure[0]] !== undefined) {
- entityInfo.treasure = [
- this.dungeon.treasure[0] - playerX,
- this.dungeon.treasure[1] - playerY
- ];
- }
-
- return {
+ // Create the state object
+ const state = {
gameState: this.gameState,
player: {
- // Player is always at relative (0,0)
- x: 0,
+ x: 0, // Player is always at (0,0) in relative coordinates
y: 0,
hasKey: this.player.hasKey || false,
hasTreasure: this.player.hasTreasure || false
},
- visibleTiles: relativeVisibleTiles,
- ...entityInfo // Only include visible entities
+ visibleTiles: relativeVisibleTiles
};
+
+ // Add monster if in view
+ if (this.monster && this.visibleTiles[this.monster.y] &&
+ this.visibleTiles[this.monster.y][this.monster.x] !== undefined) {
+ state.monster = {
+ x: this.monster.x - playerX, // Relative position
+ y: this.monster.y - playerY
+ };
+ }
+
+ // Add entrance if defined
+ if (this.dungeon && this.dungeon.entrance) {
+ // Use array format for consistency
+ state.entrance = [
+ this.dungeon.entrance.x - playerX,
+ this.dungeon.entrance.y - playerY
+ ];
+ }
+
+ // Add exit if in view
+ if (this.dungeon && this.dungeon.exit &&
+ this.visibleTiles[this.dungeon.exit.y] &&
+ this.visibleTiles[this.dungeon.exit.y][this.dungeon.exit.x] !== undefined) {
+ state.exit = [
+ this.dungeon.exit.x - playerX,
+ this.dungeon.exit.y - playerY
+ ];
+ }
+
+ // Add treasure if in view
+ if (this.dungeon && this.dungeon.treasure &&
+ this.visibleTiles[this.dungeon.treasure.y] &&
+ this.visibleTiles[this.dungeon.treasure.y][this.dungeon.treasure.x] !== undefined) {
+ state.treasure = [
+ this.dungeon.treasure.x - playerX,
+ this.dungeon.treasure.y - playerY
+ ];
+ }
+
+ return state;
}
}
+// Check if monster killed player
+function checkMonsterKill(player, monster) {
+ // Check for direct overlap - monster on same tile as player
+ if (monster.x === player.x && monster.y === player.y) {
+ return true; // Monster killed player
+ }
+
+ // Check for adjacent tiles (optional - if you want 1-tile proximity kills)
+ const dx = Math.abs(monster.x - player.x);
+ const dy = Math.abs(monster.y - player.y);
+
+ // If monster is adjacent (one tile away in any direction)
+ if ((dx <= 1 && dy === 0) || (dx === 0 && dy <= 1)) {
+ return true; // Monster killed player
+ }
+
+ return false; // Player is safe
+}
+
module.exports = Game;
\ No newline at end of file
diff --git a/html/index.html b/html/index.html
index 6137395..a32ff62 100644
--- a/html/index.html
+++ b/html/index.html
@@ -197,73 +197,36 @@
scrollChat();
});
- // Critical game events
- // Replace the game_start handler with this improved version
+ // Add or update the game_start handler
socket.on('game_start', function(data) {
- console.log("๐ฎ Received game_start broadcast:", data);
+ console.log("๐ฎ Game start received with data:", data);
+ $('#messages').append($('
').text("Starting game..."));
- // Log all socketIds for debugging
- console.log("Current socket ID:", socket.id);
- console.log("Target socket ID:", data.targetSocket);
-
- // Check if this event is for us
- const isForUs = !data.targetSocket || data.targetSocket === socket.id;
- console.log("Is this event for us?", isForUs ? "YES" : "NO");
-
- // Only process if directed at us or broadcast
- if (!isForUs) {
- console.log("Ignoring game_start message meant for another client");
- return;
+ // Stop waiting screen animation if it's running
+ if (typeof Game !== 'undefined') {
+ Game.stopWaitingScreen();
}
- // Check for required data
if (!data) {
- console.error("โ Error: Received empty game data!");
$('#messages').append($('').text("Error: No game data received"));
return;
}
- // Check if we have player data
- if (!data.player || typeof data.player.x === 'undefined') {
- console.error("โ Error: Invalid player data in game_start");
- $('#messages').append($('').text("Error: Invalid player data"));
- return;
- }
-
- // Track visibleTiles
- if (!data.visibleTiles) {
- console.warn("โ ๏ธ Warning: No visibleTiles in game data, creating empty object");
- data.visibleTiles = {};
- }
-
- $('#messages').append($('').text("Starting game..."));
-
try {
- // Display what we received
- $('#messages').append($('').text(`Entering the dungeon at position (${data.player.x},${data.player.y})...`));
+ // Start the game with received data
+ var success = Game.startGame(data);
+ console.log("Game start result:", success ? "SUCCESS" : "FAILED");
- // Start the game with received data (check if Game is available)
- if (typeof Game !== 'undefined' && Game.startGame) {
- var success = Game.startGame(data);
- console.log("Game start result:", success ? "SUCCESS โ
" : "FAILED โ");
+ if (!success) {
+ $('#messages').append($('').text("Game start failed - trying ASCII mode"));
- if (!success) {
- $('#messages').append($('').text("Game start failed - trying ASCII mode"));
-
- // Try ASCII mode as fallback
- if (typeof Game.switchToAsciiMode === 'function') {
- Game.switchToAsciiMode();
- success = Game.startGame(data);
- }
- }
- } else {
- console.error("Game object not available yet!");
- // Try again in a moment
- setTimeout(function() {
- if (typeof Game !== 'undefined' && Game.startGame) {
+ // Try switching to ASCII mode
+ if (document.getElementById('toggle-mode')) {
+ document.getElementById('toggle-mode').click();
+ setTimeout(function() {
Game.startGame(data);
- }
- }, 500);
+ }, 100);
+ }
}
window.focus();
@@ -271,44 +234,49 @@
console.error("Error starting game:", err);
$('#messages').append($('').text("Error: " + err.message));
}
-
- // Ensure chat is scrolled
- if (typeof scrollChat === 'function') {
- scrollChat();
- }
});
-
- // Add game update handler
+
+ // Game update handler
socket.on('game_update', function(data) {
- console.log("๐ฎ Received game update:", data);
+ console.log("๐ฎ GAME UPDATE received:", data);
- // Update game state
- if (typeof Game !== 'undefined' && typeof Game.updateGameState === 'function') {
- Game.updateGameState(data);
- }
-
- // Log player position
- if (data.player) {
- console.log(`Player now at: (${data.player.x}, ${data.player.y})`);
+ if (typeof Game !== 'undefined') {
+ // Update all entity positions
+ Game._player = data.player;
+ Game._visibleTiles = data.visibleTiles;
+ Game._monster = data.monster;
+ Game._entrance = data.entrance;
+ Game._exit = data.exit;
+ Game._treasure = data.treasure;
+
+ // Draw updated screen
+ Game._drawGameScreen();
}
});
- // Replace the chat form submission handler
+ // Update the chat form submission handler that processes the "enter" command
+
$('#chatForm').submit(function(e) {
e.preventDefault();
var msg = $('#chatInput').val().trim();
- if (msg) {
- console.log(`๐ SENDING CHAT: "${msg}"`);
+
+ // Don't send empty messages
+ if (!msg) return false;
+
+ console.log("๐ SENDING CHAT:", msg);
+ socket.emit('chat message', msg);
+ $('#chatInput').val('');
+
+ // If the message is "enter", show the waiting screen
+ if (msg.toLowerCase() === 'enter') {
+ console.log("๐ 'ENTER' COMMAND DETECTED - expecting game_start response");
- // Special handling for enter command
- if (msg.toLowerCase() === 'enter') {
- console.log("๐ 'ENTER' COMMAND DETECTED - expecting game_start response");
- $('#messages').append($('').text("Requesting to enter the dungeon..."));
+ // Show waiting screen
+ if (typeof Game !== 'undefined') {
+ Game.drawWaitingScreen();
}
-
- socket.emit('chat message', msg);
- $('#chatInput').val('');
}
+
return false;
});
});
@@ -437,317 +405,65 @@ document.getElementById('toggle-mode').addEventListener('click', function() {
});
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-โจ๏ธ Game Controls Active
-
-
-
-
-
-
-
-
-
diff --git a/html/js/game.js b/html/js/game.js
index ec053b8..30bd294 100644
--- a/html/js/game.js
+++ b/html/js/game.js
@@ -24,153 +24,29 @@ var Game = {
_message: "", // To display messages like 'waiting'
_visibleTiles: {}, // Store currently visible tiles
_exploredTiles: {}, // Store all tiles the player has seen
-
+ _isTileMode: false, // Track if we're in tile mode
+
_isFirefox: function() {
return navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
},
init: function() {
- console.log("Initializing Game...");
+ console.log("Initializing game");
- // Check for Firefox
- var isFirefox = this._isFirefox();
- if (isFirefox) {
- console.log("Firefox detected, applying special handling");
- }
-
- try {
- // Create a display with the defined options
- var displayOptions = { ...options }; // Clone options
-
- // Firefox-specific tweaks if needed
- if (isFirefox) {
- // Force numerical dimensions for Firefox
- displayOptions.width = parseInt(displayOptions.width);
- displayOptions.height = parseInt(displayOptions.height);
- }
-
- console.log("Creating ROT.Display with options:", displayOptions);
- this._display = new ROT.Display(displayOptions);
- console.log("ROT.Display created.");
-
- // Get the DOM element and append display
- var container = document.querySelector('.rotdis');
- console.log("Found display container:", container);
-
- if (container) {
- // Clear any existing content
- container.innerHTML = '';
-
- // Add the new canvas
- var canvas = this._display.getContainer();
-
- // Firefox-specific attributes
- if (isFirefox) {
- canvas.style.width = 'auto';
- canvas.style.height = 'auto';
- canvas.style.display = 'block';
- canvas.style.margin = 'auto';
- }
-
- container.appendChild(canvas);
- console.log("Display container attached to DOM");
-
- // Draw initial welcome screen
- console.log("Drawing initial welcome screen");
- this._drawWelcomeScreen();
-
- // Extra check for Firefox
- if (isFirefox) {
- // Give Firefox a moment to render properly
- setTimeout(() => {
- console.log("Firefox re-render check");
- this._drawWelcomeScreen();
- }, 100);
- }
-
+ // Verify if tileset is actually loaded
+ this.checkTileset(function(isLoaded) {
+ if (isLoaded) {
+ Game._setupTileDisplay();
} else {
- console.error("Could not find .rotdis container");
+ Game._setupAsciiDisplay();
}
- } catch (e) {
- console.error("Error during game initialization:", e);
- }
-
- // Initialize empty map
- this._map = [];
- for (var i = 0; i < this._mapWidth; i++) {
- this._map[i] = [];
- for (var j = 0; j < this._mapHeight; j++) {
- // Default to wall visually; adjust as needed
- this._map[i][j] = 1;
- }
- }
-
- var game = this;
- // Set up keyboard input - ONLY FOR MOVEMENT, not for starting the game
- window.addEventListener("keydown", function(e) {
- // Check if an input/textarea has focus - if so, don't capture keys for game
- const activeElement = document.activeElement;
- const isInputFocused = activeElement && (
- activeElement.tagName === 'INPUT' ||
- activeElement.tagName === 'TEXTAREA' ||
- activeElement.id === 'm' // Your chat input id
- );
- // If input is focused or game isn't active, don't process game controls
- if (isInputFocused || !game._gameActive) {
- return;
- }
-
- // Movement controls (only when game is active AND no input has focus)
- var direction = "";
- switch (e.keyCode) {
- case 38: case 75: case 87: // up, k, w
- direction = "up";
- e.preventDefault(); // Prevent page scrolling
- break;
- case 40: case 74: case 83: // down, j, s
- direction = "down";
- e.preventDefault(); // Prevent page scrolling
- break;
- case 37: case 72: case 65: // left, h, a
- direction = "left";
- e.preventDefault(); // Prevent page scrolling
- break;
- case 39: case 76: case 68: // right, l, d
- direction = "right";
- e.preventDefault(); // Prevent page scrolling
- break;
- default:
- return;
- }
-
- // If a direction key was pressed, update local state and send to server
- if (direction) {
- console.log("Game state updated:", direction);
-
- // Send the move command to the server
- if (window.socket) {
- console.log("Sending move command to server:", direction);
- window.socket.emit('move', direction);
- }
- }
+ // Draw the welcome screen
+ Game._drawWelcomeScreen();
+
+ // Set up the keyboard handler
+ Game._initializeKeyboardHandler();
});
-
- // Force draw welcome screen
- this._gameActive = false;
- this._message = "Type 'enter' in chat to play!";
- console.log("Drawing initial welcome screen");
- this._drawWelcomeScreen();
-
- // Verify welcome screen was drawn by checking for canvas content
- setTimeout(() => {
- const container = this._display.getContainer();
- const canvas = container.querySelector("canvas");
- if (canvas) {
- console.log("Canvas dimensions:", canvas.width, canvas.height);
- console.log("Canvas is visible:", canvas.style.display !== "none");
- }
- }, 500);
},
// Initialize game with data from server
@@ -315,244 +191,637 @@ var Game = {
if (tileType === 1) { // Wall
this._display.draw(screenX, screenY, '#', '#fff', '#000');
} else { // Floor
- this._display.draw(screenX, screenY, '.', '#888', '#000');
+ const tileChar = this.isAsciiMode() ? '.' : '\'';
+ this._display.draw(screenX, screenY, tileChar, '#888', '#000');
}
}
}
}
+ // Draw special entities if they exist in game state
+
+ // Check for entrance/exit at player position
+ const playerIsAtEntrance = this._entrance &&
+ this._entrance.x === 0 &&
+ this._entrance.y === 0;
+
+ const playerIsAtExit = this._exit &&
+ this._exit[0] === 0 &&
+ this._exit[1] === 0;
+
+ // Draw entrance if it exists in the game state
+ if (this._entrance) {
+ // Handle different data formats - could be [x,y] array or {x,y} object
+ const entranceX = Array.isArray(this._entrance) ? this._entrance[0] : this._entrance.x;
+ const entranceY = Array.isArray(this._entrance) ? this._entrance[1] : this._entrance.y;
+ const entranceScreenX = centerX + entranceX;
+ const entranceScreenY = centerY + entranceY;
+
+ console.log("Entrance position:", entranceX, entranceY);
+ console.log("Player relative position:", this._player.x, this._player.y);
+
+ if (entranceScreenX >= 0 && entranceScreenY >= 0 &&
+ entranceScreenX < this._screenWidth && entranceScreenY < this._screenHeight) {
+ // Draw entrance with bright green color for visibility
+ this._display.draw(entranceScreenX, entranceScreenY, '<', '#0f0', '#000');
+ console.log(`Drew entrance at screen position (${entranceScreenX},${entranceScreenY})`);
+ }
+ }
+
+ // Draw exit if it exists in the game state
+ if (this._exit) {
+ // Handle different data formats - could be [x,y] array or {x,y} object
+ const exitX = Array.isArray(this._exit) ? this._exit[0] : this._exit.x;
+ const exitY = Array.isArray(this._exit) ? this._exit[1] : this._exit.y;
+ const exitScreenX = centerX + exitX;
+ const exitScreenY = centerY + exitY;
+
+ console.log("Exit position:", exitX, exitY);
+
+ if (exitScreenX >= 0 && exitScreenY >= 0 &&
+ exitScreenX < this._screenWidth && exitScreenY < this._screenHeight) {
+ // Draw exit with bright green color for visibility
+ this._display.draw(exitScreenX, exitScreenY, '>', '#0f0', '#000');
+ console.log(`Drew exit at screen position (${exitScreenX},${exitScreenY})`);
+ }
+ }
+
+ // Draw treasure if it exists in the game state
+ if (this._treasure) {
+ const treasureX = Array.isArray(this._treasure) ? this._treasure[0] : this._treasure.x;
+ const treasureY = Array.isArray(this._treasure) ? this._treasure[1] : this._treasure.y;
+ const treasureScreenX = centerX + treasureX;
+ const treasureScreenY = centerY + treasureY;
+
+ if (treasureScreenX >= 0 && treasureScreenY >= 0 &&
+ treasureScreenX < this._screenWidth && treasureScreenY < this._screenHeight) {
+ this._display.draw(treasureScreenX, treasureScreenY, '$', '#ff0', '#000');
+ console.log(`Drew treasure at screen position (${treasureScreenX},${treasureScreenY})`);
+ }
+ }
+
+ // Draw monster if it exists in the game state
+ if (this._monster) {
+ const monsterX = this._monster.x;
+ const monsterY = this._monster.y;
+ const monsterScreenX = centerX + monsterX;
+ const monsterScreenY = centerY + monsterY;
+
+ if (monsterScreenX >= 0 && monsterScreenY >= 0 &&
+ monsterScreenX < this._screenWidth && monsterScreenY < this._screenHeight) {
+ this._display.draw(monsterScreenX, monsterScreenY, '~', '#f00', '#000');
+ console.log(`Drew monster at screen position (${monsterScreenX},${monsterScreenY})`);
+ }
+ }
+
// Always draw player at center of screen
- this._display.draw(centerX, centerY, '@', '#ff0', '#000');
- console.log("Drew player at center:", centerX, centerY);
+ // Special case: If player is at entrance/exit, add visual indicator
+ if (playerIsAtEntrance) {
+ console.log("Player is at entrance - drawing special indicator");
+ this._display.draw(centerX, centerY, '@', '#0ff', '#000'); // Special color for player on entrance
+ } else if (playerIsAtExit) {
+ console.log("Player is at exit - drawing special indicator");
+ this._display.draw(centerX, centerY, '@', '#0ff', '#000'); // Special color for player on exit
+ } else {
+ this._display.draw(centerX, centerY, '@', '#ff0', '#000'); // Normal player
+ }
- // Draw other entities if visible (treasure, exit, entrance)
- // ... additional drawing code for other entities ...
-
- console.log("Game screen drawing complete");
+ console.log(`Drew player at center of screen (${centerX},${centerY})`);
},
_isVisible: function(x, y) {
// Check if a tile is currently visible
return this._visibleTiles[y] && this._visibleTiles[y][x] !== undefined;
},
-
- // Add these functions to your client-side Game object (not the server-side one)
- // Function to switch to ASCII mode
- switchToAsciiMode: function() {
- console.log("Switching to ASCII mode");
+ // Add this helper method near other drawing functions
+ _drawBorder: function(color) {
+ const w = this._screenWidth;
+ const h = this._screenHeight;
- var asciiOptions = {
- width: 25,
- height: 19,
- fontSize: 16,
- fontFamily: "monospace",
- bg: "#000",
- fg: "#fff"
- };
+ // Use the stored mode state to determine character
+ const borderChar = this._isTileMode ? "#" : "+";
+ color = color || '#888'; // Default color is gray
- try {
- // Create new display with ASCII mode
- var newDisplay = new ROT.Display(asciiOptions);
-
- // Replace the existing display
- var container = document.querySelector('.rotdis');
- if (container) {
- container.innerHTML = '';
- container.appendChild(newDisplay.getContainer());
- this._display = newDisplay;
-
- // Redraw current game state
- if (this._gameActive) {
- this._drawGameScreen();
- } else {
- this._drawWelcomeScreen();
- }
- } else {
- console.error("Could not find .rotdis container");
- }
- } catch (err) {
- console.error("Error switching to ASCII mode:", err);
- }
- },
-
- // Fix the welcome screen drawing
- _drawWelcomeScreen: function() {
- console.log("Drawing welcome screen...");
- this._display.clear();
-
- // Draw borders
- for (var x = 0; x < this._display.getOptions().width; x++) {
- for (var y = 0; y < this._display.getOptions().height; y++) {
- if (x === 0 || y === 0 || x === this._display.getOptions().width-1 || y === this._display.getOptions().height-1) {
- this._display.draw(x, y, "#", "#666", "#000");
+ // Draw the border
+ for (let x = 0; x < w; x++) {
+ for (let y = 0; y < h; y++) {
+ if (x === 0 || y === 0 || x === w-1 || y === h-1) {
+ this._display.draw(x, y, borderChar, color, '#000');
}
}
}
-
- // Draw welcome text
- var centerX = Math.floor(this._display.getOptions().width / 2);
- var centerY = Math.floor(this._display.getOptions().height / 2);
-
- // For ASCII mode, we can use drawText with colors
- var y = 5;
- this._display.drawText(this._screenWidth / 2 - 5, y++, "%c{yellow}WOWGUE");
- y++;
- this._display.drawText(this._screenWidth / 2 - 11, y++, "Type enter to start!");
- this._display.drawText(this._screenWidth / 2 - 11, y++, "Or type help...");
- y+=2;
- this._display.drawText(this._screenWidth / 2 - 10, y++, "@ - This is you");
- this._display.drawText(this._screenWidth / 2 - 10, y++, "> - Escape the dungeon");
- this._display.drawText(this._screenWidth / 2 - 10, y++, "~ - Avoid the wonster");
- this._display.drawText(this._screenWidth / 2 - 10, y++, "$ - Secure the bag");
- y+=2;
-
- // Draw instructions
- this._display.drawText(2, this._display.getOptions().height - 2, "Type enter in chat =");
},
- _drawWinScreen: function(hasTreasure) {
+ // Add these functions right before the closing }; of the Game object
+
+ // Win screen
+ drawWinScreen: function(hasTreasure) {
+ console.log("Drawing win screen, hasTreasure:", hasTreasure);
+
+ // Clear the display
this.clearDisplay();
- var y = 5;
- this._display.drawText(this._screenWidth / 2 - 5, y++, "%c{green}YOU WIN!");
- y++;
+
+ // Draw green border
+ this._drawBorder('#0f0');
+
+ // Draw win message
+ let y = 5;
+ y = this._drawCenteredText(y, "YOU ESCAPED!", "green");
+ y += 1;
+
+ const subtitle = hasTreasure ? "WITH THE TREASURE!" : "ALIVE!";
+ y = this._drawCenteredText(y, subtitle, "yellow");
+ y += 2;
if (hasTreasure) {
- this._display.drawText(this._screenWidth / 2 - 15, y++, "You found the treasure and escaped!");
- } else {
- this._display.drawText(this._screenWidth / 2 - 10, y++, "You escaped the dungeon!");
+ y = this._drawCenteredText(y, "You won extra Wownero!", "yellow");
+ y += 1;
}
- y++;
- this._gameActive = false;
- this._message = "Press Enter to Play Again!";
- this._display.drawText(this._screenWidth / 2 - (this._message.length / 2), y++, `%c{yellow}${this._message}`);
+ y += 1;
+ this._drawCenteredText(y, "Type ENTER to retry", "white");
},
- _drawLoseScreen: function(reason) {
+ // Lose screen
+ drawLoseScreen: function(reason) {
+ console.log("Drawing lose screen, reason:", reason);
+
+ // Clear the display
this.clearDisplay();
- var y = 5;
- this._display.drawText(this._screenWidth / 2 - 5, y++, "%c{red}GAME OVER!");
- y++;
-
- if (reason === 'timeout') {
- this._display.drawText(this._screenWidth / 2 - 20, y++, "You didn't escape before the next block!");
- } else if (reason === 'caught') {
- this._display.drawText(this._screenWidth / 2 - 15, y++, "The monster caught you!");
- } else {
- this._display.drawText(this._screenWidth / 2 - 10, y++, `Reason: ${reason}`);
+
+ // Draw red border
+ this._drawBorder('#f00');
+
+ // Draw lose message
+ let y = 5;
+ y = this._drawCenteredText(y, "YOU DIED!", "red");
+ y += 1;
+
+ let subtitle = "";
+ if (reason === 'monster') {
+ subtitle = "KILLED BY THE MONSTER";
+ } else if (reason === 'timeout') {
+ subtitle = "YOU RAN OUT OF TIME";
}
- y++;
- this._gameActive = false;
- this._message = "Press Enter to Play Again!";
- this._display.drawText(this._screenWidth / 2 - (this._message.length / 2), y++, `%c{yellow}${this._message}`);
- },
-
- getDisplay: function() {
- return this._display;
- },
-
- getScreenWidth: function() {
- return this._screenWidth;
- },
-
- getScreenHeight: function() {
- return this._screenHeight;
- },
-
- // Add these functions to your Game object
-
- // Check if we're in ASCII mode
- isAsciiMode: function() {
- if (!this._display) return false;
- var options = this._display.getOptions();
- return !options.layout || options.layout !== "tile";
- },
-
- // Switch to ASCII mode
- switchToAsciiMode: function() {
- console.log("Switching to ASCII mode");
+ if (subtitle) {
+ y = this._drawCenteredText(y, subtitle, "red");
+ y += 1;
+ }
- var asciiOptions = {
- width: 25,
- height: 19,
- fontSize: 16,
- fontFamily: "monospace",
- bg: "#000",
- fg: "#fff"
- };
+ y += 2;
+ this._drawCenteredText(y, "Type ENTER to retry", "white");
+ },
+
+ // Public welcome screen method - add if missing
+ drawWelcomeScreen: function() {
+ this._drawWelcomeScreen();
+ },
+
+ _drawWelcomeScreen: function() {
+ console.log("Drawing welcome screen");
- try {
- // Create new display with ASCII mode
- var newDisplay = new ROT.Display(asciiOptions);
-
- // Replace the existing display in DOM
- var container = document.querySelector('.rotdis');
- if (container) {
- container.innerHTML = '';
- container.appendChild(newDisplay.getContainer());
-
- // Update the game's display reference
- this._display = newDisplay;
-
- console.log("Switched to ASCII mode successfully");
-
- // Redraw current game state
- if (this._gameActive) {
- this._drawGameScreen();
- } else {
- this._drawWelcomeScreen();
- }
-
- return true;
- } else {
- console.error("Could not find .rotdis container to replace display");
- return false;
+ // Clear the display
+ this.clearDisplay();
+
+ // Draw the border with gray color
+ this._drawBorder('#888');
+
+ // Draw content with centered text
+ let y = 3;
+ y = this._drawCenteredText(y, "WOWGUE", "white");
+ y += 4;
+
+ // Draw instructions
+ y = this._drawCenteredText(y, "Type enter to start!", "yellow");
+ y = this._drawCenteredText(y, "Or type help...", "yellow");
+ y += 2;
+
+ // Draw legend - keep these special characters with tile mappings
+ const centerX = Math.floor(this._screenWidth / 2) - 11; // Manual alignment for legend
+ this._display.drawText(centerX, y, "%c{cyan}@ %c{white}- This is you"); y++;
+ this._display.drawText(centerX, y, "%c{green}> %c{white}- Escape the dungeon"); y++;
+ this._display.drawText(centerX, y, "%c{red}~ %c{white}- Avoid the monster"); y++;
+ this._display.drawText(centerX, y, "%c{yellow}$ %c{white}- Secure the bag"); y++;
+
+ // Draw footer
+ y += 1;
+ this._drawCenteredText(17, "Type enter in chat =", "gray");
+ },
+
+ _initializeKeyboardHandler: function() {
+ console.log("Initializing keyboard handler");
+
+ // Use a local reference to avoid 'this' scope issues in event handler
+ const game = this;
+
+ // Add event listener for keydown
+ window.addEventListener('keydown', function(e) {
+ // Only process keys when game is active
+ if (!game._gameActive) {
+ return;
}
- } catch (err) {
- console.error("Error switching to ASCII mode:", err);
- return false;
- }
+
+ let direction = null;
+
+ // Determine direction based on key
+ 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:
+ // Not a movement key
+ return;
+ }
+
+ // Prevent default behavior for these keys (like scrolling)
+ e.preventDefault();
+
+ console.log(`Key pressed: ${e.key}, sending direction: ${direction}`);
+
+ // Send the movement to the server
+ if (window.socket && direction) {
+ window.socket.emit('move', direction);
+ }
+ });
+
+ console.log("Keyboard handler initialized");
+ },
+
+ // Mode switching methods
+ isAsciiMode: function() {
+ return !this._isTileMode;
},
- // Switch to tile mode
switchToTileMode: function() {
console.log("Switching to tile mode");
+ if (!window.tileSet || !window.tileMap) {
+ console.error("Cannot switch to tile mode: tileset or tilemap not available");
+ return false;
+ }
+
+ // Save current game state and screen center
+ const gameWasActive = this._gameActive;
+
+ // Create new display with tile options
+ const oldContainer = this._display.getContainer();
+ const parentElement = oldContainer.parentElement;
+
try {
- // Create new display with tile mode
- var newDisplay = new ROT.Display(options);
+ this._isTileMode = true; // Update mode state
+ this._display = new ROT.Display({
+ width: this._screenWidth,
+ height: this._screenHeight,
+ fontSize: 16,
+ fontFamily: "monospace",
+ fg: "#fff",
+ bg: "#000",
+ layout: "tile",
+ tileWidth: 32,
+ tileHeight: 32,
+ tileSet: window.tileSet,
+ tileMap: window.tileMap
+ });
- // Replace the existing display in DOM
- var container = document.querySelector('.rotdis');
- if (container) {
- container.innerHTML = '';
- container.appendChild(newDisplay.getContainer());
-
- // Update the game's display reference
- this._display = newDisplay;
-
- console.log("Switched to tile mode successfully");
-
- // Redraw current game state
- if (this._gameActive) {
- this._drawGameScreen();
- } else {
- this._drawWelcomeScreen();
- }
-
- return true;
- } else {
- console.error("Could not find .rotdis container to replace display");
- return false;
+ console.log("Created tile display with options:", this._display.getOptions());
+
+ // Replace old container with new one
+ if (parentElement) {
+ parentElement.removeChild(oldContainer);
+ parentElement.appendChild(this._display.getContainer());
}
+
+ // Redraw the screen
+ if (gameWasActive) {
+ this._drawGameScreen();
+ } else {
+ this._drawWelcomeScreen();
+ }
+
+ return true;
} catch (err) {
+ this._isTileMode = false; // Revert if failed
console.error("Error switching to tile mode:", err);
return false;
}
+ },
+
+ switchToAsciiMode: function() {
+ console.log("Switching to ASCII mode");
+
+ // Save current game state
+ const gameWasActive = this._gameActive;
+
+ // Create new display with ASCII options
+ const oldContainer = this._display.getContainer();
+ const parentElement = oldContainer.parentElement;
+
+ try {
+ this._isTileMode = false; // Update mode state
+ this._display = new ROT.Display({
+ width: this._screenWidth,
+ height: this._screenHeight,
+ fontSize: 18,
+ fontFamily: "monospace",
+ fg: "#fff",
+ bg: "#000",
+ layout: "rect"
+ });
+
+ console.log("Created ASCII display with options:", this._display.getOptions());
+
+ // Replace old container with new one
+ if (parentElement) {
+ parentElement.removeChild(oldContainer);
+ parentElement.appendChild(this._display.getContainer());
+ }
+
+ // Redraw the screen
+ if (gameWasActive) {
+ this._drawGameScreen();
+ } else {
+ this._drawWelcomeScreen();
+ }
+
+ return true;
+ } catch (err) {
+ console.error("Error switching to ASCII mode:", err);
+ return false;
+ }
+ },
+
+ // Update the init function to properly handle the tileset
+
+ init: function() {
+ console.log("Initializing game");
+
+ // Verify if tileset is actually loaded
+ this.checkTileset(function(isLoaded) {
+ if (isLoaded) {
+ Game._setupTileDisplay();
+ } else {
+ Game._setupAsciiDisplay();
+ }
+
+ // Draw the welcome screen
+ Game._drawWelcomeScreen();
+
+ // Set up the keyboard handler
+ Game._initializeKeyboardHandler();
+ });
+ },
+
+ // Add helper function to check if tileset is truly loaded
+ checkTileset: function(callback) {
+ console.log("Checking if tileset is loaded...");
+
+ // Give a short delay to ensure scripts are fully loaded
+ setTimeout(function() {
+ // More detailed logging to help debug
+ console.log("Tileset check:");
+ console.log(" window.tileSet exists:", !!window.tileSet);
+ console.log(" window.tileMap exists:", !!window.tileMap);
+ console.log(" tileMap keys:", window.tileMap ? Object.keys(window.tileMap).length : 0);
+
+ // First do a quick check
+ if (!window.tileSet || !window.tileMap ||
+ !Object.keys(window.tileMap).length) {
+ console.log("โ Tileset or tilemap not available or empty");
+ callback(false);
+ return;
+ }
+
+ // For images, we need to make sure it's really loaded
+ if (window.tileSet.complete) {
+ console.log("โ
Tileset already loaded!");
+ callback(true);
+ } else {
+ console.log("โณ Tileset still loading, adding load event listener");
+ // Add load listener to handle when it completes
+ window.tileSet.addEventListener('load', function() {
+ console.log("โ
Tileset loaded via event!");
+ callback(true);
+ });
+
+ window.tileSet.addEventListener('error', function() {
+ console.log("โ Error loading tileset");
+ callback(false);
+ });
+
+ // Set a timeout just in case
+ setTimeout(function() {
+ if (!window.tileSet.complete) {
+ console.log("โฐ Tileset load timed out");
+ callback(false);
+ }
+ }, 3000);
+ }
+ }, 100); // Short delay to ensure scripts are loaded
+ },
+
+ // Setup tile display
+ _setupTileDisplay: function() {
+ console.log("Setting up tile display");
+
+ this._isTileMode = true; // Set tile mode state
+
+ this._display = new ROT.Display({
+ width: this._screenWidth,
+ height: this._screenHeight,
+ fontSize: 16,
+ fontFamily: "monospace",
+ fg: "#fff",
+ bg: "#000",
+ layout: "tile",
+ tileWidth: 32,
+ tileHeight: 32,
+ tileSet: window.tileSet,
+ tileMap: window.tileMap
+ });
+
+ // Place the display in the div with class "rotdis"
+ const container = this._display.getContainer();
+ const rotdisElement = document.querySelector(".rotdis");
+ if (rotdisElement) {
+ // Clear any existing content first
+ rotdisElement.innerHTML = '';
+ rotdisElement.appendChild(container);
+ }
+
+ console.log("โ
Tile display setup complete");
+ },
+
+ // Setup ASCII display
+ _setupAsciiDisplay: function() {
+ console.log("Setting up ASCII display");
+
+ this._isTileMode = false; // Set ASCII mode state
+
+ this._display = new ROT.Display({
+ width: this._screenWidth,
+ height: this._screenHeight,
+ fontSize: 18,
+ fontFamily: "monospace",
+ fg: "#fff",
+ bg: "#000",
+ layout: "rect"
+ });
+
+ // Place the display in the div with class "rotdis"
+ const container = this._display.getContainer();
+ const rotdisElement = document.querySelector(".rotdis");
+ if (rotdisElement) {
+ // Clear any existing content first
+ rotdisElement.innerHTML = '';
+ rotdisElement.appendChild(container);
+ }
+
+ console.log("โ
ASCII display setup complete");
+ },
+
+ // Add this method to the Game object
+
+ // Enhanced waiting screen with improved animation
+
+ drawWaitingScreen: function() {
+ console.log("Drawing waiting screen");
+
+ // Clear the display
+ this.clearDisplay();
+
+ // Draw border
+ this._drawBorder('#888');
+
+ // Draw title
+ this._drawCenteredText(5, "WAITING FOR DUNGEON...", "yellow");
+
+ const _this = this;
+ let dots = 0;
+ let frame = 0;
+
+ // Set up dungeon scene elements (static)
+ const width = this._screenWidth;
+ const centerY = 14;
+ const floorY = centerY + 1;
+
+ // Define wall positions - now as constants for collision detection
+ const leftWallX = 5;
+ const rightWallX = width - 6;
+
+ // Store the interval ID so we can clear it later
+ this._waitingInterval = setInterval(function() {
+ // Clear the animation area only (not the whole screen)
+ for (let x = 1; x < width-1; x++) {
+ _this._display.draw(x, centerY, ' ', '#fff', '#000');
+ _this._display.draw(x, centerY-1, ' ', '#fff', '#000');
+ }
+
+ // Draw floor
+ for (let x = 1; x < width-1; x++) {
+ _this._display.draw(x, floorY, '.', '#888', '#000');
+ }
+
+ // Draw walls and items (static elements)
+ _this._display.draw(leftWallX, centerY, '#', '#fff', '#000'); // Left Wall
+ _this._display.draw(rightWallX, centerY, '#', '#fff', '#000'); // Right Wall
+ _this._display.draw(width-9, floorY-1, '$', '#ff0', '#000'); // Treasure
+ _this._display.draw(width-4, floorY-1, '>', '#0f0', '#000'); // Exit
+
+ // Calculate player position using a smoother sine wave
+ // This gives a natural back-and-forth motion
+ const playerPhase = (frame % 120) / 120; // 0.0 to 1.0 over 120 frames
+ const playerSine = Math.sin(playerPhase * 2 * Math.PI);
+
+ // Map the sine wave (-1 to 1) to the available space between walls
+ // Add 0.5 to playerSine to map from [-1,1] to [-0.5,1.5], then clamp to [0,1]
+ const normalizedPos = Math.max(0, Math.min(1, playerSine * 0.5 + 0.5));
+
+ // Calculate player position, keeping a safe distance from walls
+ const safeLeftX = leftWallX + 2;
+ const safeRightX = rightWallX - 2;
+ const playerX = Math.round(safeLeftX + normalizedPos * (safeRightX - safeLeftX));
+
+ // Monster follows player with some lag and variation
+ // Use a different phase for the monster to create pursuing behavior
+ const monsterPhase = ((frame - 15) % 120) / 120; // 15 frames behind player
+ const monsterSine = Math.sin(monsterPhase * 2 * Math.PI);
+ const monsterNormPos = Math.max(0, Math.min(1, monsterSine * 0.5 + 0.5));
+
+ // Monster stays in leftmost part of screen, never going beyond middle
+ const monsterMaxX = Math.min(playerX - 2, Math.floor(width/2) - 2);
+ const monsterMinX = leftWallX + 2;
+ const monsterX = Math.round(monsterMinX + monsterNormPos * (monsterMaxX - monsterMinX));
+
+ // Draw player and monster with proper collision detection
+ _this._display.draw(playerX, centerY, '@', '#ff0', '#000');
+ _this._display.draw(monsterX, centerY, '~', '#f00', '#000');
+
+ // Proper waiting text animation - first clear the line
+ const centerX = Math.floor(_this._screenWidth / 2);
+ for (let x = centerX-10; x <= centerX+10; x++) {
+ _this._display.draw(x, 10, ' ', '#fff', '#000');
+ }
+
+ // Now draw the waiting text with fixed position
+ const waitingText = "Waiting";
+ const dotsText = ".".repeat(dots % 4);
+ const startX = centerX - Math.floor(waitingText.length / 2) - 2;
+ _this._display.drawText(startX, 10, "%c{white}" + waitingText + dotsText);
+
+ // Update animation counters
+ dots++;
+ frame++;
+ }, 100); // Faster update rate for smoother animation
+ },
+
+ // Method to stop the waiting animation
+ stopWaitingScreen: function() {
+ if (this._waitingInterval) {
+ clearInterval(this._waitingInterval);
+ this._waitingInterval = null;
+ }
+ },
+
+ // Add these helper functions for text and animation
+
+ // Helper function to draw centered text
+ _drawCenteredText: function(y, text, color) {
+ const centerX = Math.floor(this._screenWidth / 2);
+ const x = centerX - Math.floor(text.length / 2);
+
+ // Handle color formatting
+ if (color) {
+ text = "%c{" + color + "}" + text;
+ }
+
+ this._display.drawText(x, y, text);
+ return y + 1; // Return next line position
}
-};
+
+}; // End of Game object
socket.on('game_start', function(data) {
console.log("๐ฎ Game start received with data:", data);
@@ -564,12 +833,6 @@ socket.on('game_start', function(data) {
}
try {
- // Make sure all game UI elements are visible
- $('.container, .banner, .rotdis, .chat-container').css('display', '');
-
- // Display what we received
- $('#messages').append($('').text(`Entering the dungeon...`));
-
// Start the game with received data
var success = Game.startGame(data);
console.log("Game start result:", success ? "SUCCESS" : "FAILED");
diff --git a/html/js/options.js b/html/js/options.js
index 61b9019..6c70afd 100644
--- a/html/js/options.js
+++ b/html/js/options.js
@@ -163,3 +163,13 @@ var options = {
" ": [992, 64]
}
};
+
+// Update the tileset loading to properly set the global variables
+
+// Make sure the variables are properly set in the global scope
+window.tileSet = tileSet;
+window.tileMap = options.tileMap;
+
+// Add logging to confirm they're set
+console.log("โ
Tileset assigned to window.tileSet:", !!window.tileSet);
+console.log("โ
TileMap assigned to window.tileMap:", !!window.tileMap, "with keys:", window.tileMap ? Object.keys(window.tileMap).length : 0);
diff --git a/index.js b/index.js
index b485e4f..5ea4d3b 100644
--- a/index.js
+++ b/index.js
@@ -94,16 +94,13 @@ function startGamesForWaiting(blockHeight) {
while (WAITING_PLAYERS.length > 0) {
const playerEntry = WAITING_PLAYERS.shift();
const serverId = playerEntry.serverId;
- const clientId = playerEntry.clientId;
- console.log(`Processing player: server=${serverId}, client=${clientId}`);
+ console.log(`Processing player: server=${serverId}`);
- // Get the user object
const currentUser = getUserBySocket(serverId);
if (currentUser) {
currentUser.blockRec = blockHeight;
- console.log(`Creating new game for user ${serverId}`);
try {
const game = currentUser.startGame(80, 40);
@@ -113,20 +110,18 @@ function startGamesForWaiting(blockHeight) {
gameState.blockHeight = blockHeight;
console.log(`๐ฎ SENDING GAME_START to ${serverId}`);
- console.log(`Player position: (${game.player.x}, ${game.player.y})`);
- // Send a single game start message to chat
- io.to(serverId).emit('message', `Entering the dungeon at position (0,0)...`);
-
- // Then send the actual game state
+ // Send ONLY ONE message - the game state itself
io.to(serverId).emit('game_start', gameState);
console.log(`Game started for player ${serverId}`);
} catch (error) {
console.error(`Error creating game:`, error);
+ io.to(serverId).emit('message', 'Error starting game: ' + error.message);
}
} else {
console.error(`User not found for socket ${serverId}`);
+ io.to(serverId).emit('message', 'Error: User not found');
}
}
}
@@ -179,8 +174,7 @@ io.on('connection', function(socket) {
'Welcome, to enter the dungeon type Enter, and you will be given a Wownero address, ' +
'which you must send between 10-100 WOW to enter. The more you send, the more you can win.');
}
- // Update the 'enter' command handler to NOT start games immediately
-
+ // Update the 'enter' command handler to send waiting_status
if (msg.toLowerCase() == 'enter') {
console.log(`Player ${socket.id} requested to enter the dungeon`);
@@ -195,28 +189,42 @@ io.on('connection', function(socket) {
WAITING_PLAYERS.push({
serverId: socket.id,
clientId: socket.id,
- user: currentUser
+ user: currentUser,
+ joinedAt: Date.now()
});
- // Tell player they're in queue
- io.to(socket.id).emit('message', 'Requesting to enter the dungeon...');
+ // Send waiting status data for display - THIS IS THE KEY PART
io.to(socket.id).emit('waiting_status', {
status: 'waiting',
message: 'Waiting for the next block to be found...',
position: WAITING_PLAYERS.length,
- currentBlock: debugBlockHeight || lastBlockHeight
+ currentBlock: debugBlockHeight || lastBlockHeight || 0,
+ joinTime: Date.now()
});
- // Log the queue state
console.log(`Added player to queue. Current queue: ${WAITING_PLAYERS.length} players`);
-
- // NO LONGER start game immediately - wait for next block
- // startGamesForWaiting(lastBlockHeight);
+ } else {
+ io.to(socket.id).emit('message', 'You are already in the queue. Please wait.');
}
} else {
io.to(socket.id).emit('message', 'Error: Could not add you to the game queue. Please try again.');
}
}
+ // Add a handler for the 'cancel' command
+ else if (msg.toLowerCase() == 'cancel') {
+ // Remove player from waiting queue
+ const index = WAITING_PLAYERS.findIndex(p => p.serverId === socket.id);
+
+ if (index !== -1) {
+ WAITING_PLAYERS.splice(index, 1);
+ io.to(socket.id).emit('message', 'You have left the queue.');
+
+ // Reset to welcome screen
+ io.to(socket.id).emit('queue_cancelled');
+ } else {
+ io.to(socket.id).emit('message', 'You were not in the queue.');
+ }
+ }
else {
io.emit('message', msg);
}