diff --git a/html/js/network/socketHandlers.js b/html/js/network/socketHandlers.js
index d5e46cc..50f17b9 100644
--- a/html/js/network/socketHandlers.js
+++ b/html/js/network/socketHandlers.js
@@ -1708,81 +1708,95 @@ const SocketHandlers = {
// Entry Choice Modal
// =====================
+ // ONE consolidated entry modal: timing (when) + stakes (free vs ranked) in a single dialog, so
+ // the player never hits the old timing-modal-then-payment-modal double. Each card emits the
+ // FINAL intent — Free sends {free:true} (server skips its options modal), Ranked uses a credit
+ // or opens the real payment UI. Nothing starts until a card is clicked.
showEntryChoiceModal: function(opts) {
opts = opts || {};
- // Remove any existing modal
$('#entryChoiceOverlay').remove();
-
+ var freeAvailable = !!opts.freeAvailable;
+ var hasCredits = !!opts.hasCredits;
var cost = this._creditsPerGame || 1;
- var costText = opts.freeAvailable
- ? 'Free play — no credits needed'
- : ('Cost: ' + cost + ' credit per game');
+ var currency = this._currencyLabel || 'WOW';
+ var timing = 'wait'; // safe default
+
var $overlay = $('
').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'
+ background: 'rgba(0,0,0,0.74)', zIndex: 3000, display: 'flex',
+ alignItems: 'center', justifyContent: 'center'
});
-
var $modal = $('
').css({
- background: '#0a1a0a', border: '2px solid #0f0', borderRadius: '8px',
- padding: '24px', maxWidth: '420px', width: '90%', color: '#0f0',
- fontFamily: 'monospace', textAlign: 'center'
+ background: 'linear-gradient(180deg,#101609,#0b0f07)', border: '1px solid #33421f',
+ borderRadius: '12px', width: '92%', maxWidth: '480px', color: '#e6ead0',
+ fontFamily: 'monospace', boxShadow: '0 22px 60px rgba(0,0,0,.6)', overflow: 'hidden'
});
- $modal.html(
- '
How do you want to enter?
' +
- '
' + costText + '
' +
- '
⚡ Start Now (risky)Game starts immediately — you die when next block arrives ' +
- '
🛡️ Wait for Next Block (safe)Queues you — full block window to escape ' +
- '
Cancel '
- );
+ function seg(id, on, t, d, danger) {
+ var col = on ? (danger ? '#ff6a1a' : '#4fd463') : '#8b9a78';
+ var bg = on ? (danger ? 'linear-gradient(180deg,#4a1c05,#2a1103)' : 'linear-gradient(180deg,#123a17,#0b2410)') : '#0a0f07';
+ var bc = on ? (danger ? '#ff6a1a' : '#4fd463') : '#33421f';
+ return '
' +
+ '' + t + ' ' +
+ '' + d + ' ';
+ }
+ function stake(id, k, t, s, accent) {
+ return '
' +
+ '
' + k + ' ' +
+ '
' + t + '
' +
+ '
' + s + '
' +
+ '
ENTER ▸ ';
+ }
+ function begin(msg, emit) {
+ $('#entryChoiceOverlay').remove();
+ emit();
+ $('#messages').append($('
').text(msg));
+ if (typeof UI !== 'undefined' && UI.scrollChat) UI.scrollChat();
+ if (typeof ScreenManager !== 'undefined' && ScreenManager.drawWaitingScreen) ScreenManager.drawWaitingScreen();
+ }
+ function pay() {
+ $('#entryChoiceOverlay').remove();
+ if (typeof PaymentUI !== 'undefined' && PaymentUI.show) PaymentUI.show();
+ }
+ function render() {
+ $modal.html(
+ '' +
+ '⚔ ' +
+ '
Enter the Dungeon ' +
+ 'esc ' +
+ '' +
+ '' +
+ '
1 · When do you drop in?
' +
+ '
' +
+ seg('ecWait', timing === 'wait', '🛡 Next block', 'Safe — full block window', false) +
+ seg('ecNow', timing === 'now', '⚡ Right now', 'Risky — die on next block', true) +
+ '
' +
+ '
2 · Choose your stakes
' +
+ (freeAvailable ? stake('ecFree', '🆓', 'Free Play', 'No cost · Pleb leaderboard', '#123a17') : '') +
+ stake('ecRank', '💰', 'Ranked', (hasCredits ? '1 credit' : ('Pay ' + cost + ' ' + currency)) + ' · Hall of Champions', '#2a1a4a') +
+ '
' +
+ '
'
+ );
+ $('#ecWait').on('click', function () { timing = 'wait'; render(); });
+ $('#ecNow').on('click', function () { timing = 'now'; render(); });
+ $('#ecX').on('click', function () { $('#entryChoiceOverlay').remove(); });
+ $('#ecFree').on('click', function () {
+ if (timing === 'now') begin('⚡ Free game — dropping in now...', function () { socket.emit('auto_start', { free: true }); });
+ else begin('🛡️ Free game — queued for the next block...', function () { socket.emit('join_queue'); });
+ });
+ $('#ecRank').on('click', function () {
+ if (!hasCredits) { pay(); return; }
+ if (timing === 'now') begin('⚡ Ranked — dropping in now...', function () { socket.emit('auto_start'); });
+ else begin('🛡️ Ranked — queued for the next block...', function () { socket.emit('join_queue'); });
+ });
+ $('#ecBuy').on('click', pay);
+ }
- $overlay.append($modal);
+ $overlay.append($modal).on('click', function (e) { if (e.target === this) $('#entryChoiceOverlay').remove(); });
$('body').append($overlay);
-
- // Handlers
- $('#entryChoiceNow').on('click', function() {
- $('#entryChoiceOverlay').remove();
- socket.emit('auto_start');
- $('#messages').append($(' ').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($(' ').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();
- }
- });
+ render();
}
};