From 8ca841364a6bc4e588f78df99451a0768fe60524 Mon Sep 17 00:00:00 2001 From: giteaadmin Date: Fri, 24 Apr 2026 15:11:25 +0000 Subject: [PATCH] fix(play+editor): quiz_carry question on map panel in embed + optional Q zone play: show quiz-map-question-panel in editor embed for quiz_carry only; bounds from quizQuestionArea when painted else hub; refactor tile bounds helper; init quizQuestionArea on join; draw gold Q tiles in play; strip hidden for carry editor: paint quizQuestion on quiz_carry, ensureQuizCarryAreas syncs quizQuestionArea, toggle draw mode + hints; editor.html help + cache v0.040 play.js v=0.093 Made-with: Cursor --- www/html/Game/public/editor.html | 4 +- www/html/Game/public/js/editor.js | 49 +++++++++++++++++++---- www/html/Game/public/js/play.js | 64 +++++++++++++------------------ www/html/Game/public/play.html | 2 +- 4 files changed, 72 insertions(+), 47 deletions(-) diff --git a/www/html/Game/public/editor.html b/www/html/Game/public/editor.html index 7020074..2cb5be9 100644 --- a/www/html/Game/public/editor.html +++ b/www/html/Game/public/editor.html @@ -173,7 +173,7 @@
  • พื้นที่สุ่มจุดเกิด (ฟ้าอ่อน) — ไม่วาดช่องนี้ได้ ใช้ปุ่ม «ตั้งจุดเกิด»
  • พื้นที่เริ่มเกม (ห้องโถง — ส้ม) — host ยืนแล้วกดเริ่ม
  • ถามตอบ: โซนถูก/ผิด + พื้นที่คำถาม (ทอง)
  • -
  • หยิบมาวาง: ม่วง = โชว์คำถาม (กำแพง) · interactive เขียว = ยืนแล้วกด F ส่งคำตอบ · สีต่าง ๆ = ตัวเลือก 1–16 · คำถามในแมป quizQuestions ใส่ choices + correctIndex หรือ answerTrue
  • +
  • หยิบมาวาง: ม่วง = โซนกลาง (กำแพง) · ทอง = พื้นที่โชว์ข้อความคำถามบนแผนที่ (ถ้าไม่วาดทอง ข้อความไปที่โซนม่วง) · interactive เขียว = ยืนแล้วกด F ส่งคำตอบ · สี = ตัวเลือก 1–16 · quizQuestions ใส่ choices + correctIndex หรือ answerTrue
  • Stack: โหมดวาดจุดปล่อย (ฟ้า) · จุดซ้อนตึก (ชมพู)
  • กระโดดให้รอด: โหมด แพลตฟอร์ม (ฟ้าอมเขียว) · กำแพง = ขอบซ้ายขวา/บน · Space / W = กระโดด
  • ลูกโป้งยิงบอส: จุดเกิดผู้เล่น P1–P6 + จุดเกิดบอส 1 ช่อง · ในเกมเดินช้า ยิงมีดีเลย์ · ลูกโป้งหมด = ตกรอบ
  • @@ -188,7 +188,7 @@ - +
    v —
    diff --git a/www/html/Game/public/js/editor.js b/www/html/Game/public/js/editor.js index 8485ffc..70c221c 100644 --- a/www/html/Game/public/js/editor.js +++ b/www/html/Game/public/js/editor.js @@ -203,6 +203,18 @@ if (!quizCarryOptionArea[y] || quizCarryOptionArea[y].length !== width) quizCarryOptionArea[y] = Array(width).fill(0); } } + if (!quizQuestionArea.length || quizQuestionArea.length !== height) { + const existingQ = quizQuestionArea.slice().map(r => r && r.slice()); + quizQuestionArea = []; + for (let y = 0; y < height; y++) { + const row = existingQ[y] && existingQ[y].length === width ? existingQ[y].slice() : Array(width).fill(0); + quizQuestionArea.push(row); + } + } else { + for (let y = 0; y < height; y++) { + if (!quizQuestionArea[y] || quizQuestionArea[y].length !== width) quizQuestionArea[y] = Array(width).fill(0); + } + } } function ensureQuizBattleDomeArea() { @@ -450,7 +462,7 @@ const gt = gameTypeEl ? gameTypeEl.value : 'zep'; if (dqTrue) dqTrue.hidden = gt !== 'quiz'; if (dqFalse) dqFalse.hidden = gt !== 'quiz'; - if (dqQ) dqQ.hidden = gt !== 'quiz'; + if (dqQ) dqQ.hidden = gt !== 'quiz' && gt !== 'quiz_carry'; if (dqStackR) dqStackR.hidden = gt !== 'stack'; if (dqStackL) dqStackL.hidden = gt !== 'stack'; const showCarry = gt === 'quiz_carry'; @@ -528,7 +540,8 @@ froggerWrap.style.display = 'none'; if (hint) { hint.innerHTML = '

    หยิบมาวาง — หยิบตัวเลือกแล้วไปส่งที่โซน interactive (เขียว)

    ' + editorHintBulletList([ - 'โซนกลาง (ม่วง) — แสดงคำถาม · ในเกมเป็นกำแพง (เดินเข้าไม่ได้)', + 'โซนกลาง (ม่วง) — กำแพง · ถ้าไม่วาดโซนทองด้านล่าง ข้อความคำถามจะโชว์บนโซนม่วงในเกม', + 'พื้นที่โชว์ข้อความคำถาม (ทอง) — วาดโซนทองแยกได้ · ในเกมข้อความจะอยู่ตรงโซนที่วาด', 'โซน interactive (เขียว) — ยืนบนหรือชิดขอบแล้วกด F ส่งคำตอบ (วาดอย่างน้อย 1 ช่อง — ถ้าไม่วาดจะใช้ชิดโซนกลางแทน)', 'ตัวเลือก 1–16 — ตรงกับลำดับ choices ใน Admin หรือแมป', 'เล่นพร้อมกันได้ · คำถามจากแมปหรือ Admin', @@ -645,7 +658,10 @@ if (btnClrBb) btnClrBb.hidden = gt !== 'balloon_boss'; if (drawModeEl && drawModeEl.value === 'balloonBossPlayerPaint' && gt !== 'balloon_boss') drawModeEl.value = 'wall'; if (drawModeEl && drawModeEl.value === 'balloonBossBossPaint' && gt !== 'balloon_boss') drawModeEl.value = 'wall'; - if (drawModeEl && (drawModeEl.value === 'quizTrue' || drawModeEl.value === 'quizFalse' || drawModeEl.value === 'quizQuestion') && gt !== 'quiz') { + if (drawModeEl && (drawModeEl.value === 'quizTrue' || drawModeEl.value === 'quizFalse') && gt !== 'quiz') { + drawModeEl.value = 'wall'; + } + if (drawModeEl && drawModeEl.value === 'quizQuestion' && gt !== 'quiz' && gt !== 'quiz_carry') { drawModeEl.value = 'wall'; } if (drawModeEl && (drawModeEl.value === 'stackRelease' || drawModeEl.value === 'stackLand') && gt !== 'stack') { @@ -903,6 +919,19 @@ ctx.textAlign = 'left'; } if (gtDraw === 'quiz_carry') { + if (quizQuestionArea[y] && quizQuestionArea[y][x] === 1) { + ctx.fillStyle = 'rgba(255, 214, 102, 0.44)'; + ctx.fillRect(tx + 2, ty + 2, tileSize - 4, tileSize - 4); + ctx.strokeStyle = 'rgba(224, 185, 70, 0.96)'; + ctx.lineWidth = 2; + ctx.strokeRect(tx + 2, ty + 2, tileSize - 4, tileSize - 4); + ctx.lineWidth = 1; + ctx.fillStyle = '#1a1b26'; + ctx.font = 'bold 9px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText('Q', tx + tileSize / 2, ty + tileSize / 2 + 3); + ctx.textAlign = 'left'; + } const ov = quizCarryOptionArea[y] && quizCarryOptionArea[y][x]; if (quizCarryHubArea[y] && quizCarryHubArea[y][x] === 1) { ctx.fillStyle = 'rgba(187, 154, 247, 0.45)'; @@ -1145,9 +1174,14 @@ quizFalseArea[y][x] = left ? 1 : 0; if (left) quizTrueArea[y][x] = 0; } else if (drawModeEl.value === 'quizQuestion') { - if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'quiz') return; - ensureQuizAreas(); - quizQuestionArea[y][x] = left ? 1 : 0; + const gtq = gameTypeEl ? gameTypeEl.value : gameType; + if (gtq === 'quiz') { + ensureQuizAreas(); + quizQuestionArea[y][x] = left ? 1 : 0; + } else if (gtq === 'quiz_carry') { + ensureQuizCarryAreas(); + quizQuestionArea[y][x] = left ? 1 : 0; + } else return; } else if (drawModeEl.value === 'stackRelease') { if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'stack') return; ensureStackAreas(); @@ -1324,7 +1358,8 @@ gameType = gameTypeEl.value; if (drawModeEl && drawModeEl.value === 'startGame' && gameType !== 'lobby') drawModeEl.value = 'wall'; if (drawModeEl && drawModeEl.value === 'spawnArea' && gameType === 'frogger') drawModeEl.value = 'wall'; - if (drawModeEl && (drawModeEl.value === 'quizTrue' || drawModeEl.value === 'quizFalse' || drawModeEl.value === 'quizQuestion') && gameType !== 'quiz') drawModeEl.value = 'wall'; + if (drawModeEl && (drawModeEl.value === 'quizTrue' || drawModeEl.value === 'quizFalse') && gameType !== 'quiz') drawModeEl.value = 'wall'; + if (drawModeEl && drawModeEl.value === 'quizQuestion' && gameType !== 'quiz' && gameType !== 'quiz_carry') drawModeEl.value = 'wall'; if (drawModeEl && (drawModeEl.value === 'stackRelease' || drawModeEl.value === 'stackLand') && gameType !== 'stack') drawModeEl.value = 'wall'; if (drawModeEl && quizCarryEditorCarryModes().indexOf(drawModeEl.value) >= 0 && gameType !== 'quiz_carry') drawModeEl.value = 'wall'; if (drawModeEl && drawModeEl.value === 'jumpSurvivePlatform' && gameType !== 'jump_survive') drawModeEl.value = 'wall'; diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index 9197a11..5efab5c 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -688,9 +688,7 @@ window.__playQuizFeedbackT = setTimeout(() => { el.classList.add('is-hidden'); }, 4200); } - function getQuizQuestionAreaTileBounds(md) { - if (!md || md.gameType !== 'quiz') return null; - const grid = md.quizQuestionArea; + function getTileBoundsForOnesGrid(grid) { if (!grid || !grid.length) return null; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (let yy = 0; yy < grid.length; yy++) { @@ -709,12 +707,23 @@ return { minX, minY, maxX, maxY }; } + function getQuizQuestionAreaTileBounds(md) { + if (!md || md.gameType !== 'quiz') return null; + return getTileBoundsForOnesGrid(md.quizQuestionArea); + } + + /** quiz_carry: โซนทองพิเศษสำหรับข้อความคำถาม (ถ้าไม่วาด ใช้โซนกลางม่วงแทนใน syncPlayQuizMapPanel) */ + function getQuizCarryQuestionAreaTileBounds(md) { + if (!md || md.gameType !== 'quiz_carry') return null; + return getTileBoundsForOnesGrid(md.quizQuestionArea); + } + function syncPlayQuizMapPanel() { const panel = document.getElementById('quiz-map-question-panel'); const textEl = document.getElementById('quiz-map-question-text'); if (!panel || !textEl || !mapData) return; - /* เอดิเตอร์ embed: อย่าวางแผงคำถามทับกลางจอ (quiz / quiz_carry ใช้กรอบทองนี้) — บังมองและรู้สึกเหมือนค้าง */ - if (previewMode && editorEmbedReturn) { + /* เอดิเตอร์ embed: ซ่อนแผงทองเฉพาะโหมด quiz (ทับกลาง) — quiz_carry ต้องโชว์ตามโซนที่วาดบนแผนที่ */ + if (previewMode && editorEmbedReturn && !isQuizCarry()) { panel.classList.add('is-hidden'); panel.setAttribute('aria-hidden', 'true'); return; @@ -727,7 +736,7 @@ if (!isQuiz() && !isQuizCarry()) return; const bounds = isQuiz() ? getQuizQuestionAreaTileBounds(mapData) - : getQuizCarryHubTileBounds(mapData); + : (getQuizCarryQuestionAreaTileBounds(mapData) || getQuizCarryHubTileBounds(mapData)); const text = (playQuizText || '').trim(); if (!bounds || !text) { panel.classList.add('is-hidden'); @@ -749,41 +758,14 @@ panel.setAttribute('aria-hidden', 'false'); } - /** เอดิเตอร์ embed quiz_carry: แผงทองทับกลางถูกปิด — แสดงคำถามแถบล่างแทน (ไม่บังจอ) */ + /** quiz_carry + embed: คำถามไปที่แผงทองบนแผนที่ / ใน overlay นับถอยหลัง — ไม่ใช้แถบบน */ function syncQuizCarryEmbedQuestionStrip() { const wrap = document.getElementById('quiz-carry-embed-q-strip'); const p = document.getElementById('quiz-carry-embed-q-strip-text'); if (!wrap || !p) return; - if (!(previewMode && editorEmbedReturn && isQuizCarry())) { - wrap.classList.remove('quiz-carry-embed-q-strip--countdown'); - wrap.classList.add('is-hidden'); - wrap.setAttribute('aria-hidden', 'true'); - return; - } - const blocking = isQuizCarryEmbedCountdownBlockingMovement(); - const pending = quizCarryEmbedPendingQuestion; - let t = ''; - if (blocking && pending) { - t = formatQuizCarryQuestionHud(pending); - if (t) wrap.classList.add('quiz-carry-embed-q-strip--countdown'); - else wrap.classList.remove('quiz-carry-embed-q-strip--countdown'); - } else if (blocking) { - wrap.classList.remove('quiz-carry-embed-q-strip--countdown'); - wrap.classList.add('is-hidden'); - wrap.setAttribute('aria-hidden', 'true'); - return; - } else { - wrap.classList.remove('quiz-carry-embed-q-strip--countdown'); - t = (playQuizText || '').trim(); - } - if (!t) { - wrap.classList.add('is-hidden'); - wrap.setAttribute('aria-hidden', 'true'); - return; - } - p.textContent = t; - wrap.classList.remove('is-hidden'); - wrap.setAttribute('aria-hidden', 'false'); + wrap.classList.remove('quiz-carry-embed-q-strip--countdown'); + wrap.classList.add('is-hidden'); + wrap.setAttribute('aria-hidden', 'true'); } function updatePlayQuizTimerDisplay() { @@ -5027,6 +5009,7 @@ if (!mapData.quizCarryHubArea) mapData.quizCarryHubArea = []; if (!mapData.quizCarryOptionArea) mapData.quizCarryOptionArea = []; if (!mapData.quizQuestions) mapData.quizQuestions = []; + if (!mapData.quizQuestionArea) mapData.quizQuestionArea = []; normalizeQuizCarryLayersInPlay(mapData); } if (mapData.gameType === 'quiz_battle') { @@ -6944,6 +6927,13 @@ ctx.strokeStyle = 'rgba(200, 170, 255, 0.75)'; ctx.strokeRect(sx + 2, sy + 2, size - 4, size - 4); } + const isCarryQ = mapData.quizQuestionArea && mapData.quizQuestionArea[y] && mapData.quizQuestionArea[y][x] === 1; + if (isCarryQ) { + ctx.fillStyle = 'rgba(255, 214, 102, 0.3)'; + ctx.fillRect(sx + 2, sy + 2, size - 4, size - 4); + ctx.strokeStyle = 'rgba(224, 185, 70, 0.82)'; + ctx.strokeRect(sx + 2, sy + 2, size - 4, size - 4); + } const ov = mapData.quizCarryOptionArea && mapData.quizCarryOptionArea[y] && mapData.quizCarryOptionArea[y][x]; if (ov >= 1 && ov <= QUIZ_CARRY_MAX_OPTION_SLOTS) { ctx.fillStyle = quizCarryMinimapOptionFillCss(ov); diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index bc4db86..6625ec4 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -765,7 +765,7 @@ - +
    v —