mini game 4 update table

This commit is contained in:
2026-04-29 08:42:34 +00:00
parent 5df6d16c6b
commit 0975874262
8 changed files with 106 additions and 44 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+25 -25
View File
@@ -30,16 +30,32 @@
"fixedBorder": "rgba(122, 200, 255, 0)",
"fillBg": "rgba(12, 10, 20, 0)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 2.5,
"borderWidthPx": 0,
"plaqueImageUrl": "https:\/\/srv1361159.hstgr.cloud\/Game\/img\/quiz-carry\/board-1.png"
},
{
"borderMode": "neon",
"fixedBorder": "rgba(122, 200, 255, 0.9)",
"fillBg": "rgba(12, 10, 20, 0.88)",
"borderMode": "fixed",
"fixedBorder": "rgba(122, 200, 255, 0)",
"fillBg": "rgba(12, 10, 20, 0)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 2.5,
"plaqueImageUrl": ""
"borderWidthPx": 0,
"plaqueImageUrl": "https:\/\/srv1361159.hstgr.cloud\/Game\/img\/quiz-carry\/board-2.png"
},
{
"borderMode": "fixed",
"fixedBorder": "rgba(122, 200, 255, 0)",
"fillBg": "rgba(12, 10, 20, 0)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 0,
"plaqueImageUrl": "https:\/\/srv1361159.hstgr.cloud\/Game\/img\/quiz-carry\/board-3.png"
},
{
"borderMode": "fixed",
"fixedBorder": "rgba(122, 200, 255, 0)",
"fillBg": "rgba(12, 10, 20, 0)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 0,
"plaqueImageUrl": "https:\/\/srv1361159.hstgr.cloud\/Game\/img\/quiz-carry\/board-4.png"
},
{
"borderMode": "neon",
@@ -47,23 +63,7 @@
"fillBg": "rgba(12, 10, 20, 0.88)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 2.5,
"plaqueImageUrl": ""
},
{
"borderMode": "neon",
"fixedBorder": "rgba(122, 200, 255, 0.9)",
"fillBg": "rgba(12, 10, 20, 0.88)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 2.5,
"plaqueImageUrl": ""
},
{
"borderMode": "neon",
"fixedBorder": "rgba(122, 200, 255, 0.9)",
"fillBg": "rgba(12, 10, 20, 0.88)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 2.5,
"plaqueImageUrl": ""
"plaqueImageUrl": "https:\/\/srv1361159.hstgr.cloud\/Game\/img\/quiz-carry\/board-5.png"
},
{
"borderMode": "neon",
@@ -159,7 +159,7 @@
"fixedBorder": "rgba(122, 200, 255, 0)",
"fillBg": "rgba(12, 10, 20, 0)",
"textColor": "rgba(248, 249, 255, 1)",
"borderWidthPx": 2.5,
"borderWidthPx": 0,
"plaqueImageUrl": "https:\/\/srv1361159.hstgr.cloud\/Game\/img\/quiz-carry\/board-1.png"
},
"questions": [
@@ -278,5 +278,5 @@
"correctIndex": 0
}
],
"carryChoicePlaqueMapScale": 1.25
"carryChoicePlaqueMapScale": 1.7
}
+1 -1
View File
@@ -271,7 +271,7 @@
</div>
</div>
<script src="js/version.js?v=0.0169"></script>
<script src="js/editor.js?v=20260428-quiz-carry-toast-silent"></script>
<script src="js/editor.js?v=20260428-quiz-carry-save-warn"></script>
<div class="version-tag">v —</div>
</body>
</html>
+15
View File
@@ -2144,6 +2144,21 @@
if (data.ok) {
const id = data.mapId || mapId;
statusEl.textContent = 'บันทึกแล้ว รหัสฉาก: ' + id + (showMapInGame ? '' : ' (ซ่อนกริดในเกม — รูปบนกริดยังแสดง) — ออกจากห้องแล้วเข้าใหม่ หรือกด F5 ในหน้าเล่น ถึงจะเห็นผล');
if (gameType === 'quiz_carry' && gridImageSprites.length) {
const used = new Set();
gridImageSprites.forEach(function (s) {
if (s && Number.isFinite(s.i) && s.i >= 0) used.add(Math.floor(s.i));
});
const noHeld = [];
used.forEach(function (idx) {
const e = gridImageLibrary[idx];
if (!gridLibHeldUrl(e)) noHeld.push(idx);
});
if (noHeld.length) {
const labels = noHeld.sort(function (a, b) { return a - b; }).map(function (i) { return '#' + (i + 1); });
statusEl.textContent += ' — เตือน (หยิบมาวาง): คลัง ' + labels.join(', ') + ' ยังไม่มีรูป «หยิบแล้ว» — ในเกมรูปบนพื้น/มือจะไม่สลับ (อัปโหลดช่องที่สองในคลัง หรือให้ held ต่างจาก idle)';
}
}
if (!mapId) window.history.replaceState({}, '', 'editor.html' + buildEditorSearch(id));
} else statusEl.textContent = data.error || 'บันทึกไม่ได้';
})
+62 -15
View File
@@ -2147,6 +2147,34 @@
return false;
}
/** มีใครกำลังถือตัวเลือกอยู่หรือไม่ — ใช้ให้รูปบนกริดที่ทับโซนกลาง (hub) เปลี่ยนสถานะ */
function quizCarryAnyPlayerOrBotHolding() {
if (me && me.quizCarryHeld != null) return true;
for (const o of others.values()) {
if (o && o.quizCarryHeld != null) return true;
}
return false;
}
/** สไปรต์ทับช่องโซนกลาง (ม่วง) อย่างน้อย 1 ช่อง */
function spriteOverlapsQuizCarryHubArea(md, sp) {
if (!md || md.gameType !== 'quiz_carry' || !sp) return false;
const hub = md.quizCarryHubArea;
if (!hub || !hub.length) return false;
const mw = md.width || 20, mh = md.height || 15;
const x0 = sp.x | 0, y0 = sp.y | 0, ww = sp.w | 0, hh = sp.h | 0;
for (let yy = y0; yy < y0 + hh; yy++) {
if (yy < 0 || yy >= mh) continue;
const row = hub[yy];
if (!row) continue;
for (let xx = x0; xx < x0 + ww; xx++) {
if (xx < 0 || xx >= mw) continue;
if (row[xx] === 1) return true;
}
}
return false;
}
/** ตัวเลือกหนึ่งข้อ — มีคนถือป้ายอยู่ได้แค่คนเดียว (หยิบซ้ำไม่ได้) */
function pickQuizCarryTargetOptionIndexAvoidingTaken(preferredIdx) {
const n = quizCarryCurrent && quizCarryCurrent.choices ? quizCarryCurrent.choices.length : 0;
@@ -6298,26 +6326,37 @@
});
}
/** โซนตัวเลือกที่ทับสไปรต์มากที่สุด — quiz_carry สลับรูป idle/held */
/** โซนตัวเลือกที่ทับสไปรต์มากที่สุด — quiz_carry สลับรูป idle/held · ถ้าไม่ทับเลยลองขยายขอบ 1 ช่อง (มาร์กเกอร์ชิดขอบสไปรต์) */
function dominantQuizCarryOptionInSpriteRect(md, sp) {
if (!md || md.gameType !== 'quiz_carry' || !md.quizCarryOptionArea) return null;
const g = md.quizCarryOptionArea;
const mw = md.width || 20, mh = md.height || 15;
const x0 = sp.x | 0, y0 = sp.y | 0, ww = sp.w | 0, hh = sp.h | 0;
const bx = sp.x | 0, by = sp.y | 0, bw = sp.w | 0, bh = sp.h | 0;
const counts = new Map();
for (let yy = y0; yy < y0 + hh; yy++) {
if (yy < 0 || yy >= mh) continue;
const row = g[yy];
if (!row) continue;
for (let xx = x0; xx < x0 + ww; xx++) {
if (xx < 0 || xx >= mw) continue;
const v = row[xx];
const n = typeof v === 'number' ? v : parseInt(String(v), 10);
if (!Number.isFinite(n) || n < 1 || n > QUIZ_CARRY_MAX_OPTION_SLOTS) continue;
const idx = n - 1;
counts.set(idx, (counts.get(idx) || 0) + 1);
function addOverlapRect(x0, y0, ww, hh) {
for (let yy = y0; yy < y0 + hh; yy++) {
if (yy < 0 || yy >= mh) continue;
const row = g[yy];
if (!row) continue;
for (let xx = x0; xx < x0 + ww; xx++) {
if (xx < 0 || xx >= mw) continue;
const v = row[xx];
const n = typeof v === 'number' ? v : parseInt(String(v), 10);
if (!Number.isFinite(n) || n < 1 || n > QUIZ_CARRY_MAX_OPTION_SLOTS) continue;
const idx = n - 1;
counts.set(idx, (counts.get(idx) || 0) + 1);
}
}
}
addOverlapRect(bx, by, bw, bh);
if (!counts.size) {
const pad = 1;
const x0 = Math.max(0, bx - pad);
const y0 = Math.max(0, by - pad);
const x1 = Math.min(mw, bx + bw + pad);
const y1 = Math.min(mh, by + bh + pad);
addOverlapRect(x0, y0, x1 - x0, y1 - y0);
}
if (!counts.size) return null;
let best = null;
let bestN = -1;
@@ -8291,11 +8330,18 @@
const wh = sp.h * tileSize;
if (wx0 + ww <= worldMinX || wx0 >= worldMaxX || wy0 + wh <= worldMinY || wy0 >= worldMaxY) continue;
let gim = mapGridImageImgs[sp.i];
if (isQuizCarry() && quizCarryCurrent) {
let carrySpriteAlpha = 1;
if (isQuizCarry()) {
const domOpt = quizCarryOptionIndexForGridSprite(mapData, sp);
if (domOpt != null && quizCarryOptionHeldByAnyone(domOpt)) {
const onHub = spriteOverlapsQuizCarryHubArea(mapData, sp);
const anyHeld = quizCarryAnyPlayerOrBotHolding();
let carryVisualActive = false;
if (domOpt != null && quizCarryOptionHeldByAnyone(domOpt)) carryVisualActive = true;
else if (domOpt == null && onHub && anyHeld) carryVisualActive = true;
if (carryVisualActive) {
const gh = mapGridImageHeldImgs[sp.i];
if (gh && gh.complete && gh.naturalWidth) gim = gh;
else carrySpriteAlpha = 0.32;
}
}
if (!gim || !gim.complete || !gim.naturalWidth) continue;
@@ -8304,6 +8350,7 @@
const sw = ww * zDraw;
const sh = wh * zDraw;
ctx.save();
if (carrySpriteAlpha < 1) ctx.globalAlpha = carrySpriteAlpha;
ctx.beginPath();
ctx.rect(sx0, sy0, sw, sh);
ctx.clip();
+1 -1
View File
@@ -980,7 +980,7 @@
</div>
<script src="/Game/socket.io/socket.io.js"></script>
<script src="js/version.js?v=0.0166"></script>
<script src="js/play.js?v=0.133"></script>
<script src="js/play.js?v=0.135"></script>
<div class="version-tag">v —</div>
</body>
</html>