From 03cc22ac4c45606a07e64394c8d9b3308f5856a8 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Sun, 27 Apr 2025 20:08:38 -0400 Subject: [PATCH] First kinda working version! --- game.js | 116 ++++- html/index.html | 942 +++++++++++++++++++++++++++++-------- html/js/game.js | 539 +++++++++++++++++---- html/js/options.js | 58 ++- html/styles/stylesheet.css | 284 ++++++----- index.js | 502 ++++++++++++-------- user.js | 63 ++- 7 files changed, 1862 insertions(+), 642 deletions(-) diff --git a/game.js b/game.js index c4c8432..0a53a8f 100644 --- a/game.js +++ b/game.js @@ -65,14 +65,16 @@ function generateDungeon(width, height) { class Game { constructor(socketId, mapWidth, mapHeight) { this.socketId = socketId; - this.width = mapWidth || 50; - this.height = mapHeight || 30; + 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 = { x: 0, y: 0, hasKey: false, hasTreasure: false }; this.monster = { x: 0, y: 0 }; this.fee = 0; // Amount player paid + this.visibleTiles = {}; // Will store visible tiles + this.fov = null; // FOV calculator this.generateDungeon(); } @@ -92,6 +94,31 @@ class Game { this.monster.x = center[0]; this.monster.y = 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) => { + if (!this.visibleTiles[y]) this.visibleTiles[y] = {}; + this.visibleTiles[y][x] = this.dungeon.map[y][x]; + }); } movePlayer(dx, dy) { @@ -99,10 +126,13 @@ class Game { const newY = this.player.y + dy; // Check if the move is valid (not into a wall) - if (this.dungeon.map[newY][newX] === 0) { + if (this.dungeon.map[newY] && this.dungeon.map[newY][newX] === 0) { this.player.x = newX; this.player.y = newY; + // Update FOV after movement + this.updateFOV(); + // Check if player found treasure if (this.dungeon.treasure && newX === this.dungeon.treasure[0] && @@ -169,16 +199,82 @@ class Game { } } + // Replace getState() method with this relative-coordinates version getState() { + // Only include visible information and translate to player-relative coordinates + const playerX = this.player.x; + const playerY = this.player.y; + + // Create relative FOV map + 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 + if (!relativeVisibleTiles[relY]) { + relativeVisibleTiles[relY] = {}; + } + + // Add relative position tile + 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 { gameState: this.gameState, - player: this.player, - monster: this.monster, - map: this.dungeon.map, - entrance: this.dungeon.entrance, - exit: this.dungeon.exit, - treasure: this.dungeon.treasure, - hasTreasure: this.player.hasTreasure + player: { + // Player is always at relative (0,0) + x: 0, + y: 0, + hasKey: this.player.hasKey || false, + hasTreasure: this.player.hasTreasure || false + }, + visibleTiles: relativeVisibleTiles, + ...entityInfo // Only include visible entities }; } } diff --git a/html/index.html b/html/index.html index cad0755..6137395 100644 --- a/html/index.html +++ b/html/index.html @@ -1,207 +1,755 @@ - - Wowgue - - - - -
-
-

-
- -
- Block height: 0 -
- -
-
-
    -
  • Welcome to Wowgue!
  • -
  • Type 'hello' to begin
  • -
+ + Wowgue - A Wownero Roguelike + + + + + + + + + + + + + + + + +
+ +
- -
- -
-
- - + + + +
+
Block Height: Loading...
+
Status: Connecting...
+
+ + +
+ + +
+
+
    +
    +
    + + +
    + + + -
    - -
    - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + + + - + if (typeof Game !== 'undefined' && Game.startGame) { + Game.startGame(data); + } + }, 500); + } + + window.focus(); + } catch (err) { + console.error("Error starting game:", err); + $('#messages').append($('
  • ').text("Error: " + err.message)); + } + + // Ensure chat is scrolled + if (typeof scrollChat === 'function') { + scrollChat(); + } +}); + + + + + + + + + + + + + + + + + + + +
    โŒจ๏ธ Game Controls Active
    + + + + + + + + + + + diff --git a/html/js/game.js b/html/js/game.js index 6e19788..ec053b8 100644 --- a/html/js/game.js +++ b/html/js/game.js @@ -1,3 +1,12 @@ +// 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); + } + }; +} + var Game = { // Change dimensions to match the display options (25 ร— 19 tiles) _screenWidth: 25, // Used for drawing coordinates (columns) @@ -13,21 +22,80 @@ var Game = { _treasure: null, _gameActive: false, _message: "", // To display messages like 'waiting' + _visibleTiles: {}, // Store currently visible tiles + _exploredTiles: {}, // Store all tiles the player has seen + + _isFirefox: function() { + return navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + }, init: function() { - // Assuming 'options' is defined globally or passed correctly - console.log("Creating ROT.Display with options:", options); + console.log("Initializing Game..."); + + // Check for Firefox + var isFirefox = this._isFirefox(); + if (isFirefox) { + console.log("Firefox detected, applying special handling"); + } + try { - this._display = new ROT.Display(options); + // 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."); - // Do not append here; we use external logic to append container + + // 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); + } + + } else { + console.error("Could not find .rotdis container"); + } } catch (e) { - console.error("Error creating ROT.Display:", e); - alert("Failed to initialize display. Check console for errors."); - return; + console.error("Error during game initialization:", e); } - // Initialize empty map representation based on _mapWidth and _mapHeight + // Initialize empty map this._map = []; for (var i = 0; i < this._mapWidth; i++) { this._map[i] = []; @@ -38,141 +106,309 @@ var Game = { } var game = this; - // Set up keyboard input + // Set up keyboard input - ONLY FOR MOVEMENT, not for starting the game window.addEventListener("keydown", function(e) { - var code = e.keyCode; - - // If game isn't active, ignore all keys except Enter - if (!game._gameActive && code !== 13) { + // 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; } - // Enter key to start game - if (code === 13 && !game._gameActive) { - console.log("Enter pressed, sending 'enter' message to server."); - socket.emit('chat message', 'enter'); - game._message = "Connecting..."; - game._drawWelcomeScreen(); - e.preventDefault(); - return; - } - - // Movement controls + // Movement controls (only when game is active AND no input has focus) var direction = ""; - switch (code) { + 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 (direction && game._gameActive) { - socket.emit('move', direction); - e.preventDefault(); + // 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 welcome screen - this._message = "Press Enter or type 'enter' to Play!"; + // 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 startGame: function(data) { console.log("Starting game with data:", data); - this._map = data.map; - this._player = data.player; - this._monster = data.monster; - this._entrance = data.entrance; - this._exit = data.exit; - this._treasure = data.treasure; - this._gameActive = true; + if (!data) { + console.error("No game data received!"); + return false; + } - this._drawGameScreen(); + try { + // Set game as active + this._gameActive = true; + + // Set all game properties from data + this._player = data.player || { x: 12, y: 12 }; + this._monster = data.monster; + this._entrance = data.entrance; + this._exit = data.exit; + this._treasure = data.treasure; + + // Get visible tiles, ensuring they're valid + if (data.visibleTiles && typeof data.visibleTiles === 'object') { + // Store the visible tiles + this._visibleTiles = data.visibleTiles; + console.log("Visible tiles:", Object.keys(this._visibleTiles).length, "rows"); + } else { + // If no tiles, 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; + + // Create a 5x5 visible area around player + 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(); + + // Clear and redraw the screen + this.clearDisplay(); + + console.log("Drawing game screen for active game"); + console.log("Player position:", this._player); + + // Draw the game with explicit console logging after each step + this._drawGameScreen(); + + return true; + } catch (err) { + console.error("Error starting game:", err); + return false; + } }, - + + // Update game state with new data from server + 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(); + } + + // Redraw the game screen + console.log("Redrawing game screen after update"); + this._drawGameScreen(); + } catch (err) { + console.error("Error updating game state:", err); + } + }, + + _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]; + } + } + }, + clearDisplay: function() { // Clear the entire display area this._display.clear(); }, _drawGameScreen: function() { + console.log("Drawing game screen..."); + + // Clear the display this.clearDisplay(); - - // Draw map - for (var x = 0; x < this._mapWidth; x++) { - for (var y = 0; y < this._mapHeight; y++) { - var tile = this._map[y] && this._map[y][x] ? this._map[y][x] : 1; + + // Calculate screen center for display + const centerX = Math.floor(this._screenWidth / 2); + const centerY = Math.floor(this._screenHeight / 2); + + console.log("Screen center:", centerX, centerY); + + // Draw visible tiles centered on player + for (let relY in this._visibleTiles) { + relY = parseInt(relY); + for (let relX in this._visibleTiles[relY]) { + relX = parseInt(relX); - if (tile === 0) { - this._display.draw(x, y, "'", "#fff"); // Floor - } else { - this._display.draw(x, y, "#", "#777"); // Wall + // Convert to screen coordinates (centered) + const screenX = centerX + relX; + const screenY = centerY + relY; + + // Only draw if on screen + if (screenX >= 0 && screenY >= 0 && screenX < this._screenWidth && screenY < this._screenHeight) { + const tileType = this._visibleTiles[relY][relX]; + + // Draw tile based on type + if (tileType === 1) { // Wall + this._display.draw(screenX, screenY, '#', '#fff', '#000'); + } else { // Floor + this._display.draw(screenX, screenY, '.', '#888', '#000'); + } } } } - - // Draw special features - if (this._entrance) { - this._display.draw(this._entrance[0], this._entrance[1], "<", "#0f0"); - } - if (this._exit) { - this._display.draw(this._exit[0], this._exit[1], ">", "#0f0"); - } + // Always draw player at center of screen + this._display.draw(centerX, centerY, '@', '#ff0', '#000'); + console.log("Drew player at center:", centerX, centerY); - if (this._treasure && (!this._player || !this._player.hasTreasure)) { - this._display.draw(this._treasure[0], this._treasure[1], "$", "#ff0"); - } + // Draw other entities if visible (treasure, exit, entrance) + // ... additional drawing code for other entities ... + + console.log("Game screen drawing complete"); + }, + + _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) - // Draw player - if (this._player) { - this._display.draw(this._player.x, this._player.y, "@", "#fff"); + // Function to switch to ASCII mode + switchToAsciiMode: function() { + console.log("Switching to ASCII mode"); + + var asciiOptions = { + width: 25, + height: 19, + fontSize: 16, + fontFamily: "monospace", + bg: "#000", + fg: "#fff" + }; + + 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); } - - // Draw monster - if (this._monster) { - this._display.draw(this._monster.x, this._monster.y, "~", "#f00"); - } - - // Draw status line - var statusY = this._mapHeight; // Position below map - this._display.drawText(0, statusY, "--------------------------------------------------------------------------------"); // Separator - statusY++; - - var statusLine = `Use Arrows/WASD/HJKL. Find ($) and reach exit (>). Avoid (~).`; - if (this._player && this._player.hasTreasure) { - statusLine = "You have the treasure ($)! Reach the exit (>)!"; - } - this._display.drawText(1, statusY, statusLine); }, + // Fix the welcome screen drawing _drawWelcomeScreen: function() { - this.clearDisplay(); + 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 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 monster"); - this._display.drawText(this._screenWidth / 2 - 10, y++, "$ - Find the treasure"); + this._display.drawText(this._screenWidth / 2 - 10, y++, "~ - Avoid the wonster"); + this._display.drawText(this._screenWidth / 2 - 10, y++, "$ - Secure the bag"); y+=2; - // Display dynamic message (like waiting or error) - if (this._message) { - this._display.drawText(this._screenWidth / 2 - this._message.length / 2, y++, `%c{yellow}${this._message}`); - } + + // Draw instructions + this._display.drawText(2, this._display.getOptions().height - 2, "Type enter in chat ="); }, _drawWinScreen: function(hasTreasure) { @@ -223,5 +459,136 @@ var Game = { 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"); + + var asciiOptions = { + width: 25, + height: 19, + fontSize: 16, + fontFamily: "monospace", + bg: "#000", + fg: "#fff" + }; + + 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; + } + } catch (err) { + console.error("Error switching to ASCII mode:", err); + return false; + } + }, + + // Switch to tile mode + switchToTileMode: function() { + console.log("Switching to tile mode"); + + try { + // Create new display with tile mode + var newDisplay = new ROT.Display(options); + + // 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; + } + } catch (err) { + console.error("Error switching to tile mode:", err); + return false; + } } -}; \ No newline at end of file +}; + +socket.on('game_start', function(data) { + console.log("๐ŸŽฎ Game start received with data:", data); + $('#messages').append($('
  • ').text("Starting game...")); + + if (!data) { + $('#messages').append($('
  • ').text("Error: No game data received")); + return; + } + + 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"); + + if (!success) { + $('#messages').append($('
  • ').text("Game start failed - trying ASCII mode")); + + // Try switching to ASCII mode + if (document.getElementById('toggle-mode')) { + document.getElementById('toggle-mode').click(); + setTimeout(function() { + Game.startGame(data); + }, 100); + } + } + + window.focus(); + } catch (err) { + console.error("Error starting game:", err); + $('#messages').append($('
  • ').text("Error: " + err.message)); + } +}); \ No newline at end of file diff --git a/html/js/options.js b/html/js/options.js index e939726..61b9019 100644 --- a/html/js/options.js +++ b/html/js/options.js @@ -1,16 +1,62 @@ var tileSet = document.createElement("img"); -var tileSetLoaded = false; // Flag to track loading status +var tileSetLoaded = false; +// More robust error handling tileSet.onload = function() { - console.log("Tileset image loaded successfully."); + console.log("โœ… Tileset image loaded successfully!"); tileSetLoaded = true; - // If Game.init was waiting, maybe trigger it now (handled in index.html) + + // Add visible verification on the page + var statusElem = document.createElement('div'); + statusElem.style.position = 'fixed'; + statusElem.style.bottom = '40px'; + statusElem.style.right = '10px'; + statusElem.style.background = 'rgba(0,255,0,0.5)'; + statusElem.style.color = 'white'; + statusElem.style.padding = '5px'; + statusElem.style.fontSize = '12px'; + statusElem.style.zIndex = '1000'; + statusElem.textContent = 'โœ“ Tileset loaded'; + document.body.appendChild(statusElem); + setTimeout(() => statusElem.remove(), 5000); }; + tileSet.onerror = function() { - console.error("Failed to load tileset image!"); - // Handle error appropriately - maybe fallback to a different layout? + console.error("โŒ Failed to load tileset image!"); + alert("Tileset image failed to load. Switching to ASCII mode."); + + // Force ASCII mode when tileset fails to load + if (typeof Game !== 'undefined' && Game._display) { + Game.switchToAsciiMode(); + } }; -tileSet.src = "tiles.png"; // Set src AFTER setting onload/onerror + +// 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) { + console.error("Failed to load tileset after trying multiple paths"); + alert("Could not load tileset. Switching to ASCII mode."); + if (typeof Game !== 'undefined' && Game._display) { + Game.switchToAsciiMode(); + } + } + }, 1000); + } + }, 1000); + } +}, 1000); var options = { layout: "tile", diff --git a/html/styles/stylesheet.css b/html/styles/stylesheet.css index e15c511..20ebfe0 100644 --- a/html/styles/stylesheet.css +++ b/html/styles/stylesheet.css @@ -1,199 +1,225 @@ -/* Basic body styling */ -body { - background-color: #222; - color: #eee; - font-family: sans-serif; - margin: 0; - padding: 0; - display: flex; /* Use flexbox to center wrapper */ - justify-content: center; - align-items: flex-start; /* Align wrapper to top */ - min-height: 100vh; +/* Font declarations */ +@font-face { + font-family: 'PixelOperator'; + src: url('fonts/pixel-operator/PixelOperator.ttf') format('truetype'); + font-weight: normal; + font-style: normal; } -/* Wrapper grid container */ -.wrapper { +@font-face { + font-family: 'PixelOperator'; + src: url('fonts/pixel-operator/PixelOperator-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; +} + +/* Updated layout with 5-row structure */ +.container { display: grid; - /* Define grid areas */ grid-template-areas: "header header header" "status game game" "chat game game" - "chat game game" - "chat input input" + "input input input" "footer footer footer"; - /* Define column sizes: status/chat column, game columns */ - grid-template-columns: 250px 1fr 1fr; /* Left sidebar fixed width, game takes remaining space */ - /* Define row sizes */ - grid-template-rows: auto auto 1fr 1fr auto auto; /* Header, status, chat rows, input, footer */ - gap: 10px; /* Spacing between grid items */ - padding: 10px; - max-width: 1400px; - width: 95%; - border: 1px solid #444; - background-color: #333; - height: 95vh; /* Set height to fill most of viewport */ + grid-template-columns: 350px 1fr 1fr; + grid-template-rows: auto auto 1fr auto auto; + height: 100vh; + width: 100%; + max-width: 100vw; + overflow: hidden; + gap: 1px; + background-color: #222; } -/* Assign elements to grid areas */ -.header { - grid-area: header; - background-color: #444; - padding: 5px; -} - -.status { - grid-area: status; - background-color: #444; - padding: 10px; +/* Header area */ +.banner { + grid-area: header; + background-color: #111; + padding: 5px; + color: white; + text-align: left; + border-bottom: 1px solid #444; + font-family: 'PixelOperator', monospace; font-weight: bold; + font-size: 24px; + letter-spacing: 1px; } -.chat { - grid-area: chat; - background-color: #444; +/* Status area for block height display */ +.status { + grid-area: status; + background-color: #222; padding: 10px; + color: #0f0; + font-family: monospace; display: flex; flex-direction: column; - overflow: hidden; /* Prevent overflow */ + justify-content: flex-start; + border-right: 1px solid #444; } +/* Firefox-specific fixes */ +@-moz-document url-prefix() { + .rotdis { + display: block !important; + position: relative; + height: 608px; + min-width: 800px; + } + + .rotdis canvas { + position: absolute !important; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: auto !important; + height: auto !important; + } +} + +/* Make sure rotdis is properly visible across all browsers */ .rotdis { grid-area: game; - background-color: #111; - display: flex; + background-color: #000; + border: 2px solid #444; + display: flex !important; justify-content: center; align-items: center; + min-height: 608px; + width: 100%; + position: relative; overflow: hidden; + z-index: 5; } -/* Make ROT.js canvas display properly */ .rotdis canvas { - display: block; - max-width: 100%; - max-height: 100%; - object-fit: contain; /* Preserve aspect ratio */ + display: block !important; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; } -#messageInput { - grid-area: input; - background-color: #444; - padding: 10px; +/* Add debug overlay to see container bounds */ +.rotdis::before { + content: "GAME AREA"; + position: absolute; + top: 5px; + left: 5px; + color: rgba(255,255,255,0.3); + font-family: monospace; + font-size: 12px; + z-index: 1; + pointer-events: none; } -.footer { - grid-area: footer; - background-color: #444; - padding: 5px; - text-align: center; +/* Chat messages area */ +.chat-container { + grid-area: chat; + background-color: #222; + display: flex; + flex-direction: column; + border-right: 1px solid #444; + overflow: hidden; /* Prevents chat from causing scrolling */ } -/* Header styling */ -.header h1 { - margin: 0; - padding: 5px; - text-align: center; -} - -/* Status styling */ -#blockheight { - color: lightblue; -} - -/* Chat box styling */ #messageBox { - flex-grow: 1; /* Allow message box to fill chat area */ - border: 1px solid #555; - background-color: #2a2a2a; - overflow-y: auto; /* Enable vertical scrolling */ - height: 100%; /* Fill available height */ - margin-bottom: 5px; + flex-grow: 1; + overflow-y: auto; + padding: 10px; + height: 100%; + max-height: 100%; } #messages { list-style-type: none; margin: 0; - padding: 5px; + padding: 0; + color: #fff; /* Default text color */; } #messages li { - padding: 2px 0; - border-bottom: 1px dashed #444; - font-size: 0.9em; + padding: 3px 5px; + margin-bottom: 2px; + word-break: break-word; +} + +/* Chat input area, now in its own row */ +.input-container { + grid-area: input; + background-color: #333; + padding: 10px; + border-top: 1px solid #444; + border-bottom: 1px solid #444; } -/* Chat input styling */ #chatForm { display: flex; + width: 100%; } #chatInput { flex-grow: 1; - padding: 5px; - border: 1px solid #555; - background-color: #eee; - color: #111; + padding: 8px; + border: none; + background-color: #444; + color: #fff; + border-radius: 3px 0 0 3px; } #chatForm button { - padding: 5px 10px; - border: 1px solid #555; - background-color: #666; - color: #eee; + padding: 0 20px; + background-color: #555; + color: white; + border: none; cursor: pointer; + border-radius: 0 3px 3px 0; } -#chatForm button:hover { - background-color: #777; -} - -/* Teletype styling */ +/* Add this to your stylesheet to use the font in other places */ #staticbanner { - margin: 0; - padding: 5px; - text-align: left; - font-size: 24px; - color: #fff; -} - -#banner { - display: inline-block; - min-height: 1em; -} - -#bannercursor { - display: inline-block; - color: #fff; -} - -/* Add these styles for chat messages */ - -#messages li.self-command { - color: #aaf; - font-style: italic; -} - -#messages li.payment { - color: #ff0; + font-family: 'PixelOperator', monospace; font-weight: bold; - border-left: 3px solid #ff0; - padding-left: 5px; - margin: 5px 0; + letter-spacing: 1px; + padding: 10px; } -#messages li.win { +.footer { + grid-area: footer; + background-color: #111; + color: #888; + padding: 5px; + text-align: center; + font-size: 0.8em; + font-family: 'PixelOperator', sans-serif; +} + +/* Game messages styling */ +li.game-start { color: #0f0; font-weight: bold; + text-align: center; + margin: 5px 0; + padding: 2px 0; + background: rgba(0,255,0,0.1); } -#messages li.lose { +li.error { color: #f00; font-weight: bold; + font-family: monospace; + padding: 2px 5px; + background: rgba(255,0,0,0.1); + border-left: 2px solid #f00; } -#messages li.game-start { +li.debug { color: #0ff; - font-weight: bold; - margin: 5px 0; + font-family: monospace; + font-size: 0.9em; + padding-left: 10px; + border-left: 2px solid #0ff; } diff --git a/index.js b/index.js index a568585..b485e4f 100644 --- a/index.js +++ b/index.js @@ -4,73 +4,131 @@ var http = require('http').Server(app); var io = require('socket.io')(http); var rpc = require('./rpccalls.js'); var db = require('./dbcalls.js'); -var user = require('./user.js'); +var user = require('./user'); + +// Function to get a user by socket ID +// Enhance the getUserBySocket function to try both IDs + +function getUserBySocket(socketId) { + console.log(`Looking up user with socketId: ${socketId}`); + + // Try direct lookup first + let foundUser = user.getUserBySocketId(socketId); + + // If not found but we have a socket map, try the mapped ID + if (!foundUser && clientSocketMap.has(socketId)) { + const mappedId = clientSocketMap.get(socketId); + console.log(`Socket ID ${socketId} not found directly, trying mapped ID: ${mappedId}`); + foundUser = user.getUserBySocketId(mappedId); + } + + console.log(`User lookup result for ${socketId}: ${foundUser ? "FOUND" : "NOT FOUND"}`); + return foundUser; +} + const Game = require('./game'); const activeGames = new Map(); // Maps socketId to Game objects let lastBlockHeight = 0; // Track last block height const MIN_FEE = 10; // Minimum entrance fee in WOW const MAX_FEE = 100; // Maximum entrance fee in WOW const WAITING_PLAYERS = []; // Players waiting for the next block - -var users = []; -var players = []; +const DEBUG_MODE = true; // Set to false to disable debug mode +let debugBlockHeight = 1; +const clientSocketMap = new Map(); // To track client-to-server socket ID mappings app.use(express.static('html')); app.get('/', function(req, res) { res.sendfile('index.html'); }); -// Function to get a user by socket ID -function getUserBySocket(socketId) { - return users.find(user => user.socketid === socketId); -} - -// Modified block check function -setInterval(function() { - rpc.daemonCall("get_block_count", "", function(result) { - const currentHeight = result.result.count; - rpc.lastBlock.setHeight(currentHeight); +// Replace your existing block check interval with this conditional version +if (DEBUG_MODE) { + console.log("๐Ÿ› DEBUG MODE ENABLED - Simulating blocks every 30 seconds"); + + // Initial debug block + io.emit('blockheight', debugBlockHeight); + + // Debug block height simulator + setInterval(function() { + debugBlockHeight++; + console.log(`๐Ÿ› DEBUG: New simulated block: ${debugBlockHeight}`); + io.emit('blockheight', debugBlockHeight); - // If new block found - if (currentHeight > lastBlockHeight) { - console.log(`New block found: ${currentHeight}`); - io.emit('blockheight', currentHeight); + // Start games for waiting players + startGamesForWaiting(debugBlockHeight); + + // Check active games for timeout + checkGamesTimeout(debugBlockHeight); + + }, 30000); // Every 30 seconds +} else { + // Your original block check code + setInterval(function() { + rpc.daemonCall("get_block_count", "", function(result) { + const currentHeight = result.result.count; + rpc.lastBlock.setHeight(currentHeight); - // Start games for waiting players - startGamesForWaiting(currentHeight); - - // Check active games for timeout - checkGamesTimeout(currentHeight); - - lastBlockHeight = currentHeight; - } else { - io.emit('blockheight', currentHeight); - } - }); -}, 10000); + // If new block found + if (currentHeight > lastBlockHeight) { + console.log(`New block found: ${currentHeight}`); + io.emit('blockheight', currentHeight); + + // Start games for waiting players + startGamesForWaiting(currentHeight); + + // Check active games for timeout + checkGamesTimeout(currentHeight); + + lastBlockHeight = currentHeight; + } else { + io.emit('blockheight', currentHeight); + } + }); + }, 5000); +} // Start games for waiting players function startGamesForWaiting(blockHeight) { - while (WAITING_PLAYERS.length > 0) { - const userId = WAITING_PLAYERS.shift(); - const user = getUserBySocket(userId); - - if (user) { - user.blockRec = blockHeight; - const game = user.startGame(80, 40); // Create a new game - activeGames.set(userId, game); - - io.to(userId).emit('game_start', { - map: game.dungeon.map, - player: game.player, - monster: game.monster, - entrance: game.dungeon.entrance, - exit: game.dungeon.exit, - treasure: game.dungeon.treasure, - blockHeight: blockHeight - }); + console.log(`Starting games for ${WAITING_PLAYERS.length} waiting players at block height ${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}`); + + // 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); + activeGames.set(serverId, game); + + const gameState = game.getState(); + 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 + io.to(serverId).emit('game_start', gameState); + + console.log(`Game started for player ${serverId}`); + } catch (error) { + console.error(`Error creating game:`, error); + } + } else { + console.error(`User not found for socket ${serverId}`); + } } - } } // Check if any active games have timed out @@ -98,33 +156,66 @@ io.on('connection', function(socket) { console.log(socket.client.id); console.log(socket.handshake.address); - db.insertVisitor(socket.client.id, socket.handshake.address, 0); - users.push(new user.User(socket.client.id, socket.handshake.address)); + // Create and register user (automatically stored in registry) + new user.User(socket.id, socket.handshake.address); + // Also store socket.client.id as an attribute + const newUser = getUserBySocket(socket.id); + if (newUser) { + newUser.clientId = socket.client.id; + console.log(`User created with both socket.id (${socket.id}) and socket.client.id (${socket.client.id})`); + } - io.to(`${socket.client.id}`).emit('welcome', socket.client.id); + // Insert into visitors DB + db.insertVisitor(socket.client.id, socket.handshake.address, 0); + + // Send welcome message + io.to(socket.client.id).emit('welcome', socket.client.id); socket.on('chat message', function(msg) { - console.log(msg); + console.log('Message received:', msg); if (msg.toLowerCase() == 'hello') { io.to(`${socket.client.id}`).emit('message', '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.'); } - else if (msg.toLowerCase() == 'enter') { - const currentUser = getUserBySocket(socket.client.id); - - if (currentUser) { - // FOR TESTING: Skip payment and directly queue the player - currentUser.verifyPayment(10); // Minimal test fee - WAITING_PLAYERS.push(socket.client.id); + // Update the 'enter' command handler to NOT start games immediately + + if (msg.toLowerCase() == 'enter') { + console.log(`Player ${socket.id} requested to enter the dungeon`); - io.to(`${socket.client.id}`).emit('message', - 'For testing: You will enter the dungeon when the next block is found.'); - - // To test without waiting for a block, immediately start a game - startGameForPlayer(socket.client.id, lastBlockHeight || 1); - } + // Use the updated user lookup function + const currentUser = getUserBySocket(socket.id); + + if (currentUser) { + console.log(`Found user for socket ${socket.id}, adding to queue...`); + + // Add to waiting players queue + if (!WAITING_PLAYERS.some(p => p.serverId === socket.id)) { + WAITING_PLAYERS.push({ + serverId: socket.id, + clientId: socket.id, + user: currentUser + }); + + // Tell player they're in queue + io.to(socket.id).emit('message', 'Requesting to enter the dungeon...'); + 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 + }); + + // 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', 'Error: Could not add you to the game queue. Please try again.'); + } } else { io.emit('message', msg); @@ -133,153 +224,180 @@ io.on('connection', function(socket) { // Handle player movement socket.on('move', function(direction) { - const game = activeGames.get(socket.client.id); + console.log(`Player ${socket.id} moved: ${direction}`); + + // Find the active game + let game = activeGames.get(socket.id); + + if (!game) { + // Try fallback methods to find the game + console.log(`Game not found for ${socket.id}, trying client.id: ${socket.client.id}`); + game = activeGames.get(socket.client.id); + } + + if (!game) { + const mappedId = clientSocketMap.get(socket.id); + if (mappedId) { + console.log(`Game not found, trying mapped ID: ${mappedId}`); + game = activeGames.get(mappedId); + } + } if (game) { - let dx = 0, dy = 0; - - // Convert direction to dx,dy - switch(direction) { - case 'up': dy = -1; break; - case 'down': dy = 1; break; - case 'left': dx = -1; break; - case 'right': dx = 1; break; - } - - const result = game.movePlayer(dx, dy); - - // Send updated game state - socket.emit('game_update', { - player: game.player, - monster: game.monster, - status: result.status - }); - - // Handle win/lose conditions - if (result.status === 'won') { - const user = getUserBySocket(socket.client.id); - const reward = user.calculateReward(); + let dx = 0, dy = 0; - socket.emit('game_over', { - status: 'won', - hasTreasure: game.player.hasTreasure, - reward: reward - }); + // Convert direction to dx,dy + switch(direction) { + case 'up': dy = -1; break; + case 'down': dy = 1; break; + case 'left': dx = -1; break; + case 'right': dx = 1; break; + } - // TODO: Process payout + // Server still uses absolute coordinates internally + console.log(`Moving player ${dx},${dy} from (${game.player.x},${game.player.y})`); + const result = game.movePlayer(dx, dy); + console.log(`Move result: ${result.status}`); - activeGames.delete(socket.client.id); - } - else if (result.status === 'lost') { - socket.emit('game_over', { - status: 'lost', - reason: result.reason - }); - - activeGames.delete(socket.client.id); - } + if (result.status === 'won') { + io.to(socket.id).emit('game_over', { + status: 'won', + hasTreasure: game.player.hasTreasure + }); + } else if (result.status === 'lost') { + io.to(socket.id).emit('game_over', { + status: 'lost', + reason: result.reason + }); + } else { + // Get updated game state (now with relative coordinates) + const updatedState = game.getState(); + + // Send updated game state back to client + console.log(`Sending updated game state to ${socket.id}`); + io.to(socket.id).emit('game_update', updatedState); + } + } else { + console.log(`No active game found for player ${socket.id}`); } }); // Handle disconnection - socket.on('disconnect', function () { - console.log('A user disconnected'); + socket.on('disconnect', function() { + console.log('User disconnected', socket.client.id); + user.removeUser(socket.client.id); + }); + + // Add this to your socket handlers in index.js + socket.on('debug_ping', function(data) { + console.log(`Debug ping received from ${socket.client.id}`); + socket.emit('debug_pong', { + message: "Hello from server!", + clientTime: data.time, + serverTime: Date.now(), + socketId: socket.client.id + }); + }); + + socket.on('register_client', function(data) { + console.log(`Client registered: ${socket.id} (server) <-> ${data.clientId} (client)`); - // Clean up any active games - if (activeGames.has(socket.client.id)) { - activeGames.delete(socket.client.id); + // Store both mappings for lookup + clientSocketMap.set(data.clientId, socket.id); + clientSocketMap.set(socket.id, data.clientId); + + // Send confirmation back to client + socket.emit('socket_registered', { + clientId: data.clientId, + serverId: socket.id, + success: true + }); +}); + +}); // End of io.on('connection') handler + +// Add this debug function to check all registered users + +function debugRegisteredUsers() { + console.log("\n๐Ÿ” CHECKING ALL REGISTERED USERS:"); + const allUsers = user.getAllUsers ? user.getAllUsers() : null; + + if (!allUsers) { + console.log("No getAllUsers function available or no users registered"); + return; } - users = spliceOut(socket.client.id, "socketid", users); - }); -}); + console.log(`Found ${allUsers.length} registered users:`); + allUsers.forEach((u, i) => { + console.log(`User ${i+1}:`, { + id: u.id, + clientId: u.clientId || 'undefined', + address: u.address + }); + }); + console.log(); // Empty line for readability +} + +// Call this after a user registers and at intervals +setInterval(debugRegisteredUsers, 10000); http.listen(3000, function() { console.log('listening on *:3000'); }); - -// Remove objects from an array if one attribute matches -function spliceOut(target, field, array) { - for (var i = array.length - 1; i >= 0; --i) { - if (array[i][field] == target) { - array.splice(i,1); - } - } - return array; -} -// Check if objects are in an array if one attribute matches -function checkArray(target, field, array) { - for (var i = array.length - 1; i >= 0; --i) { - if (array[i][field] == target) { - return true; - } - } - return false; -} - -// Add this function to generate a dungeon -function generateDungeon(width, height) { - // Create a new instance of ROT.Map.Digger - const digger = new ROT.Map.Digger(width, height, { - roomWidth: [3, 9], - roomHeight: [3, 5], - corridorLength: [2, 5], - dugPercentage: 0.2 - }); - - // Create a map representation - const map = Array(height).fill().map(() => Array(width).fill(1)); // 1 = wall - - // Callback to dig out rooms and corridors - digger.create((x, y, value) => { - map[y][x] = value; // 0 = floor, 1 = wall - }); - - // Get rooms and place special features - const rooms = digger.getRooms(); - let entrance = null; - let exit = null; - let treasure = null; - - if (rooms.length > 0) { - // Place entrance in the first room - entrance = rooms[0].getCenter(); - - // Place exit in the last room - exit = rooms[rooms.length-1].getCenter(); - - // Place treasure in a middle room - const treasureRoom = rooms[Math.floor(rooms.length/2)]; - treasure = treasureRoom.getCenter(); - } - - return { - map: map, - entrance: entrance, - exit: exit, - treasure: treasure, - rooms: rooms - }; -} - -// Add this helper function to start a game for a specific player immediately: +// Make sure this function is defined in your file function startGameForPlayer(socketId, blockHeight) { - const user = getUserBySocket(socketId); + console.log(`Starting game for player ${socketId} at block height ${blockHeight}`); - if (user) { - user.blockRec = blockHeight; - const game = user.startGame(25, 19); // Create a new game with dimensions matching our display + const currentUser = getUserBySocket(socketId); + if (!currentUser) { + console.error(`User with socket ID ${socketId} not found`); + return; + } + + try { + // Create a new game + const game = currentUser.startGame(25, 19); + if (!game) { + console.error("Game creation failed"); + return; + } + + // Store the game in activeGames map activeGames.set(socketId, game); - io.to(socketId).emit('game_start', { - map: game.dungeon.map, - player: game.player, + // Get game state - ensure it has the required fields + const gameState = { + gameState: "active", // Add this so frontend knows it's active + player: { + x: game.player.x, + y: game.player.y, + hasKey: !!game.player.hasKey, + hasTreasure: !!game.player.hasTreasure + }, monster: game.monster, - entrance: game.dungeon.entrance, - exit: game.dungeon.exit, - treasure: game.dungeon.treasure, - blockHeight: blockHeight - }); + visibleTiles: game.calculateFOV ? game.calculateFOV() : {}, + entrance: [game.dungeon.entrance.x, game.dungeon.entrance.y], + exit: [game.dungeon.exit.x, game.dungeon.exit.y], + treasure: [game.dungeon.treasure.x, game.dungeon.treasure.y] + }; + + console.log("Sending game_start with state:", JSON.stringify(gameState, null, 2).substring(0, 200) + "..."); + + // Add a small delay before sending (sometimes helps with socket timing issues) + setTimeout(() => { + debugSocket(socketId, 'game_start', gameState); + // Send initial game state to client + io.to(socketId).emit('game_start', gameState); + console.log(`Game started for player ${socketId}`); + }, 100); + } catch (err) { + console.error("Error starting game:", err); + io.to(socketId).emit('message', 'Error starting game: ' + err.message); } } + +function debugSocket(socketId, eventName, data) { + console.log(`๐Ÿ”Œ SOCKET DEBUG: Sending ${eventName} to ${socketId.substring(0, 8)}...`); + console.log(`๐Ÿ“ฆ PAYLOAD (first 300 chars): ${JSON.stringify(data).substring(0, 300)}...`); +} diff --git a/user.js b/user.js index b4e13d9..2163eab 100644 --- a/user.js +++ b/user.js @@ -1,3 +1,8 @@ +const Game = require('./game'); // Add this import at the top + +// Global users registry +const userRegistry = new Map(); + function User(socketid, ip) { this.id = null; this.socketid = socketid; @@ -12,34 +17,48 @@ function User(socketid, ip) { this.game = null; // Reference to current game this.paymentAmount = 0; this.paymentVerified = false; + + // Auto-register in the registry + userRegistry.set(socketid, this); + console.log(`User registered with socket ID: ${socketid}`); } -User.prototype.startGame = function(mapWidth, mapHeight) { - const Game = require('./game'); - this.game = new Game(this.socketid, mapWidth, mapHeight); - return this.game; -}; +// Add lookup function +function getUserBySocketId(socketId) { + const user = userRegistry.get(socketId); + console.log(`Looking up user ${socketId}: ${user ? "FOUND" : "NOT FOUND"}`); + return user; +} -User.prototype.verifyPayment = function(amount) { - this.paymentAmount = amount; - this.paymentVerified = true; - return true; -}; +// Add function to remove user +function removeUser(socketId) { + const removed = userRegistry.delete(socketId); + console.log(`User ${socketId} removed: ${removed ? "YES" : "NO"}`); + return removed; +} -User.prototype.calculateReward = function() { - if (!this.game || this.game.gameState !== 'won') return 0; - - // Base reward is 2x the entrance fee - let reward = this.paymentAmount * 2; - - // Bonus for finding treasure - if (this.game.player.hasTreasure) { - reward = this.paymentAmount * 5; +// Get all users as array +function getAllUsers() { + return Array.from(userRegistry.values()); +} + +// Add this method to your User function/class +User.prototype.startGame = function(width, height) { + console.log(`Creating new game for user ${this.socketid} with dimensions ${width}x${height}`); + try { + this.game = new Game(this.socketid, width, height); + return this.game; + } catch (err) { + console.error("Error creating game:", err); + return null; } - - return reward; }; +// Rest of your existing code... + module.exports = { - User: User + User: User, + getUserBySocketId: getUserBySocketId, + removeUser: removeUser, + getAllUsers: getAllUsers };