mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-19 07:08:33 +02:00
Fix rewards, make tx hash clickable, add win payout notification, and add entry choice modal
This commit is contained in:
@@ -201,7 +201,20 @@ const InputHandler = {
|
||||
setupClickHandlers: function() {
|
||||
// Add click handling for the HTML START button
|
||||
$('#startButton').click(function(e) {
|
||||
// Attempt immediate start
|
||||
// If early entry is available and user has credits, show choice modal
|
||||
if (typeof SocketHandlers !== 'undefined') {
|
||||
var isPaidCredits = SocketHandlers._gameMode === 'PAID_CREDITS';
|
||||
var hasCredits = SocketHandlers._creditsBalance >= (SocketHandlers._creditsPerGame || 1);
|
||||
var earlyAllowed = SocketHandlers._earlyEntryConfig &&
|
||||
SocketHandlers._earlyEntryConfig.enabled &&
|
||||
SocketHandlers._earlyEntryConfig.allowInCreditsMode;
|
||||
if (isPaidCredits && hasCredits && earlyAllowed) {
|
||||
SocketHandlers.showEntryChoiceModal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Default: attempt immediate start
|
||||
socket.emit('auto_start');
|
||||
|
||||
// Check if we should show waiting screen
|
||||
|
||||
@@ -506,8 +506,21 @@ const SocketHandlers = {
|
||||
}
|
||||
}
|
||||
|
||||
UI.scrollChat();
|
||||
const won = data && (data.status === 'won' || data.reason === 'escaped');
|
||||
|
||||
// Show payout notification for wins
|
||||
if (won && data.payout && data.payout.amount) {
|
||||
var decimals = (SocketHandlers._cryptoType === 'WOW') ? 11 : 12;
|
||||
var amountFormatted = (data.payout.amount / Math.pow(10, decimals)).toFixed(4);
|
||||
var currency = SocketHandlers._cryptoType || 'WOW';
|
||||
var multiplierText = data.payout.multiplier ? ' (' + data.payout.multiplier + 'x)' : '';
|
||||
var treasureText = data.payout.treasure ? ' + Treasure bonus!' : '';
|
||||
$('#messages').append($('<li style="color:#4ade80; font-weight:bold; padding:4px 0;">').html(
|
||||
'💰 Payout queued: ' + amountFormatted + ' ' + currency + multiplierText + treasureText
|
||||
));
|
||||
}
|
||||
|
||||
UI.scrollChat();
|
||||
if (won) {
|
||||
SocketHandlers._setBannerStatus('Won', '#0f0');
|
||||
} else {
|
||||
@@ -591,6 +604,8 @@ const SocketHandlers = {
|
||||
SocketHandlers._directPayoutsEnabled = !!data.directPayoutsEnabled;
|
||||
SocketHandlers._paymentsEnabled = !!data.paymentsEnabled;
|
||||
SocketHandlers._smirkEnabled = data.smirkEnabled !== false; // Default to true if not specified
|
||||
SocketHandlers._cryptoType = data.cryptoType || 'WOW';
|
||||
SocketHandlers._explorerTxUrl = data.explorerTxUrl || null;
|
||||
|
||||
// Initialize Smirk auth if enabled and not already initialized
|
||||
if (SocketHandlers._smirkEnabled && typeof SmirkAuth !== 'undefined' && !SmirkAuth._initialized) {
|
||||
@@ -1433,14 +1448,91 @@ const SocketHandlers = {
|
||||
*/
|
||||
onEarlyEntryError: function(data) {
|
||||
console.error('Early entry error:', data);
|
||||
|
||||
|
||||
// Re-enable button
|
||||
const $btn = $('#earlyEntryButton');
|
||||
$btn.prop('disabled', false).text('⚡ ENTER NOW (RISKY!) ⚡');
|
||||
|
||||
|
||||
const message = data?.message || 'Early entry not available';
|
||||
$('#messages').append($('<li class="error" style="color:#f00;">').text('Early entry failed: ' + message));
|
||||
UI.scrollChat();
|
||||
},
|
||||
|
||||
// =====================
|
||||
// Entry Choice Modal
|
||||
// =====================
|
||||
|
||||
showEntryChoiceModal: function() {
|
||||
// Remove any existing modal
|
||||
$('#entryChoiceOverlay').remove();
|
||||
|
||||
var cost = this._creditsPerGame || 1;
|
||||
var $overlay = $('<div id="entryChoiceOverlay">').css({
|
||||
position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
|
||||
background: 'rgba(0,0,0,0.7)', zIndex: 3000,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center'
|
||||
});
|
||||
|
||||
var $modal = $('<div>').css({
|
||||
background: '#0a1a0a', border: '2px solid #0f0', borderRadius: '8px',
|
||||
padding: '24px', maxWidth: '420px', width: '90%', color: '#0f0',
|
||||
fontFamily: 'monospace', textAlign: 'center'
|
||||
});
|
||||
|
||||
$modal.html(
|
||||
'<div style="font-size:18px; font-weight:bold; margin-bottom:16px;">How do you want to enter?</div>' +
|
||||
'<div style="font-size:12px; color:#888; margin-bottom:20px;">Cost: ' + cost + ' credit per game</div>' +
|
||||
'<button id="entryChoiceNow" style="' +
|
||||
'display:block; width:100%; padding:12px; margin-bottom:10px; cursor:pointer; ' +
|
||||
'background:linear-gradient(180deg,#662200,#441100); border:2px solid #ff6600; ' +
|
||||
'color:#ffcc00; font-family:monospace; font-size:14px; font-weight:bold; border-radius:4px;' +
|
||||
'">⚡ Start Now (risky)<br><span style="font-size:11px; font-weight:normal; color:#cc9966;">Game starts immediately — you die when next block arrives</span></button>' +
|
||||
'<button id="entryChoiceQueue" style="' +
|
||||
'display:block; width:100%; padding:12px; margin-bottom:10px; cursor:pointer; ' +
|
||||
'background:linear-gradient(180deg,#003300,#001a00); border:2px solid #0f0; ' +
|
||||
'color:#0f0; font-family:monospace; font-size:14px; font-weight:bold; border-radius:4px;' +
|
||||
'">🛡️ Wait for Next Block (safe)<br><span style="font-size:11px; font-weight:normal; color:#6a6;">Queues you — full block window to escape</span></button>' +
|
||||
'<button id="entryChoiceCancel" style="' +
|
||||
'display:block; width:100%; padding:8px; cursor:pointer; ' +
|
||||
'background:transparent; border:1px solid #555; color:#888; ' +
|
||||
'font-family:monospace; font-size:12px; border-radius:4px;' +
|
||||
'">Cancel</button>'
|
||||
);
|
||||
|
||||
$overlay.append($modal);
|
||||
$('body').append($overlay);
|
||||
|
||||
// Handlers
|
||||
$('#entryChoiceNow').on('click', function() {
|
||||
$('#entryChoiceOverlay').remove();
|
||||
socket.emit('auto_start');
|
||||
$('#messages').append($('<li style="color:#ff6600;">').text('⚡ Starting game immediately...'));
|
||||
UI.scrollChat();
|
||||
if (typeof ScreenManager !== 'undefined' && ScreenManager.drawWaitingScreen) {
|
||||
ScreenManager.drawWaitingScreen();
|
||||
}
|
||||
});
|
||||
|
||||
$('#entryChoiceQueue').on('click', function() {
|
||||
$('#entryChoiceOverlay').remove();
|
||||
socket.emit('join_queue');
|
||||
$('#messages').append($('<li style="color:#0f0;">').text('🛡️ Joining queue — waiting for next block...'));
|
||||
UI.scrollChat();
|
||||
if (typeof ScreenManager !== 'undefined' && ScreenManager.drawWaitingScreen) {
|
||||
ScreenManager.drawWaitingScreen();
|
||||
}
|
||||
});
|
||||
|
||||
$('#entryChoiceCancel').on('click', function() {
|
||||
$('#entryChoiceOverlay').remove();
|
||||
});
|
||||
|
||||
// Close on overlay click (outside modal)
|
||||
$overlay.on('click', function(e) {
|
||||
if (e.target === this) {
|
||||
$('#entryChoiceOverlay').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -143,7 +143,11 @@ const TransactionHistory = {
|
||||
<div style="color:#888; font-size:0.85em; margin-top:4px;">
|
||||
${payout.reason || 'Game reward'} • ${date}
|
||||
</div>
|
||||
${payout.txHash ? `<div style="color:#60a5fa; font-size:0.75em; margin-top:4px; word-break:break-all;">TX: ${payout.txHash.substring(0, 16)}...</div>` : ''}
|
||||
${payout.txHash ? `<div style="font-size:0.75em; margin-top:4px; word-break:break-all;">${
|
||||
typeof SocketHandlers !== 'undefined' && SocketHandlers._explorerTxUrl
|
||||
? '<a href="' + SocketHandlers._explorerTxUrl + payout.txHash + '" target="_blank" rel="noopener" style="color:#60a5fa; text-decoration:underline;">TX: ' + payout.txHash.substring(0, 16) + '...</a>'
|
||||
: '<span style="color:#60a5fa;">TX: ' + payout.txHash.substring(0, 16) + '...</span>'
|
||||
} <button class="copy-hash-btn" data-hash="${payout.txHash}" style="font-size:10px; padding:1px 4px; cursor:pointer; background:#333; color:#fff; border:1px solid #555; border-radius:3px;">Copy</button></div>` : ''}
|
||||
</div>
|
||||
`);
|
||||
|
||||
|
||||
@@ -391,6 +391,17 @@ ALERT_COOLDOWN_MS=3600000
|
||||
# Set to false for Monero stagenet deployments
|
||||
SMIRK_ENABLED=true
|
||||
|
||||
# =============================================================================
|
||||
# SECTION 14b: BLOCK EXPLORER
|
||||
# =============================================================================
|
||||
# URL prefix for linking transaction hashes to a block explorer.
|
||||
# Include the trailing slash. TX hash will be appended directly.
|
||||
# Leave unset to disable clickable TX links in transaction history.
|
||||
#
|
||||
# Wownero: https://explore.wowne.ro/tx/
|
||||
# Monero: https://xmrchain.net/tx/
|
||||
EXPLORER_TX_URL=https://explore.wowne.ro/tx/
|
||||
|
||||
# =============================================================================
|
||||
# SECTION 15: DEVELOPMENT/DEBUG OPTIONS
|
||||
# =============================================================================
|
||||
|
||||
@@ -1071,7 +1071,8 @@ class GameModeManager {
|
||||
allowInCreditsMode: this.configSnapshot?.earlyEntry?.allowInCreditsMode ?? false
|
||||
},
|
||||
// Smirk wallet integration (disabled for Monero stagenet)
|
||||
smirkEnabled: process.env.SMIRK_ENABLED !== 'false'
|
||||
smirkEnabled: process.env.SMIRK_ENABLED !== 'false',
|
||||
explorerTxUrl: process.env.EXPLORER_TX_URL || null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -424,7 +424,7 @@ app.get('/api/user/:socketId/payouts', asyncHandler(async (req, res) => {
|
||||
|
||||
// Calculate total received (confirmed payouts)
|
||||
const totalResult = await db.query(
|
||||
`SELECT COALESCE(SUM(amount), 0) as total_received FROM payouts WHERE user_id = $1 AND status = 'confirmed'`,
|
||||
`SELECT COALESCE(SUM(amount), 0) as total_received FROM payouts WHERE user_id = $1 AND status = 'completed'`,
|
||||
[user.id]
|
||||
);
|
||||
|
||||
|
||||
@@ -463,6 +463,7 @@ class SocketHandlers {
|
||||
}
|
||||
});
|
||||
socket.on('auto_start', () => this.handleAutoStart(socket)); // New handler for start button
|
||||
socket.on('join_queue', () => this.handleJoinQueue(socket)); // Queue instead of auto-start
|
||||
socket.on('early_entry', () => this.handleEarlyEntry(socket)); // Early entry without waiting for block
|
||||
socket.on('address:prompt', () => this.handleAddressPrompt(socket));
|
||||
|
||||
@@ -581,6 +582,32 @@ class SocketHandlers {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle join_queue request — adds player to block queue instead of starting immediately
|
||||
*/
|
||||
async handleJoinQueue(socket) {
|
||||
try {
|
||||
await this.queueHandler.handleGameQueue(
|
||||
socket,
|
||||
this.connectionHandler.getUserBySocket.bind(this.connectionHandler)
|
||||
);
|
||||
// queueHandler already emits queue status and adds to queue
|
||||
// Client receives queue_joined event from broadcastManager
|
||||
const pos = this.queueManager.getPlayerIndex(socket.id);
|
||||
if (pos !== -1) {
|
||||
const currentBlock = this.debugManager.getCurrentBlockHeight ? this.debugManager.getCurrentBlockHeight() : 0;
|
||||
socket.emit('queue_joined', {
|
||||
position: pos + 1,
|
||||
currentBlock: currentBlock,
|
||||
nextBlock: currentBlock + 1
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('handleJoinQueue error:', err);
|
||||
this.broadcastManager.sendStatusUpdate(socket.id, 'error', 'Failed to join queue.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle early entry request - start game immediately without waiting for block
|
||||
* Only available for free mode and credits mode (not direct payment mode)
|
||||
|
||||
Reference in New Issue
Block a user