diff --git a/www/html/Admin/admin.js b/www/html/Admin/admin.js
index 7e8fefe..72aa2b2 100644
--- a/www/html/Admin/admin.js
+++ b/www/html/Admin/admin.js
@@ -365,6 +365,11 @@
var ams = Number(data.carryAnswerMs);
ansSec.value = String(Number.isFinite(ams) ? Math.round(ams / 1000) : 5);
}
+ var sessLen = el('quiz-carry-session-len');
+ if (sessLen) {
+ var sl = parseInt(String(data.carrySessionLength), 10);
+ sessLen.value = String(Number.isFinite(sl) && sl >= 0 ? sl : 0);
+ }
setMsg('quiz-carry-settings-msg', '', '');
}).catch(function (e) {
setMsg('quiz-carry-settings-msg', e.message || 'โหลดไม่ได้', 'error');
@@ -416,11 +421,16 @@
if (ansSec > 300) ansSec = 300;
var carryReadMs = readSec * 1000;
var carryAnswerMs = ansSec * 1000;
+ var sessEl = el('quiz-carry-session-len');
+ var carrySessionLength = sessEl ? parseInt(String(sessEl.value || '0'), 10) : 0;
+ if (!Number.isFinite(carrySessionLength) || carrySessionLength < 0) carrySessionLength = 0;
+ if (carrySessionLength > 500) carrySessionLength = 500;
gameQuizFetch('PUT', {
carryQuestions: carryQuestions,
carryReadMs: carryReadMs,
carryAnswerMs: carryAnswerMs,
+ carrySessionLength: carrySessionLength,
}).then(function () {
setMsg('quiz-carry-settings-msg', 'บันทึกแล้ว', 'ok');
}).catch(function (e) {
diff --git a/www/html/Admin/api/game-quiz-settings.php b/www/html/Admin/api/game-quiz-settings.php
index f15cf3f..7fb7a8e 100644
--- a/www/html/Admin/api/game-quiz-settings.php
+++ b/www/html/Admin/api/game-quiz-settings.php
@@ -46,7 +46,7 @@ function merge_quiz_settings_disk_from_put(string $rawBody): bool
}
}
}
- $mergeKeys = ['readMs', 'answerMs', 'betweenMs', 'carryReadMs', 'carryAnswerMs', 'questions', 'carryQuestions', 'battleQuizMcq'];
+ $mergeKeys = ['readMs', 'answerMs', 'betweenMs', 'carryReadMs', 'carryAnswerMs', 'carrySessionLength', 'questions', 'carryQuestions', 'battleQuizMcq'];
foreach ($mergeKeys as $k) {
if (array_key_exists($k, $patch)) {
$base[$k] = $patch[$k];
diff --git a/www/html/Admin/index.html b/www/html/Admin/index.html
index f87202a..570e03d 100644
--- a/www/html/Admin/index.html
+++ b/www/html/Admin/index.html
@@ -175,6 +175,10 @@
นับจากเมื่อหยิบได้ · สูงสุด 300 วิ
+
รายการคำถาม
diff --git a/www/html/Game/public/img/quiz-carry/btn-ready.png b/www/html/Game/public/img/quiz-carry/btn-ready.png
new file mode 100644
index 0000000..6b50bfc
Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/btn-ready.png differ
diff --git a/www/html/Game/public/img/quiz-carry/btn-start.png b/www/html/Game/public/img/quiz-carry/btn-start.png
new file mode 100644
index 0000000..cdc7776
Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/btn-start.png differ
diff --git a/www/html/Game/public/img/quiz-carry/howto.png b/www/html/Game/public/img/quiz-carry/howto.png
new file mode 100644
index 0000000..e45cc2d
Binary files /dev/null and b/www/html/Game/public/img/quiz-carry/howto.png differ
diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js
index 260b7a4..fa7b08a 100644
--- a/www/html/Game/public/js/play.js
+++ b/www/html/Game/public/js/play.js
@@ -569,6 +569,15 @@
let quizCarryOptionRevealAt = 0;
let quizCarryAnswerCloseAt = 0;
let quizCarryCarryTimingMs = { carryReadMs: 3000, carryAnswerMs: 5000 };
+ /** พรีวิว (รวม editor embed): จำนวนข้อที่เล่นก่อนโอเวอร์เลย์จบ — 0 = ไม่จบอัตโนมัติ */
+ let quizCarrySessionLength = 0;
+ let quizCarryRoundsCompleted = 0;
+ let quizCarrySessionEnded = false;
+ /** embed พรีวิว: รอ Ready / START ของโฮสต์ก่อนนับ 3–2–1 */
+ let quizCarryPregameActive = false;
+ let playHostId = null;
+ /** ซิงก์จากเซิร์ฟเวอร์: socketId → กด Ready แล้ว */
+ let quizCarryLobbyReadyMap = {};
/** jump_survive — กล้องตามตัวในกรอบ + แพลตฟอร์มเลื่อนขึ้น (scroll) ไม่ลากทั้งฉาก */
let jumpSurviveCamCenterX = 0;
@@ -789,6 +798,12 @@
return Math.max(minV, Math.min(maxV, Math.floor(v)));
}
+ function clampCarrySessionLen(n, def) {
+ const v = Number(n);
+ if (Number.isNaN(v)) return def;
+ return Math.max(0, Math.min(500, Math.floor(v)));
+ }
+
function buildQuizPoolFromMap(md) {
if (!md || !Array.isArray(md.quizQuestions)) return [];
return md.quizQuestions
@@ -1157,6 +1172,7 @@
if (mapData.gameType === 'balloon_boss') {
applyBalloonBossSpawnLayoutPlay();
}
+ if (quizCarryPregameActive && mapData && mapData.gameType === 'quiz_carry') updateQuizCarryPregameHud();
}
function pickRandomWalkableCellInAnswerGrid(grid, o) {
@@ -1863,6 +1879,120 @@
return !!(previewMode && editorEmbedReturn && isQuizCarry() && quizCarryEmbedCountdownEndAt > Date.now());
}
+ function isQuizCarryEmbedLobbyBlockingMovement() {
+ return !!(previewMode && editorEmbedReturn && isQuizCarry() && quizCarryPregameActive);
+ }
+
+ function quizCarryPregameHumanIds() {
+ const ids = [];
+ if (myId != null && !isPreviewBotId(myId)) ids.push(String(myId));
+ others.forEach((_, id) => {
+ if (!isPreviewBotId(id)) ids.push(String(id));
+ });
+ return ids;
+ }
+
+ function quizCarryPregameBotCount() {
+ let n = 0;
+ others.forEach((_, id) => {
+ if (isPreviewBotId(id)) n++;
+ });
+ return n;
+ }
+
+ function quizCarryPregameReadyNumerator() {
+ let n = quizCarryPregameBotCount();
+ quizCarryPregameHumanIds().forEach((id) => {
+ if (quizCarryLobbyReadyMap[id]) n++;
+ });
+ return n;
+ }
+
+ function quizCarryPregameTotalPlayers() {
+ return quizCarryPregameHumanIds().length + quizCarryPregameBotCount();
+ }
+
+ function isMePlayHost() {
+ if (myId == null) return false;
+ if (playHostId == null) return true;
+ return String(playHostId) === String(myId);
+ }
+
+ function hideQuizCarryPregameOverlay() {
+ const ov = document.getElementById('quiz-carry-pregame-overlay');
+ if (ov) {
+ ov.classList.add('is-hidden');
+ ov.setAttribute('aria-hidden', 'true');
+ }
+ }
+
+ function showQuizCarryPregameOverlay() {
+ const ov = document.getElementById('quiz-carry-pregame-overlay');
+ if (ov) {
+ ov.classList.remove('is-hidden');
+ ov.setAttribute('aria-hidden', 'false');
+ }
+ updateQuizCarryPregameHud();
+ }
+
+ function quizCarrySyncGuestReadyIfNeeded() {
+ if (!quizCarryPregameActive || myId == null || isMePlayHost()) return;
+ const sid = String(myId);
+ if (quizCarryLobbyReadyMap[sid]) return;
+ quizCarryLobbyReadyMap[sid] = true;
+ if (socket && socket.connected) socket.emit('quiz-carry-lobby-ready', { ready: true });
+ }
+
+ function updateQuizCarryPregameHud() {
+ if (!quizCarryPregameActive) return;
+ const st = document.getElementById('quiz-carry-pregame-status');
+ const primary = document.getElementById('quiz-carry-pregame-primary');
+ const primaryImg = document.getElementById('quiz-carry-pregame-primary-img');
+ const num = quizCarryPregameReadyNumerator();
+ const tot = Math.max(1, quizCarryPregameTotalPlayers());
+ if (st) {
+ st.textContent = 'Ready Status : ' + num + '/' + tot;
+ }
+ const humans = quizCarryPregameHumanIds();
+ const humansReady = humans.length > 0 && humans.every((id) => !!quizCarryLobbyReadyMap[id]);
+ const meReady = !!(myId && quizCarryLobbyReadyMap[String(myId)]);
+ if (primaryImg) {
+ primaryImg.src = humansReady ? '/Game/img/quiz-carry/btn-start.png' : '/Game/img/quiz-carry/btn-ready.png';
+ primaryImg.alt = humansReady ? 'START' : 'READY';
+ }
+ if (primary) {
+ primary.classList.toggle('is-start-phase', humansReady);
+ primary.classList.toggle('is-read-only', !isMePlayHost());
+ primary.classList.toggle('is-pressed', isMePlayHost() && meReady && !humansReady);
+ primary.disabled = !isMePlayHost();
+ primary.setAttribute('aria-pressed', humansReady ? 'false' : (meReady ? 'true' : 'false'));
+ primary.title = isMePlayHost()
+ ? (humansReady ? 'START' : (meReady ? 'ยกเลิก READY' : 'READY'))
+ : (humansReady ? 'START (โฮสต์เท่านั้น)' : 'READY (โฮสต์เท่านั้น)');
+ }
+ }
+
+ function beginQuizCarryEmbedPregame() {
+ if (!(previewMode && editorEmbedReturn && isQuizCarry())) return;
+ quizCarryPregameActive = true;
+ quizCarryLobbyReadyMap = {};
+ quizCarryPregameHumanIds().forEach((id) => { quizCarryLobbyReadyMap[id] = false; });
+ showQuizCarryPregameOverlay();
+ if (socket && socket.connected) socket.emit('quiz-carry-lobby-sync-request');
+ quizCarrySyncGuestReadyIfNeeded();
+ updateQuizCarryPregameHud();
+ }
+
+ function endQuizCarryEmbedPregameAndStart() {
+ if (!quizCarryPregameActive) return;
+ quizCarryPregameActive = false;
+ hideQuizCarryPregameOverlay();
+ quizCarryPickNextQuestion();
+ if (!(previewMode && editorEmbedReturn && quizCarryEmbedCountdownEndAt > Date.now())) {
+ quizCarryRestoreEmbedPhaseLabel();
+ }
+ }
+
function quizCarryOptionsPickableNow() {
if (!isQuizCarry() || !quizCarryCurrent) return true;
if (!quizCarryOptionRevealAt || quizCarryOptionRevealAt <= 0) return true;
@@ -1888,6 +2018,8 @@
}
function updateQuizCarryCarryPhaseHud() {
+ if (quizCarrySessionEnded) return;
+ if (quizCarryPregameActive) return;
if (!isQuizCarry() || !quizCarryCurrent) return;
const phaseEl = document.getElementById('quiz-game-phase-label');
if (!phaseEl) return;
@@ -1906,6 +2038,8 @@
}
function tickQuizCarryRoundTimers() {
+ if (quizCarrySessionEnded) return;
+ if (quizCarryPregameActive) return;
if (!isQuizCarry() || !mapData || !quizCarryCurrent) return;
if (quizCarryEmbedCountdownEndAt > Date.now()) return;
if (quizCarryEmbedPendingQuestion) return;
@@ -1913,7 +2047,9 @@
if (Date.now() < quizCarryAnswerCloseAt) return;
quizCarryAnswerCloseAt = 0;
quizCarryOptionRevealAt = 0;
- showQuizCarryToast('หมดเวลา — ข้อถัดไป', false);
+ 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;
@@ -1921,7 +2057,7 @@
o.botPath = [];
});
renderPlayQuizScoreboard(playLiveQuizScores);
- quizCarryPickNextQuestion();
+ quizCarryAfterRoundResolved();
}
/** นับ 3–2–1: ไม่โชว์ข้อความคำถามใน overlay — มีแค่เลข */
@@ -1982,6 +2118,8 @@
}
function quizCarryPickNextQuestion() {
+ if (quizCarrySessionEnded) return;
+ if (quizCarryPregameActive) return;
if (!quizCarryPool.length) {
quizCarryCurrent = null;
quizCarryEmbedPendingQuestion = null;
@@ -2036,6 +2174,91 @@
quizCarryApplyPhaseTimersForCurrentQuestion();
}
+ /** หลังจบข้อหนึ่งข้อ (ถูกหรือหมดเวลา): พรีวิวนับรอบจนครบ carrySessionLength แล้วจบเซสชัน */
+ function quizCarryAfterRoundResolved() {
+ if (!isQuizCarry()) return;
+ if (!previewMode) {
+ quizCarryPickNextQuestion();
+ return;
+ }
+ quizCarryRoundsCompleted++;
+ const lim = quizCarrySessionLength;
+ if (lim > 0 && quizCarryRoundsCompleted >= lim) {
+ showQuizCarrySessionCompleteOverlay();
+ return;
+ }
+ quizCarryPickNextQuestion();
+ }
+
+ function showQuizCarrySessionCompleteOverlay() {
+ if (quizCarrySessionEnded) return;
+ quizCarrySessionEnded = true;
+ quizCarryPregameActive = false;
+ hideQuizCarryPregameOverlay();
+ quizCarryCurrent = null;
+ quizCarryEmbedPendingQuestion = null;
+ quizCarryEmbedCountdownEndAt = 0;
+ quizCarryEmbedCountdownStartAt = 0;
+ quizCarryOptionRevealAt = 0;
+ quizCarryAnswerCloseAt = 0;
+ hideQuizCarryEmbedCountdownOverlay();
+ me.quizCarryHeld = null;
+ others.forEach((o) => {
+ if (!o) return;
+ o.quizCarryHeld = null;
+ o.botPath = [];
+ });
+ playQuizText = 'ครบชุดคำถามแล้ว';
+ const qEl = document.getElementById('quiz-game-question');
+ if (qEl) qEl.textContent = playQuizText;
+ const phaseEl = document.getElementById('quiz-game-phase-label');
+ if (phaseEl) phaseEl.textContent = 'ครบ ' + String(quizCarryRoundsCompleted) + ' ข้อ';
+ const ov = document.getElementById('gauntlet-ended-overlay');
+ const msgEl = document.getElementById('gauntlet-ended-message');
+ const titleEl = document.getElementById('gauntlet-ended-title');
+ const listEl = document.getElementById('gauntlet-ended-rankings');
+ const btn = document.getElementById('btn-gauntlet-ended-lobby');
+ if (!ov || !msgEl || !listEl) return;
+ if (titleEl) titleEl.textContent = 'เกมจบ · Game over';
+ msgEl.textContent = 'ครบจำนวนคำถามที่ตั้งไว้ (' + String(quizCarryRoundsCompleted) + ' ข้อ) · Quiz carry session finished';
+ listEl.innerHTML = '';
+ const ranks = [];
+ if (myId != null) {
+ ranks.push({
+ id: myId,
+ nickname: (me.nickname || nick || 'คุณ').trim() || 'คุณ',
+ score: Math.max(0, Number(playLiveQuizScores[myId]) || 0),
+ });
+ }
+ others.forEach((o, id) => {
+ if (!o) return;
+ ranks.push({
+ id,
+ nickname: (o.nickname && String(o.nickname).trim()) ? String(o.nickname).trim() : String(id),
+ score: Math.max(0, Number(playLiveQuizScores[id]) || 0),
+ });
+ });
+ ranks.sort((a, b) => b.score - a.score || String(a.nickname).localeCompare(String(b.nickname), 'th'));
+ ranks.forEach((r, i) => {
+ const li = document.createElement('li');
+ const isMe = myId != null && String(r.id) === String(myId);
+ li.textContent = `${i + 1}. ${(r && r.nickname) || '—'} — ${Math.max(0, Number(r && r.score) || 0)}`;
+ if (isMe) li.className = 'gauntlet-ended-me';
+ listEl.appendChild(li);
+ });
+ ov.classList.remove('is-hidden');
+ function goLobby() {
+ window.location.href = 'room-lobby.html?space=' + encodeURIComponent(spaceId) + '&nick=' + encodeURIComponent(nick);
+ }
+ if (btn) {
+ btn.onclick = () => {
+ if (previewMode && editorEmbedReturn) ov.classList.add('is-hidden');
+ else goLobby();
+ };
+ }
+ renderPlayQuizScoreboard(playLiveQuizScores);
+ }
+
function showQuizCarryToast(msg, ok) {
const el = document.getElementById('play-quiz-feedback');
if (!el) return;
@@ -2052,6 +2275,8 @@
*/
function tryQuizCarryInteractionForPlayer(actorId, ox, oy, opts) {
opts = opts || {};
+ if (quizCarryPregameActive) return false;
+ if (quizCarrySessionEnded) return false;
if (!isQuizCarry() || !mapData || !quizCarryCurrent) return false;
const md = mapData;
const footprint = quizTilesFootprintPlay(ox, oy);
@@ -2073,10 +2298,12 @@
const ok = held === correct;
if (ok) {
playLiveQuizScores[actorId] = (playLiveQuizScores[actorId] || 0) + 1;
- showQuizCarryToast('ถูกต้อง +1 แต้ม — สุ่มคำถามใหม่', true);
+ const lim = previewMode && isQuizCarry() ? quizCarrySessionLength : 0;
+ const willEnd = lim > 0 && (quizCarryRoundsCompleted + 1) >= lim;
+ showQuizCarryToast(willEnd ? 'ถูกต้อง +1 แต้ม — ครบชุดคำถาม' : 'ถูกต้อง +1 แต้ม — สุ่มคำถามใหม่', true);
me.quizCarryHeld = null;
others.forEach((o) => { if (o) o.quizCarryHeld = null; });
- quizCarryPickNextQuestion();
+ quizCarryAfterRoundResolved();
} else {
showQuizCarryToast('ผิด — คืนป้ายที่โซนตัวเลือกแล้ว กด F หยิบใหม่ได้', false);
}
@@ -2115,6 +2342,8 @@
}
function resetQuizCarryPlayState() {
+ quizCarryPregameActive = false;
+ hideQuizCarryPregameOverlay();
quizCarryPool = [];
quizCarryCurrent = null;
quizCarryEmbedPendingQuestion = null;
@@ -2122,6 +2351,8 @@
quizCarryEmbedCountdownEndAt = 0;
quizCarryOptionRevealAt = 0;
quizCarryAnswerCloseAt = 0;
+ quizCarryRoundsCompleted = 0;
+ quizCarrySessionEnded = false;
hideQuizCarryEmbedCountdownOverlay();
me.quizCarryHeld = null;
others.forEach((o) => {
@@ -2132,6 +2363,9 @@
}
async function loadQuizCarryPoolAndStart() {
+ quizCarryRoundsCompleted = 0;
+ quizCarrySessionEnded = false;
+ quizCarrySessionLength = 0;
let pool = [];
try {
const r = await fetch(BASE + '/api/quiz-settings');
@@ -2141,6 +2375,7 @@
carryReadMs: clampPreviewMs(s.carryReadMs, 3000, 0, 120000),
carryAnswerMs: clampPreviewMs(s.carryAnswerMs, 5000, 1000, 300000),
};
+ quizCarrySessionLength = clampCarrySessionLen(s.carrySessionLength, 0);
const carryOnly = s && Array.isArray(s.carryQuestions) && s.carryQuestions.length > 0;
const source = carryOnly ? s.carryQuestions : (s && Array.isArray(s.questions) ? s.questions : []);
for (let i = 0; i < source.length; i++) {
@@ -2160,7 +2395,11 @@
if (qEl) qEl.textContent = playQuizText;
return;
}
- quizCarryPickNextQuestion();
+ if (previewMode && editorEmbedReturn) {
+ beginQuizCarryEmbedPregame();
+ } else {
+ quizCarryPickNextQuestion();
+ }
if (!(previewMode && editorEmbedReturn && quizCarryEmbedCountdownEndAt > Date.now())) {
quizCarryRestoreEmbedPhaseLabel();
}
@@ -2252,6 +2491,8 @@
}
function stepQuizCarryPreviewBots() {
+ if (quizCarrySessionEnded) return;
+ if (quizCarryPregameActive) return;
if (!previewFillBots || !isQuizCarry() || !mapData || !quizCarryCurrent) return;
const w = mapData.width || 20, h = mapData.height || 15;
const nowPf = performance.now();
@@ -5062,6 +5303,7 @@
const prevWasSpaceShooter = mapData && mapData.gameType === 'space_shooter';
const prevWasBalloonBoss = mapData && mapData.gameType === 'balloon_boss';
mapData = gameMapData;
+ if (res && res.hostId != null) playHostId = res.hostId;
if (mapData.gameType !== 'space_shooter') {
spaceShooterGameEnded = false;
if (prevWasSpaceShooter) {
@@ -5336,6 +5578,23 @@
quizCarryHeld: null,
});
if (previewFillBots) rebalancePreviewBots();
+ if (quizCarryPregameActive && isQuizCarry()) updateQuizCarryPregameHud();
+ });
+ socket.on('host-changed', (d) => {
+ if (d && d.hostId != null) playHostId = d.hostId;
+ if (quizCarryPregameActive && isQuizCarry()) updateQuizCarryPregameHud();
+ });
+ socket.on('quiz-carry-lobby-sync', (d) => {
+ if (!d || typeof d.readyMap !== 'object') return;
+ quizCarryLobbyReadyMap = { ...d.readyMap };
+ if (quizCarryPregameActive && isQuizCarry()) {
+ quizCarrySyncGuestReadyIfNeeded();
+ updateQuizCarryPregameHud();
+ }
+ });
+ socket.on('quiz-carry-lobby-started', () => {
+ if (!(previewMode && editorEmbedReturn && isQuizCarry())) return;
+ endQuizCarryEmbedPregameAndStart();
});
socket.on('user-move', (data) => {
if (data && myId != null && data.id === myId) {
@@ -7630,7 +7889,7 @@
requestAnimationFrame(tick);
return;
}
- if (isQuizCarryEmbedCountdownBlockingMovement()) {
+ if (isQuizCarryEmbedLobbyBlockingMovement() || isQuizCarryEmbedCountdownBlockingMovement()) {
me.isWalking = false;
playPath = [];
enforceQuizBattleLaneOnMePlay();
@@ -7758,6 +8017,32 @@
}
});
})();
+ (function wireQuizCarryPregameOverlay() {
+ const primary = document.getElementById('quiz-carry-pregame-primary');
+ if (!primary) return;
+ primary.addEventListener('click', () => {
+ if (!quizCarryPregameActive || myId == null) return;
+ if (!isMePlayHost()) return;
+ const humans = quizCarryPregameHumanIds();
+ const humansReady = humans.length > 0 && humans.every((id) => !!quizCarryLobbyReadyMap[id]);
+ if (humansReady) {
+ /* ผู้เล่นจริงคนเดียว + พรีวิว: ไม่ต้องรอ ACK เซิร์ฟ (ห้องที่ map บนเซิร์ฟยังไม่ใช่ quiz_carry จะเคยล้มเหลว) */
+ if (humans.length === 1 && isMePlayHost()) {
+ endQuizCarryEmbedPregameAndStart();
+ return;
+ }
+ socket.emit('quiz-carry-lobby-start', {}, (r) => {
+ if (!r || !r.ok) showQuizCarryToast((r && r.error) || 'START ไม่ได้', false);
+ });
+ return;
+ }
+ const sid = String(myId);
+ const next = !quizCarryLobbyReadyMap[sid];
+ quizCarryLobbyReadyMap[sid] = next;
+ updateQuizCarryPregameHud();
+ socket.emit('quiz-carry-lobby-ready', { ready: next });
+ });
+ })();
window.addEventListener('beforeunload', () => socket.emit('leave-space'));
window.addEventListener('resize', () => {
if (!mapData || !canvas) return;
diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html
index 444c2cf..ab6484f 100644
--- a/www/html/Game/public/play.html
+++ b/www/html/Game/public/play.html
@@ -186,6 +186,126 @@
z-index: 10120;
top: max(52px, calc(env(safe-area-inset-top) + 46px));
}
+ /* พรีวิว embed quiz_carry: How to play + Ready / START ก่อน 3–2–1 */
+ #quiz-carry-pregame-overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 10078;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: max(10px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right)) max(10px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left));
+ box-sizing: border-box;
+ }
+ #quiz-carry-pregame-overlay.is-hidden { display: none !important; }
+ .quiz-carry-pregame-backdrop {
+ position: absolute;
+ inset: 0;
+ background: rgba(4, 6, 14, 0.78);
+ backdrop-filter: blur(10px);
+ }
+ .quiz-carry-pregame-sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+ }
+ .quiz-carry-pregame-card {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ max-width: min(94vw, 900px);
+ width: 100%;
+ max-height: min(94vh, 900px);
+ overflow: hidden;
+ background: rgba(10, 12, 26, 0.55);
+ border: none;
+ border-radius: 4px;
+ box-shadow: none;
+ pointer-events: auto;
+ }
+ .quiz-carry-pregame-art {
+ flex: 1 1 auto;
+ min-height: 0;
+ margin: 0;
+ padding: 0;
+ overflow: auto;
+ background: transparent;
+ display: flex;
+ align-items: flex-start;
+ justify-content: center;
+ }
+ .quiz-carry-pregame-howto-img {
+ display: block;
+ width: 100%;
+ height: auto;
+ max-height: min(62vh, 560px);
+ object-fit: contain;
+ object-position: top center;
+ border: none;
+ outline: none;
+ box-shadow: none;
+ }
+ .quiz-carry-pregame-footer {
+ flex: 0 0 auto;
+ padding: 0.85rem 1rem 1rem;
+ background: linear-gradient(180deg, rgba(8, 10, 22, 0.2), rgba(8, 10, 22, 0.96));
+ border-top: none;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.65rem;
+ }
+ .quiz-carry-pregame-status {
+ margin: 0;
+ text-align: center;
+ font: 700 clamp(0.8rem, 2.4vw, 0.95rem) / 1.35 system-ui, "Kanit", sans-serif;
+ color: #fbcfe8;
+ text-shadow: 0 0 16px rgba(244, 114, 182, 0.5);
+ letter-spacing: 0.02em;
+ }
+ .quiz-carry-pregame-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.85rem 1.25rem;
+ justify-content: center;
+ align-items: center;
+ }
+ .quiz-carry-pregame-primary-btn {
+ background: transparent;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ line-height: 0;
+ border-radius: 6px;
+ transition: transform 0.12s ease, filter 0.12s ease, opacity 0.15s ease;
+ }
+ .quiz-carry-pregame-primary-btn:hover:not(:disabled):not(.is-read-only) {
+ transform: scale(1.03);
+ filter: brightness(1.08);
+ }
+ .quiz-carry-pregame-primary-btn.is-pressed:not(.is-start-phase) {
+ filter: brightness(1.12) drop-shadow(0 0 12px rgba(94, 234, 212, 0.55));
+ }
+ .quiz-carry-pregame-primary-btn.is-read-only,
+ .quiz-carry-pregame-primary-btn:disabled {
+ opacity: 0.38;
+ pointer-events: none;
+ cursor: default;
+ filter: grayscale(0.2);
+ }
+ .quiz-carry-pregame-primary-btn img {
+ display: block;
+ width: min(220px, 42vw);
+ height: auto;
+ }
#play-quiz-scoreboard {
position: fixed;
top: max(56px, env(safe-area-inset-top));
@@ -696,6 +816,23 @@
+
+
+
+
ประลองความรู้ คำศัพท์ในกระบวนการยุติธรรม — How to play
+
+

+
+
+
+
คำถาม · Question
@@ -765,7 +902,7 @@
-
+
v —