JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
<div class="rules-page"> <!-- ── TABS ── --> <div class="rules-tabs-wrap"> <button class="rules-tab rules-tab-active" id="tab-bj" onclick="rulesShowTab('bj')"> <span class="rtab-icon">🃏</span> <span class="rtab-label">Blackjack Rules</span> </button> <button class="rules-tab" id="tab-game" onclick="rulesShowTab('game')"> <span class="rtab-icon">🎮</span> <span class="rtab-label">How to Play — 21 Desires</span> </button> </div> <!-- ══════════════════════════════ TAB : BLACKJACK ══════════════════════════════ --> <div id="rules-panel-bj"> <div class="rules-header"> <h1 class="rules-title">📖 BLACKJACK <span>RULES</span></h1> <p class="rules-tagline">Everything you need to know to play like a pro</p> </div> <!-- OBJECTIF --> <div class="rules-section"> <div class="rules-section-title">🎯 Objective</div> <div class="rules-card rules-card-wide"> <div class="rules-visual"> <div class="rv-hand"> <div class="rv-card rv-card-red">A<span>♥</span></div> <div class="rv-card">K<span>♠</span></div> </div> <div class="rv-arrow">VS</div> <div class="rv-hand"> <div class="rv-card">7<span>♣</span></div> <div class="rv-card rv-card-red">8<span>♦</span></div> <div class="rv-card">6<span>♠</span></div> </div> </div> <div class="rules-text"> <p>Get closer to <strong>21</strong> than the dealer — without going over. Go over 21 and you <strong>bust</strong> and lose your bet immediately.</p> <div class="rules-chips"> <span class="rc-chip rc-good">Player 21 ✓</span> <span class="rc-chip rc-good">Dealer 21 = Push</span> <span class="rc-chip rc-bad">Over 21 = Bust 💀</span> </div> </div> </div> </div> <!-- VALEUR DES CARTES --> <div class="rules-section"> <div class="rules-section-title">🃏 Card Values</div> <div class="rules-values-grid"> <div class="rv-val-block"> <div class="rv-cards-row"> <div class="rv-card rv-sm">2<span>♠</span></div> <div class="rv-card rv-sm rv-card-red">3<span>♥</span></div> <div class="rv-card rv-sm">4<span>♣</span></div> <span class="rv-dots">···</span> <div class="rv-card rv-sm rv-card-red">9<span>♦</span></div> </div> <div class="rv-val-label">Face value</div> </div> <div class="rv-val-block"> <div class="rv-cards-row"> <div class="rv-card rv-sm">J<span>♠</span></div> <div class="rv-card rv-sm rv-card-red">Q<span>♥</span></div> <div class="rv-card rv-sm">K<span>♣</span></div> </div> <div class="rv-val-label rv-val-big">= 10</div> </div> <div class="rv-val-block rv-val-ace"> <div class="rv-cards-row"> <div class="rv-card rv-sm rv-card-ace">A<span>♠</span></div> </div> <div class="rv-val-label">= <strong>1 or 11</strong></div> <div class="rv-val-sub">Auto-adjusts to avoid bust</div> </div> </div> </div> <!-- BLACKJACK --> <div class="rules-section"> <div class="rules-section-title">⭐ Blackjack — Best Hand</div> <div class="rules-card rules-card-highlight"> <div class="rules-visual"> <div class="rv-hand rv-hand-bj"> <div class="rv-card rv-card-red rv-card-lg">A<span>♥</span></div> <div class="rv-card rv-card-lg">K<span>♠</span></div> <div class="rv-bj-badge">BJ ⭐</div> </div> </div> <div class="rules-text"> <p>An <strong>Ace + any 10-value card</strong> on your first two cards = Blackjack.</p> <p>Blackjack pays <strong>3:2</strong> — bet $100, win $150. And you earn a <strong>free pack</strong>! 🎴</p> <div class="rules-chips"> <span class="rc-chip rc-gold">Instant win</span> <span class="rc-chip rc-gold">3:2 payout</span> <span class="rc-chip rc-gold">Free pack 🎴</span> </div> </div> </div> </div> <!-- ACTIONS --> <div class="rules-section"> <div class="rules-section-title">🎮 Your Actions</div> <div class="rules-actions-grid"> <div class="rules-action-card"> <div class="rac-icon">👆</div> <div class="rac-name">HIT</div> <div class="rv-hand rv-hand-sm"> <div class="rv-card rv-sm">7<span>♠</span></div> <div class="rv-card rv-sm rv-card-red">5<span>♥</span></div> <div class="rv-card rv-sm rv-card-new">3<span>♣</span></div> </div> <p class="rac-desc">Draw another card. Keep hitting until you're happy or you bust.</p> </div> <div class="rules-action-card"> <div class="rac-icon">✋</div> <div class="rac-name">STAND</div> <div class="rv-hand rv-hand-sm"> <div class="rv-card rv-sm rv-card-red">Q<span>♦</span></div> <div class="rv-card rv-sm">8<span>♣</span></div> </div> <p class="rac-desc">Keep your current hand. The dealer then plays their turn.</p> </div> <div class="rules-action-card rules-action-card-gold"> <div class="rac-icon">✖️2</div> <div class="rac-name">DOUBLE DOWN</div> <div class="rv-hand rv-hand-sm"> <div class="rv-card rv-sm rv-card-red">A<span>♥</span></div> <div class="rv-card rv-sm">6<span>♠</span></div> <div class="rv-card rv-sm rv-card-facedown">?</div> </div> <p class="rac-desc">Double your bet and receive exactly <strong>one</strong> more card. High risk, high reward.</p> <div class="rac-condition">Only on first two cards</div> </div> <div class="rules-action-card rules-action-card-purple"> <div class="rac-icon">✂️</div> <div class="rac-name">SPLIT</div> <div class="rv-split-visual"> <div class="rv-hand rv-hand-sm"> <div class="rv-card rv-sm">8<span>♠</span></div> <div class="rv-card rv-sm rv-card-new">4<span>♣</span></div> </div> <div class="rv-split-arrow">⟵ ⟶</div> <div class="rv-hand rv-hand-sm"> <div class="rv-card rv-sm rv-card-red">8<span>♥</span></div> <div class="rv-card rv-sm rv-card-new">K<span>♦</span></div> </div> </div> <p class="rac-desc">If both cards are the same value, split into <strong>two separate hands</strong>. Each needs its own bet.</p> <div class="rac-condition">Only on matching pairs</div> </div> </div> </div> <!-- DEALER --> <div class="rules-section"> <div class="rules-section-title">🤖 The Dealer</div> <div class="rules-card"> <div class="rules-visual rules-visual-col"> <div class="rv-rule-row"> <div class="rv-card rv-sm">7<span>♣</span></div> <div class="rv-card rv-sm rv-card-facedown">?</div> <span class="rv-rule-label">Dealer shows one card face-down</span> </div> <div class="rv-rule-row"> <div class="rv-rule-icon">📏</div> <span class="rv-rule-label">Dealer <strong>must hit</strong> on 16 or below</span> </div> <div class="rv-rule-row"> <div class="rv-rule-icon">🛑</div> <span class="rv-rule-label">Dealer <strong>must stand</strong> on 17 or above</span> </div> </div> </div> </div> <!-- OUTCOMES --> <div class="rules-section"> <div class="rules-section-title">💰 Payouts</div> <div class="rules-payouts-grid"> <div class="rp-row rp-win"> <span class="rp-icon">✅</span> <span class="rp-label">Win</span> <span class="rp-value">1 : 1 · Bet $100 → +$100</span> </div> <div class="rp-row rp-bj"> <span class="rp-icon">⭐</span> <span class="rp-label">Blackjack</span> <span class="rp-value">3 : 2 · Bet $100 → +$150</span> </div> <div class="rp-row rp-push"> <span class="rp-icon">🤝</span> <span class="rp-label">Push (Tie)</span> <span class="rp-value">0 : 0 · Bet returned</span> </div> <div class="rp-row rp-lose"> <span class="rp-icon">❌</span> <span class="rp-label">Lose / Bust</span> <span class="rp-value">-1 : 1 · Bet $100 → -$100</span> </div> </div> </div> <!-- TOKENS --> <div class="rules-section"> <div class="rules-section-title">🎫 Token System</div> <div class="rules-card rules-card-token"> <div class="rules-text"> <p>Every time you <strong>win a hand</strong>, you earn tokens for the table you played at.</p> <div class="rules-token-formula"> <span class="rtf-label">Winnings</span> <span class="rtf-op">÷ 10</span> <span class="rtf-eq">=</span> <span class="rtf-result">🎫 Tokens</span> </div> <p class="rtf-example">Win $200 → earn <strong>20 tokens</strong> for that table</p> <p>Tokens are spent in the <strong>Card Shop</strong> to open packs. They're separate per table and never reset.</p> </div> </div> </div> </div><!-- end #rules-panel-bj --> <!-- ══════════════════════════════ TAB : GAME RULES ══════════════════════════════ --> <div id="rules-panel-game" style="display:none;"> <div class="rules-header"> <h1 class="rules-title">🎮 THE <span>21 DESIRES</span></h1> <p class="rules-tagline">The complete game guide</p> </div> <!-- 01 BLACKJACK --> <div class="rules-section"> <div class="rules-section-title">01 — Play Blackjack</div> <div class="rules-card rules-card-wide"> <div class="rules-text"> <p>Pick a table in the Casino, set your bet and beat the dealer. Hit, Stand, Double Down or Split — standard Blackjack rules apply. Stay under 22. Dealer hits below 17.</p> <div class="rules-chips"> <span class="rc-chip rc-good">$50 · $100 · $250 bets</span> <span class="rc-chip rc-gold">Blackjack = 3:2 + free pack 🎴</span> <span class="rc-chip rc-good">Start with $1,500</span> <span class="rc-chip rc-bad">Below $50 = Game Over 💀</span> </div> </div> </div> </div> <!-- 02 TOKENS --> <div class="rules-section"> <div class="rules-section-title">02 — Earn Tokens</div> <div class="rules-card rules-card-token"> <div class="rules-text"> <p>Win a hand → earn tokens for that table. Tokens are <strong>per-table</strong> and never reset unless you hit Game Over.</p> <div class="rules-token-formula"> <span class="rtf-label">Winnings</span> <span class="rtf-op">÷ 10</span> <span class="rtf-eq">=</span> <span class="rtf-result">🎫 Tokens</span> </div> <p class="rtf-example">$50 bet → +10 🎫 · $100 → +20 🎫 · $250 → +50 🎫</p> </div> </div> </div> <!-- 03 TABLES --> <div class="rules-section"> <div class="rules-section-title">03 — Unlock Tables</div> <div class="rules-card rules-card-wide"> <div class="rules-text"> <p>Tables unlock as your <strong>total earnings</strong> grow. Each table has its own card packs with increasingly explicit content.</p> <div class="rules-payouts-grid" style="margin-top:8px;"> <div class="rp-row rp-win"><span class="rp-icon">🃏</span><span class="rp-label">The Vice</span><span class="rp-value">Available at start</span></div> <div class="rp-row rp-win"><span class="rp-icon">💋</span><span class="rp-label">The Boudoir</span><span class="rp-value">Earn $5,000</span></div> <div class="rp-row rp-bj"><span class="rp-icon">🔥</span><span class="rp-label">The Heat</span><span class="rp-value">Earn $20,000 ✦ Coming Soon</span></div> <div class="rp-row rp-bj"><span class="rp-icon">💀</span><span class="rp-label">The Slut Pit</span><span class="rp-value">Earn $30,000 ✦ Coming Soon</span></div> <div class="rp-row rp-lose"><span class="rp-icon">👑</span><span class="rp-label">Hetero VIP</span><span class="rp-value">Earn $75,000 ✦ Coming Soon</span></div> </div> </div> </div> </div> <!-- 04 PACKS --> <div class="rules-section"> <div class="rules-section-title">04 — Open Packs & Collect</div> <div class="rules-card rules-card-highlight"> <div class="rules-text"> <p>Spend tokens in the <strong>Card Shop</strong> to open packs. Each pack reveals <strong>3 animated adult GIF cards</strong> one by one. Cards are drawn randomly by rarity weight.</p> <div class="rules-chips" style="margin-top:6px;"> <span class="rc-chip" style="background:rgba(160,160,176,0.12);color:#a0a0b0;border-color:rgba(160,160,176,0.25);">⚪ Common — 60%</span> <span class="rc-chip" style="background:rgba(79,195,247,0.1);color:#4fc3f7;border-color:rgba(79,195,247,0.28);">🔵 Rare — 25%</span> <span class="rc-chip" style="background:rgba(191,0,255,0.1);color:#c084fc;border-color:rgba(191,0,255,0.28);">🟣 Epic — 12%</span> <span class="rc-chip rc-gold">🌟 Legendary — 3%</span> </div> <p style="margin-top:8px;">Duplicate cards stack in your collection. Sell extras in <strong>Sell Cards</strong> for cash : ⚪ $10 · 🔵 $30 · 🟣 $80 · 🌟 $200.</p> </div> </div> </div> <!-- 05 LUCKY WHEEL --> <div class="rules-section"> <div class="rules-section-title">05 — Lucky Wheel</div> <div class="rules-card"> <div class="rules-text"> <p>Every <strong>20 hands played</strong>, the Lucky Wheel lights up in the sidebar. Spin it for free — you always win something.</p> <div class="rules-payouts-grid" style="margin-top:6px;"> <div class="rp-row rp-win"><span class="rp-icon">💰</span><span class="rp-label">Cash</span><span class="rp-value">$100 / $150 / $200 / $300 / $500★</span></div> <div class="rp-row rp-win"><span class="rp-icon">🎫</span><span class="rp-label">Tokens</span><span class="rp-value">50 / 75 / 100 — choose your table</span></div> <div class="rp-row rp-gold"><span class="rp-icon">🎴</span><span class="rp-label">Free Pack</span><span class="rp-value">Choose your table</span></div> </div> </div> </div> </div> <!-- 06 GAME OVER --> <div class="rules-section"> <div class="rules-section-title">06 — Game Over</div> <div class="rules-card"> <div class="rules-text"> <p>Balance drops below <strong>$50</strong> → Game Over. Your balance resets to <strong>$1,500</strong>, but you lose a portion of your collection and tokens. <strong>Penalties escalate</strong> with each game over.</p> <div class="rules-payouts-grid" style="margin-top:6px;"> <div class="rp-row rp-push"><span class="rp-icon">1️⃣</span><span class="rp-label">1st Game Over</span><span class="rp-value">Lose 1/4 of collection & tokens</span></div> <div class="rp-row rp-push"><span class="rp-icon">2️⃣</span><span class="rp-label">2nd</span><span class="rp-value">Lose 1/3</span></div> <div class="rp-row rp-bj"><span class="rp-icon">3️⃣</span><span class="rp-label">3rd</span><span class="rp-value">Lose 1/2</span></div> <div class="rp-row rp-bj"><span class="rp-icon">4️⃣</span><span class="rp-label">4th</span><span class="rp-value">Lose 3/4</span></div> <div class="rp-row rp-lose"><span class="rp-icon">💀</span><span class="rp-label">5th+</span><span class="rp-value">Lose EVERYTHING</span></div> </div> </div> </div> </div> <!-- 07 SELL --> <div class="rules-section"> <div class="rules-section-title">07 — Sell Duplicates</div> <div class="rules-card rules-card-token"> <div class="rules-text"> <p>Got multiple copies of the same card? Head to <strong>Sell Cards</strong> in the sidebar. You always keep 1 copy — only extras can be sold. Cash goes directly to your balance.</p> <div class="rules-chips"> <span class="rc-chip" style="background:rgba(160,160,176,0.1);color:#a0a0b0;border-color:rgba(160,160,176,0.2);">⚪ Common — $10</span> <span class="rc-chip" style="background:rgba(79,195,247,0.08);color:#4fc3f7;border-color:rgba(79,195,247,0.22);">🔵 Rare — $30</span> <span class="rc-chip" style="background:rgba(191,0,255,0.08);color:#c084fc;border-color:rgba(191,0,255,0.22);">🟣 Epic — $80</span> <span class="rc-chip rc-gold">🌟 Legendary — $200</span> </div> </div> </div> </div> </div><!-- end #rules-panel-game --> </div> <<done>> <<script>> (function() { var navLinks = document.querySelectorAll('.casino-nav a'); navLinks.forEach(function(l) { l.classList.add('btn','btn-stand'); }); /* Tab switch function — global scope so onclick works */ window.rulesShowTab = function(tab) { document.getElementById('rules-panel-bj').style.display = tab === 'bj' ? '' : 'none'; document.getElementById('rules-panel-game').style.display = tab === 'game' ? '' : 'none'; document.getElementById('tab-bj').classList.toggle('rules-tab-active', tab === 'bj'); document.getElementById('tab-game').classList.toggle('rules-tab-active', tab === 'game'); }; /* Check if we arrived via a "game rules" deep-link */ if (window._rulesOpenTab === 'game') { window.rulesShowTab('game'); delete window._rulesOpenTab; } })(); <</script>> <</done>> <style> /* ── Rules Tabs ── */ .rules-tabs-wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 0; width: 100%; box-sizing: border-box; margin-bottom: 16px; border-radius: 14px; overflow: hidden; border: 1px solid rgba(255,255,255,0.1); } .rules-tab { font-family: 'Orbitron', monospace; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.1em; padding: 18px 20px; border: none; background: rgba(255,255,255,0.03); color: rgba(210,200,235,0.4); cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; gap: 9px; border-right: 1px solid rgba(255,255,255,0.08); } .rules-tab:last-child { border-right: none; } .rtab-icon { font-size: 1.3rem; } .rtab-label { line-height: 1.3; } .rules-tab:hover { background: rgba(255,255,255,0.07); color: rgba(210,200,235,0.75); } .rules-tab-active { background: rgba(255,45,120,0.14) !important; color: var(--neon-pink) !important; box-shadow: inset 0 -3px 0 var(--neon-pink); } </style>
<<silently>><<set $gameState = "betting">><</silently>> <div id="table-banner"></div> <div id="streak-bar-wrap"></div> <<if $playerMoney >= 50>> <div class="betting-screen"> <div class="betting-hero"> <div class="betting-hero-label">◈ Choose Your Stakes ◈</div> <h2 class="betting-hero-title">Place Your Bet</h2> <p class="betting-hero-sub">Win a hand to earn tokens — the bigger the bet, the more tokens you pocket!</p> </div> <div class="betting-grid"> <<if $playerMoney >= 50>><<link "Bet $50" "Deal Cards">><<set $currentBet = 50>><<set $baseBet = 50>><</link>><</if>> <<if $playerMoney >= 100>><<link "Bet $100" "Deal Cards">><<set $currentBet = 100>><<set $baseBet = 100>><</link>><</if>> <<if $playerMoney >= 250>><<link "Bet $250" "Deal Cards">><<set $currentBet = 250>><<set $baseBet = 250>><</link>><</if>> </div> </div> <<done>> <<script>> (function() { /* ── Bannière table dynamique ── */ var v = State.variables; var banner = document.getElementById('table-banner'); if (banner) { var tableObj = window.TABLES ? window.TABLES.find(function(t) { return t.id === v.activeTable; }) : null; var tableName = tableObj ? tableObj.name : v.activeTable; var tableEmoji = tableObj ? tableObj.emoji : '🎰'; var tableColor = tableObj ? tableObj.color : 'linear-gradient(90deg,#ff2d78,#bf00ff)'; var tokens = v.tokens ? (v.tokens[v.activeTable] || 0) : 0; var branchLabels = { hetero:'Straight', gay:'Gay', trans:'Trans' }; var branchLabel = branchLabels[v.activeBranch] || ''; banner.innerHTML = '<div class="table-banner-bar" style="background:' + tableColor + '"></div>' + '<div class="table-banner-body">' + '<div class="table-banner-left">' + '<div class="table-banner-branch">' + branchLabel + '</div>' + '<div class="table-banner-name">' + '<span class="table-banner-emoji">' + tableEmoji + '</span>' + tableName + '</div>' + '</div>' + '<div class="table-banner-right">' + '<div class="table-banner-stat">' + '<span class="tbs-label">💰 Balance</span>' + '<span class="tbs-value">' + formatMoney(v.playerMoney) + '</span>' + '</div>' + '<div class="table-banner-stat">' + '<span class="tbs-label">🎫 Tokens</span>' + '<span class="tbs-value tbs-tokens">' + tokens + '</span>' + '</div>' + '<a class="table-banner-back" id="tbn-back-casino">← Casino</a>' + '</div>' + '</div>'; var backBtn = document.getElementById('tbn-back-casino'); if (backBtn) backBtn.addEventListener('click', function() { Engine.play('Casino'); }); } /* Bet amount → tokens si victoire normale (mise x2, /10) */ const betData = [ { amount: 50, tokens: 10, label: '💵 Standard', desc: 'Safe play', cls: '' }, { amount: 100, tokens: 20, label: '🔥 High Roll', desc: 'Go bold', cls: 'bet-btn-hot' }, { amount: 250, tokens: 50, label: '💎 All In', desc: 'Fortune favors', cls: 'bet-btn-vip' } ]; const grid = document.querySelector('.betting-grid'); if (!grid) return; const eligible = betData.filter(b => b.amount <= v.playerMoney); const links = grid.querySelectorAll('a'); links.forEach((link, i) => { const bet = eligible[i]; if (!bet) return; link.classList.add('bet-btn'); if (bet.cls) link.classList.add(bet.cls); link.innerHTML = '<span class="bet-label">' + bet.label + '</span>' + '<span class="bet-amount">$' + bet.amount + '</span>' + '<span class="bet-desc">' + bet.desc + '</span>' + '<span class="bet-tokens">🎫 +' + bet.tokens + ' tokens if win</span>'; }); var _TBG = { 'vice': 'Images/scenes/the_vice.png', 'boudoir': 'Images/scenes/the_boudoir.png', 'heat': 'Images/scenes/the_heat.png', 'slutpit': 'Images/scenes/the_slut_pit.png', 'heterovip': 'Images/scenes/the_hetero_vip.png', 'rainbow': 'Images/scenes/the_rainbow.png', 'gayvip': 'Images/scenes/the_gay_vip.png', 'transroom': 'Images/scenes/the_trans_room.png', 'transvip': 'Images/scenes/the_trans_vip.png', 'penthouse': 'Images/scenes/the_penthouse.png' }; (function applyTableBg() { var at = State.variables.activeTable; var img = at && _TBG[at] ? _TBG[at] : null; document.body.style.backgroundImage = img ? 'linear-gradient(rgba(0,0,0,0.62),rgba(0,0,0,0.55),rgba(0,0,0,0.62)), url("' + img + '")' : ''; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundPosition = 'center'; document.body.style.backgroundAttachment = 'fixed'; })(); window.showFirstVisitTip('blackjack_table', { icon: '🃏', title: 'Place Your Bet', body: 'Choose how much you want to wager before the cards are dealt. The higher the bet, the more tokens you earn on a win.', bodyScarlett: 'Before we start — think carefully about your bet. The bigger the risk, the more tokens you walk away with. I like players who aren\'t afraid.', tips: [ '💰 $50 → +10 tokens on win', '💰 $100 → +20 tokens on win', '💰 $250 → +50 tokens on win', '⭐ Blackjack pays 3:2 and grants a free pack' ] }); /* ── STREAK BAR ── */ (function() { var wrap = document.getElementById('streak-bar-wrap'); if (!wrap) return; var streak = (v.currentStreak && v.currentStreak[v.activeTable]) ? v.currentStreak[v.activeTable] : 0; var milestones = [3, 5, 7, 10]; var icons = ['🔥','🔥','🔥','👑']; var labels = ['+20 tkn', '+$150', 'Free Pack', 'New Pack +$300']; var segments = milestones.map(function(m, i) { var reached = streak >= m; var active = streak === m; return '<div class="streak-seg' + (reached ? ' streak-seg-done' : '') + (active ? ' streak-seg-active' : '') + '">' + '<div class="streak-seg-icon">' + icons[i] + '</div>' + '<div class="streak-seg-num">×' + m + '</div>' + '<div class="streak-seg-label">' + labels[i] + '</div>' + '</div>'; }).join('<div class="streak-line' + (streak > 0 ? ' streak-line-active' : '') + '"></div>'); wrap.innerHTML = '<div id="streak-bar">' + '<div class="streak-title">' + (streak > 0 ? '🔥 <span class="streak-count">' + streak + '</span> win streak' : '🔥 Win Streak') + '</div>' + '<div class="streak-milestones">' + segments + '</div>' + '</div>'; })(); })(); <</script>> <</done>> <<else>> <<goto "Game Over">> <</if>>
<div class="casino-main"> <div class="section-title" id="shop-title">🛍️ Card Shop</div> <div class="shop-rarity-legend"> <span class="rchip rchip-c">⚪ Common 60%</span> <span class="rchip rchip-r">🔵 Rare 25%</span> <span class="rchip rchip-e">🟣 Epic 12%</span> <span class="rchip rchip-l">🌟 Legendary 3%</span> </div> <div id="shop-container"></div> </div> <<done>> <<script>> (function() { var v = State.variables; var container = document.getElementById('shop-container'); if (!container) return; /* Titre dynamique selon branche */ var branchLabels = { hetero:'🔥 Straight — Card Shop', gay:'🌈 Gay — Card Shop', trans:'💜 Trans — Card Shop' }; var shopTitle = document.getElementById('shop-title'); if (shopTitle) shopTitle.textContent = branchLabels[v.activeBranch] || '🛍️ Card Shop'; /* Filtrer les tables selon la branche active Penthouse visible uniquement si les deux VIP sont débloqués */ var branchFilter = v.activeBranch; /* Tables disponibles vs coming soon */ var COMING_SOON = ['heat', 'slutpit', 'heterovip', 'rainbow', 'gayvip', 'transroom', 'transvip', 'penthouse']; TABLES.forEach(function(table) { if (table.branch !== branchFilter && table.branch !== 'both') return; if (table.branch === 'both') { if (!v.unlockedTables.includes('heterovip') || !v.unlockedTablesGay.includes('gayvip')) return; } var tablePacks = PACKS.filter(function(p) { return p.table === table.id; }); if (!tablePacks.length) return; var isComingSoon = COMING_SOON.indexOf(table.id) !== -1; var isUnlocked = ( table.branch === 'hetero' ? v.unlockedTables.indexOf(table.id) !== -1 : table.branch === 'gay' ? v.unlockedTablesGay.indexOf(table.id) !== -1 : table.branch === 'trans' ? v.unlockedTablesTrans.indexOf(table.id) !== -1 : v.unlockedTables.indexOf('penthouse') !== -1 ); var section = document.createElement('div'); /* ── COMING SOON ── */ if (isComingSoon) { section.className = 'shop-table-section shop-section-cs'; section.innerHTML = '<div class="shop-table-header">' + '<div class="shop-table-bar" style="background:' + table.color + ';opacity:0.25"></div>' + '<span class="shop-table-emoji" style="filter:grayscale(0.6) opacity(0.5)">' + table.emoji + '</span>' + '<span class="shop-table-name" style="opacity:0.4">' + table.name + '</span>' + '<span class="badge-cs" style="margin-left:auto;">✦ Coming Soon</span>' + '</div>' + '<div class="shop-cs-block">' + '<div class="shop-cs-icon">🔮</div>' + '<div class="shop-cs-title">' + table.name + '</div>' + '<div class="shop-cs-sub">' + tablePacks.length + ' pack' + (tablePacks.length > 1 ? 's' : '') + ' · ' + tablePacks.reduce(function(acc, p) { return acc + CARD_DB.filter(function(c) { return p.categories.indexOf(c.category) !== -1; }).length; }, 0) + ' cards incoming</div>' + '<div class="shop-cs-cats">' + tablePacks[0].categories.map(function(tag) { return '<span class="cat-tag cat-tag-cs">' + categoryLabel(tag) + '</span>'; }).join('') + '</div>' + '</div>'; container.appendChild(section); return; } /* ── TABLE NORMALE ── */ section.className = 'shop-table-section' + (isUnlocked ? '' : ' shop-section-locked'); section.innerHTML = '<div class="shop-table-header">' + '<div class="shop-table-bar" style="background:' + table.color + (isUnlocked ? '' : ';opacity:0.3') + '"></div>' + '<span class="shop-table-emoji">' + table.emoji + '</span>' + '<span class="shop-table-name">' + table.name + '</span>' + (!isUnlocked ? '<span class="badge badge-locked" style="margin-left:auto;">🔒 Locked</span>' : '') + '</div>'; /* Afficher le solde de jetons pour cette table */ if (!v.tokens) v.tokens = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }; var tableTokenBalance = v.tokens[table.id] || 0; if (isUnlocked) { var tokenDisplay = document.createElement('div'); tokenDisplay.className = 'shop-token-balance'; tokenDisplay.innerHTML = table.emoji + ' <span class="stb-count">' + tableTokenBalance + '</span> tokens available'; section.appendChild(tokenDisplay); } tablePacks.forEach(function(pack) { var tableTokens = v.tokens[pack.table] || 0; var canAfford = tableTokens >= pack.price; /* Compter cartes uniques possédées dans les catégories de ce pack */ var owned = CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1 && v.collection[c.id] !== undefined; }).length; var total = CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1; }).length; var card = document.createElement('div'); card.className = 'pack-card' + (!isUnlocked ? ' pack-locked' : ''); var catTags = pack.categories.map(function(tag) { return '<span class="cat-tag">' + categoryLabel(tag) + '</span>'; }).join(''); var actionHtml = ''; if (!isUnlocked) { actionHtml = '<div class="pack-lock-msg">🔒 Unlock ' + table.name + ' to access</div>'; } else if (!canAfford) { actionHtml = '<button class="btn btn-pack-buy" disabled>' + table.emoji + ' ' + pack.price + ' tokens — Need ' + Math.max(0, pack.price - tableTokens) + ' more</button>'; } else { actionHtml = '<button class="btn btn-pack-buy btn-primary" data-pack="' + pack.id + '">🎴 Open Pack — ' + table.emoji + ' ' + pack.price + ' tokens</button>'; if (pack.forcedRarity) { actionHtml = '<button class="btn btn-pack-buy btn-gold" data-pack="' + pack.id + '">🌟 Legendary Pack — ' + table.emoji + ' ' + pack.price + ' tokens</button>'; } } card.innerHTML = '<div class="pack-header">' + '<span class="pack-emoji">' + pack.emoji + '</span>' + '<div>' + '<div class="pack-name">' + pack.name + '</div>' + '<div class="pack-cats">' + catTags + '</div>' + '</div>' + '</div>' + '<div class="pack-progress-row">' + '<span class="pack-owned">' + owned + ' / ' + total + ' unique</span>' + '<div class="pack-prog-bar"><div class="pack-prog-fill" style="width:' + (total > 0 ? Math.round(owned/total*100) : 0) + '%"></div></div>' + '</div>' + '<div class="pack-action">' + actionHtml + '</div>'; section.appendChild(card); }); container.appendChild(section); }); /* Achat */ container.querySelectorAll('.btn-pack-buy[data-pack]').forEach(function(btn) { btn.addEventListener('click', function() { var packId = this.getAttribute('data-pack'); var result = openPack(packId); if (result === true) { Engine.play('Open Pack'); } else if (result === 'empty') { alert('⚠️ No cards available in this pack yet. Add cards to CARD_DB first.'); } else { alert('⚠️ Cannot open pack — check your balance or table access.'); } }); }); })(); window.showFirstVisitTip('card_shop', { icon: '🛍️', title: 'Card Shop', body: 'Spend the tokens you earn at each table to open card packs. Each pack reveals 3 animated adult cards.', bodyScarlett: 'Spend your tokens wisely. Every pack you open brings you closer to my rarest cards — and trust me, the Legendaries are worth chasing.', tips: [ '🎫 Tokens are earned by winning hands', '🎴 Each pack reveals 3 cards with random rarity', '⚪🔵🟣🌟 Common · Rare · Epic · Legendary', '📦 Each table has its own exclusive pack' ] }); <</script>> <</done>>
<div class="casino-main"> <div class="section-title" id="casino-world-title">🎰 Available Tables</div> <div id="tables-container"></div> </div> <<done>> <<script>> (function() { var v = State.variables; /* ── Vérification des déblocages de tables au chargement du Casino ── */ if (v.activeBranch === 'hetero' || !v.activeBranch) { if (v.earnedHetero >= 5000 && v.unlockedTables.indexOf('boudoir') === -1) v.unlockedTables.push('boudoir'); if (v.earnedHetero >= 20000 && v.unlockedTables.indexOf('heat') === -1) v.unlockedTables.push('heat'); if (v.earnedHetero >= 30000 && v.unlockedTables.indexOf('slutpit') === -1) v.unlockedTables.push('slutpit'); if (v.earnedHetero >= 75000 && v.unlockedTables.indexOf('heterovip') === -1) v.unlockedTables.push('heterovip'); } if (v.gayUnlocked) { if (v.earnedGay >= 30000 && v.unlockedTablesGay.indexOf('gayvip') === -1) v.unlockedTablesGay.push('gayvip'); } if (v.transUnlocked) { if (v.earnedTrans >= 30000 && v.unlockedTablesTrans.indexOf('transvip') === -1) v.unlockedTablesTrans.push('transvip'); } if (v.unlockedTables.indexOf('heterovip') !== -1 && v.gayUnlocked && v.unlockedTablesGay.indexOf('gayvip') !== -1 && v.totalEarned >= 200000) { if (v.unlockedTables.indexOf('penthouse') === -1) v.unlockedTables.push('penthouse'); } var container = document.getElementById('tables-container'); if (!container) return; /* World title */ var worldTitle = document.getElementById('casino-world-title'); var worldLabels = { hetero:'🔥 Straight — Available Tables', gay:'🌈 Gay — Available Tables', trans:'💜 Trans — Available Tables' }; if (worldTitle) worldTitle.textContent = worldLabels[v.activeBranch] || '🎰 Available Tables'; function renderTable(t, isUnlocked, earned) { var card = document.createElement('div'); card.className = 'table-card' + (isUnlocked ? '' : ' table-locked'); if (isUnlocked) card.id = 'tbl-' + t.id; var pack = PACKS.find(function(p) { return p.table === t.id && !p.forcedRarity; }); var packPrice = pack ? formatMoney(pack.price) : ''; var accentBar = '<div class="table-accent-bar" style="background:' + t.color + (isUnlocked ? '' : ';opacity:0.3') + '"></div>'; var body = ''; if (isUnlocked) { var badge = (t.id === 'heterovip' || t.id === 'gayvip' || t.id === 'transvip' || t.id === 'penthouse') ? '<span class="badge badge-legendary">★ VIP</span>' : '<span class="badge badge-open">✦ Open</span>'; body = '<span class="table-icon">' + t.emoji + '</span>' + '<div class="table-name">' + t.name + '</div>' + '<div class="table-tokens-display">' + t.emoji + ' ' + (State.variables.tokens ? (State.variables.tokens[t.id] || 0) : 0) + ' tokens</div>' + badge; } else { var CASINO_CS = ['heat','slutpit','heterovip','rainbow','gayvip','transroom','transvip','penthouse']; var isCasinoCS = CASINO_CS.indexOf(t.id) !== -1; var pct = t.threshold > 0 ? Math.min(100, Math.round(earned / t.threshold * 100)) : 100; var need = formatMoney(t.threshold); body = '<span class="table-icon" style="' + (isCasinoCS ? 'filter:grayscale(0.5) opacity(0.6)' : '') + '">' + t.emoji + '</span>' + '<div class="table-name" style="' + (isCasinoCS ? 'opacity:0.5' : '') + '">' + t.name + '</div>' + (isCasinoCS ? '<div class="table-cs-badge">✦ Coming Soon</div>' : '<div class="table-desc">Unlocks at <strong>' + need + '</strong> earned in this branch.</div>' + '<div class="unlock-bar-wrap"><div class="unlock-bar-fill" style="width:' + pct + '%;background:' + t.color + '"></div></div>' + '<div class="table-meta">' + formatMoney(earned) + ' / ' + need + '</div>' ) + '<span class="badge badge-locked">🔒 Locked</span>'; } card.innerHTML = accentBar + body; if (isUnlocked) { card.addEventListener('click', function() { v.activeTable = t.id; v.activeBranch = t.branch === 'both' ? v.activeBranch : t.branch; v.gameState = 'betting'; Engine.play('Blackjack Table'); }); } return card; } if (v.activeBranch === 'hetero') { var sec = document.createElement('div'); sec.className = 'branch-section'; sec.innerHTML = '<div class="branch-header hetero-header">🔥 Straight Branch <span class="branch-earned">Earned : ' + formatMoney(v.earnedHetero) + '</span></div>'; TABLES.filter(function(t) { return t.branch === 'hetero'; }).forEach(function(t) { sec.appendChild(renderTable(t, v.unlockedTables.indexOf(t.id) !== -1, v.earnedHetero)); }); container.appendChild(sec); } else if (v.activeBranch === 'gay') { var sec = document.createElement('div'); sec.className = 'branch-section'; sec.innerHTML = '<div class="branch-header gay-header">🌈 Gay Branch <span class="branch-earned">Earned : ' + formatMoney(v.earnedGay) + '</span></div>'; TABLES.filter(function(t) { return t.branch === 'gay'; }).forEach(function(t) { sec.appendChild(renderTable(t, v.unlockedTablesGay.indexOf(t.id) !== -1, v.earnedGay)); }); container.appendChild(sec); } else if (v.activeBranch === 'trans') { var sec = document.createElement('div'); sec.className = 'branch-section'; sec.innerHTML = '<div class="branch-header" style="background:linear-gradient(90deg,rgba(249,168,212,0.12),transparent);border-left:3px solid #f9a8d4;color:#f9a8d4;">💜 Trans Branch <span class="branch-earned">Earned : ' + formatMoney(v.earnedTrans) + '</span></div>'; TABLES.filter(function(t) { return t.branch === 'trans'; }).forEach(function(t) { sec.appendChild(renderTable(t, v.unlockedTablesTrans.indexOf(t.id) !== -1, v.earnedTrans)); }); container.appendChild(sec); } var pentTable = TABLES.find(function(t) { return t.id === 'penthouse'; }); if (pentTable) { var pentUnlocked = v.unlockedTables.indexOf('penthouse') !== -1; var pentSec = document.createElement('div'); pentSec.className = 'branch-section'; pentSec.innerHTML = '<div class="branch-header penthouse-header">💎 The Penthouse — Ultimate VIP</div>'; pentSec.appendChild(renderTable(pentTable, pentUnlocked, v.totalEarned)); container.appendChild(pentSec); } })(); window.showFirstVisitTip('casino', { icon: '🎰', title: 'Welcome to the Casino', body: 'This is your hub. Choose a table and start playing Blackjack to earn money and tokens.', bodyScarlett: 'Welcome. I\'m Scarlett — your guide through The 21 Desires. Pick a table, place your bet, and let\'s see what the cards have in store for you tonight.', tips: [ '🃏 Click a table to start a new hand', '🎫 Each table has its own token pool', '🔒 New tables unlock as you earn more money', '✦ Coming Soon tables are still in development' ] }); /* ── TIPS DE DÉBLOQUAGE DE TABLE ── */ (function() { var v = State.variables; if (v.activeBranch === 'hetero' && v.tableUnlockSeen) { var TABLE_UNLOCK_TIPS = { boudoir: { title: 'New Table Unlocked — The Boudoir 💋', bodyScarlett: 'You\'ve earned your way into The Boudoir. Things get a little more... intimate here. New packs, new content. Don\'t be shy.', tips: [ '💋 The Boudoir has its own exclusive card packs', '🎫 Earn tokens here by winning hands at this table', '🔞 More explicit content than The Vice', '🎴 New categories unlocked in this table\'s packs' ] }, heat: { title: 'New Table Unlocked — The Heat 🔥', bodyScarlett: 'The Heat. You\'ve come a long way. The cards here are something else — explicit, raw, and very hard to forget. You\'ve been warned.', tips: [ '🔥 The Heat features the most explicit categories so far', '🎴 New categories : Anal, Blowjob, Pussy Fucking and more', '🎫 Its own token pool — start earning here', '🌟 Legendary cards are rarer but worth everything' ] }, slutpit: { title: 'New Table Unlocked — The Slut Pit 💀', bodyScarlett: 'The Slut Pit. Most players never make it this far. The content here is... not for the faint-hearted. Enjoy.', tips: [ '💀 The Slut Pit — the most extreme content in the casino', '🎴 New categories : BBC, Creampie, Gangbang, Threesome', '🎫 Its own token pool — spend them wisely', '🌟 Highest concentration of Legendary cards' ] }, heterovip: { title: 'VIP Access Unlocked — Hetero VIP 👑', bodyScarlett: 'VIP. You\'ve earned it. This room has access to every single category — and a Legendary Pack that guarantees something special. Welcome to the top.', tips: [ '👑 VIP Pack includes all hetero categories', '🌟 Legendary Pack available — guaranteed Legendary card', '💎 The rarest cards in the game are here', '✦ You\'ve reached the top of the Straight branch' ] }, penthouse: { title: 'The Penthouse Unlocked 💎', bodyScarlett: 'The Penthouse. All branches, all categories, no limits. This is the highest table in the house — and it belongs to you now.', tips: [ '💎 The Penthouse draws from every category in the game', '🌟 Legendary Pack available — all categories included', '✦ The ultimate table — you\'ve unlocked everything', '🎴 Wild Card packs can drop anything' ] } }; var checkTables = ['boudoir', 'heat', 'slutpit', 'heterovip', 'penthouse']; for (var i = 0; i < checkTables.length; i++) { var tbl = checkTables[i]; if ( v.unlockedTables.indexOf(tbl) !== -1 && !v.tableUnlockSeen[tbl] ) { v.tableUnlockSeen[tbl] = true; var tipCfg = TABLE_UNLOCK_TIPS[tbl]; (function(cfg) { setTimeout(function() { window.showFirstVisitTip('table_unlock_' + tbl, { icon: '🔓', title: cfg.title, body: cfg.title, bodyScarlett: cfg.bodyScarlett, tips: cfg.tips }); }, 800); })(tipCfg); break; /* Un seul tip à la fois */ } } } })(); <</script>> <</done>>
<div class="credits-page"> <div class="credits-content"> <div class="credits-logo-wrap"> <img src="Images/credits/credit_logo.png" class="credits-logo" alt="Draggora Studio Logo"> </div> <div class="credits-studio-name">DRAGGORA STUDIO</div> <div class="credits-tagline">Independent Adult Game Developer</div> <div class="credits-divider"></div> <div class="credits-section"> <div class="credits-section-title">🎮 The 21 Desires</div> <div class="credits-game-info"> <span class="credits-version">Version 0.1.1</span> <span class="credits-year">© 2026 Draggora Studio</span> </div> <p class="credits-desc">A blackjack card-collecting adult game. Play, earn, unlock and collect explicit animated cards across multiple tables and categories.</p> </div> <div class="credits-divider"></div> <div class="credits-section"> <div class="credits-section-title">🔗 Find us</div> <div class="credits-links"> <a class="credits-link credits-link-patreon" href="https://patreon.com/Draggora_Studio?utm_medium=unknown&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink" target="_blank"> <span class="cl-icon">🎁</span> <span class="cl-body"> <span class="cl-label">Patreon</span> <span class="cl-sub">Support the project & get early access</span> </span> <span class="cl-arrow">→</span> </a> <a class="credits-link credits-link-itch" href="https://draggora-studio.itch.io/the-21-desires" target="_blank"> <span class="cl-icon">🎮</span> <span class="cl-body"> <span class="cl-label">itch.io</span> <span class="cl-sub">Download & follow updates</span> </span> <span class="cl-arrow">→</span> </a> <a class="credits-link credits-link-mail" href="mailto:draggora_studio@outlook.fr"> <span class="cl-icon">✉️</span> <span class="cl-body"> <span class="cl-label">Contact</span> <span class="cl-sub">draggora_studio@outlook.fr</span> </span> <span class="cl-arrow">→</span> </a> </div> </div> <div class="credits-divider"></div> <div class="credits-legal"> <p>This game contains explicit adult content intended for persons aged 18 or older. All characters depicted are fictional and over the age of 18. This is a work of fiction — any resemblance to real persons or events is purely coincidental.</p> <p>Some visual content used may belong to third parties. If you are the owner of content displayed in The 21 Desires and wish to have it removed, please contact us at <strong>draggora_studio@outlook.fr</strong>.</p> </div> </div> </div> <<done>> <<script>> (function() { /* Credits page loaded */ })(); <</script>> <</done>> <style> /* ══════════════════════════════════════════ CREDITS PAGE — redesign lisible ══════════════════════════════════════════ */ .credits-page { min-height: 100vh; display: flex; flex-direction: column; background: #06040f; padding-bottom: 80px; } .credits-content { max-width: 680px; margin: 40px auto 0; padding: 0 24px; display: flex; flex-direction: column; align-items: center; gap: 0; position: relative; z-index: 2; text-align: center; } .credits-logo-wrap { margin-bottom: 20px; } .credits-logo { width: 180px; height: 180px; object-fit: contain; filter: drop-shadow(0 0 32px rgba(255,45,120,0.45)); } .credits-studio-name { font-family: 'Orbitron', monospace; font-size: 1.6rem; font-weight: 900; letter-spacing: 0.22em; background: linear-gradient(135deg, #ffd700, #ff4477, #ffd700); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 8px; } .credits-tagline { font-family: 'Share Tech Mono', monospace; font-size: 0.82rem; letter-spacing: 0.3em; color: rgba(210,200,235,0.65); text-transform: uppercase; margin-bottom: 4px; } .credits-divider { width: 100%; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,215,0,0.25), transparent); margin: 28px 0; } .credits-section { width: 100%; text-align: left; } .credits-section-title { font-family: 'Orbitron', monospace; font-size: 0.78rem; font-weight: 900; letter-spacing: 0.25em; text-transform: uppercase; color: rgba(255,215,0,0.8); margin-bottom: 14px; } .credits-game-info { display: flex; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; } .credits-version, .credits-year { font-family: 'Share Tech Mono', monospace; font-size: 0.85rem; letter-spacing: 0.1em; color: rgba(210,200,235,0.8); padding: 5px 14px; border: 1px solid rgba(255,255,255,0.12); border-radius: 20px; background: rgba(255,255,255,0.05); } .credits-desc { font-family: 'Rajdhani', sans-serif; font-size: 1.05rem; font-weight: 500; color: rgba(225,215,245,0.85); line-height: 1.7; margin: 0; } /* Links */ .credits-links { display: flex; flex-direction: column; gap: 12px; } .credits-link { display: flex; align-items: center; gap: 16px; padding: 16px 20px; border-radius: 14px; border: 1px solid rgba(255,255,255,0.1); background: rgba(255,255,255,0.04); text-decoration: none !important; transition: all 0.2s; cursor: pointer; } .credits-link:hover { transform: translateX(4px); border-color: rgba(255,255,255,0.2); } .credits-link-patreon:hover { border-color: rgba(255,105,0,0.5); background: rgba(255,105,0,0.08); } .credits-link-itch:hover { border-color: rgba(255,45,120,0.5); background: rgba(255,45,120,0.08); } .credits-link-mail:hover { border-color: rgba(0,245,255,0.45); background: rgba(0,245,255,0.06); } .cl-icon { font-size: 1.6rem; flex-shrink: 0; } .cl-body { display: flex; flex-direction: column; gap: 3px; flex: 1; } .cl-label { font-family: 'Orbitron', monospace; font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; color: #ffffff; } .cl-sub { font-family: 'Rajdhani', sans-serif; font-size: 0.95rem; font-weight: 500; color: rgba(210,200,235,0.7); } .cl-arrow { font-family: 'Share Tech Mono', monospace; font-size: 1rem; color: rgba(255,255,255,0.35); flex-shrink: 0; } /* Legal */ .credits-legal { width: 100%; padding: 18px 20px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.07); border-radius: 12px; } .credits-legal p { font-family: 'Rajdhani', sans-serif; font-size: 0.92rem; color: rgba(210,200,235,0.55); line-height: 1.7; margin: 0 0 10px; } .credits-legal p:last-child { margin: 0; } .credits-legal strong { color: rgba(210,200,235,0.8); } </style>
<<silently>> <<set $playerHand = []>> <<set $dealerHand = []>> <<set $isSplit = false>> <<set $splitHand = []>> <<set $splitBet = 0>> <<set $currentHand = "main">> <<set $gameState = "playing">> <<set $playerMoney -= $currentBet>> <<set $playerHand.push(drawCard())>> <<set $dealerHand.push(drawCard())>> <<set $playerHand.push(drawCard())>> <<set $dealerHand.push(drawCard())>> <<set $canDouble = ($playerMoney >= $currentBet)>> <<set $canSplit = (canSplit($playerHand) && $playerMoney >= $currentBet)>> <</silently>> <<goto "Play Hand">>
<div class="game-layout"> <div class="game-table"> <div class="dealer-zone"> <div class="zone-label dealer-label">🤖 Dealer — Drawing</div> <<print displayHand($dealerHand, false)>> <<set _dv = calculateHand($dealerHand)>> <div class="hand-total">Total : <<print _dv>></div> <<if _dv < 17>><p class="dealer-thinking">⟳ dealer drawing a card...</p><</if>> </div> <div class="player-zone"> <div class="zone-label player-label">🎭 Your main hand</div> <<print displayHand($playerHand, false)>> <div class="hand-total">Total : <<print calculateHand($playerHand)>></div> <<if $isSplit>> <div style="margin-top:10px;"> <div class="zone-label player-label">✂️ Your Split hand</div> <<print displayHand($splitHand, false)>> <div class="hand-total">Total : <<print calculateHand($splitHand)>></div> </div> <</if>> </div> </div> </div> <<set _dv = calculateHand($dealerHand)>> <<if _dv < 17>> <<set $dealerHand.push(drawCard())>> <<timed 1s>><<goto "Dealer Turn">><</timed>> <<else>> <<set $gameState = "result">> <<timed 1.2s>><<goto "Game Result">><</timed>> <</if>> <<done>> <<script>> (function() { var old = document.getElementById('game-hud'); if (old) old.remove(); var v = State.variables; var hud = document.createElement('div'); hud.id = 'game-hud'; document.getElementById('passages').classList.add('game-page'); hud.innerHTML = '<div class="hud-logo">THE <span>21</span> DESIRES</div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F4B0} Balance</span><span class="hud-stat-value">' + formatMoney(v.playerMoney) + '</span></div>' + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F3B0} Bet</span><span class="hud-stat-value cyan">' + formatMoney(v.currentBet) + '</span></div>' + '<div class="hud-sep"></div>' + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F3AB} Tokens</span><span class="hud-stat-value" style="color:#facc15;text-shadow:0 0 10px rgba(250,204,21,0.4);">' + (v.tokens ? (v.tokens[v.activeTable] || 0) : 0) + '</span></div>' + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">Turn</span><span class="hud-stat-value" style="font-size:0.95rem;color:var(--neon-purple);animation:blink 1s step-end infinite;">Dealer</span></div>' + '<div class="hud-sep"></div><div class="hud-stat"><span class="hud-stat-label">🔥 Streak</span><span class="hud-stat-value" style="color:' + ((v.currentStreak && v.currentStreak[v.activeTable] > 0) ? 'var(--neon-pink);text-shadow:0 0 10px rgba(255,45,120,0.5)' : 'rgba(255,255,255,0.3)') + ';">' + ((v.currentStreak && v.currentStreak[v.activeTable]) || 0) + '</span></div>'; document.body.appendChild(hud); var _TBG = { 'vice': 'Images/scenes/the_vice.png', 'boudoir': 'Images/scenes/the_boudoir.png', 'heat': 'Images/scenes/the_heat.png', 'slutpit': 'Images/scenes/the_slut_pit.png', 'heterovip': 'Images/scenes/the_hetero_vip.png', 'rainbow': 'Images/scenes/the_rainbow.png', 'gayvip': 'Images/scenes/the_gay_vip.png', 'transroom': 'Images/scenes/the_trans_room.png', 'transvip': 'Images/scenes/the_trans_vip.png', 'penthouse': 'Images/scenes/the_penthouse.png' }; (function applyTableBg() { var at = State.variables.activeTable; var img = at && _TBG[at] ? _TBG[at] : null; document.body.style.backgroundImage = img ? 'linear-gradient(rgba(0,0,0,0.62),rgba(0,0,0,0.55),rgba(0,0,0,0.62)), url("' + img + '")' : ''; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundPosition = 'center'; document.body.style.backgroundAttachment = 'fixed'; })(); })(); <</script>> <</done>>
<div class="go2-screen" id="go2-screen"> <div class="go2-skull">💀</div> <h1 class="go2-title">GAME OVER</h1> <p class="go2-sub">You ran out of money.</p> <div class="go2-penalty-box" id="go2-penalty-box"> <div class="go2-penalty-title">⚠️ Penalty applied</div> <div class="go2-penalty-rows" id="go2-penalty-rows"> <div class="go2-penalty-row"> <span class="go2-penalty-label">Cards lost</span> <span class="go2-penalty-val go2-val-red" id="go2-cards-lost">—</span> </div> <div class="go2-penalty-row"> <span class="go2-penalty-label">Tokens lost</span> <span class="go2-penalty-val go2-val-red" id="go2-tokens-lost">—</span> </div> <div class="go2-penalty-row"> <span class="go2-penalty-label">Starting balance</span> <span class="go2-penalty-val go2-val-green">$1,500 restored</span> </div> </div> </div> <div class="go2-warning-box" id="go2-warning-box"> <span class="go2-warning-icon" id="go2-warning-icon">⚡</span> <div> <div class="go2-warning-title" id="go2-warning-title">Warning — penalties escalate</div> <div class="go2-warning-body" id="go2-warning-body"></div> </div> </div> <div class="go2-actions"> <a class="btn btn-primary go2-btn" id="go2-try-again">▶ Try Again</a> </div> </div> <<done>> <<script>> (function() { var v = State.variables; /* ── 1. Incrémenter le compteur ── */ v.gameOverCount = (v.gameOverCount || 0) + 1; var level = Math.min(v.gameOverCount, 5); /* ── 2. Fraction de perte ── */ var fracs = { 1: 1/4, 2: 1/3, 3: 1/2, 4: 3/4, 5: 1 }; var frac = fracs[level] || 1; var labels = { 1:'1/4', 2:'1/3', 3:'1/2', 4:'3/4', 5:'ALL' }; var penaltyLabel = labels[level]; var nextLabel = labels[Math.min(level + 1, 5)] || 'ALL'; /* ── 3. Supprimer les cartes ── */ var col = v.collection || {}; var ownedIds = Object.keys(col).filter(function(id) { return col[id] > 0; }); var totalOwned = ownedIds.length; /* Fisher-Yates shuffle */ var shuffled = ownedIds.slice(); for (var i = shuffled.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var tmp = shuffled[i]; shuffled[i] = shuffled[j]; shuffled[j] = tmp; } var toRemove = level >= 5 ? totalOwned : Math.floor(totalOwned * frac); for (var k = 0; k < toRemove; k++) { if (shuffled[k]) delete col[shuffled[k]]; } /* ── 4. Réduire les tokens ── */ if (!v.tokens) v.tokens = {}; Object.keys(v.tokens).forEach(function(tkey) { v.tokens[tkey] = Math.max(0, Math.floor(v.tokens[tkey] * (1 - frac))); }); /* ── 5. Reset argent + stats + lucky wheel ── */ v.playerMoney = 1500; v.totalWins = 0; v.totalLosses = 0; v.totalPushes = 0; v.totalHandsPlayed = 0; v.biggestWin = 0; v.wheelHandsCount = 0; v.wheelReady = false; v.currentStreak = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }; /* ── 6. Mettre à jour l'affichage ── */ var cardsEl = document.getElementById('go2-cards-lost'); var tokensEl = document.getElementById('go2-tokens-lost'); var warnBody = document.getElementById('go2-warning-body'); var warnIcon = document.getElementById('go2-warning-icon'); var warnBox = document.getElementById('go2-warning-box'); var warnTitle= document.getElementById('go2-warning-title'); if (cardsEl) cardsEl.textContent = '−' + toRemove + ' / ' + totalOwned + ' (' + penaltyLabel + ')'; if (tokensEl) tokensEl.textContent = '−' + penaltyLabel + ' of all tokens'; var isMax = (level >= 5); if (warnBody) { if (isMax) { warnBody.innerHTML = 'Any future game over will wipe your <strong>entire collection</strong> and all tokens. Play carefully.'; } else { warnBody.innerHTML = 'Next game over you will lose <strong>' + nextLabel + '</strong> of your collection and tokens. Manage your bankroll carefully.'; } } if (isMax && warnBox) warnBox.classList.add('go2-warning-max'); if (isMax && warnIcon) warnIcon.textContent = '☠️'; if (isMax && warnTitle) warnTitle.textContent = 'Maximum penalty reached'; /* ── 7. Désactiver tous les boutons sidebar sauf Try Again ── */ var sidebar = document.getElementById('nav-sidebar'); if (sidebar) { sidebar.querySelectorAll('a, button').forEach(function(el) { el.style.pointerEvents = 'none'; el.style.opacity = '0.25'; el.style.cursor = 'not-allowed'; }); } /* ── 8. Listener Try Again ── */ var btn = document.getElementById('go2-try-again'); if (btn) { btn.style.pointerEvents = ''; btn.style.opacity = ''; btn.style.cursor = ''; btn.addEventListener('click', function() { Engine.play('Casino'); }); } if (typeof buildNavSidebar === 'function') buildNavSidebar(); /* Re-désactiver après rebuild sidebar */ setTimeout(function() { var sb2 = document.getElementById('nav-sidebar'); if (sb2) { sb2.querySelectorAll('a, button').forEach(function(el) { el.style.pointerEvents = 'none'; el.style.opacity = '0.25'; el.style.cursor = 'not-allowed'; }); } }, 50); })(); <</script>> <</done>> <style> /* ══════════════════════════════════════════ GAME OVER PAGE ══════════════════════════════════════════ */ .go2-screen { min-height: 80vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px; padding: 40px 20px; max-width: 560px; margin: 0 auto; text-align: center; } .go2-skull { font-size: 4rem; filter: drop-shadow(0 0 20px rgba(255,51,80,0.6)); animation: goShake 0.6s ease-out; } @keyframes goShake { 0%,100% { transform: translateX(0) rotate(0); } 20% { transform: translateX(-10px) rotate(-5deg); } 40% { transform: translateX(10px) rotate(5deg); } 60% { transform: translateX(-6px) rotate(-3deg); } 80% { transform: translateX(6px) rotate(3deg); } } .go2-title { font-family: 'Orbitron', monospace; font-size: clamp(2rem, 6vw, 3rem); font-weight: 900; color: #ff3350; letter-spacing: 0.15em; text-shadow: 0 0 30px rgba(255,51,80,0.5), 0 0 60px rgba(255,51,80,0.2); margin: 0; } .go2-sub { font-family: 'Rajdhani', sans-serif; font-size: 1.1rem; color: rgba(210,200,235,0.5); letter-spacing: 0.1em; margin: 0; } .go2-penalty-box { width: 100%; background: rgba(255,51,80,0.06); border: 1px solid rgba(255,51,80,0.25); border-radius: 16px; padding: 20px 24px; display: flex; flex-direction: column; gap: 14px; } .go2-penalty-title { font-family: 'Orbitron', monospace; font-size: 0.72rem; font-weight: 900; letter-spacing: 0.2em; text-transform: uppercase; color: rgba(255,51,80,0.7); text-align: left; padding-bottom: 10px; border-bottom: 1px solid rgba(255,51,80,0.12); } .go2-penalty-rows { display: flex; flex-direction: column; gap: 10px; } .go2-penalty-row { display: flex; justify-content: space-between; align-items: center; } .go2-penalty-label { font-family: 'Rajdhani', sans-serif; font-size: 0.95rem; font-weight: 600; color: rgba(210,200,235,0.6); } .go2-penalty-val { font-family: 'Share Tech Mono', monospace; font-size: 0.9rem; font-weight: bold; } .go2-val-red { color: #ff3350; text-shadow: 0 0 10px rgba(255,51,80,0.4); } .go2-val-green { color: #00f5a0; text-shadow: 0 0 10px rgba(0,245,160,0.3); } .go2-warning-box { width: 100%; display: flex; align-items: flex-start; gap: 14px; padding: 16px 20px; background: rgba(255,200,0,0.05); border: 1px solid rgba(255,200,0,0.2); border-radius: 14px; text-align: left; } .go2-warning-max { background: rgba(255,51,80,0.06) !important; border-color: rgba(255,51,80,0.25) !important; } .go2-warning-icon { font-size: 1.6rem; flex-shrink: 0; } .go2-warning-title { font-family: 'Orbitron', monospace; font-size: 0.72rem; font-weight: 900; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255,200,0,0.8); margin-bottom: 6px; } .go2-warning-max .go2-warning-title { color: rgba(255,80,80,0.8); } .go2-warning-body { font-family: 'Rajdhani', sans-serif; font-size: 0.95rem; font-weight: 500; color: rgba(210,200,235,0.65); line-height: 1.5; } .go2-warning-body strong { color: rgba(255,200,0,0.9); } .go2-warning-max .go2-warning-body strong { color: #ff3350; } .go2-actions { margin-top: 8px; } .go2-btn { padding: 16px 56px !important; font-size: 1rem !important; letter-spacing: 0.15em !important; } </style>
<<silently>> <<set _dv = calculateHand($dealerHand)>> <<set _pv = calculateHand($playerHand)>> <<set _pb = (_pv > 21)>> <<set _db = (_dv > 21)>> <<set _pBJ = isBlackjack($playerHand)>> <<set _dBJ = isBlackjack($dealerHand)>> <<set _totalWin = 0>> <<set $lastResultBlackjack = false>> <<if _pb>> <<set _mainResult = "bust">> <<elseif _pBJ && !_dBJ>> <<set _mainResult = "blackjack">> <<set _totalWin += $currentBet + Math.floor($currentBet * 1.5)>> <<set $lastResultBlackjack = true>> <<elseif _pBJ && _dBJ>> <<set _mainResult = "push">> <<set _totalWin += $currentBet>> <<elseif _db>> <<set _mainResult = "win">> <<set _totalWin += $currentBet * 2>> <<elseif _pv > _dv>> <<set _mainResult = "win">> <<set _totalWin += $currentBet * 2>> <<elseif _pv < _dv>> <<set _mainResult = "lose">> <<else>> <<set _mainResult = "push">> <<set _totalWin += $currentBet>> <</if>> <<if $isSplit>> <<set _sv = calculateHand($splitHand)>> <<set _sb = (_sv > 21)>> <<if _sb>> <<set _splitResult = "bust">> <<elseif _db>> <<set _splitResult = "win">> <<set _totalWin += $splitBet * 2>> <<elseif _sv > _dv>> <<set _splitResult = "win">> <<set _totalWin += $splitBet * 2>> <<elseif _sv < _dv>> <<set _splitResult = "lose">> <<else>> <<set _splitResult = "push">> <<set _totalWin += $splitBet>> <</if>> <</if>> <<set $playerMoney += _totalWin>> <<set $totalHandsPlayed += 1>> /* Lucky Wheel — incrément compteur */ <<if !$wheelReady>> <<set $wheelHandsCount += 1>> <<if $wheelHandsCount >= 20>><<set $wheelReady = true>><</if>> <</if>> <<if _totalWin > 0>> <<set $totalEarned += _totalWin>> /* Incrémenter le compteur de la bonne branche */ <<if $activeBranch === "gay">> <<set $earnedGay += _totalWin>> <<elseif $activeBranch === "trans">> <<set $earnedTrans += _totalWin>> <<else>> <<set $earnedHetero += _totalWin>> <</if>> /* Gagner des jetons de la table active — 1 jeton par $10 gagnés */ <<set _tokensEarned = Math.floor(_totalWin / 10)>> <<if _tokensEarned > 0>> <<if !$tokens>> <<set $tokens = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }>> <</if>> <<set $tokens[$activeTable] += _tokensEarned>> <</if>> <</if>> <<if _mainResult === "win" || _mainResult === "blackjack">> <<set $totalWins += 1>> <<elseif _mainResult === "lose" || _mainResult === "bust">> <<set $totalLosses += 1>> <<else>> <<set $totalPushes += 1>> <</if>> <<if _totalWin > $biggestWin>><<set $biggestWin = _totalWin>><</if>> <<set $lastHandResult = _mainResult>> <<set $lastHandTokens = Math.floor(_totalWin / 10)>> /* Streak */ <<if $isSplit>> <<if (_mainResult === "win" || _mainResult === "blackjack") && (_splitResult === "win" || _splitResult === "blackjack")>> <<set $currentStreak[$activeTable] = ($currentStreak[$activeTable] || 0) + 1>> <<else>> <<set $currentStreak[$activeTable] = 0>> <</if>> <<elseif _mainResult === "win" || _mainResult === "blackjack">> <<set $currentStreak[$activeTable] = ($currentStreak[$activeTable] || 0) + 1>> <<elseif _mainResult === "lose" || _mainResult === "bust">> <<set $currentStreak[$activeTable] = 0>> <</if>> /* Déblocages branche hétéro */ <<if $earnedHetero >= 5000 && !$unlockedTables.includes("boudoir")>><<set $unlockedTables.push("boudoir")>><</if>> <<if $earnedHetero >= 20000 && !$unlockedTables.includes("heat")>><<set $unlockedTables.push("heat")>><</if>> <<if $earnedHetero >= 30000 && !$unlockedTables.includes("slutpit")>><<set $unlockedTables.push("slutpit")>><</if>> <<if $earnedHetero >= 75000 && !$unlockedTables.includes("heterovip")>><<set $unlockedTables.push("heterovip")>><</if>> /* Déblocages branche gay */ <<if $gayUnlocked>> <<if $earnedGay >= 30000 && !$unlockedTablesGay.includes("gayvip")>><<set $unlockedTablesGay.push("gayvip")>><</if>> <</if>> /* Déblocage branche trans — $50 000 gagnés au total */ <<if $totalEarned >= 50000 && !$transUnlocked>> <<set $transUnlocked = true>> <</if>> /* Déblocages tables trans */ <<if $transUnlocked>> <<if $earnedTrans >= 30000 && !$unlockedTablesTrans.includes("transvip")>><<set $unlockedTablesTrans.push("transvip")>><</if>> <</if>> /* Déblocage Penthouse — les deux branches VIP requises */ <<if $unlockedTables.includes("heterovip") && $gayUnlocked && $unlockedTablesGay.includes("gayvip") && $totalEarned >= 200000>> <<if !$unlockedTables.includes("penthouse")>><<set $unlockedTables.push("penthouse")>><</if>> <</if>> <</silently>> <div class="game-layout"> <div class="game-table"> <div class="dealer-zone"> <div class="zone-label dealer-label">🤖 Dealer — <<print _dv>><<if _db>> 💥 BUST<</if>></div> <<print displayHand($dealerHand, false)>> <div class="hand-total <<if _db>>bust<</if>>">Total : <<print _dv>><<if _dBJ>> — BLACKJACK ⭐<</if>></div> </div> <div class="player-zone"> <div class="zone-label player-label">🎭 Your hand</div> <<print displayHand($playerHand, false)>> <div class="hand-total">Total : <<print _pv>></div> <<if _mainResult === "blackjack">> <div class="result-banner result-blackjack">⭐ BLACKJACK!<div class="result-amount">+<<print formatMoney(Math.floor($currentBet * 1.5))>></div><div class="result-tokens">🎫 +<<print _tokensEarned>> tokens</div></div> <<elseif _mainResult === "win">> <div class="result-banner result-win">✅ YOU WIN!<div class="result-amount">+<<print formatMoney($currentBet)>></div><div class="result-tokens">🎫 +<<print _tokensEarned>> tokens</div></div> <<elseif _mainResult === "bust">> <div class="result-banner result-bust">💥 BUST!<div class="result-amount">-<<print formatMoney($currentBet)>></div></div> <<elseif _mainResult === "lose">> <div class="result-banner result-lose">❌ YOU LOSE<div class="result-amount">-<<print formatMoney($currentBet)>></div></div> <<else>> <div class="result-banner result-push">🤝 PUSH — Bet returned</div> <</if>> <<if $isSplit>> <div style="margin-top:8px;"> <div class="zone-label player-label">✂️ Split hand</div> <<print displayHand($splitHand, false)>> <div class="hand-total">Total : <<print _sv>></div> <<if _splitResult === "win">> <div class="result-banner result-win">✅ WIN<div class="result-amount">+<<print formatMoney($splitBet)>></div><div class="result-tokens">🎫 +<<print Math.floor($splitBet * 2 / 10)>> tokens</div></div> <<elseif _splitResult === "bust">> <div class="result-banner result-bust">💥 BUST<div class="result-amount">-<<print formatMoney($splitBet)>></div></div> <<elseif _splitResult === "lose">> <div class="result-banner result-lose">❌ LOSE<div class="result-amount">-<<print formatMoney($splitBet)>></div></div> <<else>> <div class="result-banner result-push">🤝 PUSH</div> <</if>> </div> <</if>> </div> </div> </div> <<done>> <<script>> (function() { var old = document.getElementById('game-hud'); if (old) old.remove(); var v = State.variables; var hud = document.createElement('div'); hud.id = 'game-hud'; document.getElementById('passages').classList.add('game-page'); hud.innerHTML = '<div class="hud-logo">THE <span>21</span> DESIRES</div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F4B0} Balance</span><span class="hud-stat-value">' + formatMoney(v.playerMoney) + '</span></div>' + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F3B0} Bet</span><span class="hud-stat-value cyan">' + formatMoney(v.currentBet) + '</span></div>' + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F3AB} Tokens</span><span class="hud-stat-value" style="color:#facc15;text-shadow:0 0 10px rgba(250,204,21,0.4);">' + (v.tokens ? (v.tokens[v.activeTable] || 0) : 0) + '</span></div>' + '<div class="hud-sep"></div><div class="hud-stat"><span class="hud-stat-label">🔥 Streak</span><span class="hud-stat-value" style="color:' + ((v.currentStreak && v.currentStreak[v.activeTable] > 0) ? 'var(--neon-pink);text-shadow:0 0 10px rgba(255,45,120,0.5)' : 'rgba(255,255,255,0.3)') + ';">' + ((v.currentStreak && v.currentStreak[v.activeTable]) || 0) + '</span></div>' + '<div class="hud-actions">' + '<a class="btn btn-primary hud-btn-newhand">🎰 New Hand</a>' + '<a class="btn btn-stand hud-btn-casino">🏠 Casino</a>' + '</div>'; document.body.appendChild(hud); hud.querySelector('.hud-btn-newhand').addEventListener('click', function() { Engine.play('Blackjack Table'); }); hud.querySelector('.hud-btn-casino').addEventListener('click', function() { Engine.play('Casino'); }); var _TBG = { 'vice': 'Images/scenes/the_vice.png', 'boudoir': 'Images/scenes/the_boudoir.png', 'heat': 'Images/scenes/the_heat.png', 'slutpit': 'Images/scenes/the_slut_pit.png', 'heterovip': 'Images/scenes/the_hetero_vip.png', 'rainbow': 'Images/scenes/the_rainbow.png', 'gayvip': 'Images/scenes/the_gay_vip.png', 'transroom': 'Images/scenes/the_trans_room.png', 'transvip': 'Images/scenes/the_trans_vip.png', 'penthouse': 'Images/scenes/the_penthouse.png' }; (function applyTableBg() { var at = State.variables.activeTable; var img = at && _TBG[at] ? _TBG[at] : null; document.body.style.backgroundImage = img ? 'linear-gradient(rgba(0,0,0,0.62),rgba(0,0,0,0.55),rgba(0,0,0,0.62)), url("' + img + '")' : ''; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundPosition = 'center'; document.body.style.backgroundAttachment = 'fixed'; })(); /* ── Boutons rapides — barre fixe au bas de l'écran de jeu ── */ var canReplay = v.playerMoney >= (v.baseBet || v.currentBet); var replayBet = v.baseBet || v.currentBet; var quickBar = document.createElement('div'); quickBar.id = 'result-action-bar'; quickBar.style.cssText = [ 'position:fixed', 'left:240px', /* après le HUD fixe */ 'right:0', 'bottom:46px', /* au-dessus de la save bar */ 'z-index:80', 'display:flex', 'gap:14px', 'align-items:center', 'justify-content:center', 'padding:14px 24px', 'background:rgba(6,4,15,0.92)', 'border-top:1px solid rgba(255,45,120,0.25)', 'backdrop-filter:blur(10px)', 'flex-wrap:wrap' ].join(';'); if (canReplay) { var btnReplay = document.createElement('a'); btnReplay.className = 'btn btn-primary result-qbtn'; btnReplay.style.cssText = 'font-size:1rem!important;padding:14px 28px!important;letter-spacing:0.08em!important;'; btnReplay.innerHTML = '🎰 Same bet — ' + formatMoney(replayBet); btnReplay.addEventListener('click', function() { v.currentBet = replayBet; Engine.play('Deal Cards'); }); quickBar.appendChild(btnReplay); } var btnBets = document.createElement('a'); btnBets.className = 'btn btn-secondary result-qbtn'; btnBets.style.cssText = 'font-size:1rem!important;padding:14px 28px!important;letter-spacing:0.08em!important;'; btnBets.innerHTML = '💸 Change bet'; btnBets.addEventListener('click', function() { Engine.play('Blackjack Table'); }); quickBar.appendChild(btnBets); var btnCasino = document.createElement('a'); btnCasino.className = 'btn btn-stand result-qbtn'; btnCasino.style.cssText = 'font-size:1rem!important;padding:14px 28px!important;letter-spacing:0.08em!important;'; btnCasino.innerHTML = '🏠 Casino'; btnCasino.addEventListener('click', function() { Engine.play('Casino'); }); quickBar.appendChild(btnCasino); document.body.appendChild(quickBar); /* ── STREAK REWARDS ── */ (function() { var v = State.variables; if (v.activeBranch !== 'hetero') return; var streak = (v.currentStreak && v.currentStreak[v.activeTable]) ? v.currentStreak[v.activeTable] : 0; var MILESTONES = [ { streak: 3, key: 's3', reward: function() { var tbl = v.activeTable || 'vice'; if (!v.tokens) v.tokens = {}; v.tokens[tbl] = (v.tokens[tbl] || 0) + 20; }, rewardLabel: '+20 tokens on this table', scarlettMsg: 'Three in a row. You\'re getting the hang of this. I added 20 tokens to your table — consider it a tip.', tips: [ '🔥 Win 3 hands in a row → +20 tokens', '🔥🔥 Win 5 in a row → +$150 cash', '🔥🔥🔥 Win 7 in a row → 1 free pack', '👑 Win 10 in a row → New cards pack + $300' ] }, { streak: 5, key: 's5', reward: function() { v.playerMoney += 150; }, rewardLabel: '+$150 cash', scarlettMsg: 'Five straight wins. The table is yours tonight. Here\'s $150 — you\'ve earned it.', tips: [ '🔥🔥 5-win streak → +$150 cash ✓', '🔥🔥🔥 Win 7 in a row → 1 free pack', '👑 Win 10 in a row → New cards pack + $300', '⚠️ One loss resets the streak — stay focused' ] }, { streak: 7, key: 's7', reward: function() { var pack = window.PACKS.find(function(p) { return p.table === (v.activeTable || 'vice') && !p.forcedRarity; }); if (pack) { var result = window.openPack(pack.id, true); if (result === true) v._streakFreePack = pack.id; } }, rewardLabel: '1 free pack', scarlettMsg: 'Seven wins. That\'s not luck anymore — that\'s skill. I\'m impressed. Take a free pack, you deserve it.', tips: [ '🔥🔥🔥 7-win streak → free pack ✓', '👑 Win 10 in a row → New cards pack + $300', '🎴 Your free pack has been added — go open it', '⚠️ One loss resets the streak' ] }, { streak: 10, key: 's10', reward: function() { v.playerMoney += 300; /* Pack avec cartes non possédées uniquement */ var tbl = v.activeTable || 'vice'; var pack = window.PACKS.find(function(p) { return p.table === tbl && !p.forcedRarity; }); if (pack) { var col = v.collection || {}; var pool = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1 && col[c.id] === undefined; }); /* Fallback si tout est possédé */ if (!pool.length) pool = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1; }); if (pool.length) { if (!v.tokens) v.tokens = {}; /* Déclencher l'ouverture de pack directement */ v.currentPackId = pack.id; v.pendingPack = []; for (var i = 0; i < 3; i++) { var shuffled = pool.slice().sort(function() { return Math.random() - 0.5; }); var card = shuffled[0]; if (card) { v.pendingPack.push(card.id); if (v.collection[card.id] === undefined) v.collection[card.id] = 0; v.collection[card.id]++; /* Retirer du pool pour éviter doublons dans ce pack */ pool = pool.filter(function(c) { return c.id !== card.id; }); if (!pool.length) pool = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1; }); } } v._streakNewCardsPack = true; } } }, rewardLabel: 'New cards pack + $300', scarlettMsg: 'Ten. Wins. In. A. Row. I\'ve never seen anything like it. Take the $300. And here\'s a pack — I made sure every card in it is something you don\'t have yet.', tips: [ '👑 10-win streak → New cards only pack + $300 ✓', '💰 $300 added to your balance', '🎴 Go open your pack — all new cards guaranteed', '🔥 The streak resets — can you do it again?' ] } ]; /* Trouver le milestone atteint */ var milestone = null; for (var i = MILESTONES.length - 1; i >= 0; i--) { if (streak === MILESTONES[i].streak) { milestone = MILESTONES[i]; break; } } if (!milestone) return; /* Appliquer la récompense */ milestone.reward(); /* Tip Scarlett — première fois seulement. Toast discret les fois suivantes */ var isFirst = !v.streakMilestoneSeen[milestone.key]; if (isFirst) v.streakMilestoneSeen[milestone.key] = true; var delay = (v.lastHandResult === 'blackjack') ? 2500 : 700; if (isFirst) { setTimeout(function() { window.showFirstVisitTip('streak_' + milestone.key, { icon: '🔥', title: 'Streak Bonus — ' + milestone.rewardLabel, body: milestone.rewardLabel, bodyScarlett: milestone.scarlettMsg, tips: milestone.tips, onClose: (v._streakFreePack || v._streakNewCardsPack) ? function() { v._streakFreePack = null; v._streakNewCardsPack = false; Engine.play('Open Pack'); } : null, closeBtnLabel: (v._streakFreePack || v._streakNewCardsPack) ? '🎴 Open Pack' : null }); }, delay); } else { /* Toast discret */ setTimeout(function() { var icons = { s3:'🔥', s5:'🔥🔥', s7:'🔥🔥🔥', s10:'👑' }; var toast = document.createElement('div'); toast.className = 'streak-toast'; toast.innerHTML = '<span class="streak-toast-icon">' + (icons[milestone.key] || '🔥') + '</span>' + '<span class="streak-toast-text">' + streak + '-win streak! <strong>' + milestone.rewardLabel + '</strong></span>' + ((v._streakFreePack || v._streakNewCardsPack) ? '<button class="streak-toast-pack">🎴 Open Pack</button>' : ''); document.body.appendChild(toast); if (v._streakFreePack || v._streakNewCardsPack) { toast.querySelector('.streak-toast-pack').addEventListener('click', function() { toast.remove(); v._streakFreePack = null; v._streakNewCardsPack = false; Engine.play('Open Pack'); }); } /* Apparition */ setTimeout(function() { toast.classList.add('streak-toast-visible'); }, 50); /* Disparition auto après 4s */ setTimeout(function() { toast.classList.remove('streak-toast-visible'); setTimeout(function() { if (toast.parentNode) toast.remove(); }, 400); }, 4000); }, delay); } })(); if (v.lastResultBlackjack) { /* Trouver le pack standard de la table active */ var freePack = PACKS.find(function(p) { return p.table === v.activeTable && !p.forcedRarity; }); /* Lancer l'animation après un court délai */ setTimeout(function() { triggerBlackjackCelebration(freePack); }, 400); } /* ── PREMIER GAIN : tip Scarlett ── */ if (!v.firstWinSeen && v.activeBranch === 'hetero' && (v.lastHandResult === 'win' || v.lastHandResult === 'blackjack')) { v.firstWinSeen = true; var tokensJustEarned = v.lastHandTokens || 0; setTimeout(function() { window.showFirstVisitTip('first_win', { icon: '🎰', title: 'First Win', body: 'You just won your first hand and earned tokens.', bodyScarlett: 'Not bad for a start. You just earned ' + tokensJustEarned + ' tokens for this table — don\'t let them sit there. Head to the Card Shop and see what I\'ve got waiting for you.', tips: [ '🎫 Tokens are earned on every winning hand', '🛍️ Spend them in the Card Shop to open packs', '🎴 Each pack reveals 3 animated adult cards', '🌟 Rare to Legendary — the best cards are worth the wait' ] }); }, 600); } })(); function triggerBlackjackCelebration(freePack) { /* Supprimer ancienne overlay si présente */ var oldOverlay = document.getElementById('bj-celebration'); if (oldOverlay) oldOverlay.remove(); var packName = freePack ? freePack.emoji + ' ' + freePack.name : 'a free pack'; var overlay = document.createElement('div'); overlay.id = 'bj-celebration'; overlay.innerHTML = '<div class="bj-backdrop"></div>' + '<div class="bj-content">' + '<div class="bj-stars" id="bj-stars"></div>' + '<div class="bj-title">⭐ BLACKJACK! ⭐</div>' + '<div class="bj-subtitle">Perfect hand — you earned a free pack!</div>' + '<div class="bj-pack-reward">' + '<div class="bj-pack-icon">' + (freePack ? freePack.emoji : '🎴') + '</div>' + '<div class="bj-pack-name">' + packName + '</div>' + '<div class="bj-pack-label">FREE PACK</div>' + '</div>' + '<button class="btn bj-btn-open" id="bj-btn-open">🎴 Open Pack Now</button>' + '</div>'; document.body.appendChild(overlay); /* Générer étoiles/particules */ var stars = document.getElementById('bj-stars'); for (var i = 0; i < 24; i++) { var star = document.createElement('div'); star.className = 'bj-star'; star.style.cssText = 'left:' + (Math.random() * 100) + '%;' + 'animation-delay:' + (Math.random() * 0.8) + 's;' + 'animation-duration:' + (1.2 + Math.random() * 1.2) + 's;' + 'font-size:' + (0.8 + Math.random() * 1.4) + 'rem;'; star.textContent = ['⭐','✨','💫','🌟'][Math.floor(Math.random()*4)]; stars.appendChild(star); } /* Ouvrir le pack maintenant */ document.getElementById('bj-btn-open').addEventListener('click', function() { overlay.remove(); if (freePack) { var result = openPack(freePack.id, true); /* true = gratuit */ if (result === true || result === 'free') { Engine.play('Open Pack'); } } }); } <</script>> <</done>>
<div class="htp-screen" id="htp-screen"> <div class="htp-header"> <p class="htp-pretitle">◈ Welcome to ◈</p> <h1 class="game-title htp-title">THE <span>21</span> DESIRES</h1> <p class="htp-subtitle">Here's how it works — it only takes a minute.</p> </div> <div class="htp-steps"> <!-- ÉTAPE 1 : BLACKJACK --> <div class="htp-step htp-step-1"> <div class="htp-step-icon-wrap"> <div class="htp-step-num">01</div> <div class="htp-step-icon">🃏</div> </div> <div class="htp-step-body"> <h2 class="htp-step-title">Play Blackjack</h2> <p class="htp-step-desc">Choose your table, place your bet and beat the dealer. Standard rules — hit, stand, double down or split. Get closer to 21 than the house without going over.</p> <p class="htp-step-hint">💡 New to Blackjack? Full rules are available anytime in the <strong>Rules</strong> section of the sidebar.</p> <div class="htp-demo-cards"> <div class="htp-card htp-card-red"><span class="htp-c-tl">A<br>♥</span><span class="htp-c-mid">♥</span><span class="htp-c-br">A<br>♥</span></div> <div class="htp-card htp-card-black"><span class="htp-c-tl">K<br>♠</span><span class="htp-c-mid">♠</span><span class="htp-c-br">K<br>♠</span></div> <div class="htp-card-result">= <span class="htp-bj-label">BLACKJACK ⭐</span></div> </div> </div> </div> <!-- ÉTAPE 2 : TOKENS --> <div class="htp-step htp-step-2"> <div class="htp-step-icon-wrap"> <div class="htp-step-num">02</div> <div class="htp-step-icon">🎫</div> </div> <div class="htp-step-body"> <h2 class="htp-step-title">Earn Tokens</h2> <p class="htp-step-desc">Every winning hand earns you tokens — the bigger your bet, the more you pocket. Tokens are tied to each table and spent in the Card Shop.</p> <div class="htp-token-demo"> <div class="htp-token-row"><span class="htp-bet">$50 bet</span><span class="htp-arrow">→</span><span class="htp-tok">+10 🎫</span></div> <div class="htp-token-row"><span class="htp-bet">$100 bet</span><span class="htp-arrow">→</span><span class="htp-tok">+20 🎫</span></div> <div class="htp-token-row htp-tok-gold"><span class="htp-bet">$250 bet</span><span class="htp-arrow">→</span><span class="htp-tok">+50 🎫</span></div> </div> </div> </div> <!-- ÉTAPE 3 : CARTES ADULTES --> <div class="htp-step htp-step-3"> <div class="htp-step-icon-wrap"> <div class="htp-step-num">03</div> <div class="htp-step-icon">💎</div> </div> <div class="htp-step-body"> <h2 class="htp-step-title">Collect Adult Cards</h2> <p class="htp-step-desc">This is where it gets hot. Spend your tokens in the Card Shop to open packs and unlock <strong>explicit adult content</strong> — animated GIFs across dozens of categories. Each pack reveals 3 cards, ranging from common to legendary rarity. The more tables you unlock, the more content becomes available.</p> <p class="htp-step-desc" style="margin-top:2px;">Below — a preview of what's waiting for you in the packs:</p> <!-- Vraies cartes GIF depuis la base --> <div class="htp-pack-demo"> <div class="htp-pack-card" id="htp-pc-1"> <div class="htp-pc-inner htp-pc-back"> <div class="htp-pc-back-pattern">🂠</div> </div> </div> <div class="htp-pack-card" id="htp-pc-2"> <div class="htp-pc-inner htp-pc-back"> <div class="htp-pc-back-pattern">🂠</div> </div> </div> <div class="htp-pack-card" id="htp-pc-3"> <div class="htp-pc-inner htp-pc-back"> <div class="htp-pc-back-pattern">🂠</div> </div> </div> </div> <button class="htp-reveal-btn" id="htp-reveal-btn">🎴 Reveal pack preview</button> <div class="htp-rarity-legend"> <span class="htp-rar htp-rar-common">⚪ Common</span> <span class="htp-rar htp-rar-rare">🔵 Rare</span> <span class="htp-rar htp-rar-epic">🟣 Epic</span> <span class="htp-rar htp-rar-legendary">🌟 Legendary</span> </div> </div> </div> <!-- BONUS : Lucky Wheel --> <div class="htp-step htp-step-bonus"> <div class="htp-step-icon-wrap"> <div class="htp-step-num htp-bonus-num">✦</div> <div class="htp-step-icon">🎡</div> </div> <div class="htp-step-body"> <h2 class="htp-step-title">Lucky Wheel <span class="htp-bonus-tag">Bonus</span></h2> <p class="htp-step-desc">Every 20 hands played, the Lucky Wheel unlocks. Spin it to win cash, tokens or a random card — for free. A little extra luck never hurt anyone.</p> </div> </div> </div> <div class="htp-cta"> <<link "✦ I'm ready — Choose my World" "World Select">><</link>> </div> </div> <<done>> <<script>> (function() { /* Style le bouton CTA */ var cta = document.querySelector('.htp-cta a'); if (cta) cta.classList.add('btn', 'btn-primary', 'htp-cta-btn'); /* ── Demo pack : 3 vraies cartes fixes (common / rare / legendary) ── */ var DEMO_PACK = [ { file: 'Images/cards/girl_strip/girl_strip_1.gif', name: 'Girl Strip #1', rarity: 'common', label: 'Common', color: '#aaaaaa', icon: '⚪' }, { file: 'Images/cards/girl_strip/girl_strip_4.gif', name: 'Girl Strip #4', rarity: 'rare', label: 'Rare', color: '#60a5fa', icon: '🔵' }, { file: 'Images/cards/girl_strip/girl_strip_8.gif', name: 'Girl Strip #8', rarity: 'legendary', label: 'Legendary', color: '#ffd700', icon: '🌟' } ]; var revealed = false; var revealBtn = document.getElementById('htp-reveal-btn'); function revealCard(id, card, delay) { setTimeout(function() { var el = document.getElementById(id); if (!el) return; el.innerHTML = '<div class="htp-pc-inner htp-pc-front" style="border-color:' + card.color + ';box-shadow:0 0 20px ' + card.color + '55;">' + '<img class="htp-pc-gif" src="' + card.file + '" alt="' + card.name + '">' + '<div class="htp-pc-footer">' + '<span class="htp-pc-rarity-icon">' + card.icon + '</span>' + '<span class="htp-pc-label" style="color:' + card.color + '">' + card.label + '</span>' + '</div>' + '</div>'; el.classList.add('htp-pc-revealed'); }, delay); } if (revealBtn) { revealBtn.addEventListener('click', function() { if (revealed) return; /* une seule fois */ var ids = ['htp-pc-1','htp-pc-2','htp-pc-3']; DEMO_PACK.forEach(function(card, i) { revealCard(ids[i], card, i * 600); }); revealed = true; revealBtn.disabled = true; revealBtn.style.opacity = '0.4'; revealBtn.style.cursor = 'default'; }); } })(); <</script>> <</done>> <style> /* ══════════════════════════════════════════ HOW TO PLAY ══════════════════════════════════════════ */ .htp-screen { min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 40px 24px 60px; gap: 36px; max-width: 820px; margin: 0 auto; } .htp-header { text-align: center; display: flex; flex-direction: column; gap: 8px; } .htp-pretitle { font-family: 'Share Tech Mono', monospace; font-size: 0.88rem; letter-spacing: 0.4em; color: rgba(255,45,120,0.7); text-transform: uppercase; } .htp-title { font-size: clamp(1.8rem, 5vw, 3rem) !important; } .htp-subtitle { font-family: 'Rajdhani', sans-serif; font-size: 1.15rem; color: rgba(210,200,235,0.75); letter-spacing: 0.08em; margin-top: 4px; } /* ── Steps ── */ .htp-steps { display: flex; flex-direction: column; gap: 20px; width: 100%; } .htp-step { display: flex; gap: 20px; align-items: flex-start; background: rgba(14,10,30,0.75); border: 1px solid rgba(255,255,255,0.06); border-radius: 18px; padding: 26px 30px; backdrop-filter: blur(4px); position: relative; overflow: hidden; } .htp-step::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; border-radius: 18px 18px 0 0; } .htp-step-1::before { background: linear-gradient(90deg, transparent, var(--neon-cyan), transparent); } .htp-step-2::before { background: linear-gradient(90deg, transparent, var(--gold), transparent); } .htp-step-3::before { background: linear-gradient(90deg, transparent, var(--neon-purple), transparent); } .htp-step-bonus::before { background: linear-gradient(90deg, transparent, var(--neon-pink), transparent); } .htp-step-icon-wrap { display: flex; flex-direction: column; align-items: center; gap: 4px; flex-shrink: 0; min-width: 52px; } .htp-step-num { font-family: 'Orbitron', monospace; font-size: 0.68rem; font-weight: 900; letter-spacing: 0.12em; color: rgba(255,45,120,0.5); } .htp-bonus-num { color: rgba(255,215,0,0.6); } .htp-step-icon { font-size: 2.2rem; line-height: 1; } .htp-step-body { display: flex; flex-direction: column; gap: 12px; flex: 1; } .htp-step-title { font-family: 'Orbitron', monospace; font-size: 1.1rem; font-weight: 900; color: var(--text-primary); letter-spacing: 0.08em; display: flex; align-items: center; gap: 10px; margin: 0; } .htp-bonus-tag { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; background: rgba(255,215,0,0.12); border: 1px solid rgba(255,215,0,0.3); color: var(--gold); padding: 3px 8px; border-radius: 20px; } .htp-step-desc { font-family: 'Rajdhani', sans-serif; font-size: 1.08rem; font-weight: 500; color: rgba(218, 210, 240, 0.9); line-height: 1.6; margin: 0; } .htp-step-desc strong { color: #fff; font-weight: 700; } .htp-step-hint { font-family: 'Rajdhani', sans-serif; font-size: 0.98rem; font-weight: 500; color: rgba(0,245,255,0.65); background: rgba(0,245,255,0.05); border: 1px solid rgba(0,245,255,0.12); border-radius: 8px; padding: 8px 14px; margin: 0; line-height: 1.5; } .htp-step-hint strong { color: rgba(0,245,255,0.9); font-weight: 700; } /* ── Demo blackjack cards ── */ .htp-demo-cards { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .htp-card { width: 64px; height: 92px; border-radius: 8px; background: white; border: 2px solid rgba(255,255,255,0.8); display: flex; align-items: center; justify-content: center; position: relative; box-shadow: 0 4px 14px rgba(0,0,0,0.5); animation: dealCard 0.4s ease-out; } .htp-c-tl { position: absolute; top: 4px; left: 5px; font-family: 'Share Tech Mono', monospace; font-size: 0.78rem; font-weight: bold; line-height: 1.2; text-align: center; } .htp-c-br { position: absolute; bottom: 4px; right: 5px; font-family: 'Share Tech Mono', monospace; font-size: 0.78rem; font-weight: bold; line-height: 1.2; text-align: center; transform: rotate(180deg); } .htp-c-mid { font-size: 1.6rem; line-height: 1; } .htp-card-red .htp-c-tl, .htp-card-red .htp-c-br, .htp-card-red .htp-c-mid { color: #cc0022; } .htp-card-black .htp-c-tl, .htp-card-black .htp-c-br, .htp-card-black .htp-c-mid { color: #111; } .htp-card-result { font-family: 'Rajdhani', sans-serif; font-size: 1.05rem; font-weight: 700; color: rgba(255,255,255,0.55); display: flex; align-items: center; gap: 6px; } .htp-bj-label { font-family: 'Orbitron', monospace; font-size: 0.82rem; font-weight: 900; color: var(--gold); text-shadow: 0 0 12px rgba(255,215,0,0.5); letter-spacing: 0.1em; } /* ── Token demo ── */ .htp-token-demo { display: flex; flex-direction: column; gap: 7px; } .htp-token-row { display: flex; align-items: center; gap: 14px; font-family: 'Share Tech Mono', monospace; font-size: 0.95rem; padding: 8px 14px; background: rgba(0,245,255,0.04); border: 1px solid rgba(0,245,255,0.1); border-radius: 8px; } .htp-bet { color: rgba(255,255,255,0.72); min-width: 82px; } .htp-arrow { color: rgba(255,255,255,0.22); } .htp-tok { color: var(--neon-cyan); font-weight: bold; } .htp-tok-gold { background: rgba(255,215,0,0.05); border-color: rgba(255,215,0,0.18); } .htp-tok-gold .htp-tok { color: var(--gold); } .htp-tok-gold .htp-bet { color: var(--gold); opacity: 0.85; } /* ── Pack demo avec vrais GIFs ── */ .htp-pack-demo { display: flex; gap: 14px; flex-wrap: wrap; } .htp-pack-card { width: 110px; height: 156px; perspective: 700px; cursor: default; flex-shrink: 0; } .htp-pc-inner { width: 100%; height: 100%; border-radius: 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; border: 2px solid; overflow: hidden; } .htp-pc-back { background: linear-gradient(135deg, #1a0535, #3d0a6e); border-color: var(--neon-purple); box-shadow: 0 0 14px rgba(191,0,255,0.25); } .htp-pc-back-pattern { font-size: 3rem; color: var(--neon-purple); text-shadow: 0 0 10px var(--neon-purple); opacity: 0.6; } .htp-pc-front { background: #0a0616; justify-content: flex-end; gap: 0; padding: 0; } .htp-pc-gif { width: 100%; flex: 1; object-fit: cover; display: block; min-height: 0; } .htp-pc-footer { width: 100%; display: flex; align-items: center; justify-content: center; gap: 5px; padding: 5px 6px; background: rgba(0,0,0,0.65); flex-shrink: 0; } .htp-pc-rarity-icon { font-size: 0.9rem; line-height: 1; } .htp-pc-label { font-family: 'Orbitron', monospace; font-size: 0.58rem; font-weight: 900; letter-spacing: 0.1em; text-transform: uppercase; } .htp-pack-card.htp-pc-revealed .htp-pc-inner { animation: pcReveal 0.5s cubic-bezier(0.34, 1.2, 0.64, 1); } @keyframes pcReveal { 0% { transform: rotateY(90deg) scale(0.85); opacity: 0; } 100% { transform: rotateY(0deg) scale(1); opacity: 1; } } .htp-reveal-btn { font-family: 'Rajdhani', sans-serif; font-size: 1rem; font-weight: 700; letter-spacing: 0.1em; padding: 11px 22px; border-radius: 10px; background: rgba(191,0,255,0.1); border: 1px solid rgba(191,0,255,0.3); color: #d966ff; cursor: pointer; transition: all 0.2s; align-self: flex-start; } .htp-reveal-btn:hover:not(:disabled) { background: rgba(191,0,255,0.2); border-color: var(--neon-purple); box-shadow: 0 0 16px rgba(191,0,255,0.25); } .htp-rarity-legend { display: flex; gap: 10px; flex-wrap: wrap; } .htp-rar { font-family: 'Share Tech Mono', monospace; font-size: 0.78rem; letter-spacing: 0.08em; padding: 4px 11px; border-radius: 20px; } .htp-rar-common { background: rgba(170,170,170,0.08); color: #bbb; border: 1px solid rgba(170,170,170,0.2); } .htp-rar-rare { background: rgba(96,165,250,0.08); color: #60a5fa; border: 1px solid rgba(96,165,250,0.25); } .htp-rar-epic { background: rgba(192,132,252,0.08); color: #c084fc; border: 1px solid rgba(192,132,252,0.25); } .htp-rar-legendary{ background: rgba(255,215,0,0.08); color: var(--gold); border: 1px solid rgba(255,215,0,0.3); } /* ── CTA ── */ .htp-cta { display: flex; justify-content: center; margin-top: 8px; } .htp-cta-btn { padding: 18px 52px !important; font-size: 1.12rem !important; letter-spacing: 0.14em !important; } </style>
<div class="intro-screen"> <div class="intro-bg"> <img src="Images/scenes/entry.png" class="intro-img" alt="The 21 Desires"> <div class="intro-overlay"></div> </div> <div class="intro-content"> <div class="intro-top"> <p class="intro-pretitle">◈ Step inside ◈</p> <h1 class="game-title intro-title">THE <span>21</span> DESIRES</h1> <p class="intro-subtitle">The cards never lie. But they always tempt.<br>Play your hand. Collect your fantasies.</p> </div> <div class="intro-bottom"> <<link "▶ Enter the Casino" "How To Play">><</link>> </div> </div> </div> <<done>> <<script>> (function() { const btn = document.querySelector('.intro-bottom a'); if (btn) btn.classList.add('btn', 'btn-intro-enter'); })(); <</script>> <</done>> <style> .intro-screen { position: fixed; inset: 0; width: 100vw; height: 100vh; overflow: hidden; display: flex; align-items: center; justify-content: center; z-index: 100; } .intro-bg { position: absolute; inset: 0; } .intro-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; } .intro-overlay { position: absolute; inset: 0; background: /* Halo lumineux centré bas — fait ressortir les cartes sur la table */ radial-gradient(ellipse 60% 35% at 50% 85%, rgba(6,4,15,0.0) 0%, rgba(6,4,15,0.15) 40%, rgba(6,4,15,0.6) 100% ), /* Assombrissement des bords gauche/droit */ radial-gradient(ellipse 100% 100% at 50% 50%, transparent 40%, rgba(6,4,15,0.5) 100% ), /* Gradient vertical général — top sombre, milieu clair, bas modéré */ linear-gradient(to bottom, rgba(6,4,15,0.80) 0%, rgba(6,4,15,0.25) 30%, rgba(6,4,15,0.20) 55%, rgba(6,4,15,0.55) 75%, rgba(6,4,15,0.85) 100% ); } .intro-content { position: relative; z-index: 10; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 48px 24px 60px; text-align: center; } .intro-top { display: flex; flex-direction: column; align-items: center; gap: 12px; animation: introFadeDown 1.2s ease-out; } .intro-pretitle { font-family: 'Share Tech Mono', monospace; font-size: 0.85rem; letter-spacing: 0.5em; color: rgba(255,45,120,0.8); text-transform: uppercase; text-shadow: 0 0 10px rgba(255,45,120,0.5); } .intro-title { font-size: clamp(2.5rem, 8vw, 5rem) !important; text-shadow: 0 0 20px var(--neon-pink), 0 0 50px rgba(255,45,120,0.4), 0 0 100px rgba(255,45,120,0.15) !important; } .intro-subtitle { font-family: 'Rajdhani', sans-serif; font-size: clamp(1rem, 2.5vw, 1.3rem); font-weight: 600; color: rgba(240,230,255,0.95); line-height: 1.7; letter-spacing: 0.05em; text-shadow: 0 2px 4px rgba(0,0,0,1), 0 0 20px rgba(0,0,0,0.9); margin-top: 8px; } .intro-bottom { animation: introFadeUp 1.4s ease-out; } .btn-intro-enter { padding: 18px 52px !important; font-size: 1.15rem !important; letter-spacing: 0.15em !important; background: linear-gradient(135deg, #c4005e, #ff2d78) !important; border: 1px solid var(--neon-pink) !important; color: white !important; box-shadow: 0 0 30px rgba(255,45,120,0.5), 0 0 60px rgba(255,45,120,0.2), inset 0 1px 0 rgba(255,255,255,0.15) !important; text-shadow: none !important; border-radius: 6px; transition: all 0.3s !important; display: inline-block; } .btn-intro-enter:hover { background: linear-gradient(135deg, #ff2d78, #ff6ea3) !important; box-shadow: 0 0 50px rgba(255,45,120,0.7), 0 0 100px rgba(255,45,120,0.3) !important; transform: translateY(-3px) scale(1.03); color: white !important; text-shadow: none !important; } @keyframes introFadeDown { from { opacity: 0; transform: translateY(-30px); } to { opacity: 1; transform: translateY(0); } } @keyframes introFadeUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } } </style>
<div style="min-height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:40px 20px;"> <h1 class="game-title" style="margin-bottom:16px;">THE <span>21</span> DESIRES</h1> <p style="font-family:'Share Tech Mono',monospace; font-size:0.9rem; color:var(--text-muted); letter-spacing:0.2em; margin-bottom:40px;">ACCESS DENIED</p> <div style="font-size:4rem; margin-bottom:24px; filter:drop-shadow(0 0 15px rgba(255,51,102,0.5));">🚫</div> <p style="color:var(--text-muted); font-size:1rem; line-height:1.7; max-width:400px;">You have chosen to leave.<br>Thank you for your honesty.<br><br> <span style="color:var(--text-primary);">Come back when you're ready.</span></p> <div style="margin-top:36px;"> <<link "← Go Back" "Warning">><</link>> </div> </div> <<done>> <<script>> (function() { const link = document.querySelector('#passages a'); if (link) { link.style.fontFamily = "'Rajdhani', sans-serif"; link.style.fontSize = "0.95rem"; link.style.letterSpacing = "0.1em"; } })(); <</script>> <</done>>
<h1 class="game-title">THE <span>21</span> DESIRES</h1> <div class="hud"> <div class="hud-item"> <span class="hud-label">💰 Balance</span> <span class="hud-value mono"><<print formatMoney($playerMoney)>></span> </div> <div class="hud-divider"></div> <div class="hud-item"> <span class="hud-label">💎 Unique</span> <span class="hud-value mono"><<print collectionCount()>> / <<print CARD_DB.length>></span> </div> </div> <div class="casino-hub"> <div class="casino-nav"> <<link "🎰 Tables" "Casino">><</link>> <<link "🛍️ Card Shop" "Card Shop">><</link>> <<link "💱 Sell Cards" "Sell Cards">><</link>> </div> <div class="section-title">📚 My Collection</div> <div class="coll-toolbar"> <!-- Ligne 1 : rareté + dropdowns --> <div class="coll-toolbar-row"> <div class="coll-filters" id="coll-filters-rarity"> <button class="coll-filter active" data-rarity="all">All</button> <button class="coll-filter" data-rarity="legendary">🌟 Leg.</button> <button class="coll-filter" data-rarity="epic">🟣 Epic</button> <button class="coll-filter" data-rarity="rare">🔵 Rare</button> <button class="coll-filter" data-rarity="common">⚪ Common</button> </div> <div class="coll-dropdowns"> <select class="coll-dropdown" id="coll-cat-select"> <option value="all">📂 All Categories</option> </select> <select class="coll-dropdown" id="coll-table-select"> <option value="all">🎰 All Tables</option> <option value="vice">🃏 The Vice</option> <option value="boudoir">💋 The Boudoir</option> <option value="heat">🔥 The Heat</option> <option value="slutpit">💀 The Slut Pit</option> <option value="heterovip">👑 Hetero VIP</option> </select> </div> </div> <!-- Ligne 2 : tri --> <div class="coll-sort-bar" id="coll-sort-bar"> <span class="coll-sort-label">Sort :</span> <button class="coll-sort active" data-sort="chronological">📋 Category</button> <button class="coll-sort" data-sort="rarity">🌟 Rarity</button> <button class="coll-sort" data-sort="alpha">🔤 A → Z</button> </div> </div> <div id="coll-grid" class="coll-grid"></div> </div> <<done>> <<script>> (function() { var v = State.variables; var col = v.collection; var grid = document.getElementById('coll-grid'); var fRarity = document.getElementById('coll-filters-rarity'); var catSel = document.getElementById('coll-cat-select'); var tableSel = document.getElementById('coll-table-select'); var sortBar = document.getElementById('coll-sort-bar'); if (!grid) return; /* Nav buttons */ var navLinks = document.querySelectorAll('.casino-nav a'); if (navLinks[0]) navLinks[0].classList.add('btn','btn-stand'); if (navLinks[1]) navLinks[1].classList.add('btn','btn-primary'); if (navLinks[2]) navLinks[2].classList.add('btn','btn-stand'); var activeRarity = 'all'; var activeCat = 'all'; var activeTable = 'all'; var activeSort = 'chronological'; var rarityOrder = { legendary:0, epic:1, rare:2, common:3 }; /* Catégories hetero uniquement */ var HETERO_CATS = window.CATEGORIES.filter(function(c) { return c.branch === 'hetero'; }); /* Mapping catégorie → table (basé sur les packs) */ var catToTable = {}; var tableOrder = ['vice','boudoir','heat','slutpit','heterovip']; window.PACKS.filter(function(p) { return p.branch === 'hetero'; }).forEach(function(pack) { /* On prend la table la plus "tôt" dans la progression */ pack.categories.forEach(function(cat) { if (!catToTable[cat]) catToTable[cat] = pack.table; else { var cur = tableOrder.indexOf(catToTable[cat]); var nw = tableOrder.indexOf(pack.table); if (nw < cur) catToTable[cat] = pack.table; } }); }); /* Populate dropdown catégories */ HETERO_CATS.forEach(function(cat) { var opt = document.createElement('option'); opt.value = cat.tag; opt.textContent = cat.label; catSel.appendChild(opt); }); /* Table labels */ var tableLabels = { vice:'The Vice', boudoir:'The Boudoir', heat:'The Heat', slutpit:'The Slut Pit', heterovip:'Hetero VIP' }; function getCardTable(card) { return catToTable[card.category] || 'heterovip'; } function sortCards(cards) { var sorted = cards.slice(); if (activeSort === 'rarity') { sorted.sort(function(a,b) { return rarityOrder[a.rarity] - rarityOrder[b.rarity] || a.category.localeCompare(b.category) || naturalSort(a.id, b.id); }); } else if (activeSort === 'alpha') { sorted.sort(function(a,b) { return a.category.localeCompare(b.category) || a.name.localeCompare(b.name); }); } return sorted; } function naturalSort(a, b) { var re = /(\d+)/g; var aParts = a.split(re), bParts = b.split(re); for (var i = 0; i < Math.max(aParts.length, bParts.length); i++) { var aSeg = aParts[i] || '', bSeg = bParts[i] || ''; var aNum = parseInt(aSeg,10), bNum = parseInt(bSeg,10); if (!isNaN(aNum) && !isNaN(bNum)) { if (aNum !== bNum) return aNum - bNum; } else { if (aSeg !== bSeg) return aSeg.localeCompare(bSeg); } } return 0; } function renderGrid() { grid.innerHTML = ''; /* Cartes hetero seulement */ var heteroCatTags = HETERO_CATS.map(function(c) { return c.tag; }); var allCards = window.CARD_DB.filter(function(c) { if (heteroCatTags.indexOf(c.category) === -1) return false; var okRarity = activeRarity === 'all' || c.rarity === activeRarity; var okCat = activeCat === 'all' || c.category === activeCat; var okTable = activeTable === 'all' || getCardTable(c) === activeTable; return okRarity && okCat && okTable; }); var sorted = sortCards(allCards); if (!sorted.length) { grid.innerHTML = '<p class="coll-empty">No cards match this filter.</p>'; return; } var prevTable = null; sorted.forEach(function(card) { var owned = col[card.id] !== undefined; var rar = window.RARITIES[card.rarity]; var count = col[card.id] || 0; var el = document.createElement('div'); if (owned) { el.className = 'coll-card'; el.style.cssText = 'border-color:' + rar.color + ';box-shadow:0 0 10px ' + rar.glow + ';'; el.innerHTML = '<img src="' + card.file + '" class="coll-img" alt="' + card.name + '" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\';">' + '<div class="coll-img-placeholder" style="display:none;color:' + rar.color + ';">' + rarityIcon(card.rarity) + '</div>' + '<div class="coll-footer">' + '<div class="coll-rarity" style="color:' + rar.color + ';">' + rarityIcon(card.rarity) + ' ' + rar.label + '</div>' + '<div class="coll-cat">' + categoryLabel(card.category) + '</div>' + '<div class="coll-name">' + card.name + '</div>' + (count > 1 ? '<div class="coll-count">×' + count + '</div>' : '') + '</div>'; el.addEventListener('click', function() { openLightbox(card, rar); }); } else { el.className = 'coll-card coll-card-unknown'; el.innerHTML = '<div class="coll-unknown-icon">?</div>' + '<div class="coll-footer">' + '<div class="coll-rarity" style="color:' + rar.color + ';opacity:0.5;">' + rarityIcon(card.rarity) + ' ' + rar.label + '</div>' + '<div class="coll-cat" style="opacity:0.4;">' + categoryLabel(card.category) + '</div>' + '<div class="coll-name" style="opacity:0.3;">???</div>' + '</div>'; } grid.appendChild(el); }); } /* Lightbox */ function openLightbox(card, rar) { var old = document.getElementById('coll-lightbox'); if (old) old.remove(); var lb = document.createElement('div'); lb.id = 'coll-lightbox'; lb.innerHTML = '<div class="lb-backdrop"></div>' + '<div class="lb-content">' + '<button class="lb-close">✕</button>' + '<div class="lb-card" style="border-color:' + rar.color + ';box-shadow:0 0 40px ' + rar.glow + ',0 0 80px ' + rar.glow + ';">' + '<img src="' + card.file + '" class="lb-img" alt="' + card.name + '">' + '<div class="lb-footer">' + '<div class="lb-rarity" style="color:' + rar.color + ';">' + rarityIcon(card.rarity) + ' ' + rar.label + '</div>' + '<div class="lb-cat">' + categoryLabel(card.category) + '</div>' + '<div class="lb-name">' + card.name + '</div>' + '</div>' + '</div>' + '</div>'; document.body.appendChild(lb); lb.querySelector('.lb-backdrop').addEventListener('click', function() { lb.remove(); }); lb.querySelector('.lb-close').addEventListener('click', function() { lb.remove(); }); } /* Listeners */ fRarity.querySelectorAll('.coll-filter').forEach(function(btn) { btn.addEventListener('click', function() { activeRarity = this.getAttribute('data-rarity'); fRarity.querySelectorAll('.coll-filter').forEach(function(b) { b.classList.remove('active'); }); this.classList.add('active'); renderGrid(); }); }); catSel.addEventListener('change', function() { activeCat = this.value; renderGrid(); }); tableSel.addEventListener('change', function() { activeTable = this.value; renderGrid(); }); sortBar.querySelectorAll('.coll-sort').forEach(function(btn) { btn.addEventListener('click', function() { activeSort = this.getAttribute('data-sort'); sortBar.querySelectorAll('.coll-sort').forEach(function(b) { b.classList.remove('active'); }); this.classList.add('active'); renderGrid(); }); }); renderGrid(); })(); window.showFirstVisitTip('collection', { icon: '📚', title: 'Your Collection', body: 'All cards you\'ve ever unlocked are shown here. Cards you haven\'t found yet appear as mystery slots.', bodyScarlett: 'These are the cards you\'ve earned so far. Every mystery slot is something you haven\'t found yet. Keep playing — I have a lot more to show you.', tips: [ '🔍 Filter by rarity, category or table', '🖱️ Click a card to view it full size', '×2 ×3 means you own multiple copies', '💱 Head to Sell Cards to cash in duplicates' ] }); <</script>> <</done>> <style> /* ── Collection layout ── */ .casino-hub .section-title { margin-bottom: 10px; } .coll-toolbar { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; text-align: left; } .coll-toolbar-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .coll-dropdowns { display: flex; gap: 8px; flex-wrap: wrap; } /* Dropdown catégorie */ .coll-dropdown-wrap { display: flex; align-items: center; } .coll-dropdown { font-family: 'Rajdhani', sans-serif; font-size: 0.88rem; font-weight: 600; letter-spacing: 0.05em; padding: 5px 32px 5px 12px; border-radius: 20px; background: var(--dark-card); border: 1px solid rgba(255,255,255,0.1); color: rgba(210,200,240,0.85); cursor: pointer; appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='rgba(255,45,120,0.7)' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; background-color: var(--dark-card); min-width: 160px; transition: border-color 0.2s; } .coll-dropdown:focus { outline: none; border-color: rgba(255,45,120,0.45); } .coll-dropdown option { background: #0e0a1e; color: rgba(220,210,240,0.9); } /* Séparateur de table dans la grille */ .coll-table-sep { grid-column: 1 / -1; font-family: 'Orbitron', monospace; font-size: 0.72rem; font-weight: 900; letter-spacing: 0.2em; text-transform: uppercase; color: rgba(255,215,0,0.55); padding: 10px 0 4px; border-bottom: 1px solid rgba(255,215,0,0.12); margin-bottom: 4px; } </style>
<div class="casino-main"> <div class="section-title" id="pack-title">🎴 Opening Pack...</div> <div id="pack-reveal-area" class="pack-reveal-area-big"></div> <div class="action-bar" id="pack-actions" style="justify-content:center; margin-top:24px; display:none;"></div> </div> <<done>> <<script>> (function() { var v = State.variables; var pending = v.pendingPack; var area = document.getElementById('pack-reveal-area'); var actions = document.getElementById('pack-actions'); var title = document.getElementById('pack-title'); if (!area) return; if (!pending || !pending.length) { if (title) title.textContent = '⚠️ No cards available yet'; area.innerHTML = '<p style="text-align:center;color:var(--text-muted);padding:60px 20px;">This pack has no cards yet.<br>Add cards to CARD_DB to start collecting!</p>'; actions.style.display = 'flex'; var bBack = document.createElement('a'); bBack.className = 'btn btn-stand'; bBack.textContent = '🛍️ Back to Shop'; bBack.addEventListener('click', function() { Engine.play('Card Shop'); }); actions.appendChild(bBack); return; } var pack = PACKS.find(function(p) { return p.id === v.currentPackId; }); if (pack && title) title.textContent = pack.emoji + ' ' + pack.name; var currentIndex = 0; var totalCards = pending.length; /* Compteur */ var counter = document.createElement('div'); counter.className = 'pack-counter'; counter.textContent = '1 / ' + totalCards; area.parentNode.insertBefore(counter, area); function showCard(index) { area.innerHTML = ''; counter.textContent = (index + 1) + ' / ' + totalCards; var cardId = pending[index]; var cardObj = getCard(cardId); /* Carte dos — clic pour retourner */ var wrapper = document.createElement('div'); wrapper.className = 'big-card-wrapper'; var inner = document.createElement('div'); inner.className = 'big-card-inner'; /* Dos */ var back = document.createElement('div'); back.className = 'big-card-back'; back.innerHTML = '<div class="big-card-back-symbol">🂠</div>' + '<div class="big-card-back-hint">Click to reveal</div>'; /* Face */ var front = document.createElement('div'); front.className = 'big-card-front'; inner.appendChild(back); inner.appendChild(front); wrapper.appendChild(inner); area.appendChild(wrapper); /* Bouton suivant / terminer (caché au départ) */ var nextBtn = document.createElement('button'); nextBtn.className = 'btn btn-primary pack-next-btn'; var isLast = (index === totalCards - 1); nextBtn.textContent = isLast ? '✓ Finish' : 'Next Card →'; nextBtn.style.display = 'none'; area.appendChild(nextBtn); /* Flip au clic sur la carte */ wrapper.addEventListener('click', function() { if (inner.classList.contains('flipped')) return; if (!cardObj) { inner.classList.add('flipped'); nextBtn.style.display = 'block'; return; } var rar = RARITIES[cardObj.rarity]; /* collection[cardId] === 1 → première fois qu'on possède cette carte → NEW collection[cardId] > 1 → doublon → +1 */ var isNew = (v.collection[cardId] === 1); var badge = isNew ? '<div class="reveal-badge badge-new">✦ NEW</div>' : '<div class="reveal-badge badge-dupe">+1</div>'; front.innerHTML = '<div class="big-card-face" style="border-color:' + rar.color + ';box-shadow:0 0 40px ' + rar.glow + ',0 0 80px ' + rar.glow + ';">' + badge + '<img src="' + cardObj.file + '" class="big-card-img" alt="' + cardObj.name + '" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\';">' + '<div class="big-card-img-placeholder" style="display:none;color:' + rar.color + ';">' + rarityIcon(cardObj.rarity) + '</div>' + '<div class="big-card-footer">' + '<span class="big-card-rarity" style="color:' + rar.color + ';">' + rarityIcon(cardObj.rarity) + ' ' + rar.label + '</span>' + '<span class="big-card-name">' + cardObj.name + '</span>' + '</div>' + '</div>'; inner.classList.add('flipped'); if (cardObj.rarity === 'legendary') inner.classList.add('effect-legendary'); else if (cardObj.rarity === 'epic') inner.classList.add('effect-epic'); else if (cardObj.rarity === 'rare') inner.classList.add('effect-rare'); nextBtn.style.display = 'block'; }); /* Suivant */ nextBtn.addEventListener('click', function() { currentIndex++; if (currentIndex < totalCards) { showCard(currentIndex); } else { showActions(); } }); } function showActions() { area.innerHTML = ''; counter.style.display = 'none'; var packName = pack ? pack.name : 'Pack'; var packEmoji = pack ? pack.emoji : '🎴'; var packTableObj = pack && window.TABLES ? window.TABLES.find(function(t) { return t.id === pack.table; }) : null; var packTableEmoji = packTableObj ? packTableObj.emoji : '🎫'; if (!v.tokens) v.tokens = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }; var tableTokens2 = pack ? (v.tokens[pack.table] || 0) : 0; var canAgain = pack && tableTokens2 >= pack.price; /* Conteneur principal */ var wrap = document.createElement('div'); wrap.className = 'pack-end-screen'; /* Message de fin */ var msg = document.createElement('div'); msg.className = 'pack-end-msg'; msg.innerHTML = '<span class="pack-end-emoji">' + packEmoji + '</span>' + '<span class="pack-end-text">Pack opened!</span>'; wrap.appendChild(msg); /* Grille de boutons */ var grid = document.createElement('div'); grid.className = 'pack-end-grid' + (canAgain ? ' pack-end-grid-3' : ' pack-end-grid-2'); /* Bouton 1 — View Collection */ var b1 = document.createElement('a'); b1.className = 'pack-end-btn pack-end-btn-collection'; b1.innerHTML = '<span class="peb-icon">📚</span><span class="peb-label">My Collection</span><span class="peb-sub">See your cards</span>'; b1.addEventListener('click', function() { Engine.play('My Collection'); }); grid.appendChild(b1); /* Bouton 2 — Open Another (conditionnel) */ if (canAgain) { var b2 = document.createElement('a'); b2.className = 'pack-end-btn pack-end-btn-again'; b2.innerHTML = '<span class="peb-icon">' + packEmoji + '</span><span class="peb-label">Open Another</span><span class="peb-sub">' + packTableEmoji + ' ' + pack.price + ' tokens</span>'; b2.addEventListener('click', function() { if (openPack(v.currentPackId)) Engine.play('Open Pack'); }); grid.appendChild(b2); } /* Bouton 3 — Back to Shop */ var b3 = document.createElement('a'); b3.className = 'pack-end-btn pack-end-btn-shop'; b3.innerHTML = '<span class="peb-icon">🛍️</span><span class="peb-label">Card Shop</span><span class="peb-sub">Buy more packs</span>'; b3.addEventListener('click', function() { Engine.play('Card Shop'); }); grid.appendChild(b3); wrap.appendChild(grid); actions.style.display = 'block'; actions.appendChild(wrap); } showCard(0); })(); window.showFirstVisitTip('open_pack', { icon: '🎴', title: 'Opening a Pack', body: 'Click each card to flip it and reveal what\'s inside. Cards range from Common to Legendary rarity.', bodyScarlett: 'Click each card to reveal what\'s inside. Some of them are very... special. The Legendaries are rare — but they\'re worth every token.', tips: [ '🖱️ Click the card to reveal it', '✦ NEW means it\'s your first copy', '+1 means you already own this card', '💱 Duplicates can be sold in the Sell menu' ] }); <</script>> <</done>>
<div class="game-layout"> <div class="game-table"> <div class="dealer-zone"> <div class="zone-label dealer-label">🤖 Dealer</div> <<print displayHand($dealerHand, true)>> <div class="hand-total">Visible : <<print calculateHand([$dealerHand[1]])>></div> </div> <div class="player-zone"> <<if $currentHand === "main">> <div class="zone-label player-label">🎭 Your hand<<if $isSplit>> (main)<</if>></div> <<print displayHand($playerHand, false)>> <<silently>><<set _pv = calculateHand($playerHand)>><</silently>> <<if _pv > 21>> <div class="hand-total bust">💥 <<print _pv>> — BUST!</div> <<if $isSplit>> <<set $currentHand = "split">> <<goto "Play Hand">> <<else>> <<silently>> <<set $gameState = "result">> <<set $totalLosses += 1>> <<set $totalHandsPlayed += 1>> <</silently>> <<goto "Game Result">> <</if>> <<elseif isBlackjack($playerHand) && !$isSplit>> <div class="hand-total blackjack-total">⭐ <<print _pv>> — BLACKJACK!</div> <<silently>><<set $gameState = "dealer">><</silently>> <<goto "Dealer Turn">> <<elseif _pv === 21>> <div class="hand-total" style="color:var(--win-green);text-shadow:0 0 16px var(--win-green);">✦ <<print _pv>> — Standing automatically</div> <<silently>><<set $gameState = "dealer">><</silently>> <<timed 0.9s>> <<if $isSplit>> <<set $currentHand = "split">> <<goto "Play Hand">> <<else>> <<goto "Dealer Turn">> <</if>> <</timed>> <<else>> <div class="hand-total">Total : <<print softDisplayScore($playerHand)>></div> <div class="action-bar"> <<link "🃏 Hit">><<set $playerHand.push(drawCard())>><<goto "Play Hand">><</link>> <<link "✋ Stand">> <<if $isSplit>> <<set $currentHand = "split">> <<goto "Play Hand">> <<else>> <<set $gameState = "dealer">> <<goto "Dealer Turn">> <</if>> <</link>> <<if $playerHand.length === 2 && $canDouble && !$isSplit>> <<link "⬆️ Double Down">> <<set $playerMoney -= $currentBet>> <<set $currentBet *= 2>> <<set $playerHand.push(drawCard())>> <<if calculateHand($playerHand) > 21>> <<set $gameState = "result">> <<set $totalLosses += 1>> <<set $totalHandsPlayed += 1>> <<goto "Game Result">> <<else>> <<set $gameState = "dealer">> <<goto "Dealer Turn">> <</if>> <</link>> <</if>> <<if $playerHand.length === 2 && $canSplit && !$isSplit>> <<link "✂️ Split">> <<set $isSplit = true>> <<set $splitHand.push($playerHand.pop())>> <<set $splitBet = $currentBet>> <<set $playerMoney -= $currentBet>> <<set $playerHand.push(drawCard())>> <<set $splitHand.push(drawCard())>> <<goto "Play Hand">> <</link>> <</if>> </div> <</if>> <<else>> <div class="zone-label player-label">✂️ Your hand (Split)</div> <<print displayHand($splitHand, false)>> <<silently>><<set _sv = calculateHand($splitHand)>><</silently>> <<if _sv > 21>> <div class="hand-total bust">💥 <<print _sv>> — BUST!</div> <<silently>><<set $gameState = "dealer">><</silently>> <<goto "Dealer Turn">> <<elseif _sv === 21>> <div class="hand-total" style="color:var(--win-green);text-shadow:0 0 16px var(--win-green);">✦ <<print _sv>> — Standing automatically</div> <<silently>><<set $gameState = "dealer">><</silently>> <<timed 0.9s>><<goto "Dealer Turn">><</timed>> <<else>> <div class="hand-total">Total : <<print softDisplayScore($splitHand)>></div> <div class="action-bar"> <<link "🃏 Hit">><<set $splitHand.push(drawCard())>><<goto "Play Hand">><</link>> <<link "✋ Stand">><<set $gameState = "dealer">><<goto "Dealer Turn">><</link>> </div> <</if>> <</if>> </div> </div> </div> <<done>> <<script>> (function() { var _TBG = { 'vice': 'Images/scenes/the_vice.png', 'boudoir': 'Images/scenes/the_boudoir.png', 'heat': 'Images/scenes/the_heat.png', 'slutpit': 'Images/scenes/the_slut_pit.png', 'heterovip': 'Images/scenes/the_hetero_vip.png', 'rainbow': 'Images/scenes/the_rainbow.png', 'gayvip': 'Images/scenes/the_gay_vip.png', 'transroom': 'Images/scenes/the_trans_room.png', 'transvip': 'Images/scenes/the_trans_vip.png', 'penthouse': 'Images/scenes/the_penthouse.png' }; (function applyTableBg() { var at = State.variables.activeTable; var img = at && _TBG[at] ? _TBG[at] : null; document.body.style.backgroundImage = img ? 'linear-gradient(rgba(0,0,0,0.62),rgba(0,0,0,0.55),rgba(0,0,0,0.62)), url("' + img + '")' : ''; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundPosition = 'center'; document.body.style.backgroundAttachment = 'fixed'; })(); /* Boutons action — redesign avec icône + label + sous-titre */ const bar = document.querySelector('.action-bar'); if (bar) { bar.querySelectorAll('a').forEach(function(link) { const txt = link.textContent.trim(); link.classList.add('action-btn'); if (txt.includes('Hit')) { link.classList.add('btn', 'btn-hit'); link.innerHTML = '<span class="action-btn-icon">🃏</span>' + '<span class="action-btn-label">Hit</span>' + '<span class="action-btn-sub">Draw a card</span>'; } else if (txt.includes('Stand')) { link.classList.add('btn', 'btn-stand'); link.innerHTML = '<span class="action-btn-icon">✋</span>' + '<span class="action-btn-label">Stand</span>' + '<span class="action-btn-sub">Keep your hand</span>'; } else if (txt.includes('Double')) { link.classList.add('btn', 'btn-gold'); link.innerHTML = '<span class="action-btn-icon">✖️2</span>' + '<span class="action-btn-label">Double Down</span>' + '<span class="action-btn-sub">One card · Double bet</span>'; } else if (txt.includes('Split')) { link.classList.add('btn', 'btn-secondary'); link.innerHTML = '<span class="action-btn-icon">✂️</span>' + '<span class="action-btn-label">Split</span>' + '<span class="action-btn-sub">Two hands</span>'; } }); } /* HUD fixe gauche */ var old = document.getElementById('game-hud'); if (old) old.remove(); var v = State.variables; var hud = document.createElement('div'); hud.id = 'game-hud'; document.getElementById('passages').classList.add('game-page'); hud.innerHTML = '<div class="hud-logo">THE <span>21</span> DESIRES</div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F4B0} Balance</span><span class="hud-stat-value">' + formatMoney(v.playerMoney) + '</span></div>' + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F3B0} Bet</span><span class="hud-stat-value cyan">' + formatMoney(v.currentBet) + '</span></div>' + (v.isSplit ? '<div class="hud-sep"></div><div class="hud-stat"><span class="hud-stat-label">Split</span><span class="hud-stat-value cyan">' + formatMoney(v.splitBet) + '</span></div>' : '') + '<div class="hud-sep"></div>' + '<div class="hud-stat"><span class="hud-stat-label">\u{1F3AB} Tokens</span><span class="hud-stat-value" style="color:#facc15;text-shadow:0 0 10px rgba(250,204,21,0.4);">' + (v.tokens ? (v.tokens[v.activeTable] || 0) : 0) + '</span></div>' + '<div class="hud-sep"></div><div class="hud-stat"><span class="hud-stat-label">🔥 Streak</span><span class="hud-stat-value" style="color:' + ((v.currentStreak && v.currentStreak[v.activeTable] > 0) ? 'var(--neon-pink);text-shadow:0 0 10px rgba(255,45,120,0.5)' : 'rgba(255,255,255,0.3)') + ';">' + ((v.currentStreak && v.currentStreak[v.activeTable]) || 0) + '</span></div>'; document.body.appendChild(hud); })(); <</script>> <</done>>
<div class="hud"> <div class="hud-item"> <span class="hud-label">💰 Balance</span> <span class="hud-value mono" id="sell-balance"><<print formatMoney($playerMoney)>></span> </div> <div class="hud-divider"></div> <div class="hud-item"> <span class="hud-label">💱 Sell value</span> <span class="hud-value mono" id="sell-total-val" style="color:var(--neon-cyan);">$0</span> </div> </div> <div class="casino-hub"> <div class="casino-nav"> <<link "📚 My Collection" "My Collection">><</link>> <<link "🛍️ Card Shop" "Card Shop">><</link>> </div> <div class="section-title">💱 Sell Duplicates</div> <div class="sell-intro"> <p>Here you can sell any card you own <strong>more than once</strong>. You always keep one copy — only the extras can be sold. Rarer cards fetch a higher price.</p> </div> <div class="sell-info-bar"> <span class="sell-price-chip sell-price-common">⚪ Common — $10</span> <span class="sell-price-chip sell-price-rare">🔵 Rare — $30</span> <span class="sell-price-chip sell-price-epic">🟣 Epic — $80</span> <span class="sell-price-chip sell-price-legendary">🌟 Legendary — $200</span> </div> <div class="sell-summary-box" id="sell-summary-box"> <div class="sell-summary-left"> <span class="sell-summary-count" id="sell-summary-count">0 cards</span> <span class="sell-summary-label">to sell</span> </div> <div class="sell-summary-value" id="sell-summary-value">+$0</div> <button class="sell-confirm-btn" id="sell-all-btn">💱 Sell all duplicates</button> </div> <div id="sell-grid" class="sell-grid"></div> <div id="sell-empty" class="sell-empty"> <div class="sell-empty-icon">🃏</div> <p>You have no duplicate cards to sell.</p> </div> </div> <<done>> <<script>> (function() { var v = State.variables; var col = v.collection; var grid = document.getElementById('sell-grid'); var emptyEl = document.getElementById('sell-empty'); var navLinks = document.querySelectorAll('.casino-nav a'); if (navLinks[0]) navLinks[0].classList.add('btn','btn-stand'); if (navLinks[1]) navLinks[1].classList.add('btn','btn-stand'); /* Masquer le bloc vide par défaut via JS (évite les problèmes SugarCube avec style inline) */ if (emptyEl) emptyEl.style.display = 'none'; var PRICES = window.SELL_PRICES || { common:5, rare:15, epic:40, legendary:100 }; function getDuplicates() { return window.CARD_DB.filter(function(c) { return (col[c.id] || 0) >= 2; }); } function calcTotal(dupes) { return dupes.reduce(function(sum, c) { return sum + ((col[c.id] - 1) * PRICES[c.rarity]); }, 0); } function calcTotalCards(dupes) { return dupes.reduce(function(sum, c) { return sum + (col[c.id] - 1); }, 0); } function updateSummary(dupes) { var totalCards = calcTotalCards(dupes); var totalVal = calcTotal(dupes); var countEl = document.getElementById('sell-summary-count'); var valEl = document.getElementById('sell-summary-value'); var btn = document.getElementById('sell-all-btn'); var hudVal = document.getElementById('sell-total-val'); if (countEl) countEl.textContent = totalCards + ' card' + (totalCards !== 1 ? 's' : ''); if (valEl) valEl.textContent = '+' + formatMoney(totalVal); if (hudVal) hudVal.textContent = formatMoney(totalVal); if (btn) { btn.disabled = (totalCards === 0); btn.style.opacity = totalCards === 0 ? '0.3' : '1'; btn.style.cursor = totalCards === 0 ? 'not-allowed' : 'pointer'; } } function renderGrid() { grid.innerHTML = ''; var dupes = getDuplicates(); updateSummary(dupes); if (!dupes.length) { if (emptyEl) emptyEl.style.display = 'flex'; grid.style.display = 'none'; return; } if (emptyEl) emptyEl.style.display = 'none'; grid.style.display = ''; dupes.forEach(function(card) { var rar = window.RARITIES[card.rarity]; var count = col[card.id] || 0; var sellable = count - 1; var price = PRICES[card.rarity]; var el = document.createElement('div'); el.className = 'sell-card'; el.style.borderColor = rar.color; var img = document.createElement('img'); img.src = card.file; img.className = 'sell-card-img'; img.alt = card.name; var placeholder = document.createElement('div'); placeholder.className = 'sell-card-placeholder'; placeholder.style.cssText = 'display:none;color:' + rar.color; placeholder.textContent = rarityIcon(card.rarity); img.onerror = function() { this.style.display = 'none'; placeholder.style.display = 'flex'; }; var badge = document.createElement('div'); badge.className = 'sell-card-count-badge'; badge.textContent = '\u00d7' + count; var imgWrap = document.createElement('div'); imgWrap.className = 'sell-card-img-wrap'; imgWrap.appendChild(img); imgWrap.appendChild(placeholder); imgWrap.appendChild(badge); var body = document.createElement('div'); body.className = 'sell-card-body'; body.innerHTML = '<div class="sell-card-rarity" style="color:' + rar.color + '">' + rarityIcon(card.rarity) + ' ' + rar.label + '</div>' + '<div class="sell-card-name">' + card.name + '</div>' + '<div class="sell-card-meta">' + '<span>\u00d7' + sellable + ' to sell</span>' + '<span class="sell-card-gain">+' + formatMoney(sellable * price) + '</span>' + '</div>'; el.appendChild(imgWrap); el.appendChild(body); grid.appendChild(el); }); } /* Sell all button */ document.getElementById('sell-all-btn').addEventListener('click', function() { var dupes = getDuplicates(); var totalGain = calcTotal(dupes); if (totalGain === 0) return; dupes.forEach(function(card) { col[card.id] = 1; }); v.playerMoney += totalGain; var balEl = document.getElementById('sell-balance'); if (balEl) { balEl.textContent = formatMoney(v.playerMoney); balEl.style.transition = 'color 0.15s'; balEl.style.color = '#00f5ff'; setTimeout(function() { balEl.style.color = ''; }, 900); } if (typeof buildNavSidebar === 'function') buildNavSidebar(); renderGrid(); }); renderGrid(); })(); window.showFirstVisitTip('sell_cards', { icon: '💱', title: 'Sell Duplicates', body: 'Got multiples of the same card? Sell the extras for cash. You always keep at least one copy of each card.', bodyScarlett: 'Got doubles? Sell the extras for cash. I always make sure you keep at least one copy of each card — I\'m generous like that.', tips: [ '⚪ Common → $10 each', '🔵 Rare → $30 each', '🟣 Epic → $80 each', '🌟 Legendary → $200 each' ] }); <</script>> <</done>> <style> /* ══════════════════════════════════════════ SELL CARDS ══════════════════════════════════════════ */ .sell-intro { font-family: 'Rajdhani', sans-serif; font-size: 1rem; font-weight: 500; color: rgba(210,200,240,0.65); line-height: 1.6; margin-bottom: 12px; padding: 12px 16px; background: rgba(255,255,255,0.02); border-radius: 10px; border-left: 2px solid rgba(0,245,255,0.25); } .sell-intro strong { color: rgba(255,255,255,0.85); font-weight: 700; } .sell-info-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; padding: 10px 14px; background: rgba(0,245,255,0.03); border: 1px solid rgba(0,245,255,0.1); border-radius: 12px; } .sell-price-chip { font-family: 'Share Tech Mono', monospace; font-size: 0.75rem; letter-spacing: 0.08em; padding: 3px 10px; border-radius: 20px; } .sell-price-common { background: rgba(160,160,176,0.1); color:#a0a0b0; border:1px solid rgba(160,160,176,0.2); } .sell-price-rare { background: rgba(79,195,247,0.08); color:#4fc3f7; border:1px solid rgba(79,195,247,0.2); } .sell-price-epic { background: rgba(191,0,255,0.08); color:#bf00ff; border:1px solid rgba(191,0,255,0.2); } .sell-price-legendary{ background: rgba(255,215,0,0.08); color:var(--gold); border:1px solid rgba(255,215,0,0.25); } .sell-info-note { font-family: 'Rajdhani', sans-serif; font-size: 0.82rem; color: rgba(210,200,235,0.35); margin-left: auto; font-style: italic; } /* Summary box */ .sell-summary-box { display: flex; align-items: center; gap: 16px; padding: 16px 20px; margin-bottom: 16px; background: rgba(0,245,255,0.05); border: 1px solid rgba(0,245,255,0.15); border-radius: 14px; flex-wrap: wrap; } .sell-summary-left { display: flex; align-items: baseline; gap: 6px; } .sell-summary-count { font-family: 'Orbitron', monospace; font-size: 1.2rem; font-weight: 900; color: var(--text-primary); } .sell-summary-label { font-family: 'Rajdhani', sans-serif; font-size: 0.9rem; color: rgba(210,200,235,0.45); } .sell-summary-value { font-family: 'Share Tech Mono', monospace; font-size: 1.4rem; font-weight: bold; color: var(--neon-cyan); text-shadow: 0 0 12px rgba(0,245,255,0.35); margin-left: auto; } .sell-confirm-btn { font-family: 'Orbitron', monospace; font-size: 0.78rem; font-weight: 900; letter-spacing: 0.1em; padding: 12px 24px; border-radius: 10px; background: linear-gradient(135deg, rgba(0,180,200,0.3), rgba(0,245,255,0.2)); border: 1px solid rgba(0,245,255,0.4); color: var(--neon-cyan); cursor: pointer; transition: all 0.2s; text-shadow: 0 0 8px rgba(0,245,255,0.3); } .sell-confirm-btn:hover:not(:disabled) { background: linear-gradient(135deg, rgba(0,200,220,0.4), rgba(0,245,255,0.3)); box-shadow: 0 0 20px rgba(0,245,255,0.25); transform: translateY(-2px); } .sell-confirm-btn:disabled { opacity: 0.3; cursor: not-allowed; transform: none; } /* Grille */ .sell-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; } .sell-card { background: var(--dark-card); border: 2px solid; border-radius: 12px; overflow: hidden; } .sell-card-img-wrap { position: relative; width: 100%; aspect-ratio: 2/3; overflow: hidden; } .sell-card-img { width: 100%; height: 100%; object-fit: cover; display: block; } .sell-card-placeholder { width: 100%; height: 100%; display: none; align-items: center; justify-content: center; font-size: 2rem; background: var(--dark-panel); } .sell-card-count-badge { position: absolute; top: 5px; right: 5px; font-family: 'Orbitron', monospace; font-size: 0.65rem; font-weight: 900; padding: 2px 7px; border-radius: 20px; background: rgba(6,4,15,0.88); color: rgba(255,255,255,0.9); border: 1px solid rgba(255,255,255,0.2); backdrop-filter: blur(4px); } .sell-card-body { padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 4px; } .sell-card-rarity { font-family: 'Share Tech Mono', monospace; font-size: 0.68rem; letter-spacing: 0.08em; } .sell-card-name { font-family: 'Rajdhani', sans-serif; font-size: 0.85rem; font-weight: 700; color: var(--text-primary); line-height: 1.2; } .sell-card-meta { display: flex; justify-content: space-between; align-items: center; font-family: 'Share Tech Mono', monospace; font-size: 0.68rem; color: rgba(210,200,235,0.45); margin-top: 2px; } .sell-card-gain { color: var(--neon-cyan); font-weight: bold; } /* Empty state */ .sell-empty { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 60px 20px; text-align: center; } .sell-empty-icon { font-size: 3rem; opacity: 0.25; } .sell-empty p { font-family: 'Rajdhani', sans-serif; font-size: 1rem; color: rgba(210,200,235,0.3); } </style>
<div class="spin-wheel-page"> <div class="spin-wheel-header"> <h1 class="spin-wheel-title">🎡 Lucky Wheel</h1> <p class="spin-wheel-subtitle" id="spin-wheel-subtitle">Spin to win</p> </div> <div class="spin-wheel-arena"> <div class="spin-wheel-pointer">▼</div> <svg id="spin-wheel-svg" width="360" height="360" viewBox="0 0 360 360"></svg> <!-- Overlay résultat centré sur/au-dessus de la roue --> <div class="spin-result-overlay" id="spin-result-box"> <div class="spin-result-icon" id="spin-result-icon">🎉</div> <div class="spin-result-label">Your reward</div> <div class="spin-result-value" id="spin-result-value"></div> <div class="spin-result-card" id="spin-result-card" style="display:none"></div> <a class="btn-claim" id="spin-claim-btn">✦ Claim & Continue</a> </div> </div> <div class="spin-wheel-btn-wrap"> <a class="btn-spin" id="spin-btn">SPIN THE WHEEL</a> </div> </div> <<done>> <<script>> (function() { var v = State.variables; var branch = v.activeBranch || 'hetero'; /* Sous-titre selon la branche */ var subtitles = { hetero:'Straight Edition', gay:'Gay Edition', trans:'Trans Edition' }; var sub = document.getElementById('spin-wheel-subtitle'); if (sub) sub.textContent = subtitles[branch] || 'Spin to win'; /* Récupérer les segments de la branche */ var segments = (window.WHEEL_SEGMENTS && window.WHEEL_SEGMENTS[branch]) ? window.WHEEL_SEGMENTS[branch] : window.WHEEL_SEGMENTS.hetero; var n = segments.length; /* 10 */ /* ── Construire le SVG ── */ var svg = document.getElementById('spin-wheel-svg'); var size = 420; svg.setAttribute('width', size); svg.setAttribute('height', size); svg.setAttribute('viewBox', '0 0 ' + size + ' ' + size); var cx = size / 2, cy = size / 2; var rOuter = size / 2 - 10; /* rayon des segments */ var rInner = 34; /* rayon du hub central */ var TAU = Math.PI * 2; var angleStep = TAU / n; /* Palettes de couleurs raffinées par paire de segments */ var PALETTES = [ ['#c0375a', '#e05575'], /* rose foncé / clair */ ['#1a7a6e', '#22a090'], /* teal foncé / clair */ ['#8b4513', '#c4611c'], /* brun / orange */ ['#4a1a7a', '#6b28b0'], /* violet */ ['#8b1a1a', '#b52222'], /* rouge */ ['#1a5a8b', '#2278b5'], /* bleu */ ['#7a6a00', '#b09800'], /* doré terne / vif */ ['#1a7a3a', '#22a050'], /* vert */ ['#6b1a5a', '#9a2882'], /* magenta */ ['#2a2a6b', '#3a3ab0'], /* bleu nuit */ ]; var defs = '<defs>'; for (var i = 0; i < n; i++) { var colors = PALETTES[i % PALETTES.length]; /* Gradient radial : plus clair vers le centre, plus sombre vers le bord */ defs += '<radialGradient id="sg' + i + '" cx="45%" cy="35%" r="70%">' + '<stop offset="0%" stop-color="' + colors[1] + '" stop-opacity="1"/>' + '<stop offset="100%" stop-color="' + colors[0] + '" stop-opacity="1"/>' + '</radialGradient>'; } defs += '</defs>'; var svgContent = defs; /* ── Fond noir derrière la roue ── */ svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rOuter + 8) + '" fill="#0a0616"/>'; /* ── Anneau décoratif extérieur ── */ svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rOuter + 6) + '"' + ' fill="none" stroke="rgba(255,215,0,0.08)" stroke-width="10"/>'; svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rOuter + 2) + '"' + ' fill="none" stroke="rgba(255,215,0,0.55)" stroke-width="2.5"/>'; svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rOuter - 2) + '"' + ' fill="none" stroke="rgba(255,215,0,0.18)" stroke-width="1"/>'; /* ── Segments ── */ for (var i = 0; i < n; i++) { var startA = i * angleStep - Math.PI / 2; var endA = (i + 1) * angleStep - Math.PI / 2; var x1 = cx + rOuter * Math.cos(startA); var y1 = cy + rOuter * Math.sin(startA); var x2 = cx + rOuter * Math.cos(endA); var y2 = cy + rOuter * Math.sin(endA); var d = 'M ' + cx.toFixed(1) + ' ' + cy.toFixed(1) + ' L ' + x1.toFixed(1) + ' ' + y1.toFixed(1) + ' A ' + rOuter + ' ' + rOuter + ' 0 0 1 ' + x2.toFixed(1) + ' ' + y2.toFixed(1) + ' Z'; /* Segment rempli + bordure séparatrice fine */ svgContent += '<path d="' + d + '" fill="url(#sg' + i + ')" stroke="rgba(0,0,0,0.4)" stroke-width="1.5"/>'; /* Arc décoratif intérieur (anneau lumineux à 80% du rayon) */ var rArc = rOuter * 0.80; var arcX1 = cx + rArc * Math.cos(startA + 0.05); var arcY1 = cy + rArc * Math.sin(startA + 0.05); var arcX2 = cx + rArc * Math.cos(endA - 0.05); var arcY2 = cy + rArc * Math.sin(endA - 0.05); svgContent += '<path d="M ' + arcX1.toFixed(1) + ' ' + arcY1.toFixed(1) + ' A ' + rArc + ' ' + rArc + ' 0 0 1 ' + arcX2.toFixed(1) + ' ' + arcY2.toFixed(1) + '"' + ' fill="none" stroke="rgba(255,255,255,0.12)" stroke-width="1.5" stroke-linecap="round"/>'; /* ── Texte du segment ── */ var midA = (i + 0.5) * angleStep - Math.PI / 2; var tDist = rOuter * 0.58; var tx = cx + tDist * Math.cos(midA); var ty = cy + tDist * Math.sin(midA); var rotDeg = (i + 0.5) * (360 / n); var rawLabel = segments[i].label; /* Séparer icône (premier char si emoji/spécial) et valeur */ /* On cherche si le label contient un espace — si oui : 2 lignes */ var lines; if (rawLabel.indexOf(' ') > -1) { var spaceIdx = rawLabel.indexOf(' '); lines = [ rawLabel.slice(0, spaceIdx), rawLabel.slice(spaceIdx + 1) ]; } else { lines = [ rawLabel ]; } var lineH = 18; /* hauteur de ligne en px SVG */ var totalH = lines.length * lineH; var startY = ty - totalH / 2 + lineH * 0.5; svgContent += '<g transform="rotate(' + rotDeg.toFixed(1) + ',' + tx.toFixed(1) + ',' + ty.toFixed(1) + ')">'; for (var li = 0; li < lines.length; li++) { var lx = tx, ly = startY + li * lineH; var fs = li === 0 && lines.length > 1 ? '13' : '16'; var fw = '900'; var opacity = li === 0 && lines.length > 1 ? '0.75' : '1'; /* Ombre portée pour lisibilité */ svgContent += '<text x="' + lx.toFixed(1) + '" y="' + ly.toFixed(1) + '"' + ' text-anchor="middle" dominant-baseline="middle"' + ' fill="rgba(0,0,0,0.7)" font-weight="' + fw + '"' + ' font-family="Orbitron,monospace" font-size="' + fs + '"' + ' dx="1" dy="1">' + lines[li] + '</text>'; svgContent += '<text x="' + lx.toFixed(1) + '" y="' + ly.toFixed(1) + '"' + ' text-anchor="middle" dominant-baseline="middle"' + ' fill="rgba(255,255,255,' + opacity + ')" font-weight="' + fw + '"' + ' font-family="Orbitron,monospace" font-size="' + fs + '">' + lines[li] + '</text>'; } svgContent += '</g>'; } /* ── Séparateurs dorés entre segments ── */ for (var i = 0; i < n; i++) { var a = i * angleStep - Math.PI / 2; var lx = cx + rInner * Math.cos(a); var ly = cy + rInner * Math.sin(a); var lx2 = cx + rOuter * Math.cos(a); var ly2 = cy + rOuter * Math.sin(a); svgContent += '<line x1="' + lx.toFixed(1) + '" y1="' + ly.toFixed(1) + '"' + ' x2="' + lx2.toFixed(1) + '" y2="' + ly2.toFixed(1) + '"' + ' stroke="rgba(255,215,0,0.3)" stroke-width="1"/>'; } /* ── Anneau intérieur avant le hub ── */ svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rInner + 4) + '"' + ' fill="rgba(0,0,0,0.45)" stroke="rgba(255,215,0,0.25)" stroke-width="1"/>'; /* ── Hub central ── */ svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + rInner + '"' + ' fill="radial-gradient(#1a0a30,#06040f)" stroke="none"/>'; svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + rInner + '"' + ' fill="#09060f"/>'; svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rInner - 2) + '"' + ' fill="none" stroke="rgba(255,215,0,0.7)" stroke-width="2"/>'; svgContent += '<circle cx="' + cx + '" cy="' + cy + '" r="' + (rInner - 8) + '"' + ' fill="none" stroke="rgba(255,215,0,0.2)" stroke-width="1"/>'; svgContent += '<text x="' + cx + '" y="' + (cy + 1) + '"' + ' text-anchor="middle" dominant-baseline="middle"' + ' fill="rgba(255,215,0,0.9)" font-size="20">🎡</text>'; svg.innerHTML = svgContent; /* ── Logique de spin ── */ var currentRotation = 0; var spinning = false; var spinBtn = document.getElementById('spin-btn'); spinBtn.addEventListener('click', function() { if (spinning) return; spinning = true; spinBtn.classList.add('disabled'); /* Tirer le segment gagnant */ var winIndex = Math.floor(Math.random() * n); var seg = segments[winIndex]; /* Calculer la rotation cible */ /* Pour que le segment winIndex soit sous le pointeur (haut = 270° = -90°) */ /* Le pointeur est à 0° dans le repère SVG (haut), segments commencent à -90° */ /* Angle du centre du segment winIndex dans la rotation actuelle */ var segCenterDeg = (winIndex + 0.5) * (360 / n); /* depuis le top, sens horaire */ /* On veut ramener ce segment au pointeur (0°) → rotation = -segCenterDeg */ /* + plusieurs tours complets pour l'effet dramatique */ var fullSpins = 5 + Math.floor(Math.random() * 3); var targetRot = currentRotation + (fullSpins * 360) + (360 - segCenterDeg); /* Normaliser pour éviter drift infini */ currentRotation = targetRot; svg.style.transition = 'transform 4.5s cubic-bezier(0.17, 0.67, 0.12, 1.0)'; svg.style.transform = 'rotate(' + targetRot + 'deg)'; /* Après la fin de l'animation */ setTimeout(function() { showResult(seg, winIndex); }, 4700); }); /* ── Affichage du résultat ── */ function showResult(seg, idx) { var box = document.getElementById('spin-result-box'); var iconEl = document.getElementById('spin-result-icon'); var valueEl = document.getElementById('spin-result-value'); var cardDiv = document.getElementById('spin-result-card'); var claimBtn = document.getElementById('spin-claim-btn'); v._wheelRewardType = seg.type; v._wheelRewardValue = seg.value; if (seg.type === 'money') { iconEl.textContent = '💰'; valueEl.textContent = '+' + formatMoney(seg.value); valueEl.style.color = '#ffd700'; claimBtn.style.display = ''; claimBtn.textContent = '✦ Claim & Continue'; claimBtn.onclick = function() { v.playerMoney += v._wheelRewardValue; finalizeClaim(); }; } else if (seg.type === 'tokens') { iconEl.textContent = '🎫'; valueEl.textContent = '+' + seg.value + ' tokens'; valueEl.style.color = 'var(--neon-cyan)'; claimBtn.style.display = 'none'; /* Afficher le sélecteur de table après un court délai */ setTimeout(function() { showTablePicker('tokens', seg.value, box); }, 400); } else if (seg.type === 'card') { iconEl.textContent = '🎴'; valueEl.textContent = '1 Pack'; valueEl.style.color = '#d966ff'; claimBtn.style.display = 'none'; setTimeout(function() { showTablePicker('pack', 1, box); }, 400); } box.style.display = 'flex'; box.style.animation = 'resultReveal 0.4s cubic-bezier(0.34,1.1,0.64,1) forwards'; /* Bloquer la sidebar pour empêcher de naviguer sans claim */ document.querySelectorAll('#nav-sidebar .nsb-btn, #nav-sidebar a').forEach(function(el) { el.style.pointerEvents = 'none'; el.style.opacity = '0.25'; }); } /* ── Sélecteur de table ── */ function showTablePicker(rewardType, rewardValue, box) { /* Supprimer un éventuel picker existant */ var old = document.getElementById('wheel-table-picker'); if (old) old.remove(); /* Tables disponibles selon la branche */ var branchTables = window.TABLES.filter(function(t) { if (branch === 'hetero') return t.branch === 'hetero'; if (branch === 'gay') return t.branch === 'gay'; if (branch === 'trans') return t.branch === 'trans'; return false; }); var picker = document.createElement('div'); picker.id = 'wheel-table-picker'; picker.className = 'wtp-overlay'; var title = rewardType === 'tokens' ? '🎫 +' + rewardValue + ' tokens — Choose your table' : '🎴 1 Free Pack — Choose your table'; var rows = branchTables.map(function(t) { /* Vérifier si la table est débloquée */ var isUnlocked = ( branch === 'hetero' ? v.unlockedTables.indexOf(t.id) !== -1 : branch === 'gay' ? v.unlockedTablesGay.indexOf(t.id) !== -1 : v.unlockedTablesTrans.indexOf(t.id) !== -1 ); /* Compter cartes possédées vs total pour ce pack */ var pack = window.PACKS.find(function(p) { return p.table === t.id && !p.forcedRarity; }); var owned = 0, total = 0; if (pack) { total = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1; }).length; owned = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1 && v.collection[c.id] !== undefined; }).length; } var pct = total > 0 ? Math.round(owned / total * 100) : 0; if (!isUnlocked) { return '<div class="wtp-row wtp-row-locked">' + '<span class="wtp-emoji" style="filter:grayscale(1) opacity(0.4)">' + t.emoji + '</span>' + '<span class="wtp-name" style="opacity:0.35">' + t.name + '</span>' + '<span class="wtp-locked-badge">🔒 Not unlocked</span>' + '</div>'; } return '<button class="wtp-row" data-table="' + t.id + '">' + '<span class="wtp-emoji">' + t.emoji + '</span>' + '<span class="wtp-name">' + t.name + '</span>' + '<span class="wtp-progress">' + '<span class="wtp-count">' + owned + '/' + total + '</span>' + '<span class="wtp-bar"><span class="wtp-bar-fill" style="width:' + pct + '%"></span></span>' + '</span>' + '</button>'; }).join(''); picker.innerHTML = '<div class="wtp-box">' + '<div class="wtp-title">' + title + '</div>' + '<div class="wtp-subtitle">Your collection progress per table :</div>' + '<div class="wtp-list">' + rows + '</div>' + '</div>'; /* Insérer dans la page (pas dans l'overlay roue) */ document.body.appendChild(picker); /* Listeners sur chaque ligne */ picker.querySelectorAll('button.wtp-row').forEach(function(btn) { btn.addEventListener('click', function() { var tableId = this.getAttribute('data-table'); if (rewardType === 'tokens') { if (!v.tokens) v.tokens = {}; v.tokens[tableId] = (v.tokens[tableId] || 0) + rewardValue; picker.remove(); finalizeClaim(); } else { /* Pack : ouvrir un pack de la table choisie */ var pack = window.PACKS.find(function(p) { return p.table === tableId && !p.forcedRarity; }); picker.remove(); if (pack && window.openPack) { var result = window.openPack(pack.id, true); if (result === true || result === 'free') { finalizeClaim(true); /* true = ne pas rediriger casino, aller Open Pack */ } else { finalizeClaim(); } } else { finalizeClaim(); } } }); }); } function finalizeClaim(openPackPage) { /* Réactiver la sidebar */ document.querySelectorAll('#nav-sidebar .nsb-btn, #nav-sidebar a').forEach(function(el) { el.style.pointerEvents = ''; el.style.opacity = ''; }); v.wheelHandsCount = 0; v.wheelReady = false; delete v._wheelRewardType; delete v._wheelRewardValue; delete v._wheelCardId; if (openPackPage) { Engine.play('Open Pack'); } else { Engine.play('Casino'); } } /* Claim simple (argent) */ var claimBtn = document.getElementById('spin-claim-btn'); if (claimBtn && !claimBtn._bound) { claimBtn._bound = true; } /* Appliquer le fond de table */ document.body.style.backgroundImage = ''; document.body.style.backgroundSize = ''; document.body.style.backgroundPosition = ''; })(); window.showFirstVisitTip('spin_wheel', { icon: '🎡', title: 'Lucky Wheel', body: 'The Lucky Wheel unlocks every 20 hands played. Spin it to win cash, tokens or a free pack — always a positive reward.', bodyScarlett: 'Every 20 hands, I give you a little gift. Spin it — there\'s no losing here. Consider it my way of keeping you at the table.', tips: [ '🎡 Unlocks every 20 hands, automatically', '💰 Cash rewards go directly to your balance', '🎫 Tokens — choose which table gets them', '🎴 Pack reward — choose your table too' ] }); <</script>> <</done>>
/* ══════════════════════════════════════════ SETUP : VALEURS DES CARTES & CONFIG ══════════════════════════════════════════ */ <<set setup.cardValues = { "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "10": 10, "j": 10, "q": 10, "k": 10, "ace": 11 }>> <<set setup.suits = ["spades", "clubs", "diamonds", "hearts"]>> <<set setup.ranks = ["2","3","4","5","6","7","8","9","10","j","q","k","ace"]>> /* ══════════════════════════════════════════ FONCTIONS JAVASCRIPT ══════════════════════════════════════════ */ <<script>> /* Tirer une carte aléatoire (sabot infini) */ window.drawCard = function() { const suit = setup.suits[Math.floor(Math.random() * 4)]; const rank = setup.ranks[Math.floor(Math.random() * 13)]; return { rank, suit }; }; /* Calculer la valeur d'une main (gestion As) */ window.calculateHand = function(hand) { let value = 0, aces = 0; hand.forEach(card => { value += setup.cardValues[card.rank]; if (card.rank === "ace") aces++; }); while (value > 21 && aces > 0) { value -= 10; aces--; } return value; }; /* Affichage du score : X/Y quand main souple (As valant 11), sinon juste la valeur */ window.softDisplayScore = function(hand) { var hard = 0; hand.forEach(function(c) { hard += (c.rank === 'ace') ? 1 : setup.cardValues[c.rank]; }); var soft = calculateHand(hand); /* Main souple = soft > hard, soft <= 21, pas un blackjack */ if (soft > hard && soft <= 21 && !isBlackjack(hand)) { return hard + ' / ' + soft; } return '' + soft; }; /* Afficher une carte (image) */ window.displayCard = function(card, hidden = false) { if (hidden) { return `<div class="card card-back"> <div class="card-inner">🂠</div> </div>`; } const suitSymbols = { spades: "♠", clubs: "♣", diamonds: "♦", hearts: "♥" }; const rankDisplay = { "ace": "A", "j": "J", "q": "Q", "k": "K" }; const r = rankDisplay[card.rank] || card.rank; const s = suitSymbols[card.suit]; const isRed = (card.suit === "diamonds" || card.suit === "hearts"); const colorClass = isRed ? "card-red" : "card-black"; return `<div class="card ${colorClass}"> <div class="card-corner card-tl">${r}<br>${s}</div> <div class="card-center">${s}</div> <div class="card-corner card-br">${r}<br>${s}</div> </div>`; }; /* Afficher une main complète */ window.displayHand = function(hand, hideFirst = false) { let html = '<div class="hand-display">'; hand.forEach((card, i) => { html += displayCard(card, hideFirst && i === 0); }); html += '</div>'; return html; }; /* Peut-on splitter ? */ window.canSplit = function(hand) { if (hand.length !== 2) return false; return setup.cardValues[hand[0].rank] === setup.cardValues[hand[1].rank]; }; /* Blackjack naturel = As + carte valant 10, en 2 cartes */ window.isBlackjack = function(hand) { if (hand.length !== 2) return false; const hasAce = hand.some(c => c.rank === "ace"); const hasTen = hand.some(c => setup.cardValues[c.rank] === 10 && c.rank !== "ace"); return hasAce && hasTen; }; /* Formater l'argent joliment */ window.formatMoney = function(n) { return "$" + n.toLocaleString(); }; /* ══════════════════════════════════════════ DONNÉES : RARETÉS ══════════════════════════════════════════ */ window.RARITIES = { common: { label:"Common", color:"#a0a0b0", glow:"rgba(160,160,176,0.4)" }, rare: { label:"Rare", color:"#4fc3f7", glow:"rgba(79,195,247,0.5)" }, epic: { label:"Epic", color:"#bf00ff", glow:"rgba(191,0,255,0.5)" }, legendary: { label:"Legendary", color:"#ffd700", glow:"rgba(255,215,0,0.6)" } }; /* Prix de revente par rareté */ window.SELL_PRICES = { common:10, rare:30, epic:80, legendary:200 }; /* ══════════════════════════════════════════ BRANCHES hetero : progression indépendante gay : progression indépendante, unlock via bouton ══════════════════════════════════════════ */ window.BRANCHES = { hetero: { id:"hetero", label:"Straight", earnedVar:"earnedHetero" }, gay: { id:"gay", label:"Gay", earnedVar:"earnedGay" }, trans: { id:"trans", label:"Trans", earnedVar:"earnedTrans" } }; /* ══════════════════════════════════════════ TABLES branch : "hetero" | "gay" | "both" threshold : earnedXxx requis (0 = toujours ouverte dans sa branche) ══════════════════════════════════════════ */ window.TABLES = [ /* ── BRANCHE HÉTÉRO ── */ { id:"vice", branch:"hetero", name:"The Vice", emoji:"🃏", threshold:0, minBet:10, maxBet:250, color:"linear-gradient(90deg,#ff2d78,#bf00ff)" }, { id:"boudoir", branch:"hetero", name:"The Boudoir", emoji:"💋", threshold:5000, minBet:25, maxBet:500, color:"linear-gradient(90deg,#ff69b4,#ff2d78)" }, { id:"heat", branch:"hetero", name:"The Heat", emoji:"🔥", threshold:20000, minBet:50, maxBet:1000, color:"linear-gradient(90deg,#ff6600,#ff2d78)" }, { id:"slutpit", branch:"hetero", name:"The Slut Pit", emoji:"💀", threshold:30000, minBet:75, maxBet:1500, color:"linear-gradient(90deg,#8b0000,#ff2d78)" }, { id:"heterovip", branch:"hetero", name:"Hetero VIP", emoji:"👑", threshold:75000, minBet:100, maxBet:2500, color:"linear-gradient(90deg,#ffd700,#ff2d78)" }, /* ── BRANCHE GAY ── */ { id:"rainbow", branch:"gay", name:"The Rainbow", emoji:"🌈", threshold:0, minBet:10, maxBet:250, color:"linear-gradient(90deg,#a855f7,#3b82f6)" }, { id:"gayvip", branch:"gay", name:"Gay VIP", emoji:"🏳️🌈", threshold:30000, minBet:100, maxBet:2500, color:"linear-gradient(90deg,#ffd700,#a855f7)" }, /* ── BRANCHE TRANS ── */ { id:"transroom", branch:"trans", name:"The Trans Room",emoji:"💜", threshold:0, minBet:10, maxBet:250, color:"linear-gradient(90deg,#f9a8d4,#a855f7)" }, { id:"transvip", branch:"trans", name:"Trans VIP", emoji:"🏳️⚧️", threshold:30000, minBet:100, maxBet:2500, color:"linear-gradient(90deg,#ffd700,#f9a8d4)" }, /* ── VIP ULTIME ── */ { id:"penthouse", branch:"both", name:"The Penthouse", emoji:"💎", threshold:200000, minBet:500, maxBet:5000, color:"linear-gradient(90deg,#ffd700,#00f5ff)" } ]; /* ══════════════════════════════════════════ CATÉGORIES — tag → label + branch ══════════════════════════════════════════ */ window.CATEGORIES = [ /* Hétéro */ { tag:"girl_strip", label:"Girl Strip", branch:"hetero" }, { tag:"girl_ass", label:"Girl Ass", branch:"hetero" }, { tag:"tits", label:"Tits", branch:"hetero" }, { tag:"pussy_eating", label:"Pussy Eating", branch:"hetero" }, { tag:"handjob", label:"Handjob", branch:"hetero" }, { tag:"handjob_cum", label:"Handjob Cum", branch:"hetero" }, { tag:"anal", label:"Anal", branch:"hetero" }, { tag:"blowjob", label:"Blowjob", branch:"hetero" }, { tag:"fingering", label:"Fingering", branch:"hetero" }, { tag:"pussy_fucking", label:"Pussy Fucking", branch:"hetero" }, { tag:"tits_fuck", label:"Tits Fuck", branch:"hetero" }, { tag:"rimjob", label:"Rimjob", branch:"hetero" }, { tag:"bbc", label:"BBC", branch:"hetero" }, { tag:"creampie", label:"Creampie", branch:"hetero" }, { tag:"threesome", label:"Threesome", branch:"hetero" }, { tag:"gangbang", label:"Gangbang", branch:"hetero" }, { tag:"cumshot", label:"Cumshot", branch:"hetero" }, /* Gay */ { tag:"gay_solo", label:"Gay Solo", branch:"gay" }, { tag:"gay_scenes", label:"Gay Scenes", branch:"gay" }, /* Trans */ { tag:"sissy", label:"Sissy", branch:"trans" }, { tag:"trans_solo", label:"Trans Solo", branch:"trans" }, { tag:"trans_scenes", label:"Trans Scenes", branch:"trans" } ]; /* ══════════════════════════════════════════ PAQUETS categories[] : tags autorisés dans ce pack forcedRarity : si défini, toutes les cartes ont cette rareté ══════════════════════════════════════════ */ window.PACKS = [ /* ── THE VICE ── */ { id:"vice_pack", table:"vice", branch:"hetero", price:50, name:"Vice Pack", emoji:"🃏", desc:"Girl Strip · Girl Ass · Tits · Lesbian · Pussy Eating · Massage", categories:["girl_strip","girl_ass","tits","pussy_eating"] }, /* ── THE BOUDOIR ── */ { id:"boudoir_pack", table:"boudoir", branch:"hetero", price:50, name:"Boudoir Pack", emoji:"💋", desc:"Pussy Eating · Handjob · Blowjob · Fingering · Tit Fuck", categories:["pussy_eating","handjob","handjob_cum","fingering","tits_fuck","blowjob"] }, /* ── THE HEAT ── */ { id:"heat_pack", table:"heat", branch:"hetero", price:50, name:"Heat Pack", emoji:"🔥", desc:"Anal · Blowjob · Fingering · Pussy Fucking · Tit Fuck · Rimjob · Cumshot", categories:["anal","blowjob","handjob_cum","fingering","pussy_fucking","rimjob","cumshot"] }, /* ── THE SLUT PIT ── */ { id:"slutpit_pack", table:"slutpit", branch:"hetero", price:50, name:"Slut Pit Pack", emoji:"💀", desc:"BBC · Creampie · Threesome · Gangbang · Rimjob · Cumshot", categories:["bbc","creampie","threesome","gangbang","rimjob","cumshot"] }, /* ── HETERO VIP ── */ { id:"heterovip_pack", table:"heterovip", branch:"hetero", price:80, name:"Hetero VIP Pack", emoji:"👑", desc:"Exclusive hetero content — all categories", categories:["girl_strip","girl_ass","tits","pussy_eating","handjob","anal","blowjob","handjob_cum","fingering","tits_fuck","pussy_fucking","rimjob","bbc","creampie","threesome","gangbang","cumshot"] }, { id:"heterovip_legendary", table:"heterovip", branch:"hetero", price:200, name:"Hetero Legendary Pack", emoji:"🌟", desc:"Guaranteed Legendary — hetero only", categories:["girl_strip","girl_ass","tits","pussy_eating","handjob","anal","blowjob","handjob_cum","fingering","tits_fuck","pussy_fucking","rimjob","bbc","creampie","threesome","gangbang","cumshot"], forcedRarity:"legendary" }, /* ── THE RAINBOW ── */ { id:"rainbow_pack", table:"rainbow", branch:"gay", price:50, name:"Rainbow Pack", emoji:"🌈", desc:"Gay Solo · Gay Scenes", categories:["gay_solo","gay_scenes"] }, /* ── GAY VIP ── */ { id:"gayvip_pack", table:"gayvip", branch:"gay", price:80, name:"Gay VIP Pack", emoji:"🏳️🌈", desc:"Exclusive gay content — all categories", categories:["gay_solo","gay_scenes"] }, { id:"gayvip_legendary", table:"gayvip", branch:"gay", price:200, name:"Gay Legendary Pack", emoji:"🌟", desc:"Guaranteed Legendary — gay only", categories:["gay_solo","gay_scenes"], forcedRarity:"legendary" }, /* ── THE TRANS ROOM ── */ { id:"trans_pack", table:"transroom", branch:"trans", price:50, name:"Trans Pack", emoji:"💜", desc:"Sissy · Trans Solo · Trans Scenes", categories:["sissy","trans_solo","trans_scenes"] }, /* ── TRANS VIP ── */ { id:"transvip_pack", table:"transvip", branch:"trans", price:80, name:"Trans VIP Pack", emoji:"🏳️⚧️", desc:"Exclusive trans content — all categories", categories:["sissy","trans_solo","trans_scenes"] }, { id:"transvip_legendary", table:"transvip", branch:"trans", price:200, name:"Trans Legendary Pack", emoji:"🌟", desc:"Guaranteed Legendary — trans only", categories:["sissy","trans_solo","trans_scenes"], forcedRarity:"legendary" }, /* ── THE PENTHOUSE (VIP ULTIME) ── */ { id:"penthouse_pack", table:"penthouse", branch:"both", price:100, name:"Penthouse Pack", emoji:"💎", desc:"Wild Card — all categories from both branches", categories:["girl_strip","girl_ass","tits","pussy_eating","handjob","anal","blowjob","handjob_cum","fingering","tits_fuck","pussy_fucking","rimjob","bbc","creampie","threesome","gangbang","cumshot","gay_solo","gay_scenes","sissy","trans_solo","trans_scenes"] }, { id:"penthouse_legendary", table:"penthouse", branch:"both", price:250, name:"Penthouse Legendary Pack", emoji:"💎🌟", desc:"Guaranteed Legendary — all categories", categories:["girl_strip","girl_ass","tits","pussy_eating","handjob","anal","blowjob","handjob_cum","fingering","tits_fuck","pussy_fucking","rimjob","bbc","creampie","threesome","gangbang","cumshot","gay_solo","gay_scenes","sissy","trans_solo","trans_scenes"], forcedRarity:"legendary" } ]; /* ══════════════════════════════════════════ CARD_DB — vide au démarrage Ajouter les cartes ici au format : { id:"girl_strip_001", name:"Girl Strip #001", rarity:"common", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_001.gif" } ══════════════════════════════════════════ */ window.CARD_DB = [ /* ── GIRL STRIP ── */ { id:"girl_strip_1", name:"Girl Strip #1", rarity:"common", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_1.gif" }, { id:"girl_strip_2", name:"Girl Strip #2", rarity:"common", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_2.gif" }, { id:"girl_strip_3", name:"Girl Strip #3", rarity:"common", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_3.gif" }, { id:"girl_strip_4", name:"Girl Strip #4", rarity:"rare", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_4.gif" }, { id:"girl_strip_5", name:"Girl Strip #5", rarity:"rare", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_5.gif" }, { id:"girl_strip_6", name:"Girl Strip #6", rarity:"rare", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_6.gif" }, { id:"girl_strip_7", name:"Girl Strip #7", rarity:"epic", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_7.gif" }, { id:"girl_strip_8", name:"Girl Strip #8", rarity:"legendary", category:"girl_strip", file:"Images/cards/girl_strip/girl_strip_8.gif" }, /* ── GIRL ASS ── */ { id:"girl_ass_1", name:"Girl Ass #1", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_1.gif" }, { id:"girl_ass_2", name:"Girl Ass #2", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_2.gif" }, { id:"girl_ass_3", name:"Girl Ass #3", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_3.gif" }, { id:"girl_ass_4", name:"Girl Ass #4", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_4.gif" }, { id:"girl_ass_5", name:"Girl Ass #5", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_5.gif" }, { id:"girl_ass_6", name:"Girl Ass #6", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_6.gif" }, { id:"girl_ass_7", name:"Girl Ass #7", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_7.gif" }, { id:"girl_ass_8", name:"Girl Ass #8", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_8.gif" }, { id:"girl_ass_9", name:"Girl Ass #9", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_9.gif" }, { id:"girl_ass_10", name:"Girl Ass #10", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_10.gif" }, { id:"girl_ass_11", name:"Girl Ass #11", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_11.gif" }, { id:"girl_ass_12", name:"Girl Ass #12", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_12.gif" }, { id:"girl_ass_13", name:"Girl Ass #13", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_13.gif" }, { id:"girl_ass_14", name:"Girl Ass #14", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_14.gif" }, { id:"girl_ass_15", name:"Girl Ass #15", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_15.gif" }, { id:"girl_ass_16", name:"Girl Ass #16", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_16.gif" }, { id:"girl_ass_17", name:"Girl Ass #17", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_17.gif" }, { id:"girl_ass_18", name:"Girl Ass #18", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_18.gif" }, { id:"girl_ass_19", name:"Girl Ass #19", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_19.gif" }, { id:"girl_ass_20", name:"Girl Ass #20", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_20.gif" }, { id:"girl_ass_21", name:"Girl Ass #21", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_21.gif" }, { id:"girl_ass_22", name:"Girl Ass #22", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_22.gif" }, { id:"girl_ass_23", name:"Girl Ass #23", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_23.gif" }, { id:"girl_ass_24", name:"Girl Ass #24", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_24.gif" }, { id:"girl_ass_25", name:"Girl Ass #25", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_25.gif" }, { id:"girl_ass_26", name:"Girl Ass #26", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_26.gif" }, { id:"girl_ass_27", name:"Girl Ass #27", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_27.gif" }, { id:"girl_ass_28", name:"Girl Ass #28", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_28.gif" }, { id:"girl_ass_29", name:"Girl Ass #29", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_29.gif" }, { id:"girl_ass_30", name:"Girl Ass #30", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_30.gif" }, { id:"girl_ass_31", name:"Girl Ass #31", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_31.gif" }, { id:"girl_ass_32", name:"Girl Ass #32", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_32.gif" }, { id:"girl_ass_33", name:"Girl Ass #33", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_33.gif" }, { id:"girl_ass_34", name:"Girl Ass #34", rarity:"rare", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_34.gif" }, { id:"girl_ass_35", name:"Girl Ass #35", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_35.gif" }, { id:"girl_ass_36", name:"Girl Ass #36", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_36.gif" }, { id:"girl_ass_37", name:"Girl Ass #37", rarity:"legendary", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_37.gif" }, { id:"girl_ass_38", name:"Girl Ass #38", rarity:"common", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_38.gif" }, { id:"girl_ass_39", name:"Girl Ass #39", rarity:"epic", category:"girl_ass", file:"Images/cards/girl_ass/girl_ass_39.gif" }, /* ── TITS ── */ { id:"tits_1", name:"Tits #1", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_1.gif" }, { id:"tits_2", name:"Tits #2", rarity:"common", category:"tits", file:"Images/cards/tits/tits_2.gif" }, { id:"tits_3", name:"Tits #3", rarity:"common", category:"tits", file:"Images/cards/tits/tits_3.gif" }, { id:"tits_4", name:"Tits #4", rarity:"common", category:"tits", file:"Images/cards/tits/tits_4.gif" }, { id:"tits_5", name:"Tits #5", rarity:"common", category:"tits", file:"Images/cards/tits/tits_5.gif" }, { id:"tits_6", name:"Tits #6", rarity:"common", category:"tits", file:"Images/cards/tits/tits_6.gif" }, { id:"tits_7", name:"Tits #7", rarity:"legendary", category:"tits", file:"Images/cards/tits/tits_7.gif" }, { id:"tits_8", name:"Tits #8", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_8.gif" }, { id:"tits_9", name:"Tits #9", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_9.gif" }, { id:"tits_10", name:"Tits #10", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_10.gif" }, { id:"tits_11", name:"Tits #11", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_11.gif" }, { id:"tits_12", name:"Tits #12", rarity:"common", category:"tits", file:"Images/cards/tits/tits_12.gif" }, { id:"tits_13", name:"Tits #13", rarity:"common", category:"tits", file:"Images/cards/tits/tits_13.gif" }, { id:"tits_14", name:"Tits #14", rarity:"common", category:"tits", file:"Images/cards/tits/tits_14.gif" }, { id:"tits_15", name:"Tits #15", rarity:"common", category:"tits", file:"Images/cards/tits/tits_15.gif" }, { id:"tits_16", name:"Tits #16", rarity:"common", category:"tits", file:"Images/cards/tits/tits_16.gif" }, { id:"tits_17", name:"Tits #17", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_17.gif" }, { id:"tits_18", name:"Tits #18", rarity:"common", category:"tits", file:"Images/cards/tits/tits_18.gif" }, { id:"tits_19", name:"Tits #19", rarity:"common", category:"tits", file:"Images/cards/tits/tits_19.gif" }, { id:"tits_20", name:"Tits #20", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_20.gif" }, { id:"tits_21", name:"Tits #21", rarity:"common", category:"tits", file:"Images/cards/tits/tits_21.gif" }, { id:"tits_22", name:"Tits #22", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_22.gif" }, { id:"tits_23", name:"Tits #23", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_23.gif" }, { id:"tits_24", name:"Tits #24", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_24.gif" }, { id:"tits_25", name:"Tits #25", rarity:"common", category:"tits", file:"Images/cards/tits/tits_25.gif" }, { id:"tits_26", name:"Tits #26", rarity:"common", category:"tits", file:"Images/cards/tits/tits_26.gif" }, { id:"tits_27", name:"Tits #27", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_27.gif" }, { id:"tits_28", name:"Tits #28", rarity:"common", category:"tits", file:"Images/cards/tits/tits_28.gif" }, { id:"tits_29", name:"Tits #29", rarity:"common", category:"tits", file:"Images/cards/tits/tits_29.gif" }, { id:"tits_30", name:"Tits #30", rarity:"common", category:"tits", file:"Images/cards/tits/tits_30.gif" }, { id:"tits_31", name:"Tits #31", rarity:"common", category:"tits", file:"Images/cards/tits/tits_31.gif" }, { id:"tits_32", name:"Tits #32", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_32.gif" }, { id:"tits_33", name:"Tits #33", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_33.gif" }, { id:"tits_34", name:"Tits #34", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_34.gif" }, { id:"tits_35", name:"Tits #35", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_35.gif" }, { id:"tits_36", name:"Tits #36", rarity:"common", category:"tits", file:"Images/cards/tits/tits_36.gif" }, { id:"tits_37", name:"Tits #37", rarity:"common", category:"tits", file:"Images/cards/tits/tits_37.gif" }, { id:"tits_38", name:"Tits #38", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_38.gif" }, { id:"tits_39", name:"Tits #39", rarity:"common", category:"tits", file:"Images/cards/tits/tits_39.gif" }, { id:"tits_40", name:"Tits #40", rarity:"epic", category:"tits", file:"Images/cards/tits/tits_40.gif" }, { id:"tits_41", name:"Tits #41", rarity:"rare", category:"tits", file:"Images/cards/tits/tits_41.gif" }, { id:"tits_42", name:"Tits #42", rarity:"common", category:"tits", file:"Images/cards/tits/tits_42.gif" }, /* ── PUSSY EATING ── */ { id:"pussy_eating_1", name:"Pussy Eating #1", rarity:"rare", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_1.gif" }, { id:"pussy_eating_2", name:"Pussy Eating #2", rarity:"rare", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_2.gif" }, { id:"pussy_eating_3", name:"Pussy Eating #3", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_3.gif" }, { id:"pussy_eating_4", name:"Pussy Eating #4", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_4.gif" }, { id:"pussy_eating_5", name:"Pussy Eating #5", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_5.gif" }, { id:"pussy_eating_6", name:"Pussy Eating #6", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_6.gif" }, { id:"pussy_eating_7", name:"Pussy Eating #7", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_7.gif" }, { id:"pussy_eating_8", name:"Pussy Eating #8", rarity:"epic", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_8.gif" }, { id:"pussy_eating_9", name:"Pussy Eating #9", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_9.gif" }, { id:"pussy_eating_10", name:"Pussy Eating #10", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_10.gif" }, { id:"pussy_eating_11", name:"Pussy Eating #11", rarity:"rare", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_11.gif" }, { id:"pussy_eating_12", name:"Pussy Eating #12", rarity:"epic", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_12.gif" }, { id:"pussy_eating_13", name:"Pussy Eating #13", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_13.gif" }, { id:"pussy_eating_14", name:"Pussy Eating #14", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_14.gif" }, { id:"pussy_eating_15", name:"Pussy Eating #15", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_15.gif" }, { id:"pussy_eating_16", name:"Pussy Eating #16", rarity:"common", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_16.gif" }, { id:"pussy_eating_17", name:"Pussy Eating #17", rarity:"legendary", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_17.gif" }, { id:"pussy_eating_18", name:"Pussy Eating #18", rarity:"rare", category:"pussy_eating", file:"Images/cards/pussy_eating/pussy_eating_18.gif" }, /* ── HANDJOB ── */ { id:"handjob_1", name:"Handjob #1", rarity:"rare", category:"handjob", file:"Images/cards/handjob/handjob_1.gif" }, { id:"handjob_2", name:"Handjob #2", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_2.gif" }, { id:"handjob_3", name:"Handjob #3", rarity:"rare", category:"handjob", file:"Images/cards/handjob/handjob_3.gif" }, { id:"handjob_4", name:"Handjob #4", rarity:"rare", category:"handjob", file:"Images/cards/handjob/handjob_4.gif" }, { id:"handjob_5", name:"Handjob #5", rarity:"rare", category:"handjob", file:"Images/cards/handjob/handjob_5.gif" }, { id:"handjob_6", name:"Handjob #6", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_6.gif" }, { id:"handjob_7", name:"Handjob #7", rarity:"epic", category:"handjob", file:"Images/cards/handjob/handjob_7.gif" }, { id:"handjob_8", name:"Handjob #8", rarity:"rare", category:"handjob", file:"Images/cards/handjob/handjob_8.gif" }, { id:"handjob_9", name:"Handjob #9", rarity:"epic", category:"handjob", file:"Images/cards/handjob/handjob_9.gif" }, { id:"handjob_10", name:"Handjob #10", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_10.gif" }, { id:"handjob_11", name:"Handjob #11", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_11.gif" }, { id:"handjob_12", name:"Handjob #12", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_12.gif" }, { id:"handjob_13", name:"Handjob #13", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_13.gif" }, { id:"handjob_14", name:"Handjob #14", rarity:"rare", category:"handjob", file:"Images/cards/handjob/handjob_14.gif" }, { id:"handjob_15", name:"Handjob #15", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_15.gif" }, { id:"handjob_16", name:"Handjob #16", rarity:"epic", category:"handjob", file:"Images/cards/handjob/handjob_16.gif" }, { id:"handjob_17", name:"Handjob #17", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_17.gif" }, { id:"handjob_18", name:"Handjob #18", rarity:"legendary", category:"handjob", file:"Images/cards/handjob/handjob_18.gif" }, { id:"handjob_19", name:"Handjob #19", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_19.gif" }, { id:"handjob_20", name:"Handjob #20", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_20.gif" }, { id:"handjob_21", name:"Handjob #21", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_21.gif" }, { id:"handjob_22", name:"Handjob #22", rarity:"common", category:"handjob", file:"Images/cards/handjob/handjob_22.gif" }, /* ── FINGERING (18 cartes) ── */ { id:"fingering_1", name:"Fingering #1", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_1.gif" }, { id:"fingering_2", name:"Fingering #2", rarity:"rare", category:"fingering", file:"Images/cards/fingering/fingering_2.gif" }, { id:"fingering_3", name:"Fingering #3", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_3.gif" }, { id:"fingering_4", name:"Fingering #4", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_4.gif" }, { id:"fingering_5", name:"Fingering #5", rarity:"epic", category:"fingering", file:"Images/cards/fingering/fingering_5.gif" }, { id:"fingering_6", name:"Fingering #6", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_6.gif" }, { id:"fingering_7", name:"Fingering #7", rarity:"rare", category:"fingering", file:"Images/cards/fingering/fingering_7.gif" }, { id:"fingering_8", name:"Fingering #8", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_8.gif" }, { id:"fingering_9", name:"Fingering #9", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_9.gif" }, { id:"fingering_10", name:"Fingering #10", rarity:"legendary", category:"fingering", file:"Images/cards/fingering/fingering_10.gif" }, { id:"fingering_11", name:"Fingering #11", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_11.gif" }, { id:"fingering_12", name:"Fingering #12", rarity:"rare", category:"fingering", file:"Images/cards/fingering/fingering_12.gif" }, { id:"fingering_13", name:"Fingering #13", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_13.gif" }, { id:"fingering_14", name:"Fingering #14", rarity:"epic", category:"fingering", file:"Images/cards/fingering/fingering_14.gif" }, { id:"fingering_15", name:"Fingering #15", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_15.gif" }, { id:"fingering_16", name:"Fingering #16", rarity:"rare", category:"fingering", file:"Images/cards/fingering/fingering_16.gif" }, { id:"fingering_17", name:"Fingering #17", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_17.gif" }, { id:"fingering_18", name:"Fingering #18", rarity:"common", category:"fingering", file:"Images/cards/fingering/fingering_18.gif" }, /* ── HANDJOB CUM (16 cartes) ── */ { id:"handjob_cum_1", name:"Handjob Cum #1", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_1.gif" }, { id:"handjob_cum_2", name:"Handjob Cum #2", rarity:"rare", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_2.gif" }, { id:"handjob_cum_3", name:"Handjob Cum #3", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_3.gif" }, { id:"handjob_cum_4", name:"Handjob Cum #4", rarity:"epic", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_4.gif" }, { id:"handjob_cum_5", name:"Handjob Cum #5", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_5.gif" }, { id:"handjob_cum_6", name:"Handjob Cum #6", rarity:"rare", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_6.gif" }, { id:"handjob_cum_7", name:"Handjob Cum #7", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_7.gif" }, { id:"handjob_cum_8", name:"Handjob Cum #8", rarity:"legendary", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_8.gif" }, { id:"handjob_cum_9", name:"Handjob Cum #9", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_9.gif" }, { id:"handjob_cum_10", name:"Handjob Cum #10", rarity:"rare", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_10.gif" }, { id:"handjob_cum_11", name:"Handjob Cum #11", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_11.gif" }, { id:"handjob_cum_12", name:"Handjob Cum #12", rarity:"epic", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_12.gif" }, { id:"handjob_cum_13", name:"Handjob Cum #13", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_13.gif" }, { id:"handjob_cum_14", name:"Handjob Cum #14", rarity:"rare", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_14.gif" }, { id:"handjob_cum_15", name:"Handjob Cum #15", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_15.gif" }, { id:"handjob_cum_16", name:"Handjob Cum #16", rarity:"common", category:"handjob_cum", file:"Images/cards/handjob_cum/handjob_cum_16.gif" }, /* ── TITS FUCK (15 cartes) ── */ { id:"tits_fuck_1", name:"Tits Fuck #1", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_1.gif" }, { id:"tits_fuck_2", name:"Tits Fuck #2", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_2.gif" }, { id:"tits_fuck_3", name:"Tits Fuck #3", rarity:"rare", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_3.gif" }, { id:"tits_fuck_4", name:"Tits Fuck #4", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_4.gif" }, { id:"tits_fuck_5", name:"Tits Fuck #5", rarity:"epic", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_5.gif" }, { id:"tits_fuck_6", name:"Tits Fuck #6", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_6.gif" }, { id:"tits_fuck_7", name:"Tits Fuck #7", rarity:"rare", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_7.gif" }, { id:"tits_fuck_8", name:"Tits Fuck #8", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_8.gif" }, { id:"tits_fuck_9", name:"Tits Fuck #9", rarity:"legendary", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_9.gif" }, { id:"tits_fuck_10", name:"Tits Fuck #10", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_10.gif" }, { id:"tits_fuck_11", name:"Tits Fuck #11", rarity:"rare", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_11.gif" }, { id:"tits_fuck_12", name:"Tits Fuck #12", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_12.gif" }, { id:"tits_fuck_13", name:"Tits Fuck #13", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_13.gif" }, { id:"tits_fuck_14", name:"Tits Fuck #14", rarity:"epic", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_14.gif" }, { id:"tits_fuck_15", name:"Tits Fuck #15", rarity:"common", category:"tits_fuck", file:"Images/cards/tits_fuck/tits_fuck_15.gif" }, /* ── BLOWJOB (55 cartes) ── */ { id:"blowjob_1", name:"Blowjob #1", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_1.gif" }, { id:"blowjob_2", name:"Blowjob #2", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_2.gif" }, { id:"blowjob_3", name:"Blowjob #3", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_3.gif" }, { id:"blowjob_4", name:"Blowjob #4", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_4.gif" }, { id:"blowjob_5", name:"Blowjob #5", rarity:"legendary", category:"blowjob", file:"Images/cards/Blowjob/blowjob_5.gif" }, { id:"blowjob_6", name:"Blowjob #6", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_6.gif" }, { id:"blowjob_7", name:"Blowjob #7", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_7.gif" }, { id:"blowjob_8", name:"Blowjob #8", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_8.gif" }, { id:"blowjob_9", name:"Blowjob #9", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_9.gif" }, { id:"blowjob_10", name:"Blowjob #10", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_10.gif" }, { id:"blowjob_11", name:"Blowjob #11", rarity:"epic", category:"blowjob", file:"Images/cards/Blowjob/blowjob_11.gif" }, { id:"blowjob_12", name:"Blowjob #12", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_12.gif" }, { id:"blowjob_13", name:"Blowjob #13", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_13.gif" }, { id:"blowjob_14", name:"Blowjob #14", rarity:"epic", category:"blowjob", file:"Images/cards/Blowjob/blowjob_14.gif" }, { id:"blowjob_15", name:"Blowjob #15", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_15.gif" }, { id:"blowjob_16", name:"Blowjob #16", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_16.gif" }, { id:"blowjob_17", name:"Blowjob #17", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_17.gif" }, { id:"blowjob_18", name:"Blowjob #18", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_18.gif" }, { id:"blowjob_19", name:"Blowjob #19", rarity:"legendary", category:"blowjob", file:"Images/cards/Blowjob/blowjob_19.gif" }, { id:"blowjob_20", name:"Blowjob #20", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_20.gif" }, { id:"blowjob_21", name:"Blowjob #21", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_21.gif" }, { id:"blowjob_22", name:"Blowjob #22", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_22.gif" }, { id:"blowjob_23", name:"Blowjob #23", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_23.gif" }, { id:"blowjob_24", name:"Blowjob #24", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_24.gif" }, { id:"blowjob_25", name:"Blowjob #25", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_25.gif" }, { id:"blowjob_26", name:"Blowjob #26", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_26.gif" }, { id:"blowjob_27", name:"Blowjob #27", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_27.gif" }, { id:"blowjob_28", name:"Blowjob #28", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_28.gif" }, { id:"blowjob_29", name:"Blowjob #29", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_29.gif" }, { id:"blowjob_30", name:"Blowjob #30", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_30.gif" }, { id:"blowjob_31", name:"Blowjob #31", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_31.gif" }, { id:"blowjob_32", name:"Blowjob #32", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_32.gif" }, { id:"blowjob_33", name:"Blowjob #33", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_33.gif" }, { id:"blowjob_34", name:"Blowjob #34", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_34.gif" }, { id:"blowjob_35", name:"Blowjob #35", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_35.gif" }, { id:"blowjob_36", name:"Blowjob #36", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_36.gif" }, { id:"blowjob_37", name:"Blowjob #37", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_37.gif" }, { id:"blowjob_38", name:"Blowjob #38", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_38.gif" }, { id:"blowjob_39", name:"Blowjob #39", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_39.gif" }, { id:"blowjob_40", name:"Blowjob #40", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_40.gif" }, { id:"blowjob_41", name:"Blowjob #41", rarity:"epic", category:"blowjob", file:"Images/cards/Blowjob/blowjob_41.gif" }, { id:"blowjob_42", name:"Blowjob #42", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_42.gif" }, { id:"blowjob_43", name:"Blowjob #43", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_43.gif" }, { id:"blowjob_44", name:"Blowjob #44", rarity:"epic", category:"blowjob", file:"Images/cards/Blowjob/blowjob_44.gif" }, { id:"blowjob_45", name:"Blowjob #45", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_45.gif" }, { id:"blowjob_46", name:"Blowjob #46", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_46.gif" }, { id:"blowjob_47", name:"Blowjob #47", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_47.gif" }, { id:"blowjob_48", name:"Blowjob #48", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_48.gif" }, { id:"blowjob_49", name:"Blowjob #49", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_49.gif" }, { id:"blowjob_50", name:"Blowjob #50", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_50.gif" }, { id:"blowjob_51", name:"Blowjob #51", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_51.gif" }, { id:"blowjob_52", name:"Blowjob #52", rarity:"common", category:"blowjob", file:"Images/cards/Blowjob/blowjob_52.gif" }, { id:"blowjob_53", name:"Blowjob #53", rarity:"epic", category:"blowjob", file:"Images/cards/Blowjob/blowjob_53.gif" }, { id:"blowjob_54", name:"Blowjob #54", rarity:"rare", category:"blowjob", file:"Images/cards/Blowjob/blowjob_54.gif" }, { id:"blowjob_55", name:"Blowjob #55", rarity:"epic", category:"blowjob", file:"Images/cards/Blowjob/blowjob_55.gif" }, ]; /* Poids de probabilité par rareté */ window.RARITY_WEIGHTS = { common:60, rare:25, epic:12, legendary:3 }; /* Tirer une rareté pondérée */ window.rollRarity = function(forcedRarity) { if (forcedRarity) return forcedRarity; var total = 0; for (var k in window.RARITY_WEIGHTS) total += window.RARITY_WEIGHTS[k]; var roll = Math.random() * total; for (var rarity in window.RARITY_WEIGHTS) { roll -= window.RARITY_WEIGHTS[rarity]; if (roll <= 0) return rarity; } return "common"; }; /* Tirer une carte pour un pack */ window.drawPackCard = function(packId) { var pack = window.PACKS.find(function(p) { return p.id === packId; }); if (!pack) return null; var rarity = window.rollRarity(pack.forcedRarity || null); var pool = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1 && c.rarity === rarity; }); if (!pool.length) { pool = window.CARD_DB.filter(function(c) { return pack.categories.indexOf(c.category) !== -1; }); } if (!pool.length) return null; return pool[Math.floor(Math.random() * pool.length)]; }; /* Ouvrir un pack (3 cartes) */ window.openPack = function(packId, free) { var v = State.variables; var pack = window.PACKS.find(function(p) { return p.id === packId; }); if (!pack) return false; /* Initialiser tokens si absent (sauvegarde ancienne) */ if (!v.tokens) v.tokens = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }; if (!free) { /* Vérifier solde de jetons pour cette table */ var tableTokens = v.tokens[pack.table] || 0; if (tableTokens < pack.price) return false; /* Vérifier table débloquée */ var tableOk = ( v.unlockedTables.indexOf(pack.table) !== -1 || v.unlockedTablesGay.indexOf(pack.table) !== -1 || v.unlockedTablesTrans.indexOf(pack.table) !== -1 || pack.table === 'vice' || pack.table === 'rainbow' || pack.table === 'transroom' ); if (!tableOk) return false; } /* Vérifier qu'il y a des cartes disponibles */ var hasCards = window.CARD_DB.some(function(c) { return pack.categories.indexOf(c.category) !== -1; }); if (!hasCards) return 'empty'; /* Déduire les jetons sauf en mode gratuit */ if (!free) v.tokens[pack.table] -= pack.price; v.currentPackId = packId; v.pendingPack = []; for (var i = 0; i < 3; i++) { var card = window.drawPackCard(packId); if (card) { v.pendingPack.push(card.id); if (v.collection[card.id] === undefined) v.collection[card.id] = 0; v.collection[card.id]++; } } return true; }; /* Récupérer les jetons d'une table */ window.getTableTokens = function(tableId) { var v = State.variables; if (!v.tokens) return 0; return v.tokens[tableId] || 0; }; /* Formatter les jetons */ window.formatTokens = function(n) { return n + ' 🎫'; }; /* Récupérer une carte par id */ window.getCard = function(cardId) { return window.CARD_DB.find(function(c) { return c.id === cardId; }) || null; }; /* Nombre de cartes uniques possédées */ window.collectionCount = function() { return Object.keys(State.variables.collection).length; }; /* Icône rareté */ window.rarityIcon = function(r) { return { common:"⚪", rare:"🔵", epic:"🟣", legendary:"🌟" }[r] || ""; }; /* Label lisible d'une catégorie */ window.categoryLabel = function(tag) { var cat = window.CATEGORIES.find(function(c) { return c.tag === tag; }); return cat ? cat.label : tag; }; /* Tables accessibles selon l'état du joueur */ window.isTableUnlocked = function(tableId) { var v = State.variables; var tbl = window.TABLES.find(function(t) { return t.id === tableId; }); if (!tbl) return false; if (tbl.branch === "hetero") return v.unlockedTables.indexOf(tableId) !== -1; if (tbl.branch === "gay") return v.gayUnlocked && v.unlockedTablesGay.indexOf(tableId) !== -1; if (tbl.branch === "trans") return v.transUnlocked && v.unlockedTablesTrans.indexOf(tableId) !== -1; if (tbl.branch === "both") return v.unlockedTables.indexOf("heterovip") !== -1 && v.gayUnlocked && v.unlockedTablesGay.indexOf("gayvip") !== -1; return false; }; <</script>> /* ══════════════════════════════════════════ VARIABLES DU JEU ══════════════════════════════════════════ */ <<set $playerMoney = 1500>> <<set $currentBet = 0>> <<set $baseBet = 0>> <<set $gameState = "betting">> <<set $playerHand = []>> <<set $dealerHand = []>> <<set $canDouble = false>> <<set $canSplit = false>> <<set $isSplit = false>> <<set $splitHand = []>> <<set $splitBet = 0>> <<set $currentHand = "main">> /* Stats */ <<set $totalWins = 0>> <<set $totalLosses = 0>> <<set $totalPushes = 0>> <<set $totalHandsPlayed = 0>> <<set $biggestWin = 0>> <<set $earnedHetero = 0>> <<set $earnedGay = 0>> <<set $earnedTrans = 0>> <<set $totalEarned = 0>> /* ══════════════════════════════════════════ COLLECTION SYSTEM ══════════════════════════════════════════ */ <<set $collection = {}>> /* Branche hétéro */ <<set $unlockedTables = ["vice"]>> /* Branche gay */ <<set $gayUnlocked = true>> <<set $unlockedTablesGay = ["rainbow"]>> /* Branche trans */ <<set $transUnlocked = false>> <<set $unlockedTablesTrans = ["transroom"]>> /* Table active */ <<set $activeTable = "vice">> <<set $activeBranch = "hetero">> /* Pack en cours */ <<set $pendingPack = []>> <<set $currentPackId = "vice_pack">> /* ══════════════════════════════════════════ JETONS PAR TABLE — monnaie de progression Gagnés en jouant, dépensés pour les packs ══════════════════════════════════════════ */ <<set $tokens = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }>> <<set $lastResultBlackjack = false>> /* Lucky Wheel */ <<set $wheelHandsCount = 0>> <<set $wheelReady = false>> /* Pages vues (pour les bulles first-visit) */ <<set $visitedPages = {}>> /* Game Over count */ <<set $gameOverCount = 0>> /* NPC — session flags */ <<set $scarlettSeenThisSession = false>> <<set $firstWinSeen = false>> /* Table unlock tips */ <<set $tableUnlockSeen = { boudoir:false, heat:false, slutpit:false, heterovip:false, penthouse:false }>> /* Streak system */ <<set $currentStreak = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }>> <<set $streakMilestoneSeen = { s3:false, s5:false, s7:false, s10:false }>> /* ══════════════════════════════════════════ MIGRATION — compatibilité saves anciennes Ajouter ici chaque nouvelle variable intro- duite dans une update, avec sa valeur par défaut. Ne jamais écraser une valeur existante. v0.1 → v0.2 ══════════════════════════════════════════ */ <<script>> (function() { var v = State.variables; if (v.scarlettSeenThisSession === undefined) v.scarlettSeenThisSession = false; if (v.firstWinSeen === undefined) v.firstWinSeen = false; if (v.lastHandResult === undefined) v.lastHandResult = ''; if (v.lastHandTokens === undefined) v.lastHandTokens = 0; if (v.heatUnlockedSeen === undefined) v.heatUnlockedSeen = false; if (v.tableUnlockSeen === undefined) v.tableUnlockSeen = { boudoir:false, heat:false, slutpit:false, heterovip:false, penthouse:false }; if (v.currentStreak === undefined || typeof v.currentStreak !== 'object') v.currentStreak = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }; if (v.streakMilestoneSeen === undefined) v.streakMilestoneSeen = { s3:false, s5:false, s7:false, s10:false }; })(); <</script>> /* ══════════════════════════════════════════ BROWSER SAVES — 3 slots SugarCube ══════════════════════════════════════════ */ <<run Config.saves.slots = 3;>> <<script>> /* ══════════════════════════════════════════ SIDEBAR DE NAVIGATION PERSISTANTE Présente sur toutes les pages sauf : Warning, Leave, Intro, World Select ══════════════════════════════════════════ */ var NO_SIDEBAR = ['Warning', 'Leave', 'Intro', 'How To Play', 'World Select', 'Welcome Straight']; var GAME_PAGES = ['Play Hand', 'Dealer Turn', 'Game Result']; /* ══════════════════════════════════════════ FIRST-VISIT TIPS ══════════════════════════════════════════ */ /* ══════════════════════════════════════════ DEV PANEL — Ctrl+Shift+D Accessible uniquement au concepteur ══════════════════════════════════════════ */ (function() { var DEV_PANEL_ID = 'dev-cheat-panel'; function buildDevPanel() { if (document.getElementById(DEV_PANEL_ID)) return; var panel = document.createElement('div'); panel.id = DEV_PANEL_ID; panel.innerHTML = '<div id="dev-panel-title">⚙️ DEV PANEL</div>' + '<div class="dev-section-label">💰 Money</div>' + '<div class="dev-row">' + '<button class="dev-btn" data-action="money" data-val="5000">+$5k</button>' + '<button class="dev-btn" data-action="money" data-val="20000">+$20k</button>' + '<button class="dev-btn" data-action="money" data-val="50000">+$50k</button>' + '<button class="dev-btn" data-action="money" data-val="200000">+$200k</button>' + '</div>' + '<div class="dev-section-label">🎫 Tokens (table active)</div>' + '<div class="dev-row">' + '<button class="dev-btn" data-action="tokens" data-val="100">+100</button>' + '<button class="dev-btn" data-action="tokens" data-val="500">+500</button>' + '<button class="dev-btn" data-action="tokens" data-val="2000">+2000</button>' + '</div>' + '<div class="dev-section-label">📊 Earned (branche hetero)</div>' + '<div class="dev-row">' + '<button class="dev-btn" data-action="earned" data-val="5000">+5k</button>' + '<button class="dev-btn" data-action="earned" data-val="20000">+20k</button>' + '<button class="dev-btn" data-action="earned" data-val="30000">+30k</button>' + '<button class="dev-btn" data-action="earned" data-val="75000">+75k</button>' + '</div>' + '<div class="dev-section-label">🎡 Lucky Wheel</div>' + '<div class="dev-row">' + '<button class="dev-btn" data-action="wheel_ready">Activate Wheel</button>' + '</div>' + '<div class="dev-section-label">🔥 Win Streak (table active)</div>' + '<div class="dev-row">' + '<button class="dev-btn" data-action="streak_plus1">+1 Streak</button>' + '<button class="dev-btn dev-btn-reset" data-action="streak_reset">Reset Streak</button>' + '</div>' + '<div class="dev-section-label">🔄 Reset</div>' + '<div class="dev-row">' + '<button class="dev-btn dev-btn-reset" data-action="reset_tips">Reset Tips</button>' + '<button class="dev-btn dev-btn-reset" data-action="reset_session">Reset Session</button>' + '<button class="dev-btn dev-btn-reset" data-action="reset_tables">Reset Tables</button>' + '</div>' + '<div id="dev-panel-feedback"></div>' + '<button id="dev-close-btn">✕ Close</button>'; document.body.appendChild(panel); function feedback(msg) { var el = document.getElementById('dev-panel-feedback'); if (el) { el.textContent = msg; setTimeout(function() { el.textContent = ''; }, 1500); } } panel.querySelectorAll('.dev-btn').forEach(function(btn) { btn.addEventListener('click', function() { var v = State.variables; var action = this.getAttribute('data-action'); var val = parseInt(this.getAttribute('data-val')) || 0; if (action === 'money') { v.playerMoney += val; feedback('✦ +$' + val.toLocaleString() + ' added'); } else if (action === 'tokens') { var tbl = v.activeTable || 'vice'; if (!v.tokens) v.tokens = {}; v.tokens[tbl] = (v.tokens[tbl] || 0) + val; feedback('✦ +' + val + ' tokens → ' + tbl); } else if (action === 'earned') { v.earnedHetero = (v.earnedHetero || 0) + val; v.totalEarned = (v.totalEarned || 0) + val; feedback('✦ earnedHetero +' + val.toLocaleString()); } else if (action === 'wheel_ready') { v.wheelHandsCount = 20; v.wheelReady = true; feedback('✦ Lucky Wheel activated!'); /* Refresh wheel button in sidebar */ var wheelBtn = document.getElementById('nsb-btn-wheel'); if (wheelBtn) { wheelBtn.classList.add('nsb-btn-wheel-claim'); wheelBtn.innerHTML = '<div class="nsb-wheel-top"><span class="nsb-wheel-icon">🎡</span> ✦ Claim Reward</div>'; wheelBtn.style.borderColor = 'rgba(255,215,0,0.6)'; wheelBtn.style.background = 'rgba(255,215,0,0.1)'; wheelBtn.style.color = 'var(--gold)'; } } else if (action === 'streak_plus1') { var tbl = v.activeTable || 'vice'; if (!v.currentStreak) v.currentStreak = {}; v.currentStreak[tbl] = (v.currentStreak[tbl] || 0) + 1; feedback('✦ Streak → ' + v.currentStreak[tbl] + ' on ' + tbl); } else if (action === 'streak_reset') { var tbl = v.activeTable || 'vice'; if (v.currentStreak) v.currentStreak[tbl] = 0; feedback('✦ Streak reset on ' + tbl); } else if (action === 'reset_tips') { v.visitedPages = {}; feedback('✦ All tips reset'); } else if (action === 'reset_session') { v.scarlettSeenThisSession = false; v.firstWinSeen = false; feedback('✦ Session flags reset'); } else if (action === 'reset_tables') { v.tableUnlockSeen = { boudoir:false, heat:false, slutpit:false, heterovip:false, penthouse:false }; feedback('✦ Table unlock tips reset'); } /* Refresh sidebar balance display */ var balEl = document.querySelector('.nsb-balance-val'); if (balEl) balEl.textContent = '$' + (State.variables.playerMoney || 0).toLocaleString(); }); }); document.getElementById('dev-close-btn').addEventListener('click', function() { panel.remove(); }); } /* Écouter Ctrl+Shift+D */ document.addEventListener('keydown', function(e) { if (e.ctrlKey && e.shiftKey && e.key === 'D') { e.preventDefault(); if (document.getElementById(DEV_PANEL_ID)) { document.getElementById(DEV_PANEL_ID).remove(); } else { buildDevPanel(); } } }); })(); window.showFirstVisitTip = function(pageKey, cfg) { var v = State.variables; if (!v.visitedPages) v.visitedPages = {}; if (v.visitedPages[pageKey]) return; v.visitedPages[pageKey] = true; var isHetero = v.activeBranch === 'hetero'; var overlay = document.createElement('div'); overlay.className = 'fvt-overlay'; var scarlettHeader = isHetero ? '<div class="fvt-scarlett-header">' + '<img src="Images/npc/welcomer_straight.png" class="fvt-scarlett-avatar" alt="Scarlett">' + '<div class="fvt-scarlett-info">' + '<span class="fvt-scarlett-name">✦ Scarlett</span>' + '<span class="fvt-scarlett-sub">Your guide</span>' + '</div>' + '</div>' : '<div class="fvt-icon">' + (cfg.icon || '💡') + '</div>'; var closeLabel = cfg.closeBtnLabel || 'Got it ✓'; var box = document.createElement('div'); box.className = 'fvt-box' + (isHetero ? ' fvt-box-scarlett' : ''); box.innerHTML = scarlettHeader + '<div class="fvt-title">' + cfg.title + '</div>' + '<div class="fvt-body">' + (isHetero && cfg.bodyScarlett ? cfg.bodyScarlett : cfg.body) + '</div>' + (cfg.tips ? '<ul class="fvt-tips">' + cfg.tips.map(function(t) { return '<li>' + t + '</li>'; }).join('') + '</ul>' : '') + '<button class="fvt-btn fvt-close-btn-inner">' + closeLabel + '</button>'; overlay.appendChild(box); document.body.appendChild(overlay); function close() { overlay.remove(); if (cfg.onClose) cfg.onClose(); } box.querySelector('.fvt-close-btn-inner').addEventListener('click', close); overlay.addEventListener('click', function(e) { if (e.target === overlay) close(); }); }; /* ══════════════════════════════════════════ LUCKY WHEEL — Segments par branche ══════════════════════════════════════════ */ window.WHEEL_SEGMENTS = { hetero: [ { type:'money', value:100, label:'$100', color:'#7a5a00' }, { type:'tokens', value:50, label:'50 tkn', color:'#005a66' }, { type:'money', value:200, label:'$200', color:'#9d1a00' }, { type:'card', value:1, label:'1 Pack', color:'#5c008f' }, { type:'money', value:150, label:'$150', color:'#7a5a00' }, { type:'tokens', value:100, label:'100 tkn', color:'#005a66' }, { type:'money', value:300, label:'$300', color:'#9d1a00' }, { type:'money', value:100, label:'$100', color:'#7a5a00' }, { type:'tokens', value:75, label:'75 tkn', color:'#005a66' }, { type:'money', value:500, label:'$500 🌟', color:'#c4005e' } ], gay: [ { type:'money', value:100, label:'$100', color:'#4a0080' }, { type:'tokens', value:50, label:'50 tkn', color:'#2d0060' }, { type:'money', value:200, label:'$200', color:'#6b0099' }, { type:'card', value:1, label:'1 Pack', color:'#1e0044' }, { type:'money', value:150, label:'$150', color:'#4a0080' }, { type:'tokens', value:100, label:'100 tkn', color:'#2d0060' }, { type:'money', value:300, label:'$300', color:'#6b0099' }, { type:'money', value:100, label:'$100', color:'#4a0080' }, { type:'tokens', value:75, label:'75 tkn', color:'#2d0060' }, { type:'money', value:500, label:'$500 🌟', color:'#a855f7' } ], trans: [ { type:'money', value:100, label:'$100', color:'#80003a' }, { type:'tokens', value:50, label:'50 tkn', color:'#660030' }, { type:'money', value:200, label:'$200', color:'#99004a' }, { type:'card', value:1, label:'1 Pack', color:'#3d001f' }, { type:'money', value:150, label:'$150', color:'#80003a' }, { type:'tokens', value:100, label:'100 tkn', color:'#660030' }, { type:'money', value:300, label:'$300', color:'#99004a' }, { type:'money', value:100, label:'$100', color:'#80003a' }, { type:'tokens', value:75, label:'75 tkn', color:'#660030' }, { type:'money', value:500, label:'$500 🌟', color:'#f9a8d4' } ] }; /* Pool de cartes par branche pour la roue */ window.WHEEL_CARD_CATEGORIES = { hetero: ["girl_strip","girl_ass","tits","pussy_eating","handjob","anal","blowjob","fingering","tits_fuck","pussy_fucking","rimjob","bbc","creampie","threesome","gangbang","cumshot"], gay: ["gay_solo","gay_scenes","sissy"], trans: ["sissy","trans_solo","trans_scenes"] }; /* Tirer une carte pour la roue (depuis le pool de la branche) */ window.drawWheelCard = function(branch) { var v = State.variables; var cats = window.WHEEL_CARD_CATEGORIES[branch] || window.WHEEL_CARD_CATEGORIES.hetero; var rarity = window.rollRarity(null); var pool = window.CARD_DB.filter(function(c) { return cats.indexOf(c.category) !== -1 && c.rarity === rarity; }); if (!pool.length) pool = window.CARD_DB.filter(function(c) { return cats.indexOf(c.category) !== -1; }); if (!pool.length) return null; var card = pool[Math.floor(Math.random() * pool.length)]; if (v.collection[card.id] === undefined) v.collection[card.id] = 0; v.collection[card.id]++; return card; }; function buildNavSidebar() { var old = document.getElementById('nav-sidebar'); if (old) old.remove(); var v = State.variables; if (!v) return; var branchColors = { hetero:'var(--neon-pink)', gay:'#a855f7', trans:'#f9a8d4' }; var branchNames = { hetero:'Straight', gay:'Gay', trans:'Trans' }; var branchIcons = { hetero:'Images/icons/straight_icon.png', gay:'Images/icons/gay_icon.png', trans:'Images/icons/trans_icon.png' }; var branchColor = branchColors[v.activeBranch] || 'var(--neon-pink)'; var branchName = branchNames[v.activeBranch] || ''; var branchIcon = branchIcons[v.activeBranch] || ''; var earnedVal = v.activeBranch === 'gay' ? v.earnedGay : v.activeBranch === 'trans' ? v.earnedTrans : v.earnedHetero; var sidebar = document.createElement('div'); sidebar.id = 'nav-sidebar'; /* Jetons de la table active */ if (!v.tokens) v.tokens = { vice:0, boudoir:0, heat:0, slutpit:0, heterovip:0, rainbow:0, gayvip:0, transroom:0, transvip:0, penthouse:0 }; var activeTable = v.activeTable || 'vice'; var activeTokens = v.tokens[activeTable] || 0; var tableObj = window.TABLES ? window.TABLES.find(function(t) { return t.id === activeTable; }) : null; var tableLabel = tableObj ? tableObj.name : activeTable; var tableEmoji = tableObj ? tableObj.emoji : '🎫'; var wCount = Math.min(v.wheelHandsCount || 0, 20); var wReady = v.wheelReady || false; var wPct = Math.round((wCount / 20) * 100); var wheelBtnHtml = wReady ? '<a class="nsb-btn nsb-btn-wheel nsb-btn-wheel-ready" id="nsb-btn-wheel">' + '<span class="nsb-wheel-icon">🎡</span> Lucky Wheel' + '</a>' : '<a class="nsb-btn nsb-btn-wheel nsb-btn-wheel-locked" id="nsb-btn-wheel">' + '<div class="nsb-wheel-top"><span class="nsb-wheel-icon">🎡</span> Lucky Wheel</div>' + '<div class="nsb-wheel-progress-wrap">' + '<div class="nsb-wheel-bar" style="width:' + wPct + '%"></div>' + '</div>' + '<span class="nsb-wheel-count">' + wCount + ' / 20 hands</span>' + '</a>'; sidebar.innerHTML = '<div class="nsb-logo">THE <span>21</span> DESIRES</div>' + '<div class="nsb-world-block" style="--branch-color:' + branchColor + ';">' + '<div class="nsb-world-icon-wrap">' + '<img src="' + branchIcon + '" class="nsb-world-icon-img" alt="' + branchName + '" onerror="this.style.opacity=\'0.3\'">' + '<div class="nsb-world-icon-glow"></div>' + '</div>' + '<span class="nsb-world-name" style="color:' + branchColor + ';">' + branchName + '</span>' + '</div>' + '<div class="nsb-sep"></div>' + '<div class="nsb-stat">' + '<span class="nsb-label">💰 Balance</span>' + '<span class="nsb-value">' + formatMoney(v.playerMoney) + '</span>' + '</div>' + '<div class="nsb-sep"></div>' + '<div class="nsb-stat">' + '<span class="nsb-label">' + tableEmoji + ' ' + tableLabel + '</span>' + '<span class="nsb-value nsb-value-sm nsb-token">' + activeTokens + ' tokens</span>' + '</div>' + '<div class="nsb-sep"></div>' + '<div class="nsb-stat">' + '<span class="nsb-label">💎 Collection</span>' + '<span class="nsb-value nsb-value-sm">' + collectionCount() + ' cards</span>' + '</div>' + '<div class="nsb-actions">' + '<a class="nsb-btn" id="nsb-btn-casino">🎰 Casino</a>' + '<a class="nsb-btn" id="nsb-btn-shop">🛍️ Card Shop</a>' + '<a class="nsb-btn" id="nsb-btn-collection">📚 My Collection</a>' + '<a class="nsb-btn nsb-btn-sell" id="nsb-btn-sell">💱 Sell Cards</a>' + '<div class="nsb-divider-thin"></div>' + wheelBtnHtml + '<div class="nsb-divider-thin"></div>' + '<a class="nsb-btn nsb-btn-rules" id="nsb-btn-rules">📖 Rules</a>' + '<a class="nsb-btn nsb-btn-world" id="nsb-btn-world">🌍 Switch World</a>' + '</div>'; document.body.appendChild(sidebar); /* ── Si une récompense roue est en attente de claim, bloquer toute navigation ── */ var rewardPending = !!(v._wheelRewardType); if (rewardPending) { /* Ajouter une bannière d'avertissement */ var lockBanner = document.createElement('div'); lockBanner.className = 'nsb-reward-lock'; lockBanner.innerHTML = '🎡 Claim your reward first!'; var actionsDiv = sidebar.querySelector('.nsb-actions'); if (actionsDiv) actionsDiv.insertBefore(lockBanner, actionsDiv.firstChild); /* Désactiver visuellement tous les boutons nav sauf la roue */ ['nsb-btn-casino','nsb-btn-shop','nsb-btn-collection','nsb-btn-sell','nsb-btn-rules','nsb-btn-world'].forEach(function(id) { var el = document.getElementById(id); if (el) { el.classList.add('nsb-btn-disabled'); el.style.pointerEvents = 'none'; el.style.opacity = '0.25'; } }); /* Rediriger vers la roue si le joueur a quitté sans cliquer Claim */ var wheelBtnEl = document.getElementById('nsb-btn-wheel'); if (wheelBtnEl) { wheelBtnEl.classList.add('nsb-btn-wheel-claim'); wheelBtnEl.innerHTML = '<div class="nsb-wheel-top"><span class="nsb-wheel-icon">🎡</span> ✦ Claim Reward</div>'; wheelBtnEl.style.borderColor = 'rgba(255,215,0,0.6)'; wheelBtnEl.style.background = 'rgba(255,215,0,0.1)'; wheelBtnEl.style.color = 'var(--gold)'; wheelBtnEl.style.animation = 'wheelPulse 1.5s ease-in-out infinite'; wheelBtnEl.addEventListener('click', function() { Engine.play('Spin Wheel'); }); } return; /* Ne pas attacher les autres listeners */ } document.getElementById('nsb-btn-casino').addEventListener('click', function() { Engine.play('Casino'); }); document.getElementById('nsb-btn-shop').addEventListener('click', function() { Engine.play('Card Shop'); }); document.getElementById('nsb-btn-collection').addEventListener('click', function() { Engine.play('My Collection'); }); document.getElementById('nsb-btn-sell').addEventListener('click', function() { Engine.play('Sell Cards'); }); document.getElementById('nsb-btn-rules').addEventListener('click', function() { Engine.play('Blackjack Rules'); }); document.getElementById('nsb-btn-world').addEventListener('click', function() { Engine.play('World Select'); }); var wheelBtn = document.getElementById('nsb-btn-wheel'); if (wheelBtn) { wheelBtn.addEventListener('click', function() { if (State.variables.wheelReady) Engine.play('Spin Wheel'); }); } } var TABLE_PAGES = ['Blackjack Table', 'Play Hand', 'Dealer Turn', 'Game Result']; $(document).on(':passagerender', function(ev) { var title = ev.passage.title; /* Fond de table — uniquement sur les pages de jeu et de pari */ if (TABLE_PAGES.indexOf(title) === -1) { document.body.style.backgroundImage = ''; document.body.style.backgroundSize = ''; document.body.style.backgroundPosition = ''; document.body.style.backgroundAttachment = ''; } /* Game HUD — uniquement sur les pages de jeu */ if (GAME_PAGES.indexOf(title) === -1) { var hud = document.getElementById('game-hud'); if (hud) hud.remove(); var p = document.getElementById('passages'); if (p) p.classList.remove('game-page'); } /* Barre de résultat — supprimée dès qu'on quitte Game Result */ var rab = document.getElementById('result-action-bar'); if (rab) rab.remove(); /* Nav sidebar */ if (NO_SIDEBAR.indexOf(title) === -1) { buildNavSidebar(); var passages = document.getElementById('passages'); if (passages) { passages.style.marginLeft = '240px'; passages.style.maxWidth = 'calc(100% - 240px)'; passages.style.paddingLeft = '24px'; } } else { var sb = document.getElementById('nav-sidebar'); if (sb) sb.remove(); var passages = document.getElementById('passages'); if (passages) { passages.style.marginLeft = ''; passages.style.maxWidth = ''; passages.style.paddingLeft = ''; } } /* Bouton Credits — réévaluer à chaque passage */ setTimeout(function() { var btn = document.getElementById('btn-credits-save'); if (!btn) return; var v3 = State.variables; var CREDITS_BLOCKED = ['Spin Wheel', 'Game Over', 'Warning', 'Intro', 'How To Play', 'World Select', 'Leave', 'Credits']; var locked = !v3.activeBranch || CREDITS_BLOCKED.indexOf(title) !== -1; btn.disabled = locked; btn.style.opacity = locked ? '0.2' : ''; btn.style.cursor = locked ? 'not-allowed' : ''; btn.title = locked ? 'Choose your world first' : ''; }, 80); }); /* ── SCARLETT LOAD WELCOME ── */ window.showScarlettLoadWelcome = function() { if (State.variables.activeBranch !== 'hetero') return; var lines = [ "You're back. I kept your seat warm.", "Took you long enough. The cards missed you.", "Welcome back. Ready to pick up where we left off?", "I knew you'd return. They always do.", "Your seat's been waiting. Let's not waste any more time.", "Back already? Good. I was getting bored without you." ]; var line = lines[Math.floor(Math.random() * lines.length)]; var screen = document.createElement('div'); screen.id = 'scarlett-load-screen'; screen.innerHTML = '<div id="sls-center">' + '<div id="sls-img-wrap">' + '<img src="Images/npc/welcomer_straight.png" id="sls-img" alt="Scarlett">' + '</div>' + '<div id="sls-dialogue">' + '<div id="sls-pretitle">◈ The 21 Desires — Straight</div>' + '<div id="sls-name">— Scarlett</div>' + '<div id="sls-text">' + line + '</div>' + '<button id="sls-btn">Enter the Casino ›</button>' + '</div>' + '</div>'; document.body.appendChild(screen); document.getElementById('sls-btn').addEventListener('click', function() { screen.style.opacity = '0'; setTimeout(function() { screen.remove(); }, 400); }); }; /* ── SAVE BAR : injectée une seule fois au démarrage ── */ $(document).one(':passagerender', function() { if (document.getElementById('save-bar')) return; /* ── PANEL SLOTS (affiché au-dessus de la barre) ── */ var slotsPanel = document.createElement('div'); slotsPanel.id = 'slots-panel'; slotsPanel.style.display = 'none'; document.body.appendChild(slotsPanel); var slotsPanelVisible = false; function formatSlotDate(ts) { var d = new Date(ts); return d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], { hour:'2-digit', minute:'2-digit' }); } function renderSlotsPanel() { slotsPanel.innerHTML = ''; var title = document.createElement('div'); title.className = 'slots-panel-title'; title.textContent = '💾 Browser Save Slots'; slotsPanel.appendChild(title); var subtitle = document.createElement('div'); subtitle.className = 'slots-panel-subtitle'; subtitle.textContent = 'Saved in this browser only — use Export for cross-device saves'; slotsPanel.appendChild(subtitle); for (var i = 0; i < 3; i++) { (function(slot) { var row = document.createElement('div'); row.className = 'slot-row'; var info = document.createElement('div'); info.className = 'slot-info'; var isEmpty = Save.slots.isEmpty(slot); var slotData = null; try { slotData = isEmpty ? null : Save.slots.get(slot); } catch(e) {} var hasData = !isEmpty && slotData !== null; if (!hasData) { info.innerHTML = '<span class="slot-num">SLOT ' + (slot + 1) + '</span>' + '<span class="slot-empty">— Empty —</span>'; } else { var meta = (slotData && slotData.metadata) ? slotData.metadata : {}; var dateStr = (slotData && slotData.date) ? formatSlotDate(slotData.date) : '—'; info.innerHTML = '<span class="slot-num">SLOT ' + (slot + 1) + '</span>' + '<span class="slot-date">' + dateStr + '</span>' + '<span class="slot-meta">' + '💰 ' + (meta.money !== undefined ? '$' + meta.money.toLocaleString() : '?') + ' · 🎴 ' + (meta.cards !== undefined ? meta.cards + ' cards' : '?') + '</span>'; } var btnSave = document.createElement('button'); btnSave.className = 'btn-slot btn-slot-save'; btnSave.textContent = 'Save'; btnSave.addEventListener('click', function() { var v = State.variables; var meta = { money: v.playerMoney || 0, cards: Object.keys(v.collection || {}).length }; Save.slots.save(slot, 'Slot ' + (slot + 1), meta); showToast('✦ Saved to Slot ' + (slot + 1) + '!', 'success'); renderSlotsPanel(); }); var btnLoad = document.createElement('button'); btnLoad.className = 'btn-slot btn-slot-load'; btnLoad.textContent = 'Load'; btnLoad.disabled = !hasData; if (hasData) { btnLoad.addEventListener('click', function() { var overlay = document.createElement('div'); overlay.id = 'load-overlay'; overlay.style.cssText = 'position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:rgba(6,4,15,0.85);backdrop-filter:blur(6px);'; overlay.innerHTML = '<div style="text-align:center;display:flex;flex-direction:column;align-items:center;gap:14px;">' + '<div style="font-size:2.5rem;">💾</div>' + '<div style="font-family:\'Orbitron\',monospace;font-size:0.9rem;font-weight:700;letter-spacing:0.25em;color:#4fc3f7;text-transform:uppercase;">Save Loaded</div>' + '<div style="font-family:\'Rajdhani\',sans-serif;font-size:1rem;color:rgba(210,200,240,0.7);">Restoring your game...</div>' + '</div>'; document.body.appendChild(overlay); /* Supprimer l'overlay au prochain rendu de passage */ /* Charger la save après l'overlay */ setTimeout(function() { Save.slots.load(slot); /* Supprimer l'overlay et afficher Scarlett après le chargement */ setTimeout(function() { var ol = document.getElementById('load-overlay'); if (ol) ol.remove(); window.showScarlettLoadWelcome(); }, 800); }, 1200); }); } var btnDel = document.createElement('button'); btnDel.className = 'btn-slot btn-slot-delete'; btnDel.textContent = '✕'; btnDel.disabled = !hasData; if (hasData) { btnDel.addEventListener('click', function() { Save.slots.delete(slot); showToast('✦ Slot ' + (slot + 1) + ' deleted.', 'success'); renderSlotsPanel(); }); } row.appendChild(info); row.appendChild(btnSave); row.appendChild(btnLoad); row.appendChild(btnDel); slotsPanel.appendChild(row); })(i); } } /* ── BARRE ── */ var bar = document.createElement('div'); bar.id = 'save-bar'; /* Input file caché pour l'import */ var fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.id = 'save-file-input'; fileInput.accept = '.21dsave'; /* Label */ var label = document.createElement('span'); label.className = 'save-label'; label.textContent = '💾 Save'; /* Bouton Slots */ var btnSlots = document.createElement('button'); btnSlots.className = 'btn-save btn-save-slots'; btnSlots.innerHTML = '📂 Slots'; btnSlots.addEventListener('click', function() { slotsPanelVisible = !slotsPanelVisible; if (slotsPanelVisible) { renderSlotsPanel(); slotsPanel.style.display = 'flex'; btnSlots.classList.add('btn-save-slots-active'); } else { slotsPanel.style.display = 'none'; btnSlots.classList.remove('btn-save-slots-active'); } }); /* Fermer le panel si on clique ailleurs */ document.addEventListener('click', function(e) { if (slotsPanelVisible && !slotsPanel.contains(e.target) && e.target !== btnSlots) { slotsPanelVisible = false; slotsPanel.style.display = 'none'; btnSlots.classList.remove('btn-save-slots-active'); } }); /* Bouton Export */ var btnExport = document.createElement('button'); btnExport.className = 'btn-save btn-save-export'; btnExport.innerHTML = '⬇ Export Save'; /* Bouton Import */ var btnImport = document.createElement('button'); btnImport.className = 'btn-save btn-save-import'; btnImport.innerHTML = '⬆ Load Save'; /* Toast feedback */ var toast = document.createElement('span'); toast.className = 'save-toast'; function showToast(msg, type) { toast.textContent = msg; toast.className = 'save-toast ' + type + ' visible'; setTimeout(function() { toast.classList.remove('visible'); }, 2500); } /* EXPORT : sérialiser la save SugarCube → fichier .21dsave */ btnExport.addEventListener('click', function() { try { var data = Save.serialize(); if (!data) throw new Error('No save data'); var blob = new Blob([data], { type: 'text/plain' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'the21desires_' + Date.now() + '.21dsave'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showToast('✦ Save exported!', 'success'); } catch(e) { showToast('✕ Export failed', 'error'); } }); /* IMPORT : lire le fichier et désérialiser */ btnImport.addEventListener('click', function() { fileInput.click(); }); fileInput.addEventListener('change', function() { var file = fileInput.files[0]; if (!file) return; var reader = new FileReader(); reader.onload = function(e) { try { var overlay = document.createElement('div'); overlay.id = 'load-overlay'; overlay.style.cssText = 'position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:rgba(6,4,15,0.85);backdrop-filter:blur(6px);'; overlay.innerHTML = '<div style="text-align:center;display:flex;flex-direction:column;align-items:center;gap:14px;">' + '<div style="font-size:2.5rem;">💾</div>' + '<div style="font-family:\'Orbitron\',monospace;font-size:0.9rem;font-weight:700;letter-spacing:0.25em;color:#4fc3f7;text-transform:uppercase;">Save Loaded</div>' + '<div style="font-family:\'Rajdhani\',sans-serif;font-size:1rem;color:rgba(210,200,240,0.7);">Restoring your game...</div>' + '</div>'; document.body.appendChild(overlay); setTimeout(function() { Save.deserialize(e.target.result); setTimeout(function() { var ol = document.getElementById('load-overlay'); if (ol) ol.remove(); window.showScarlettLoadWelcome(); }, 800); }, 1200); } catch(err) { showToast('✕ Invalid save file', 'error'); } }; reader.readAsText(file); fileInput.value = ''; }); bar.appendChild(fileInput); bar.appendChild(label); bar.appendChild(btnSlots); bar.appendChild(btnExport); bar.appendChild(btnImport); bar.appendChild(toast); /* Bouton Credits — coin droit */ var btnCredits = document.createElement('button'); btnCredits.id = 'btn-credits-save'; btnCredits.className = 'btn-save btn-save-credits'; btnCredits.innerHTML = '✦ Credits'; btnCredits.addEventListener('click', function() { if (!this.disabled) Engine.play('Credits'); }); bar.appendChild(btnCredits); document.body.appendChild(bar); }); <</script>>
<div class="warning-screen"> <div class="warning-logo"> <h1 class="game-title warning-title">THE <span>21</span> DESIRES</h1> <p class="warning-tagline">◈ Adult Content Warning ◈</p> </div> <div class="warning-box"> <div class="warning-icon-wrap"> <div class="warning-icon">⚠️</div> <div class="warning-icon-label">Before you enter</div> </div> <div class="warning-items"> <div class="warning-item"> <span class="warning-num">01</span> <p>All characters portrayed in this game are <strong>18 years of age or older</strong>.</p> </div> <div class="warning-item"> <span class="warning-num">02</span> <p>This game contains <strong>explicit sexual content</strong> including nudity, adult animations, and sexually graphic material. Viewer discretion is advised.</p> </div> <div class="warning-item"> <span class="warning-num">03</span> <p>Some images or media used may belong to third parties. If you are the owner of any content displayed in <em>The 21 Desires</em> and wish to have it removed, please contact the developer.</p> </div> <div class="warning-item"> <span class="warning-num">04</span> <p>This is a work of <strong>fiction</strong>. All events, characters, and interactions are entirely fabricated and bear no relation to real-world persons or situations.</p> </div> </div> <div class="warning-divider"></div> <p class="warning-consent">By continuing, you confirm that you are of <strong>legal age</strong> to view <strong>adult content</strong> in your country, and that you have read and accepted the above.</p> <div class="warning-buttons"> <<link "✦ I am 18 or older — Enter" "Intro">><</link>> <a href="https://www.google.com" class="btn-warning-leave">✕ I am under 18 — Leave</a> </div> </div> </div> <<done>> <<script>> (function() { const btns = document.querySelectorAll('.warning-buttons a'); if (btns[0]) btns[0].classList.add('btn', 'btn-primary', 'btn-warning-enter'); if (btns[1]) btns[1].classList.add('btn', 'btn-warning-leave'); })(); <</script>> <</done>> <style> .warning-screen { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; gap: 28px; } .warning-logo { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; } .warning-title { font-size: clamp(2rem, 6vw, 3.2rem) !important; } .warning-tagline { font-family: 'Share Tech Mono', monospace; font-size: 0.78rem; letter-spacing: 0.45em; color: rgba(200,180,220,0.45); text-transform: uppercase; } .warning-box { max-width: 600px; width: 100%; background: rgba(14,10,30,0.85); border: 1px solid rgba(255,45,120,0.15); border-radius: 20px; padding: 28px 36px; position: relative; backdrop-filter: blur(8px); display: flex; flex-direction: column; gap: 18px; } .warning-box::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; border-radius: 20px 20px 0 0; background: linear-gradient(90deg, transparent, var(--neon-pink), var(--neon-purple), var(--neon-pink), transparent); } .warning-icon-wrap { display: flex; flex-direction: column; align-items: center; gap: 8px; margin-bottom: 0; } .warning-icon { font-size: 2.6rem; line-height: 1; filter: drop-shadow(0 0 12px rgba(255,215,0,0.5)); } .warning-icon-label { font-family: 'Orbitron', monospace; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.3em; text-transform: uppercase; color: rgba(255,215,0,0.5); } .warning-items { display: flex; flex-direction: column; gap: 10px; } .warning-item { display: flex; gap: 14px; align-items: flex-start; padding: 11px 14px; background: rgba(255,255,255,0.025); border-radius: 10px; border: 1px solid rgba(255,255,255,0.04); } .warning-num { font-family: 'Orbitron', monospace; font-size: 0.6rem; font-weight: 900; color: var(--neon-pink); opacity: 0.6; min-width: 22px; padding-top: 3px; letter-spacing: 0.05em; flex-shrink: 0; } .warning-item p { font-family: 'Rajdhani', sans-serif; font-size: 1.05rem; font-weight: 500; color: rgba(220, 210, 240, 0.82); line-height: 1.6; margin: 0; } .warning-item p strong { color: #fff; font-weight: 700; } .warning-item p em { color: var(--neon-cyan); font-style: normal; font-weight: 600; } .warning-divider { height: 1px; background: linear-gradient(90deg, transparent, rgba(255,45,120,0.2), transparent); margin: 0; } .warning-consent { font-family: 'Rajdhani', sans-serif; font-size: 1rem; font-weight: 500; color: rgba(210, 200, 230, 0.65); line-height: 1.6; text-align: center; } .warning-consent strong { color: rgba(255,255,255,0.88); font-weight: 700; } .warning-buttons { display: flex; flex-direction: column; gap: 10px; } .btn-warning-enter { width: 100%; text-align: center; padding: 17px !important; font-size: 1rem !important; letter-spacing: 0.12em !important; } .btn-warning-leave { width: 100%; text-align: center; padding: 12px !important; background: transparent !important; border: 1px solid rgba(255,255,255,0.08) !important; color: rgba(180,170,200,0.4) !important; font-family: 'Rajdhani', sans-serif; font-weight: 600; font-size: 0.85rem; letter-spacing: 0.12em; text-transform: uppercase; border-radius: 8px; transition: all 0.2s; text-shadow: none !important; display: block; cursor: pointer; } .btn-warning-leave:hover { border-color: rgba(255,80,80,0.4) !important; color: rgba(255,110,110,0.75) !important; background: rgba(255,40,40,0.04) !important; text-shadow: none !important; } </style>
<<silently>><<set $scarlettSeenThisSession = true>><</silently>> <div id="welcome-straight-screen"> <button id="welcome-skip-btn">Skip ›</button> <div id="welcome-center"> <!-- Image --> <div id="welcome-img-wrap"> <img src="Images/npc/welcomer_straight.png" id="welcome-npc-img" alt="Scarlett"> </div> <!-- Dialogue --> <div id="welcome-dialogue-wrap"> <div id="welcome-game-title">◈ The 21 Desires — Straight</div> <div id="welcome-npc-name">Scarlett</div> <div id="welcome-dialogue-text"></div> <div id="welcome-dialogue-dots"> <span class="wd-dot active"></span> <span class="wd-dot"></span> <span class="wd-dot"></span> </div> <button id="welcome-next-btn">Continue ›</button> </div> </div> </div> <<done>> <<script>> (function() { var lines = [ "Welcome back\u2026 I\u2019ve been waiting for you.", "I\u2019m Scarlett. I run things around here \u2014 the cards, the odds, and everything in between.", "Take a seat. Let\u2019s see if tonight is your night." ]; var current = 0; var textEl = document.getElementById('welcome-dialogue-text'); var nextBtn = document.getElementById('welcome-next-btn'); var skipBtn = document.getElementById('welcome-skip-btn'); var dots = document.querySelectorAll('.wd-dot'); function showLine(i) { if (!textEl) return; textEl.style.opacity = '0'; setTimeout(function() { textEl.textContent = lines[i]; textEl.style.opacity = '1'; }, 200); dots.forEach(function(d, idx) { d.classList.toggle('active', idx === i); }); if (nextBtn) { nextBtn.textContent = (i >= lines.length - 1) ? 'Enter the Casino \u203a' : 'Continue \u203a'; } } showLine(0); if (nextBtn) { nextBtn.addEventListener('click', function() { if (current < lines.length - 1) { current++; showLine(current); } else { Engine.play('Casino'); } }); } if (skipBtn) { skipBtn.addEventListener('click', function() { Engine.play('Casino'); }); } })(); <</script>> <</done>> <style> /* ── Masquer la save bar sur cette page ── */ body:has(#welcome-straight-screen) #save-bar { display: none !important; } #welcome-straight-screen { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; background: radial-gradient(ellipse at 70% 50%, rgba(30,10,50,0.95) 0%, rgba(6,4,15,1) 70%); overflow: hidden; } /* Fond décoratif */ #welcome-straight-screen::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 60% 40% at 75% 60%, rgba(255,45,120,0.06) 0%, transparent 70%), radial-gradient(ellipse 40% 60% at 20% 50%, rgba(191,0,255,0.04) 0%, transparent 70%); pointer-events: none; } /* ── Skip ── */ #welcome-skip-btn { position: absolute; top: 20px; right: 24px; background: transparent; border: 1px solid rgba(255,255,255,0.1); color: rgba(255,255,255,0.3); font-family: 'Share Tech Mono', monospace; font-size: 0.75rem; letter-spacing: 0.2em; padding: 5px 14px; border-radius: 20px; cursor: pointer; transition: all 0.2s; z-index: 10; } #welcome-skip-btn:hover { border-color: rgba(255,255,255,0.25); color: rgba(255,255,255,0.55); } /* ── Conteneur central ── */ #welcome-center { position: relative; z-index: 2; display: flex; align-items: center; gap: 60px; max-width: 1000px; width: 90%; padding: 0 20px; } /* ── Image ── */ #welcome-img-wrap { flex: 0 0 auto; position: relative; } #welcome-npc-img { width: 320px; height: 440px; object-fit: cover; object-position: center top; border-radius: 20px; border: 1px solid rgba(255,45,120,0.2); box-shadow: 0 0 0 1px rgba(255,255,255,0.04), 0 20px 60px rgba(0,0,0,0.7), 0 0 40px rgba(255,45,120,0.1); animation: welcomeImgIn 0.8s ease-out; display: block; } /* Lueur sous l'image */ #welcome-img-wrap::after { content: ''; position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); width: 200px; height: 30px; background: radial-gradient(ellipse, rgba(255,45,120,0.25), transparent 70%); pointer-events: none; } /* ── Dialogue ── */ #welcome-dialogue-wrap { flex: 1; display: flex; flex-direction: column; align-items: flex-start; animation: welcomeTextIn 0.7s ease-out 0.2s both; } #welcome-game-title { font-family: 'Share Tech Mono', monospace; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.4em; color: rgba(255,45,120,0.45); text-transform: uppercase; margin-bottom: 28px; } #welcome-npc-name { font-family: 'Orbitron', monospace; font-size: 1.1rem; font-weight: 700; letter-spacing: 0.2em; color: var(--neon-pink); text-transform: uppercase; margin-bottom: 18px; text-shadow: 0 0 16px rgba(255,45,120,0.45); display: flex; align-items: center; gap: 12px; } #welcome-npc-name::before { content: ''; display: inline-block; width: 28px; height: 1px; background: var(--neon-pink); opacity: 0.7; } #welcome-dialogue-text { font-family: 'Rajdhani', sans-serif; font-size: 1.2rem; font-weight: 500; color: rgba(240,230,255,0.92); line-height: 1.8; min-height: 80px; max-width: 360px; transition: opacity 0.22s; margin-bottom: 28px; } #welcome-dialogue-dots { display: flex; gap: 7px; margin-bottom: 32px; } .wd-dot { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,45,120,0.2); transition: all 0.3s; } .wd-dot.active { background: var(--neon-pink); box-shadow: 0 0 10px rgba(255,45,120,0.7); width: 22px; border-radius: 4px; } #welcome-next-btn { padding: 13px 32px; background: linear-gradient(135deg, #c4005e, #ff2d78); border: 1px solid rgba(255,45,120,0.5); border-radius: 8px; color: white; font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 0.95rem; letter-spacing: 0.15em; text-transform: uppercase; cursor: pointer; transition: all 0.25s; box-shadow: 0 0 24px rgba(255,45,120,0.25); } #welcome-next-btn:hover { background: linear-gradient(135deg, #ff2d78, #ff6ea3); box-shadow: 0 0 40px rgba(255,45,120,0.5); transform: translateY(-2px); } @keyframes welcomeImgIn { from { opacity: 0; transform: translateY(16px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } } @keyframes welcomeTextIn { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: translateX(0); } } </style>
<div class="world-select-screen"> <div class="world-select-title"> <h1 class="game-title">THE <span>21</span> DESIRES</h1> <p class="world-select-subtitle">◈ Choose Your World ◈</p> </div> <div class="world-cards" id="world-cards-container"></div> </div> <<done>> <<script>> (function() { var v = State.variables; var container = document.getElementById('world-cards-container'); if (!container) return; var worlds = [ { id: 'hetero', cls: 'world-card-straight', icon: 'Images/icons/straight_icon.png', name: 'Straight', desc: 'Girls, couples & all hetero fantasies.', unlocked: true }, { id: 'gay', cls: 'world-card-gay', icon: 'Images/icons/gay_icon.png', name: 'Gay', desc: 'Men, couples & gay scenes.', unlocked: false, comingSoon: true }, { id: 'trans', cls: 'world-card-trans', icon: 'Images/icons/trans_icon.png', name: 'Trans', desc: 'Sissy, trans solo & trans scenes.', unlocked: false, comingSoon: true } ]; worlds.forEach(function(w) { var isActive = v.activeBranch === w.id; var card = document.createElement('div'); card.className = 'world-card ' + w.cls + (w.unlocked ? '' : ' world-locked') + (isActive ? ' world-active' : ''); var badge = isActive ? '<div class="world-badge-active">ACTIVE</div>' : ''; var statusHtml = ''; if (w.comingSoon) { statusHtml = '<div class="world-coming-soon"><span class="world-cs-icon">✦</span> Coming Soon</div>'; } else if (!w.unlocked) { var pct = Math.min(100, Math.round(w.earned / w.threshold * 100)); statusHtml = '<span class="world-status world-status-locked">🔒 Locked</span>' + '<div class="world-lock-progress">' + '<div class="world-lock-bar-wrap"><div class="world-lock-bar-fill" style="width:' + pct + '%"></div></div>' + '<div class="world-lock-text">' + formatMoney(w.earned) + ' / ' + formatMoney(w.threshold) + ' total earned</div>' + '</div>'; } else if (isActive) { statusHtml = '<span class="world-status world-status-active">✦ Current World</span>'; } else { statusHtml = '<span class="world-status world-status-open">✦ Enter</span>'; } card.innerHTML = badge + '<div class="world-icon"><img src="' + w.icon + '" alt="' + w.name + '" class="world-icon-img" onerror="this.style.opacity=\'0.3\'"></div>' + '<div class="world-name">' + w.name + '</div>' + '<div class="world-desc">' + w.desc + '</div>' + statusHtml; /* Click : entrer dans le monde (uniquement si débloqué et pas coming soon) */ if (w.unlocked && !w.comingSoon) { card.addEventListener('click', function() { v.activeBranch = w.id; if (w.id === 'hetero') { v.activeTable = v.unlockedTables[v.unlockedTables.length - 1] || 'vice'; if (!v.scarlettSeenThisSession) { Engine.play('Welcome Straight'); } else { Engine.play('Casino'); } } else if (w.id === 'gay') { v.activeTable = v.unlockedTablesGay[v.unlockedTablesGay.length - 1] || 'rainbow'; Engine.play('Casino'); } else if (w.id === 'trans') { v.activeTable = v.unlockedTablesTrans[v.unlockedTablesTrans.length - 1] || 'transroom'; Engine.play('Casino'); } }); } container.appendChild(card); }); })(); <</script>> <</done>>