mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-19 07:08:33 +02:00
First kinda working version!
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+745
-197
@@ -1,207 +1,755 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Wowgue</title>
|
||||
<link rel="stylesheet" type="text/css" href="styles/stylesheet.css">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<div class='wrapper'>
|
||||
<div class='header'>
|
||||
<h1 id='staticbanner'><span id='banner'></span><span id='bannercursor'></span></h1>
|
||||
</div>
|
||||
<!-- Status area (block height) -->
|
||||
<div class='status'>
|
||||
Block height: <span id="blockheight">0</span>
|
||||
</div>
|
||||
<!-- Chat area -->
|
||||
<div class='chat'>
|
||||
<div id='messageBox'>
|
||||
<ul id='messages'>
|
||||
<li>Welcome to Wowgue!</li>
|
||||
<li>Type 'hello' to begin</li>
|
||||
</ul>
|
||||
<head>
|
||||
<title>Wowgue - A Wownero Roguelike</title>
|
||||
<link rel="stylesheet" type="text/css" href="styles/stylesheet.css">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
|
||||
<!-- First load socket.io -->
|
||||
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script>
|
||||
|
||||
<!-- THEN initialize the socket -->
|
||||
<script>
|
||||
// Create socket connection BEFORE loading game.js
|
||||
window.socket = io();
|
||||
</script>
|
||||
|
||||
<!-- Only AFTER socket is initialized, load other scripts -->
|
||||
<script src="js/jquery-3.4.1.min.js"></script>
|
||||
<script src="js/rot.min.js"></script>
|
||||
<script src="js/options.js"></script>
|
||||
<script src="js/game.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Header area -->
|
||||
<header class="banner">
|
||||
<div id="staticbanner">
|
||||
<span id="banner"></span><span id="bannercursor">|</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Main game display -->
|
||||
<div class='rotdis'></div>
|
||||
<!-- Input area -->
|
||||
<div id='messageInput'>
|
||||
<form action="" id='chatForm'>
|
||||
<input autocomplete="off" id='chatInput' />
|
||||
<button>Send</button>
|
||||
</header>
|
||||
|
||||
<!-- Status area for block height -->
|
||||
<div class="status">
|
||||
<div id="blockHeight">Block Height: <span id="heightValue">Loading...</span></div>
|
||||
<div id="connectionStatus">Status: <span id="statusValue">Connecting...</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Game display -->
|
||||
<div class="rotdis"></div>
|
||||
|
||||
<!-- Chat messages area -->
|
||||
<div class="chat-container">
|
||||
<div id="messageBox">
|
||||
<ul id="messages"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat input in its own row -->
|
||||
<div class="input-container">
|
||||
<form id="chatForm" action="">
|
||||
<input id="chatInput" autocomplete="off" placeholder="Type a command..." />
|
||||
<button type="submit">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="footer">
|
||||
footer
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/jquery-3.4.1.min.js"></script>
|
||||
<script src="js/options.js"></script>
|
||||
<script src="js/game.js"></script>
|
||||
<script src="js/rot.min.js"></script>
|
||||
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script>
|
||||
<script src="js/teletype.js"></script>
|
||||
<script>
|
||||
// Initialize socket connection
|
||||
var socket = io();
|
||||
|
||||
// Socket event listeners
|
||||
$(function() {
|
||||
// Welcome message - when server confirms connection
|
||||
socket.on('welcome', function(msg) {
|
||||
console.log('Received welcome:', msg);
|
||||
$('#messages').append($('<li>').text("Connected to server!"));
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
// Blockheight update
|
||||
socket.on('blockheight', function(msg) {
|
||||
$('#blockheight').text(msg);
|
||||
});
|
||||
|
||||
// Regular chat messages
|
||||
socket.on('message', function(msg) {
|
||||
console.log('Received message:', msg);
|
||||
$('#messages').append($('<li>').text(msg));
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
// Payment request (we'll handle this later)
|
||||
socket.on('payment_request', function(data) {
|
||||
console.log('Payment request:', data);
|
||||
$('#messages').append($('<li class="payment">').text(
|
||||
"Send between " + data.minFee + " and " + data.maxFee + " WOW to: " + data.address
|
||||
));
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
// Game start event
|
||||
socket.on('game_start', function(data) {
|
||||
console.log('Game starting:', data);
|
||||
$('#messages').append($('<li class="game-start">').text("Starting game..."));
|
||||
scrollChat();
|
||||
|
||||
// Start the game with the received data
|
||||
Game.startGame(data);
|
||||
Game._drawGameScreen(); // Draw the initial game screen
|
||||
});
|
||||
|
||||
// Game update event
|
||||
socket.on('game_update', function(data) {
|
||||
console.log('Game update:', data);
|
||||
|
||||
// Update game state
|
||||
Game._player = data.player;
|
||||
Game._monster = data.monster;
|
||||
|
||||
// Redraw the game screen
|
||||
Game._drawGameScreen();
|
||||
});
|
||||
|
||||
// Game over event
|
||||
socket.on('game_over', function(data) {
|
||||
console.log('Game over:', data);
|
||||
|
||||
// Handle game over state
|
||||
if (data.status === 'won') {
|
||||
Game._drawWinScreen(data.hasTreasure);
|
||||
$('#messages').append($('<li class="win">').text(
|
||||
"You won! " + (data.hasTreasure ? "You found the treasure!" : "")
|
||||
));
|
||||
} else {
|
||||
Game._drawLoseScreen(data.reason);
|
||||
$('#messages').append($('<li class="lose">').text(
|
||||
"Game over: " + data.reason
|
||||
));
|
||||
}
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
// Handle chat form submission
|
||||
$('#chatForm').submit(function(e) {
|
||||
e.preventDefault(); // Prevent page reload
|
||||
var message = $('#chatInput').val();
|
||||
|
||||
if (message.trim() !== '') {
|
||||
// Send message to server
|
||||
socket.emit('chat message', message);
|
||||
|
||||
// Special handling for commands
|
||||
if (message.toLowerCase() === 'enter') {
|
||||
$('#messages').append($('<li class="self-command">').text(
|
||||
"Requesting to enter the dungeon..."
|
||||
));
|
||||
}
|
||||
|
||||
// Clear input field
|
||||
$('#chatInput').val('');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Helper function to scroll chat to bottom
|
||||
function scrollChat() {
|
||||
var messageBox = $('#messageBox');
|
||||
messageBox.scrollTop(messageBox.prop("scrollHeight"));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Game initialization -->
|
||||
<script>
|
||||
// Function to initialize the game and append its display
|
||||
function initializeAndAppendDisplay() {
|
||||
console.log("Initializing Game...");
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
Wownero Roguelike v0.1.0 | Use WASD or arrow keys to move
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add teletype JS implementation -->
|
||||
<script>
|
||||
// Simple teletype implementation
|
||||
(function($) {
|
||||
$.fn.teletype = function(options) {
|
||||
var settings = $.extend({
|
||||
text: [],
|
||||
typeDelay: 100,
|
||||
backDelay: 50,
|
||||
pause: 1000,
|
||||
loop: true,
|
||||
cursor: ''
|
||||
}, options);
|
||||
|
||||
return this.each(function() {
|
||||
var $el = $(this);
|
||||
var currentTextIndex = 0;
|
||||
var currentCharIndex = 0;
|
||||
var isTyping = true;
|
||||
|
||||
function type() {
|
||||
var currentText = settings.text[currentTextIndex];
|
||||
|
||||
if (isTyping) {
|
||||
// Typing forward
|
||||
if (currentCharIndex < currentText.length) {
|
||||
$el.text($el.text() + currentText.charAt(currentCharIndex));
|
||||
currentCharIndex++;
|
||||
setTimeout(type, settings.typeDelay);
|
||||
} else {
|
||||
// Reached end of text, pause before backspacing
|
||||
isTyping = false;
|
||||
setTimeout(type, settings.pause);
|
||||
}
|
||||
} else {
|
||||
// Backspacing
|
||||
if (currentCharIndex > 0) {
|
||||
$el.text($el.text().slice(0, -1));
|
||||
currentCharIndex--;
|
||||
setTimeout(type, settings.backDelay);
|
||||
} else {
|
||||
// Finished backspacing, move to next text
|
||||
isTyping = true;
|
||||
currentTextIndex = (currentTextIndex + 1) % settings.text.length;
|
||||
if (currentTextIndex === 0 && !settings.loop) {
|
||||
// Stop if we've gone through all texts and not looping
|
||||
return;
|
||||
}
|
||||
setTimeout(type, settings.typeDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start typing
|
||||
type();
|
||||
});
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
// Cursor blink effect
|
||||
(function($) {
|
||||
$.fn.blink = function(options) {
|
||||
var settings = $.extend({
|
||||
text: ['|', ' '],
|
||||
delay: 500
|
||||
}, options);
|
||||
|
||||
return this.each(function() {
|
||||
var $el = $(this);
|
||||
var currentIndex = 0;
|
||||
|
||||
function blink() {
|
||||
$el.text(settings.text[currentIndex]);
|
||||
currentIndex = (currentIndex + 1) % settings.text.length;
|
||||
setTimeout(blink, settings.delay);
|
||||
}
|
||||
|
||||
blink();
|
||||
});
|
||||
};
|
||||
})(jQuery);
|
||||
</script>
|
||||
|
||||
<!-- Initialize teletype animation -->
|
||||
<script>
|
||||
$(function() {
|
||||
$('#banner').teletype({
|
||||
text: ["Welcome traveler...", "You have arrived at...", "The Wowngeon"],
|
||||
typeDelay: 100,
|
||||
backDelay: 50,
|
||||
pause: 1500
|
||||
});
|
||||
|
||||
$('#bannercursor').blink({
|
||||
text: ['|', ' '],
|
||||
delay: 500
|
||||
});
|
||||
|
||||
// After animations are complete, set static text
|
||||
setTimeout(function() {
|
||||
$('#banner, #bannercursor').fadeOut(500, function() {
|
||||
$('#staticbanner').text("THE WOWNGEON");
|
||||
});
|
||||
}, 14000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Global scroll helper function -->
|
||||
<script>
|
||||
function scrollChat() {
|
||||
var messageBox = $('#messageBox');
|
||||
messageBox.scrollTop(messageBox.prop("scrollHeight"));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
// Socket event listeners
|
||||
socket.on('connect', function() {
|
||||
console.log("Connected with socket ID:", socket.id);
|
||||
|
||||
// Tell server our client ID
|
||||
socket.emit('register_client', {
|
||||
clientId: socket.id,
|
||||
userAgent: navigator.userAgent
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('welcome', function(msg) {
|
||||
$('#messages').append($('<li>').text("Connected to server!"));
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
socket.on('message', function(msg) {
|
||||
$('#messages').append($('<li>').text(msg));
|
||||
scrollChat();
|
||||
});
|
||||
|
||||
// Critical game events
|
||||
// Replace the game_start handler with this improved version
|
||||
socket.on('game_start', function(data) {
|
||||
console.log("🎮 Received game_start broadcast:", data);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Check for required data
|
||||
if (!data) {
|
||||
console.error("❌ Error: Received empty game data!");
|
||||
$('#messages').append($('<li class="error">').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($('<li class="error">').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($('<li class="game-start">').text("Starting game..."));
|
||||
|
||||
try {
|
||||
// Display what we received
|
||||
$('#messages').append($('<li>').text(`Entering the dungeon at position (${data.player.x},${data.player.y})...`));
|
||||
|
||||
// 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($('<li class="error">').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) {
|
||||
Game.startGame(data);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
window.focus();
|
||||
} catch (err) {
|
||||
console.error("Error starting game:", err);
|
||||
$('#messages').append($('<li class="error">').text("Error: " + err.message));
|
||||
}
|
||||
|
||||
// Ensure chat is scrolled
|
||||
if (typeof scrollChat === 'function') {
|
||||
scrollChat();
|
||||
}
|
||||
});
|
||||
|
||||
// Add game update handler
|
||||
socket.on('game_update', function(data) {
|
||||
console.log("🎮 Received game update:", 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})`);
|
||||
}
|
||||
});
|
||||
|
||||
// Replace the chat form submission handler
|
||||
$('#chatForm').submit(function(e) {
|
||||
e.preventDefault();
|
||||
var msg = $('#chatInput').val().trim();
|
||||
if (msg) {
|
||||
console.log(`📝 SENDING CHAT: "${msg}"`);
|
||||
|
||||
// Special handling for enter command
|
||||
if (msg.toLowerCase() === 'enter') {
|
||||
console.log("🔑 'ENTER' COMMAND DETECTED - expecting game_start response");
|
||||
$('#messages').append($('<li style="color:#ff0">').text("Requesting to enter the dungeon..."));
|
||||
}
|
||||
|
||||
socket.emit('chat message', msg);
|
||||
$('#chatInput').val('');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Initialize the game when everything is ready -->
|
||||
<script>
|
||||
$(function() {
|
||||
Game.init();
|
||||
|
||||
if (!Game.getDisplay()) {
|
||||
console.error("Game display object not found after Game.init(). Cannot append.");
|
||||
return;
|
||||
}
|
||||
|
||||
var displayContainer = Game.getDisplay().getContainer();
|
||||
var targetElement = $('.rotdis');
|
||||
if (targetElement.length > 0) {
|
||||
targetElement.append(displayContainer);
|
||||
console.log("ROT display appended to .rotdis");
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Block height update script -->
|
||||
<script>
|
||||
// Function to update block height display
|
||||
function updateBlockHeight(height) {
|
||||
$('#heightValue').text(height);
|
||||
}
|
||||
|
||||
// Listen for block updates from server
|
||||
socket.on('blockheight', function(height) {
|
||||
console.log(`📈 BLOCK HEIGHT: ${height}`);
|
||||
updateBlockHeight(height);
|
||||
$('#statusValue').text('Connected');
|
||||
$('#statusValue').css('color', '#0f0');
|
||||
});
|
||||
|
||||
// Set a test value if no response
|
||||
setTimeout(function() {
|
||||
if ($('#heightValue').text() === 'Loading...') {
|
||||
updateBlockHeight('---');
|
||||
$('#statusValue').text('Waiting...');
|
||||
$('#statusValue').css('color', '#ff0');
|
||||
}
|
||||
}, 5000);
|
||||
</script>
|
||||
|
||||
<!-- Add browser detection info -->
|
||||
<div id="browser-info" style="position: fixed; right: 10px; top: 40px; background: rgba(0,0,0,0.7); color: #0f0; padding: 5px; font-family: monospace; font-size: 12px; z-index: 1000; display: none;">
|
||||
<div>Browser: <span id="browser-name">checking...</span></div>
|
||||
<div>Display: <span id="display-status">checking...</span></div>
|
||||
<button onclick="document.getElementById('browser-info').style.display='none'" style="margin-top: 5px; background: #333; border: 1px solid #666; color: white; font-size: 10px; padding: 2px 4px;">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Check browser and display status
|
||||
$(function() {
|
||||
setTimeout(function() {
|
||||
var browserInfo = document.getElementById('browser-info');
|
||||
browserInfo.style.display = 'block';
|
||||
|
||||
// Detect browser
|
||||
var browserName = "Unknown";
|
||||
if (navigator.userAgent.indexOf("Firefox") !== -1) {
|
||||
browserName = "Firefox";
|
||||
} else if (navigator.userAgent.indexOf("Chrome") !== -1) {
|
||||
browserName = "Chrome/Chromium";
|
||||
} else if (navigator.userAgent.indexOf("Safari") !== -1) {
|
||||
browserName = "Safari";
|
||||
} else if (navigator.userAgent.indexOf("Edge") !== -1) {
|
||||
browserName = "Edge";
|
||||
}
|
||||
|
||||
document.getElementById('browser-name').textContent = browserName;
|
||||
|
||||
// Check display status
|
||||
var rotdis = document.querySelector('.rotdis');
|
||||
var canvas = rotdis ? rotdis.querySelector('canvas') : null;
|
||||
|
||||
if (canvas) {
|
||||
document.getElementById('display-status').textContent =
|
||||
"OK (" + canvas.width + "x" + canvas.height + ")";
|
||||
document.getElementById('display-status').style.color = "#0f0";
|
||||
} else {
|
||||
document.getElementById('display-status').textContent = "NOT FOUND";
|
||||
document.getElementById('display-status').style.color = "#f00";
|
||||
}
|
||||
|
||||
// Hide after 10 seconds
|
||||
setTimeout(function() {
|
||||
browserInfo.style.display = 'none';
|
||||
}, 10000);
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add ASCII Mode button at the top right -->
|
||||
<button id="toggle-mode" style="position: fixed; right: 10px; top: 10px; z-index: 1000; background: #333; color: white; padding: 5px; border: 1px solid #666; border-radius: 4px;">
|
||||
Switch to ASCII Mode
|
||||
</button>
|
||||
|
||||
<script>
|
||||
document.getElementById('toggle-mode').addEventListener('click', function() {
|
||||
try {
|
||||
// Check current mode and toggle accordingly
|
||||
if (Game.isAsciiMode && Game.isAsciiMode()) {
|
||||
console.log("Switching to TILE mode...");
|
||||
if (typeof Game.switchToTileMode === 'function') {
|
||||
Game.switchToTileMode();
|
||||
this.textContent = "Switch to ASCII Mode";
|
||||
this.style.background = "#333";
|
||||
this.style.color = "white";
|
||||
} else {
|
||||
console.error("Target element '.rotdis' not found.");
|
||||
document.body.appendChild(displayContainer);
|
||||
console.error("Game.switchToTileMode is not defined");
|
||||
this.textContent = "Error: Tile Mode Not Available";
|
||||
this.style.background = "#990000";
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
// Wait for tileset to be loaded, then initialize game
|
||||
if (tileSetLoaded) {
|
||||
initializeAndAppendDisplay();
|
||||
} else {
|
||||
console.log("Tileset not loaded yet, waiting for onload...");
|
||||
tileSet.addEventListener('load', function() {
|
||||
initializeAndAppendDisplay();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$(function() {
|
||||
// Initialize teletype only after DOM is ready
|
||||
$('#banner').teletype({
|
||||
text: ["Welcome traveler...", "You have arrived at...", "The Wowngeon"]
|
||||
});
|
||||
$('#bannercursor').teletype({
|
||||
text: ['|', ' '],
|
||||
delay: 0,
|
||||
pause: 500
|
||||
});
|
||||
} else {
|
||||
console.log("Switching to ASCII mode...");
|
||||
if (typeof Game.switchToAsciiMode === 'function') {
|
||||
Game.switchToAsciiMode();
|
||||
this.textContent = "Switch to Tile Mode";
|
||||
this.style.background = "#444";
|
||||
this.style.color = "#0f0";
|
||||
} else {
|
||||
console.error("Game.switchToAsciiMode is not defined");
|
||||
this.textContent = "Error: ASCII Mode Not Available";
|
||||
this.style.background = "#990000";
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error switching display mode:", err);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add this outside of any $(function() { ... }) block, similar to blockheight handler -->
|
||||
<script>
|
||||
// Listen for game start events from server
|
||||
socket.on('game_start', function(data) {
|
||||
console.log("🎮 GAME START EVENT received:", data);
|
||||
|
||||
// Check for required data
|
||||
if (!data) {
|
||||
console.error("❌ Error: Received empty game data!");
|
||||
$('#messages').append($('<li class="error">').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($('<li class="error">').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($('<li class="game-start">').text("Starting game..."));
|
||||
|
||||
try {
|
||||
// Display what we received
|
||||
$('#messages').append($('<li>').text(`Entering the dungeon at position (${data.player.x},${data.player.y})...`));
|
||||
|
||||
// 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 ❌");
|
||||
|
||||
// Add timeout to change text after animation completes
|
||||
if (!success) {
|
||||
$('#messages').append($('<li class="error">').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() {
|
||||
$('#banner, #bannercursor').remove();
|
||||
$('#staticbanner').text("The Wowngeon");
|
||||
}, 14340);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
if (typeof Game !== 'undefined' && Game.startGame) {
|
||||
Game.startGame(data);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
window.focus();
|
||||
} catch (err) {
|
||||
console.error("Error starting game:", err);
|
||||
$('#messages').append($('<li class="error">').text("Error: " + err.message));
|
||||
}
|
||||
|
||||
// Ensure chat is scrolled
|
||||
if (typeof scrollChat === 'function') {
|
||||
scrollChat();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Enhanced socket logging wrapper -->
|
||||
<script>
|
||||
// Enhanced socket logging wrapper - FIXED VERSION
|
||||
(function(socket) {
|
||||
// Save original emit function
|
||||
const originalEmit = socket.emit;
|
||||
|
||||
// Override emit to log outgoing events
|
||||
socket.emit = function(event, data) {
|
||||
console.log(`📤 SOCKET EMIT: ${event}`, data);
|
||||
return originalEmit.apply(this, arguments);
|
||||
};
|
||||
|
||||
// DON'T override the normal 'on' function - this is causing issues
|
||||
// Instead, add a listener for all events
|
||||
socket.onAny((event, ...args) => {
|
||||
console.log(`📥 SOCKET RECEIVED EVENT: ${event}`, args);
|
||||
|
||||
// Special logging for game_start
|
||||
if (event === 'game_start') {
|
||||
const data = args[0];
|
||||
console.log("🎮 GAME START DETAILS:");
|
||||
console.log("- Player:", data && data.player ? `(${data.player.x},${data.player.y})` : "Missing");
|
||||
console.log("- VisibleTiles:", data && data.visibleTiles ? Object.keys(data.visibleTiles).length + " rows" : "Missing");
|
||||
}
|
||||
});
|
||||
})(socket);
|
||||
|
||||
// Force refresh socket connection status
|
||||
setTimeout(() => {
|
||||
console.log("Socket connection status check:", socket.connected ? "CONNECTED" : "DISCONNECTED");
|
||||
if (!socket.connected) {
|
||||
console.error("⚠️ Socket is not connected! Attempting reconnect...");
|
||||
socket.connect();
|
||||
}
|
||||
}, 3000);
|
||||
</script>
|
||||
|
||||
<!-- Add keyboard event logging -->
|
||||
<script>
|
||||
// Global keyboard event listener
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Only log when game is active
|
||||
if (Game && Game._gameActive) {
|
||||
console.log(`🔤 KEY: ${e.key} (code: ${e.keyCode})`);
|
||||
|
||||
// Log movement keys specifically
|
||||
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'a', 's', 'd'].includes(e.key)) {
|
||||
console.log(`🏃♂️ MOVEMENT: ${e.key}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add game state debugging helper -->
|
||||
<script>
|
||||
// Function to log current game state
|
||||
function debugGameState() {
|
||||
if (!Game) {
|
||||
console.error("Game object not available");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("🔍 GAME STATE:");
|
||||
console.log("- Active:", Game._gameActive);
|
||||
console.log("- Player:", Game._player ?
|
||||
`(${Game._player.x},${Game._player.y})` : "Not set");
|
||||
console.log("- Monster:", Game._monster ?
|
||||
`(${Game._monster.x},${Game._monster.y})` : "Not present");
|
||||
console.log("- Display:", Game._display ? "Initialized" : "Not initialized");
|
||||
|
||||
// Log visible tiles count
|
||||
const visibleTileCount = Game._visibleTiles ?
|
||||
Object.keys(Game._visibleTiles).reduce((sum, rowKey) => {
|
||||
return sum + Object.keys(Game._visibleTiles[rowKey]).length;
|
||||
}, 0) : 0;
|
||||
console.log("- Visible tiles:", visibleTileCount);
|
||||
|
||||
// Run this every 10 seconds while game is active
|
||||
if (Game._gameActive) {
|
||||
setTimeout(debugGameState, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
// Start debug logging when game activates
|
||||
const originalStartGame = Game.startGame;
|
||||
Game.startGame = function(data) {
|
||||
const result = originalStartGame.call(this, data);
|
||||
if (result) {
|
||||
console.log("Game started successfully, initiating debug logging");
|
||||
debugGameState();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// FOV debugging helper - add at end of page
|
||||
function debugClientFOV() {
|
||||
if (typeof Game === 'undefined') {
|
||||
console.error("Game not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("🔍 CLIENT FOV DEBUG:");
|
||||
console.log("- Game active:", Game._gameActive || false);
|
||||
console.log("- Player:", Game._player ?
|
||||
`(${Game._player.x},${Game._player.y})` : "Not set");
|
||||
|
||||
// Check visibleTiles
|
||||
if (!Game._visibleTiles) {
|
||||
console.error("❌ NO VISIBLE TILES DATA!");
|
||||
return;
|
||||
}
|
||||
|
||||
const rowKeys = Object.keys(Game._visibleTiles);
|
||||
console.log(`- Client has ${rowKeys.length} FOV rows`);
|
||||
|
||||
if (rowKeys.length > 0) {
|
||||
// Sample first row
|
||||
const sampleRow = rowKeys[0];
|
||||
const cellCount = Object.keys(Game._visibleTiles[sampleRow]).length;
|
||||
console.log(` - Sample row ${sampleRow} has ${cellCount} cells`);
|
||||
|
||||
// Check if player is in visible tiles
|
||||
if (Game._player && Game._visibleTiles[Game._player.y] &&
|
||||
Game._visibleTiles[Game._player.y][Game._player.x] !== undefined) {
|
||||
console.log(" ✅ Player position IS in visible tiles");
|
||||
} else if (Game._player) {
|
||||
console.log(` ❌ Player position (${Game._player.x},${Game._player.y}) NOT in visible tiles!`);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw debug visualization
|
||||
if (Game._display) {
|
||||
console.log("Drawing debug visualization of FOV data...");
|
||||
// Mark all visible tiles with a special character
|
||||
for (const y in Game._visibleTiles) {
|
||||
for (const x in Game._visibleTiles[y]) {
|
||||
Game._display.draw(parseInt(x), parseInt(y), "·", "#ff0", "#008");
|
||||
}
|
||||
}
|
||||
|
||||
// Re-draw player on top
|
||||
if (Game._player) {
|
||||
Game._display.draw(Game._player.x, Game._player.y, "@", "#fff", "#000");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add keyboard shortcut: Ctrl+Shift+F
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'F') {
|
||||
console.log("FOV debug triggered by keyboard shortcut");
|
||||
debugClientFOV();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add this function at the end of your debugging scripts -->
|
||||
<script>
|
||||
function sendTestMove(direction) {
|
||||
console.log("📤 Sending test move:", direction);
|
||||
socket.emit('move', direction);
|
||||
}
|
||||
// Then you can test with sendTestMove("right") in console
|
||||
</script>
|
||||
|
||||
<!-- Add this to your index.html to show input focus state -->
|
||||
<style>
|
||||
.game-focus-indicator {
|
||||
position: fixed;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
color: white;
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.chat-focused .game-focus-indicator {
|
||||
color: #aaa;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="game-focus-indicator">⌨️ Game Controls Active</div>
|
||||
|
||||
<script>
|
||||
// Add this to monitor focus state
|
||||
$(function() {
|
||||
const chatInput = $('#chatInput');
|
||||
const body = $('body');
|
||||
|
||||
chatInput.on('focus', function() {
|
||||
body.addClass('chat-focused');
|
||||
$('.game-focus-indicator').text('⌨️ Chat Input Active');
|
||||
});
|
||||
|
||||
chatInput.on('blur', function() {
|
||||
body.removeClass('chat-focused');
|
||||
$('.game-focus-indicator').text('⌨️ Game Controls Active');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add direct game_start_broadcast listener -->
|
||||
<script>
|
||||
socket.on('game_start_broadcast', function(data) {
|
||||
console.log("🎮 Game start broadcast received:", data);
|
||||
|
||||
// Check if this message is for us
|
||||
const isForUs = data.targetSocketId === socket.id;
|
||||
console.log(`Game broadcast for socket ${data.targetSocketId}, our ID is ${socket.id}, match: ${isForUs}`);
|
||||
|
||||
if (isForUs) {
|
||||
console.log("✅ This game broadcast is for us!");
|
||||
// Process as if it were a direct game_start message
|
||||
if (typeof Game !== 'undefined' && typeof Game.startGame === 'function') {
|
||||
Game.startGame(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add this handler to show waiting status -->
|
||||
|
||||
<script>
|
||||
// Add waiting_status handler
|
||||
socket.on('waiting_status', function(data) {
|
||||
console.log("⏱️ Received waiting status:", data);
|
||||
|
||||
if (data.status === 'waiting') {
|
||||
// Show waiting message
|
||||
$('#messages').append($('<li style="color:#ff0">').text(data.message));
|
||||
|
||||
// Update game area with waiting screen
|
||||
if (typeof Game !== 'undefined' && typeof Game.showWaitingScreen === 'function') {
|
||||
Game.showWaitingScreen(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+453
-86
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
socket.on('game_start', function(data) {
|
||||
console.log("🎮 Game start received with data:", data);
|
||||
$('#messages').append($('<li class="game-start">').text("Starting game..."));
|
||||
|
||||
if (!data) {
|
||||
$('#messages').append($('<li class="error">').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($('<li>').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($('<li class="error">').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($('<li class="error">').text("Error: " + err.message));
|
||||
}
|
||||
});
|
||||
+52
-6
@@ -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",
|
||||
|
||||
+155
-129
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)}...`);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user