From ede5174a2fa7bf98eb068e515f1e249793b9d70a Mon Sep 17 00:00:00 2001 From: giteaadmin Date: Tue, 5 May 2026 17:34:57 +0000 Subject: [PATCH] minigame 5 add more design 1.1 --- www/html/Game/public/js/play.js | 97 ++++++++++++++++++++++++------ www/html/Game/public/js/version.js | 2 +- www/html/Game/public/play.html | 10 +-- www/html/Game/server.js | 18 ++++-- 4 files changed, 99 insertions(+), 28 deletions(-) diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index 1d66fc9..5aea5f1 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -4660,6 +4660,27 @@ : (humansReady ? 'START (โฮสต์เท่านั้น)' : 'READY (โฮสต์เท่านั้น)'); } + /** Jump Survival mnptfts2 — Ready Status / ปุ่ม เดียวกับ Stack Tower */ + function updateJumpSurviveMissionHowtoHud() { + if (!isJumpSurviveMissionUiMapPlay() || jumpSurviveMissionPhase !== 'howto') return; + const st = document.getElementById('gauntlet-crown-howto-status'); + const btn = document.getElementById('btn-gch-ready'); + if (!st || !btn) return; + const humans = quizCarryPregameHumanIds(); + const tot = Math.max(1, quizCarryPregameTotalPlayers()); + const num = gauntletCrownPregameReadyNumerator(); + st.classList.remove('is-hidden'); + st.textContent = 'Ready Status : ' + num + '/' + tot; + const humansReady = humans.length > 0 && humans.every((id) => !!gauntletCrownLobbyReadyMap[id]); + btn.classList.toggle('is-start-phase', humansReady); + btn.classList.toggle('is-read-only', !isMePlayHost()); + btn.disabled = !isMePlayHost(); + btn.setAttribute('aria-pressed', humansReady ? 'false' : ((myId && gauntletCrownLobbyReadyMap[String(myId)]) ? 'true' : 'false')); + btn.title = isMePlayHost() + ? (humansReady ? 'START' : ((myId && gauntletCrownLobbyReadyMap[String(myId)]) ? 'ยกเลิก READY' : 'READY')) + : (humansReady ? 'START (โฮสต์เท่านั้น)' : 'READY (โฮสต์เท่านั้น)'); + } + function beginStackTowerMissionCountdownThenRun() { if (!isStackTowerMissionUiMapPlay()) return; if (stackTowerMissionCountdownTimer) { @@ -4960,21 +4981,28 @@ gauntletCrownHowtoVisible = true; ov.classList.remove('is-hidden'); const st = document.getElementById('gauntlet-crown-howto-status'); - if (st) { - st.textContent = ''; - st.classList.remove('gch-status--jumper'); - st.classList.add('is-hidden'); - } + if (st) st.classList.remove('gch-status--jumper'); const btn = document.getElementById('btn-gch-ready'); - if (btn) { - const humans = quizCarryPregameHumanIds(); - const soleHuman = humans.length === 1; - const canGo = soleHuman || isMePlayHost(); - btn.classList.remove('is-start-phase'); - btn.classList.toggle('is-read-only', !canGo); - btn.disabled = !canGo; - btn.title = canGo ? 'READY — เริ่มนับถอยหลัง' : 'รอโฮสต์ · Wait for host'; - btn.setAttribute('aria-pressed', 'false'); + const humans = quizCarryPregameHumanIds(); + const totPlayers = Math.max(1, quizCarryPregameTotalPlayers()); + const soleParticipant = totPlayers === 1; + if (soleParticipant) { + if (st) { + st.textContent = ''; + st.classList.add('is-hidden'); + } + if (btn) { + const canGo = humans.length === 1 || isMePlayHost(); + btn.classList.remove('is-start-phase'); + btn.classList.toggle('is-read-only', !canGo); + btn.disabled = !canGo; + btn.title = canGo ? 'READY — เริ่มนับถอยหลัง' : 'รอโฮสต์ · Wait for host'; + btn.setAttribute('aria-pressed', 'false'); + } + } else { + if (socket && socket.connected) socket.emit('gauntlet-crown-lobby-sync-request'); + gauntletCrownSyncGuestReadyIfNeeded(); + updateJumpSurviveMissionHowtoHud(); } } @@ -9439,7 +9467,8 @@ function gauntletCrownSyncGuestReadyIfNeeded() { const inQuizQuestionHowto = isQuizQuestionMissionUiMapPlay() && quizQuestionMissionPhase === 'howto'; const inStackTowerHowto = isStackTowerMissionUiMapPlay() && stackTowerMissionPhase === 'howto'; - if (gauntletCrownPregamePhase !== 'howto' && !inQuizQuestionHowto && !inStackTowerHowto) return; + const inJumpSurviveHowto = isJumpSurviveMissionUiMapPlay() && jumpSurviveMissionPhase === 'howto'; + if (gauntletCrownPregamePhase !== 'howto' && !inQuizQuestionHowto && !inStackTowerHowto && !inJumpSurviveHowto) return; if (myId == null || isMePlayHost()) return; const sid = String(myId); if (gauntletCrownLobbyReadyMap[sid]) return; @@ -12997,12 +13026,14 @@ if (quizCarryPregameActive && isQuizCarry()) updateQuizCarryPregameHud(); if (isQuizQuestionMissionUiMapPlay() && quizQuestionMissionPhase === 'howto') updateQuizQuestionMissionHowtoHud(); if (isStackTowerMissionUiMapPlay() && stackTowerMissionPhase === 'howto') updateStackTowerMissionHowtoHud(); + if (isJumpSurviveMissionUiMapPlay() && jumpSurviveMissionPhase === 'howto') updateJumpSurviveMissionHowtoHud(); }); socket.on('host-changed', (d) => { if (d && d.hostId != null) playHostId = d.hostId; if (quizCarryPregameActive && isQuizCarry()) updateQuizCarryPregameHud(); if (isQuizQuestionMissionUiMapPlay() && quizQuestionMissionPhase === 'howto') updateQuizQuestionMissionHowtoHud(); if (isStackTowerMissionUiMapPlay() && stackTowerMissionPhase === 'howto') updateStackTowerMissionHowtoHud(); + if (isJumpSurviveMissionUiMapPlay() && jumpSurviveMissionPhase === 'howto') updateJumpSurviveMissionHowtoHud(); }); socket.on('quiz-carry-lobby-sync', (d) => { if (!d || typeof d.readyMap !== 'object') return; @@ -13028,6 +13059,9 @@ } else if (isStackTowerMissionUiMapPlay() && stackTowerMissionPhase === 'howto') { gauntletCrownSyncGuestReadyIfNeeded(); updateStackTowerMissionHowtoHud(); + } else if (isJumpSurviveMissionUiMapPlay() && jumpSurviveMissionPhase === 'howto') { + gauntletCrownSyncGuestReadyIfNeeded(); + updateJumpSurviveMissionHowtoHud(); } }); socket.on('gauntlet-crown-lobby-started', () => { @@ -13039,6 +13073,10 @@ beginStackTowerMissionCountdownThenRun(); return; } + if (isJumpSurviveMissionUiMapPlay() && jumpSurviveMissionPhase === 'howto') { + beginJumpSurviveMissionCountdownThenRun(); + return; + } if (!usesCrownLobbyShellPlay()) return; beginGauntletCrownCountdownThenRun(); }); @@ -13147,6 +13185,7 @@ if (previewFillBots) rebalancePreviewBots(); if (isQuizQuestionMissionUiMapPlay() && quizQuestionMissionPhase === 'howto') updateQuizQuestionMissionHowtoHud(); if (isStackTowerMissionUiMapPlay() && stackTowerMissionPhase === 'howto') updateStackTowerMissionHowtoHud(); + if (isJumpSurviveMissionUiMapPlay() && jumpSurviveMissionPhase === 'howto') updateJumpSurviveMissionHowtoHud(); }); socket.on('chat', (data) => { const box = document.getElementById('chat-messages'); @@ -16543,8 +16582,32 @@ if (isJumpSurviveMissionUiMapPlay()) { if (jumpSurviveMissionPhase !== 'howto') return; const humans = quizCarryPregameHumanIds(); - if (!(humans.length === 1 || isMePlayHost())) return; - beginJumpSurviveMissionCountdownThenRun(); + const totPlayers = Math.max(1, quizCarryPregameTotalPlayers()); + if (totPlayers === 1) { + if (!(humans.length === 1 || isMePlayHost())) return; + beginJumpSurviveMissionCountdownThenRun(); + return; + } + if (!isMePlayHost()) { + if (myId == null) return; + const sid = String(myId); + const next = !gauntletCrownLobbyReadyMap[sid]; + gauntletCrownLobbyReadyMap[sid] = next; + updateJumpSurviveMissionHowtoHud(); + if (socket && socket.connected) socket.emit('gauntlet-crown-lobby-ready', { ready: next }); + return; + } + if (myId == null) return; + const humansReady = humans.length > 0 && humans.every((id) => !!gauntletCrownLobbyReadyMap[id]); + if (humansReady) { + socket.emit('gauntlet-crown-lobby-start', {}, (r) => { if (!r || !r.ok) { /* ignore */ } }); + return; + } + const sidH = String(myId); + const nextH = !gauntletCrownLobbyReadyMap[sidH]; + gauntletCrownLobbyReadyMap[sidH] = nextH; + updateJumpSurviveMissionHowtoHud(); + if (socket && socket.connected) socket.emit('gauntlet-crown-lobby-ready', { ready: nextH }); return; } if (isSpaceShooterMissionUiMapPlay()) { diff --git a/www/html/Game/public/js/version.js b/www/html/Game/public/js/version.js index e261392..642fbbd 100644 --- a/www/html/Game/public/js/version.js +++ b/www/html/Game/public/js/version.js @@ -1,6 +1,6 @@ // ทุกครั้งที่มีการเพิ่มหรือเปลี่ยน ให้เพิ่ม v +0.0001 // หลังแก้ค่าแล้วต้อง copy ไป path ที่ Nginx ชี้ (หรือรัน copy-frogger-files-only.sh) ถึงจะเห็นบนเว็บ -window.APP_VERSION = '0.0300'; +window.APP_VERSION = '0.0301'; document.addEventListener('DOMContentLoaded', function () { var t = document.querySelector('.version-tag'); if (t) t.textContent = 'v ' + window.APP_VERSION; diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index a879696..d42f1d0 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -1338,10 +1338,10 @@ margin: 0; max-width: 100%; text-align: center; - font-weight: 800; + font: 800 clamp(0.75rem, 2vw, 0.9rem) / 1.35 ui-sans-serif, system-ui, 'Segoe UI', 'Kanit', sans-serif; color: #f8fafc; - font-size: clamp(0.75rem, 2vw, 0.9rem); - text-shadow: 0 1px 4px rgba(0, 0, 0, 0.85), 0 0 14px rgba(0, 0, 0, 0.55); + letter-spacing: 0.02em; + text-shadow: 0 1px 4px rgba(0, 0, 0, 0.85), 0 0 14px rgba(0, 0, 0, 0.55), 0 0 10px rgba(122, 248, 255, 0.18); } .gch-inner.gch-inner--art .btn-gch-ready { pointer-events: auto; @@ -3021,8 +3021,8 @@ - - + +
v —
diff --git a/www/html/Game/server.js b/www/html/Game/server.js index 2ea2133..3fd1889 100644 --- a/www/html/Game/server.js +++ b/www/html/Game/server.js @@ -15,6 +15,8 @@ const POST_CASE_LOBBY_SPACE_ID = 'mn8nx46h'; const SUSPECT_INVESTIGATION_QUIZ_MAP_ID = 'mng8a80o'; /** Stack Tower ภารกิจ — lobby READY / START เดียวกับ quiz mission (mng8a80o) */ const STACK_TOWER_MISSION_MAP_ID = 'mnn93hpi'; +/** Jump Survival ภารกิจ Jumper — lobby READY / START เดียวกับ Stack Tower */ +const JUMP_SURVIVE_MISSION_MAP_ID = 'mnptfts2'; /** ห้องสาธารณะที่สร้างแล้วไม่มีคนเข้าเกินนี้ → ลบออกจาก memory (Join Room ไม่โชว์ห้องผี) */ const SPACE_EMPTY_TTL_MS = 45000; // Chat completion models from https://developers.openai.com/api/docs/models (Feb 2025) @@ -2923,8 +2925,14 @@ function isStackTowerMissionShellSpace(space) { return !!(md && md.gameType === 'stack'); } +function isJumpSurviveMissionShellSpace(space) { + if (!space || !space.mapId || String(space.mapId) !== JUMP_SURVIVE_MISSION_MAP_ID) return false; + const md = (space.mapId && maps.get(space.mapId)) || space.mapData; + return !!(md && md.gameType === 'jump_survive'); +} + function isCrownLobbyShellSpace(space) { - return isGauntletCrownHeistMapBySpace(space) || isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space); + return isGauntletCrownHeistMapBySpace(space) || isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space) || isJumpSurviveMissionShellSpace(space); } function newBalloonBossShellGauntletRunState() { @@ -3718,7 +3726,7 @@ io.on('connection', (socket) => { io.to(spaceId).emit('gauntlet-crown-lobby-sync', { readyMap: { ...space.gauntletCrownLobbyReady } }); } startGauntletTicker(spaceId, space); - } else if (isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space)) { + } else if (isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space) || isJumpSurviveMissionShellSpace(space)) { if (!space.gauntletCrownLobbyReady || typeof space.gauntletCrownLobbyReady !== 'object') space.gauntletCrownLobbyReady = {}; space.gauntletCrownLobbyReady[socket.id] = false; if (!space.gauntletRun) space.gauntletRun = newBalloonBossShellGauntletRunState(); @@ -3767,7 +3775,7 @@ io.on('connection', (socket) => { if (mdJoin.gameType === 'gauntlet' && space.gauntletRun) { joinCb.gauntletEndsAt = space.gauntletRun.endsAt != null ? space.gauntletRun.endsAt : null; } - if ((isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space)) && space.gauntletRun) { + if ((isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space) || isJumpSurviveMissionShellSpace(space)) && space.gauntletRun) { joinCb.gauntletCrownRunHeld = !!space.gauntletRun.crownRunHeld; } if (typeof cb === 'function') cb(joinCb); @@ -4032,7 +4040,7 @@ io.on('connection', (socket) => { gauntletEliminated: !!p.gauntletEliminated, })); } - if (md && (isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space))) { + if (md && (isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space) || isJumpSurviveMissionShellSpace(space))) { if (!space.gauntletCrownLobbyReady || typeof space.gauntletCrownLobbyReady !== 'object') space.gauntletCrownLobbyReady = {}; space.peers.forEach((_p, id) => { space.gauntletCrownLobbyReady[id] = false; @@ -4047,7 +4055,7 @@ io.on('connection', (socket) => { peersSnap: peersSnap || undefined, }; if (peersSnap) gameStartPayload.gauntletEndsAt = gauntletEndsAtEmit != null ? gauntletEndsAtEmit : null; - if (mapId && maps.has(mapId) && (isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space))) { + if (mapId && maps.has(mapId) && (isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space) || isJumpSurviveMissionShellSpace(space))) { const grs = space.gauntletRun; gameStartPayload.gauntletCrownRunHeld = !!(grs && grs.crownRunHeld); }