mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-17 22:31:26 +02:00
fix transparency
This commit is contained in:
@@ -4,14 +4,15 @@ A blockchain-based roguelike dungeon crawler built on the Wownero network.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Wowngeon is a web-based roguelike game that integrates with the Wownero blockchain for a unique gaming experience. Players enter a randomly generated dungeon with the goal of escaping before the next block is discovered on the network.
|
||||
Wowngeon is a web-based roguelike game that integrates with the Wownero blockchain for a unique gaming experience. Players enter a randomly generated dungeon with the goal of escaping before the next block is discovered on the network. The player requests to enter (and optionally pay an entrance fee), and then waits for the next block to be mined. When the next block is found, the player is placed in the dungeon and must escape before the next block is found. If they fail to escape, they die and lose their entrance fee. There is a singular monster and treasure in the dungeon. If the monster catches the player, they die. If the player finds the treasure and manages to escape, they could win 3x or 5x instead of just 2x for escaping sans treasure. The game is designed to be played in a web browser, making it accessible to a wide audience.
|
||||
The game is built using Node.js for the backend, with Express and Socket.io for real-time communication. The frontend is developed using HTML5, CSS, and JavaScript, with ROT.js for rendering the dungeon and managing game mechanics. The Wownero blockchain is integrated via RPC calls to monitor block height and handle transactions.
|
||||
|
||||
### Key Features:
|
||||
|
||||
- **Blockchain-linked gameplay**: Players enter when one block is found and must escape before the next block (approximately 5 minutes in Wownero)
|
||||
- **Permadeath mechanics**: If a player fails to escape before the next block is found, they die
|
||||
- **Random dungeons**: Each game features procedurally generated maps using ROT.js
|
||||
- **Monster AI**: Evade monsters as you navigate the dungeon
|
||||
- **Monster AI**: Evade monster as you navigate the dungeon
|
||||
- **Treasure hunting**: Find treasure for bonus rewards
|
||||
- **Wownero integration**: Players pay an entrance fee (10-100 WOW) and can win rewards
|
||||
|
||||
|
||||
@@ -115,51 +115,56 @@ class Game {
|
||||
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];
|
||||
this.fov.compute(this.player.x, this.player.y, 10, (x, y, r, visibility) => { // Added r and visibility params
|
||||
if (visibility > 0) { // Only add if tile is actually visible
|
||||
if (!this.visibleTiles[y]) {
|
||||
this.visibleTiles[y] = {};
|
||||
}
|
||||
// Store the actual tile type (0 for floor, 1 for wall) from the dungeon map
|
||||
if (this.dungeon && this.dungeon.map && this.dungeon.map[y] && this.dungeon.map[y][x] !== undefined) {
|
||||
this.visibleTiles[y][x] = this.dungeon.map[y][x];
|
||||
} else {
|
||||
// Should not happen if FOV is within map bounds, but as a fallback:
|
||||
// this.visibleTiles[y][x] = 1; // Default to wall if outside known map
|
||||
}
|
||||
}
|
||||
});
|
||||
// console.log("FOV updated. Player:", this.player.x, this.player.y, "Visible tiles count:", Object.keys(this.visibleTiles).reduce((acc, k) => acc + Object.keys(this.visibleTiles[k]).length, 0));
|
||||
}
|
||||
|
||||
movePlayer(dx, dy) {
|
||||
const newX = this.player.x + dx;
|
||||
const newY = this.player.y + dy;
|
||||
|
||||
// Check if the move is valid (not into a wall)
|
||||
if (this.dungeon.map[newY] && this.dungeon.map[newY][newX] === 0) {
|
||||
// Check if the move is valid (not into a wall and within map bounds)
|
||||
if (this.dungeon &&
|
||||
this.dungeon.map[newY] &&
|
||||
this.dungeon.map[newY][newX] !== undefined &&
|
||||
this.dungeon.map[newY][newX] === 0) { // 0 is floor
|
||||
|
||||
this.player.x = newX;
|
||||
this.player.y = newY;
|
||||
console.log(`Player moved to ${newX},${newY} in game for socket ${this.socketId}`);
|
||||
|
||||
// Update FOV after movement
|
||||
// Update FOV after moving
|
||||
this.updateFOV();
|
||||
|
||||
// Check if player found treasure
|
||||
if (this.dungeon.treasure &&
|
||||
newX === this.dungeon.treasure[0] &&
|
||||
newY === this.dungeon.treasure[1]) {
|
||||
// Check for game events like finding treasure or exit
|
||||
if (this.dungeon.exit && newX === this.dungeon.exit[0] && newY === this.dungeon.exit[1]) {
|
||||
// Handle win condition (e.g., escaped)
|
||||
this.gameState = 'won'; // Or some other state
|
||||
return { status: 'moved', event: 'escaped', player: this.player, visibleTiles: this.visibleTiles };
|
||||
}
|
||||
if (this.dungeon.treasure && newX === this.dungeon.treasure[0] && newY === this.dungeon.treasure[1] && !this.player.hasTreasure) {
|
||||
this.player.hasTreasure = true;
|
||||
// Remove treasure from map or mark as collected
|
||||
return { status: 'moved', event: 'treasure_found', player: this.player, visibleTiles: this.visibleTiles };
|
||||
}
|
||||
|
||||
// Check if player reached exit
|
||||
if (this.dungeon.exit &&
|
||||
newX === this.dungeon.exit[0] &&
|
||||
newY === this.dungeon.exit[1]) {
|
||||
this.gameState = 'won';
|
||||
return { status: 'won', hasTreasure: this.player.hasTreasure };
|
||||
}
|
||||
|
||||
// Move monster after player moves
|
||||
this.moveMonster();
|
||||
|
||||
// Check if monster caught player
|
||||
if (checkMonsterKill(this.player, this.monster)) {
|
||||
this.gameState = 'lost';
|
||||
return { status: 'lost', reason: 'caught' };
|
||||
}
|
||||
|
||||
return { status: 'moved' };
|
||||
return { status: 'moved', player: this.player, visibleTiles: this.visibleTiles };
|
||||
}
|
||||
|
||||
console.log(`Player move to ${newX},${newY} for socket ${this.socketId} is invalid (wall or out of bounds).`);
|
||||
return { status: 'invalid' };
|
||||
}
|
||||
|
||||
@@ -202,77 +207,26 @@ class Game {
|
||||
// Update the getState() method to include all entities with relative positions
|
||||
getState() {
|
||||
// Calculate relative position of all entities from player's perspective
|
||||
const playerX = this.player.x;
|
||||
const playerY = this.player.y;
|
||||
// const playerX = this.player.x; // Not needed if sending absolute visibleTiles
|
||||
// const playerY = this.player.y; // Not needed if sending absolute visibleTiles
|
||||
|
||||
// Convert all visible tiles to relative coordinates
|
||||
const relativeVisibleTiles = {};
|
||||
for (const y in this.visibleTiles) {
|
||||
for (const x in this.visibleTiles[y]) {
|
||||
const relX = parseInt(x) - playerX;
|
||||
const relY = parseInt(y) - playerY;
|
||||
|
||||
// Create row if needed
|
||||
if (!relativeVisibleTiles[relY]) {
|
||||
relativeVisibleTiles[relY] = {};
|
||||
}
|
||||
|
||||
// Store tile type at relative position
|
||||
relativeVisibleTiles[relY][relX] = this.visibleTiles[y][x];
|
||||
}
|
||||
}
|
||||
|
||||
// Create the state object
|
||||
const state = {
|
||||
// The client-side Game.js expects absolute coordinates for visibleTiles.
|
||||
// So, no conversion to relative coordinates is needed here for visibleTiles.
|
||||
// Just ensure this.visibleTiles is correctly populated by updateFOV.
|
||||
|
||||
// console.log("getState called. Player:", this.player, "Visible tiles keys:", Object.keys(this.visibleTiles || {}).length);
|
||||
|
||||
return {
|
||||
gameState: this.gameState,
|
||||
player: {
|
||||
x: 0, // Player is always at (0,0) in relative coordinates
|
||||
y: 0,
|
||||
hasKey: this.player.hasKey || false,
|
||||
hasTreasure: this.player.hasTreasure || false
|
||||
},
|
||||
visibleTiles: relativeVisibleTiles
|
||||
player: { ...this.player }, // Send a copy of player object
|
||||
monster: this.monster ? { ...this.monster } : null, // Send a copy if monster exists
|
||||
// items: { ...this.items }, // If you have items, send them too
|
||||
visibleTiles: { ...this.visibleTiles }, // Send a copy of visible tiles
|
||||
// You might also want to send entrance/exit if they are always known or become known
|
||||
// entrance: this.dungeon ? this.dungeon.entrance : null,
|
||||
// exit: this.dungeon ? this.dungeon.exit : null,
|
||||
// treasure: this.dungeon ? this.dungeon.treasure : null
|
||||
};
|
||||
|
||||
// Add monster if in view
|
||||
if (this.monster && this.visibleTiles[this.monster.y] &&
|
||||
this.visibleTiles[this.monster.y][this.monster.x] !== undefined) {
|
||||
state.monster = {
|
||||
x: this.monster.x - playerX, // Relative position
|
||||
y: this.monster.y - playerY
|
||||
};
|
||||
}
|
||||
|
||||
// Add entrance if defined
|
||||
if (this.dungeon && this.dungeon.entrance) {
|
||||
// Use array format for consistency
|
||||
state.entrance = [
|
||||
this.dungeon.entrance.x - playerX,
|
||||
this.dungeon.entrance.y - playerY
|
||||
];
|
||||
}
|
||||
|
||||
// Add exit if in view
|
||||
if (this.dungeon && this.dungeon.exit &&
|
||||
this.visibleTiles[this.dungeon.exit.y] &&
|
||||
this.visibleTiles[this.dungeon.exit.y][this.dungeon.exit.x] !== undefined) {
|
||||
state.exit = [
|
||||
this.dungeon.exit.x - playerX,
|
||||
this.dungeon.exit.y - playerY
|
||||
];
|
||||
}
|
||||
|
||||
// Add treasure if in view
|
||||
if (this.dungeon && this.dungeon.treasure &&
|
||||
this.visibleTiles[this.dungeon.treasure.y] &&
|
||||
this.visibleTiles[this.dungeon.treasure.y][this.dungeon.treasure.x] !== undefined) {
|
||||
state.treasure = [
|
||||
this.dungeon.treasure.x - playerX,
|
||||
this.dungeon.treasure.y - playerY
|
||||
];
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+126
-135
@@ -18,6 +18,7 @@
|
||||
<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/teletype.js"></script> <!-- Load teletype.js -->
|
||||
<script src="js/game.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -36,7 +37,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Game display -->
|
||||
<div class="rotdis"></div>
|
||||
<div id="game-display" class="rotdis"></div>
|
||||
|
||||
<!-- Chat messages area -->
|
||||
<div class="chat-container">
|
||||
@@ -59,89 +60,6 @@
|
||||
</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() {
|
||||
@@ -152,10 +70,10 @@
|
||||
pause: 1500
|
||||
});
|
||||
|
||||
$('#bannercursor').blink({
|
||||
text: ['|', ' '],
|
||||
delay: 500
|
||||
});
|
||||
// $(\'#bannercursor\').blink({ // <<< COMMENT OUT THIS BLOCK
|
||||
// text: [\'|\', \' \'],
|
||||
// delay: 500
|
||||
// });
|
||||
|
||||
// After animations are complete, set static text
|
||||
setTimeout(function() {
|
||||
@@ -202,34 +120,36 @@
|
||||
console.log("🎮 Game start received with data:", data);
|
||||
$('#messages').append($('<li class="game-start">').text("Starting game..."));
|
||||
|
||||
// Stop waiting screen animation if it's running
|
||||
if (typeof Game !== 'undefined') {
|
||||
Game.stopWaitingScreen();
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
$('#messages').append($('<li class="error">').text("Error: No game data received"));
|
||||
$('#messages').append($('<li class="error">').text("Error: No game data received from server."));
|
||||
if (typeof Game !== 'undefined') Game._drawWelcomeScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Start the game with received data
|
||||
var success = Game.startGame(data);
|
||||
// Pass player, map, monsters, items, and initialVisibleTiles if available
|
||||
var success = Game.startGame(
|
||||
data.player,
|
||||
data.map,
|
||||
data.monsters,
|
||||
data.items,
|
||||
data.visibleTiles // Pass initial visible tiles from server
|
||||
);
|
||||
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);
|
||||
}
|
||||
$('#messages').append($('<li class="error">').text("Game start failed. Check console for details."));
|
||||
if (typeof Game !== 'undefined') Game._drawWelcomeScreen();
|
||||
} else {
|
||||
// Shift focus to the game display area after successful game start
|
||||
$('#game-display').focus();
|
||||
updateFocusIndicator();
|
||||
}
|
||||
|
||||
window.focus();
|
||||
} catch (err) {
|
||||
console.error("Error starting game:", err);
|
||||
$('#messages').append($('<li class="error">').text("Error: " + err.message));
|
||||
@@ -238,19 +158,24 @@
|
||||
|
||||
// Game update handler
|
||||
socket.on('game_update', function(data) {
|
||||
console.log("🎮 GAME UPDATE received:", data);
|
||||
// console.log("🎮 GAME UPDATE received:", data); // Reduced verbosity
|
||||
|
||||
if (typeof Game !== 'undefined') {
|
||||
// Update all entity positions
|
||||
Game._player = data.player;
|
||||
Game._visibleTiles = data.visibleTiles;
|
||||
Game._monster = data.monster;
|
||||
Game._entrance = data.entrance;
|
||||
Game._exit = data.exit;
|
||||
Game._treasure = data.treasure;
|
||||
if (typeof Game !== 'undefined' && Game._gameActive) { // Ensure game is active
|
||||
if (data.player) Game._player = data.player;
|
||||
if (data.visibleTiles) {
|
||||
Game._visibleTiles = data.visibleTiles;
|
||||
Game._updateExploredTiles(); // Update explored based on new visibility
|
||||
}
|
||||
if (data.monster) Game._monster = data.monster; // Update monster data
|
||||
if (data.items) Game._items = data.items; // Update items data
|
||||
// Potentially update _entrance, _exit, _treasure if they are part of game_update
|
||||
// Game._entrance = data.entrance;
|
||||
// Game._exit = data.exit;
|
||||
// Game._treasure = data.treasure;
|
||||
|
||||
// Draw updated screen
|
||||
Game._drawGameScreen();
|
||||
} else {
|
||||
// console.log("Game not active or Game undefined, skipping game_update draw.");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -268,6 +193,7 @@
|
||||
$('#chatInput').val('');
|
||||
|
||||
// If the message is "enter", show the waiting screen
|
||||
// AND shift focus to game display if game is about to start
|
||||
if (msg.toLowerCase() === 'enter') {
|
||||
console.log("🔑 'ENTER' COMMAND DETECTED - expecting game_start response");
|
||||
|
||||
@@ -275,11 +201,85 @@
|
||||
if (typeof Game !== 'undefined') {
|
||||
Game.drawWaitingScreen();
|
||||
}
|
||||
// Focus will be shifted in the 'game_start' handler after success
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Set initial focus to chat input
|
||||
$('#chatInput').focus();
|
||||
updateFocusIndicator(); // Initial focus indicator update
|
||||
|
||||
// Add focus/blur listeners for visual indicator
|
||||
$('#chatInput').on('focus', updateFocusIndicator).on('blur', updateFocusIndicator);
|
||||
$('#game-display').on('focus', updateFocusIndicator).on('blur', updateFocusIndicator);
|
||||
|
||||
// Keyboard listener for game actions (WASD, Arrow Keys)
|
||||
// Ensure this is attached to an element that can receive focus, like the game-display div.
|
||||
// The game-display div needs a tabindex attribute to be focusable.
|
||||
$('#game-display').attr('tabindex', '-1'); // Make it focusable
|
||||
|
||||
$(document).on('keydown', function(e) {
|
||||
// console.log(`Keydown event: ${e.key}`, "Active element:", document.activeElement, "Game active?:", Game ? Game._gameActive : "Game undefined");
|
||||
|
||||
if (document.activeElement === $('#game-display')[0]) {
|
||||
// console.log("Game display has focus.");
|
||||
if (Game && Game._gameActive) {
|
||||
// console.log("Game is active. Processing key:", e.key);
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
let moved = false;
|
||||
|
||||
switch(e.key) {
|
||||
case 'w': case 'ArrowUp': dy = -1; moved = true; break;
|
||||
case 's': case 'ArrowDown': dy = 1; moved = true; break;
|
||||
case 'a': case 'ArrowLeft': dx = -1; moved = true; break;
|
||||
case 'd': case 'ArrowRight': dx = 1; moved = true; break;
|
||||
}
|
||||
|
||||
if (moved) {
|
||||
console.log(`Attempting to move: dx=${dx}, dy=${dy}. Key: ${e.key}`);
|
||||
e.preventDefault(); // Prevent page scrolling
|
||||
// Emit movement to server instead of directly calling Game.movePlayer
|
||||
socket.emit('player_move', { dx: dx, dy: dy });
|
||||
console.log(`Emitted player_move: dx=${dx}, dy=${dy}`);
|
||||
} else {
|
||||
// console.log("Key pressed on game display, but not a movement key:", e.key);
|
||||
}
|
||||
} else {
|
||||
console.log("Game display has focus, but Game is not active. Game object:", Game, "Game._gameActive:", Game ? Game._gameActive : "N/A");
|
||||
}
|
||||
} else if (e.key === 'Enter' && document.activeElement !== $('#chatInput')[0]) {
|
||||
// console.log("Enter pressed, focusing chat input.");
|
||||
// If Enter is pressed and chat is not focused, focus chat
|
||||
$('#chatInput').focus();
|
||||
updateFocusIndicator();
|
||||
} else {
|
||||
// console.log("Keydown event, but game display not focused or not Enter key for chat focus. Active element:", document.activeElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Function to update visual focus indicator
|
||||
function updateFocusIndicator() {
|
||||
var gameDisplay = $('#game-display');
|
||||
var chatInput = $('#chatInput');
|
||||
|
||||
if (document.activeElement === gameDisplay[0]) {
|
||||
gameDisplay.css('outline', '2px solid yellow'); // Or some other visual cue
|
||||
chatInput.css('outline', 'none');
|
||||
console.log("Focus: Game Display");
|
||||
} else if (document.activeElement === chatInput[0]) {
|
||||
chatInput.css('outline', '2px solid cyan'); // Or some other visual cue
|
||||
gameDisplay.css('outline', 'none');
|
||||
console.log("Focus: Chat Input");
|
||||
} else {
|
||||
gameDisplay.css('outline', 'none');
|
||||
chatInput.css('outline', 'none');
|
||||
console.log("Focus: None or Other");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Initialize the game when everything is ready -->
|
||||
@@ -373,34 +373,25 @@ $(function() {
|
||||
<script>
|
||||
document.getElementById('toggle-mode').addEventListener('click', function() {
|
||||
try {
|
||||
var button = document.getElementById('toggle-mode');
|
||||
// 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("Game.switchToTileMode is not defined");
|
||||
this.textContent = "Error: Tile Mode Not Available";
|
||||
this.style.background = "#990000";
|
||||
}
|
||||
} else {
|
||||
if (Game && Game._isTileMode) { // If currently in Tile Mode
|
||||
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";
|
||||
}
|
||||
button.textContent = 'Switch to Tile Mode';
|
||||
} else { console.error("Game.switchToAsciiMode is not defined"); }
|
||||
} else if (Game) { // If currently in ASCII mode (or Game object exists and not in tile mode)
|
||||
console.log("Switching to TILE mode...");
|
||||
if (typeof Game.switchToTileMode === 'function') {
|
||||
Game.switchToTileMode();
|
||||
button.textContent = 'Switch to ASCII Mode';
|
||||
} else { console.error("Game.switchToTileMode is not defined"); }
|
||||
} else {
|
||||
console.error("Game object not available to toggle mode.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error switching display mode:", err);
|
||||
} catch (e) {
|
||||
console.error("Error switching display mode:", e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
+611
-748
File diff suppressed because it is too large
Load Diff
+12
-10
@@ -23,12 +23,13 @@ tileSet.onload = function() {
|
||||
|
||||
tileSet.onerror = function() {
|
||||
console.error("❌ Failed to load tileset image!");
|
||||
alert("Tileset image failed to load. Switching to ASCII mode.");
|
||||
alert("Tileset image failed to load. Game will attempt to use ASCII mode.");
|
||||
window.globalTileSetStatus = "error"; // Set a status for Game.js to check if needed
|
||||
|
||||
// Force ASCII mode when tileset fails to load
|
||||
if (typeof Game !== 'undefined' && Game._display) {
|
||||
Game.switchToAsciiMode();
|
||||
}
|
||||
// REMOVED: if (typeof Game !== 'undefined' && Game._display) {
|
||||
// REMOVED: Game.switchToAsciiMode();
|
||||
// REMOVED: }
|
||||
};
|
||||
|
||||
// Try different possible paths (to handle serving from different directories)
|
||||
@@ -45,12 +46,13 @@ setTimeout(function() {
|
||||
tileSet.src = "/tiles.png"; // Try from root
|
||||
|
||||
setTimeout(function() {
|
||||
if (!tileSetLoaded) {
|
||||
if (!tileSetLoaded && window.globalTileSetStatus !== "error") { // Check if error already handled
|
||||
console.error("Failed to load tileset after trying multiple paths");
|
||||
alert("Could not load tileset. Switching to ASCII mode.");
|
||||
if (typeof Game !== 'undefined' && Game._display) {
|
||||
Game.switchToAsciiMode();
|
||||
}
|
||||
alert("Could not load tileset. Game will attempt to use ASCII mode.");
|
||||
window.globalTileSetStatus = "error"; // Set a status
|
||||
// REMOVED: if (typeof Game !== 'undefined' && Game._display) {
|
||||
// REMOVED: Game.switchToAsciiMode();
|
||||
// REMOVED: }
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
@@ -60,7 +62,7 @@ setTimeout(function() {
|
||||
|
||||
var options = {
|
||||
layout: "tile",
|
||||
bg: "#000", // Black background instead of transparent for better visibility
|
||||
bg: "transparent", // Black background instead of transparent for better visibility
|
||||
// Adjusted dimensions for ~800x600 display
|
||||
width: 25, // 25 tiles * 32px/tile = 800px
|
||||
height: 19, // 19 tiles * 32px/tile = 608px
|
||||
|
||||
@@ -59,39 +59,19 @@
|
||||
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: #000;
|
||||
border: 2px solid #444;
|
||||
display: flex !important;
|
||||
display: flex !important; /* Using flex to center canvas */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 608px;
|
||||
width: 100%;
|
||||
/* min-height: 608px; */ /* REMOVED to allow shrinking */
|
||||
width: 100%; /* Ensure it takes full width of its grid area */
|
||||
height: 100%; /* Ensure it takes full height of its grid area */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow: hidden; /* Important to clip canvas if it somehow overflows */
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@@ -100,6 +80,9 @@
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: crisp-edges;
|
||||
max-width: 100%; /* ADDED: Scale down to fit container width */
|
||||
max-height: 100%; /* ADDED: Scale down to fit container height */
|
||||
object-fit: contain; /* ADDED: Maintain aspect ratio when scaling */
|
||||
}
|
||||
|
||||
/* Add debug overlay to see container bounds */
|
||||
|
||||
@@ -231,62 +231,36 @@ io.on('connection', function(socket) {
|
||||
});
|
||||
|
||||
// Handle player movement
|
||||
socket.on('move', function(direction) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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}`);
|
||||
|
||||
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
|
||||
});
|
||||
socket.on('player_move', function(moveData) { // Changed from 'move' to 'player_move' and added moveData
|
||||
console.log(`Player move event received from ${socket.id}:`, moveData);
|
||||
const currentUser = getUserBySocket(socket.id);
|
||||
|
||||
if (currentUser && currentUser.game && currentUser.game.gameState === 'active') {
|
||||
if (typeof moveData.dx === 'number' && typeof moveData.dy === 'number') {
|
||||
const game = currentUser.game;
|
||||
const moveResult = game.movePlayer(moveData.dx, moveData.dy); // This should update player pos and FOV
|
||||
|
||||
if (moveResult && moveResult.status === 'moved') {
|
||||
// Optional: Monster moves after player
|
||||
// game.moveMonster();
|
||||
|
||||
const updatedGameState = game.getState(); // Get the new state
|
||||
|
||||
// Log before sending to client for debugging
|
||||
console.log(`Sending game_update to ${socket.id} after player move. Player:`, updatedGameState.player, "Visible tiles keys:", Object.keys(updatedGameState.visibleTiles || {}));
|
||||
|
||||
io.to(socket.id).emit('game_update', updatedGameState);
|
||||
} 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);
|
||||
console.log(`Player move from ${socket.id} was invalid or resulted in no change.`);
|
||||
// Optionally, send an update even for invalid moves if you want to provide feedback
|
||||
// const currentGameState = game.getState();
|
||||
// io.to(socket.id).emit('game_update', currentGameState);
|
||||
}
|
||||
} else {
|
||||
console.error(`Invalid moveData received from ${socket.id}:`, moveData);
|
||||
}
|
||||
} else {
|
||||
console.log(`No active game found for player ${socket.id}`);
|
||||
console.log(`Player move event from ${socket.id} ignored: No active game found for user.`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user