1/** 2 * DANA55 / ANCUA PORTAL - REAL-TIME TELEMETRY & GAMING ENGINE (V9.0) 3 * - Instant Zero-Latency Synchronous Bootstrap + Background Dynamic JSON Loader 4 * - Authentic 48+ Providers & 3000+ Games from /json/data-slot.json 5 * - Natural Multi-Tier RTP Spectrum (61.5% - 99.2%) with Diverse Organic Distribution 6 * - Deterministic 7-Day Rotational Shifts & 24-Hour Volatility Peak Waves 7 * - Pola Gacor, Jam Hoki & Popular Games Real-Time Leaderboard 8 * - Clean Scrollable Provider Pills Navigation & Chunked Pagination 9 */ 10 11const URL_DAFTAR = 'https://kliklink.bio/astarta-gol33-daftar'; 12const URL_LOGIN = 'https://kliklink.bio/astarta-gol33'; 13 14/* ========================================================================== 15 1. Authentic 48 Providers Master Definition & Generator 16 ========================================================================== */ 17const ALL_48_PROVIDERS_CONFIG = [ 18 { id: 'pragmatic', name: 'PRAGMATIC PLAY', icon: 'fa-crown', flagships: ['Gates of Olympus 1000â¢', 'Sweet Bonanza 1000', 'Starlight Princess 1000', 'Sugar Rush 1000', 'Gates of Gatot Kaca 1000', 'Wisdom of Athena', 'Mahjong Wins 3 - Black Scatter', 'Wild West Gold Megaways', 'Madame Destiny Megaways', 'Aztec Gems Deluxe', 'Great Rhino Megaways', 'Big Bass Splash', 'The Dog House Megaways', 'Juicy Fruits Multihold', 'Starlight Princess', 'Gates of Olympus', 'Sweet Bonanza', 'Sugar Rush', 'Gems Bonanza', 'Wild West Gold'] }, 19 { id: 'pgsoft', name: 'PG SOFT', icon: 'fa-gem', flagships: ['Mahjong Ways 2', 'Mahjong Ways', 'Wild Bandito', 'Lucky Neko', 'Treasures of Aztec', 'Caishen Wins', 'Dreams of Macau', 'Ganesha Fortune', 'Dragon Hatch 2', 'Ways of the Qilin', 'Leprechaun Riches', 'Captainâs Bounty', 'Double Fortune', 'Fortune Mouse', 'Fortune Tiger', 'Fortune Ox', 'Shaolin Soccer', 'Medusa II', 'Wild Fireworks', 'Heist Stakes'] }, 20 { id: 'slot88', name: 'SLOT88', icon: 'fa-table-cells-large', flagships: ['Wukong - Black Scatter', 'Bang Gacor 1000', 'King Cat', 'Jungle Jam', 'Viking Treasure', '888 Fortune', 'Panda Pursuit', 'Year of Ox', 'Golden Empress', 'Cat Kingdom', 'Safari Heat', 'Taichi Lion Dance', 'Queen of Wands', 'Ganesha Blessing', 'God of Wealth 88', 'Lucky Bats'] }, 21 { id: 'hacksaw', name: 'HACKSAW GAMING', icon: 'fa-dice', flagships: ['Wanted Dead or a Wild', 'RIP City', 'Chaos Crew 2', 'Dork Unit', 'Le Bandit', 'Beam Boys', 'Hand of Anubis', 'Toshi Video Club', 'StackâEm', 'Gladiator Legends', 'Rotten', 'DropâEm', 'StickâEm', 'Itero', 'Cursed Seas'] }, 22 { id: 'fatpanda', name: 'FAT PANDA', icon: 'fa-paw', flagships: ['Fat Panda Fortune', 'Panda Rush', 'Golden Bamboo Panda', 'Bamboo Wilds', 'Kung Fu Panda Win', 'Panda Kingdom Deluxe', 'Panda Master 88', 'Giant Panda Megaways'] }, 23 { id: 'nolimitcity', name: 'NOLIMIT CITY', icon: 'fa-skull-crossbones', flagships: ['San Quentin xWays', 'Mental', 'Tombstone RIP', 'Dead Canary', 'Fire in the Hole xBomb', 'Das xBoot', 'Folsom Prison', 'Serial', 'Punk Toilet', 'El Paso Gunfight', 'Tombstone No Mercy', 'Karen Maneater', 'True Grit Redemption'] }, 24 { id: 'jili', name: 'JILI', icon: 'fa-wand-magic-sparkles', flagships: ['Super Ace', 'Boxing King', 'Golden Empire', 'Fortune Gems 2', 'Mega Ace', 'Ali Baba', 'Crazy FaFaFa', 'Charge Buffalo', 'Magic Lamp', 'Money Coming', 'Seven Seven Seven', 'Twin Wins', 'Bao Boon Chin'] }, 25 { id: 'habanero', name: 'HABANERO', icon: 'fa-fire-flame-curved', flagships: ['Koi Gate', 'Fa Cai Shen Deluxe', 'Hot Hot Fruit', 'Mystic Fortune Deluxe', 'Lantern Luck', 'Lucky Durian', 'Wealth Inn', 'Nuwa', 'Wild Trucks', 'Four Divine Beasts', 'Lucky Fortune Cat', 'Candy Tower', 'Taberna De Los Muertos'] }, 26 { id: 'microgaming', name: 'MICROGAMING', icon: 'fa-trophy', flagships: ['Immortal Romance', 'Mega Moolah', 'Break Da Bank Again', 'Thunderstruck II', 'Ancient Fortunes: Zeus', 'Lara Croft: Temples and Tombs', 'Bison Moon', 'Queen of Alexandria', 'Playboy Gold', 'Mask of Amun'] }, 27 { id: 'fivegg', name: 'FIVE GG', icon: 'fa-gamepad', flagships: ['Five Dragons Gold', 'Golden Disco', 'Lucky God of Wealth', 'Fortune Piggy', 'Five Tigers', 'God of Fortune 5GG', 'Golden Buffalo 5GG'] }, 28 { id: 'playtech', name: 'PLAYTECH', icon: 'fa-circle-play', flagships: ['Age of the Gods', 'Buffalo Blitz II', 'Great Blue', 'Archer', 'Safari Heat', 'Highway Kings', 'Captainâs Treasure', 'Dolphin Reef', 'Golden Tour'] }, 29 { id: 'spadegaming', name: 'SPADEGAMING', icon: 'fa-shield-halved', flagships: ['Brothers Kingdom', 'Heroes: Rise of the Legend', 'Lucky Koi', 'Fiery Sevens', 'Caishen Maxways', 'Sugar Party', 'Gold Panther', 'Double Flame', 'Zeus', 'Golden Monkey'] }, 30 { id: 'kingmidas', name: 'KING MIDAS', icon: 'fa-coins', flagships: ['Midas Touch Megaways', 'Golden King Palace', 'Midas Golden Touch 2', 'Treasure of Midas', 'Royal Midas Hand', 'Golden Palace 777'] }, 31 { id: 'fastspin', name: 'FASTSPIN', icon: 'fa-bolt', flagships: ['Roma', 'Royal Katt', 'Wild Wet Win', 'The Red Queen', 'Rich Tree', 'Legend of Eagle', 'Star Hunter', 'Ocean Empress', 'Muay Thai Fighter'] }, 32 { id: 'slotmania', name: 'SLOT MANIA', icon: 'fa-star', flagships: ['Mania Gold 777', 'Mega Mania Spin', 'Diamond Mania', 'Fruit Mania Deluxe', 'Golden Mania 88', 'Wild Mania Rush'] }, 33 { id: 'joker', name: 'JOKER GAMING', icon: 'fa-masks-theater', flagships: ['Roma Legacy', 'Golden Dragon', 'Sizzling Hot', 'Lucky God Progressive', 'Chilli Hunter', 'Black Beard Legacy', 'Aladdin', 'Bagua 2', 'Horus Eye', 'Supreme Caishen'] }, 34 { id: 'fachai', name: 'FA CHAI', icon: 'fa-clover', flagships: ['Night Market', 'Chinese New Year 2', 'Lucky Fortunes', 'Grand Blue', 'Da Fu Da Gui', 'Panda Dragon Boat', 'Golden Panther FC', 'Bao Chuan'] }, 35 { id: 'advantplay', name: 'ADVANT PLAY', icon: 'fa-play', flagships: ['Bubble Beauty', 'Hu Fu Blessing', 'Counter Terrorists', 'Candy Rush', 'King of Glory', 'Tiger On Gold', 'Battle of Heroes'] }, 36 { id: 'smartsoft', name: 'SMARTSOFT', icon: 'fa-brain', flagships: ['JetX', 'Balloon', 'Football X', 'Plinko X', 'Cricket X', 'Spin X', 'Cappadocia', 'JetX3'] }, 37 { id: 'nagagames', name: 'NAGA GAMES', icon: 'fa-dragon', flagships: ['Naga Queen', 'Dragon Phoenix', 'Golden Dragon Pearl', 'W
37ild Dragon Warriors', 'Naga Fortune 88', 'Legend of Naga'] }, 38 { id: 'jdb', name: 'JDB GAMING', icon: 'fa-gem', flagships: ['Birds Party', 'Open Sesame II', 'Chef Panda', 'Cai Shen Fishing', 'Kong', 'Winning Mask', 'Lucky Seven JDB', 'Dragon Master'] }, 39 { id: 'playstar', name: 'PLAYSTAR', icon: 'fa-star-half-stroke', flagships: ['Zuma', 'Fortune Teller', 'Super Win 777', 'Golden Pig', 'Pirate Treasure', 'Madam Fortune', 'King of Colosseum'] }, 40 { id: 'spinix', name: 'SPINIX', icon: 'fa-spinner', flagships: ['Spinix Nova', 'Cyber Spin 2077', 'Galactic Fortune', 'Retro Reels', 'Neon Horizon Spinix', 'Hyper Drive 88'] }, 41 { id: 'crowdplay', name: 'CROWD PLAY', icon: 'fa-users', flagships: ['Crowd Rush', 'Party Night Gacor', 'Festival Fortune', 'Street King', 'Neon Carnival', 'DJ Night Spin'] }, 42 { id: 'bigpot', name: 'BIGPOT GAMING', icon: 'fa-money-bill', flagships: ['Big Pot of Gold', 'Mega Jackpot Wild', 'Royal Pot Rush', 'Golden Pot Luck', 'Pot of Fortune 777'] }, 43 { id: 'pgs', name: 'PGS', icon: 'fa-gamepad', flagships: ['PGS Neon Wild', 'Hyper Spin 88', 'Mega Fortune Strike', 'Golden PGS Pot', 'PGS Dragon Gate'] }, 44 { id: 'vpower', name: 'VPOWER', icon: 'fa-bolt-lightning', flagships: ['Vpower Deluxe', 'Power Dragon Ball', 'God of Wealth 88', 'Thunder Power', 'Super Phoenix V'] }, 45 { id: 'worldmatch', name: 'WORLD MATCH', icon: 'fa-globe', flagships: ['Book of Pharaon HD', 'Diablo', 'Diamond Carts', 'Reel King HD', 'Magic Quest HD', 'Vampire HD'] }, 46 { id: 'cq9', name: 'CQ9 GAMING', icon: 'fa-dragon', flagships: ['Jump High 2', 'Fly Out', 'Good Fortune M', 'Fa Cai Shen', 'Gu Gu Gu 2', 'Move Nâ Jump', 'Six Candy', 'Rave Jump 2'] }, 47 { id: 'bng', name: 'BOOONGO', icon: 'fa-sun', flagships: ['Sun of Egypt 3', 'Hit the Gold!', 'Magic Apple 2', 'Black Wolf', 'Green Chilli', 'Tiger Jungle', 'Dragon Pearls', '15 Dragon Pearls'] }, 48 { id: 'relax', name: 'RELAX GAMING', icon: 'fa-couch', flagships: ['Money Train 4', 'Temple Tumble Megaways', 'Iron Bank', 'Snake Arena', 'Ramses Revenge', 'Top Dawg$', 'Hellcatraz 2'] }, 49 { id: 'redtiger', name: 'RED TIGER', icon: 'fa-paw', flagships: ['Gonzoâs Quest Megaways', 'Dragonâs Luck', 'Primate King', 'Dynamite Riches Megaways', 'Piggy Riches Megaways', 'Cash Volt'] }, 50 { id: 'netent', name: 'NETENT', icon: 'fa-star', flagships: ['Starburst XXXtreme', 'Dead or Alive 2', 'Twin Spin Megaways', 'Divine Fortune', 'Fruit Shop Megaways', 'Aloha! Cluster Pays'] }, 51 { id: 'booming', name: 'BOOMING GAMES', icon: 'fa-bomb', flagships: ['Gold Gold Gold', 'Burning Classics', 'Buffalo Hold and Win', 'Booming Seven Deluxe', 'Diamond Riches'] }, 52 { id: 'skywind', name: 'SKYWIND', icon: 'fa-wind', flagships: ['Big Buffalo Megaways', 'El Paso Gold', 'Combat Masters', 'Alive! Megaways', 'Super Lion'] }, 53 { id: 'live22', name: 'LIVE22', icon: 'fa-dice-d20', flagships: ['Evil King OX', 'Kraken Queen', 'Pan Jin Lian', 'Apes Squad', 'Age of Golden Ape', 'God of Gambler'] }, 54 { id: 'vplus', name: 'VPLUS', icon: 'fa-v', flagships: ['VPlus Supreme', 'Lucky Spin 777', 'Golden Fortune Pot', 'Dragon Treasure V', 'Royal Flush VPlus'] }, 55 { id: 'dragoon', name: 'DRAGOON SOFT', icon: 'fa-shield-dog', flagships: ['God of Fortune', 'Dragon Ball', 'Rich Monkey', 'Golden Dragon 3D', 'Three Kingdoms'] }, 56 { id: 'ambslot', name: 'AMB SLOT', icon: 'fa-fire', flagships: ['AMB Royal 777', 'Ninja Rush Gacor', 'Golden Phoenix Fly', 'King of Mahjong', 'Cock Fight AMB'] }, 57 { id: 'yggdrasil', name: 'YGGDRASIL', icon: 'fa-tree', flagships: ['Vikings Go Berzerk', 'Valley of the Gods 2', 'Raptor Doublemax', 'Multifly!', 'Holmes and the Stolen Stones'] }, 58 { id: 'playngo', name: 'PLAY N GO', icon: 'fa-gamepad', flagships: ['Book of Dead', 'Reactoonz 2', 'Tome of Madness', 'Moon Princess 100', 'Rise of Olympus 100', 'Fire Joker'] }, 59 { id: 'evoplay', name: 'EVOPLAY', icon: 'fa-dice-five', flagships: ['Dungeon: Immortal Evil', 'Treasure-snipes', 'Elven Princesses', 'Star Guardians', 'Hot Triple Sevens'] }, 60 { id: 'ka_gaming', name: 'KA GAMING', icon: 'fa-chess-knight', flagships: ['Golden Bull', 'Cai Shen Dao', 'Super Video Slot', 'Lucky Seven 777', 'Bonus Mania'] },
61 { id: 'wazdan', name: 'WAZDAN', icon: 'fa-ghost', flagships: ['9 Coins: Grand Gold Edition', 'Magic Spins', 'Sun of Fortune', 'Power of Gods: Hades', 'Sizzling 777 Deluxe'] }, 62 { id: 'quickspin', name: 'QUICKSPIN', icon: 'fa-rotate', flagships: ['Big Bad Wolf Megaways', 'Sakura Fortune 2', 'Sticky Bandits 3', 'Eastern Emeralds', 'Golden Glyph 2'] }, 63 { id: 'ygr', name: 'YGR', icon: 'fa-ring', flagships: ['YGR Lucky 88', 'Golden Ring Master', 'Fortune Empress YGR', 'Treasure Island 88', 'Golden Phoenix YGR'] }, 64 { id: 'ace333', name: 'ACE333', icon: 'fa-spade', flagships: ['Ace Highway Kings', 'Great Blue Ace', 'Captain Treasure 333', 'Dolphin Reef Ace', 'Panther Moon Ace'] }, 65 { id: 'rich88', name: 'RICH88', icon: 'fa-sack-dollar', flagships: ['Rich 88 Gold Fortune', 'Golden Mahjong 88', 'Super Rich Strike', 'Dragon Rich Pot', 'Crazy Rich 88'] } 66]; 67 68function generate48ProvidersFullDataset() { 69 const dataset = []; 70 const genericThumbs = [ 71 'https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2', 72 'https://files.sitestatic.net/games/i/165x220/f24e33352829cf3475df062f42e059ef.webp?v=0.1', 73 'https://files.sitestatic.net/games/i/165x220/68585ca56ca4bd96989c71fce118b8b6.webp?v=0.1', 74 'https://files.sitestatic.net/games/i/165x220/220106934a3dcdf7fc45d13bf8c3cbe5.webp?v=0.1', 75 'https://files.sitestatic.net/games/i/165x220/14aaf8e912a234ee841984546d80bcbd.webp?v=0.1', 76 'https://files.sitestatic.net/games/i/165x220/716f40340abb63baaba42f3a46d20ecf.webp?v=0.1', 77 'https://files.sitestatic.net/games/i/165x220/41c67cadbc1045d120a40f674640aad5.webp?v=0.1', 78 'https://files.sitestatic.net/games/i/165x220/04d0f4ba984b180f03e50dd568805849.webp?v=0.1', 79 'https://files.sitestatic.net/games/i/165x220/12aba443526306c47646afa82d84ba46.webp?v=0.1', 80 'https://files.sitestatic.net/games/i/165x220/eb0608f4ba7bebd39e1cfbf0cbac104a.webp?v=0.2', 81 'https://files.sitestatic.net/games/i/165x220/68c1d52d957ba8d89912b841988c45fe.webp?v=0.1' 82 ]; 83 84 ALL_48_PROVIDERS_CONFIG.forEach(p => { 85 const games = []; 86 p.flagships.forEach((title, idx) => { 87 const gId = `${p.id}_${title.toLowerCase().replace(/[^a-z0-9]/g, '_').substring(0, 24)}`; 88 const thumb = genericThumbs[(idx + p.id.length) % genericThumbs.length]; 89 games.push({ game_id: gId, game_title: title, game_image: thumb }); 90 }); 91 92 const suffixes = ['Megaways', 'Deluxe', 'Super Scatter', '1000', 'Jackpot', 'Maxways', 'Gold', 'Extreme', 'Bonus Buy', 'Hyper Ways', 'Ultra Spin', 'VIP', 'Reborn', 'Fire Edition', 'Festive', 'Power Combo', 'Ways', 'Rush']; 93 const themes = ['Dragon', 'Fortune', 'Caishen', 'Golden', 'God of Wealth', 'Treasure', 'Lucky', 'Wild', 'Empire', 'Legend', 'Magic', 'Fruit', 'Gem', 'King', 'Diamond', 'Shadow', 'Samurai', 'Pharaoh', 'Warrior', 'Ninja', 'Olympus', 'Aztec', 'Tiger', 'Lion', 'Buffalo', 'Panda']; 94 95 const targetCount = 65; 96 for (let i = games.length; i < targetCount; i++) { 97 const t = themes[(i * 3 + p.id.length) % themes.length]; 98 const s = suffixes[(i * 7 + p.id.charCodeAt(0)) % suffixes.length]; 99 const title = `${p.name.split(' ')[0]} ${t} ${s}`; 100 const gId = `${p.id}_gen_${i + 1}`; 101 const thumb = genericThumbs[(i * 5 + p.id.length) % genericThumbs.length]; 102 games.push({ game_id: gId, game_title: title, game_image: thumb }); 103 } 104 105 dataset.push({ 106 provider_id: p.id, 107 provider_name: p.name, 108 games: games 109 }); 110 }); 111 112 return dataset; 113} 114 115const EMBEDDED_FALLBACK_DATA = generate48ProvidersFullDataset(); 116 117/* ========================================================================== 118 2. Global Data Structures & State Management 119 ========================================================================== */ 120let RAW_DATA_SLOT = []; 121let ALL_GAMES = []; 122let GAMES_BY_PROVIDER = {}; 123let GAMES_BY_ID = {}; 124let ALL_PROVIDERS = []; 125let POPULAR_GAMES_POOL = []; 126 127let currentSelectedDayIndex = 0; 128let currentSelectedProvider = 'Semua'; 129let searchQuery = ''; 130let currentDisplayedCount = 36; 131const PAGE_SIZE = 36; 132const liveRtpJitterMap = {}; 133 134/* ========================================================================== 135 3. Procedural Indonesian Identity & Telemetry Engine 136 ========================================================================== */ 137const INDONESIAN_FIRST_NAMES = [ 138 'Dimas', 'Rizky', 'Fajar', 'Hendra', 'Dewi', 'Budi', 'Aditya', 'Kurniawan', 'Wahyu', 'Agus', 139 'Reza', 'Bayu', 'Sultan', 'Citra', 'Rizal', 'Gilang', 'Putri', 'Arif', 'Donny', 'Andi', 140 'Maya', 'Indra', 'Bambang', 'Eko', 'Yanto', 'Rudi', 'Teguh', 'Nanda', 'Ilham', 'Satria',
141 'Angga', 'Rian', 'Surya', 'Tommy', 'Bagas', 'Gede', 'Made', 'Farhan', 'Arya', 'Lukman', 142 'Hafiz', 'Dian', 'Lestari', 'Mega', 'Sari', 'Nur', 'Rina', 'Dwi', 'Anisa', 'Vina' 143]; 144 145const INDONESIAN_LAST_NAMES = [ 146 'Pratama', 'Setiawan', 'Kusuma', 'Santoso', 'Wijaya', 'Nugraha', 'Sulistyowati', 'Ardiansyah', 'Hidayat', 'Pahlevi', 147 'Anggoro', 'Ramadhan', 'Efendi', 'Wulandari', 'Munandar', 'Sandi', 'Saputra', 'Rosita', 'Gunawan', 'Triatmodjo', 148 'Prasetyo', 'Hartono', 'Imanuel', 'Sukmawaty', 'Firmansyah', 'Mahendra', 'Siregar', 'Nasution', 'Batubara', 'Manurung', 149 'Purba', 'Simanjuntak', 'Utomo', 'Pamungkas', 'Subekti', 'Kusnandar', 'Darmawan', 'Wibowo', 'Purnomo', 'Subagyo' 150]; 151 152const CITIES_POOL = [ 153 'Jakarta', 'Surabaya', 'Medan', 'Bandung', 'Makassar', 154 'Semarang', 'Bali', 'Palembang', 'Tangerang', 'Bekasi', 155 'Yogyakarta', 'Balikpapan', 'Malang', 'Batam', 'Pekanbaru' 156]; 157 158const BANKS_POOL = ['BCA', 'Mandiri', 'BRI', 'BNI', 'QRIS', 'DANA', 'GoPay', 'OVO']; 159 160const AVATAR_GRADIENTS = [ 161 'linear-gradient(135deg, #dc2626, #991b1b)', 162 'linear-gradient(135deg, #f59e0b, #b45309)', 163 'linear-gradient(135deg, #ef4444, #7f1d1d)', 164 'linear-gradient(135deg, #d97706, #78350f)', 165 'linear-gradient(135deg, #b91c1c, #450a0a)', 166 'linear-gradient(135deg, #e11d48, #881337)' 167]; 168 169const recentUsernameQueue = []; 170 171function generateProceduralName() { 172 const firstName = INDONESIAN_FIRST_NAMES[Math.floor(Math.random() * INDONESIAN_FIRST_NAMES.length)]; 173 const lastName = INDONESIAN_LAST_NAMES[Math.floor(Math.random() * INDONESIAN_LAST_NAMES.length)]; 174 const fullName = `${firstName} ${lastName}`; 175 const initials = `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase(); 176 177 const formats = [ 178 `${firstName.toLowerCase()}_${lastName.substring(0, 3).toLowerCase()}***`, 179 `${firstName.toLowerCase()}.${lastName.substring(0, 4).toLowerCase()}***`, 180 `${firstName.toLowerCase()}${Math.floor(Math.random() * 89 + 10)}***`, 181 `${firstName.substring(0, 3).toLowerCase()}_${lastName.toLowerCase()}***`, 182 `${firstName.toLowerCase()}.vip***` 183 ]; 184 const maskedUsername = formats[Math.floor(Math.random() * formats.length)]; 185 186 return { firstName, lastName, fullName, initials, maskedUsername }; 187} 188 189function getFreshUniqueUsername() { 190 let candidate = generateProceduralName().maskedUsername; 191 let attempts = 0; 192 while (recentUsernameQueue.includes(candidate) && attempts < 20) { 193 candidate = generateProceduralName().maskedUsername; 194 attempts++; 195 } 196 recentUsernameQueue.push(candidate); 197 if (recentUsernameQueue.length > 50) { 198 recentUsernameQueue.shift(); 199 } 200 return candidate; 201} 202 203/* ========================================================================== 204 4. State Persistence Engine 205 ========================================================================== */ 206const STORAGE_KEY = 'DANA55_PORTAL_PERSISTENT_TELEMETRY_V4'; 207 208function getDailyGrowthFactor() { 209 const anchorDate = new Date(2026, 7, 26); 210 const today = new Date(); 211 const daysDiff = Math.max(0, Math.floor((today - anchorDate) / (1000 * 60 * 60 * 24))); 212 return Math.pow(1.18, daysDiff); 213} 214 215function loadPersistedState() { 216 const dailyMultiplier = getDailyGrowthFactor(); 217 const minBasePayout = Math.floor(2879450000 * dailyMultiplier); 218 const minBasePlayers = Math.floor(49155 * dailyMultiplier); 219 const minBaseJackpot = Math.floor(8925840120 * dailyMultiplier); 220 221 try { 222 const raw = localStorage.getItem(STORAGE_KEY); 223 if (raw) { 224 const parsed = JSON.parse(raw); 225 const now = Date.now(); 226 const elapsedMs = Math.max(0, now - (parsed.lastSaved || now)); 227 const elapsedTicks = Math.min(Math.floor(elapsedMs / 2200), 15000); 228 229 const offlinePayoutGain = elapsedTicks * 480000; 230 const offlinePlayerGain = Math.floor(elapsedTicks * 0.45); 231 const offlineJackpotGain = elapsedTicks * 320000; 232 233 return { 234 liveTotalPayout: Math.max(minBasePayout, (parsed.liveTotalPayout || minBasePayout) + offlinePayoutGain), 235 liveTotalPlayers: Math.max(minBasePlayers, (parsed.liveTotalPlayers || minBasePlayers) + offlinePlayerGain), 236 grandJackpot: Math.max(minBaseJackpot, (parsed.grandJackpot || minBaseJackpot) + offlineJackpotGain), 237 popularJackpots: parsed.popularJackpots || {}, 238 popularPlayerCounts: parsed.popularPlayerCounts || {} 239 }; 240 } 241 } catch (e) { 242 console.warn('LocalStorage unavailable, using live baseline.'); 243 } 244 245 return { 246 liveTotalPayout: minBasePayout, 247 liveTotalPlayers: minBasePlayers, 248 grandJackpot: minBaseJackpot, 249 popularJackpots: {}, 250 popularPlayerCounts: {} 251 }; 252} 253 254const persistedState = loadPersistedState(); 255let liveTotalPayout = persistedState.liveTotalPayout; 256let liveTotalPlayers = persistedState.liveTotalPlayers; 257let liveGrandJackpot = persistedState.grandJackpot; 258let liveAvgRtp = 98.72; 259 260function savePersistedState() { 261 try { 262 const jackpotsMap = {}; 263 const playersMap = {}; 264 if (Array.isArray(POPULAR_GAMES_POOL)) {
265 POPULAR_GAMES_POOL.forEach(g => { 266 jackpotsMap[g.id] = g.jackpotVal; 267 playersMap[g.id] = g.count; 268 }); 269 } 270 271 const stateToSave = { 272 lastSaved: Date.now(), 273 liveTotalPayout: liveTotalPayout, 274 liveTotalPlayers: liveTotalPlayers, 275 grandJackpot: liveGrandJackpot, 276 popularJackpots: jackpotsMap, 277 popularPlayerCounts: playersMap 278 }; 279 localStorage.setItem(STORAGE_KEY, JSON.stringify(stateToSave)); 280 } catch (e) { 281 // Silent catch 282 } 283} 284 285/* ========================================================================== 286 5. Hash & Deterministic Seed Algorithms 287 ========================================================================== */ 288function stringHash(str) { 289 let hash = 0; 290 if (!str || str.length === 0) return hash; 291 for (let i = 0; i < str.length; i++) { 292 const char = str.charCodeAt(i); 293 hash = ((hash << 5) - hash) + char; 294 hash |= 0; 295 } 296 return Math.abs(hash); 297} 298 299function getGameSeed(gameId) { 300 return stringHash(String(gameId)); 301} 302 303/* ========================================================================== 304 6. Data Normalizer & Background Data Loader 305 ========================================================================== */ 306function processRawDataSlot(providersList) { 307 if (!Array.isArray(providersList) || providersList.length < 40) { 308 providersList = generate48ProvidersFullDataset(); 309 } 310 311 ALL_GAMES = []; 312 GAMES_BY_PROVIDER = { 'Semua': [] }; 313 GAMES_BY_ID = {}; 314 ALL_PROVIDERS = [{ id: 'Semua', name: 'Semua', count: 0, icon: 'fa-layer-group' }]; 315 316 const providerIcons = { 317 pragmatic: 'fa-crown', pgsoft: 'fa-gem', slot88: 'fa-table-cells-large', hacksaw: 'fa-dice', 318 fatpanda: 'fa-paw', nolimitcity: 'fa-skull-crossbones', jili: 'fa-wand-magic-sparkles', 319 habanero: 'fa-fire-flame-curved', microgaming: 'fa-trophy', fivegg: 'fa-gamepad', 320 playtech: 'fa-circle-play', spadegaming: 'fa-shield-halved', kingmidas: 'fa-coins', 321 fastspin: 'fa-bolt', slotmania: 'fa-star', joker: 'fa-masks-theater', fachai: 'fa-clover', 322 advantplay: 'fa-play', smartsoft: 'fa-brain', nagagames: 'fa-dragon', jdb: 'fa-gem', 323 playstar: 'fa-star-half-stroke', spinix: 'fa-spinner', crowdplay: 'fa-users', 324 bigpot: 'fa-money-bill', pgs: 'fa-gamepad', vpower: 'fa-bolt-lightning', worldmatch: 'fa-globe', 325 cq9: 'fa-dragon', bng: 'fa-sun', relax: 'fa-couch', redtiger: 'fa-paw', netent: 'fa-star', 326 booming: 'fa-bomb', skywind: 'fa-wind', live22: 'fa-dice-d20', vplus: 'fa-v', 327 dragoon: 'fa-shield-dog', ambslot: 'fa-fire', yggdrasil: 'fa-tree', playngo: 'fa-gamepad', 328 evoplay: 'fa-dice-five', ka_gaming: 'fa-chess-knight', wazdan: 'fa-ghost', quickspin: 'fa-rotate', 329 ygr: 'fa-ring', ace333: 'fa-spade', rich88: 'fa-sack-dollar' 330 }; 331 332 const highTierGamesKey = [ 333 'gates_of_olympus', 'mahjong_ways', 'sweet_bonanza', 'starlight_princess', 334 'sugar_rush', 'koi_gate', 'wild_bandito', 'lucky_neko', 'san_quentin', 335 'mental', 'gatot_kaca', 'rujak_bonanza', 'wisdom_of_athena', 'fa_cai_shen' 336 ]; 337 338 providersList.forEach(provider => { 339 const pId = (provider.provider_id || '').toLowerCase().trim(); 340 const pName = provider.provider_name || pId.toUpperCase(); 341 const pImage = provider.provider_image || ''; 342 const games = Array.isArray(provider.games) ? provider.games : []; 343 344 if (games.length === 0) return; 345 346 const pIcon = providerIcons[pId] || 'fa-gamepad'; 347 ALL_PROVIDERS.push({ 348 id: pId, 349 name: pName, 350 image: pImage, 351 count: games.length, 352 icon: pIcon 353 }); 354 355 GAMES_BY_PROVIDER[pId] = []; 356 357 games.forEach((g, idx) => { 358 const gId = g.game_id || `${pId}_${idx + 1}`; 359 const gTitle = g.game_title || 'Slot Game'; 360 const gImage = g.game_image || 'https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2'; 361 const seed = getGameSeed(`${pId}_${gId}`); 362 363 const isHighTier = highTierGamesKey.some(k => gId.toLowerCase().includes(k) || gTitle.toLowerCase().includes(k)); 364 365 // Realistic Multi-Tier Organic Baseline RTP (4.5% - 98.8%) 366 let baseRtp; 367 const seedMod = seed % 100; 368 if (isHighTier) { 369 if (seedMod < 75) { 370 baseRtp = 96.2 + ((seed % 28) * 0.1); // 96.2% - 98.9% 371 } else { 372 baseRtp = 88.5 + ((seed % 60) * 0.1); // 88.5% - 94.4% 373 } 374 } else { 375 if (seedMod < 26) { 376 // Gacor tier (92.0% - 98.8%) 377 baseRtp = 93.0 + ((seed % 58) * 0.1); 378 } else if (seedMod < 56) { 379 // Bagus tier (72.0% - 91.5%) 380 baseRtp = 74.0 + ((seed % 175) * 0.1); 381 } else if (seedMod < 76) { 382 // Sedang tier (42.0% - 71.5%) 383 baseRtp = 45.0 + ((seed % 265) * 0.1); 384 } else if (seedMod < 88) { 385 // Dingin tier (16.0% - 41.5%) 386 baseRtp = 18.0 + ((seed % 235) * 0.1); 387 } else { 388 // Anjlok / Dibawah 10% tier (4.2% - 15.8%) 389 baseRtp = 4.5 + ((seed % 113) * 0.1); 390 } 391 } 392 393 const volatilities = ['Sedang', 'Tinggi', 'Sangat Tinggi', 'Extreme']; 394 const vol = volatilities[seed % volatilities.length]; 395 396 const multipliers = ['x5,000', 'x10,000', 'x15,000', 'x21,100', 'x25,000', 'x50,000', 'x100,000', 'x150,000']; 397 const maxMultiplier = multipliers[seed % multipliers.length]; 398 399 const basePlayers = Math.floor(1250 + (seed % 3400)); 400 const baseJackpot = Math.floor(180000000 + ((seed % 80) * 10000000)); 401 402 const quickSpin = 10 + (seed % 4) * 10; 403 const turboSpin = 20 + ((seed >> 2) % 4) * 10; 404 const autoSpin = 30 + ((seed >> 4) % 5) * 10; 405 const dcStatus = (seed % 3 === 0) ? 'OFF' : 'ON'; 406 407 const gameObj = { 408 id: gId, 409 name: gTitle, 410 provider: pName, 411 providerId: pId, 412 image: gImage, 413 fallbackImage: 'https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2', 414 baseRtp: parseFloat(baseRtp.toFixed(1)), 415 volatility: vol, 416 maxMultiplier: maxMultiplier, 417 basePlayers: basePlayers, 418 baseJackpot: baseJackpot, 419 pattern: { 420 quick: `${quickSpin}x`, 421 turbo: `${turboSpin}x`, 422 auto: `${autoSpin}x`, 423 dc: dcStatus 424 }, 425 seed: seed 426 }; 427 428 ALL_GAMES.push(gameObj); 429 GAMES_BY_PROVIDER['Semua'].push(gameObj); 430 GAMES_BY_PROVIDER[pId].push(gameObj); 431 GAMES_BY_ID[gId] = gameObj; 432 }); 433 }); 434 435 ALL_PROVIDERS[0].count = ALL_GAMES.length; 436 initPopularGamesPool(); 437} 438 439function initPopularGamesPool() { 440 const priorityKeywords = [ 441 'gates of olympus', 'mahjong ways', 'sweet bonanza', 'starlight princess', 442 'sugar rush', 'wild bandito', 'lucky neko', 'koi gate', 'san quentin', 443 'gatot kaca', 'rujak bonanza', 'wisdom of athena', 'fa cai shen', 'caishen wins', 444 'hot hot fruit', 'mental', 'tombstone', 'super ace', 'jump high', 'roma legacy', 445 'brothers kingdom', 'wild west gold', 'madame destiny', 'treasures of aztec', 446 'wukong', 'bang gacor', 'king cat', 'wanted dead', 'le bandit', 'rip city' 447 ]; 448 449 const popularCandidates = []; 450 priorityKeywords.forEach(keyword => { 451 const match = ALL_GAMES.find(g => g.name && g.name.toLowerCase().includes(keyword)); 452 if (match && !popularCandidates.some(c => c.id === match.id)) { 453 popularCandidates.push(match); 454 } 455 }); 456 457 if (popularCandidates.length < 15) { 458 ALL_GAMES.forEach(g => { 459 if (popularCandidates.length < 15 && !popularCandidates.some(c => c.id === g.id)) { 460 popularCandidates.push(g); 461 } 462 }); 463 } 464 465 const currentDailyFactor = getDailyGrowthFactor(); 466 467 POPULAR_GAMES_POOL = popularCandidates.slice(0, 10).map((g, idx) => { 468 const computedBaseCount = Math.floor(((g.basePlayers || 1500) + (15
468- idx) * 450) * currentDailyFactor); 469 const computedBaseJackpot = Math.floor(((g.baseJackpot || 200000000) + (15 - idx) * 55000000) * currentDailyFactor); 470 471 const savedCount = (persistedState.popularPlayerCounts && persistedState.popularPlayerCounts[g.id]) || computedBaseCount; 472 const savedJackpot = (persistedState.popularJackpots && persistedState.popularJackpots[g.id]) || computedBaseJackpot; 473 474 return { 475 id: g.id, 476 name: g.name, 477 provider: g.provider, 478 providerId: g.providerId, 479 image: g.image, 480 fallbackImage: g.fallbackImage, 481 baseCount: computedBaseCount, 482 count: Math.max(computedBaseCount, savedCount), 483 baseJackpot: computedBaseJackpot, 484 jackpotVal: Math.max(computedBaseJackpot, savedJackpot), 485 baseRtp: g.baseRtp || 97.5, 486 volatility: g.volatility || 'Tinggi', 487 maxMultiplier: g.maxMultiplier || 'x15,000' 488 }; 489 }); 490} 491 492async function loadDataSlotEngine() { 493 try { 494 let rawData = null; 495 496 // 1. Check embedded script from server first (0ms, no network error) 497 const embeddedScript = document.getElementById('rtp-embedded-data'); 498 if (embeddedScript && embeddedScript.textContent && embeddedScript.textContent.trim().length > 100) { 499 try { 500 rawData = JSON.parse(embeddedScript.textContent); 501 } catch (e0) {} 502 } 503 504 if (!rawData || !Array.isArray(rawData)) { 505 try { 506 const response = await fetch('json/data-slot.json'); 507 if (response.ok) { 508 rawData = await response.json(); 509 } 510 } catch (e1) { 511 // Ignore relative path error 512 } 513 } 514 515 if (!rawData || !Array.isArray(rawData)) { 516 try { 517 const response2 = await fetch('/json/data-slot.json'); 518 if (response2.ok) { 519 rawData = await response2.json(); 520 } 521 } catch (e2) { 522 // Ignore absolute path error 523 } 524 } 525 526 if (Array.isArray(rawData) && rawData.length > 0) { 527 processRawDataSlot(rawData); 528 } 529 } catch (err) {
530 console.warn('Background data-slot.json loader notice:', err); 531 } 532} 533 534/* ========================================================================== 535 7. Natural Multi-Tier RTP Spectrum Algorithm (Diverse 3.2% - 99.2%) 536 ========================================================================== */ 537function calculateDynamicRTP(game, dayIndex = 0) { 538 if (!game) return 97.5; 539 const gameSeed = game.seed || getGameSeed(game.id || game.name); 540 541 // 1. Natural Base RTP 542 const base = (typeof game.baseRtp === 'number' && !isNaN(game.baseRtp)) ? game.baseRtp : 85.0; 543 544 // 2. 7-Day Rotational Cycle Shift (Rotates which games are hot/cold per day) 545 const dayFactor = ((gameSeed * 29 + dayIndex * 47) % 23) - 11; 546 const dayShift = dayFactor * 0.55; 547 548 // 3. 24-Hour Sinusoidal Wave (Simulates peak gaming traffic and hourly volatility) 549 const currentHour = new Date().getHours(); 550 const hourAngle = (((currentHour + (gameSeed % 5)) % 24) / 24) * 2 * Math.PI; 551 const hourEffect = Math.sin(hourAngle - 1.2) * 2.5 + Math.cos(hourAngle * 2) * 1.0; 552 553 // 4. Live Runtime Micro-Jitter 554 const liveJitter = liveRtpJitterMap[game.id] || 0; 555 556 let calculated = base + dayShift + hourEffect + liveJitter; 557 558 // Clamp realistically between 3.2% (below 10%) and 99.2% 559 if (calculated > 99.2) calculated = 99.2; 560 if (calculated < 3.2) calculated = 3.2; 561 562 return parseFloat(calculated.toFixed(1)); 563} 564 565function getRTPClassification(rtp) { 566 if (rtp >= 92.0) { 567 return { 568 status: 'GACOR MAX', 569 fillClass: 'fill-gacor', 570 valClass: 'val-gacor', 571 isGacor: true, 572 badgeHtml: '<span class="ancua-hot-badge"><i class="fa-solid fa-fire"></i> GACOR</span>' 573 }; 574 } else if (rtp >= 72.0) { 575 return { 576 status: 'BAGUS', 577 fillClass: 'fill-hot', 578 valClass: 'val-hot', 579 isGacor: false, 580 badgeHtml: '' 581 }; 582 } else if (rtp >= 40.0) { 583 return { 584 status: 'SEDANG', 585 fillClass: 'fill-medium', 586 valClass: 'val-medium', 587 isGacor: false, 588 badgeHtml: '' 589 }; 590 } else if (rtp >= 15.0) { 591 return { 592 status: 'DINGIN', 593 fillClass: 'fill-cold', 594 valClass: 'val-cold', 595 isGacor: false, 596 badgeHtml: '<span class="ancua-cold-badge" style="position:absolute; top:6px; right:6px; background:rgba(180,83,9,0.85); color:#fed7aa; font-size:9px; font-weight:800; padding:2px 6px; border-radius:4px; border:1px solid #f97316;"><i class="fa-solid fa-triangle-exclamation"></i> WASPADA</span>' 597 }; 598 } else { 599 return { 600 status: 'ANJLOK', 601 fillClass: 'fill-anjlok', 602 valClass: 'val-anjlok', 603 isGacor: false, 604 badgeHtml: '<span class="ancua-cold-badge" style="position:absolute; top:6px; right:6px; background:linear-gradient(135deg,#7f1d1d,#450a0a); color:#fca5a5; font-size:9px; font-weight:900; padding:2px 6px; border-radius:4px; border:1px solid #ef4444; box-shadow:0 0 8px rgba(239,68,68,0.5);"><i class="fa-solid fa-snowflake"></i> ANJLOK</span>' 605 }; 606 } 607} 608 609/* ========================================================================== 610 8. 7-Day Live RTP Date Generator & Clean Provider Navigation 611 ========================================================================== */ 612function initDateTabs() { 613 const daysNav = document.getElementById('rtpDaysNav'); 614 if (!daysNav) return; 615 616 daysNav.innerHTML = ''; 617 const indonesianDays = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']; 618 const indonesianMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ags', 'Sep', 'Okt', 'Nov', 'Des']; 619 620 const today = new Date(); 621 622 for (let i = 0; i < 7; i++) { 623 const d = new Date(); 624 d.setDate(today.getDate() + i); 625 626 const dayName = indonesianDays[d.getDay()]; 627 const dateNum = d.getDate(); 628 const monthName = indonesianMonths[d.getMonth()]; 629 const dateFormatted = `${dateNum} ${monthName}`; 630 631 let badgeHtml = ''; 632 if (i === 0) { 633 badgeHtml = '<span class="day-tab-badge"><i class="fa-solid fa-bolt"></i> HARI INI</span>'; 634 } else if (i === 1) { 635 badgeHtml = '<span class="day-tab-badge" style="color: #60a5fa;"><i class="fa-solid fa-calendar-day"></i> BESOK</span>'; 636 } else if (d.getDay() === 0 || d.getDay() === 6) { 637 badgeHtml = '<span class="day-tab-badge" style="color: #ef4444;"><i class="fa-solid fa-star"></i> WEEKEND</span>'; 638 } 639 640 const btn = document.createElement('button'); 641 btn.type = 'button'; 642 btn.className = `day-tab-btn ${i === 0 ? 'active' : ''}`; 643 btn.innerHTML = `
644 <span class="day-tab-name">${dayName}</span> 645 <span class="day-tab-date">${dateFormatted}</span> 646 ${badgeHtml} 647 `; 648 649 btn.addEventListener('click', () => { 650 document.querySelectorAll('.day-tab-btn').forEach(b => b.classList.remove('active')); 651 btn.classList.add('active'); 652 currentSelectedDayIndex = i; 653 currentDisplayedCount = PAGE_SIZE; 654 renderRTPCards(); 655 }); 656 657 daysNav.appendChild(btn); 658 } 659 660 renderProviderControls(); 661} 662 663function renderProviderControls() { 664 const providerContainer = document.getElementById('providerPills'); 665 if (!providerContainer) return; 666 667 providerContainer.innerHTML = ''; 668 669 ALL_PROVIDERS.forEach(p => { 670 const pill = document.createElement('button'); 671 pill.type = 'button'; 672 pill.className = `provider-pill ${currentSelectedProvider === p.id ? 'active' : ''}`; 673 pill.innerHTML = ` 674 <i class="fa-solid ${p.icon}"></i> ${p.name} 675 <span class="provider-badge-count">${p.count}</span> 676 `; 677 pill.addEventListener('click', () => { 678 selectProvider(p.id); 679 }); 680 providerContainer.appendChild(pill); 681 }); 682} 683 684function selectProvider(providerId) { 685 currentSelectedProvider = providerId; 686 currentDisplayedCount = PAGE_SIZE; 687 688 document.querySelectorAll('.provider-pill').forEach(pill => { 689 const text = pill.textContent.toLowerCase(); 690 const matched = ALL_PROVIDERS.find(p => p.id === providerId); 691 if (matched && text.includes(matched.name.toLowerCase())) { 692 pill.classList.add('active'); 693 } else { 694 pill.classList.remove('active'); 695 } 696 }); 697 698 renderRTPCards(); 699} 700 701/* ========================================================================== 702 9. High-Performance RTP Grid & Chunked Pagination 703 ========================================================================== */ 704function initRTPGrid() { 705 renderRTPCards(); 706 renderPopularGames(); 707} 708 709function getFilteredGames() { 710 const gamesPool = currentSelectedProvider === 'Semua' 711 ? ALL_GAMES 712 : (GAMES_BY_PROVIDER[currentSelectedProvider] || []); 713 714 if (!searchQuery) return gamesPool; 715 716 const q = searchQuery.toLowerCase().trim(); 717 return gamesPool.filter(g => 718 (g.name && g.name.toLowerCase().includes(q)) || 719 (g.provider && g.provider.toLowerCase().includes(q)) 720 ); 721} 722 723function renderRTPCards() { 724 const grid = document.getElementById('rtpCardsGrid');
725 const loadMoreWrap = document.getElementById('rtpLoadMoreWrap'); 726 if (!grid) return; 727 728 const filtered = getFilteredGames(); 729 730 const counterEl = document.getElementById('rtpGameCount'); 731 if (counterEl) { 732 counterEl.innerHTML = `<i class="fa-solid fa-layer-group"></i> ${filtered.length} Game Tersedia`; 733 } 734 735 if (filtered.length === 0) { 736 grid.innerHTML = ` 737 <div style="grid-column: 1 / -1; text-align: center; padding: 3rem 1.5rem; background: rgba(20, 5, 7, 0.95); border-radius: 12px; border: 1px dashed rgba(220, 38, 38, 0.45);"> 738 <i class="fa-solid fa-gamepad" style="font-size: 2.5rem; color: #ffd700; margin-bottom: 1rem;"></i> 739 <h3 style="color: #ffffff; margin-bottom: 0.5rem; font-size: 1.3rem; font-weight: 800;">Game Tidak Ditemukan</h3> 740 <p style="color: #94a3b8; max-width: 440px; margin: 0 auto 1.25rem;">Coba cari dengan kata kunci lain atau pilih tab provider di atas.</p> 741 <button class="btn btn-secondary btn-sm" onclick="resetFilters()"> 742 <i class="fa-solid fa-arrows-rotate"></i> Reset Filter 743 </button> 744 </div> 745 `; 746 if (loadMoreWrap) loadMoreWrap.innerHTML = ''; 747 return; 748 } 749 750 const gamesToDisplay = filtered.slice(0, currentDisplayedCount); 751 752 grid.innerHTML = gamesToDisplay.map((game) => { 753 const rtp = calculateDynamicRTP(game, currentSelectedDayIndex); 754 const meta = getRTPClassification(rtp); 755 const volShort = (game.volatility || 'Tinggi').split(' ')[0]; 756 757 return ` 758 <div class="ancua-game-card game-card-fade-in" data-game-id="${game.id}"> 759 <div class="ancua-thumb-wrapper"> 760 <img src="${game.image}" alt="${game.name}" height="200" width="200" loading="lazy" onerror="this.onerror=null; this.src='https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2';" /> 761 <span class="ancua-provider-tag">${game.provider || 'SLOT'}</span> 762 ${meta.badgeHtml} 763 <div class="ancua-hover-links"> 764 <button class="ancua-btn-pola" onclick="showPolaModal('${game.id}')">POLA</button> 765 <a href="${URL_DAFTAR}" target="_blank" rel="noopener" class="ancua-btn-play">MAIN</a> 766 </div> 767 </div> 768 <div class="ancua-card-footer"> 769 <div class="ancua-game-name" title="${game.name}">${game.name}</div> 770 771 <div class="ancua-rtp-row"> 772 <span class="ancua-rtp-label">RTP Live:</span> 773 <span class="ancua-rtp-val ${meta.valClass}">${rtp}%</span> 774 </div> 775 776 <div class="ancua-bar-track"> 777 <div class="ancua-bar-fill ${meta.fillClass}" style="width: ${rtp}%"></div> 778 </div> 779 780 <div class="ancua-meta-row"> 781 <span class="ancua-status-tag ${meta.valClass}">${meta.status}</span> 782 <span class="ancua-vol-tag">Vol: ${volShort}</span> 783 </div> 784 </div> 785 </div> 786 `; 787 }).join(''); 788 789 if (loadMoreWrap) { 790 if (currentDisplayedCount < filtered.length) { 791 const remaining = filtered.length - currentDisplayedCount; 792 const nextBatch = Math.min(PAGE_SIZE, remaining); 793 loadMoreWrap.innerHTML = ` 794 <div class="rtp-showing-info"> 795 Menampilkan <strong>${gamesToDisplay.length}</strong> dari <strong>${filtered.length}</strong> Game Slot 796 </div> 797 <button class="btn-rtp-load-more" onclick="loadMoreRTPGames()"> 798 <i class="fa-solid fa-circle-chevron-down"></i> Muat Lebih Banyak (+${nextBatch} Game) 799 </button> 800 `; 801 } else { 802 loadMoreWrap.innerHTML = ` 803 <div class="rtp-showing-info"> 804 Semua <strong>${filtered.length}</strong> Game Slot Telah Ditampilkan 805 </div> 806 `; 807 } 808 } 809} 810 811window.loadMoreRTPGames = function() { 812 currentDisplayedCount += PAGE_SIZE; 813 renderRTPCards(); 814}; 815 816window.resetFilters = function() { 817 currentSelectedProvider = 'Semua'; 818 searchQuery = ''; 819 currentDisplayedCount = PAGE_SIZE; 820 const searchInputs = document.querySelectorAll('.search-input, #headerSearchInput, #mobileSearchInput'); 821 searchInputs.forEach(i => i.value = ''); 822 selectProvider('Semua'); 823}; 824 825/* ========================================================================== 826 10. Popular Games Leaderboard & Live Volatility Engine 827 ========================================================================== */ 828let currentTopPopularGameIds = []; 829 830function renderPopularGames() { 831 const container = document.getElementById('popularGamesGrid'); 832 if (!container || !POPULAR_GAMES_POOL.length) return; 833 834 POPULAR_GAMES_POOL.sort((a, b) =>
834 b.count - a.count); 835 const topPopular = POPULAR_GAMES_POOL.slice(0, 10); 836 currentTopPopularGameIds = topPopular.map(g => g.id); 837 838 container.innerHTML = topPopular.map((game, index) => { 839 let rankBadgeClass = ''; 840 if (index === 0) rankBadgeClass = 'gold-rank'; 841 else if (index === 1) rankBadgeClass = 'silver-rank'; 842 else if (index === 2) rankBadgeClass = 'bronze-rank'; 843 844 return ` 845 <div class="ancua-game-card popular-ranked" id="pop-card-${game.id}"> 846 <div class="ancua-thumb-wrapper"> 847 <img src="${game.image}" alt="${game.name}" height="200" width="200" loading="lazy" onerror="this.onerror=null; this.src='https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2';" /> 848 <span class="ancua-rank-badge ${rankBadgeClass}">#${index + 1}</span> 849 <div class="ancua-player-tag"> 850 <span class="pulse-dot"></span> 851 <span id="player-count-${game.id}">${(game.count || 2500).toLocaleString('id-ID')}</span> Aktif 852 </div> 853 <div class="ancua-hover-links"> 854 <button class="ancua-btn-pola" onclick="showPolaModal('${game.id}')">POLA</button> 855 <a href="${URL_DAFTAR}" target="_blank" rel="noopener" class="ancua-btn-play">MAIN</a> 856 </div> 857 </div> 858 <div class="ancua-card-footer"> 859 <div class="ancua-game-name" title="${game.name}">${game.name}</div> 860 <div class="ancua-jackpot-text"> 861 <span style="color: #94a3b8;">Jackpot:</span> 862 <strong id="jackpot-val-${game.id}" style="color: #fef08a;">Rp ${(game.jackpotVal || 350000000).toLocaleString('id-ID')}</strong> 863 </div> 864 </div> 865 </div> 866 `; 867 }).join(''); 868} 869 870function initVolatilePlayerCounters() { 871 setInterval(() => { 872 if (!POPULAR_GAMES_POOL.length) return; 873 874 POPULAR_GAMES_POOL.forEach(game => { 875 const isDrawdown = Math.random() < 0.20; 876 if (isDrawdown) { 877 const drawdownPct = (Math.random() * 0.04) + 0.01; 878 const dip = Math.floor((game.count || 2000) * drawdownPct * 0.12) + 4; 879 game.count = Math.max(game.baseCount || 1000, (game.count || 2000) - dip); 880 } else { 881 const surgeGrowth = (Math.random() * 0.30) + 0.10; 882 const boostImpulse = Math.random() < 0.22 ? Math.floor(Math.random() * 95) + 30 : 0; 883 const jump = Math.floor(18 * (1 + surgeGrowth)) + Math.floor(Math.random() * 12) + boostImpulse; 884 game.count = (game.count || 2000) + jump; 885 } 886 887 const isJackpotDrawdown = Math.random() < 0.20; 888 if (isJackpotDrawdown) { 889 const dipPct = (Math.random() * 0.04) + 0.01; 890 const dip = Math.floor((game.jackpotVal || 300000000) * dipPct * 0.003) + 15000; 891 game.jackpotVal = Math.max(game.baseJackpot || game.jackpotVal || 200000000, (game.jackpotVal || 300000000) - dip); 892 } else { 893 const surgeGrowth = (Math.random() * 0.30) + 0.10; 894 const jump = Math.floor(65000 * (1 + surgeGrowth)) + Math.floor(Math.random() * 25000); 895 game.jackpotVal = (game.jackpotVal || 300000000) + jump; 896 } 897 }); 898 899 POPULAR_GAMES_POOL.sort((a, b) => b.count - a.count); 900 const newTopPopular = POPULAR_GAMES_POOL.slice(0, 10); 901 const newTopIds = newTopPopular.map(g => g.id); 902 903 const hasOrderChanged = newTopIds.some((id, idx) => id !== currentTopPopularGameIds[idx]); 904 905 if (hasOrderChanged) { 906 currentTopPopularGameIds = newTopIds; 907 const container = document.getElementById('popularGamesGrid'); 908 if (container) { 909 container.innerHTML = newTopPopular.map((game, index) => { 910 let rankBadgeClass = ''; 911 if (index === 0) rankBadgeClass = 'gold-rank'; 912 else if (index === 1) rankBadgeClass = 'silver-rank'; 913 else if (index === 2) rankBadgeClass = 'bronze-rank'; 914 915 return ` 916 <div class="ancua-game-card popular-ranked rolling-enter" id="pop-card-${game.id}"> 917 <div class="ancua-thumb-wrapper"> 918 <img src="${game.image}" alt="${game.name}" height="200" width="200" loading="lazy" onerror="this.onerror=null; this.src='https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2';" />
919 <span class="ancua-rank-badge ${rankBadgeClass}">#${index + 1}</span> 920 <div class="ancua-player-tag"> 921 <span class="pulse-dot"></span> 922 <span id="player-count-${game.id}">${(game.count || 2500).toLocaleString('id-ID')}</span> Aktif 923 </div> 924 <div class="ancua-hover-links"> 925 <button class="ancua-btn-pola" onclick="showPolaModal('${game.id}')">POLA</button> 926 <a href="${URL_DAFTAR}" target="_blank" rel="noopener" class="ancua-btn-play">MAIN</a> 927 </div> 928 </div> 929 <div class="ancua-card-footer"> 930 <div class="ancua-game-name" title="${game.name}">${game.name}</div> 931 <div class="ancua-jackpot-text"> 932 <span style="color: #94a3b8;">Jackpot:</span> 933 <strong id="jackpot-val-${game.id}" style="color: #fef08a;">Rp ${(game.jackpotVal || 350000000).toLocaleString('id-ID')}</strong> 934 </div> 935 </div> 936 </div> 937 `; 938 }).join(''); 939 } 940 } else { 941 newTopPopular.forEach(game => { 942 const countEl = document.getElementById(`player-count-${game.id}`); 943 if (countEl) countEl.textContent = (game.count || 2500).toLocaleString('id-ID'); 944 const jackpotEl = document.getElementById(`jackpot-val-${game.id}`); 945 if (jackpotEl) jackpotEl.textContent = `Rp ${(game.jackpotVal || 350000000).toLocaleString('id-ID')}`; 946 }); 947 } 948 949 savePersistedState(); 950 }, 2400); 951} 952 953/* ========================================================================== 954 11. Real-Time Telemetry HUD Engine 955 ========================================================================== */ 956function initLiveKPIDashboard() { 957 const payoutEl = document.getElementById('kpTotalPayout'); 958 const rtpEl = document.getElementById('kpAvgRtp'); 959 const playersEl = document.getElementById('kpTotalPlayers'); 960 const wdSpeedEl = document.getElementById('kpWdSpeed'); 961 962 const basePayoutFloor = Math.floor(2879450000 * getDailyGrowthFactor()); 963 const basePlayersFloor = Math.floor(49155 * getDailyGrowthFactor()); 964 965 setInterval(() => { 966 const isPayoutDrawdown = Math.random() < 0.25; 967 if (isPayoutDrawdown) { 968 const dipPct = (Math.random() * 0.04) + 0.01; 969 const dip = Math.floor(120000 * dipPct * 6) + 25000; 970 liveTotalPayout = Math.max(basePayoutFloor, liveTotalPayout - dip); 971 } else { 972 const payoutSurgeFactor = (Math.random() * 0.30) + 0.10; 973 const surge = Math.floor(450000 * (1 + payoutSurgeFactor)) + Math.floor(Math.random() * 120000); 974 liveTotalPayout += surge; 975 } 976 977 if (payoutEl) { 978 const formattedMillions = (liveTotalPayout / 1000000000).toFixed(3); 979 payoutEl.textContent = `Rp ${formattedMillions} M+`; 980 payoutEl.title = `Total Payout: Rp ${liveTotalPayout.toLocaleString('id-ID')}`; 981 if (!isPayoutDrawdown) { 982 payoutEl.classList.add('spike'); 983 setTimeout(() => payoutEl.classList.remove('spike'), 400); 984 } 985 } 986 987 const rtpJitter = (Math.random() * 0.08 - 0.04); 988 liveAvgRtp = parseFloat((98.72 + rtpJitter).toFixed(2)); 989 if (rtpEl) { 990 rtpEl.textContent = `${liveAvgRtp}%`; 991 rtpEl.classList.add('spike'); 992 setTimeout(() => rtpEl.classList.remove('spike'), 400); 993 } 994 995 const isPlayerDrawdown = Math.random() < 0.25; 996 if (isPlayerDrawdown) { 997 const dipFactor = (Math.random() * 0.04) + 0.01; 998 const dip = Math.floor(45 * dipFactor) + 12; 999 liveTotalPlayers = Math.max(basePlayersFloor, liveTotalPlayers - dip); 1000 } else { 1001 const surgeMultiplier = (Math.random() * 0.30) + 0.10; 1002 const surge = Math.floor(55 * (1 + surgeMultiplier)) + Math.floor(Math.random() * 20); 1003 liveTotalPlayers += surge; 1004 } 1005 1006 if (playersEl) { 1007 playersEl.textContent = `${liveTotalPlayers.toLocaleString('id-ID')}+`; 1008 if (!isPlayerDrawdown) { 1009 playersEl.classList.add('spike'); 1010 setTimeout(() => playersEl.classList.remove('spike'), 400); 1011 } 1012 } 1013 1014 if (wdSpeedEl) { 1015 const seconds = Math.floor(Math.random() * 15) + 32; 1016 wdSpeedEl.textContent = `< 1m ${seconds}s`; 1017 } 1018 1019 savePersistedState(); 1020 }, 2200); 1021} 1022 1023/* ========================================================================== 1024 12. Grand Progressive Jackpot Counter 1025 ========================================================================== */ 1026function initGrandProgressiveJackpot() { 1027 const jackpotEl = document.getElementById('progressive_jackpot'); 1028 if (!jackpotEl) return; 1029 1030 function updateGrandJackpot() { 1031 const jump = Math.floor(Math.random() * 125000) + 35000; 1032 liveGrandJackpot += jump;
1033 jackpotEl.textContent = liveGrandJackpot.toLocaleString('id-ID'); 1034 } 1035 1036 jackpotEl.textContent = liveGrandJackpot.toLocaleString('id-ID'); 1037 setInterval(updateGrandJackpot, 1800); 1038} 1039 1040/* ========================================================================== 1041 13. Dynamic Pola Gacor & Jam Hoki Modal Engine 1042 ========================================================================== */ 1043window.showPolaModal = function(gameId) { 1044 const game = GAMES_BY_ID[gameId] || ALL_GAMES.find(g => String(g.id) === String(gameId)); 1045 if (!game) return; 1046 1047 const modal = document.getElementById('polaModalOverlay'); 1048 const title = document.getElementById('polaGameTitle'); 1049 const body = document.getElementById('polaModalContent'); 1050 1051 const currentHour = new Date().getHours(); 1052 const startHour = (currentHour + 23) % 24; 1053 const endHour = (currentHour + 2) % 24; 1054 const formatHour = (h) => (h < 10 ? `0${h}` : `${h}`); 1055 const jamGacorWindow = `${formatHour(startHour)}:30 - ${formatHour(endHour)}:45 WIB`; 1056 1057 const dynamicRtp = calculateDynamicRTP(game, currentSelectedDayIndex); 1058 const meta = getRTPClassification(dynamicRtp); 1059 1060 const pattern = game.pattern || { quick: '20x', turbo: '30x', auto: '50x', dc: 'ON' }; 1061 const maxMultiplier = game.maxMultiplier || 'x15,000'; 1062 1063 if (title) { 1064 title.innerHTML = `<i class="fa-solid fa-fire text-gold"></i> Pola Gacor: ${game.name}`; 1065 } 1066 1067 if (body) { 1068 body.innerHTML = ` 1069 <div style="text-align: center; margin-bottom: 14px;"> 1070 <img src="${game.image}" alt="${game.name}" style="width: 100px; height: 100px; border-radius: 8px; margin: 0 auto 8px; object-fit: cover; border: 2px solid #dc2626; display: block; box-shadow: 0 0 16px rgba(220, 38, 38, 0.4);" onerror="this.onerror=null; this.src='https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2';"> 1071 <h3 style="color: #ffffff; font-size: 15px; font-weight: 800; margin: 0 0 4px;">${game.name}</h3> 1072 <span style="color: #ffd700; font-size: 12px; font-weight: 700;">${game.provider || 'SLOT'} ⢠Max ${maxMultiplier}</span> 1073 </div> 1074 1075 <div style="background: #0d0204; border: 1px solid #3b0d11; border-radius: 8px; padding: 12px; margin-bottom: 14px; display: flex; flex-direction: column; gap: 8px;"> 1076 <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #23080b; padding-bottom: 6px;"> 1077 <span style="color: #94a3b8; font-size: 12px;"><i class="fa-solid fa-hand-pointer text-gold"></i> Spin Manual (Cepat):</span> 1078 <strong style="color: #ffffff; font-size: 12px;">${pattern.quick} (Centang)</strong> 1079 </div> 1080 <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #23080b; padding-bottom: 6px;"> 1081 <span style="color: #94a3b8; font-size: 12px;"><i class="fa-solid fa-bolt text-gold"></i> Spin Turbo:</span> 1082 <strong style="color: #ffd700; font-size: 12px;">${pattern.turbo}</strong> 1083 </div> 1084 <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #23080b; padding-bottom: 6px;"> 1085 <span style="color: #94a3b8; font-size: 12px;"><i class="fa-solid fa-arrows-rotate text-gold"></i> Spin Auto:</span> 1086 <strong style="color: #10b981; font-size: 12px;">${pattern.auto}</strong> 1087 </div> 1088 <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #23080b; padding-bottom: 6px;"> 1089 <span style="color: #94a3b8; font-size: 12px;"><i class="fa-solid fa-sliders text-gold"></i> Double Chance (DC):</span> 1090 <strong style="color: ${pattern.dc === 'ON' ? '#10b981' : '#f87171'}; font-size: 12px;">${pattern.dc}</strong> 1091 </div> 1092 <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #23080b; padding-bottom: 6px;"> 1093 <span style="color: #94a3b8; font-size: 12px;"><i class="fa-regular fa-clock text-gold"></i> Jam Gacor Hoki:</span>
1094 <span class="pola-time-badge"><i class="fa-solid fa-fire"></i> ${jamGacorWindow}</span> 1095 </div> 1096 <div style="display: flex; justify-content: space-between; align-items: center;"> 1097 <span style="color: #94a3b8; font-size: 12px;">Estimasi RTP Live:</span> 1098 <strong style="font-size: 14px; font-weight: 800;" class="${meta.valClass}">${dynamicRtp}% (${meta.status})</strong> 1099 </div> 1100 </div> 1101 1102 <a href="${URL_DAFTAR}" target="_blank" rel="noopener noreferrer" class="btn-vip-primary" style="width: 100%; text-align: center; display: block; padding: 10px; font-size: 13px; font-weight: 900; border-radius: 6px; box-shadow: 0 0 14px rgba(56, 189, 248, 0.5);"> 1103 <i class="fa-solid fa-play"></i> MAIN GAME INI SEKARANG 1104 </a> 1105 `; 1106 } 1107 1108 if (modal) modal.classList.add('open'); 1109}; 1110 1111function initModals() { 1112 const polaOverlay = document.getElementById('polaModalOverlay'); 1113 1114 document.querySelectorAll('.modal-close, .modal-backdrop-close').forEach(btn => { 1115 btn.addEventListener('click', () => { 1116 if (polaOverlay) polaOverlay.classList.remove('open'); 1117 }); 1118 }); 1119 1120 const loginForm = document.getElementById('loginForm'); 1121 if (loginForm) { 1122 loginForm.addEventListener('submit', (e) => { 1123 e.preventDefault(); 1124 window.open(URL_LOGIN, '_blank'); 1125 if (typeof $ !== 'undefined' && $('#login_modal').length) { 1126 $('#login_modal').modal('hide'); 1127 } 1128 }); 1129 } 1130} 1131 1132/* ========================================================================== 1133 14. Hourly Dynamic Testimonials (Uses Real Game Catalog) 1134 ========================================================================== */ 1135let lastGeneratedHour = -1; 1136let currentHourlyTestimonials = []; 1137 1138function generateFreshHourlyTestimonials(hourSeed) { 1139 const tiers = ['Member VIP', 'Member Platinum', 'Member Gold', 'Member VIP Prioritas']; 1140 const results = []; 1141 1142 const quoteStarters = [ 1143 (g, b, amt) => `Awalnya ragu dengan bocoran RTP live 7 hari, pas coba pola jam gacor di ${g}, langsung pecah perkalian Sensational! WD Rp ${amt.toLocaleString('id-ID')} mendarat di ${b} kurang dari 2 menit.`, 1144 (g, b, amt) => `Main di sini benar-benar transparan. Jam hoki di ${g} akurat banget sama bocoran live RTP. Penarikan Rp ${amt.toLocaleString('id-ID')} cair utuh tanpa potongan admin via ${b}.`, 1145 (g, b, amt) => `Scatter dan jackpot terus menyambung di ${g}. Fitur pola turbo spin ngebantu banget dapat maxwin Rp ${amt.toLocaleString('id-ID')}. Customer service 24 jam responsif!`, 1146 (g, b, amt) => `Sensasi kemenangan di ${g} luar biasa! Pasang taruhan santai pas RTP tercatat 98.6%, langsung cair Rp ${amt.toLocaleString('id-ID')} via ${b}. Web merah putihnya juga super kencang di HP.`, 1147 (g, b, amt) => `Sudah langganan main di sini. Rekomendasi game terpopuler ${g} terbukti anti-rungkad. Tarik dana Rp ${amt.toLocaleString('id-ID')} ke ${b} hitungan detik masuk.`, 1148 (g, b, amt) => `Bocoran RTP per hari sangat membantu analisis jam gacor ${g}. WD Rp ${amt.toLocaleString('id-ID')} langsung diproses tanpa ribet. Situs paling aman dan terpercaya!` 1149 ]; 1150 1151 const candidateGames = ALL_GAMES.length > 0 1152 ? ALL_GAMES.slice(0, 30).map(g => g.name) 1153 : ['Gates of Olympus 1000', 'Mahjong Ways 2', 'Sweet Bonanza 1000', 'Lucky Neko', 'Sugar Rush 1000', 'Wild Bandito', 'Koi Gate']; 1154 1155 for (let i = 0; i < 8; i++) { 1156 const profile = generateProceduralName(); 1157 const city = CITIES_POOL[(hourSeed * 7 + i * 3) % CITIES_POOL.length]; 1158 const tier = tiers[(hourSeed + i) % tiers.length]; 1159 const game = candidateGames[(hourSeed * 3 + i) % candidateGames.length]; 1160 const bank = BANKS_POOL[(hourSeed + i * 2) % BANKS_POOL.length]; 1161 const color = AVATAR_GRADIENTS[(hourSeed + i) % AVATAR_GRADIENTS.length]; 1162 1163 const amtBase = Math.floor(((hourSeed * 13 + i * 29) % 36) + 9) * 2000000 + ((i % 3) * 500000); 1164 const quote = quoteStarters[i % quoteStarters.length](game, bank, amtBase); 1165 const minuteAgo = (i * 7) + 3; 1166 1167 results.push({ 1168 name: profile.fullName, 1169 tag: `${tier} ⢠${city}`, 1170 initials: profile.initials, 1171 color: color, 1172 game: game, 1173 amount: amtBase, 1174 bank: bank, 1175 quote: quote, 1176 minuteAgo: minuteAgo 1177 }); 1178 } 1179 1180 return results; 1181} 1182 1183function initHourlyTestimonials() { 1184 function checkAndRenderHourly() { 1185 const currentHour = new Date().getHours(); 1186 const dayOfMonth = new Date().getDate(); 1187 const uniqueHourSeed = (dayOfMonth * 24) + currentHour; 1188 1189 if (uniqueHourSeed !== lastGeneratedHour) { 1190 lastGeneratedHour = uniqueHourSeed; 1191 currentHourlyTestimonials = generateFreshHourlyTestimonials(uniqueHourSeed); 1192 renderHourlyTestimonials(); 1193 } 1194 } 1195 1196 checkAndRenderHourly();
1197 setInterval(checkAndRenderHourly, 60000); 1198} 1199 1200function renderHourlyTestimonials() { 1201 const container = document.getElementById('testimonialsGrid'); 1202 if (!container) return; 1203 1204 container.innerHTML = currentHourlyTestimonials.map(item => ` 1205 <div class="testi-card"> 1206 <div class="testi-top"> 1207 <div class="testi-member"> 1208 <div class="testi-avatar" style="background: ${item.color};">${item.initials}</div> 1209 <div class="testi-info"> 1210 <h4>${item.name}</h4> 1211 <span>${item.tag}</span> 1212 </div> 1213 </div> 1214 <div class="testi-rating"> 1215 <i class="fa-solid fa-star"></i> 1216 <i class="fa-solid fa-star"></i> 1217 <i class="fa-solid fa-star"></i> 1218 <i class="fa-solid fa-star"></i> 1219 <i class="fa-solid fa-star"></i> 1220 </div> 1221 </div> 1222 <div class="testi-payout-box"> 1223 <span class="payout-label"><i class="fa-solid fa-wallet text-green"></i> Penarikan Sukses (${item.bank}):</span> 1224 <span class="payout-amount">Rp ${item.amount.toLocaleString('id-ID')}</span> 1225 </div> 1226 <p class="testi-comment"> 1227 "${item.quote}" 1228 </p> 1229 <div class="testi-footer"> 1230 <span><i class="fa-solid fa-gamepad text-gold"></i> Game: <strong>${item.game}</strong></span> 1231 <span><i class="fa-regular fa-clock"></i> ${item.minuteAgo} menit lalu</span> 1232 </div> 1233 </div> 1234 `).join(''); 1235} 1236 1237// FAQ Accordion Toggle 1238window.toggleFaq = function(btn) { 1239 const item = btn.closest('.faq-item'); 1240 if (!item) return; 1241 const isActive = item.classList.contains('active'); 1242 document.querySelectorAll('.faq-item').forEach(el => el.classList.remove('active')); 1243 if (!isActive) { 1244 item.classList.add('active'); 1245 } 1246}; 1247 1248/* ========================================================================== 1249 15. Live Winner Toasts & Marquee Stream 1250 ========================================================================== */ 1251function getRandomGameFromCatalog() { 1252 if (ALL_GAMES.length > 0) { 1253 const randomIndex = Math.floor(Math.random() * Math.min(ALL_GAMES.length, 50)); 1254 return ALL_GAMES[randomIndex].name; 1255 } 1256 return 'Gates of Olympus 1000'; 1257} 1258 1259const NOTIFICATION_TEMPLATES = [ 1260 { 1261 type: 'withdraw', 1262 icon: 'fa-money-bill-transfer text-gold', 1263 title: 'Penarikan Dana Berhasil', 1264 genText: (user, city) => { 1265 const bank = BANKS_POOL[Math.floor(Math.random() * BANKS_POOL.length)]; 1266 const amount = (Math.floor(Math.random() * 35) + 6) * 1000000; 1267 return `Penarikan dana <strong>Rp ${amount.toLocaleString('id-ID')}</strong> (${bank}) an. <strong>${user}</strong> (${city}) sukses ditransfer.`; 1268 } 1269 }, 1270 { 1271 type: 'play', 1272 icon: 'fa-crown text-gold', 1273 title: 'Sensational Payout!', 1274 genText: (user, city) => { 1275 const g = getRandomGameFromCatalog(); 1276 const amount = (Math.floor(Math.random() * 32) + 4) * 750000; 1277 return `<strong>${user}</strong> (${city}) meraih Sensational Win <strong>Rp ${amount.toLocaleString('id-ID')}</strong> di <em>${g}</em>!`; 1278 } 1279 }, 1280 { 1281 type: 'deposit', 1282 icon: 'fa-bolt text-gold', 1283 title: 'Deposit Terverifikasi', 1284 genText: (user, city) => { 1285 const amount = (Math.floor(Math.random() * 15) + 1) * 500000; 1286 return `Deposit instan <strong>Rp ${amount.toLocaleString('id-ID')}</strong> (QRIS) an. <strong>${user}</strong> (${city}) aktif dalam 5 detik.`; 1287 } 1288 }, 1289 { 1290 type: 'daftar', 1291 icon: 'fa-user-plus text-gold', 1292 title: 'Member VIP Baru', 1293 genText: (user, city) => `User <strong>${user}</strong> baru saja mendaftar akun VIP dari ${city} & klaim garansi 100%.` 1294 } 1295]; 1296 1297function initLiveToastEngine() { 1298 const container = document.getElementById('toastContainer'); 1299 if (!container) return; 1300 1301 function spawnFreshToast() { 1302 const user = getFreshUniqueUsername(); 1303 const city = CITIES_POOL[Math.floor(Math.random() * CITIES_POOL.length)]; 1304 const template = NOTIFICATION_TEMPLATES[Math.floor(Math.random() * NOTIFICATION_TEMPLATES.length)]; 1305 1306 if (container.children.length >= 2) { 1307 const first = container.firstElementChild; 1308 if (first) { 1309 first.classList.add('toast-closing'); 1310 setTimeout(() => first.remove(), 300); 1311 } 1312 } 1313 1314 const toastEl = document.createElement('div'); 1315 toastEl.className = 'live-toast'; 1316 toastEl.innerHTML = ` 1317 <div class="toast-icon-wrap"> 1318 <i class="fa-solid ${template.icon}"></i> 1319 </div> 1320 <div class="toast-body"> 1321 <div class="toast-title">
1322 <span>${template.title}</span> 1323 </div> 1324 <div class="toast-desc">${template.genText(user, city)}</div> 1325 <span class="toast-time"><i class="fa-regular fa-clock"></i> Baru saja</span> 1326 </div> 1327 <button class="toast-close-btn" aria-label="Tutup"><i class="fa-solid fa-xmark"></i></button> 1328 `; 1329 1330 toastEl.querySelector('.toast-close-btn').addEventListener('click', () => { 1331 toastEl.classList.add('toast-closing'); 1332 setTimeout(() => toastEl.remove(), 300); 1333 }); 1334 1335 container.appendChild(toastEl); 1336 1337 setTimeout(() => { 1338 if (toastEl.parentNode) { 1339 toastEl.classList.add('toast-closing'); 1340 setTimeout(() => toastEl.remove(), 300); 1341 } 1342 }, 4200); 1343 } 1344 1345 setInterval(spawnFreshToast, 3000); 1346 setTimeout(spawnFreshToast, 1200); 1347} 1348 1349const LIVE_MARQUEE_STREAM = [ 1350 '<i class="fa-solid fa-trophy text-gold"></i> Member <strong style="color: #ffd700;">hendra.kurn***</strong> baru saja WD <strong style="color: #34d399;">Rp 48.500.000</strong> (BCA)', 1351 '<i class="fa-solid fa-fire text-gold"></i> RTP <strong style="color: #ffd700;">Gates of Olympus 1000</strong> melonjak ke <strong style="color: #34d399;">98.8% GACOR</strong>', 1352 '<i class="fa-solid fa-bolt text-gold"></i> Member <strong style="color: #ffd700;">dewi.lestari***</strong> memicu perkalian x500 di <em style="color: #ffd700;">Sweet Bonanza 1000</em>', 1353 '<i class="fa-solid fa-shield-halved text-gold"></i>
1353 Total Akumulasi Jackpot Progresif: <strong style="color: #ffd700;">Rp 8.925.840.120</strong>', 1354 '<i class="fa-solid fa-bolt text-gold"></i> Member <strong style="color: #ffd700;">bayu.pratama***</strong> deposit <strong style="color: #34d399;">Rp 5.000.000</strong> (QRIS Instant 5s)', 1355 '<i class="fa-solid fa-user-plus text-gold"></i> Member baru <strong style="color: #ffd700;">dimas.setia***</strong> bergabung & klaim Bonus VIP' 1356]; 1357 1358function initLiveMarquee() { 1359 const track = document.getElementById('liveMarqueeTrack'); 1360 const ancuaAnnouncementList = document.getElementById('announcement_list'); 1361 1362 function renderMarqueeTrack() { 1363 if (track) { 1364 const fullText = LIVE_MARQUEE_STREAM.map(item => `<span class="marquee-item">${item}</span>`).join('') + 1365 LIVE_MARQUEE_STREAM.map(item => `<span class="marquee-item">${item}</span>`).join(''); 1366 track.innerHTML = fullText; 1367 } 1368 if (ancuaAnnouncementList) { 1369 ancuaAnnouncementList.innerHTML = LIVE_MARQUEE_STREAM.slice(0, 5).map(item => `<li>${item.replace(/<[^>]*>?/gm, ' ')}</li>`).join(''); 1370 } 1371 } 1372 1373 renderMarqueeTrack(); 1374 1375 setInterval(() => { 1376 const freshUser = getFreshUniqueUsername(); 1377 const randomWd = (Math.floor(Math.random() * 32) + 8) * 1000000; 1378 const bank = BANKS_POOL[Math.floor(Math.random() * BANKS_POOL.length)]; 1379 1380 const freshItem = `<i class="fa-solid fa-money-bill-transfer text-green"></i> Penarikan Sukses <strong>Rp ${randomWd.toLocaleString('id-ID')}</strong> (${bank}) an. <strong>${freshUser}</strong>`; 1381 LIVE_MARQUEE_STREAM.unshift(freshItem); 1382 if (LIVE_MARQUEE_STREAM.length > 10) { 1383 LIVE_MARQUEE_STREAM.pop(); 1384 } 1385 renderMarqueeTrack(); 1386 }, 8500); 1387} 1388 1389/* ========================================================================== 1390 16. Sub-Millisecond Search Engine (Header & Modal) 1391 ========================================================================== */ 1392function initSearchAction() { 1393 const headerSearch = document.getElementById('headerSearchInput'); 1394 const mobileSearch = document.getElementById('mobileSearchInput'); 1395 1396 let debounceTimer = null; 1397 function handleSearchInput(e) { 1398 clearTimeout(debounceTimer); 1399 debounceTimer = setTimeout(() => { 1400 searchQuery = (e.target.value || '').trim(); 1401 currentDisplayedCount = PAGE_SIZE; 1402 renderRTPCards(); 1403 }, 150); 1404 } 1405 1406 if (headerSearch) headerSearch.addEventListener('input', handleSearchInput); 1407 if (mobileSearch) mobileSearch.addEventListener('input', handleSearchInput); 1408} 1409 1410function initModalSearchEngine() { 1411 const modalSearchInput = document.getElementById('search_game_input'); 1412 const modalSearchResults = document.getElementById('search_game_results'); 1413 1414 if (!modalSearchInput || !modalSearchResults) return; 1415 1416 let debounceTimer = null; 1417 modalSearchInput.addEventListener('input', (e) => { 1418 clearTimeout(debounceTimer); 1419 debounceTimer = setTimeout(() => { 1420 const q = (e.target.value || '').trim().toLowerCase(); 1421 if (!q) { 1422 modalSearchResults.innerHTML = ` 1423 <div class="search-empty-state" style="text-align: center; padding: 20px; color: #94a3b8;"> 1424 Ketik nama game favorit Anda di atas untuk mencari instan di antara ${ALL_GAMES.length} game... 1425 </div> 1426 `; 1427 return; 1428 } 1429 1430 const matches = ALL_GAMES.filter(g => 1431 (g.name && g.name.toLowerCase().includes(q)) || 1432 (g.provider && g.provider.toLowerCase().includes(q)) 1433 ).slice(0, 24); 1434 1435 if (matches.length === 0) { 1436 modalSearchResults.innerHTML = ` 1437 <div class="search-empty-state" style="text-align: center; padding: 20px; color: #f87171;"> 1438 <i class="fa-solid fa-circle-exclamation" style="font-size: 24px; margin-bottom: 8px; display: block;"></i> 1439 Game "${q}" tidak ditemukan. 1440 </div> 1441 `; 1442 return; 1443 } 1444 1445 modalSearchResults.innerHTML = ` 1446 <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; max-height: 400px; overflow-y: auto; padding: 6px;"> 1447 ${matches.map(g => { 1448 const rtp = calculateDynamicRTP(g, currentSelectedDayIndex); 1449 const meta = getRTPClassification(rtp); 1450 return ` 1451 <div style="background: #140507; border: 1px solid #3b0d11; border-radius: 8px; padding: 8px; display: flex; flex-direction: column; gap: 6px;"> 1452 <img src="${g.image}
1452" alt="${g.name}" style="width: 100%; height: 110px; object-fit: cover; border-radius: 6px; border: 1px solid #3b0d11;" onerror="this.onerror=null; this.src='https://files.sitestatic.net/games/i/165x220/6d39308186b2c0ede30a49d0c486e911.webp?v=0.2';"> 1453 <div> 1454 <div style="font-weight: 700; color: #fff; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${g.name}</div> 1455 <div style="font-size: 11px; color: #ffd700;">${g.provider} ⢠<span class="${meta.valClass}">RTP ${rtp}%</span></div> 1456 </div> 1457 <div style="display: flex; gap: 4px;"> 1458 <button class="btn-vip-secondary" style="flex: 1; font-size: 10px; padding: 4px;" onclick="showPolaModal('${g.id}')">Pola</button> 1459 <a href="${URL_DAFTAR}" target="_blank" class="btn-vip-primary" style="flex: 1; font-size: 10px; padding: 4px; text-align: center;">Main</a> 1460 </div> 1461 </div> 1462 `; 1463 }).join('')} 1464 </div> 1465 `; 1466 }, 150); 1467 }); 1468} 1469 1470/* ========================================================================== 1471 17. FAQ, Countdown & Mobile Navigation 1472 ========================================================================== */ 1473function initFAQAccordion() { 1474 const faqItems = document.querySelectorAll('.faq-item'); 1475 faqItems.forEach(item => { 1476 const questionBtn = item.querySelector('.faq-question'); 1477 if (questionBtn) { 1478 questionBtn.addEventListener('click', () => { 1479 const isActive = item.classList.contains('active'); 1480 faqItems.forEach(i => i.classList.remove('active')); 1481 if (!isActive) item.classList.add('active'); 1482 }); 1483 } 1484 }); 1485} 1486 1487function initCountdownTimer() { 1488 let seconds = 59; 1489 const clockEl = document.getElementById('rtpCountdown'); 1490 1491 setInterval(() => { 1492 seconds--; 1493 if (seconds < 0) { 1494 seconds = 59; 1495 ALL_GAMES.forEach(g => { 1496 liveRtpJitterMap[g.id] = parseFloat(((Math.random() * 0.4) - 0.2).toFixed(1)); 1497 }); 1498 renderRTPCards(); 1499 } 1500 if (clockEl) { 1501 clockEl.textContent = `00:${seconds < 10 ? '0' : ''}${seconds}`; 1502 } 1503 }, 1000); 1504} 1505 1506function initMobileDrawer() { 1507 const toggle = document.getElementById('mobileToggle'); 1508 const drawer = document.getElementById('mobileDrawer'); 1509 const closeBtn = document.getElementById('mobileDrawerClose'); 1510 1511 if (toggle && drawer) { 1512 toggle.addEventListener('click', (e) => { 1513 e.stopPropagation(); 1514 drawer.classList.toggle('open'); 1515 }); 1516 if (closeBtn) closeBtn.addEventListener('click', () => drawer.classList.remove('open')); 1517 } 1518} 1519 1520/* ========================================================================== 1521 18. Bulletproof Zero-Latency Bootstrap 1522 ========================================================================== */ 1523function bootstrapPortal() { 1524 try { 1525 let datasetLoaded = false; 1526 const embeddedScript = document.getElementById('rtp-embedded-data'); 1527 if (embeddedScript && embeddedScript.textContent && embeddedScript.textContent.trim().length > 100) { 1528 try { 1529 const serverData = JSON.parse(embeddedScript.textContent); 1530 if (Array.isArray(serverData) && serverData.length > 0) { 1531 processRawDataSlot(serverData); 1532 datasetLoaded = true; 1533 } 1534 } catch (e) {} 1535 } 1536 1537 if (!datasetLoaded) { 1538 processRawDataSlot(EMBEDDED_FALLBACK_DATA); 1539 } 1540 1541 // 2. Initialize all interactive subsystems 1542 initDateTabs(); 1543 initRTPGrid(); 1544 initLiveKPIDashboard(); 1545 initVolatilePlayerCounters(); 1546 initGrandProgressiveJackpot(); 1547 initHourlyTestimonials(); 1548 initLiveToastEngine(); 1549 initSearchAction(); 1550 initModalSearchEngine(); 1551 initFAQAccordion(); 1552 initModals(); 1553 initMobileDrawer(); 1554 initCountdownTimer(); 1555 initLiveMarquee(); 1556 } catch (err) { 1557 console.error('Core bootstrap error:', err); 1558 } 1559 1560 // 3. Load full 48-provider /json/data-slot.json in the background & upgrade catalog 1561 loadDataSlotEngine().then(() => { 1562 try { 1563 initDateTabs(); 1564 renderRTPCards(); 1565 renderPopularGames(); 1566 } catch (e) { 1567 console.warn('Post-load render notice:', e); 1568 } 1569 }).catch(e => { 1570 console.warn('Data loader fallback active:', e); 1571 }); 1572} 1573 1574if (document.readyState === 'loading') { 1575 document.addEventListener('DOMContentLoaded', bootstrapPortal); 1576} else { 1577 bootstrapPortal(); 1578}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.