mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-24 09:38:32 +02:00
refactor frontend and fix debug mode
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
// Refactored Game Object - Main game controller using modular architecture
|
||||
// This provides a mock socket when the real one isn't available
|
||||
if (typeof window !== 'undefined' && !window.socket) {
|
||||
window.socket = {
|
||||
emit: function(event, data) {
|
||||
// Mock socket emit - removed debug logging
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var Game = {
|
||||
_screenWidth: 25, // Default values, will be updated from options
|
||||
_screenHeight: 19,
|
||||
_inputEnabled: true,
|
||||
_messageLog: [],
|
||||
_maxLogMessages: 5,
|
||||
|
||||
init: function() {
|
||||
// Update screen dimensions from options
|
||||
if (typeof options !== 'undefined') {
|
||||
this._screenWidth = options.width || 25;
|
||||
this._screenHeight = options.height || 19;
|
||||
}
|
||||
|
||||
// Initialize all modules
|
||||
if (!DisplayManager.init(this._screenWidth, this._screenHeight)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScreenManager.init(this._screenWidth, this._screenHeight);
|
||||
RenderEngine.init(this._screenWidth, this._screenHeight);
|
||||
GameState.init();
|
||||
|
||||
// Draw welcome screen
|
||||
ScreenManager.drawWelcomeScreen();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_ensureDisplay: function() {
|
||||
return DisplayManager.ensureDisplay();
|
||||
},
|
||||
|
||||
_isFirefox: function() {
|
||||
return navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
||||
},
|
||||
|
||||
startGame: function(playerData, mapData, monsterData, itemData, initialVisibleTiles, lightingData, torchData) {
|
||||
if (!this._ensureDisplay()) {
|
||||
console.error("Game: Cannot start game, display not ready.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DisplayManager.clearDisplay();
|
||||
|
||||
// Force clear all display caches and artifacts
|
||||
if (typeof DisplayManager !== 'undefined' && DisplayManager.forceClearToBlack) {
|
||||
DisplayManager.forceClearToBlack();
|
||||
}
|
||||
|
||||
// Stop the block simulation to prevent welcome screen from interfering
|
||||
if (typeof ScreenManager !== 'undefined' && ScreenManager.stopBlockSimulation) {
|
||||
ScreenManager.stopBlockSimulation();
|
||||
}
|
||||
|
||||
try {
|
||||
GameState.setGameActive(true);
|
||||
|
||||
// Clear any previous game state but preserve what we need for initialization
|
||||
GameState._exploredTiles = {};
|
||||
// Don't clear _visibleTiles yet - let initializeMap handle it properly
|
||||
GameState._map = {};
|
||||
GameState._items = {};
|
||||
GameState._monster = null;
|
||||
GameState._entrance = null;
|
||||
GameState._exit = null;
|
||||
GameState._treasure = null;
|
||||
|
||||
// Initialize player
|
||||
GameState.initializePlayer(playerData);
|
||||
|
||||
// Initialize other game objects
|
||||
GameState._monster = monsterData;
|
||||
GameState._items = itemData;
|
||||
GameState._entrance = null;
|
||||
GameState._exit = null;
|
||||
GameState._treasure = null;
|
||||
|
||||
// Initialize lighting and torch data
|
||||
if (lightingData) {
|
||||
GameState._lighting = lightingData;
|
||||
}
|
||||
if (torchData) {
|
||||
GameState._torches = torchData;
|
||||
}
|
||||
|
||||
// Initialize map and validate player position
|
||||
GameState.initializeMap(mapData, initialVisibleTiles, this._screenWidth, this._screenHeight);
|
||||
|
||||
if (!GameState.validatePlayerPosition(this._screenWidth, this._screenHeight)) {
|
||||
ScreenManager.drawCenteredText(10, "Error: Map invalid / No player start!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute field of view
|
||||
GameState.computeFieldOfView();
|
||||
|
||||
// Setup scheduler and engine
|
||||
if (!GameState._scheduler) GameState._scheduler = new ROT.Scheduler.Simple();
|
||||
else GameState._scheduler.clear();
|
||||
if (!GameState._engine) GameState._engine = new ROT.Engine(GameState._scheduler);
|
||||
|
||||
GameState._scheduler.add(GameState._player, true);
|
||||
|
||||
if (!GameState._map[GameState._player.y] || GameState._map[GameState._player.y][GameState._player.x] === undefined) {
|
||||
console.error(`Player position (${GameState._player.x}, ${GameState._player.y}) is STILL invalid before drawing. Map issue?`);
|
||||
ScreenManager.drawCenteredText(10, "Error: Player invalid for draw!");
|
||||
return false;
|
||||
}
|
||||
|
||||
this._drawGameScreen();
|
||||
return true;
|
||||
|
||||
} catch (err) {
|
||||
console.error("Error during Game.startGame:", err);
|
||||
console.error("Error name:", err.name, "Error message:", err.message, "Stack:", err.stack);
|
||||
if (err.message && (err.message.includes("_map") || err.message.includes("Cannot read properties of undefined"))) {
|
||||
console.error("Detailed error info: Player:", GameState._player, "Map keys:", Object.keys(GameState._map || {}).length);
|
||||
}
|
||||
try {
|
||||
ScreenManager.drawCenteredText(10, "Error starting game!");
|
||||
ScreenManager.drawCenteredText(12, "Check console (F12) for details.");
|
||||
} catch (drawError) {
|
||||
console.error("Failed to draw error message on screen:", drawError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Update game state with new data from server
|
||||
updateGameState: function(data) {
|
||||
const needsRedraw = GameState.updateGameState(data);
|
||||
|
||||
// Redraw the game screen if any relevant data changed
|
||||
if (needsRedraw) {
|
||||
this._drawGameScreen();
|
||||
}
|
||||
},
|
||||
|
||||
clearDisplay: function() {
|
||||
DisplayManager.clearDisplay();
|
||||
},
|
||||
|
||||
_drawGameScreen: function() {
|
||||
if (!this._ensureDisplay()) return;
|
||||
const gameState = GameState.getGameStateForRender();
|
||||
|
||||
RenderEngine.drawGameScreen(gameState);
|
||||
},
|
||||
|
||||
// Screen drawing methods - delegate to ScreenManager
|
||||
_drawWelcomeScreen: function() {
|
||||
ScreenManager.drawWelcomeScreen();
|
||||
},
|
||||
|
||||
drawWinScreen: function(hasTreasure) {
|
||||
ScreenManager.drawWinScreen(hasTreasure);
|
||||
},
|
||||
|
||||
drawLoseScreen: function(reason) {
|
||||
ScreenManager.drawLoseScreen(reason);
|
||||
},
|
||||
|
||||
drawWaitingScreen: function() {
|
||||
ScreenManager.drawWaitingScreen();
|
||||
},
|
||||
|
||||
stopWaitingScreen: function() {
|
||||
ScreenManager.stopWaitingScreen();
|
||||
},
|
||||
|
||||
// Display mode switching - delegate to DisplayManager
|
||||
switchToAsciiMode: function() {
|
||||
DisplayManager.switchToAsciiMode();
|
||||
},
|
||||
|
||||
switchToTileMode: function() {
|
||||
DisplayManager.switchToTileMode();
|
||||
},
|
||||
|
||||
// Player movement - delegate to GameState
|
||||
movePlayer: function(dx, dy) {
|
||||
const moved = GameState.movePlayer(dx, dy, this._screenWidth, this._screenHeight);
|
||||
if (moved) {
|
||||
this._drawGameScreen();
|
||||
}
|
||||
},
|
||||
|
||||
// Debug functions - delegate to GameState
|
||||
debugPrintMap: function() {
|
||||
GameState.debugPrintMap(this._screenWidth, this._screenHeight);
|
||||
},
|
||||
|
||||
debugTileMapping: function() {
|
||||
GameState.debugTileMapping();
|
||||
},
|
||||
|
||||
// Properties for backward compatibility
|
||||
get _gameActive() {
|
||||
return GameState.isGameActive();
|
||||
},
|
||||
|
||||
set _gameActive(value) {
|
||||
GameState.setGameActive(value);
|
||||
},
|
||||
|
||||
get _player() {
|
||||
return GameState._player;
|
||||
},
|
||||
|
||||
get _monster() {
|
||||
return GameState._monster;
|
||||
},
|
||||
|
||||
get _map() {
|
||||
return GameState._map;
|
||||
},
|
||||
|
||||
get _items() {
|
||||
return GameState._items;
|
||||
},
|
||||
|
||||
get _visibleTiles() {
|
||||
return GameState._visibleTiles;
|
||||
},
|
||||
|
||||
get _exploredTiles() {
|
||||
return GameState._exploredTiles;
|
||||
}
|
||||
};
|
||||
|
||||
// Debug utility functions (keep as-is for compatibility)
|
||||
const GameDebug = {
|
||||
log: function(message) {
|
||||
console.log(message);
|
||||
this.updateDebugDisplay(message);
|
||||
this.sendToServer(message);
|
||||
},
|
||||
|
||||
updateDebugDisplay: function(message) {
|
||||
const debugContent = document.getElementById('debug-content');
|
||||
if (debugContent) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
debugContent.innerHTML += `<div>[${timestamp}] ${message}</div>`;
|
||||
// Keep only last 10 debug messages
|
||||
const lines = debugContent.children;
|
||||
if (lines.length > 10) {
|
||||
debugContent.removeChild(lines[0]);
|
||||
}
|
||||
// Auto-scroll to bottom
|
||||
const debugDisplay = document.getElementById('debug-display');
|
||||
debugDisplay.scrollTop = debugDisplay.scrollHeight;
|
||||
}
|
||||
},
|
||||
|
||||
sendToServer: function(message) {
|
||||
try {
|
||||
fetch('/debug', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ message: message })
|
||||
}).catch(err => {
|
||||
// Silently fail if server unavailable
|
||||
});
|
||||
} catch (err) {
|
||||
// Silently fail
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Make them available globally
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Game = Game;
|
||||
window.GameDebug = GameDebug;
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
// GameState - Handles game state management and updates
|
||||
var GameState = {
|
||||
_player: null,
|
||||
_monster: null,
|
||||
_map: {},
|
||||
_items: {},
|
||||
_gameActive: false,
|
||||
_visibleTiles: {},
|
||||
_exploredTiles: {},
|
||||
_entrance: null,
|
||||
_exit: null,
|
||||
_treasure: null,
|
||||
_message: null,
|
||||
_scheduler: null,
|
||||
_engine: null,
|
||||
_lighting: {},
|
||||
_torches: [],
|
||||
|
||||
init: function() {
|
||||
this.reset();
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._player = null;
|
||||
this._monster = null;
|
||||
this._map = {};
|
||||
this._items = {};
|
||||
this._gameActive = false;
|
||||
this._visibleTiles = {};
|
||||
this._exploredTiles = {};
|
||||
this._entrance = null;
|
||||
this._exit = null;
|
||||
this._treasure = null;
|
||||
this._message = null;
|
||||
this._lighting = {};
|
||||
this._torches = [];
|
||||
|
||||
if (this._scheduler) this._scheduler.clear();
|
||||
if (!this._scheduler) this._scheduler = new ROT.Scheduler.Simple();
|
||||
if (!this._engine) this._engine = new ROT.Engine(this._scheduler);
|
||||
},
|
||||
|
||||
setGameActive: function(active) {
|
||||
this._gameActive = active;
|
||||
},
|
||||
|
||||
isGameActive: function() {
|
||||
return this._gameActive;
|
||||
},
|
||||
|
||||
initializePlayer: function(playerData) {
|
||||
if (playerData && typeof playerData.x === 'number' && typeof playerData.y === 'number') {
|
||||
this._player = { x: playerData.x, y: playerData.y };
|
||||
} else {
|
||||
console.warn("Invalid or missing playerData.x/y, using default player position.", playerData);
|
||||
this._player = { x: 12, y: 12 }; // Default position
|
||||
}
|
||||
// console.log("Player initialized to:", this._player);
|
||||
return this._player;
|
||||
},
|
||||
|
||||
initializeMap: function(mapData, initialVisibleTiles, screenWidth, screenHeight) {
|
||||
this._map = {};
|
||||
this._visibleTiles = {};
|
||||
this._exploredTiles = this._exploredTiles || {};
|
||||
|
||||
let mapInitialized = false;
|
||||
|
||||
// Try to initialize from mapData.tiles
|
||||
if (mapData && mapData.tiles && typeof mapData.tiles === 'object' && Object.keys(mapData.tiles).length > 0) {
|
||||
for (var yKey in mapData.tiles) {
|
||||
const y = parseInt(yKey);
|
||||
this._map[y] = {};
|
||||
for (var xKey in mapData.tiles[y]) {
|
||||
const x = parseInt(xKey);
|
||||
this._map[y][x] = mapData.tiles[y][x];
|
||||
}
|
||||
}
|
||||
mapInitialized = true;
|
||||
}
|
||||
// Try array-based mapData
|
||||
else if (mapData && Array.isArray(mapData) && mapData.length > 0) {
|
||||
for (let y = 0; y < mapData.length; y++) {
|
||||
this._map[y] = {};
|
||||
if (mapData[y] && Array.isArray(mapData[y])) {
|
||||
for (let x = 0; x < mapData[y].length; x++) {
|
||||
this._map[y][x] = mapData[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
mapInitialized = true;
|
||||
}
|
||||
// Try initialVisibleTiles
|
||||
else if (initialVisibleTiles && typeof initialVisibleTiles === 'object' && Object.keys(initialVisibleTiles).length > 0) {
|
||||
this._visibleTiles = JSON.parse(JSON.stringify(initialVisibleTiles)); // Deep copy
|
||||
|
||||
for (const yKey in this._visibleTiles) {
|
||||
const y = parseInt(yKey);
|
||||
this._map[y] = this._map[y] || {};
|
||||
for (const xKey in this._visibleTiles[y]) {
|
||||
const x = parseInt(xKey);
|
||||
this._map[y][x] = this._visibleTiles[y][x];
|
||||
}
|
||||
}
|
||||
mapInitialized = true;
|
||||
}
|
||||
|
||||
// Create default map if nothing worked
|
||||
if (!mapInitialized) {
|
||||
console.warn("No valid mapData or initialVisibleTiles provided. Creating default map for testing.");
|
||||
const defaultMapWidth = screenWidth;
|
||||
const defaultMapHeight = screenHeight;
|
||||
for (let y = 0; y < defaultMapHeight; y++) {
|
||||
this._map[y] = {};
|
||||
for (let x = 0; x < defaultMapWidth; x++) {
|
||||
this._map[y][x] = (x === 0 || x === defaultMapWidth - 1 || y === 0 || y === defaultMapHeight - 1) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
// Adjust player position if it's in a wall of the default map
|
||||
if (this._map[this._player.y] && this._map[this._player.y][this._player.x] === 1) {
|
||||
this._player.x = Math.floor(defaultMapWidth / 2);
|
||||
this._player.y = Math.floor(defaultMapHeight / 2);
|
||||
if (this._map[this._player.y][this._player.x] === 1) {
|
||||
this._map[this._player.y][this._player.x] = 0; // Force floor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mapInitialized;
|
||||
},
|
||||
|
||||
validatePlayerPosition: function(screenWidth, screenHeight) {
|
||||
// Validate player position against the map
|
||||
if (!this._map[this._player.y] || this._map[this._player.y][this._player.x] === undefined) {
|
||||
console.error(`Player position (${this._player.x}, ${this._player.y}) is invalid or outside map boundaries. Attempting to find fallback.`);
|
||||
let foundFallback = false;
|
||||
for (let y_scan = 0; y_scan < screenHeight; y_scan++) {
|
||||
if (this._map[y_scan]) {
|
||||
for (let x_scan = 0; x_scan < screenWidth; x_scan++) {
|
||||
const tile = this._map[y_scan][x_scan];
|
||||
// Check for both legacy (0) and new ("'1", "'2") floor tiles
|
||||
if (tile === 0 || tile === "'1" || tile === "'2") {
|
||||
this._player.x = x_scan;
|
||||
this._player.y = y_scan;
|
||||
foundFallback = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundFallback) break;
|
||||
}
|
||||
if (!foundFallback) {
|
||||
console.error("Could not find a valid fallback starting position for the player on the map.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
computeFieldOfView: function() {
|
||||
// Initialize FOV with corrected wall detection callback
|
||||
var fov = new ROT.FOV.PreciseShadowcasting(function(x, y) {
|
||||
// Return true if the tile is transparent (walkable), false if it blocks light
|
||||
if (!this._map[y] || this._map[y][x] === undefined) {
|
||||
return false; // Unknown tiles block light
|
||||
}
|
||||
const tile = this._map[y][x];
|
||||
// Check for both legacy (0) and new ("'1", "'2") floor tiles
|
||||
return tile === 0 || tile === "'1" || tile === "'2";
|
||||
}.bind(this));
|
||||
|
||||
// If map was NOT built from initialVisibleTiles, compute FOV now
|
||||
if (!this._visibleTiles || Object.keys(this._visibleTiles).length === 0) {
|
||||
this._visibleTiles = {};
|
||||
fov.compute(this._player.x, this._player.y, 10, function(x, y, r, visibility) {
|
||||
if (!this._visibleTiles[y]) this._visibleTiles[y] = {};
|
||||
if (this._map[y] && this._map[y][x] !== undefined) {
|
||||
this._visibleTiles[y][x] = this._map[y][x];
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// NOTE: Don't call updateExploredTiles() here on initial game load
|
||||
// Explored tiles should only be updated when player moves, not on first FOV computation
|
||||
},
|
||||
|
||||
updateExploredTiles: function() {
|
||||
// Add all currently visible tiles to explored tiles
|
||||
for (var yKey in this._visibleTiles) {
|
||||
const y = parseInt(yKey);
|
||||
if (!this._exploredTiles[y]) this._exploredTiles[y] = {};
|
||||
for (var xKey in this._visibleTiles[y]) {
|
||||
const x = parseInt(xKey);
|
||||
this._exploredTiles[y][x] = this._visibleTiles[y][x];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateGameState: function(data) {
|
||||
if (!this._gameActive) {
|
||||
console.warn("GameState.updateGameState called, but game is not active. Ignoring update.");
|
||||
return false;
|
||||
}
|
||||
if (!data) {
|
||||
console.error("GameState.updateGameState: No update data received!");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
let needsRedraw = false;
|
||||
|
||||
// Update player state
|
||||
if (data.player) {
|
||||
this._player = data.player;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update monster state
|
||||
if (data.monster !== undefined) {
|
||||
this._monster = data.monster;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update items
|
||||
if (data.items !== undefined) {
|
||||
this._items = data.items;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update entrance, exit, treasure
|
||||
if (data.entrance !== undefined) {
|
||||
this._entrance = data.entrance;
|
||||
needsRedraw = true;
|
||||
}
|
||||
if (data.exit !== undefined) {
|
||||
this._exit = data.exit;
|
||||
needsRedraw = true;
|
||||
}
|
||||
if (data.treasure !== undefined) {
|
||||
this._treasure = data.treasure;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update lighting and torch data
|
||||
if (data.lighting !== undefined) {
|
||||
this._lighting = data.lighting;
|
||||
needsRedraw = true;
|
||||
}
|
||||
if (data.torches !== undefined) {
|
||||
this._torches = data.torches;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
// Update visible tiles (most critical for movement feedback)
|
||||
if (data.visibleTiles && typeof data.visibleTiles === 'object' && Object.keys(data.visibleTiles).length > 0) {
|
||||
this._visibleTiles = data.visibleTiles;
|
||||
|
||||
// Debug log for specific problematic coordinates
|
||||
if (this._visibleTiles[18] && (this._visibleTiles[18][36] !== undefined || this._visibleTiles[18][35] !== undefined)) {
|
||||
const clientDebug = `🔍 CLIENT y=18: x=35: ${this._visibleTiles[18][35]}, x=36: ${this._visibleTiles[18][36]}`;
|
||||
if (window.GameDebug) window.GameDebug.updateDebugDisplay(clientDebug);
|
||||
}
|
||||
if (this._visibleTiles[16] && (this._visibleTiles[16][36] !== undefined || this._visibleTiles[16][35] !== undefined)) {
|
||||
const clientDebug = `🔍 CLIENT y=16: x=35: ${this._visibleTiles[16][35]}, x=36: ${this._visibleTiles[16][36]}`;
|
||||
if (window.GameDebug) window.GameDebug.updateDebugDisplay(clientDebug);
|
||||
}
|
||||
|
||||
this.updateExploredTiles();
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
return needsRedraw;
|
||||
} catch (err) {
|
||||
console.error("Error in GameState.updateGameState:", err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
movePlayer: function(dx, dy, screenWidth, screenHeight) {
|
||||
if (!DisplayManager.ensureDisplay()) return;
|
||||
|
||||
const newX = this._player.x + dx;
|
||||
const newY = this._player.y + dy;
|
||||
|
||||
// Basic bounds checking
|
||||
if (newX < 0 || newY < 0 || newX >= screenWidth || newY >= screenHeight) {
|
||||
console.warn("Attempted to move player outside of bounds:", newX, newY);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the new position is a wall (1) or out of map bounds
|
||||
if (this._map[newY] && this._map[newY][newX] !== undefined && this._map[newY][newX] !== 1) {
|
||||
// Move the player
|
||||
this._player.x = newX;
|
||||
this._player.y = newY;
|
||||
|
||||
// Update visibility and redraw
|
||||
this.updateExploredTiles();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Debug functions
|
||||
debugPrintMap: function(screenWidth, screenHeight) {
|
||||
// Map debugging - preserve console.log for debug function
|
||||
console.log("Current game map:");
|
||||
for (let y = 0; y < screenHeight; y++) {
|
||||
let row = "";
|
||||
for (let x = 0; x < screenWidth; x++) {
|
||||
if (this._map[y] && this._map[y][x] !== undefined) {
|
||||
row += this._map[y][x] + " ";
|
||||
} else {
|
||||
row += "? "; // Unknown/undefined area
|
||||
}
|
||||
}
|
||||
console.log(row);
|
||||
}
|
||||
},
|
||||
|
||||
debugTileMapping: function() {
|
||||
// Tile mapping debugging - preserve console.log for debug function
|
||||
const testValues = [0, 1, undefined, null];
|
||||
for (const val of testValues) {
|
||||
const char = (val === 1) ? '#' : "\'";
|
||||
const debugMsg = `Test mapping: value=${val} -> char='${char}'`;
|
||||
console.log(debugMsg);
|
||||
if (window.GameDebug) window.GameDebug.updateDebugDisplay(debugMsg);
|
||||
}
|
||||
},
|
||||
|
||||
// Compute field of view for the player (LOCAL TESTING ONLY - server handles FOV)
|
||||
computeFieldOfView: function() {
|
||||
if (!this._player) {
|
||||
console.warn("Cannot compute FOV: player not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Warning for local FOV computation (keep for debugging)
|
||||
// console.log("⚠️ LOCAL FOV COMPUTATION (should only be used for testing without server)");
|
||||
|
||||
const oldVisibleTiles = JSON.parse(JSON.stringify(this._visibleTiles));
|
||||
this._visibleTiles = {};
|
||||
|
||||
const fov = new ROT.FOV.PreciseShadowcasting(function(x, y) {
|
||||
const tile = this._map[y] && this._map[y][x];
|
||||
// Check for both legacy (0) and new ("'1", "'2") floor tiles
|
||||
return tile === 0 || tile === "'1" || tile === "'2";
|
||||
}.bind(this));
|
||||
|
||||
fov.compute(this._player.x, this._player.y, 6, function(x, y, r, visibility) {
|
||||
if (!this._visibleTiles[y]) this._visibleTiles[y] = {};
|
||||
this._visibleTiles[y][x] = this._map[y] && this._map[y][x] !== undefined ? this._map[y][x] : 0;
|
||||
|
||||
// Mark as explored
|
||||
if (!this._exploredTiles[y]) this._exploredTiles[y] = {};
|
||||
this._exploredTiles[y][x] = this._map[y] && this._map[y][x] !== undefined ? this._map[y][x] : 0;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// Get game state data for rendering
|
||||
getGameStateForRender: function() {
|
||||
const renderState = {
|
||||
map: this._map,
|
||||
player: this._player,
|
||||
monster: this._monster,
|
||||
items: this._items,
|
||||
entrance: this._entrance,
|
||||
exit: this._exit,
|
||||
treasure: this._treasure,
|
||||
visibleTiles: this._visibleTiles,
|
||||
exploredTiles: this._exploredTiles,
|
||||
message: this._message,
|
||||
gameActive: this._gameActive,
|
||||
lighting: this._lighting,
|
||||
torches: this._torches
|
||||
};
|
||||
|
||||
return renderState;
|
||||
},
|
||||
|
||||
// Move player and update game state
|
||||
movePlayer: function(dx, dy, screenWidth, screenHeight) {
|
||||
if (!this._player) {
|
||||
console.warn("Cannot move player: player not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
const newX = this._player.x + dx;
|
||||
const newY = this._player.y + dy;
|
||||
|
||||
// Check bounds
|
||||
if (newX < 0 || newX >= screenWidth || newY < 0 || newY >= screenHeight) {
|
||||
// console.log("Player move blocked: out of bounds");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the new position is passable
|
||||
if (this._map[newY] && this._map[newY][newX] === 1) {
|
||||
// console.log("Player move blocked: wall at", newX, newY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update player position
|
||||
this._player.x = newX;
|
||||
this._player.y = newY;
|
||||
|
||||
// console.log("Player moved to:", this._player.x, this._player.y);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Make it available globally
|
||||
if (typeof window !== 'undefined') {
|
||||
window.GameState = GameState;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
var tileSet = document.createElement("img");
|
||||
var tileSetLoaded = false;
|
||||
|
||||
tileSet.onload = function() {
|
||||
tileSetLoaded = true;
|
||||
};
|
||||
|
||||
tileSet.onerror = function() {
|
||||
console.error("Failed to load tileset image!");
|
||||
};
|
||||
|
||||
tileSet.src = "tiles.png";
|
||||
|
||||
var options = {
|
||||
layout: "tile",
|
||||
bg: "transparent",
|
||||
// Adjusted dimensions for ~800x600 display
|
||||
width: 25, // 25 tiles * 32px/tile = 800px
|
||||
height: 19, // 19 tiles * 32px/tile = 608px
|
||||
tileWidth: 32, // Width of a single tile in pixels
|
||||
tileHeight: 32, // Height of a single tile in pixels
|
||||
tileColorize: true, // Enable colorization for tile stacks
|
||||
tileSet: tileSet,
|
||||
|
||||
// GAME CONFIGURATION - Change these to select different assets
|
||||
gameConfig: {
|
||||
playerType: "@", // Options: "@", "@2"
|
||||
treasureType: "$W", // Options: "$W" (Wownero), "$M" (Monero), "$B" (Bitcoin), "$" (Regular)
|
||||
monsterType: "~2", // Options: "~", "~2"
|
||||
|
||||
// Floor tile configuration
|
||||
primaryFloor: "'1", // Main floor tile (99% of floors)
|
||||
secondaryFloor: "'2", // Alt floor tile (1% of floors)
|
||||
floorVariation: 0.01, // Percentage of secondary floor tiles (0.01 = 1%)
|
||||
|
||||
// Torch configuration
|
||||
torchEnabled: true, // Enable/disable torch placement
|
||||
torchDensity: 0.15, // Percentage of wall tiles that get torches (0.15 = 15%)
|
||||
torchTile: "torch" // Torch tile identifier
|
||||
},
|
||||
tileMap: {
|
||||
// Keep your existing tile mappings
|
||||
"'1": [0, 0], // Floor
|
||||
">": [32, 0], // Exit
|
||||
"@": [64, 0], // Player
|
||||
"~": [96, 0], // Monster
|
||||
"$W": [960, 0], // Wownero Treasure
|
||||
"$M": [992, 0], // Monero Treasure
|
||||
"'2": [1024, 0], // Alt floor
|
||||
"<": [0, 32], // Entrance
|
||||
"=": [32, 32], // Monster Entrance
|
||||
"#": [64, 32], // Wall
|
||||
"$B": [96, 32], // Bitcoin Treasure
|
||||
"@2": [960, 32], // Player Alt
|
||||
"~2": [992, 32], // Monster Alt
|
||||
"torch": [1024, 32], // Torch
|
||||
// ... include all other necessary character mappings ...
|
||||
"a": [128, 0],
|
||||
"b": [160, 0],
|
||||
"c": [192, 0],
|
||||
"d": [224, 0],
|
||||
"e": [256, 0],
|
||||
"f": [288, 0],
|
||||
"g": [320, 0],
|
||||
"h": [352, 0],
|
||||
"i": [384, 0],
|
||||
"j": [416, 0],
|
||||
"k": [448, 0],
|
||||
"l": [480, 0],
|
||||
"m": [512, 0], // Duplicate M? Ensure one is correct
|
||||
"n": [544, 0],
|
||||
"o": [576, 0],
|
||||
"p": [608, 0],
|
||||
"q": [640, 0],
|
||||
"r": [672, 0],
|
||||
"s": [704, 0],
|
||||
"t": [736, 0],
|
||||
"u": [768, 0],
|
||||
"v": [800, 0],
|
||||
"w": [832, 0],
|
||||
"x": [864, 0],
|
||||
"y": [896, 0],
|
||||
"z": [928, 0],
|
||||
"A": [128, 32],
|
||||
"B": [160, 32],
|
||||
"C": [192, 32],
|
||||
"D": [224, 32],
|
||||
"E": [256, 32],
|
||||
"F": [288, 32],
|
||||
"G": [320, 32],
|
||||
"H": [352, 32],
|
||||
"I": [384, 32],
|
||||
"J": [416, 32],
|
||||
"K": [448, 32],
|
||||
"L": [480, 32],
|
||||
"M": [512, 32], // Already mapped M
|
||||
"N": [544, 32],
|
||||
"O": [576, 32],
|
||||
"P": [608, 32],
|
||||
"Q": [640, 32],
|
||||
"R": [672, 32],
|
||||
"S": [704, 32],
|
||||
"T": [736, 32],
|
||||
"U": [768, 32],
|
||||
"V": [800, 32],
|
||||
"W": [832, 32],
|
||||
"X": [864, 32],
|
||||
"Y": [896, 32],
|
||||
"Z": [928, 32],
|
||||
"&": [0, 64],
|
||||
"-": [32, 64],
|
||||
"+": [64, 64],
|
||||
"`": [96, 64],
|
||||
"0": [128, 64],
|
||||
"1": [160, 64],
|
||||
"2": [192, 64],
|
||||
"3": [224, 64],
|
||||
"4": [256, 64],
|
||||
"5": [288, 64],
|
||||
"6": [320, 64],
|
||||
"7": [352, 64],
|
||||
"8": [384, 64],
|
||||
"9": [416, 64],
|
||||
".": [448, 64],
|
||||
":": [480, 64],
|
||||
",": [512, 64],
|
||||
";": [544, 64],
|
||||
"(": [576, 64],
|
||||
"*": [608, 64],
|
||||
"!": [640, 64],
|
||||
"?": [672, 64],
|
||||
"}": [704, 64],
|
||||
"^": [736, 64],
|
||||
")": [768, 64],
|
||||
"[": [800, 64],
|
||||
"$": [832, 64], // remap to regular dollar sign
|
||||
"{": [864, 64],
|
||||
"%": [896, 64],
|
||||
"^": [928, 64],
|
||||
"\'": [960, 64], // New quotes mapping
|
||||
"\"": [992, 64],
|
||||
" ": [1024, 64]
|
||||
}
|
||||
};
|
||||
|
||||
// Make options globally available
|
||||
window.tileSet = tileSet;
|
||||
window.tileMap = options.tileMap;
|
||||
window.options = options;
|
||||
|
||||
// Helper functions to get configured tile types
|
||||
window.GameTiles = {
|
||||
getPlayerTile: function() {
|
||||
return options.gameConfig.playerType;
|
||||
},
|
||||
|
||||
getTreasureTile: function() {
|
||||
return options.gameConfig.treasureType;
|
||||
},
|
||||
|
||||
getMonsterTile: function() {
|
||||
return options.gameConfig.monsterType;
|
||||
},
|
||||
|
||||
getFloorTile: function(useVariation = false) {
|
||||
if (useVariation && Math.random() < options.gameConfig.floorVariation) {
|
||||
return options.gameConfig.secondaryFloor;
|
||||
}
|
||||
return options.gameConfig.primaryFloor;
|
||||
},
|
||||
|
||||
getTorchTile: function() {
|
||||
return options.gameConfig.torchTile;
|
||||
},
|
||||
|
||||
shouldPlaceTorch: function() {
|
||||
return options.gameConfig.torchEnabled && Math.random() < options.gameConfig.torchDensity;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user