Get back into it

This commit is contained in:
jwinterm
2025-09-24 14:06:42 -04:00
parent bd97d9d848
commit 4ad063f735
16 changed files with 1751 additions and 328 deletions
+109
View File
@@ -14,6 +14,8 @@ var Game = {
_inputEnabled: true,
_messageLog: [],
_maxLogMessages: 5,
_unconfirmedPayment: false,
_unconfirmedPaymentInfo: null,
init: function() {
// Update screen dimensions from options
@@ -147,6 +149,89 @@ var Game = {
}
},
// Legacy alias (some handlers referenced updateGame previously)
updateGame: function(data) {
return this.updateGameState(data);
},
// Handle game over event from server
endGame: function(data) {
try {
GameState.setGameActive(false);
// Decide which screen to draw
if (data && data.reason === 'monster') {
if (ScreenManager.drawLoseScreen) ScreenManager.drawLoseScreen('monster');
} else if (data && data.reason === 'escaped') {
if (ScreenManager.drawWinScreen) ScreenManager.drawWinScreen(data.treasure || false);
} else if (data && data.reason === 'timeout') {
if (ScreenManager.drawLoseScreen) ScreenManager.drawLoseScreen('timeout');
} else {
if (ScreenManager.drawLoseScreen) ScreenManager.drawLoseScreen('other');
}
// After short delay, allow user to return to title with Enter/start
const score = data && typeof data.score === 'number' ? data.score : 0;
const treasure = !!(data && data.treasure);
// Display score / treasure info overlay
if (ScreenManager && ScreenManager.drawCenteredText && DisplayManager.ensureDisplay()) {
const baseY = Math.floor(ScreenManager._screenHeight / 2) + 5;
ScreenManager.drawCenteredText(baseY, `Score: ${score}`);
if (treasure) {
ScreenManager.drawCenteredText(baseY + 1, 'You secured the treasure!');
}
}
// Debounce + delayed activation of restart
this._awaitingRestart = false;
const activateDelay = 800; // ms before we accept Enter
const autoReturnDelay = 10000; // auto return after 10s
// Show hint after activation delay
setTimeout(() => {
this._awaitingRestart = true;
if (ScreenManager && ScreenManager.drawCenteredText && DisplayManager.ensureDisplay()) {
const y = ScreenManager._screenHeight - 2;
const fullHint = 'Press Enter to return to title';
const shortHint = 'Press Enter';
const maxLen = ScreenManager._screenWidth - 2; // leave small margin
const hint = fullHint.length > maxLen ? shortHint : fullHint;
ScreenManager.drawCenteredText(y, hint);
}
}, activateDelay);
// Auto return timer
clearTimeout(this._autoReturnTimer);
this._autoReturnTimer = setTimeout(() => {
if (this._awaitingRestart) {
this._awaitingRestart = false;
if (ScreenManager && ScreenManager.drawWelcomeScreen) {
ScreenManager.drawWelcomeScreen();
}
}
}, autoReturnDelay);
// One-time listener setup
if (!this._restartListenerAttached) {
this._restartListenerAttached = true;
document.addEventListener('keydown', (e) => {
if (!this._awaitingRestart) return;
if (e.key === 'Enter') {
this._awaitingRestart = false;
clearTimeout(this._autoReturnTimer);
if (ScreenManager && ScreenManager.drawWelcomeScreen) {
ScreenManager.drawWelcomeScreen();
}
const chatInput = document.getElementById('chatInput');
if (chatInput) chatInput.focus();
}
});
}
} catch (err) {
console.error('Error handling endGame:', err);
}
},
clearDisplay: function() {
DisplayManager.clearDisplay();
},
@@ -173,6 +258,19 @@ var Game = {
drawWaitingScreen: function() {
ScreenManager.drawWaitingScreen();
// Overlay unconfirmed payment badge if present
if (this._unconfirmedPayment && typeof ScreenManager !== 'undefined' && DisplayManager.ensureDisplay()) {
const display = DisplayManager.getDisplay();
const badgeText = 'Unconfirmed (mempool)';
const xStart = Math.max(1, Math.floor((this._screenWidth - badgeText.length) / 2));
const y = 1; // top line inside border
for (let i = 0; i < badgeText.length; i++) {
display.draw(xStart + i, y, badgeText[i], 'rgba(180,230,255,0.9)', 'transparent');
}
// Small pulsing dot
const pulse = 0.4 + Math.sin(Date.now()/300)*0.4;
display.draw(xStart - 2, y, '●', `rgba(120,200,255,${pulse})`, 'transparent');
}
},
stopWaitingScreen: function() {
@@ -239,6 +337,17 @@ var Game = {
}
};
// Payment state helpers
Game._pendingPaymentDetected = function(info) {
this._unconfirmedPayment = true;
this._unconfirmedPaymentInfo = info || {};
};
Game._pendingPaymentConfirmed = function() {
this._unconfirmedPayment = false;
this._unconfirmedPaymentInfo = null;
};
// Debug utility functions (keep as-is for compatibility)
const GameDebug = {
log: function(message) {
+34 -7
View File
@@ -210,13 +210,40 @@ var WaitingScreenAnimator = {
const bg = ["transparent", "transparent"];
display.draw(anim.playerX, anim.playerY, ch, fg, bg);
// Light footsteps behind player
if (anim.phase === 'enter' || anim.phase === 'escape') {
const footstepX = anim.playerX - 1;
if (footstepX >= roomStartX && footstepX <= roomEndX) {
const footstepGlow = Math.max(0, Math.sin(time / 400) * 0.2);
const floorTile = GameTiles.getFloorTile();
display.draw(footstepX, anim.playerY, floorTile, `rgba(150, 200, 255, ${footstepGlow})`, "transparent");
// Bright ethereal footsteps / halo around player to indicate "primed" status
if (anim.phase === 'enter' || anim.phase === 'escape' || anim.phase === 'chase') {
const ringPulse = 0.35 + Math.sin(time / 250) * 0.25; // stronger, faster pulse
const innerPulse = 0.15 + Math.sin(time / 180 + 1.2) * 0.15;
const ringColor = (alpha) => `rgba(180, 230, 255, ${alpha})`;
const floorTile = GameTiles.getFloorTile();
// 8-way surrounding ring
const ringOffsets = [
{dx: -1, dy: 0}, {dx: 1, dy: 0}, {dx: 0, dy: -1}, {dx: 0, dy: 1}, // cardinal
{dx: -1, dy: -1}, {dx: 1, dy: -1}, {dx: -1, dy: 1}, {dx: 1, dy: 1} // diagonal
];
for (let i = 0; i < ringOffsets.length; i++) {
const rx = anim.playerX + ringOffsets[i].dx;
const ry = anim.playerY + ringOffsets[i].dy;
if (rx >= roomStartX && rx <= roomEndX && ry >= roomStartY && ry <= roomEndY) {
// subtle variation per tile
const phaseShift = (i / ringOffsets.length) * Math.PI * 2;
const alpha = (ringPulse * 0.6) + Math.sin(time / 300 + phaseShift) * 0.15;
display.draw(rx, ry, floorTile, ringColor(Math.max(0, alpha)), 'transparent');
}
}
// Trailing footprint path (a few tiles behind)
for (let trail = 1; trail <= 3; trail++) {
const tx = anim.playerX - trail;
const ty = anim.playerY;
if (tx >= roomStartX && tx <= roomEndX && ty >= roomStartY && ty <= roomEndY) {
const decay = Math.max(0, 0.5 - trail * 0.15);
const alpha = innerPulse * decay;
if (alpha > 0.03) {
display.draw(tx, ty, floorTile, `rgba(120, 200, 255, ${alpha})`, 'transparent');
}
}
}
}
}
+6 -12
View File
@@ -33,19 +33,13 @@ const InputHandler = {
if (msg.toLowerCase() === 'enter') {
socket.emit('chat message', msg);
// Show appropriate screen based on current state
const isDebugMode = window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1' ||
window.location.protocol === 'file:';
// Don't automatically switch to waiting screen - let the server control screen state
// The server will send appropriate events (payment_created, waiting_status, etc.)
// to control what the user sees
if (isDebugMode || ScreenManager.canEnterGame()) {
if (typeof ScreenManager !== 'undefined' && ScreenManager.drawWaitingScreen) {
ScreenManager.drawWaitingScreen();
}
} else {
$('#messages').append($('<li style="color: #ff0;">').text("* You have been added to the queue! Game will start after next block."));
UI.scrollChat();
}
// Add visual feedback that request was sent
$('#messages').append($('<li style="color: #0f0;">').text("🔑 Processing game entry request..."));
UI.scrollChat();
} else {
// Send other chat messages normally
socket.emit('chat message', msg);
+111 -145
View File
@@ -2,7 +2,7 @@
* Socket event handlers for the Wowngeon game
*/
const SocketHandlers = {
_initialized: false, // Flag to prevent multiple initializations
_initialized: false,
init: function() {
if (this._initialized) {
@@ -28,6 +28,7 @@ const SocketHandlers = {
// Game state handlers
socket.on('waiting_status', this.onWaitingStatus);
socket.on('queue_joined', this.onQueueJoined);
socket.on('game_start', this.onGameStart);
socket.on('game_update', this.onGameUpdate);
socket.on('game_over', this.onGameOver);
@@ -36,20 +37,19 @@ const SocketHandlers = {
// Payment/Address handlers
socket.on('address_detected', this.onAddressDetected);
socket.on('address_confirmed', this.onAddressConfirmed);
socket.on('payment_created', this.onPaymentCreated);
socket.on('payment_confirmed', this.onPaymentConfirmed);
socket.on('payment_detected', this.onPaymentDetected);
// Block height handler (broadcast to all clients)
// Block height handler
socket.on('blockheight', this.onBlockHeight);
},
onConnect: function() {
// Tell server our client ID
socket.emit('register_client', {
clientId: socket.id,
userAgent: navigator.userAgent
});
// Auto-start removed - user must manually start game
},
onWelcome: function(msg) {
@@ -63,30 +63,31 @@ const SocketHandlers = {
},
onStatusUpdate: function(data) {
// Handle player-specific status updates
// Display status message with appropriate styling
const statusClass = data.type === 'error' ? 'error' : 'status';
$('#messages').append($(`<li class="${statusClass}">`).text(data.message));
const statusColor = data.type === 'error' ? '#f00' :
data.type === 'warning' ? '#ff0' :
data.type === 'success' ? '#0f0' :
data.type === 'payment' ? '#0af' : '#fff';
$('#messages').append($('<li class="' + statusClass + '" style="color: ' + statusColor + '; white-space: pre-line;">').text(data.message));
UI.scrollChat();
},
onChatBroadcast: function(data) {
// Handle chat messages broadcast to all players
const timestamp = new Date(data.timestamp || Date.now()).toLocaleTimeString();
const chatMsg = `[${timestamp}] ${data.username || 'Anonymous'}: ${data.message}`;
$('#messages').append($('<li class="chat">').text(chatMsg));
const msgElement = $('<li style="color: #aaa;">');
if (data.socketId) {
msgElement.html('<strong>' + data.socketId.substring(0, 6) + ':</strong> ' + data.message);
} else {
msgElement.text(data.message);
}
$('#messages').append(msgElement);
UI.scrollChat();
},
onWaitingStatus: function(data) {
if (data.status === 'waiting') {
$('#messages').append($('<li style="color:#ff0;">').text(data.message));
// Show waiting screen
if (typeof Game !== 'undefined' && Game.drawWaitingScreen) {
Game.drawWaitingScreen();
}
@@ -94,8 +95,21 @@ const SocketHandlers = {
UI.scrollChat();
},
onGameStart: function(data) {
onQueueJoined: function(data) {
console.log('Queue joined:', data);
$('#messages').append($('<li class="queue-info" style="color:#0f0;">').html(
'�� <strong>Queue Joined!</strong> Position: ' + data.position + '<br>' +
'📦 Current block: ' + data.currentBlock + ', Next: ' + data.nextBlock
));
UI.scrollChat();
if (typeof Game !== 'undefined' && Game.drawWaitingScreen) {
Game.drawWaitingScreen();
}
},
onGameStart: function(data) {
$('#messages').append($('<li class="game-start">').text("Starting game..."));
if (typeof Game !== 'undefined' && Game.stopWaitingScreen) {
@@ -109,173 +123,125 @@ const SocketHandlers = {
}
try {
// Pass all game data including lighting and torches
var success = Game.startGame(data.player, data.map, data.monster, data.items, data.visibleTiles, data.lighting, data.torches);
if (!success) {
$('#messages').append($('<li class="error">').text("Game start failed. Check console for details."));
if (typeof Game !== 'undefined' && Game._drawWelcomeScreen) Game._drawWelcomeScreen();
} else {
// Shift focus to the game display area after successful game start
$('#game-display').focus();
UI.updateFocusIndicator();
setTimeout(function() {
$('#game-display').focus();
}, 100);
}
} catch (err) {
console.error("Error starting game:", err);
$('#messages').append($('<li class="error">').text("Error: " + err.message));
} catch (error) {
console.error("Error starting game:", error);
$('#messages').append($('<li class="error">').text("Game start error: " + error.message));
if (typeof Game !== 'undefined' && Game._drawWelcomeScreen) Game._drawWelcomeScreen();
}
UI.scrollChat();
},
onGameUpdate: function(data) {
if (data.visibleTiles) {
if (window.GameDebug) window.GameDebug.updateDebugDisplay("SOCKET: Received game update with visibleTiles");
}
if (typeof Game !== 'undefined' && Game._gameActive) {
Game.updateGameState(data);
// Server pushed a game state update. The Game object exposes updateGameState().
// Older code referenced Game.updateGame, which doesn't exist in the current refactor,
// so updates were silently ignored (player appeared frozen).
if (typeof Game !== 'undefined') {
if (Game.updateGameState) {
Game.updateGameState(data);
} else if (Game.updateGame) { // Fallback if an alias gets added later
Game.updateGame(data);
} else {
console.warn('Game update received but no updateGameState()/updateGame() method found on Game.');
}
}
UI.scrollChat();
},
onGameOver: function(data) {
// Update game state based on outcome
$('#messages').append($('<li class="game-over">').text("Game Over: " + data.message));
if (typeof Game !== 'undefined') {
if (data.status === 'won') {
// Show win message
$('#messages').append($('<li style="color:#0f0; font-weight:bold;">').text(
data.hasTreasure ?
"YOU ESCAPED WITH THE TREASURE! YOU WON!" :
"You escaped the dungeon alive!"
));
// Draw win screen
Game.drawWinScreen(data.hasTreasure);
} else {
// Show loss message
const reason = data.reason || "unknown";
let message = "You died in the dungeon!";
if (reason === 'monster') {
message = "You were killed by the monster!";
} else if (reason === 'timeout') {
message = "You didn't escape before the next block was found!";
}
$('#messages').append($('<li style="color:#f00; font-weight:bold;">').text(message));
// Draw lose screen
if (Game && Game.drawLoseScreen) {
Game.drawLoseScreen(reason);
}
if (Game.endGame) {
Game.endGame(data);
} else if (Game.drawLoseScreen && data && data.reason) {
// Minimal fallback if endGame not present
if (data.reason === 'monster') Game.drawLoseScreen('monster');
}
// Set game as inactive to prevent further movement
if (Game) {
Game._gameActive = false;
}
// Auto-return to title screen after 30 seconds
setTimeout(() => {
$('#messages').append($('<li style="color:#888;">').text("Returning to title screen..."));
UI.scrollChat();
// Return to welcome screen
if (Game && Game._drawWelcomeScreen) {
Game._drawWelcomeScreen();
}
// Reset game state
if (typeof GameState !== 'undefined') {
GameState.reset();
}
// Focus chat for new commands
$('#chatInput').focus();
UI.updateFocusIndicator();
}, 30000); // 30 seconds
}
UI.scrollChat();
},
onQueueCancelled: function() {
onQueueCancelled: function(data) {
$('#messages').append($('<li style="color: #ff0;">').text("Queue entry cancelled."));
if (typeof Game !== 'undefined' && Game._drawWelcomeScreen) {
Game._drawWelcomeScreen();
} else {
console.error("Game or _drawWelcomeScreen not available");
}
},
onBlockHeight: function(height) {
UI.updateBlockHeight(height);
$('#statusValue').text('Connected');
$('#statusValue').css('color', '#0f0');
UI.scrollChat();
},
onAddressDetected: function(data) {
// Handle address detection confirmation request
console.log('Address detected:', data);
$('#messages').append($('<li class="address-detected" style="color: #ff0; white-space: pre-line;">').text(data.message));
UI.scrollChat();
},
onAddressConfirmed: function(data) {
console.log('Address confirmed:', data);
$('#messages').append($('<li class="address-confirmed" style="color: #0f0; white-space: pre-line;">').text(data.message));
UI.scrollChat();
},
onPaymentCreated: function(data) {
console.log('Payment created:', data);
// Display the confirmation message with styling
const confirmationHtml = `
<div class="address-confirmation" style="
background: #ffe066;
color: #333;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
border-left: 4px solid #f0ad4e;
">
<strong>🔍 ADDRESS DETECTED</strong><br>
<strong>Type:</strong> ${data.type}<br>
<strong>Address:</strong> <code style="word-break: break-all; font-size: 11px;">${data.address}</code><br><br>
<strong style="color: #d9534f;">⚠️ WARNING: Verify this is YOUR address!</strong><br>
<strong style="color: #d9534f;">⚠️ Clipboard viruses can change addresses!</strong><br><br>
Type <strong>"confirm"</strong> to set as payout address or <strong>"cancel"</strong> to reject.
</div>
`;
$('#messages').append($(confirmationHtml));
$('#messages').append($('<li class="payment-info">').html(
'💳 <strong>Payment Required:</strong> ' + data.humanAmount + ' ' + data.cryptoType
));
UI.scrollChat();
// Focus chat input for easy confirmation
setTimeout(() => {
setTimeout(function() {
$('#chatInput').focus();
}, 100);
},
onAddressConfirmed: function(data) {
// Handle successful address confirmation
console.log('Address confirmed:', data);
onPaymentConfirmed: function(data) {
console.log('Payment confirmed:', data);
const confirmationHtml = `
<div class="address-confirmed" style="
background: #d4edda;
color: #155724;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
border-left: 4px solid #28a745;
">
<strong>✅ PAYOUT ADDRESS CONFIRMED</strong><br>
<strong>Type:</strong> ${data.type}<br>
<strong>Address:</strong> <code style="word-break: break-all; font-size: 11px;">${data.address}</code><br><br>
Future winnings will be sent to this address.
</div>
`;
if (typeof Game !== 'undefined' && Game.drawWaitingScreen) {
Game.drawWaitingScreen();
}
if (typeof Game !== 'undefined') {
Game._pendingPaymentConfirmed();
}
$('#messages').append($(confirmationHtml));
$('#messages').append($('<li class="payment-success">').html(
'✅ <strong>Payment confirmed!</strong> You are in the game queue.'
));
UI.scrollChat();
},
onPaymentDetected: function(data) {
console.log('Payment detected (mempool):', data);
if (typeof Game !== 'undefined') {
Game._pendingPaymentDetected(data);
if (Game.drawWaitingScreen) Game.drawWaitingScreen();
}
$('#messages').append($('<li class="payment-mempool" style="color:#0af;">').html(
'🌀 <strong>Payment seen in mempool</strong> – awaiting block confirmation...'
));
UI.scrollChat();
},
onBlockHeight: function(data) {
if (typeof UI !== 'undefined' && UI.updateBlockHeight) {
UI.updateBlockHeight(data.blockHeight);
}
}
};
// Ensure SocketHandlers is available globally
if (typeof window !== 'undefined') {
window.SocketHandlers = SocketHandlers;
if (typeof module !== 'undefined' && module.exports) {
module.exports = SocketHandlers;
}
// Note: SocketHandlers.init() is called from index.html after DOM ready
// to ensure proper initialization order with other modules