(function () { 'use strict'; var BASE = typeof appPath === 'function' ? appPath('/Game') : '/Game'; var SERVER = (typeof GAME_SERVER !== 'undefined' ? GAME_SERVER : '') + '/Game'; var CHAR_KEY = 'gameCharacterId'; /** รูป composite idle ทิศ down จากหน้า Character (preview-idle-layer-down) — ตรงกับ character.js */ var LOBBY_IDLE_DOWN_PREFIX = 'jdCharLobbyIdleDown:'; function getStoredLobbyIdleDownDataUrl(safeId) { if (!safeId) return ''; try { var v = localStorage.getItem(LOBBY_IDLE_DOWN_PREFIX + safeId) || ''; if (typeof v === 'string' && v.indexOf('data:image/') === 0 && v.length > 80) return v; } catch (e) { /* ignore */ } return ''; } var GUIDE_KEY = 'mainLobbyGuideDone'; var MAX_GUIDE = 5; var ML_GUIDE = typeof appPath === 'function' ? appPath('/Main-Lobby/IMAGE/guide') : '/Main-Lobby/IMAGE/guide'; /** * ตำแหน่งแถบคำแนะนำ + จุดไฮไลต์ต่อ guide-01…05 (ปรับลำดับให้ตรงไฟล์รูป) * position: 'bottom' | 'top' — ตาม mock แถบล่าง/บน * highlight: selector ของปุ่มใน lobby (null = ไม่วงกรอบ) */ var GUIDE_STEPS = [ { position: 'bottom', highlight: null, pad: 10 }, { position: 'top', highlight: '.lobby-footer-center', pad: 10 }, { position: 'top', highlight: '#btn-ai-chat', pad: 8 }, { position: 'bottom', highlight: '#btn-cloth', pad: 10 }, { position: 'bottom', highlight: '#btn-daily', pad: 10 }, ]; function checkLogin() { if (localStorage.getItem('isLoggedIn') !== 'true') { window.location.href = typeof appPath === 'function' ? appPath('/Login/') : '/Login/'; return false; } return true; } if (!checkLogin()) return; function toast(msg) { var el = document.getElementById('lobby-toast'); if (!el) return; el.textContent = msg; el.classList.add('lobby-toast--show'); clearTimeout(toast._t); toast._t = setTimeout(function () { el.classList.remove('lobby-toast--show'); }, 2600); } function padAgent(n) { var s = String(n); while (s.length < 6) s = '0' + s; return s; } function ensureAgentId() { var k = 'agentDisplayId'; var v = localStorage.getItem(k); if (!v || !/^\d{6}$/.test(v)) { v = padAgent(100000 + Math.floor(Math.random() * 899999)); try { localStorage.setItem(k, v); } catch (e) { /* ignore */ } } return v; } function getSelectedCharacterId() { try { return (localStorage.getItem(CHAR_KEY) || '').trim(); } catch (e) { return ''; } } /** ใช้เฉพาะ id ที่ปลอดเป็น segment ใน URL — กันพาธแปลก/อักขระที่เซิร์ฟไม่มีไฟล์ */ function normalizeCharacterAssetId(id) { var s = String(id == null ? '' : id).trim(); if (!s) return ''; if (s.length > 96) s = s.slice(0, 96); if (!/^[a-zA-Z0-9._-]+$/.test(s)) return ''; return s; } function mainMenuCharFallbackUrl() { return typeof appPath === 'function' ? appPath('/Main-Menu/char-main.png') : '/Main-Menu/char-main.png'; } /** * URL รูปโลบี้ — ลองทิศ down ก่อน (idle แล้ว walk) เพื่อไม่ยิง _up_idle_ ฯลฯ โดยไม่จำเป็น * ถ้า down ไม่มีค่อยลอง up / left / right * @param {string} safeId * @param {boolean} preferIdle */ function characterSpriteCandidateUrls(safeId, preferIdle) { if (!safeId) return []; var enc = encodeURIComponent(safeId); var q = '?ch=' + encodeURIComponent(safeId); var dirsPrimary = ['down']; var dirsFallback = ['up', 'left', 'right']; var wantIdle = preferIdle !== false; var urls = []; function pushForDir(d) { if (wantIdle) { urls.push(SERVER + '/img/characters/' + enc + '_' + d + '_idle.png' + q); urls.push(SERVER + '/img/characters/' + enc + '_' + d + '_idle_0.png' + q); } urls.push(SERVER + '/img/characters/' + enc + '_' + d + '.png' + q); urls.push(SERVER + '/img/characters/' + enc + '_' + d + '_0.png' + q); } for (var a = 0; a < dirsPrimary.length; a++) pushForDir(dirsPrimary[a]); for (var b = 0; b < dirsFallback.length; b++) pushForDir(dirsFallback[b]); return urls; } /** หลังลองครบแล้วไม่มีรูป — ลดการยิงซ้ำในเซสชัน (ไม่บล็อกการลองใหม่หลังอัปโหลดไฟล์แล้ว — ลบคีย์ตอนโหลดสำเร็จ) */ var CHAR_ASSET_MISS_PREFIX = 'jdCharAssetMissing:'; function markCharacterDownAssetMissing(safeId) { if (!safeId) return; try { sessionStorage.setItem(CHAR_ASSET_MISS_PREFIX + safeId, '1'); } catch (e) { /* ignore */ } } function clearCharacterDownAssetMissing(safeId) { if (!safeId) return; try { sessionStorage.removeItem(CHAR_ASSET_MISS_PREFIX + safeId); } catch (e) { /* ignore */ } } /** * ลอง URL ตามลำดับด้วย Image — ตรงกับพฤติกรรม ใน character-grid * @param {string[]} urls * @param {(url: string | null) => void} cb */ function probeFirstLoadableImageUrl(urls, cb) { var i = 0; function next() { if (i >= urls.length) { cb(null); return; } var u = urls[i++]; var img = new Image(); img.onload = function () { if (img.naturalWidth > 0) cb(u); else next(); }; img.onerror = function () { next(); }; img.src = u; } next(); } /** * @param {string} safeId * @param {(url: string | null) => void} cb */ function characterLobbySpriteFirstLiveUrl(safeId, cb) { if (!safeId) { cb(null); return; } var urls = characterSpriteCandidateUrls(safeId, true); if (!urls.length) { cb(null); return; } probeFirstLoadableImageUrl(urls, function (url) { if (url) { clearCharacterDownAssetMissing(safeId); cb(url); } else { markCharacterDownAssetMissing(safeId); cb(null); } }); } /** * id จาก localStorage หรือถ้าว่าง — ตัวสุดท้ายในรายการ API (ใหม่สุดตามลำดับเซิร์ฟ) * @param {(safeId: string) => void} callback */ function resolveLobbyCharacterId(callback) { var norm = normalizeCharacterAssetId(getSelectedCharacterId()); if (norm) { callback(norm); return; } fetch(BASE + '/api/characters', { credentials: 'same-origin', cache: 'no-store' }) .then(function (r) { return r.json(); }) .then(function (list) { if (!Array.isArray(list) || !list.length) { callback(''); return; } var last = list[list.length - 1]; var id = last && last.id ? normalizeCharacterAssetId(String(last.id)) : ''; callback(id || ''); }) .catch(function () { callback(''); }); } var PLAYER_KEY = 'jdPlayerKey'; var DISPLAY_NAME_STORAGE_KEY = 'roomLobbyDisplayName'; function ensurePlayerKey() { var k; try { k = localStorage.getItem(PLAYER_KEY); } catch (e) { k = ''; } if (!k || String(k).length < 8) { k = 'p_' + Date.now() + '_' + Math.random().toString(36).slice(2, 14); try { localStorage.setItem(PLAYER_KEY, k); } catch (e2) { /* ignore */ } } return k; } /** ดึง COINS จากเซิร์ฟ (บัญชี guest สร้างอัตโนมัติถ้ายังไม่มี) */ function syncCoinsFromServer() { var key = ensurePlayerKey(); fetch((typeof appPath === 'function' ? appPath('/Admin/api/player-coins.php') : '/Admin/api/player-coins.php') + '?playerKey=' + encodeURIComponent(key), { credentials: 'omit' }) .then(function (r) { return r.json(); }) .then(function (d) { if (!d || !d.ok) return; var c = String(Math.max(0, parseInt(d.coins, 10) || 0)); try { localStorage.setItem('jdCoins', c); } catch (e) { /* ignore */ } var coinsEl = document.getElementById('lobby-profile-coins'); if (coinsEl) coinsEl.textContent = c; }) .catch(function () { /* offline */ }); } /** รีเฟรชหลังเลือกตัวละคร / สลับแท็บ / ย้อนกลับบน tablet (bfcache) */ function syncMainLobbyCharacterUi() { applyProfileTexts(); syncCoinsFromServer(); function applyCharAssets(urlOrNull) { applyProfileAvatar(urlOrNull); applyCenterCharacterResolved(urlOrNull); } resolveLobbyCharacterId(function (id) { if (!id) { applyCharAssets(null); return; } var idleCached = getStoredLobbyIdleDownDataUrl(id); if (idleCached) { applyCharAssets(idleCached); return; } characterLobbySpriteFirstLiveUrl(id, applyCharAssets); }); } function getProfileDisplayName() { if (typeof window.jdReadDisplayName === 'function') { return window.jdReadDisplayName(); } var fromRoomLobby = ''; var fromPlayerName = ''; try { fromRoomLobby = (localStorage.getItem(DISPLAY_NAME_STORAGE_KEY) || '').trim(); fromPlayerName = (localStorage.getItem('playerName') || '').trim(); } catch (e) { /* ignore */ } return fromRoomLobby || fromPlayerName || 'MONE'; } function applyProfileTexts() { var name = getProfileDisplayName(); var coins = localStorage.getItem('jdCoins') || '0'; var nameEl = document.getElementById('lobby-profile-name'); var agentEl = document.getElementById('lobby-profile-agent-id'); var coinsEl = document.getElementById('lobby-profile-coins'); if (nameEl) nameEl.textContent = name.toUpperCase(); if (agentEl) agentEl.textContent = ensureAgentId(); if (coinsEl) coinsEl.textContent = coins; } /** @param {string | null} urlOrNull — URL ที่ HEAD ผ่านแล้ว หรือ null = ใช้ char-main */ function applyProfileAvatar(urlOrNull) { var av = document.getElementById('lobby-profile-avatar'); if (!av) return; var fb = mainMenuCharFallbackUrl(); av.onerror = null; if (!urlOrNull) { av.src = fb; return; } av.onerror = function () { av.onerror = null; av.src = fb; }; av.src = urlOrNull; } function applyCenterCharacterResolved(urlOrNull) { var centerEl = document.getElementById('lobby-character-img'); if (!centerEl) return; function showSceneOnly() { centerEl.classList.remove('lobby-character-img--visible'); centerEl.removeAttribute('src'); } function revealChar() { centerEl.classList.add('lobby-character-img--visible'); } function showFallbackCenterCharacter() { var fb = mainMenuCharFallbackUrl(); centerEl.onload = revealChar; centerEl.onerror = function () { centerEl.onerror = null; showSceneOnly(); }; centerEl.src = fb; if (centerEl.complete && centerEl.naturalWidth > 0) { revealChar(); } } function showWithChar(url) { centerEl.onload = revealChar; centerEl.onerror = function () { showFallbackCenterCharacter(); }; centerEl.src = url; if (centerEl.complete && centerEl.naturalWidth > 0) { revealChar(); } } if (!urlOrNull) { showFallbackCenterCharacter(); return; } showWithChar(urlOrNull); } // คำนวณ offset จากความสูงจอด้วย linear interpolation: // 1080 -> 18cqh, 650 -> 25cqh function applyCharacterFootOffsetByViewport() { var root = document.documentElement; if (!root) return; var h = Math.max(1, window.innerHeight || 0); var h1 = 650; var y1 = 25; var h2 = 1080; var y2 = 18; var t = (h - h1) / (h2 - h1); var cqhValue = y1 + ((y2 - y1) * t); // กันหลุดช่วงเมื่อจอเล็ก/ใหญ่เกินช่วงอ้างอิง cqhValue = Math.max(18, Math.min(25, cqhValue)); root.style.setProperty('--lobby-character-foot-offset', cqhValue.toFixed(3) + 'cqh'); } function syncLeaderboardScrollbarThumb() { var scrollEl = document.querySelector('.lobby-leaderboard-scroll'); var thumbEl = document.getElementById('lobby-leaderboard-scroll-thumb'); var trackEl = document.getElementById('lobby-leaderboard-scroll-track'); if (!scrollEl || !thumbEl) return; var styles = window.getComputedStyle(thumbEl); var topInset = parseFloat(styles.top) || 0; var minThumb = parseFloat(styles.minHeight) || 18; var maxScroll = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight); if (maxScroll <= 0) { thumbEl.style.display = 'none'; if (trackEl) trackEl.style.display = 'none'; return; } if (trackEl) trackEl.style.display = 'block'; thumbEl.style.display = 'block'; var trackHeight = (trackEl && trackEl.clientHeight) ? trackEl.clientHeight : scrollEl.clientHeight; var visibleRatio = scrollEl.clientHeight / Math.max(1, scrollEl.scrollHeight); var thumbHeight = Math.max(minThumb, Math.round(trackHeight * visibleRatio)); var maxY = Math.max(0, trackHeight - topInset - thumbHeight); var y = topInset + (maxY * (scrollEl.scrollTop / maxScroll)); thumbEl.style.height = thumbHeight + 'px'; thumbEl.style.transform = 'translateY(' + y.toFixed(2) + 'px)'; } // วาง leaderboard ให้ต่อจากปุ่ม daily โดยไม่ทับกัน function placeLeaderboardBelowDaily() { var dailyBtn = document.getElementById('btn-daily'); var leaderboard = document.querySelector('.lobby-leaderboard'); var lobbyUi = document.querySelector('.lobby-ui'); if (!dailyBtn || !leaderboard || !lobbyUi) return; var boardStyle = window.getComputedStyle(leaderboard); if (boardStyle.position !== 'absolute') { leaderboard.style.top = ''; return; } var uiRect = lobbyUi.getBoundingClientRect(); var dailyRect = dailyBtn.getBoundingClientRect(); var boardRect = leaderboard.getBoundingClientRect(); var cssGap = boardStyle.getPropertyValue('--lobby-daily-leaderboard-gap').trim(); var gapPx = 8; if (cssGap) { var n = parseFloat(cssGap); if (!Number.isNaN(n)) { if (cssGap.endsWith('cqh')) gapPx = (uiRect.height * n) / 100; else if (cssGap.endsWith('px') || /^[+-]?\d+(\.\d+)?$/.test(cssGap)) gapPx = n; else if (cssGap.endsWith('vh')) gapPx = ((window.innerHeight || 0) * n) / 100; else if (cssGap.endsWith('%')) gapPx = (uiRect.height * n) / 100; } } var nextTop = (dailyRect.bottom - uiRect.top) + gapPx; var maxTop = Math.max(0, uiRect.height - boardRect.height - Math.max(0, gapPx)); var clampedTop = Math.max(0, Math.min(nextTop, maxTop)); leaderboard.style.top = clampedTop.toFixed(2) + 'px'; } var leaderboardLayoutRaf = 0; function scheduleLeaderboardPlacement() { if (leaderboardLayoutRaf) cancelAnimationFrame(leaderboardLayoutRaf); leaderboardLayoutRaf = requestAnimationFrame(function () { leaderboardLayoutRaf = 0; placeLeaderboardBelowDaily(); syncLeaderboardScrollbarThumb(); }); } function bindLeaderboardScrollSync() { var scrollEl = document.querySelector('.lobby-leaderboard-scroll'); if (!scrollEl || scrollEl.dataset.scrollSyncBound === '1') return; scrollEl.addEventListener('scroll', syncLeaderboardScrollbarThumb, { passive: true }); scrollEl.dataset.scrollSyncBound = '1'; } function bindLeaderboardWheelSupport() { var scrollEl = document.querySelector('.lobby-leaderboard-scroll'); if (!scrollEl || scrollEl.dataset.wheelSupportBound === '1') return; scrollEl.addEventListener('wheel', function (e) { var maxScroll = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight); if (maxScroll <= 0) return; scrollEl.scrollTop += e.deltaY; syncLeaderboardScrollbarThumb(); e.preventDefault(); }, { passive: false }); scrollEl.dataset.wheelSupportBound = '1'; } function bindLeaderboardThumbDrag() { var scrollEl = document.querySelector('.lobby-leaderboard-scroll'); var thumbEl = document.getElementById('lobby-leaderboard-scroll-thumb'); var trackEl = document.getElementById('lobby-leaderboard-scroll-track'); if (!scrollEl || !thumbEl || !trackEl || thumbEl.dataset.dragBound === '1') return; var dragging = false; var dragOffsetY = 0; function getThumbMetrics() { var styles = window.getComputedStyle(thumbEl); var topInset = parseFloat(styles.top) || 0; var thumbHeight = thumbEl.clientHeight || parseFloat(thumbEl.style.height) || 18; var trackHeight = trackEl.clientHeight || scrollEl.clientHeight || 1; var maxTranslate = Math.max(topInset, trackHeight - thumbHeight); var travel = Math.max(1, maxTranslate - topInset); return { topInset: topInset, thumbHeight: thumbHeight, trackHeight: trackHeight, maxTranslate: maxTranslate, travel: travel }; } function stopDrag() { if (!dragging) return; dragging = false; thumbEl.classList.remove('is-dragging'); document.body.classList.remove('lobby-leaderboard-dragging'); } thumbEl.addEventListener('mousedown', function (e) { if (e.button !== 0) return; var maxScroll = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight); if (maxScroll <= 0) return; var metrics = getThumbMetrics(); var thumbRect = thumbEl.getBoundingClientRect(); dragOffsetY = Math.max(0, Math.min(metrics.thumbHeight, e.clientY - thumbRect.top)); dragging = true; thumbEl.classList.add('is-dragging'); document.body.classList.add('lobby-leaderboard-dragging'); e.preventDefault(); e.stopPropagation(); }); window.addEventListener('mousemove', function (e) { if (!dragging) return; var maxScroll = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight); if (maxScroll <= 0) { stopDrag(); return; } var metrics = getThumbMetrics(); var trackRect = trackEl.getBoundingClientRect(); var pointerY = e.clientY - trackRect.top; var nextTranslate = pointerY - dragOffsetY; nextTranslate = Math.max(metrics.topInset, Math.min(metrics.maxTranslate, nextTranslate)); var ratio = (nextTranslate - metrics.topInset) / metrics.travel; scrollEl.scrollTop = ratio * maxScroll; syncLeaderboardScrollbarThumb(); e.preventDefault(); }); window.addEventListener('mouseup', stopDrag); window.addEventListener('blur', stopDrag); thumbEl.dataset.dragBound = '1'; } function bindLeaderboardDragScroll() { var scrollEl = document.querySelector('.lobby-leaderboard-scroll'); if (!scrollEl || scrollEl.dataset.dragScrollBound === '1') return; var dragging = false; var moved = false; var startY = 0; var startScrollTop = 0; function stopDrag() { if (!dragging) return; dragging = false; window.setTimeout(function () { moved = false; }, 0); document.body.classList.remove('lobby-leaderboard-dragging'); scrollEl.classList.remove('is-dragging'); } scrollEl.addEventListener('mousedown', function (e) { if (e.button !== 0) return; var maxScroll = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight); if (maxScroll <= 0) return; dragging = true; moved = false; startY = e.clientY; startScrollTop = scrollEl.scrollTop; document.body.classList.add('lobby-leaderboard-dragging'); scrollEl.classList.add('is-dragging'); e.preventDefault(); }); window.addEventListener('mousemove', function (e) { if (!dragging) return; var deltaY = e.clientY - startY; if (Math.abs(deltaY) > 2) moved = true; scrollEl.scrollTop = startScrollTop - deltaY; syncLeaderboardScrollbarThumb(); e.preventDefault(); }); window.addEventListener('mouseup', stopDrag); window.addEventListener('blur', stopDrag); scrollEl.addEventListener('mouseleave', function (e) { if (!dragging) return; if (e.buttons === 0) stopDrag(); }); scrollEl.addEventListener('click', function (e) { if (!moved) return; e.preventDefault(); e.stopPropagation(); }, true); scrollEl.dataset.dragScrollBound = '1'; } var guideStep = 1; var guideImg = document.getElementById('guide-img'); var guideBar = document.getElementById('lobby-guide-bar'); var guideDim = document.getElementById('lobby-guide-dim'); var guideFocus = document.getElementById('lobby-guide-focus'); function getGuideStepConfig(step) { return GUIDE_STEPS[step - 1] || { position: 'bottom', highlight: null, pad: 10 }; } function hideGuideFocus() { if (!guideFocus) return; guideFocus.classList.add('hidden'); guideFocus.style.left = '0'; guideFocus.style.top = '0'; guideFocus.style.width = '0'; guideFocus.style.height = '0'; } function scheduleGuideFocusUpdate() { requestAnimationFrame(function () { requestAnimationFrame(updateGuideFocus); }); } function updateGuideFocus() { if (!guideFocus || !guideBar || guideBar.classList.contains('hidden')) { hideGuideFocus(); return; } var cfg = getGuideStepConfig(guideStep); if (!cfg.highlight) { hideGuideFocus(); return; } var target = document.querySelector(cfg.highlight); if (!target) { hideGuideFocus(); return; } var pad = typeof cfg.pad === 'number' ? cfg.pad : 8; var r = target.getBoundingClientRect(); if (r.width < 4 || r.height < 4) { hideGuideFocus(); return; } var cs = window.getComputedStyle(target); var br = parseInt(cs.borderRadius, 10) || 0; var radius = Math.max(br + 4, 14); guideFocus.classList.remove('hidden'); guideFocus.style.left = (r.left - pad) + 'px'; guideFocus.style.top = (r.top - pad) + 'px'; guideFocus.style.width = (r.width + pad * 2) + 'px'; guideFocus.style.height = (r.height + pad * 2) + 'px'; guideFocus.style.borderRadius = radius + 'px'; } function syncGuideBodyClass() { if (!guideBar) return; var open = !guideBar.classList.contains('hidden'); if (open) { document.body.classList.add('lobby-guide-active'); var cfg = getGuideStepConfig(guideStep); document.body.classList.toggle('lobby-guide-top-step', cfg.position === 'top'); var hasFocus = !!cfg.highlight; if (guideDim) guideDim.classList.toggle('hidden', hasFocus); } else { document.body.classList.remove('lobby-guide-active'); document.body.classList.remove('lobby-guide-top-step'); if (guideDim) guideDim.classList.add('hidden'); } if (open) { scheduleGuideFocusUpdate(); } else { hideGuideFocus(); } } function showGuideStep(n) { if (!guideImg || !guideBar) return; if (n < 1 || n > MAX_GUIDE) { guideBar.classList.add('hidden'); guideBar.classList.remove('lobby-guide-bar--top'); hideGuideFocus(); try { localStorage.setItem(GUIDE_KEY, '1'); } catch (e) { /* ignore */ } syncGuideBodyClass(); return; } guideStep = n; var cfg = getGuideStepConfig(guideStep); guideImg.src = ML_GUIDE + '/guide-' + (n < 10 ? '0' + n : String(n)) + '.png'; guideImg.alt = 'คำแนะนำ ขั้นที่ ' + n; guideBar.classList.toggle('lobby-guide-bar--top', cfg.position === 'top'); var nextBtn = document.getElementById('btn-guide-next'); if (nextBtn) { nextBtn.setAttribute('aria-label', n >= MAX_GUIDE ? 'รับทราบ' : 'ถัดไป'); } syncGuideBodyClass(); guideImg.onload = function () { scheduleGuideFocusUpdate(); }; guideImg.onerror = function () { scheduleGuideFocusUpdate(); }; scheduleGuideFocusUpdate(); } function initGuide() { if (!guideBar || !guideImg) return; try { if (localStorage.getItem(GUIDE_KEY) === '1') { guideBar.classList.add('hidden'); syncGuideBodyClass(); return; } } catch (e) { /* ignore */ } guideBar.classList.remove('hidden'); showGuideStep(1); } document.getElementById('btn-guide-next')?.addEventListener('click', function () { if (guideStep >= MAX_GUIDE) { if (guideBar) { guideBar.classList.add('hidden'); guideBar.classList.remove('lobby-guide-bar--top'); } try { localStorage.setItem(GUIDE_KEY, '1'); } catch (e) { /* ignore */ } syncGuideBodyClass(); return; } showGuideStep(guideStep + 1); }); document.addEventListener('keydown', function (e) { if (e.key !== 'Escape') return; if (!guideBar || guideBar.classList.contains('hidden')) return; e.preventDefault(); guideBar.classList.add('hidden'); guideBar.classList.remove('lobby-guide-bar--top'); hideGuideFocus(); syncGuideBodyClass(); }); window.addEventListener('resize', function () { if (guideBar && !guideBar.classList.contains('hidden')) { scheduleGuideFocusUpdate(); } }); window.addEventListener('orientationchange', function () { setTimeout(scheduleGuideFocusUpdate, 350); }); /* ใช้ capture + data-href บน .lobby-footer-center — ลดโอกาสถูกเลเยอร์อื่นแย่งคลิกก่อนถึงปุ่ม */ var footerCenter = document.querySelector('.lobby-footer-center'); if (footerCenter) { footerCenter.addEventListener('click', function (e) { var btn = e.target.closest('button[data-href]'); if (!btn || !footerCenter.contains(btn)) return; var href = btn.getAttribute('data-href'); if (!href) return; e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); window.location.href = href; }, true); } document.getElementById('btn-cloth')?.addEventListener('click', function () { if (typeof window.openCustomizePopup === 'function') { window.openCustomizePopup(); return; } window.location.href = BASE + '/character.html'; }); document.getElementById('btn-daily')?.addEventListener('click', function () { if (typeof window.openDailyPopup === 'function') { window.openDailyPopup(); return; } toast('รางวัลประจำวัน — เข้าเกมทุกวันเพื่อรับรางวัลต่อเนื่อง!'); }); document.getElementById('btn-myprofile')?.addEventListener('click', function () { if (typeof window.openProfilePopup === 'function') { window.openProfilePopup(); return; } window.location.href = typeof appPath === 'function' ? appPath('/Main-Menu/') : '/Main-Menu/'; }); function getMlAiSessionId() { try { var k = 'mlAiChatSession'; var v = localStorage.getItem(k); if (!v) { v = 'ml-' + Date.now() + '-' + Math.random().toString(36).slice(2, 11); localStorage.setItem(k, v); } return v; } catch (e) { return 'ml-' + String(Date.now()); } } var aiChatCloseBtn = document.getElementById('lobby-ai-chat-close-btn'); var aiChatPanel = document.getElementById('lobby-ai-chat-panel'); var aiChatToggleImg = document.getElementById('lobby-ai-chat-toggle-img'); var btnAiChat = document.getElementById('btn-ai-chat'); function syncAiChatPanelUi() { if (!aiChatPanel) return; var hidden = aiChatPanel.classList.contains('lobby-ai-chat-hidden'); if (btnAiChat) btnAiChat.style.display = hidden ? '' : 'none'; if (hidden || !aiChatToggleImg || !aiChatCloseBtn) return; aiChatToggleImg.src = BASE + '/img/chat-close-btn.png'; aiChatToggleImg.alt = 'ปิด'; aiChatCloseBtn.setAttribute('title', 'ปิดแชท AI'); aiChatCloseBtn.setAttribute('aria-label', 'ปิดแชท AI'); } if (aiChatCloseBtn && aiChatPanel) { aiChatCloseBtn.addEventListener('click', function () { aiChatPanel.classList.add('lobby-ai-chat-hidden'); aiChatPanel.classList.remove('chat-panel-collapsed'); syncAiChatPanelUi(); }); } if (btnAiChat && aiChatPanel) { btnAiChat.addEventListener('click', function () { if (aiChatPanel.classList.contains('lobby-ai-chat-hidden')) { aiChatPanel.classList.remove('lobby-ai-chat-hidden'); aiChatPanel.classList.remove('chat-panel-collapsed'); syncAiChatPanelUi(); var inp = document.getElementById('lobby-ai-chat-input'); if (inp) { try { inp.focus(); } catch (e) { /* ignore */ } } } else { aiChatPanel.classList.add('lobby-ai-chat-hidden'); syncAiChatPanelUi(); } }); } syncAiChatPanelUi(); var aiChatForm = document.getElementById('lobby-ai-chat-form'); var aiChatInput = document.getElementById('lobby-ai-chat-input'); var aiChatSendBtn = aiChatForm ? aiChatForm.querySelector('button[type="submit"]') : null; var aiChatLoadingEl = null; function appendAiMessage(text, isAi) { var el = document.getElementById('lobby-ai-chat-messages'); if (!el) return; if (isAi) removeAiChatLoading(); var div = document.createElement('div'); div.className = 'lobby-ai-chat-msg ' + (isAi ? 'lobby-ai-chat-msg-ai' : 'lobby-ai-chat-msg-user'); div.textContent = (isAi ? 'AI: ' : '') + text; el.appendChild(div); el.scrollTop = 1e9; } function showAiChatLoading() { var el = document.getElementById('lobby-ai-chat-messages'); if (!el || aiChatLoadingEl) return; aiChatLoadingEl = document.createElement('div'); aiChatLoadingEl.className = 'lobby-ai-chat-msg lobby-ai-chat-msg-ai lobby-ai-chat-typing'; aiChatLoadingEl.setAttribute('aria-label', 'กำลังพิมพ์'); aiChatLoadingEl.innerHTML = ''; el.appendChild(aiChatLoadingEl); el.scrollTop = 1e9; } function removeAiChatLoading() { if (aiChatLoadingEl && aiChatLoadingEl.parentNode) { aiChatLoadingEl.parentNode.removeChild(aiChatLoadingEl); aiChatLoadingEl = null; } } function setAiChatWaiting(waiting) { if (aiChatInput) aiChatInput.disabled = waiting; if (aiChatSendBtn) aiChatSendBtn.disabled = waiting; if (waiting) showAiChatLoading(); else removeAiChatLoading(); } if (aiChatForm && aiChatInput) { aiChatForm.addEventListener('submit', function (e) { e.preventDefault(); var text = (aiChatInput.value || '').trim(); if (!text) return; appendAiMessage(text, false); setAiChatWaiting(true); aiChatInput.value = ''; fetch(SERVER + '/api/ai-chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text, sessionId: getMlAiSessionId() }), }) .then(function (r) { return r.json(); }) .then(function (data) { if (data.response != null) appendAiMessage(data.response, true); else if (data.error) appendAiMessage('[ข้อผิดพลาด: ' + data.error + ']', true); }) .catch(function () { appendAiMessage('[ส่งไม่สำเร็จ]', true); }) .finally(function () { setAiChatWaiting(false); }); }); } var howto = document.getElementById('lobby-howto-overlay'); function openHowto() { if (howto) howto.classList.remove('hidden'); } function closeHowto() { if (howto) howto.classList.add('hidden'); } /** ปุ่ม ? — คู่มือทีละขั้น ตาม /Main-Lobby/IMAGE/guide */ function openGuideTutorial() { if (!guideBar || !guideImg) return; closeHowto(); guideBar.classList.remove('hidden'); showGuideStep(1); } document.getElementById('btn-tutorial')?.addEventListener('click', openGuideTutorial); document.getElementById('btn-howto-got-it')?.addEventListener('click', closeHowto); howto?.addEventListener('click', function (e) { if (e.target === howto) closeHowto(); }); applyCharacterFootOffsetByViewport(); syncMainLobbyCharacterUi(); bindLeaderboardScrollSync(); bindLeaderboardWheelSupport(); bindLeaderboardDragScroll(); bindLeaderboardThumbDrag(); scheduleLeaderboardPlacement(); initGuide(); window.addEventListener('pageshow', function () { applyCharacterFootOffsetByViewport(); syncMainLobbyCharacterUi(); scheduleLeaderboardPlacement(); }); window.addEventListener('resize', function () { applyCharacterFootOffsetByViewport(); bindLeaderboardScrollSync(); bindLeaderboardWheelSupport(); bindLeaderboardDragScroll(); bindLeaderboardThumbDrag(); scheduleLeaderboardPlacement(); }); window.addEventListener('orientationchange', function () { setTimeout(function () { applyCharacterFootOffsetByViewport(); scheduleLeaderboardPlacement(); }, 250); }); window.addEventListener('load', function () { bindLeaderboardScrollSync(); bindLeaderboardWheelSupport(); bindLeaderboardDragScroll(); bindLeaderboardThumbDrag(); scheduleLeaderboardPlacement(); }); document.addEventListener('visibilitychange', function () { if (document.visibilityState === 'visible') { applyCharacterFootOffsetByViewport(); syncMainLobbyCharacterUi(); scheduleLeaderboardPlacement(); } }); window.addEventListener('storage', function (e) { if (e.key == null || e.key === CHAR_KEY || e.key === 'playerName' || e.key === DISPLAY_NAME_STORAGE_KEY) { syncMainLobbyCharacterUi(); return; } if (e.key && String(e.key).indexOf(LOBBY_IDLE_DOWN_PREFIX) === 0) { syncMainLobbyCharacterUi(); } }); })();