diff --git a/www/html/Game/data/quiz-settings.json b/www/html/Game/data/quiz-settings.json index 75dc47e..d0425f5 100644 --- a/www/html/Game/data/quiz-settings.json +++ b/www/html/Game/data/quiz-settings.json @@ -10,16 +10,16 @@ "panelBorder": "rgba(255, 255, 255, 0)", "textColor": "rgba(241, 245, 249, 1)", "borderWidthPx": 0, - "questionFontMinPx": 10, + "questionFontMinPx": 17, "questionFontMaxPx": 24 }, "carryEmbedCountdownTheme": { "overlayBackdrop": "rgba(8, 10, 20, 0)", "innerBg": "rgba(12, 14, 28, 0)", - "innerBorder": "rgba(122, 162, 247, 0.45)", + "innerBorder": "rgba(122, 162, 247, 0)", "innerBorderWpx": 1, "innerRadiusPx": 12, - "digitColor": "#ffe066", + "digitColor": "#f2f2f2", "mapDigitCqmin": 78, "mapDigitCqh": 82, "mapDigitMaxPx": 200, diff --git a/www/html/Game/public/img/quiz-carry/.gitkeep b/www/html/Game/public/img/quiz-carry/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/html/Game/public/img/quiz-carry/Avartar-mark.png b/www/html/Game/public/img/quiz-carry/Avartar-mark.png new file mode 100644 index 0000000..45a6644 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/Avartar-mark.png differ diff --git a/www/html/Game/public/img/quiz-carry/Avartar.png b/www/html/Game/public/img/quiz-carry/Avartar.png new file mode 100644 index 0000000..dabf3ad Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/Avartar.png differ diff --git a/www/html/Game/public/img/quiz-carry/count-1.png b/www/html/Game/public/img/quiz-carry/count-1.png new file mode 100644 index 0000000..25610e3 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/count-1.png differ diff --git a/www/html/Game/public/img/quiz-carry/count-2.png b/www/html/Game/public/img/quiz-carry/count-2.png new file mode 100644 index 0000000..766441a Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/count-2.png differ diff --git a/www/html/Game/public/img/quiz-carry/count-3.png b/www/html/Game/public/img/quiz-carry/count-3.png new file mode 100644 index 0000000..3d28add Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/count-3.png differ diff --git a/www/html/Game/public/img/quiz-carry/count-4.png b/www/html/Game/public/img/quiz-carry/count-4.png new file mode 100644 index 0000000..acb5605 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/count-4.png differ diff --git a/www/html/Game/public/img/quiz-carry/count-5.png b/www/html/Game/public/img/quiz-carry/count-5.png new file mode 100644 index 0000000..b048aea Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/count-5.png differ diff --git a/www/html/Game/public/img/quiz-carry/icon-Correct.png b/www/html/Game/public/img/quiz-carry/icon-Correct.png new file mode 100644 index 0000000..a82eff1 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/icon-Correct.png differ diff --git a/www/html/Game/public/img/quiz-carry/icon-Incorrect.png b/www/html/Game/public/img/quiz-carry/icon-Incorrect.png new file mode 100644 index 0000000..30f126b Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/icon-Incorrect.png differ diff --git a/www/html/Game/public/img/quiz-carry/mission-complete-select.png b/www/html/Game/public/img/quiz-carry/mission-complete-select.png new file mode 100644 index 0000000..0d761e3 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/mission-complete-select.png differ diff --git a/www/html/Game/public/img/quiz-carry/mission-complete.png b/www/html/Game/public/img/quiz-carry/mission-complete.png new file mode 100644 index 0000000..0b15d97 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/mission-complete.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-1st.png b/www/html/Game/public/img/quiz-carry/result-1st.png new file mode 100644 index 0000000..0805bea Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-1st.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-2nd.png b/www/html/Game/public/img/quiz-carry/result-2nd.png new file mode 100644 index 0000000..4332a29 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-2nd.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-3rd.png b/www/html/Game/public/img/quiz-carry/result-3rd.png new file mode 100644 index 0000000..dfdb3a2 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-3rd.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-check.png b/www/html/Game/public/img/quiz-carry/result-check.png new file mode 100644 index 0000000..8924fc8 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-check.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-complete.png b/www/html/Game/public/img/quiz-carry/result-complete.png new file mode 100644 index 0000000..4b5f062 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-complete.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-gameover.png b/www/html/Game/public/img/quiz-carry/result-gameover.png new file mode 100644 index 0000000..610c6ec Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-gameover.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-txt-1.png b/www/html/Game/public/img/quiz-carry/result-txt-1.png new file mode 100644 index 0000000..5e7d3d5 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-txt-1.png differ diff --git a/www/html/Game/public/img/quiz-carry/result-txt-2.png b/www/html/Game/public/img/quiz-carry/result-txt-2.png new file mode 100644 index 0000000..8576e61 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/result-txt-2.png differ diff --git a/www/html/Game/public/img/quiz-carry/score+10.png b/www/html/Game/public/img/quiz-carry/score+10.png new file mode 100644 index 0000000..917f32a Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/score+10.png differ diff --git a/www/html/Game/public/img/quiz-carry/score.png b/www/html/Game/public/img/quiz-carry/score.png new file mode 100644 index 0000000..db271f3 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/score.png differ diff --git a/www/html/Game/public/img/quiz-carry/time.png b/www/html/Game/public/img/quiz-carry/time.png new file mode 100644 index 0000000..7c726fb Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/time.png differ diff --git a/www/html/Game/public/img/quiz-carry/txt-1-1.png b/www/html/Game/public/img/quiz-carry/txt-1-1.png new file mode 100644 index 0000000..1c62ced Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/txt-1-1.png differ diff --git a/www/html/Game/public/img/quiz-carry/txt-1-2.png b/www/html/Game/public/img/quiz-carry/txt-1-2.png new file mode 100644 index 0000000..a37fe60 Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/txt-1-2.png differ diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index 1ab4127..127f06c 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -1,5 +1,12 @@ (function () { const BASE = '/Game'; + /** แผงคำถามบนแมป quiz_carry — ไฟล์อยู่ public/img/quiz-carry */ + const QUIZ_CARRY_MAP_FEEDBACK_IMG = { + correct: BASE + '/img/quiz-carry/icon-Correct.png', + incorrect: BASE + '/img/quiz-carry/icon-Incorrect.png', + scorePlus: BASE + '/img/quiz-carry/score+10.png', + }; + const QUIZ_MAP_CARRY_FEEDBACK_MS = 1400; const params = new URLSearchParams(window.location.search); const spaceId = params.get('space'); const nick = params.get('nick') || 'ผู้เล่น'; @@ -811,6 +818,53 @@ textEl.style.removeProperty('overflow'); } + function clearQuizMapQuestionCarryFeedback() { + if (typeof window.__quizMapCarryFeedbackT === 'number') { + clearTimeout(window.__quizMapCarryFeedbackT); + window.__quizMapCarryFeedbackT = null; + } + const icon = document.getElementById('quiz-map-q-feedback-icon'); + const scoreWrap = document.getElementById('quiz-map-q-feedback-score-wrap'); + const scoreEl = document.getElementById('quiz-map-q-feedback-score'); + if (icon) { + icon.classList.add('is-hidden'); + icon.removeAttribute('src'); + } + if (scoreEl) scoreEl.classList.remove('quiz-map-q-feedback-score--pop'); + if (scoreWrap) { + scoreWrap.classList.add('is-hidden'); + scoreWrap.setAttribute('aria-hidden', 'true'); + } + } + + /** ไอคอนถูก/ผิดที่ขอบบนแผง + score+10 ตรงกลางใต้ข้อความ (เฉพาะถูก) — ตาม mock quiz_carry */ + function showQuizMapQuestionCarryFeedback(isCorrect) { + if (!isQuizCarry()) return; + const panel = document.getElementById('quiz-map-question-panel'); + const icon = document.getElementById('quiz-map-q-feedback-icon'); + const scoreWrap = document.getElementById('quiz-map-q-feedback-score-wrap'); + const scoreEl = document.getElementById('quiz-map-q-feedback-score'); + if (!panel || !icon || panel.classList.contains('is-hidden')) return; + clearQuizMapQuestionCarryFeedback(); + icon.src = isCorrect ? QUIZ_CARRY_MAP_FEEDBACK_IMG.correct : QUIZ_CARRY_MAP_FEEDBACK_IMG.incorrect; + icon.classList.remove('is-hidden'); + if (isCorrect && scoreWrap && scoreEl) { + scoreEl.src = QUIZ_CARRY_MAP_FEEDBACK_IMG.scorePlus; + scoreWrap.classList.remove('is-hidden'); + scoreWrap.setAttribute('aria-hidden', 'false'); + scoreEl.classList.remove('quiz-map-q-feedback-score--pop'); + void scoreEl.offsetWidth; + scoreEl.classList.add('quiz-map-q-feedback-score--pop'); + } else if (scoreWrap) { + scoreWrap.classList.add('is-hidden'); + scoreWrap.setAttribute('aria-hidden', 'true'); + } + window.__quizMapCarryFeedbackT = setTimeout(() => { + window.__quizMapCarryFeedbackT = null; + clearQuizMapQuestionCarryFeedback(); + }, QUIZ_MAP_CARRY_FEEDBACK_MS); + } + /** ค่าเริ่มต้นแผงคำถามบนแผนที่ (ตรงกับ server defaultCarryMapPanelTheme) — ใช้เมื่อยังไม่โหลดจาก API */ function defaultCarryMapPanelThemePlay() { return { @@ -1272,23 +1326,29 @@ if (!panel || !textEl || !mapData) return; /* เอดิเตอร์ embed: ซ่อนแผงทองเฉพาะโหมด quiz (ทับกลาง) — quiz_carry ต้องโชว์ตามโซนที่วาดบนแผนที่ */ if (previewMode && editorEmbedReturn && !isQuizCarry()) { + clearQuizMapQuestionCarryFeedback(); panel.classList.add('is-hidden'); panel.setAttribute('aria-hidden', 'true'); clearQuizMapPanelThemeInline(panel, textEl); return; } if (isQuizBattle()) { + clearQuizMapQuestionCarryFeedback(); panel.classList.add('is-hidden'); panel.setAttribute('aria-hidden', 'true'); clearQuizMapPanelThemeInline(panel, textEl); return; } - if (!isQuiz() && !isQuizCarry()) return; + if (!isQuiz() && !isQuizCarry()) { + clearQuizMapQuestionCarryFeedback(); + return; + } const bounds = isQuiz() ? getQuizQuestionAreaTileBounds(mapData) : (getQuizCarryQuestionAreaTileBounds(mapData) || getQuizCarryHubTileBounds(mapData)); const text = (playQuizText || '').trim(); if (!bounds || !text) { + clearQuizMapQuestionCarryFeedback(); panel.classList.add('is-hidden'); panel.setAttribute('aria-hidden', 'true'); clearQuizMapPanelThemeInline(panel, textEl); @@ -2444,43 +2504,123 @@ } } - /** พรีวิวเอดิเตอร์: ช่วงนับ 3–2–1 — ขอบเรืองรอบโซนตัวเลือก (ช่อง = quizCarryOptN บนกริด, ตามข้อถูกหรือ countdownHighlightSlot) */ - function drawQuizCarryEmbedCountdownHighlight(ctx, worldToScreen, zoom, timeMs) { - if (!previewMode || !editorEmbedReturn || !isQuizCarry() || !mapData) return; - if (mapData.carryEmbedCountdownHighlight === false) return; - const pq = quizCarryEmbedPendingQuestion; - if (!pq || !quizCarryEmbedCountdownEndAt || Date.now() >= quizCarryEmbedCountdownEndAt) return; - const choices = pq.choices; - if (!choices || !choices.length) return; - let slot1 = pq.countdownHighlightSlot; - if (!Number.isFinite(slot1) || slot1 < 1) { - let ci = Number(pq.correctIndex); - if (!Number.isFinite(ci) || ci < 0 || ci >= choices.length) ci = 0; - slot1 = ci + 1; - } else { - slot1 = Math.min(16, Math.max(1, Math.floor(slot1))); - } - const b = getQuizCarryOptionClusterBounds(mapData, slot1); - if (!b || b.minX === Infinity) return; + /** + * กล่องป้ายบนจอ (signX, signY, w, h) ให้ตรงกับ drawQuizCarryChoiceLabels สำหรับช่อง idx 0-based + */ + function computeQuizCarryChoicePlaqueScreenRect(ctx, worldToScreen, zoom, idx0Based) { + if (!isQuizCarry() || !mapData || !quizCarryCurrent) return null; + const q = quizCarryCurrent; + const choices = q.choices; + if (!choices || !choices.length || idx0Based < 0 || idx0Based >= choices.length) return null; + const i = idx0Based; + const b = getQuizCarryOptionClusterBounds(mapData, i + 1); + if (!b) return null; const ts = tileSize * zoom; - const wx0 = b.minX * tileSize; - const wy0 = b.minY * tileSize; - const wx1 = (b.maxX + 1) * tileSize; - const wy1 = (b.maxY + 1) * tileSize; - const corners = [[wx0, wy0], [wx1, wy0], [wx1, wy1], [wx0, wy1]]; - let sxMin = Infinity, syMin = Infinity, sxMax = -Infinity, syMax = -Infinity; - for (let i = 0; i < corners.length; i++) { - const [sx, sy] = worldToScreen(corners[i][0], corners[i][1]); - if (sx < sxMin) sxMin = sx; - if (sx > sxMax) sxMax = sx; - if (sy < syMin) syMin = sy; - if (sy > syMax) syMax = sy; + const ps = quizCarryPlaqueMapScaleClampedPlay(); + const choiceText = String(choices[i] || '').trim(); + const imgUrl = getQuizCarryPlaqueImageUrlForIndex(q, i); + const gridIdleEl = findQuizCarryGridIdleImgForChoiceIndex(i); + const plaqueExtra = imgUrl ? { imageUrl: imgUrl } + : (gridIdleEl && gridIdleEl.complete && gridIdleEl.naturalWidth ? { imageElement: gridIdleEl } : {}); + if (!choiceText && !plaqueExtra.imageUrl && !plaqueExtra.imageElement) return null; + const tileSpanX = (b.maxX - b.minX + 1); + const tileSpanY = (b.maxY - b.minY + 1); + const minPlaqueW = Math.ceil(Math.max(72, tileSpanX * ts - 16) * ps); + const minPlaqueH = Math.ceil(Math.max(52, tileSpanY * ts - 12) * ps); + const wx = b.cx * tileSize; + const wy = b.cy * tileSize; + const [sx, sy] = worldToScreen(wx, wy); + ctx.save(); + let signX; + let signY; + let w; + let h; + if (!choiceText && (plaqueExtra.imageUrl || plaqueExtra.imageElement)) { + const minW = minPlaqueW; + const minH = minPlaqueH; + signX = sx - minW / 2; + signY = sy - minH / 2; + w = minW; + h = minH; + ctx.restore(); + return { signX, signY, w, h }; } - const pad = Math.max(4, ts * 0.1); - const rx = sxMin - pad; - const ry = syMin - pad; - const rw = Math.max(8, sxMax - sxMin + pad * 2); - const rh = Math.max(8, syMax - syMin + pad * 2); + const line = choiceText; + const fontPx = Math.max(10, Math.min(24, ts * 0.24 * ps)); + ctx.font = '600 ' + fontPx + 'px system-ui, "Segoe UI", "Kanit", sans-serif'; + let maxW = Math.max(56, tileSpanX * ts - 14); + const lineH = fontPx * 1.2; + let textLines = canvasWordWrapLines(ctx, line, maxW); + if (!textLines.length) textLines = [line]; + if (textLines.length * lineH > tileSpanY * ts - 8 && textLines.length > 1) { + const fp2 = Math.max(9, fontPx * 0.85); + ctx.font = '600 ' + fp2 + 'px system-ui, "Segoe UI", "Kanit", sans-serif'; + const lh2 = fp2 * 1.2; + maxW = Math.max(48, maxW - 4); + textLines = canvasWordWrapLines(ctx, line, maxW); + if (textLines.length * lh2 > tileSpanY * ts - 8) { + textLines = textLines.slice(0, Math.max(1, Math.floor((tileSpanY * ts - 8) / lh2))); + } + for (let ti = 0; ti < textLines.length; ti++) { + let s = textLines[ti]; + if (ctx.measureText(s).width <= maxW) continue; + while (s.length > 2 && ctx.measureText(s + '…').width > maxW) s = s.slice(0, -1); + textLines[ti] = s + '…'; + } + const maxLineW = Math.max(...textLines.map((t) => ctx.measureText(t).width), 40); + const padX = 10; + const padY = 8; + w = Math.ceil(maxLineW + padX * 2); + h = Math.ceil(textLines.length * lh2 + padY * 2); + if (imgUrl || plaqueExtra.imageElement) { + w = Math.max(w, minPlaqueW); + h = Math.max(h, minPlaqueH); + } + signX = sx - w / 2; + signY = sy - h / 2; + ctx.restore(); + return { signX, signY, w, h }; + } + for (let ti = 0; ti < textLines.length; ti++) { + let s = textLines[ti]; + if (ctx.measureText(s).width <= maxW) continue; + while (s.length > 2 && ctx.measureText(s + '…').width > maxW) s = s.slice(0, -1); + textLines[ti] = s + '…'; + } + let maxLineW = 0; + for (let ti = 0; ti < textLines.length; ti++) { + const lw = ctx.measureText(textLines[ti]).width; + if (lw > maxLineW) maxLineW = lw; + } + const padX = 10; + const padY = 8; + w = Math.ceil(Math.max(maxLineW, 52) + padX * 2); + h = Math.ceil(textLines.length * lineH + padY * 2); + if (imgUrl || plaqueExtra.imageElement) { + w = Math.max(w, minPlaqueW); + h = Math.max(h, minPlaqueH); + } + signX = sx - w / 2; + signY = sy - h / 2; + ctx.restore(); + return { signX, signY, w, h }; + } + + /** + * ขอบเรืองรอบช่องตัวเลือก (carryEmbedCountdownHighlight + carryEmbedCountdownHighlightColor) + * ขนาดกรอบเท่าป้ายบนแมป (เดียวกับ drawQuizCarryChoiceLabels) ไม่ใช่ทั้ง cluster กริด + */ + function drawQuizCarryEmbedCountdownHighlight(ctx, worldToScreen, zoom, timeMs) { + if (!isQuizCarry() || !mapData) return; + if (mapData.carryEmbedCountdownHighlight === false) return; + const optIdx = getQuizCarryLocalGrabbableOptionIndex(); + if (optIdx == null) return; + const pr = computeQuizCarryChoicePlaqueScreenRect(ctx, worldToScreen, zoom, optIdx); + if (!pr || pr.w < 4 || pr.h < 4) return; + const rx = pr.signX; + const ry = pr.signY; + const rw = pr.w; + const rh = pr.h; const rr = Math.max(6, Math.min(16, Math.min(rw, rh) * 0.14)); const colRaw = mapData.carryEmbedCountdownHighlightColor; const strokeBase = typeof colRaw === 'string' && /^#[0-9a-fA-F]{6}$/.test(colRaw.trim()) ? colRaw.trim() : '#ffb44a'; @@ -2913,9 +3053,6 @@ if (Date.now() < quizCarryAnswerCloseAt) return; quizCarryAnswerCloseAt = 0; quizCarryOptionRevealAt = 0; - const limT = previewMode && isQuizCarry() ? quizCarrySessionLength : 0; - const willEndT = limT > 0 && (quizCarryRoundsCompleted + 1) >= limT; - showQuizCarryToast(willEndT ? 'หมดเวลา — ครบชุดคำถาม' : 'หมดเวลา — ข้อถัดไป', false); me.quizCarryHeld = null; others.forEach((o) => { if (!o) return; @@ -3013,6 +3150,7 @@ quizCarryEmbedCountdownEndAt = quizCarryEmbedCountdownStartAt + 3000; quizCarryCurrent = null; playQuizText = ''; + clearQuizMapQuestionCarryFeedback(); quizCarryOptionRevealAt = 0; quizCarryAnswerCloseAt = 0; me.quizCarryHeld = null; @@ -3035,6 +3173,7 @@ quizCarryCurrent = q; preloadQuizCarryChoiceImages(q); playQuizText = formatQuizCarryQuestionHud(q); + clearQuizMapQuestionCarryFeedback(); const qEl = document.getElementById('quiz-game-question'); if (qEl) qEl.textContent = playQuizText; me.quizCarryHeld = null; @@ -3130,14 +3269,15 @@ renderPlayQuizScoreboard(playLiveQuizScores); } + /** Toast กลางบน (หยิบ / ถูก / ผิด / คำใบ้) — ปิดการแสดงตามคำขอ UX */ function showQuizCarryToast(msg, ok) { + if (typeof window.__quizCarryToastT === 'number') clearTimeout(window.__quizCarryToastT); + window.__quizCarryToastT = null; const el = document.getElementById('play-quiz-feedback'); if (!el) return; - el.classList.remove('is-hidden'); - el.className = ok ? 'play-quiz-feedback play-quiz-feedback-ok' : 'play-quiz-feedback play-quiz-feedback-bad'; - el.textContent = msg || ''; - if (typeof window.__quizCarryToastT === 'number') clearTimeout(window.__quizCarryToastT); - window.__quizCarryToastT = setTimeout(() => { el.classList.add('is-hidden'); }, 2800); + el.classList.add('is-hidden'); + el.className = 'play-quiz-feedback'; + el.textContent = ''; } /** @@ -3171,13 +3311,23 @@ playLiveQuizScores[actorId] = (playLiveQuizScores[actorId] || 0) + 1; const lim = previewMode && isQuizCarry() ? quizCarrySessionLength : 0; const willEnd = lim > 0 && (quizCarryRoundsCompleted + 1) >= lim; + const showMapFb = !opts.silent && actorId === myId; + if (showMapFb) showQuizMapQuestionCarryFeedback(true); if (!opts.silent) { showQuizCarryToast(willEnd ? 'ถูกต้อง +1 แต้ม — ครบชุดคำถาม' : 'ถูกต้อง +1 แต้ม — สุ่มคำถามใหม่', true); } me.quizCarryHeld = null; others.forEach((o) => { if (o) o.quizCarryHeld = null; }); - quizCarryAfterRoundResolved(); + const afterOk = () => { quizCarryAfterRoundResolved(); }; + if (showMapFb) { + window.setTimeout(afterOk, 560); + } else { + afterOk(); + } } else { + if (!opts.silent && actorId === myId) { + showQuizMapQuestionCarryFeedback(false); + } if (!opts.silent) { showQuizCarryToast('ผิด — คืนป้ายที่โซนตัวเลือกแล้ว กด F หยิบใหม่ได้', false); } @@ -3227,10 +3377,10 @@ return true; } - /** ยืนบนช่องตัวเลือกและหยิบได้ */ - function quizCarryGrabPickupAvailable() { - if (!quizCarryGrabCoreGatesOk()) return false; - if (me.quizCarryHeld != null) return false; + /** ดัชนีตัวเลือก 0-based ที่ผู้เล่นท้องถิ่นยืนอยู่และหยิบได้ — ไม่มีคืน null */ + function getQuizCarryLocalGrabbableOptionIndex() { + if (!quizCarryGrabCoreGatesOk()) return null; + if (me.quizCarryHeld != null) return null; const md = mapData; const footprint = quizTilesFootprintPlay(me.x, me.y); let pickupIdx = null; @@ -3241,7 +3391,13 @@ const opt = quizCarryOptionIndexAtPlay(md, tx, ty); if (opt != null) pickupIdx = pickupIdx === null ? opt : pickupIdx; } - return pickupIdx != null && !quizCarryOptionHeldByAnyone(pickupIdx); + if (pickupIdx == null || quizCarryOptionHeldByAnyone(pickupIdx)) return null; + return pickupIdx; + } + + /** ยืนบนช่องตัวเลือกและหยิบได้ */ + function quizCarryGrabPickupAvailable() { + return getQuizCarryLocalGrabbableOptionIndex() != null; } /** ถือป้ายแล้วยืนโซนส่งได้ */ @@ -8276,6 +8432,8 @@ const gw = document.querySelector('.game-wrap'); if (gw) gw.classList.toggle('play-cyber-active', !!on); if (!on || !root) return; + const timeLabelEl = root.querySelector('.play-cyber-time-label'); + if (timeLabelEl) timeLabelEl.style.display = isQuizCarry() ? 'none' : ''; const timeVal = document.getElementById('play-cyber-time-val'); const timeSub = document.getElementById('play-cyber-time-sub'); const portrait = document.getElementById('play-cyber-portrait-img'); diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index 95d5eaf..1b45451 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -127,7 +127,7 @@ align-items: stretch; justify-content: flex-start; text-align: center; - overflow: hidden; + overflow: visible; scrollbar-width: none; -ms-overflow-style: none; --qmap-bg: rgba(12, 14, 28, 0.88); @@ -172,6 +172,48 @@ width: 0; height: 0; } + /* quiz_carry: ไอคอนถูก/ผิดโผล่ครึ่งบนกรอบแผง — ข้อความยัง clip ใน p */ + #quiz-map-q-feedback-icon { + position: absolute; + left: 50%; + top: 0; + transform: translate(-50%, -44%); + width: clamp(28px, min(24%, 5vmin), 76px); + height: auto; + z-index: 4; + pointer-events: none; + object-fit: contain; + filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.55)); + } + #quiz-map-q-feedback-icon.is-hidden { + display: none !important; + } + #quiz-map-q-feedback-score-wrap { + flex: 1 1 auto; + min-height: 0; + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; + padding: 0.12em 0 0.2em; + } + #quiz-map-q-feedback-score-wrap.is-hidden { + display: none !important; + } + #quiz-map-q-feedback-score { + width: clamp(44px, min(58%, 12vmin), 160px); + height: auto; + object-fit: contain; + filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.45)); + } + #quiz-map-q-feedback-score.quiz-map-q-feedback-score--pop { + animation: quizMapQScorePop 0.58s ease-out both; + } + @keyframes quizMapQScorePop { + 0% { transform: scale(0.55); opacity: 0; } + 58% { transform: scale(1.06); opacity: 1; } + 100% { transform: scale(1); opacity: 1; } + } #quiz-carry-embed-countdown { position: fixed; inset: 0; @@ -918,7 +960,11 @@ @@ -1013,7 +1059,7 @@ - +
v —