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 @@