mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-17 22:31:26 +02:00
refactor: remove dead move handler + stale test scratch files
4.2 (partial): Delete the dead handlePlayerMove socket handler and its orphaned playerMoveTimestamps map + cleanup. It was never registered (the live move path is MovementManager.handleMove) but, if ever wired, it double-stepped the monster and bypassed the escape/collision event handling — a loaded footgun. No behaviour change. 4.5: Delete 14 stale non-jest files from test/ (5 scratch .js scripts + 9 .html debug pages) — they contain no jest constructs and are referenced nowhere. test/ is now exclusively the 21 real jest suites. Full suite: 21 suites / 155 tests.
This commit is contained in:
@@ -43,11 +43,11 @@ Key: `S` ≤ half day · `M` ~1–3 days · `L` ~1 week+ · 🔴 launch-blocker
|
||||
- [x] **3.3 S — Multi-step monster collision fix.** ✅ Collision is now checked after **each** monster sub-step, not just after the loop. With `movesPerPlayerMove > 1` (casino's 1.5× speed) the monster could previously step onto the player's tile and off again between checks, phasing through and missing the catch (player-favorable, eroded the house edge). New `monsterCollision.test.js`.
|
||||
|
||||
## Phase 4 — De-spaghetti / structural cleanup (🟡)
|
||||
- [ ] **4.1 L — Break up `index.js`** into `app.js`, `routes/{public,user,admin,auth}.js`, `views/verify.js`, `server.js`.
|
||||
- [ ] **4.2 M — Kill duplicates.** Merge `rpcService`+`rpccalls`; delete dead `handlePlayerMove` (`socketHandlers.js:244`); unify `payout_address` widths; remove `QueryValidator` theater (`databaseManager.js:21`).
|
||||
- [x] **4.1 L — Break up `index.js` (substantially).** ✅ Extracted the verify HTML → `views/verifyPage.js` (now HTML-escaped), all 15 admin endpoints → `routes/admin.js`, and the 3 smirk endpoints → `routes/auth.js`, each as DI factories with smoke tests. **index.js: 2029 → 882 lines (-57%).** _Remaining user/public routes left inline — they're interleaved with bootstrap and have no route tests, so further slicing is diminishing-return/higher-risk; the structure + pattern are established for later._
|
||||
- [~] **4.2 M — Kill duplicates (partial).** ✅ Deleted the dead `handlePlayerMove` handler (a footgun that double-stepped the monster + bypassed escape/collision detection) and its orphaned `playerMoveTimestamps` map/cleanup — the live path is `MovementManager`. _Remaining (lower-value/higher-effort): merge `rpcService`+`rpccalls`, unify `payout_address` column widths, address the `QueryValidator` false-positive — deferred._
|
||||
- [ ] **4.3 M — Config as immutable snapshots;** stop mutating `process.env` (`gameModeManager.js:189`).
|
||||
- [ ] **4.4 M — Frontend de-spaghetti.** Shared `escapeHtml`/`dom.js`; kill double-declares + dead `_appendMessage`; explicit init ordering; upgrade/drop jQuery; gate broadcast logging (`broadcastManager.js:33`).
|
||||
- [ ] **4.5 S — Delete stale `test/` scratch files.**
|
||||
- [x] **4.5 S — Delete stale `test/` scratch files.** ✅ Removed 14 non-jest leftovers (5 `.js` scratch scripts + 9 `.html` debug pages) — confirmed they contained no jest constructs and were referenced nowhere. `test/` is now exclusively the 21 real jest suites.
|
||||
|
||||
## Phase 5 — Test & ops overhaul (🟠 — the launch gate)
|
||||
- [ ] **5.1 L — Real money-path tests** against Postgres (no stubbed `withTransaction`): under/overpay, double-confirm, batch dispatch, credit race, retry idempotency; property-test `money/atomic.js`.
|
||||
|
||||
@@ -187,8 +187,7 @@ class SocketHandlers {
|
||||
cleanupTimeoutMs: 300000 // 5 minutes to reconnect before game is lost
|
||||
});
|
||||
|
||||
// Movement manager abstraction with memory cleanup
|
||||
this.playerMoveTimestamps = new Map();
|
||||
// Movement manager abstraction (owns its own move-cooldown state)
|
||||
this.movementManager = new MovementManager({
|
||||
activeGames: this.activeGames,
|
||||
io: this.io,
|
||||
@@ -206,32 +205,6 @@ class SocketHandlers {
|
||||
* Register memory cleanup functions to prevent memory leaks
|
||||
*/
|
||||
_registerMemoryCleanups() {
|
||||
// Cleanup player move timestamps (keep for 5 minutes)
|
||||
this.memoryManager.registerCleanup(
|
||||
'playerMoveTimestamps',
|
||||
() => {
|
||||
const now = Date.now();
|
||||
const maxAge = 300000; // 5 minutes
|
||||
let cleaned = 0;
|
||||
const toDelete = [];
|
||||
|
||||
for (const [socketId, timestamp] of this.playerMoveTimestamps.entries()) {
|
||||
if (now - timestamp > maxAge) {
|
||||
toDelete.push(socketId);
|
||||
}
|
||||
}
|
||||
|
||||
for (const socketId of toDelete) {
|
||||
if (this.playerMoveTimestamps.delete(socketId)) {
|
||||
cleaned++;
|
||||
}
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
},
|
||||
300000
|
||||
);
|
||||
|
||||
// (mempool-notification dedup + TTL eviction lives in PaymentHandlers, which owns
|
||||
// the actual dedup set; the old SocketHandlers copy here was unused and is removed.)
|
||||
|
||||
@@ -245,69 +218,6 @@ class SocketHandlers {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle player movement input from client.
|
||||
* Expects moveData: { direction: 'up'|'down'|'left'|'right' }
|
||||
*/
|
||||
handlePlayerMove(socket, moveData) {
|
||||
try {
|
||||
if (!moveData || typeof moveData.direction !== 'string') return;
|
||||
const now = Date.now();
|
||||
const last = this.playerMoveTimestamps.get(socket.id) || 0;
|
||||
if (now - last < this.MOVE_COOLDOWN) return; // rate limit moves
|
||||
this.playerMoveTimestamps.set(socket.id, now);
|
||||
|
||||
const game = this.activeGames.get(socket.id);
|
||||
if (!game) return; // not in a game
|
||||
|
||||
// Translate direction to delta
|
||||
const dir = moveData.direction;
|
||||
let dx = 0, dy = 0;
|
||||
switch (dir) {
|
||||
case 'up': dy = -1; break;
|
||||
case 'down': dy = 1; break;
|
||||
case 'left': dx = -1; break;
|
||||
case 'right': dx = 1; break;
|
||||
default: return; // ignore unknown
|
||||
}
|
||||
|
||||
// Attempt move via game API (assuming game has movePlayer(dx,dy))
|
||||
if (typeof game.movePlayer === 'function') {
|
||||
game.movePlayer(dx, dy);
|
||||
} else if (game.player) {
|
||||
// Fallback direct mutation (legacy) with simple bounds check
|
||||
const newX = game.player.x + dx;
|
||||
const newY = game.player.y + dy;
|
||||
if (newX >= 0 && newY >= 0) {
|
||||
game.player.x = newX;
|
||||
game.player.y = newY;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof game.moveMonster === 'function') {
|
||||
game.moveMonster();
|
||||
}
|
||||
|
||||
// Build updated state
|
||||
let state;
|
||||
if (typeof game.getState === 'function') {
|
||||
state = game.getState();
|
||||
} else {
|
||||
state = { player: game.player };
|
||||
}
|
||||
|
||||
// Add block height context
|
||||
if (this.debugManager && typeof this.debugManager.getCurrentBlockHeight === 'function') {
|
||||
state.blockHeight = this.debugManager.getCurrentBlockHeight();
|
||||
}
|
||||
|
||||
this.io.to(socket.id).emit('game_update', state);
|
||||
|
||||
} catch (err) {
|
||||
console.error('handlePlayerMove error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a successful player move (before update is emitted) via MovementManager postMoveHook.
|
||||
* Handles monster chasing logic and immediate game over if monster catches player.
|
||||
@@ -816,9 +726,6 @@ class SocketHandlers {
|
||||
// addressToUser entries must persist so checkPaymentStatus() can query the wallet
|
||||
// RPC after session resumption. They get cleaned up when payments are confirmed or expired.
|
||||
|
||||
// Clean up movement timestamps
|
||||
this.playerMoveTimestamps.delete(socketId);
|
||||
|
||||
// Preserve queue entry for users with session/payment
|
||||
// The queue entry's socketId will be updated on reconnection
|
||||
if (this.queueManager && typeof this.queueManager.removePlayer === 'function') {
|
||||
@@ -1211,7 +1118,6 @@ class SocketHandlers {
|
||||
|
||||
// Clear remaining data structures
|
||||
this.activeGames.clear();
|
||||
this.playerMoveTimestamps.clear();
|
||||
|
||||
if (this.debugManager.CONSOLE_LOGGING) {
|
||||
console.log('✅ SocketHandlers shutdown complete');
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Debug Client Lighting</title>
|
||||
<style>
|
||||
body { font-family: monospace; background: black; color: green; padding: 20px; }
|
||||
.debug-box { border: 1px solid green; margin: 10px; padding: 10px; }
|
||||
.lighting-tile { display: inline-block; width: 20px; height: 20px; margin: 1px; text-align: center; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Client-Side Lighting Debug</h1>
|
||||
|
||||
<div class="debug-box">
|
||||
<h3>Game State Lighting Data</h3>
|
||||
<div id="lighting-status">No data received yet...</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-box">
|
||||
<h3>Lighting Visualization</h3>
|
||||
<div id="lighting-grid"></div>
|
||||
</div>
|
||||
|
||||
<div class="debug-box">
|
||||
<h3>Console Log</h3>
|
||||
<div id="console-log" style="height: 300px; overflow-y: scroll; background: #111; padding: 10px;"></div>
|
||||
</div>
|
||||
|
||||
<script src="html/js/jquery-3.4.1.min.js"></script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
const socket = io();
|
||||
|
||||
// Capture console logs
|
||||
const originalLog = console.log;
|
||||
console.log = function(...args) {
|
||||
originalLog.apply(console, args);
|
||||
const logDiv = document.getElementById('console-log');
|
||||
const logEntry = document.createElement('div');
|
||||
logEntry.textContent = new Date().toLocaleTimeString() + ': ' + args.join(' ');
|
||||
logDiv.appendChild(logEntry);
|
||||
logDiv.scrollTop = logDiv.scrollHeight;
|
||||
};
|
||||
|
||||
console.log("🔍 Debug client started");
|
||||
|
||||
socket.on('connect', function() {
|
||||
console.log('Connected with socket ID:', socket.id);
|
||||
socket.emit('register_client', {
|
||||
clientId: socket.id,
|
||||
userAgent: navigator.userAgent
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('game_start', function(data) {
|
||||
console.log('🎮 Game start received:', data);
|
||||
|
||||
if (data.lighting) {
|
||||
console.log('🔥 Initial lighting data received with', Object.keys(data.lighting).length, 'rows');
|
||||
updateLightingDisplay(data.lighting);
|
||||
}
|
||||
|
||||
if (data.torches) {
|
||||
console.log('🔦 Initial torch data received with', data.torches.length, 'torches');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('game_update', function(data) {
|
||||
console.log('🎲 Game update received');
|
||||
|
||||
if (data.lighting) {
|
||||
console.log('🔥 Lighting data in update with', Object.keys(data.lighting).length, 'rows');
|
||||
updateLightingDisplay(data.lighting);
|
||||
|
||||
// Show detailed lighting values for debugging
|
||||
let sampleCount = 0;
|
||||
for (const y in data.lighting) {
|
||||
for (const x in data.lighting[y]) {
|
||||
if (sampleCount < 5) {
|
||||
console.log(` Sample lighting[${y}][${x}] = ${data.lighting[y][x]}`);
|
||||
sampleCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('⚠️ No lighting data in game update');
|
||||
}
|
||||
|
||||
if (data.torches) {
|
||||
console.log('🔦 Torch data in update with', data.torches.length, 'torches');
|
||||
}
|
||||
});
|
||||
|
||||
function updateLightingDisplay(lightingData) {
|
||||
const statusDiv = document.getElementById('lighting-status');
|
||||
const gridDiv = document.getElementById('lighting-grid');
|
||||
|
||||
const rowCount = Object.keys(lightingData).length;
|
||||
let tileCount = 0;
|
||||
|
||||
for (const y in lightingData) {
|
||||
tileCount += Object.keys(lightingData[y]).length;
|
||||
}
|
||||
|
||||
statusDiv.innerHTML = `
|
||||
<strong>Lighting Data Received:</strong><br>
|
||||
Rows: ${rowCount}<br>
|
||||
Total tiles: ${tileCount}<br>
|
||||
Sample data: ${JSON.stringify(Object.keys(lightingData).slice(0, 3).map(y => `y=${y}: ${Object.keys(lightingData[y]).length} tiles`))}<br>
|
||||
Last updated: ${new Date().toLocaleTimeString()}
|
||||
`;
|
||||
|
||||
// Create a small visualization
|
||||
gridDiv.innerHTML = '';
|
||||
|
||||
// Get min/max coordinates for visualization
|
||||
let minY = Infinity, maxY = -Infinity, minX = Infinity, maxX = -Infinity;
|
||||
for (const y in lightingData) {
|
||||
const yNum = parseInt(y);
|
||||
minY = Math.min(minY, yNum);
|
||||
maxY = Math.max(maxY, yNum);
|
||||
for (const x in lightingData[y]) {
|
||||
const xNum = parseInt(x);
|
||||
minX = Math.min(minX, xNum);
|
||||
maxX = Math.max(maxX, xNum);
|
||||
}
|
||||
}
|
||||
|
||||
// Create grid (limit size for display)
|
||||
const displayMaxSize = 20;
|
||||
const yRange = Math.min(maxY - minY + 1, displayMaxSize);
|
||||
const xRange = Math.min(maxX - minX + 1, displayMaxSize);
|
||||
|
||||
for (let y = minY; y < minY + yRange; y++) {
|
||||
const rowDiv = document.createElement('div');
|
||||
for (let x = minX; x < minX + xRange; x++) {
|
||||
const tileDiv = document.createElement('div');
|
||||
tileDiv.className = 'lighting-tile';
|
||||
|
||||
if (lightingData[y] && lightingData[y][x] !== undefined) {
|
||||
const alpha = lightingData[y][x];
|
||||
tileDiv.style.backgroundColor = `rgba(255, 255, 0, ${1 - alpha})`;
|
||||
tileDiv.textContent = alpha.toFixed(2);
|
||||
tileDiv.title = `(${x},${y}) = ${alpha}`;
|
||||
} else {
|
||||
tileDiv.style.backgroundColor = 'gray';
|
||||
tileDiv.textContent = '?';
|
||||
}
|
||||
|
||||
rowDiv.appendChild(tileDiv);
|
||||
}
|
||||
gridDiv.appendChild(rowDiv);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-start game for testing
|
||||
setTimeout(() => {
|
||||
console.log('🎮 Auto-starting game...');
|
||||
socket.emit('message', 'enter');
|
||||
}, 1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,186 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Game Update Debugging</title>
|
||||
<style>
|
||||
body { font-family: monospace; padding: 20px; background: #222; color: #0f0; }
|
||||
.log { margin: 5px 0; padding: 5px; background: #333; border-left: 3px solid #0f0; }
|
||||
.error { border-left-color: #f00; color: #f99; }
|
||||
.warning { border-left-color: #fa0; color: #ffa; }
|
||||
.success { border-left-color: #0a0; color: #afa; }
|
||||
#controls { margin-bottom: 20px; }
|
||||
button { padding: 10px; margin: 5px; background: #444; color: #0f0; border: 1px solid #0f0; cursor: pointer; }
|
||||
button:hover { background: #0f0; color: #000; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Game Update Debugging</h1>
|
||||
|
||||
<div id="controls">
|
||||
<button onclick="connectToServer()">Connect to Server</button>
|
||||
<button onclick="startGame()">Start Game</button>
|
||||
<button onclick="clearLogs()">Clear Logs</button>
|
||||
<button onclick="testMove()">Test Move</button>
|
||||
</div>
|
||||
|
||||
<div id="status">Not connected</div>
|
||||
<div id="logs"></div>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
let socket;
|
||||
let gameActive = false;
|
||||
|
||||
function log(message, type = 'log') {
|
||||
const logDiv = document.createElement('div');
|
||||
logDiv.className = `log ${type}`;
|
||||
logDiv.innerHTML = `[${new Date().toLocaleTimeString()}] ${message}`;
|
||||
document.getElementById('logs').appendChild(logDiv);
|
||||
console.log(message);
|
||||
|
||||
// Auto scroll to bottom
|
||||
logDiv.scrollIntoView();
|
||||
}
|
||||
|
||||
function clearLogs() {
|
||||
document.getElementById('logs').innerHTML = '';
|
||||
}
|
||||
|
||||
function connectToServer() {
|
||||
if (socket) {
|
||||
socket.disconnect();
|
||||
}
|
||||
|
||||
socket = io();
|
||||
|
||||
socket.on('connect', function() {
|
||||
log('✅ Connected to server', 'success');
|
||||
document.getElementById('status').innerHTML = '✅ Connected';
|
||||
|
||||
// Register client
|
||||
socket.emit('register_client', {
|
||||
clientId: socket.id,
|
||||
userAgent: navigator.userAgent
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
log('❌ Disconnected from server', 'error');
|
||||
document.getElementById('status').innerHTML = '❌ Disconnected';
|
||||
gameActive = false;
|
||||
});
|
||||
|
||||
socket.on('game_start', function(data) {
|
||||
log('🎮 GAME_START received', 'success');
|
||||
log(`📦 Game start data keys: ${Object.keys(data).join(', ')}`, 'log');
|
||||
|
||||
if (data.lighting) {
|
||||
const lightingTileCount = Object.keys(data.lighting).reduce((acc, yKey) =>
|
||||
acc + Object.keys(data.lighting[yKey] || {}).length, 0);
|
||||
log(`🔥 GAME_START lighting: ${Object.keys(data.lighting).length} rows, ${lightingTileCount} tiles`, 'success');
|
||||
} else {
|
||||
log('⚠️ GAME_START: NO lighting data', 'warning');
|
||||
}
|
||||
|
||||
if (data.torches) {
|
||||
log(`🔦 GAME_START torches: ${data.torches.length} torches`, 'success');
|
||||
} else {
|
||||
log('⚠️ GAME_START: NO torch data', 'warning');
|
||||
}
|
||||
|
||||
gameActive = true;
|
||||
document.getElementById('status').innerHTML = '🎮 Game Active';
|
||||
});
|
||||
|
||||
socket.on('game_update', function(data) {
|
||||
log('🔄 GAME_UPDATE received', 'log');
|
||||
log(`📦 Game update data keys: ${Object.keys(data).join(', ')}`, 'log');
|
||||
|
||||
if (data.lighting) {
|
||||
const lightingTileCount = Object.keys(data.lighting).reduce((acc, yKey) =>
|
||||
acc + Object.keys(data.lighting[yKey] || {}).length, 0);
|
||||
log(`🔥 GAME_UPDATE lighting: ${Object.keys(data.lighting).length} rows, ${lightingTileCount} tiles`, 'success');
|
||||
|
||||
// Sample a few lighting values
|
||||
let samples = [];
|
||||
let count = 0;
|
||||
for (const y in data.lighting) {
|
||||
for (const x in data.lighting[y]) {
|
||||
if (count < 3) {
|
||||
samples.push(`(${x},${y}): ${data.lighting[y][x].toFixed(3)}`);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (samples.length > 0) {
|
||||
log(`💡 Sample lighting values: ${samples.join(', ')}`, 'log');
|
||||
}
|
||||
} else {
|
||||
log('⚠️ GAME_UPDATE: NO lighting data', 'warning');
|
||||
}
|
||||
|
||||
if (data.torches) {
|
||||
log(`🔦 GAME_UPDATE torches: ${data.torches.length} torches`, 'success');
|
||||
} else {
|
||||
log('⚠️ GAME_UPDATE: NO torch data', 'warning');
|
||||
}
|
||||
|
||||
if (data.player) {
|
||||
log(`👤 Player position: (${data.player.x}, ${data.player.y})`, 'log');
|
||||
}
|
||||
|
||||
if (data.visibleTiles) {
|
||||
const visibleTileCount = Object.keys(data.visibleTiles).reduce((acc, yKey) =>
|
||||
acc + Object.keys(data.visibleTiles[yKey] || {}).length, 0);
|
||||
log(`👁️ Visible tiles: ${Object.keys(data.visibleTiles).length} rows, ${visibleTileCount} tiles`, 'log');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('game_over', function(data) {
|
||||
log('🏁 GAME_OVER received', 'warning');
|
||||
gameActive = false;
|
||||
document.getElementById('status').innerHTML = '🏁 Game Over';
|
||||
});
|
||||
|
||||
socket.on('message', function(msg) {
|
||||
log(`💬 Server message: ${msg}`, 'log');
|
||||
});
|
||||
}
|
||||
|
||||
function startGame() {
|
||||
if (!socket) {
|
||||
log('❌ Not connected to server', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
log('🚀 Requesting game start...', 'log');
|
||||
socket.emit('chat message', 'enter');
|
||||
}
|
||||
|
||||
function testMove() {
|
||||
if (!socket || !gameActive) {
|
||||
log('❌ Game not active', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a random move
|
||||
const moves = [
|
||||
{ dx: 0, dy: 1 },
|
||||
{ dx: 0, dy: -1 },
|
||||
{ dx: 1, dy: 0 },
|
||||
{ dx: -1, dy: 0 }
|
||||
];
|
||||
const move = moves[Math.floor(Math.random() * moves.length)];
|
||||
|
||||
log(`🎮 Sending move: dx=${move.dx}, dy=${move.dy}`, 'log');
|
||||
socket.emit('player_move', move);
|
||||
}
|
||||
|
||||
// Auto-connect on page load
|
||||
window.addEventListener('load', function() {
|
||||
log('🔌 Page loaded, auto-connecting...', 'log');
|
||||
connectToServer();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,174 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Lighting Debug - Wowngeon</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
}
|
||||
.debug-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
border: 1px solid #444;
|
||||
background: #333;
|
||||
}
|
||||
.alpha-test {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 5px;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
#game-container {
|
||||
margin: 20px 0;
|
||||
border: 2px solid #666;
|
||||
}
|
||||
.log-output {
|
||||
background: #111;
|
||||
padding: 10px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Lighting System Debug</h1>
|
||||
|
||||
<div class="debug-section">
|
||||
<h2>Alpha Value Tests</h2>
|
||||
<p>Testing different alpha values to see how they render:</p>
|
||||
<div id="alpha-tests"></div>
|
||||
</div>
|
||||
|
||||
<div class="debug-section">
|
||||
<h2>Game Display</h2>
|
||||
<div id="game-container"></div>
|
||||
<button onclick="startNewGame()">Start New Game</button>
|
||||
<button onclick="captureCurrentLighting()">Capture Lighting Data</button>
|
||||
</div>
|
||||
|
||||
<div class="debug-section">
|
||||
<h2>Live Lighting Data</h2>
|
||||
<div id="lighting-data">No data captured yet</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-section">
|
||||
<h2>Console Log</h2>
|
||||
<div id="log-output" class="log-output"></div>
|
||||
</div>
|
||||
|
||||
<!-- Include game files -->
|
||||
<script src="html/js/jquery-3.4.1.min.js"></script>
|
||||
<script src="html/js/rot.min.js"></script>
|
||||
<script src="html/js/options.js"></script>
|
||||
<script src="html/js/displayManager.js"></script>
|
||||
<script src="html/js/screenManager.js"></script>
|
||||
<script src="html/js/gameState.js"></script>
|
||||
<script src="html/js/renderEngine.js"></script>
|
||||
<script src="html/js/game.js"></script>
|
||||
<script src="html/js/socketHandlers.js"></script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
|
||||
<script>
|
||||
// Override console.log to capture output
|
||||
const originalLog = console.log;
|
||||
console.log = function(...args) {
|
||||
originalLog.apply(console, args);
|
||||
const logOutput = document.getElementById('log-output');
|
||||
if (logOutput) {
|
||||
logOutput.innerHTML += args.join(' ') + '<br>';
|
||||
logOutput.scrollTop = logOutput.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
// Test different alpha values
|
||||
function createAlphaTests() {
|
||||
const container = document.getElementById('alpha-tests');
|
||||
const alphas = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
|
||||
|
||||
alphas.forEach(alpha => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'alpha-test';
|
||||
div.style.background = 'white';
|
||||
div.style.color = `rgba(0, 0, 0, ${alpha})`;
|
||||
div.textContent = alpha.toFixed(1);
|
||||
div.title = `Alpha: ${alpha}`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
// Start a new game and capture data
|
||||
function startNewGame() {
|
||||
console.log('🎮 Starting new game for debug...');
|
||||
if (window.Game) {
|
||||
Game.init();
|
||||
}
|
||||
}
|
||||
|
||||
// Capture current lighting data from game state
|
||||
function captureCurrentLighting() {
|
||||
console.log('🔍 Capturing current lighting data...');
|
||||
|
||||
if (window.GameState) {
|
||||
const gameState = GameState.getGameStateForRender();
|
||||
const lighting = gameState.lighting || {};
|
||||
const torches = gameState.torches || [];
|
||||
|
||||
let html = '<h3>Current Lighting State</h3>';
|
||||
html += `<p><strong>Torches:</strong> ${torches.length} positions</p>`;
|
||||
|
||||
const lightingTileCount = Object.keys(lighting).reduce((acc, yKey) =>
|
||||
acc + Object.keys(lighting[yKey] || {}).length, 0);
|
||||
html += `<p><strong>Lighting Tiles:</strong> ${lightingTileCount} tiles</p>`;
|
||||
|
||||
if (lightingTileCount > 0) {
|
||||
html += '<h4>Sample Lighting Values:</h4><ul>';
|
||||
let samples = 0;
|
||||
for (const y in lighting) {
|
||||
for (const x in lighting[y]) {
|
||||
if (samples >= 10) break;
|
||||
const alpha = lighting[y][x];
|
||||
html += `<li>(${x},${y}): ${alpha.toFixed(3)}</li>`;
|
||||
samples++;
|
||||
}
|
||||
if (samples >= 10) break;
|
||||
}
|
||||
html += '</ul>';
|
||||
}
|
||||
|
||||
document.getElementById('lighting-data').innerHTML = html;
|
||||
} else {
|
||||
document.getElementById('lighting-data').innerHTML = '<p>GameState not available</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
createAlphaTests();
|
||||
|
||||
// Initialize display manager
|
||||
if (window.DisplayManager) {
|
||||
DisplayManager.init('game-container');
|
||||
}
|
||||
|
||||
console.log('🔧 Debug page initialized');
|
||||
});
|
||||
|
||||
// Monitor game state changes
|
||||
setInterval(() => {
|
||||
if (window.GameState && window.GameState._lighting) {
|
||||
const lightingKeys = Object.keys(window.GameState._lighting);
|
||||
if (lightingKeys.length > 0) {
|
||||
console.log(`💡 Current lighting state: ${lightingKeys.length} Y-coordinates with data`);
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,92 +0,0 @@
|
||||
const ROT = require('./rot.js');
|
||||
|
||||
/**
|
||||
* Dungeon generation utilities
|
||||
*/
|
||||
class DungeonGenerator {
|
||||
static generate(width, height) {
|
||||
// Create dungeon using ROT.js Map.Digger
|
||||
const digger = new ROT.Map.Digger(width, height, {
|
||||
roomWidth: [3, 9],
|
||||
roomHeight: [3, 7],
|
||||
corridorLength: [2, 6],
|
||||
dugPercentage: 0.2
|
||||
});
|
||||
|
||||
// Initialize the empty map with walls (1)
|
||||
const map = Array(height).fill().map(() => Array(width).fill(1));
|
||||
|
||||
// Fill in floors (0) using digger's callback
|
||||
digger.create((x, y, value) => {
|
||||
// value: 0 = floor, 1 = wall
|
||||
map[y][x] = value;
|
||||
});
|
||||
|
||||
// Get rooms that were created
|
||||
const rooms = digger.getRooms();
|
||||
|
||||
// Place entrance in the first room
|
||||
const entranceRoom = rooms[0];
|
||||
const entranceCenter = entranceRoom.getCenter();
|
||||
const entrance = [entranceCenter[0], entranceCenter[1]];
|
||||
|
||||
// Place exit in the last room
|
||||
const exitRoom = rooms[rooms.length - 1];
|
||||
const exitCenter = exitRoom.getCenter();
|
||||
const exit = [exitCenter[0], exitCenter[1]];
|
||||
|
||||
// Place treasure in a middle room (not first or last)
|
||||
let treasureRoom;
|
||||
if (rooms.length > 2) {
|
||||
treasureRoom = rooms[Math.floor(rooms.length / 2)];
|
||||
} else {
|
||||
// Fallback if we have fewer than 3 rooms
|
||||
treasureRoom = rooms[0];
|
||||
}
|
||||
const treasureCenter = treasureRoom.getCenter();
|
||||
// Place treasure slightly off center to make it more interesting
|
||||
const treasure = [
|
||||
treasureCenter[0] + Math.floor(Math.random() * 3) - 1,
|
||||
treasureCenter[1] + Math.floor(Math.random() * 3) - 1
|
||||
];
|
||||
|
||||
// Ensure the treasure is on a floor tile
|
||||
if (map[treasure[1]][treasure[0]] !== 0) {
|
||||
treasure[0] = treasureCenter[0];
|
||||
treasure[1] = treasureCenter[1];
|
||||
}
|
||||
|
||||
return {
|
||||
map: map,
|
||||
rooms: rooms,
|
||||
entrance: entrance,
|
||||
exit: exit,
|
||||
treasure: treasure
|
||||
};
|
||||
}
|
||||
|
||||
static getRandomRoomCenter(rooms, excludeFirst = false, excludeLast = false) {
|
||||
if (!rooms || rooms.length === 0) return null;
|
||||
|
||||
let availableRooms = [...rooms];
|
||||
if (excludeFirst && availableRooms.length > 1) {
|
||||
availableRooms = availableRooms.slice(1);
|
||||
}
|
||||
if (excludeLast && availableRooms.length > 1) {
|
||||
availableRooms = availableRooms.slice(0, -1);
|
||||
}
|
||||
|
||||
const randomRoom = availableRooms[Math.floor(Math.random() * availableRooms.length)];
|
||||
return randomRoom.getCenter();
|
||||
}
|
||||
|
||||
static isFloorTile(map, x, y) {
|
||||
return map[y] && map[y][x] === 0;
|
||||
}
|
||||
|
||||
static isWallTile(map, x, y) {
|
||||
return map[y] && map[y][x] === 1;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DungeonGenerator;
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
const ROT = require('./rot.js');
|
||||
const Player = require('./player.js');
|
||||
const Monster = require('./monster.js');
|
||||
const DungeonGenerator = require('./dungeon.js');
|
||||
|
||||
class Game {
|
||||
constructor(socketId, mapWidth, mapHeight) {
|
||||
this.socketId = socketId;
|
||||
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 = new Player();
|
||||
this.monster = new Monster();
|
||||
this.fee = 0; // Amount player paid
|
||||
this.visibleTiles = {}; // Will store visible tiles
|
||||
this.fov = null; // FOV calculator
|
||||
this.generateDungeon();
|
||||
}
|
||||
|
||||
generateDungeon() {
|
||||
this.dungeon = DungeonGenerator.generate(this.width, this.height);
|
||||
|
||||
if (this.dungeon.entrance) {
|
||||
this.player.moveTo(this.dungeon.entrance[0], this.dungeon.entrance[1]);
|
||||
}
|
||||
|
||||
// Place monster far from entrance
|
||||
if (this.dungeon.rooms && this.dungeon.rooms.length > 2) {
|
||||
const monsterRoom = this.dungeon.rooms[Math.floor(this.dungeon.rooms.length * 0.7)];
|
||||
const center = monsterRoom.getCenter();
|
||||
this.monster.moveTo(center[0], 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, 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;
|
||||
|
||||
// Debug logging for movement validation
|
||||
console.log(`[DEBUG] Move attempt: from (${this.player.x},${this.player.y}) to (${newX},${newY}) with dx=${dx},dy=${dy}`);
|
||||
console.log(`[DEBUG] Map bounds: width=${this.width}, height=${this.height}`);
|
||||
console.log(`[DEBUG] Dungeon exists: ${!!this.dungeon}`);
|
||||
if (this.dungeon && this.dungeon.map) {
|
||||
console.log(`[DEBUG] Map row ${newY} exists: ${!!this.dungeon.map[newY]}`);
|
||||
if (this.dungeon.map[newY]) {
|
||||
console.log(`[DEBUG] Map cell [${newY}][${newX}] value: ${this.dungeon.map[newY][newX]} (0=floor, 1=wall)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.moveTo(newX, newY);
|
||||
console.log(`Player moved to ${newX},${newY} in game for socket ${this.socketId}`);
|
||||
|
||||
// Update FOV after moving
|
||||
this.updateFOV();
|
||||
|
||||
// Check for game events like finding treasure or exit
|
||||
if (this.dungeon.exit && this.player.isAt(this.dungeon.exit[0], this.dungeon.exit[1])) {
|
||||
// Handle win condition (e.g., escaped)
|
||||
this.gameState = 'won';
|
||||
return { status: 'moved', event: 'escaped', player: this.player.getState(), visibleTiles: this.visibleTiles };
|
||||
}
|
||||
if (this.dungeon.treasure && this.player.isAt(this.dungeon.treasure[0], this.dungeon.treasure[1]) && !this.player.hasTreasure) {
|
||||
this.player.hasTreasure = true;
|
||||
// Remove treasure from dungeon so it won't be sent in future updates
|
||||
this.dungeon.treasure = null;
|
||||
console.log(`Player ${this.socketId} collected treasure! hasTreasure: ${this.player.hasTreasure}`);
|
||||
return { status: 'moved', event: 'treasure_found', player: this.player.getState(), visibleTiles: this.visibleTiles };
|
||||
}
|
||||
|
||||
return { status: 'moved', player: this.player.getState(), visibleTiles: this.visibleTiles };
|
||||
}
|
||||
|
||||
|
||||
console.log(`Player move to ${newX},${newY} for socket ${this.socketId} is invalid (wall or out of bounds).`);
|
||||
|
||||
// Debug: Show map around player
|
||||
console.log(`[DEBUG] Map around player position:`);
|
||||
for (let y = this.player.y - 2; y <= this.player.y + 2; y++) {
|
||||
let row = '';
|
||||
for (let x = this.player.x - 2; x <= this.player.x + 2; x++) {
|
||||
if (x === this.player.x && y === this.player.y) {
|
||||
row += '@'; // Player position
|
||||
} else if (x === newX && y === newY) {
|
||||
row += 'X'; // Attempted move position
|
||||
} else if (this.dungeon && this.dungeon.map[y] && this.dungeon.map[y][x] !== undefined) {
|
||||
row += this.dungeon.map[y][x] === 0 ? '.' : '#';
|
||||
} else {
|
||||
row += '?'; // Out of bounds or undefined
|
||||
}
|
||||
}
|
||||
console.log(`[DEBUG] Y=${y}: ${row}`);
|
||||
}
|
||||
|
||||
return { status: 'invalid' };
|
||||
}
|
||||
|
||||
moveMonster() {
|
||||
this.monster.moveTowardPlayer(this.player, this.dungeon);
|
||||
}
|
||||
|
||||
// Update the getState() method to include all entities with relative positions
|
||||
getState() {
|
||||
const state = {
|
||||
gameState: this.gameState,
|
||||
player: this.player.getState(), // Use Player's getState method
|
||||
monster: this.monster ? this.monster.getState() : null, // Use Monster's getState method
|
||||
visibleTiles: { ...this.visibleTiles }, // Send a copy of visible tiles
|
||||
entrance: this.dungeon ? this.dungeon.entrance : null,
|
||||
exit: this.dungeon ? this.dungeon.exit : null,
|
||||
treasure: this.dungeon ? this.dungeon.treasure : null,
|
||||
};
|
||||
|
||||
// Debug logging to verify entity data
|
||||
console.log("🎮 getState() sending entities - Monster:", state.monster, "Entrance:", state.entrance, "Exit:", state.exit, "Treasure:", state.treasure);
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if monster killed player
|
||||
function checkMonsterKill(player, monster) {
|
||||
// Use Monster's hasCaughtPlayer method
|
||||
const monsterInstance = new Monster(monster.x, monster.y);
|
||||
return monsterInstance.hasCaughtPlayer(player);
|
||||
}
|
||||
|
||||
module.exports = Game;
|
||||
module.exports.checkMonsterKill = checkMonsterKill;
|
||||
@@ -1,86 +0,0 @@
|
||||
const ROT = require('./rot.js');
|
||||
|
||||
/**
|
||||
* Monster class - Manages monster state and AI
|
||||
*/
|
||||
class Monster {
|
||||
constructor(x = 0, y = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
// Move monster to new position
|
||||
moveTo(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
// Check if monster is at given coordinates
|
||||
isAt(x, y) {
|
||||
return this.x === x && this.y === y;
|
||||
}
|
||||
|
||||
// Simple AI to move toward player
|
||||
moveTowardPlayer(player, dungeon) {
|
||||
if (!player || !dungeon || !dungeon.map) return;
|
||||
|
||||
const dx = Math.sign(player.x - this.x);
|
||||
const dy = Math.sign(player.y - this.y);
|
||||
|
||||
// Try horizontal move first
|
||||
if (dx !== 0) {
|
||||
const newX = this.x + dx;
|
||||
if (dungeon.map[this.y] && dungeon.map[this.y][newX] === 0) {
|
||||
this.x = newX;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try vertical move
|
||||
if (dy !== 0) {
|
||||
const newY = this.y + dy;
|
||||
if (dungeon.map[newY] && dungeon.map[newY][this.x] === 0) {
|
||||
this.y = newY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try random direction if direct path is blocked
|
||||
const dirs = [[0,1],[1,0],[0,-1],[-1,0]];
|
||||
const shuffledDirs = ROT.RNG.shuffle(dirs.slice());
|
||||
|
||||
for (const [dx, dy] of shuffledDirs) {
|
||||
const nx = this.x + dx;
|
||||
const ny = this.y + dy;
|
||||
if (dungeon.map[ny] && dungeon.map[ny][nx] === 0) {
|
||||
this.x = nx;
|
||||
this.y = ny;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if monster has caught the player
|
||||
hasCaughtPlayer(player) {
|
||||
if (!player) return false;
|
||||
|
||||
// Check for direct overlap - monster on same tile as player
|
||||
return this.x === player.x && this.y === player.y;
|
||||
}
|
||||
|
||||
// Get monster state as object
|
||||
getState() {
|
||||
return {
|
||||
x: this.x,
|
||||
y: this.y
|
||||
};
|
||||
}
|
||||
|
||||
// Set monster state from object
|
||||
setState(state) {
|
||||
if (state.x !== undefined) this.x = state.x;
|
||||
if (state.y !== undefined) this.y = state.y;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Monster;
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Player class - Manages player state and actions
|
||||
*/
|
||||
class Player {
|
||||
constructor(x = 0, y = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.hasKey = false;
|
||||
this.hasTreasure = false;
|
||||
}
|
||||
|
||||
// Move player to new position
|
||||
moveTo(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
// Check if player is at given coordinates
|
||||
isAt(x, y) {
|
||||
return this.x === x && this.y === y;
|
||||
}
|
||||
|
||||
// Reset player state
|
||||
reset() {
|
||||
this.hasKey = false;
|
||||
this.hasTreasure = false;
|
||||
}
|
||||
|
||||
// Get player state as object
|
||||
getState() {
|
||||
return {
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
hasKey: this.hasKey,
|
||||
hasTreasure: this.hasTreasure
|
||||
};
|
||||
}
|
||||
|
||||
// Set player state from object
|
||||
setState(state) {
|
||||
if (state.x !== undefined) this.x = state.x;
|
||||
if (state.y !== undefined) this.y = state.y;
|
||||
if (state.hasKey !== undefined) this.hasKey = state.hasKey;
|
||||
if (state.hasTreasure !== undefined) this.hasTreasure = state.hasTreasure;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Player;
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Test script for XMR/WOW address detection system
|
||||
*/
|
||||
|
||||
// Test the regex patterns used in socketHandlers.js
|
||||
function testAddressDetection() {
|
||||
console.log('🧪 Testing XMR/WOW Address Detection System\n');
|
||||
|
||||
// XMR mainnet addresses start with 4, 8, A, or B and are 95 characters
|
||||
// WOW addresses start with W and are 97 characters
|
||||
const xmrRegex = /\b[48AB][1-9A-HJ-NP-Za-km-z]{94}\b/;
|
||||
const wowRegex = /\bW[1-9A-HJ-NP-Za-km-z]{96}\b/;
|
||||
|
||||
function detectXMRAddress(message) {
|
||||
const xmrMatch = message.match(xmrRegex);
|
||||
const wowMatch = message.match(wowRegex);
|
||||
|
||||
if (xmrMatch) {
|
||||
return { address: xmrMatch[0], type: 'XMR' };
|
||||
}
|
||||
if (wowMatch) {
|
||||
return { address: wowMatch[0], type: 'WOW' };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Test cases with real address formats
|
||||
const testCases = [
|
||||
{
|
||||
name: 'Valid XMR Address (starts with 8)',
|
||||
message: 'Here is my XMR address: 8Bqez9dvqY9bw3CgwMC9X1c8MX5KuRgKbiXRWg2F9UtYNYLevqjJXbQW4qYHUM1JF1792hhWYJLUufSUn9GvSC4G6wjMiRL',
|
||||
expected: { address: '8Bqez9dvqY9bw3CgwMC9X1c8MX5KuRgKbiXRWg2F9UtYNYLevqjJXbQW4qYHUM1JF1792hhWYJLUufSUn9GvSC4G6wjMiRL', type: 'XMR' }
|
||||
},
|
||||
{
|
||||
name: 'Valid XMR Address (starts with 4)',
|
||||
message: 'Send to: 44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',
|
||||
expected: { address: '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A', type: 'XMR' }
|
||||
},
|
||||
{
|
||||
name: 'Valid WOW Address (starts with WW)',
|
||||
message: 'My WOW address is: WW35SBkcZDNGRkLroGNg4tKuJtyV8JhA95CREopLPCwrj9LgRU5pSnDb4D5BHKo9oBAZSjHXWnDwCJ4mRL5F6iJ81vSmNBuRC',
|
||||
expected: { address: 'WW35SBkcZDNGRkLroGNg4tKuJtyV8JhA95CREopLPCwrj9LgRU5pSnDb4D5BHKo9oBAZSjHXWnDwCJ4mRL5F6iJ81vSmNBuRC', type: 'WOW' }
|
||||
},
|
||||
{
|
||||
name: 'Valid WOW Address (starts with Wo)',
|
||||
message: 'please send to Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP thanks',
|
||||
expected: { address: 'Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP', type: 'WOW' }
|
||||
},
|
||||
{
|
||||
name: 'Invalid XMR (too short)',
|
||||
message: '4AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgYeYTRJ5zA4NJD9QhKCciJBcH9qPq8FgZKR8U8n',
|
||||
expected: null
|
||||
},
|
||||
{
|
||||
name: 'Invalid XMR (wrong prefix)',
|
||||
message: '3AdUndXHHZ6cfufTMvppY6JwXNouMBzSkbLYfpAV5Usx3skxNgYeYTRJ5zA4NJD9QhKCciJBcH9qPq8FgZKR8U8nZZZZ',
|
||||
expected: null
|
||||
},
|
||||
{
|
||||
name: 'Invalid WOW (wrong prefix)',
|
||||
message: 'XW35SBkcZDNGRkLroGNg4tKuJtyV8JhA95CREopLPCwrj9LgRU5pSnDb4D5BHKo9oBAZSjHXWnDwCJ4mRL5F6iJ81vSmNBuRC',
|
||||
expected: null
|
||||
},
|
||||
{
|
||||
name: 'No address in message',
|
||||
message: 'hello world this is just a chat message',
|
||||
expected: null
|
||||
},
|
||||
{
|
||||
name: 'Partial address in message',
|
||||
message: 'my address starts with 4AdUnd but I wont share the full thing',
|
||||
expected: null
|
||||
}
|
||||
];
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
testCases.forEach((testCase, index) => {
|
||||
console.log(`Test ${index + 1}: ${testCase.name}`);
|
||||
console.log(`Message: "${testCase.message}"`);
|
||||
|
||||
const result = detectXMRAddress(testCase.message);
|
||||
|
||||
if (JSON.stringify(result) === JSON.stringify(testCase.expected)) {
|
||||
console.log(`✅ PASS - Detected: ${result ? `${result.type} ${result.address}` : 'null'}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(`❌ FAIL - Expected: ${JSON.stringify(testCase.expected)}`);
|
||||
console.log(`❌ FAIL - Got: ${JSON.stringify(result)}`);
|
||||
failed++;
|
||||
}
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log(`📊 Test Results: ${passed} passed, ${failed} failed`);
|
||||
|
||||
if (failed === 0) {
|
||||
console.log('🎉 All tests passed! Address detection system is working correctly.');
|
||||
} else {
|
||||
console.log('⚠️ Some tests failed. Please review the regex patterns.');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the tests
|
||||
testAddressDetection();
|
||||
|
||||
module.exports = { testAddressDetection };
|
||||
@@ -1,79 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Wowngeon Fixes Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
background: #000;
|
||||
color: #0f0;
|
||||
padding: 20px;
|
||||
}
|
||||
.test-result {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
.pass { border-color: #0f0; }
|
||||
.fail { border-color: #f00; }
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: 1px solid #666;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎮 Wowngeon Fixes Verification</h1>
|
||||
|
||||
<div class="test-result">
|
||||
<h2>🔧 Fix #1: START Button Visibility</h2>
|
||||
<p><strong>Issue:</strong> START button was invisible (cut off in game display)</p>
|
||||
<p><strong>Fix:</strong> Moved to HTML status area with proper styling</p>
|
||||
<p><strong>Test:</strong> Check if START button is visible in the status area below</p>
|
||||
</div>
|
||||
|
||||
<div class="test-result">
|
||||
<h2>⏱️ Fix #2: Auto-Entry Death Timing</h2>
|
||||
<p><strong>Issue:</strong> Players died after 2+ blocks instead of 1 block in debug mode</p>
|
||||
<p><strong>Fix:</strong> Set currentUser.blockRec = currentBlock + 1 for consistent timing</p>
|
||||
<p><strong>Test:</strong> Use "enter" command and check death timing logs in console</p>
|
||||
</div>
|
||||
|
||||
<h2>🎯 Live Game Test</h2>
|
||||
<iframe src="http://localhost:3000"></iframe>
|
||||
|
||||
<div class="test-result">
|
||||
<h3>📋 Test Instructions:</h3>
|
||||
<ol>
|
||||
<li><strong>START Button Test:</strong> Look for the green "🎮 START GAME" button in the status area (should be visible)</li>
|
||||
<li><strong>Death Timing Test:</strong> Click START or type "enter" in chat, then check browser console for timing logs</li>
|
||||
<li><strong>Expected Logs:</strong> Should see "🕒 AUTO-ENTRY: Player starts on block X, will die after block X+2"</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Test the START button visibility
|
||||
window.addEventListener('load', function() {
|
||||
setTimeout(function() {
|
||||
const iframe = document.querySelector('iframe');
|
||||
if (iframe && iframe.contentDocument) {
|
||||
const startButton = iframe.contentDocument.getElementById('startButton');
|
||||
if (startButton) {
|
||||
console.log('✅ START button found in iframe');
|
||||
const styles = window.getComputedStyle(startButton);
|
||||
console.log('START button styles:', {
|
||||
display: styles.display,
|
||||
visibility: styles.visibility,
|
||||
background: styles.backgroundColor
|
||||
});
|
||||
} else {
|
||||
console.log('❌ START button not found in iframe');
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,132 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Torch Lighting Test</title>
|
||||
<style>
|
||||
body {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
font-family: monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
#results {
|
||||
white-space: pre-wrap;
|
||||
border: 1px solid #333;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Torch Lighting System Test</h1>
|
||||
<p>Open browser console to see detailed lighting data flow test.</p>
|
||||
<div id="results"></div>
|
||||
|
||||
<script>
|
||||
console.log("🔥 TORCH LIGHTING TEST - Starting comprehensive data flow check");
|
||||
|
||||
// Test 1: Check if lighting data is preserved through GameState
|
||||
const testLightingData = {
|
||||
"18": {"35": 0.3, "36": 0.15, "37": 0.45},
|
||||
"19": {"35": 0.6, "36": 0.3, "37": 0.7},
|
||||
"20": {"35": 0.8, "36": 0.6, "37": 0.9}
|
||||
};
|
||||
|
||||
const testTorchData = [[36, 18], [42, 20], [30, 25]];
|
||||
|
||||
console.log("🧪 Test data created:", {
|
||||
lighting: testLightingData,
|
||||
torches: testTorchData
|
||||
});
|
||||
|
||||
// Test 2: Simulate the data flow through GameState
|
||||
console.log("⚡ Simulating GameState data storage...");
|
||||
|
||||
const mockGameState = {
|
||||
_lighting: {},
|
||||
_torches: [],
|
||||
|
||||
setLightingData: function(lightingData) {
|
||||
this._lighting = lightingData;
|
||||
console.log("✅ GameState: Lighting data stored:", Object.keys(this._lighting).length, "rows");
|
||||
},
|
||||
|
||||
setTorchData: function(torchData) {
|
||||
this._torches = torchData;
|
||||
console.log("✅ GameState: Torch data stored:", this._torches.length, "torches");
|
||||
},
|
||||
|
||||
getGameStateForRender: function() {
|
||||
return {
|
||||
lighting: this._lighting,
|
||||
torches: this._torches,
|
||||
player: {x: 36, y: 19},
|
||||
visibleTiles: {
|
||||
"18": {"35": "#", "36": "'1", "37": "#"},
|
||||
"19": {"35": "'1", "36": "@", "37": "'1"},
|
||||
"20": {"35": "#", "36": "'1", "37": "#"}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Test the flow
|
||||
mockGameState.setLightingData(testLightingData);
|
||||
mockGameState.setTorchData(testTorchData);
|
||||
|
||||
const renderState = mockGameState.getGameStateForRender();
|
||||
console.log("🎨 Render state includes lighting:", !!renderState.lighting);
|
||||
console.log("🎨 Render state includes torches:", !!renderState.torches);
|
||||
|
||||
// Test 3: Simulate RenderEngine processing
|
||||
console.log("🖼️ Simulating RenderEngine processing...");
|
||||
|
||||
const mockRenderEngine = {
|
||||
applyLighting: function(x, y, lightingData) {
|
||||
if (lightingData[y] && lightingData[y][x] !== undefined) {
|
||||
const alpha = lightingData[y][x];
|
||||
console.log(`🔍 Lighting at (${x},${y}): alpha=${alpha.toFixed(3)} -> rgba(0,0,0,${alpha})`);
|
||||
return alpha;
|
||||
} else {
|
||||
console.log(`⭕ No lighting data for (${x},${y})`);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Test lighting application for visible tiles
|
||||
for (const y in renderState.visibleTiles) {
|
||||
for (const x in renderState.visibleTiles[y]) {
|
||||
const lightingAlpha = mockRenderEngine.applyLighting(parseInt(x), parseInt(y), renderState.lighting);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: Check if torch positions are available
|
||||
console.log("🔦 Testing torch position access:");
|
||||
renderState.torches.forEach((torch, i) => {
|
||||
console.log(`Torch ${i + 1}: position [${torch[0]}, ${torch[1]}]`);
|
||||
});
|
||||
|
||||
document.getElementById('results').textContent =
|
||||
"✅ Torch lighting data flow test completed successfully!\n" +
|
||||
"📊 Results:\n" +
|
||||
` - Lighting data: ${Object.keys(testLightingData).length} rows\n` +
|
||||
` - Torch positions: ${testTorchData.length} torches\n` +
|
||||
` - Data flows correctly through GameState ✓\n` +
|
||||
` - RenderEngine can access lighting data ✓\n` +
|
||||
` - Alpha values generate proper rgba() backgrounds ✓\n\n` +
|
||||
"🎮 The torch lighting system should now work in the actual game!";
|
||||
|
||||
console.log("🎯 CONCLUSION: Torch lighting data flow is working correctly!");
|
||||
console.log("📋 Summary:");
|
||||
console.log(" 1. ✅ Server calculates lighting data");
|
||||
console.log(" 2. ✅ Socket handlers extract lighting/torch data");
|
||||
console.log(" 3. ✅ Game.startGame receives lighting data");
|
||||
console.log(" 4. ✅ GameState stores lighting data");
|
||||
console.log(" 5. ✅ getGameStateForRender includes lighting data");
|
||||
console.log(" 6. ✅ RenderEngine can apply lighting as rgba backgrounds");
|
||||
console.log("🔥 Torch lighting should now be visible in the game!");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,216 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Torch Lighting Verification Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
background: black;
|
||||
color: green;
|
||||
padding: 20px;
|
||||
}
|
||||
.test-section {
|
||||
margin: 20px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid green;
|
||||
}
|
||||
.status {
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
.pass { background: darkgreen; }
|
||||
.fail { background: darkred; }
|
||||
.info { background: darkblue; }
|
||||
#gameContainer {
|
||||
margin-top: 20px;
|
||||
border: 2px solid green;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🔥 Torch Lighting System Verification</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Connection Test</h2>
|
||||
<div id="connectionStatus" class="status">Connecting...</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Data Reception Test</h2>
|
||||
<div id="lightingDataStatus" class="status">Waiting for lighting data...</div>
|
||||
<div id="torchDataStatus" class="status">Waiting for torch data...</div>
|
||||
<div id="gameStateStatus" class="status">Waiting for game state...</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Rendering Test</h2>
|
||||
<div id="renderEngineStatus" class="status">Waiting for render test...</div>
|
||||
<div id="tileColorizeStatus" class="status">Checking tileColorize setting...</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Live Data</h2>
|
||||
<div id="liveData" style="font-size: 12px; max-height: 300px; overflow-y: auto;"></div>
|
||||
</div>
|
||||
|
||||
<div id="gameContainer">
|
||||
<div id="game-screen"></div>
|
||||
</div>
|
||||
|
||||
<script src="html/js/jquery-3.4.1.min.js"></script>
|
||||
<script src="html/js/rot.min.js"></script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="html/js/options.js"></script>
|
||||
<script src="html/js/displayManager.js"></script>
|
||||
<script src="html/js/screenManager.js"></script>
|
||||
<script src="html/js/gameState.js"></script>
|
||||
<script src="html/js/renderEngine.js"></script>
|
||||
<script src="html/js/game.js"></script>
|
||||
<script src="html/js/socketHandlers.js"></script>
|
||||
|
||||
<script>
|
||||
// Test status tracking
|
||||
let testResults = {
|
||||
connection: false,
|
||||
lightingData: false,
|
||||
torchData: false,
|
||||
gameState: false,
|
||||
rendering: false,
|
||||
tileColorize: false
|
||||
};
|
||||
|
||||
function updateStatus(id, message, type = 'info') {
|
||||
const element = document.getElementById(id);
|
||||
element.textContent = message;
|
||||
element.className = `status ${type}`;
|
||||
}
|
||||
|
||||
function logLiveData(message) {
|
||||
const liveData = document.getElementById('liveData');
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
liveData.innerHTML += `<div>[${timestamp}] ${message}</div>`;
|
||||
liveData.scrollTop = liveData.scrollHeight;
|
||||
}
|
||||
|
||||
// Override console.log to capture debug messages
|
||||
const originalConsoleLog = console.log;
|
||||
console.log = function(...args) {
|
||||
originalConsoleLog.apply(console, args);
|
||||
if (args[0] && typeof args[0] === 'string') {
|
||||
if (args[0].includes('🔥') || args[0].includes('💡') || args[0].includes('🔍')) {
|
||||
logLiveData('CLIENT: ' + args.join(' '));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Test tileColorize setting
|
||||
if (window.options && window.options.tileColorize === true) {
|
||||
updateStatus('tileColorizeStatus', '✅ tileColorize is enabled', 'pass');
|
||||
testResults.tileColorize = true;
|
||||
} else {
|
||||
updateStatus('tileColorizeStatus', '❌ tileColorize is not enabled', 'fail');
|
||||
}
|
||||
|
||||
// Initialize socket connection
|
||||
const socket = io();
|
||||
|
||||
socket.on('connect', function() {
|
||||
updateStatus('connectionStatus', '✅ Connected to server', 'pass');
|
||||
testResults.connection = true;
|
||||
logLiveData('Socket connected');
|
||||
|
||||
// Register user
|
||||
socket.emit('register', {});
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
updateStatus('connectionStatus', '❌ Disconnected from server', 'fail');
|
||||
testResults.connection = false;
|
||||
});
|
||||
|
||||
// Test game data reception
|
||||
socket.on('gameStart', function(data) {
|
||||
logLiveData(`GAME_START received: ${Object.keys(data).join(', ')}`);
|
||||
|
||||
// Check for lighting data
|
||||
if (data.lighting && Object.keys(data.lighting).length > 0) {
|
||||
updateStatus('lightingDataStatus', `✅ Lighting data received (${Object.keys(data.lighting).length} tiles)`, 'pass');
|
||||
testResults.lightingData = true;
|
||||
logLiveData(`Lighting sample: ${JSON.stringify(Object.entries(data.lighting).slice(0, 3))}`);
|
||||
} else {
|
||||
updateStatus('lightingDataStatus', '❌ No lighting data received', 'fail');
|
||||
}
|
||||
|
||||
// Check for torch data
|
||||
if (data.torches && data.torches.length > 0) {
|
||||
updateStatus('torchDataStatus', `✅ Torch data received (${data.torches.length} torches)`, 'pass');
|
||||
testResults.torchData = true;
|
||||
logLiveData(`Torch sample: ${JSON.stringify(data.torches.slice(0, 5))}`);
|
||||
} else {
|
||||
updateStatus('torchDataStatus', '❌ No torch data received', 'fail');
|
||||
}
|
||||
|
||||
// Test game state storage
|
||||
if (typeof window.Game !== 'undefined' && window.Game.startGame) {
|
||||
try {
|
||||
// Start the game with the received data
|
||||
window.Game.startGame(data, data.lighting, data.torches);
|
||||
updateStatus('gameStateStatus', '✅ Game state initialized successfully', 'pass');
|
||||
testResults.gameState = true;
|
||||
|
||||
// Test rendering
|
||||
setTimeout(() => {
|
||||
testRendering();
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
updateStatus('gameStateStatus', `❌ Game state error: ${error.message}`, 'fail');
|
||||
logLiveData(`Game start error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('gameUpdate', function(data) {
|
||||
if (data.lighting) {
|
||||
logLiveData(`GAME_UPDATE: lighting (${Object.keys(data.lighting).length} tiles)`);
|
||||
}
|
||||
});
|
||||
|
||||
function testRendering() {
|
||||
try {
|
||||
// Check if RenderEngine exists and has the right methods
|
||||
if (typeof window.RenderEngine !== 'undefined') {
|
||||
updateStatus('renderEngineStatus', '✅ RenderEngine available and lighting system active', 'pass');
|
||||
testResults.rendering = true;
|
||||
|
||||
// Log overall test results
|
||||
const passedTests = Object.values(testResults).filter(result => result).length;
|
||||
const totalTests = Object.keys(testResults).length;
|
||||
logLiveData(`🎯 FINAL RESULTS: ${passedTests}/${totalTests} tests passed`);
|
||||
|
||||
if (passedTests === totalTests) {
|
||||
logLiveData('🎉 ALL TESTS PASSED - Torch lighting system should be working!');
|
||||
} else {
|
||||
logLiveData('⚠️ Some tests failed - check individual results above');
|
||||
}
|
||||
} else {
|
||||
updateStatus('renderEngineStatus', '❌ RenderEngine not available', 'fail');
|
||||
}
|
||||
} catch (error) {
|
||||
updateStatus('renderEngineStatus', `❌ Rendering test error: ${error.message}`, 'fail');
|
||||
logLiveData(`Rendering error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-start game after connection
|
||||
setTimeout(() => {
|
||||
if (testResults.connection) {
|
||||
logLiveData('Auto-entering dungeon...');
|
||||
socket.emit('message', 'enter');
|
||||
}
|
||||
}, 2000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,117 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test script to demonstrate the new tile system
|
||||
* Shows floor variations, torch placement, and configurable assets
|
||||
*/
|
||||
|
||||
const DungeonGenerator = require('../src/backend/dungeon.js');
|
||||
|
||||
console.log("🏛️ WOWNGEON TILE SYSTEM TEST");
|
||||
console.log("=" * 50);
|
||||
|
||||
// Test 1: Default Configuration
|
||||
console.log("\n🔧 Test 1: Default Configuration");
|
||||
const defaultConfig = {
|
||||
playerType: "@",
|
||||
treasureType: "$W",
|
||||
monsterType: "~",
|
||||
primaryFloor: "'1",
|
||||
secondaryFloor: "'2",
|
||||
floorVariation: 0.01,
|
||||
torchEnabled: true,
|
||||
torchDensity: 0.15,
|
||||
torchTile: "torch"
|
||||
};
|
||||
|
||||
const dungeon1 = DungeonGenerator.generate(20, 15, defaultConfig);
|
||||
console.log(`Generated dungeon: ${dungeon1.map.length}x${dungeon1.map[0].length}`);
|
||||
console.log(`Entrance: [${dungeon1.entrance}]`);
|
||||
console.log(`Exit: [${dungeon1.exit}]`);
|
||||
console.log(`Treasure: [${dungeon1.treasure}]`);
|
||||
console.log(`Torches: ${dungeon1.torches.length} placed`);
|
||||
|
||||
// Count tile types
|
||||
const tileCounts = {};
|
||||
for (let y = 0; y < dungeon1.map.length; y++) {
|
||||
for (let x = 0; x < dungeon1.map[0].length; x++) {
|
||||
const tile = dungeon1.map[y][x];
|
||||
tileCounts[tile] = (tileCounts[tile] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n📊 Tile Distribution:");
|
||||
Object.keys(tileCounts).sort().forEach(tile => {
|
||||
console.log(` ${tile}: ${tileCounts[tile]} tiles`);
|
||||
});
|
||||
|
||||
if (tileCounts["'1"] && tileCounts["'2"]) {
|
||||
const floorTotal = tileCounts["'1"] + tileCounts["'2"];
|
||||
const variationPercent = (tileCounts["'2"] / floorTotal * 100).toFixed(2);
|
||||
console.log(` Floor variation: ${variationPercent}% secondary floors`);
|
||||
}
|
||||
|
||||
// Test 2: Custom Configuration
|
||||
console.log("\n🔧 Test 2: Custom Configuration (Bitcoin theme)");
|
||||
const customConfig = {
|
||||
playerType: "@2",
|
||||
treasureType: "$B",
|
||||
monsterType: "~2",
|
||||
primaryFloor: "'1",
|
||||
secondaryFloor: "'2",
|
||||
floorVariation: 0.05, // 5% variation
|
||||
torchEnabled: true,
|
||||
torchDensity: 0.25, // 25% more torches
|
||||
torchTile: "torch"
|
||||
};
|
||||
|
||||
const dungeon2 = DungeonGenerator.generate(15, 10, customConfig);
|
||||
console.log(`Generated custom dungeon: ${dungeon2.map.length}x${dungeon2.map[0].length}`);
|
||||
console.log(`Torches in custom dungeon: ${dungeon2.torches.length}`);
|
||||
|
||||
// Test 3: Show a small visual sample
|
||||
console.log("\n🗺️ Sample Dungeon (15x12):");
|
||||
const sampleDungeon = DungeonGenerator.generate(15, 12, defaultConfig);
|
||||
|
||||
console.log(" 012345678901234");
|
||||
for (let y = 0; y < sampleDungeon.map.length; y++) {
|
||||
let line = y.toString().padStart(2) + " ";
|
||||
for (let x = 0; x < sampleDungeon.map[0].length; x++) {
|
||||
let char = sampleDungeon.map[y][x];
|
||||
|
||||
// Add entities for display
|
||||
if (sampleDungeon.entrance && sampleDungeon.entrance[0] === x && sampleDungeon.entrance[1] === y) {
|
||||
char = '<';
|
||||
} else if (sampleDungeon.exit && sampleDungeon.exit[0] === x && sampleDungeon.exit[1] === y) {
|
||||
char = '>';
|
||||
} else if (sampleDungeon.treasure && sampleDungeon.treasure[0] === x && sampleDungeon.treasure[1] === y) {
|
||||
char = '$';
|
||||
}
|
||||
|
||||
// Display character mapping
|
||||
if (char === "'1") char = '.';
|
||||
else if (char === "'2") char = ':';
|
||||
else if (char === 'torch') char = 'T';
|
||||
else if (char === '#') char = '#';
|
||||
|
||||
line += char;
|
||||
}
|
||||
console.log(line);
|
||||
}
|
||||
|
||||
console.log("\nLegend:");
|
||||
console.log(" . = Primary floor ('1)");
|
||||
console.log(" : = Secondary floor ('2)");
|
||||
console.log(" # = Wall");
|
||||
console.log(" T = Torch");
|
||||
console.log(" < = Entrance");
|
||||
console.log(" > = Exit");
|
||||
console.log(" $ = Treasure");
|
||||
|
||||
console.log("\n✅ Tile System Test Complete!");
|
||||
console.log("\nFeatures verified:");
|
||||
console.log(" ✓ Configurable player/treasure/monster types");
|
||||
console.log(" ✓ Floor tile variations (primary/secondary)");
|
||||
console.log(" ✓ Configurable torch placement");
|
||||
console.log(" ✓ Enhanced dungeon generation");
|
||||
console.log(" ✓ Torch position tracking");
|
||||
Reference in New Issue
Block a user