Created
December 25, 2025 17:06
-
-
Save LrWm3/e4cfe4ea595ea672b1c288a7f65eaeee to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>Fauxlatro</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| :root { | |
| --card-w: 80px; --card-h: 115px; | |
| --joker-w: 75px; --joker-h: 105px; | |
| --bg: #0b0d14; | |
| --accent-blue: #4d94ff; --accent-red: #ff4d4d; --accent-gold: #f1c40f; | |
| --hand-bg: #1a1d2e; | |
| --suit-spade: #2c3e50; --suit-heart: #e74c3c; --suit-club: #3498db; --suit-diamond: #e67e22; | |
| } | |
| @media (min-width: 640px) { :root { --card-w: 105px; --card-h: 150px; --joker-w: 95px; --joker-h: 130px; } } | |
| body { | |
| background-color: var(--bg); color: white; height: 100vh; margin: 0; | |
| display: flex; flex-direction: column; font-family: 'Courier New', Courier, monospace; | |
| touch-action: none; user-select: none; overflow: hidden; | |
| } | |
| * { scrollbar-width: none; -ms-overflow-style: none; } | |
| *::-webkit-scrollbar { display: none; } | |
| .crt-overlay { | |
| position: fixed; top: 0; left: 0; width: 100%; height: 100%; | |
| background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.05) 50%), | |
| linear-gradient(90deg, rgba(255, 0, 0, 0.02), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03)); | |
| background-size: 100% 4px, 4px 100%; pointer-events: none; z-index: 10000; | |
| } | |
| .header-stats { position: fixed; top: 10px; left: 10px; z-index: 7000; display: flex; flex-direction: column; gap: 8px; width: 160px; } | |
| .ui-panel { background: rgba(0,0,0,0.9); padding: 6px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.1); backdrop-filter: blur(8px); } | |
| .info-btn { | |
| position: fixed; top: 10px; right: 10px; z-index: 7000; | |
| background: rgba(0,0,0,0.8); border: 1px solid rgba(255,255,255,0.2); | |
| border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; | |
| font-weight: bold; cursor: pointer; transition: background 0.2s; | |
| } | |
| .info-btn:hover { background: var(--hand-bg); border-color: var(--accent-blue); } | |
| .joker-area { position: fixed; top: 10px; left: 180px; right: 50px; height: calc(var(--joker-h) + 15px); border: 2px dashed rgba(255,255,255,0.1); border-radius: 12px; z-index: 6000; background: rgba(0,0,0,0.4); } | |
| .card, .joker-card, .planet-card, .pack-card { | |
| width: var(--card-w); height: var(--card-h); background: #fff; border-radius: 8px; position: absolute; | |
| display: flex; flex-direction: column; justify-content: space-between; padding: 10px; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.8); cursor: pointer; color: #000; | |
| transition: left 0.5s cubic-bezier(0.19, 1, 0.22, 1), top 0.5s cubic-bezier(0.19, 1, 0.22, 1), transform 0.4s cubic-bezier(0.19, 1, 0.22, 1), width 0.4s, height 0.4s, opacity 0.3s; | |
| transform: translate(-50%, -50%); | |
| } | |
| .joker-card { width: var(--joker-w); height: var(--joker-h); } | |
| .planet-card { width: var(--joker-w); height: var(--joker-h); background: #2a1a3e; color: #fff; border: 2px solid #6b4dff; } | |
| .pack-card { width: var(--joker-w); height: var(--joker-h); background: #1a3e2a; color: #fff; border: 2px solid #4dff88; } | |
| .inspected { border: 3px solid var(--accent-gold) !important; box-shadow: 0 0 25px var(--accent-gold) !important; transform: translate(-50%, -50%) scale(1.15) !important; z-index: 900 !important; } | |
| .selected { border: 3px solid var(--accent-blue); box-shadow: 0 0 20px var(--accent-blue); z-index: 100; } | |
| .dragging { position: fixed !important; z-index: 9999 !important; transition: none !important; opacity: 0.9; pointer-events: none; } | |
| .placeholder { background: rgba(255,255,255,0.05); border: 2px dashed rgba(255,255,255,0.2); box-shadow: none; visibility: visible; } | |
| .placeholder * { visibility: hidden; } | |
| .not-scoring { opacity: 0.25; filter: grayscale(0.9); transform: translate(-50%, -30%) scale(0.8) !important; } | |
| .rank-label { font-weight: 900; font-size: 1.5rem; line-height: 1; pointer-events: none; } | |
| .suit-icon { align-self: flex-end; font-size: 1.8rem; line-height: 1; pointer-events: none; } | |
| .suit-club { color: var(--suit-club); } .suit-heart { color: var(--suit-heart); } .suit-diamond { color: var(--suit-diamond); } .suit-spade { color: var(--suit-spade); } | |
| .game-viewport { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; padding-bottom: 20px; } | |
| .played-zone { width: 100%; height: 160px; position: relative; margin-bottom: 20px; pointer-events: none; } | |
| .hand-zone { width: 100%; max-width: 1000px; height: 180px; position: relative; margin: 0 auto; } | |
| .footer-panel { background: rgba(0,0,0,0.95); border-top: 2px solid #1a1d2e; padding: 15px; z-index: 2000; } | |
| .big-btn { height: 65px; min-width: 130px; border-radius: 8px; font-weight: 900; text-transform: uppercase; border: 3px solid rgba(255,255,255,0.1); cursor: pointer; } | |
| .btn-play { background: #1a2a4e; color: var(--accent-blue); } | |
| .btn-discard { background: #3e1a1a; color: var(--accent-red); } | |
| .overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.95); display: none; flex-direction: column; align-items: center; justify-content: center; z-index: 5000; } | |
| .shop-overlay { background: #06070a; z-index: 4000; padding: 20px; justify-content: center; overflow: hidden; } | |
| .shop-title-container { margin-bottom: 20px; transform: rotate(-3deg); z-index: 10; } | |
| .shop-title { font-size: 3.5rem; font-weight: 900; color: #fff; text-shadow: 0 0 10px var(--accent-blue), 0 0 20px var(--accent-blue); font-style: italic; letter-spacing: -3px; line-height: 1; } | |
| .shop-section-label { text-transform: uppercase; font-size: 10px; font-weight: 900; color: var(--accent-gold); letter-spacing: 4px; margin-bottom: 8px; opacity: 0.6; } | |
| .shop-row { position: relative; width: 100%; height: 140px; margin-bottom: 30px; display: flex; justify-content: center; } | |
| .price-tag { position: absolute; bottom: -25px; left: 50%; transform: translateX(-50%); color: var(--accent-gold); font-weight: 900; font-size: 14px; pointer-events: none; } | |
| .inspect-panel { | |
| position: fixed; top: 180px; right: 20px; width: 240px; background: var(--hand-bg); border: 2px solid var(--accent-gold); | |
| border-radius: 8px; padding: 15px; z-index: 8000; display: none; box-shadow: 0 10px 30px rgba(0,0,0,0.8); | |
| } | |
| @keyframes jiggle { 0%, 100% { transform: translate(-50%, -50%) scale(1); } 50% { transform: translate(-50%, -65%) scale(1.15); } } | |
| @keyframes pack-shake { 0%, 100% { transform: translate(-50%, -50%) rotate(0deg) scale(1.5); } 25% { transform: translate(-52%, -50%) rotate(-5deg) scale(1.6); } 75% { transform: translate(-48%, -50%) rotate(5deg) scale(1.6); } } | |
| .trigger-jiggle { animation: jiggle 0.3s ease-out; z-index: 3000 !important; box-shadow: 0 0 40px #fff; } | |
| .pack-opening-anim { animation: pack-shake 0.1s infinite; z-index: 9000 !important; } | |
| .fly-away { transform: translate(150vw, -100vh) rotate(90deg) scale(0.2) !important; opacity: 0; pointer-events: none; } | |
| </style> | |
| </head> | |
| <body onclick="handleGlobalClick(event)"> | |
| <div class="crt-overlay"></div> | |
| <button class="info-btn" onclick="toggleInfo(true)">?</button> | |
| <div class="header-stats"> | |
| <div class="ui-panel"> | |
| <p class="text-[8px] uppercase opacity-50 font-bold">Ante <span id="ante-num">1/8</span></p> | |
| <p class="text-xs font-black text-white uppercase tracking-tighter" id="blind-name">Small Blind</p> | |
| </div> | |
| <div class="ui-panel"><p class="text-[8px] uppercase opacity-50 font-bold">Money</p><p class="text-lg font-black text-yellow-400" id="money-display">$10</p></div> | |
| <div class="ui-panel"><p class="text-[8px] uppercase opacity-50 font-bold">Round Score</p><div class="flex items-baseline gap-1"><p class="text-lg font-black text-blue-400" id="total-score">0</p><p class="text-[10px] opacity-40 font-bold">/ <span id="target-score">300</span></p></div></div> | |
| </div> | |
| <div class="joker-area" id="joker-inventory-list"></div> | |
| <div class="inspect-panel" id="inspect-panel"> | |
| <h3 class="text-accent-gold font-black uppercase text-sm id-name" id="inspect-name">NAME</h3> | |
| <p class="text-[10px] text-gray-300 leading-tight mb-4" id="inspect-desc">DESCRIPTION.</p> | |
| <div id="inspect-actions"> | |
| <button class="w-full bg-red-900 border border-red-500 text-white font-black py-2 rounded text-[10px] uppercase" id="sell-btn">Sell</button> | |
| <p class="text-[8px] text-white/30 text-center mt-2 uppercase italic" id="buy-hint">Drag into slot to purchase</p> | |
| </div> | |
| </div> | |
| <div class="fixed top-[180px] left-1/2 -translate-x-1/2 z-[1500] text-center pointer-events-none"> | |
| <div class="ui-panel border-blue-500/50 min-w-[280px]" id="hand-box"> | |
| <p class="text-sm font-black uppercase tracking-widest" id="hand-name">Select Cards</p> | |
| <p class="text-lg font-black mt-1" id="hand-score-val"></p> | |
| </div> | |
| </div> | |
| <div class="game-viewport"> | |
| <div class="played-zone" id="played-list"></div> | |
| <div class="hand-zone" id="hand-list"></div> | |
| </div> | |
| <div class="footer-panel"> | |
| <div class="flex justify-center gap-3 mb-4 flex-wrap"> | |
| <button class="big-btn btn-play" id="play-btn" onclick="playHandSequence()">Play <span class="block text-[10px] opacity-60" id="play-sub">4 Left</span></button> | |
| <button class="big-btn btn-discard" id="discard-btn" onclick="discardSelected()">Discard <span class="block text-[10px] opacity-60" id="discard-sub">3 Left</span></button> | |
| <button class="big-btn bg-zinc-800 text-white min-w-[160px]" id="sort-toggle-btn" onclick="toggleSort()">Sort By Suit</button> | |
| </div> | |
| <div class="flex justify-between items-center max-w-[800px] mx-auto text-[10px] font-bold uppercase text-white/30"> | |
| <div id="sel-count">0/5 Selected</div> | |
| <button onclick="resetGame()" class="hover:text-white underline">[ Reset Run ]</button> | |
| <div id="deck-info">Deck: 52</div> | |
| </div> | |
| </div> | |
| <!-- Shop Modal --> | |
| <div id="shop-overlay" class="overlay shop-overlay"> | |
| <div class="shop-title-container"><h1 class="shop-title italic uppercase">The Shop</h1></div> | |
| <div class="w-full max-w-2xl px-4"> | |
| <div class="flex flex-col items-center"> | |
| <div class="w-full"> | |
| <div class="shop-section-label text-center">Jokers</div> | |
| <div class="shop-row" id="shop-list-jokers"></div> | |
| </div> | |
| <div class="w-full"> | |
| <div class="shop-section-label text-center">Packs</div> | |
| <div class="shop-row" id="shop-list-packs"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex gap-4"> | |
| <button class="bg-gray-800 border border-yellow-600 text-yellow-500 px-8 py-3 rounded font-black text-xs uppercase" onclick="rerollShop()">Reroll ($5)</button> | |
| <button class="bg-blue-600 px-12 py-3 rounded font-black text-xs uppercase shadow-lg shadow-blue-900/50" onclick="startNextRound()">Next Round</button> | |
| </div> | |
| </div> | |
| <!-- Pack Selection Modal --> | |
| <div id="pack-overlay" class="overlay" style="background: rgba(0,0,0,0.98); z-index: 8500;"> | |
| <div id="pack-selection-container" class="w-full h-full flex flex-col items-center justify-center"> | |
| <h2 class="text-4xl font-black italic uppercase text-accent-gold mb-2" id="pack-name-display">PACK NAME</h2> | |
| <p class="text-white opacity-50 mb-12 uppercase tracking-widest text-xs" id="pack-picks-display">Picks Left: 1</p> | |
| <div class="w-full max-w-3xl h-[200px] relative" id="pack-item-list"></div> | |
| <button class="mt-24 border border-white/20 px-8 py-3 text-[10px] uppercase font-black hover:bg-white/10" id="skip-pack-btn" onclick="closePack()">Skip Pack</button> | |
| </div> | |
| </div> | |
| <!-- Info Overlay --> | |
| <div id="info-overlay" class="overlay"> | |
| <div class="bg-black border border-white/20 p-8 rounded-lg max-w-md text-center shadow-2xl"> | |
| <h2 class="text-3xl font-black italic uppercase text-blue-400 mb-4">About Fauxlatro</h2> | |
| <p class="text-sm text-gray-300 mb-2">Inspired by the masterpiece</p> | |
| <p class="text-xl font-black text-white uppercase tracking-widest mb-4">BALATRO</p> | |
| <p class="text-[10px] text-gray-500 uppercase mb-8">All rights reserved to LocalThunk and Playstack.</p> | |
| <a href="https://www.playbalatro.com" target="_blank" class="block w-full bg-blue-600 text-white font-black py-4 rounded uppercase mb-4">Visit Official Site</a> | |
| <button class="text-xs text-gray-400 underline uppercase" onclick="toggleInfo(false)">Close</button> | |
| </div> | |
| </div> | |
| <!-- Win Overlay --> | |
| <div id="win-overlay" class="overlay"> | |
| <h1 class="text-5xl font-black text-blue-400 uppercase italic">Round Won</h1> | |
| <div class="bg-white/5 border border-white/10 p-8 rounded my-8 min-w-[320px]"> | |
| <div class="flex justify-between mb-2"><span>Base Reward:</span><span class="text-yellow-400">$4</span></div> | |
| <div class="flex justify-between mb-2"><span>Hands Left:</span><span class="text-yellow-400" id="win-bonus">$0</span></div> | |
| <div class="flex justify-between mb-2"><span>Joker Bonus:</span><span class="text-yellow-400" id="joker-gold-bonus">$0</span></div> | |
| <div class="flex justify-between border-b border-white/10 pb-2 mb-2"><span>Interest:</span><span class="text-yellow-400" id="interest-bonus">$0</span></div> | |
| <div class="flex justify-between pt-2 font-black text-2xl"><span>Total Gained:</span><span class="text-yellow-400" id="win-total">$0</span></div> | |
| </div> | |
| <button class="bg-blue-500 text-black font-black px-12 py-4 rounded uppercase" onclick="goToShop()">Enter Shop</button> | |
| </div> | |
| <!-- Lose Overlay --> | |
| <div id="lose-overlay" class="overlay"> | |
| <h1 class="text-7xl font-black text-red-600 italic uppercase">Defeated</h1> | |
| <button class="bg-red-600 text-white font-black px-12 py-4 rounded mt-10 uppercase" onclick="resetGame()">New Run</button> | |
| </div> | |
| <!-- Victory Overlay --> | |
| <div id="victory-overlay" class="overlay"> | |
| <h1 class="text-7xl font-black text-yellow-400 italic uppercase text-center">Victory!<br><span class="text-3xl">Ante 8 Defeated</span></h1> | |
| <button class="bg-yellow-400 text-black font-black px-12 py-4 rounded mt-10 uppercase" onclick="resetGame()">New Run</button> | |
| </div> | |
| <script> | |
| // --- CONSTANTS --- | |
| const ANTE_BASE_SCORES = [300, 800, 2000, 5000, 11000, 20000, 35000, 50000]; | |
| const BLIND_MODS = [ { name: "Small Blind", mult: 1.0 }, { name: "Big Blind", mult: 1.5 }, { name: "Boss Blind", mult: 2.0 } ]; | |
| const HAND_TYPES = { | |
| 'Straight Flush': { emoji: '🍉', baseChips: 100, baseMult: 8, chipMod: 40, multMod: 4 }, | |
| 'Four of a Kind': { emoji: '🍑', baseChips: 70, baseMult: 6, chipMod: 30, multMod: 3 }, | |
| 'Full House': { emoji: '🍐', baseChips: 40, baseMult: 4, chipMod: 25, multMod: 2 }, | |
| 'Flush': { emoji: '🍊', baseChips: 35, baseMult: 4, chipMod: 15, multMod: 2 }, | |
| 'Straight': { emoji: '🍓', baseChips: 30, baseMult: 4, chipMod: 30, multMod: 3 }, | |
| 'Three of a Kind': { emoji: '🍋', baseChips: 30, baseMult: 3, chipMod: 20, multMod: 2 }, | |
| 'Two Pair': { emoji: '🍇', baseChips: 20, baseMult: 2, chipMod: 20, multMod: 1 }, | |
| 'Pair': { emoji: '🍌', baseChips: 10, baseMult: 2, chipMod: 15, multMod: 1 }, | |
| 'High Card': { emoji: '🍒', baseChips: 5, baseMult: 1, chipMod: 10, multMod: 1 } | |
| }; | |
| const JOKER_POOL = [ | |
| { id: 1, name: 'Basic', emoji: '🃏', price: 2, mult: 4, chips: 0, xMult: 1, trigger: 'all', desc: "+4 Mult on every hand." }, | |
| { id: 2, name: 'Chiller', emoji: '❄️', price: 2, mult: 0, chips: 20, xMult: 1, trigger: 'all', desc: "+20 Chips on every hand." }, | |
| { id: 3, name: 'Pair Buddy', emoji: '👯', price: 4, mult: 12, chips: 0, xMult: 1, trigger: 'Pair', desc: "+12 Mult if hand contains a Pair." }, | |
| { id: 4, name: 'Flush Burn', emoji: '🔥', price: 6, mult: 0, chips: 0, xMult: 1.5, trigger: 'Flush', desc: "x1.5 Mult if hand contains a Flush." }, | |
| { id: 5, name: 'Archer', emoji: '🏹', price: 4, mult: 0, chips: 60, xMult: 1, trigger: 'Straight', desc: "+60 Chips if hand contains a Straight." }, | |
| { id: 6, name: 'Odd Todd', emoji: '🔢', price: 4, mult: 0, chips: 30, xMult: 1, trigger: 'odd', desc: "+30 Chips for scoring Odd ranks." }, | |
| { id: 7, name: 'Even Steven', emoji: '🔟', price: 4, mult: 4, chips: 0, xMult: 1, trigger: 'even', desc: "+4 Mult for scoring Even ranks." }, | |
| { id: 8, name: 'The Trio', emoji: '3️⃣', price: 8, mult: 0, chips: 0, xMult: 3, trigger: 'Three of a Kind', desc: "x3 Mult if hand contains 3 of a Kind." }, | |
| { id: 9, name: 'Golden Joker', emoji: '💰', price: 6, mult: 0, chips: 0, xMult: 1, trigger: 'win', desc: "Earn $3 extra when you win a round." }, | |
| { id: 10, name: 'Blue Joker', emoji: '💙', price: 5, mult: 0, chips: 0, xMult: 1, trigger: 'deck', desc: "+2 Chips per card remaining in Deck." }, | |
| { id: 11, name: 'Lusty', emoji: '❤️', price: 4, mult: 4, chips: 0, xMult: 1, trigger: 'suit_2', desc: "+4 Mult for scoring Hearts." }, | |
| { id: 12, name: 'Greedy', emoji: '💎', price: 4, mult: 4, chips: 0, xMult: 1, trigger: 'suit_1', desc: "+4 Mult for scoring Diamonds." }, | |
| { id: 13, name: 'Wrathful', emoji: '♠️', price: 4, mult: 4, chips: 0, xMult: 1, trigger: 'suit_0', desc: "+4 Mult for scoring Spades." }, | |
| { id: 14, name: 'Gluttonous', emoji: '♣️', price: 4, mult: 4, chips: 0, xMult: 1, trigger: 'suit_3', desc: "+4 Mult for scoring Clubs." }, | |
| { id: 15, name: 'Recycler', emoji: '♻️', price: 5, mult: 0, chips: 0, xMult: 1, trigger: 'discard_cash', desc: "Earn $1 for every Discard used." }, | |
| { id: 16, name: 'Eke Joker', emoji: '👑', price: 8, mult: 0, chips: 0, xMult: 3, trigger: 'all', desc: "x3 Mult. Hail to the King." }, | |
| { id: 17, name: 'Tater Joker', emoji: '🐶', price: 8, mult: 0, chips: 0, xMult: 3, trigger: 'all', desc: "x3 Mult. Good boy!" }, | |
| { id: 18, name: 'Banana Joker', emoji: '🍌', price: 4, mult: 15, chips: 0, xMult: 1, trigger: 'all', desc: "+15 Mult. Potassium power!" } | |
| ]; | |
| const PACK_POOL_DEF = [ | |
| { type: 'fruit', size: 'Small', price: 4, show: 3, pick: 1, count: 8 }, | |
| { type: 'fruit', size: 'Medium', price: 6, show: 5, pick: 1, count: 8 }, | |
| { type: 'fruit', size: 'Large', price: 8, show: 5, pick: 1, count: 4 }, | |
| { type: 'joker', size: 'Small', price: 8, show: 2, pick: 1, count: 4 }, | |
| { type: 'joker', size: 'Medium', price: 10, show: 4, pick: 1, count: 4 }, | |
| { type: 'joker', size: 'Large', price: 12, show: 5, pick: 2, count: 2 }, | |
| ]; | |
| // --- GLOBAL STATE --- | |
| let packDeck = [], planetDeck = [], deck = []; | |
| let totalScore = 0, money = 10, plays = 4, discards = 3; | |
| let anteIndex = 0, blindIndex = 0; | |
| let selectedOrder = [], inventory = [], shopJokers = [], shopPacks = [], isBusy = false, sortMode = 'rank'; | |
| let handLevels = Object.keys(HAND_TYPES).reduce((acc, t) => ({...acc, [t]: 1}), {}); | |
| let inspectedIdx = -1, inspectedSource = null; | |
| let activePack = null, packPicksLeft = 0, packSelectionItems = []; | |
| // --- CORE FUNCTIONS --- | |
| const sleep = (ms) => new Promise(r => setTimeout(r, ms)); | |
| const toggleInfo = (show) => document.getElementById('info-overlay').style.display = show ? 'flex' : 'none'; | |
| function initDecks() { | |
| const SUITS = [{s:'♣',c:'suit-club',v:3}, {s:'♥',c:'suit-heart',v:2}, {s:'♦',c:'suit-diamond',v:1}, {s:'♠',c:'suit-spade',v:0}]; | |
| const RANKS = [{n:'2',v:2},{n:'3',v:3},{n:'4',v:4},{n:'5',v:5},{n:'6',v:6},{n:'7',v:7},{n:'8',v:8},{n:'9',v:9},{n:'10',v:10},{n:'J',v:10,val:11},{n:'Q',v:10,val:12},{n:'K',v:10,val:13},{n:'A',v:11,val:14}]; | |
| deck = []; | |
| SUITS.forEach(s => RANKS.forEach(r => deck.push({ rank: r.n, chips: r.v, sortVal: r.val || r.v, suit: s.s, suitCss: s.c, suitVal: s.v }))); | |
| for (let i = deck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i+1)); [deck[i], deck[j]] = [deck[j], deck[i]]; } | |
| packDeck = []; | |
| PACK_POOL_DEF.forEach((p, idx) => { for(let i=0; i<p.count; i++) packDeck.push({...p, id: `pack-${idx}-${i}`}); }); | |
| planetDeck = Object.keys(HAND_TYPES).map(name => ({ name, emoji: HAND_TYPES[name].emoji })); | |
| shuffleArray(planetDeck); | |
| } | |
| function shuffleArray(arr) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } } | |
| function drawToFull() { | |
| const list = document.getElementById('hand-list'); | |
| const currentCount = list.querySelectorAll('.card:not(.dragging):not(.fly-away)').length; | |
| const needed = 8 - currentCount; | |
| for (let i = 0; i < Math.min(needed, deck.length); i++) { | |
| setTimeout(() => { | |
| const d = deck.pop(); | |
| const el = document.createElement('div'); | |
| el.className = 'card'; el.dataset.rankSort = d.sortVal; el.dataset.rankChips = d.chips; el.dataset.suitSort = d.suitVal; | |
| el.dataset.suitCss = d.suitCss; el.dataset.visualIndex = 1000 + i; | |
| el.innerHTML = `<div class="rank-label ${d.suitCss}">${d.rank}</div><div class="suit-icon ${d.suitCss}">${d.suit}</div>`; | |
| el.onpointerdown = (e) => onEntityDown(e, 'hand-list', 'hand'); | |
| list.appendChild(el); | |
| applySort(); updateUI(); | |
| }, i * 80); | |
| } | |
| } | |
| function updateSpacings(id) { | |
| const list = document.getElementById(id); | |
| if (!list) return; | |
| const items = [...list.querySelectorAll('.card:not(.dragging):not(.fly-away), .joker-card:not(.dragging), .planet-card:not(.dragging), .pack-card:not(.dragging)')]; | |
| if (id === 'hand-list') items.sort((a, b) => parseFloat(a.dataset.visualIndex) - parseFloat(b.dataset.visualIndex)); | |
| items.forEach((item, i) => { | |
| item.style.left = ((i + 1) / (items.length + 1)) * 100 + "%"; | |
| item.style.top = "50%"; | |
| const isSelected = item.classList.contains('selected'); | |
| const yOffset = isSelected ? '-45px' : '0px'; | |
| item.style.transform = `translate(-50%, calc(-50% + ${yOffset}))`; | |
| item.style.zIndex = isSelected ? 200 + i : i; | |
| }); | |
| } | |
| function getTargetScore() { return ANTE_BASE_SCORES[anteIndex] * BLIND_MODS[blindIndex].mult; } | |
| function handContains(handName, triggerName) { | |
| if (triggerName === 'all') return true; | |
| const h = { 'High Card': ['High Card', 'Pair', 'Two Pair', 'Three of a Kind', 'Straight', 'Flush', 'Full House', 'Four of a Kind', 'Straight Flush'], 'Pair': ['Pair', 'Two Pair', 'Three of a Kind', 'Full House', 'Four of a Kind'], 'Two Pair': ['Two Pair', 'Full House'], 'Three of a Kind': ['Three of a Kind', 'Full House', 'Four of a Kind'], 'Straight': ['Straight', 'Straight Flush'], 'Flush': ['Flush', 'Straight Flush'], 'Full House': ['Full House'], 'Four of a Kind': ['Four of a Kind'], 'Straight Flush': ['Straight Flush'] }; | |
| return h[triggerName]?.includes(handName) || false; | |
| } | |
| function evaluateHand() { | |
| const handCards = [...document.querySelectorAll('#hand-list .selected')].sort((a,b) => a.offsetLeft - b.offsetLeft); | |
| if (!handCards.length) return { name: "Select Cards", chips: 0, mult: 0, lvl: 1, scoring: [] }; | |
| const sortedByRank = [...handCards].sort((a,b) => parseInt(a.dataset.rankSort) - parseInt(b.dataset.rankSort)); | |
| const ranks = sortedByRank.map(c => parseInt(c.dataset.rankSort)); | |
| const suits = sortedByRank.map(c => parseInt(c.dataset.suitSort)); | |
| const counts = {}; ranks.forEach(r => counts[r] = (counts[r]||0)+1); | |
| const freq = Object.entries(counts).sort((a,b)=>b[1]-a[1]); | |
| const isFlush = new Set(suits).size === 1 && handCards.length === 5; | |
| let isStr = false; | |
| if (handCards.length === 5) { | |
| const uniq = [...new Set(ranks)]; | |
| if (uniq.length === 5 && (ranks[4]-ranks[0] === 4)) isStr = true; | |
| if (uniq.length === 5 && ranks[4] === 14 && ranks[0] === 2 && ranks[3] === 5) isStr = true; | |
| } | |
| let name = "High Card", scoringElements = []; | |
| if (isStr && isFlush) { name = "Straight Flush"; scoringElements = handCards; } | |
| else if (freq[0][1] === 4) { name = "Four of a Kind"; scoringElements = handCards.filter(c => c.dataset.rankSort == freq[0][0]); } | |
| else if (freq[0][1] === 3 && freq[1]?.[1] === 2) { name = "Full House"; scoringElements = handCards; } | |
| else if (isFlush) { name = "Flush"; scoringElements = handCards; } | |
| else if (isStr) { name = "Straight"; scoringElements = handCards; } | |
| else if (freq[0][1] === 3) { name = "Three of a Kind"; scoringElements = handCards.filter(c => c.dataset.rankSort == freq[0][0]); } | |
| else if (freq[0][1] === 2 && freq[1]?.[1] === 2) { name = "Two Pair"; scoringElements = handCards.filter(c => c.dataset.rankSort == freq[0][0] || c.dataset.rankSort == freq[1][0]); } | |
| else if (freq[0][1] === 2) { name = "Pair"; scoringElements = handCards.filter(c => c.dataset.rankSort == freq[0][0]); } | |
| else { name = "High Card"; scoringElements = [sortedByRank[sortedByRank.length-1]]; } | |
| const lvl = handLevels[name]; const d = HAND_TYPES[name]; | |
| return { name, lvl, chips: d.baseChips + (lvl-1)*d.chipMod, mult: d.baseMult + (lvl-1)*d.multMod, scoring: scoringElements }; | |
| } | |
| function updatePreview(n, c, m) { | |
| const nEl = document.getElementById('hand-name'); | |
| const sEl = document.getElementById('hand-score-val'); | |
| if (HAND_TYPES[n]) { | |
| const lvl = handLevels[n] || 1; | |
| nEl.innerText = `${HAND_TYPES[n].emoji} ${n} Lvl.${lvl}`; | |
| sEl.innerHTML = `<span style="color:#4d94ff">${c}</span> x <span style="color:#ff4d4d">${m}</span>`; | |
| } else { nEl.innerText = n; sEl.innerHTML = ""; } | |
| } | |
| async function playHandSequence() { | |
| if (isBusy) return; | |
| const evalResult = evaluateHand(); | |
| if (evalResult.name === "Select Cards") return; | |
| isBusy = true; plays--; deselectEverything(); | |
| const pZone = document.getElementById('played-list'); | |
| const hZone = document.getElementById('hand-list'); | |
| const jokerEls = document.querySelectorAll('#joker-inventory-list .joker-card'); | |
| const handCards = [...hZone.querySelectorAll('.card')].filter(el => el.classList.contains('selected')).sort((a,b) => a.offsetLeft - b.offsetLeft); | |
| const pRect = pZone.getBoundingClientRect(); | |
| for(let c of handCards) { | |
| const cR = c.getBoundingClientRect(); | |
| c.style.left = (cR.left - pRect.left + cR.width/2) + "px"; | |
| c.style.top = (cR.top - pRect.top + cR.height/2) + "px"; | |
| c.classList.remove('selected'); pZone.appendChild(c); | |
| } | |
| pZone.offsetHeight; | |
| handCards.forEach((c, i) => { c.style.left = ((i+1)/(handCards.length+1)*100)+"%"; c.style.top = "50%"; }); | |
| await sleep(600); | |
| let currentChips = evalResult.chips, currentMult = evalResult.mult; | |
| updatePreview(evalResult.name, currentChips, currentMult); | |
| for (const card of handCards) { | |
| if (!evalResult.scoring.includes(card)) { | |
| card.classList.add('not-scoring'); | |
| await sleep(150); | |
| continue; | |
| } | |
| card.classList.add('trigger-jiggle'); | |
| const rankVal = parseInt(card.dataset.rankSort); | |
| currentChips += parseInt(card.dataset.rankChips); | |
| for (let i=0; i<inventory.length; i++) { | |
| const j = inventory[i]; | |
| let triggered = (j.trigger === 'odd' && [3,5,7,9,14].includes(rankVal)) || | |
| (j.trigger === 'even' && [2,4,6,8,10].includes(rankVal)) || | |
| (j.trigger === `suit_${card.dataset.suitSort}`); | |
| if (triggered) { | |
| if (jokerEls[i]) jokerEls[i].classList.add('trigger-jiggle'); | |
| currentChips += j.chips; currentMult += j.mult; | |
| updatePreview(evalResult.name, currentChips, Math.floor(currentMult)); | |
| } | |
| } | |
| updatePreview(evalResult.name, currentChips, Math.floor(currentMult)); | |
| await sleep(400); | |
| card.classList.remove('trigger-jiggle'); | |
| jokerEls.forEach(el => el.classList.remove('trigger-jiggle')); | |
| } | |
| for (let i = 0; i < inventory.length; i++) { | |
| const j = inventory[i]; | |
| if (handContains(evalResult.name, j.trigger) || j.trigger === 'deck') { | |
| if (jokerEls[i]) jokerEls[i].classList.add('trigger-jiggle'); | |
| if (j.trigger === 'deck') currentChips += (deck.length * 2); | |
| else { currentChips += j.chips; currentMult += j.mult; } | |
| currentMult *= j.xMult; | |
| updatePreview(evalResult.name, currentChips, Math.floor(currentMult)); | |
| await sleep(500); | |
| if (jokerEls[i]) jokerEls[i].classList.remove('trigger-jiggle'); | |
| } | |
| } | |
| totalScore += (currentChips * Math.floor(currentMult)); updateUI(); | |
| await sleep(300); | |
| handCards.forEach(c => c.classList.add('fly-away')); | |
| await sleep(600); | |
| handCards.forEach(c => c.remove()); | |
| isBusy = false; | |
| if (totalScore >= getTargetScore()) showWin(); | |
| else if (plays <= 0) document.getElementById('lose-overlay').style.display = 'flex'; | |
| drawToFull(); | |
| } | |
| function updateUI() { | |
| const evalResult = evaluateHand(); | |
| const nEl = document.getElementById('hand-name'); | |
| const sEl = document.getElementById('hand-score-val'); | |
| if (HAND_TYPES[evalResult.name]) { nEl.innerText = `${HAND_TYPES[evalResult.name].emoji} ${evalResult.name} Lvl.${evalResult.lvl}`; sEl.innerHTML = `<span style="color:#4d94ff">${evalResult.chips}</span> x <span style="color:#ff4d4d">${evalResult.mult}</span>`; } else { nEl.innerText = evalResult.name; sEl.innerHTML = ""; } | |
| document.getElementById('money-display').innerText = `$${money}`; | |
| document.getElementById('total-score').innerText = totalScore.toLocaleString(); | |
| document.getElementById('target-score').innerText = Math.floor(getTargetScore()).toLocaleString(); | |
| document.getElementById('ante-num').innerText = `${anteIndex + 1}/8`; | |
| document.getElementById('blind-name').innerText = BLIND_MODS[blindIndex].name; | |
| document.getElementById('play-sub').innerText = `${plays} Left`; | |
| document.getElementById('discard-sub').innerText = `${discards} Left`; | |
| document.getElementById('sel-count').innerText = `${document.querySelectorAll('#hand-list .selected').length}/5 SELECTED`; | |
| document.getElementById('deck-info').innerText = `Deck: ${deck.length}`; | |
| updateSpacings('hand-list'); | |
| } | |
| // --- SHOP & INTERACTION --- | |
| function rerollShop(isFree = false) { | |
| if (!isFree) { if (money < 5) return; money -= 5; } | |
| const jokerPool = JOKER_POOL.filter(j => !inventory.find(inv => inv.id === j.id)); | |
| shopJokers = []; | |
| for (let i = 0; i < 2; i++) if (jokerPool.length) { | |
| const randIdx = Math.floor(Math.random() * jokerPool.length); | |
| shopJokers.push(jokerPool.splice(randIdx, 1)[0]); | |
| } | |
| shopPacks = []; | |
| const tempPackDeck = [...packDeck]; | |
| for (let i = 0; i < 2; i++) if (tempPackDeck.length) { | |
| const randIdx = Math.floor(Math.random() * tempPackDeck.length); | |
| shopPacks.push(tempPackDeck.splice(randIdx, 1)[0]); | |
| } | |
| renderShop(); updateUI(); | |
| } | |
| function renderShop() { | |
| const jCont = document.getElementById('shop-list-jokers'); | |
| const pCont = document.getElementById('shop-list-packs'); | |
| jCont.innerHTML = ''; pCont.innerHTML = ''; | |
| shopJokers.forEach((j, i) => { | |
| const div = document.createElement('div'); div.className = 'joker-card'; div.dataset.idx = i; | |
| div.innerHTML = `<span class="text-3xl">${j.emoji}</span><span class="text-[9px] font-black uppercase text-center mt-2">${j.name}</span><div class="price-tag">$${j.price}</div>`; | |
| div.onpointerdown = (e) => onEntityDown(e, 'shop-list-jokers', 'shop_joker'); | |
| jCont.appendChild(div); | |
| }); | |
| shopPacks.forEach((p, i) => { | |
| const div = document.createElement('div'); div.className = 'pack-card'; div.dataset.idx = i; | |
| div.innerHTML = `<span class="text-xs font-black uppercase text-center">${p.size} ${p.type} Pack</span><div class="price-tag">$${p.price}</div>`; | |
| div.onpointerdown = (e) => onEntityDown(e, 'shop-list-packs', 'shop_pack'); | |
| pCont.appendChild(div); | |
| }); | |
| updateSpacings('shop-list-jokers'); updateSpacings('shop-list-packs'); | |
| } | |
| async function purchasePack(idx) { | |
| const pack = shopPacks[idx]; if (money < pack.price) return; | |
| money -= pack.price; packDeck = packDeck.filter(p => p.id !== pack.id); shopPacks.splice(idx, 1); | |
| const shopEl = document.querySelectorAll('#shop-list-packs .pack-card')[idx]; | |
| shopEl.classList.add('pack-opening-anim'); shopEl.style.left = "50vw"; shopEl.style.top = "50vh"; | |
| await sleep(1000); openPack(pack); | |
| } | |
| function openPack(pack) { | |
| activePack = pack; packPicksLeft = pack.pick; packSelectionItems = []; | |
| for (let i = 0; i < pack.show; i++) { | |
| if (pack.type === 'fruit') { const p = planetDeck.shift(); packSelectionItems.push({ type: 'planet', ...p }); planetDeck.push(p); } | |
| else { const pool = JOKER_POOL.filter(j => !inventory.find(inv => inv.id === j.id)); packSelectionItems.push({ type: 'joker', ...pool[Math.floor(Math.random()*pool.length)] }); } | |
| } | |
| updatePackSelectionUI(); | |
| } | |
| function updatePackSelectionUI() { | |
| const listEl = document.getElementById('pack-item-list'); listEl.innerHTML = ''; | |
| document.getElementById('pack-name-display').innerText = `${activePack.size} ${activePack.type.toUpperCase()} PACK`; | |
| document.getElementById('pack-picks-display').innerText = `Picks Left: ${packPicksLeft}`; | |
| document.getElementById('pack-overlay').style.display = 'flex'; | |
| packSelectionItems.forEach((item, i) => { | |
| const div = document.createElement('div'); div.className = item.type === 'joker' ? 'joker-card' : 'planet-card'; div.dataset.idx = i; | |
| div.innerHTML = `<span class="text-3xl">${item.emoji}</span><span class="text-[9px] font-black uppercase text-center mt-2">${item.name}</span>`; | |
| div.onpointerdown = (e) => onEntityDown(e, 'pack-item-list', 'pack_selection'); | |
| listEl.appendChild(div); | |
| }); | |
| updateSpacings('pack-item-list'); | |
| } | |
| function closePack() { activePack = null; document.getElementById('pack-overlay').style.display = 'none'; renderShop(); } | |
| function onEntityDown(e, containerId, source) { | |
| if (isBusy) return; | |
| dTarget = e.currentTarget; dStart = { x: e.clientX, y: e.clientY }; dActive = false; | |
| const onMove = (me) => { | |
| const dist = Math.hypot(me.clientX - dStart.x, me.clientY - dStart.y); | |
| if (!dActive && dist > 8) { | |
| dActive = true; dPlaceholder = dTarget.cloneNode(true); dPlaceholder.classList.add('placeholder'); | |
| dTarget.classList.add('dragging'); dTarget.parentNode.insertBefore(dPlaceholder, dTarget); | |
| document.body.appendChild(dTarget); | |
| } | |
| if (dActive) { | |
| dTarget.style.left = me.clientX + 'px'; dTarget.style.top = me.clientY + 'px'; | |
| const listId = (source === 'hand') ? 'hand-list' : 'joker-inventory-list'; | |
| const list = document.getElementById(listId); | |
| const after = getAfterElement(list, me.clientX); | |
| if (after) list.insertBefore(dPlaceholder, after); else list.appendChild(dPlaceholder); | |
| updateSpacings(listId); | |
| } | |
| }; | |
| const onUp = (ue) => { | |
| window.removeEventListener('pointermove', onMove); window.removeEventListener('pointerup', onUp); | |
| if (!dActive) { | |
| const idx = parseInt(dTarget.dataset.idx); | |
| if (source === 'hand') toggleSelection(dTarget); | |
| else if (source === 'shop_pack') purchasePack(idx); | |
| else if (source === 'inventory') inspectEntity(idx, 'inventory'); | |
| else if (source === 'shop_joker') inspectEntity(idx, 'shop_joker'); | |
| else if (source === 'pack_selection') { | |
| const item = packSelectionItems[idx]; | |
| if (item.type === 'planet') { handLevels[item.name]++; packPicksLeft--; if (packPicksLeft <= 0) closePack(); else { packSelectionItems.splice(idx, 1); updatePackSelectionUI(); } } | |
| else inspectEntity(idx, 'pack_selection'); | |
| } | |
| } else { | |
| const invRect = document.getElementById('joker-inventory-list').getBoundingClientRect(); | |
| const droppedInInv = ue.clientY < invRect.bottom + 50 && ue.clientY > invRect.top - 50; | |
| const idx = parseInt(dTarget.dataset.idx); | |
| if (source === 'shop_joker' && droppedInInv && inventory.length < 5) { | |
| const data = shopJokers[idx]; if (money >= data.price) { money -= data.price; inventory.push(data); shopJokers.splice(idx, 1); deselectEverything(); } | |
| } else if (source === 'pack_selection' && droppedInInv && inventory.length < 5) { | |
| const data = packSelectionItems[idx]; if (data.type === 'joker') { inventory.push(data); packPicksLeft--; if (packPicksLeft <= 0) closePack(); else { packSelectionItems.splice(idx, 1); updatePackSelectionUI(); } } | |
| } else if (source === 'inventory' || source === 'hand') { | |
| const listId = (source === 'hand') ? 'hand-list' : 'joker-inventory-list'; | |
| const list = document.getElementById(listId); list.replaceChild(dTarget, dPlaceholder); | |
| dTarget.classList.remove('dragging'); dTarget.style.cssText = ""; | |
| if (source === 'inventory') inventory = [...list.querySelectorAll('.joker-card')].map(el => inventory[el.dataset.idx]); | |
| if (source === 'hand') [...list.querySelectorAll('.card')].forEach((c, i) => c.dataset.visualIndex = i); | |
| } | |
| dTarget?.remove(); dPlaceholder?.remove(); | |
| } | |
| dTarget = null; dPlaceholder = null; renderInventory(); renderShop(); updateUI(); | |
| }; | |
| window.addEventListener('pointermove', onMove); window.addEventListener('pointerup', onUp); | |
| } | |
| function getAfterElement(list, x) { | |
| const cs = [...list.querySelectorAll('.card:not(.dragging):not(.placeholder), .joker-card:not(.dragging):not(.placeholder)')]; | |
| return cs.reduce((closest, child) => { | |
| const box = child.getBoundingClientRect(); | |
| const offset = x - (box.left + box.width / 2); | |
| if (offset < 0 && offset > closest.offset) return { offset, element: child }; else return closest; | |
| }, { offset: Number.NEGATIVE_INFINITY }).element; | |
| } | |
| function renderInventory() { | |
| const cont = document.getElementById('joker-inventory-list'); cont.innerHTML = ''; | |
| inventory.forEach((j, i) => { | |
| const div = document.createElement('div'); div.className = 'joker-card' + (inspectedIdx === i && inspectedSource === 'inventory' ? ' inspected' : ''); div.dataset.idx = i; | |
| div.innerHTML = `<span class="text-3xl">${j.emoji}</span><span class="text-[9px] font-black uppercase text-center mt-2">${j.name}</span>`; | |
| div.onpointerdown = (e) => onEntityDown(e, 'joker-inventory-list', 'inventory'); | |
| cont.appendChild(div); | |
| }); | |
| updateSpacings('joker-inventory-list'); | |
| } | |
| function inspectEntity(idx, source) { | |
| let data = source === 'inventory' ? inventory[idx] : (source === 'shop_joker' ? shopJokers[idx] : packSelectionItems[idx]); | |
| inspectedIdx = idx; inspectedSource = source; | |
| document.getElementById('inspect-name').innerText = data.name; | |
| document.getElementById('inspect-desc').innerText = data.desc || `Power up ${data.name}.`; | |
| const sBtn = document.getElementById('sell-btn'); | |
| if (source === 'inventory') { const pr = Math.floor(data.price/2); sBtn.innerText = `Sell ($${pr})`; sBtn.onclick = () => { money += pr; inventory.splice(idx, 1); deselectEverything(); updateUI(); }; sBtn.classList.remove('hidden'); } else sBtn.classList.add('hidden'); | |
| document.getElementById('inspect-panel').style.display = 'block'; | |
| } | |
| function deselectEverything() { inspectedIdx = -1; inspectedSource = null; document.getElementById('inspect-panel').style.display = 'none'; renderInventory(); renderShop(); } | |
| function handleGlobalClick(e) { if (!e.target.closest('.joker-card') && !e.target.closest('.planet-card') && !e.target.closest('.pack-card') && !e.target.closest('#inspect-panel') && !e.target.closest('.info-btn')) deselectEverything(); } | |
| function toggleSelection(el) { if (el.classList.contains('selected')) el.classList.remove('selected'); else if (document.querySelectorAll('#hand-list .selected').length < 5) el.classList.add('selected'); updateUI(); } | |
| function applySort() { const list = document.getElementById('hand-list'); const items = [...list.querySelectorAll('.card:not(.dragging):not(.fly-away)')]; items.sort((a,b) => { const rA = parseInt(a.dataset.rankSort), rB = parseInt(b.dataset.rankSort), sA = parseInt(a.dataset.suitSort), sB = parseInt(b.dataset.suitSort); return sortMode === 'rank' ? (rA !== rB ? rB - rA : sB - sA) : (sA !== sB ? sB - sA : rB - rA); }); items.forEach((item, i) => item.dataset.visualIndex = i); updateSpacings('hand-list'); } | |
| function toggleSort() { sortMode = (sortMode === 'rank' ? 'suit' : 'rank'); document.getElementById('sort-toggle-btn').innerText = `Sort By ${sortMode === 'rank' ? 'Suit' : 'Rank'}`; applySort(); } | |
| function discardSelected() { if (isBusy || discards <= 0) return; const sel = [...document.querySelectorAll('#hand-list .selected')]; if (!sel.length) return; discards--; inventory.forEach(j => { if (j.trigger === 'discard_cash') money += 1; }); sel.forEach(c => { c.classList.add('fly-away'); setTimeout(()=>c.remove(), 600); }); setTimeout(drawToFull, 300); updateUI(); } | |
| function showWin() { let jB = 0; inventory.forEach(j => { if (j.trigger === 'win') jB += 3; }); const base = 4, interest = Math.min(Math.floor(money / 5), 5); const totalGained = base + plays + jB + interest; money += totalGained; document.getElementById('win-bonus').innerText = `$${plays}`; document.getElementById('joker-gold-bonus').innerText = `$${jB}`; document.getElementById('interest-bonus').innerText = `$${interest}`; document.getElementById('win-total').innerText = `$${totalGained}`; document.getElementById('win-overlay').style.display = 'flex'; updateUI(); } | |
| function goToShop() { | |
| document.getElementById('win-overlay').style.display = 'none'; | |
| document.getElementById('shop-overlay').style.display = 'flex'; | |
| rerollShop(true); | |
| } | |
| function startNextRound() { | |
| blindIndex++; if (blindIndex > 2) { blindIndex = 0; anteIndex++; } | |
| if (anteIndex >= 8) { document.getElementById('victory-overlay').style.display = 'flex'; return; } | |
| document.getElementById('shop-overlay').style.display = 'none'; document.getElementById('hand-list').innerHTML = ''; | |
| totalScore = 0; plays = 4; discards = 3; initDecks(); drawToFull(); updateUI(); | |
| } | |
| function resetGame() { location.reload(); } | |
| window.onload = () => { initDecks(); drawToFull(); renderInventory(); updateUI(); }; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment