update mock design
@@ -126,6 +126,7 @@
|
||||
<optgroup label="Quiz Battle">
|
||||
<option value="quizBattlePath" id="draw-mode-option-quiz-battle-path" hidden>เส้นทางเดิน (ม่วง — วาดอย่างน้อย 1 ช่อง = จำกัดเดินเฉพาะเส้นทาง)</option>
|
||||
<option value="quizBattleDome" id="draw-mode-option-quiz-battle-dome" hidden>โดมคำถาม (ช่องกด E ในเกม)</option>
|
||||
<option value="quizBattlePathGate" id="draw-mode-option-quiz-battle-path-gate" hidden>จุดปลดล็อก / ประตู (แดง — วางบนเส้นทาง ต้องตอบสถานีก่อนผ่าน)</option>
|
||||
</optgroup>
|
||||
<optgroup label="กระโดดให้รอด">
|
||||
<option value="jumpSurvivePlatform1" id="draw-mode-option-jump-platform-1" hidden>แพลตฟอร์ม 1 (ยืนได้ — รูปช่อง 1 จาก Admin)</option>
|
||||
@@ -162,6 +163,14 @@
|
||||
</optgroup>
|
||||
</select>
|
||||
</label>
|
||||
<span id="quiz-battle-gate-wrap" class="editor-quiz-battle-gate-wrap" style="display:none;flex-wrap:wrap;align-items:center;gap:0.35rem 0.65rem;max-width:100%;" title="เลือกคำถามที่ต้องตอบถูกก่อนผ่าน — คลิกโดมเพื่อเลือก · คลิกเส้นทางม่วงวางประตู · ขวาลบ">
|
||||
<label style="display:flex;flex-direction:column;gap:0.15rem;min-width:12rem;flex:1 1 220px;">คำถามปลดล็อก
|
||||
<select id="quiz-battle-gate-question" style="max-width:100%;">
|
||||
<option value="1">กำลังโหลดคำถาม…</option>
|
||||
</select>
|
||||
</label>
|
||||
<button type="button" id="quiz-battle-gate-reload" class="editor-btn-secondary" style="font-size:0.72rem;padding:0.2rem 0.45rem;">โหลดคำถามใหม่</button>
|
||||
</span>
|
||||
<span id="cell-color-wrap" class="editor-cell-color" style="display:none;">
|
||||
<label>สี <input type="color" id="cell-color" value="#24283b" title="สี"></label>
|
||||
<label>Alpha <input type="range" id="cell-alpha" min="0" max="100" value="100" title="ความโปร่งใส 0=ใส 100=ทึบ"> <span id="cell-alpha-value">100</span>%</label>
|
||||
@@ -407,7 +416,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/version.js?v=0.0258"></script>
|
||||
<script src="js/editor.js?v=0.0336"></script>
|
||||
<script src="js/editor.js?v=0.0338"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
|
||||
<!-- ===== QB overlay layout: map เต็มจอ + แถบเครื่องมือลอย ดัก/พับ/ปิดเปิดได้ (เพิ่มทับ ไม่แตะ editor.js) ===== -->
|
||||
|
||||
@@ -108,6 +108,13 @@
|
||||
let quizBattleDomeArea = [];
|
||||
/** quiz_battle: กริด 0/1 — ถ้ามีอย่างน้อย 1 ช่อง = ในเกมเดินได้เฉพาะบนเส้นทาง */
|
||||
let quizBattlePathArea = [];
|
||||
/** quiz_battle: กริด 0/สถานี — จุดประตูบนเส้นทาง (ต้องตอบสถานี N ก่อนผ่านช่องนี้และข้างหน้า) */
|
||||
let quizBattlePathGateArea = [];
|
||||
/** quiz_battle: หมวดคำถามของห้อง (topic1..topic10) — กรองจาก battleQuizMcq */
|
||||
let editorQuizBattleCategoryId = '';
|
||||
/** quiz_battle: รายการคำถามใน Editor [{ compId, text }] */
|
||||
let editorQuizBattleMcqList = [];
|
||||
let editorQuizBattleDomeComp = null;
|
||||
let stackReleaseArea = [], stackLandArea = [];
|
||||
/** jump_survive: แพลตฟอร์ม { x, y, w, h } tile — ใช้เต็มที่เมื่อมีมุมข้าง */
|
||||
let jumpSurvivePlatforms = [];
|
||||
@@ -1243,6 +1250,157 @@
|
||||
}
|
||||
}
|
||||
|
||||
function ensureQuizBattlePathGateArea() {
|
||||
if (!quizBattlePathGateArea.length || quizBattlePathGateArea.length !== height) {
|
||||
const existing = quizBattlePathGateArea.slice().map(r => r && r.slice());
|
||||
quizBattlePathGateArea = [];
|
||||
for (let y = 0; y < height; y++) {
|
||||
const row = existing[y] && existing[y].length === width ? existing[y].slice() : Array(width).fill(0);
|
||||
quizBattlePathGateArea.push(row);
|
||||
}
|
||||
} else {
|
||||
for (let y = 0; y < height; y++) {
|
||||
if (!quizBattlePathGateArea[y] || quizBattlePathGateArea[y].length !== width) quizBattlePathGateArea[y] = Array(width).fill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inferQuizBattleCategoryFromMapId(id) {
|
||||
const m = /^qbroom(\d+)$/i.exec(String(id || '').trim());
|
||||
if (!m) return '';
|
||||
const n = parseInt(m[1], 10);
|
||||
return n > 0 ? ('topic' + n) : '';
|
||||
}
|
||||
|
||||
function rebuildEditorQuizBattleDomeComp() {
|
||||
if (!quizBattleDomeArea.length) {
|
||||
editorQuizBattleDomeComp = null;
|
||||
return;
|
||||
}
|
||||
const w = width, h = height;
|
||||
const dome = quizBattleDomeArea;
|
||||
const comp = Array(h).fill(0).map(() => Array(w).fill(0));
|
||||
const seen = Array(h).fill(0).map(() => Array(w).fill(false));
|
||||
let compId = 0;
|
||||
for (let y = 0; y < h; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
if (!dome[y] || dome[y][x] !== 1 || seen[y][x]) continue;
|
||||
compId++;
|
||||
const q = [[x, y]];
|
||||
seen[y][x] = true;
|
||||
while (q.length) {
|
||||
const [cx, cy] = q.shift();
|
||||
comp[cy][cx] = compId;
|
||||
for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
||||
const nx = cx + dx, ny = cy + dy;
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
||||
if (!dome[ny] || dome[ny][nx] !== 1 || seen[ny][nx]) continue;
|
||||
seen[ny][nx] = true;
|
||||
q.push([nx, ny]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
editorQuizBattleDomeComp = comp;
|
||||
}
|
||||
|
||||
function getEditorQuizBattleDomeCompAt(tx, ty) {
|
||||
if (!editorQuizBattleDomeComp) rebuildEditorQuizBattleDomeComp();
|
||||
if (!editorQuizBattleDomeComp || ty < 0 || tx < 0 || ty >= height || tx >= width) return 0;
|
||||
return editorQuizBattleDomeComp[ty][tx] || 0;
|
||||
}
|
||||
|
||||
function quizBattleGateQuestionSnippet(compId) {
|
||||
const row = editorQuizBattleMcqList.find((q) => q.compId === compId);
|
||||
if (!row || !row.text) return '';
|
||||
const t = String(row.text).trim();
|
||||
return t.length > 48 ? t.slice(0, 48) + '…' : t;
|
||||
}
|
||||
|
||||
function sanitizeEditorBattleMcqRow(raw, roomCat) {
|
||||
if (!raw || !String(raw.text || '').trim()) return null;
|
||||
if (roomCat && String(raw.categoryId || '').trim() !== roomCat) return null;
|
||||
const ch = Array.isArray(raw.choices) ? raw.choices : [];
|
||||
if (ch.length !== 3) return null;
|
||||
const a = String(ch[0] || '').trim();
|
||||
const b = String(ch[1] || '').trim();
|
||||
const c = String(ch[2] || '').trim();
|
||||
if (!a || !b || !c) return null;
|
||||
return { text: String(raw.text).trim() };
|
||||
}
|
||||
|
||||
function rebuildQuizBattleGateQuestionSelect() {
|
||||
const sel = document.getElementById('quiz-battle-gate-question');
|
||||
if (!sel) return;
|
||||
const prev = sel.value;
|
||||
sel.innerHTML = '';
|
||||
if (!editorQuizBattleMcqList.length) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = '1';
|
||||
opt.textContent = 'ยังไม่มีคำถาม — บันทึกใน Admin → Quiz Battle';
|
||||
sel.appendChild(opt);
|
||||
return;
|
||||
}
|
||||
editorQuizBattleMcqList.forEach((row) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(row.compId);
|
||||
const snip = row.text.length > 56 ? row.text.slice(0, 56) + '…' : row.text;
|
||||
opt.textContent = 'ข้อ ' + row.compId + ': ' + snip;
|
||||
opt.title = row.text;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
if (prev && editorQuizBattleMcqList.some((q) => String(q.compId) === prev)) sel.value = prev;
|
||||
}
|
||||
|
||||
async function loadEditorQuizBattleMcqList() {
|
||||
const roomCat = editorQuizBattleCategoryId ? String(editorQuizBattleCategoryId).trim() : '';
|
||||
editorQuizBattleMcqList = [];
|
||||
const bust = '_=' + Date.now();
|
||||
const tryUrls = [
|
||||
BASE + '/api/quiz-settings?' + bust,
|
||||
BASE + '/api-quiz-battle-mcq.php?' + bust,
|
||||
];
|
||||
for (let u = 0; u < tryUrls.length; u++) {
|
||||
try {
|
||||
const r = await fetch(tryUrls[u], { cache: 'no-store' });
|
||||
if (!r.ok) continue;
|
||||
const s = await r.json();
|
||||
const arr = s && Array.isArray(s.battleQuizMcq) ? s.battleQuizMcq : [];
|
||||
let compId = 0;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const row = sanitizeEditorBattleMcqRow(arr[i], roomCat);
|
||||
if (!row) continue;
|
||||
compId++;
|
||||
editorQuizBattleMcqList.push({ compId: compId, text: row.text });
|
||||
}
|
||||
if (editorQuizBattleMcqList.length) break;
|
||||
} catch (e) { /* next */ }
|
||||
}
|
||||
rebuildQuizBattleGateQuestionSelect();
|
||||
if (statusEl && drawModeEl && drawModeEl.value === 'quizBattlePathGate') {
|
||||
const n = editorQuizBattleMcqList.length;
|
||||
statusEl.textContent = n
|
||||
? ('โหลดคำถามแล้ว ' + n + ' ข้อ' + (roomCat ? (' (' + roomCat + ')') : '') + ' — เลือกคำถามแล้วคลิกเส้นทางม่วง')
|
||||
: 'ยังไม่มีคำถามใน Admin → Quiz Battle' + (roomCat ? (' สำหรับ ' + roomCat) : '');
|
||||
}
|
||||
}
|
||||
|
||||
function readQuizBattleGateStationInput() {
|
||||
const sel = document.getElementById('quiz-battle-gate-question');
|
||||
let n = sel ? parseInt(sel.value, 10) : 1;
|
||||
if (!(n > 0)) n = 1;
|
||||
if (editorQuizBattleMcqList.length) n = Math.min(n, editorQuizBattleMcqList.length);
|
||||
else if (n > 99) n = 99;
|
||||
return n;
|
||||
}
|
||||
|
||||
function setQuizBattleGateQuestionSelect(compId) {
|
||||
const sel = document.getElementById('quiz-battle-gate-question');
|
||||
if (!sel || !(compId > 0)) return;
|
||||
const v = String(compId);
|
||||
if ([...sel.options].some((o) => o.value === v)) sel.value = v;
|
||||
}
|
||||
|
||||
function ensureStartGameArea() {
|
||||
if (!startGameArea.length || startGameArea.length !== height) {
|
||||
const existing = startGameArea.slice().map(r => r && r.slice());
|
||||
@@ -1912,6 +2070,7 @@
|
||||
const dqCarryOpts = document.querySelectorAll('.quiz-carry-mode-opt');
|
||||
const dqBattleDome = document.getElementById('draw-mode-option-quiz-battle-dome');
|
||||
const dqBattlePath = document.getElementById('draw-mode-option-quiz-battle-path');
|
||||
const dqBattleGate = document.getElementById('draw-mode-option-quiz-battle-path-gate');
|
||||
if (!froggerWrap) return;
|
||||
const gt = gameTypeEl ? gameTypeEl.value : 'zep';
|
||||
if (drawModeEl && drawModeEl.value === 'jumpSurvivePlatform') drawModeEl.value = 'jumpSurvivePlatform1';
|
||||
@@ -1932,6 +2091,10 @@
|
||||
dqBattlePath.hidden = gt !== 'quiz_battle';
|
||||
if (gt !== 'quiz_battle' && drawModeEl && drawModeEl.value === 'quizBattlePath') drawModeEl.value = 'wall';
|
||||
}
|
||||
if (dqBattleGate) {
|
||||
dqBattleGate.hidden = gt !== 'quiz_battle';
|
||||
if (gt !== 'quiz_battle' && drawModeEl && drawModeEl.value === 'quizBattlePathGate') drawModeEl.value = 'wall';
|
||||
}
|
||||
if (hint) hint.style.removeProperty('display');
|
||||
if (gt === 'frogger') {
|
||||
froggerWrap.style.display = 'block';
|
||||
@@ -2046,10 +2209,15 @@
|
||||
draw();
|
||||
} else if (gt === 'quiz_battle') {
|
||||
froggerWrap.style.display = 'none';
|
||||
if (!editorQuizBattleCategoryId && mapId) {
|
||||
editorQuizBattleCategoryId = inferQuizBattleCategoryFromMapId(mapId);
|
||||
}
|
||||
loadEditorQuizBattleMcqList();
|
||||
if (hint) {
|
||||
hint.innerHTML = '<p class="editor-hint-lead"><strong>Quiz Battle</strong> — เส้นทาง + โดมถาม A / B / C</p>' + editorHintBulletList([
|
||||
'โหมด <strong>เส้นทางเดิน (ม่วง)</strong> — วาดซิกแซกเหมือนทางในเกม · ถ้ามีอย่างน้อย 1 ช่อง ผู้เล่น<strong>เดินได้เฉพาะบนเส้นทาง</strong> · ไม่วาดเลย = เดินอิสระทั้งแมป',
|
||||
'โหมด <strong>โดมคำถาม</strong> — วางบนเส้นทาง (หรือทั่วแมปถ้าไม่ใช้เส้นทาง) · ในเกมกด <kbd>E</kbd>',
|
||||
'โหมด <strong>จุดปลดล็อก / ประตู (แดง)</strong> — เลือก<strong>คำถาม</strong>จากเมนู · คลิกโดมเพื่อเลือกคำถามนั้น · คลิกเส้นทางม่วงหลังโดมเพื่อวางประตู · ไม่วาด = คำนวณอัตโนมัติ',
|
||||
'รูป <strong>พื้นหลัง</strong> เปลี่ยนทีหลังได้ — ตรรกะเส้นทาง/โดมอยู่ที่กริด ไม่ผูกกราฟิก',
|
||||
'ข้อสอบจาก <strong>Admin → Quiz Battle</strong> (<code>battleQuizMcq</code>) · แนะนำ <strong>พื้นที่สุ่มจุดเกิด</strong> ให้อยู่บนเส้นทางหรือใกล้จุดเริ่ม',
|
||||
]);
|
||||
@@ -2146,7 +2314,7 @@
|
||||
if (drawModeEl && carryModes.indexOf(drawModeEl.value) >= 0 && gt !== 'quiz_carry') {
|
||||
drawModeEl.value = 'wall';
|
||||
}
|
||||
if (drawModeEl && (drawModeEl.value === 'quizBattleDome' || drawModeEl.value === 'quizBattlePath') && gt !== 'quiz_battle') {
|
||||
if (drawModeEl && (drawModeEl.value === 'quizBattleDome' || drawModeEl.value === 'quizBattlePath' || drawModeEl.value === 'quizBattlePathGate') && gt !== 'quiz_battle') {
|
||||
drawModeEl.value = 'wall';
|
||||
}
|
||||
const jPlat1 = document.getElementById('draw-mode-option-jump-platform-1');
|
||||
@@ -2190,6 +2358,7 @@
|
||||
carryEmbedCountdownArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
quizBattleDomeArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
quizBattlePathArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
quizBattlePathGateArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
stackReleaseArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
stackLandArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurvivePlatformArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
@@ -2317,6 +2486,8 @@
|
||||
if (gtDraw === 'quiz_battle') {
|
||||
ensureQuizBattlePathArea();
|
||||
ensureQuizBattleDomeArea();
|
||||
ensureQuizBattlePathGateArea();
|
||||
rebuildEditorQuizBattleDomeComp();
|
||||
}
|
||||
if (gtDraw === 'stack') ensureStackAreas();
|
||||
if (gtDraw === 'jump_survive') {
|
||||
@@ -2658,6 +2829,7 @@
|
||||
ctx.strokeRect(tx + 1, ty + 1, tileSize - 2, tileSize - 2);
|
||||
}
|
||||
if (gtDraw === 'quiz_battle' && quizBattleDomeArea[y] && quizBattleDomeArea[y][x] === 1) {
|
||||
const dComp = getEditorQuizBattleDomeCompAt(x, y);
|
||||
ctx.fillStyle = 'rgba(100, 200, 255, 0.42)';
|
||||
ctx.fillRect(tx + 2, ty + 2, tileSize - 4, tileSize - 4);
|
||||
ctx.strokeStyle = 'rgba(255, 100, 130, 0.92)';
|
||||
@@ -2667,7 +2839,21 @@
|
||||
ctx.fillStyle = '#1a1b26';
|
||||
ctx.font = 'bold 10px sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('E', tx + tileSize / 2, ty + tileSize / 2 + 3);
|
||||
ctx.fillText(dComp > 0 ? ('E' + dComp) : 'E', tx + tileSize / 2, ty + tileSize / 2 + 3);
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
if (gtDraw === 'quiz_battle' && quizBattlePathGateArea[y] && quizBattlePathGateArea[y][x] > 0) {
|
||||
const gn = quizBattlePathGateArea[y][x];
|
||||
ctx.fillStyle = 'rgba(255, 55, 55, 0.62)';
|
||||
ctx.fillRect(tx + Math.floor(tileSize / 2) - 4, ty + 2, 8, tileSize - 4);
|
||||
ctx.strokeStyle = 'rgba(255, 220, 120, 0.95)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(tx + Math.floor(tileSize / 2) - 4, ty + 2, 8, tileSize - 4);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.fillStyle = '#fff8e8';
|
||||
ctx.font = 'bold 8px sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('Q' + gn, tx + tileSize / 2, ty + tileSize - 5);
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
}
|
||||
@@ -3191,6 +3377,39 @@
|
||||
if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'quiz_battle') return;
|
||||
ensureQuizBattleDomeArea();
|
||||
quizBattleDomeArea[y][x] = left ? 1 : 0;
|
||||
rebuildEditorQuizBattleDomeComp();
|
||||
} else if (drawModeEl.value === 'quizBattlePathGate') {
|
||||
if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'quiz_battle') return;
|
||||
ensureQuizBattlePathArea();
|
||||
ensureQuizBattlePathGateArea();
|
||||
if (left && quizBattleDomeArea[y] && quizBattleDomeArea[y][x] === 1) {
|
||||
const dComp = getEditorQuizBattleDomeCompAt(x, y);
|
||||
if (dComp > 0) {
|
||||
setQuizBattleGateQuestionSelect(dComp);
|
||||
const snip = quizBattleGateQuestionSnippet(dComp);
|
||||
if (statusEl) {
|
||||
statusEl.textContent = 'เลือกคำถามข้อ ' + dComp + (snip ? (' — 「' + snip + '」') : '') + ' · คลิกเส้นทางม่วงเพื่อวางประตู';
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!quizBattlePathArea[y] || quizBattlePathArea[y][x] !== 1) {
|
||||
if (statusEl) statusEl.textContent = 'ประตูต้องวางบนเส้นทางม่วง (คลิกโดม E1/E2… เพื่อเลือกคำถาม)';
|
||||
return;
|
||||
}
|
||||
if (left) {
|
||||
const st = readQuizBattleGateStationInput();
|
||||
quizBattlePathGateArea[y][x] = st;
|
||||
const snip = quizBattleGateQuestionSnippet(st);
|
||||
if (statusEl) {
|
||||
statusEl.textContent = 'ประตูข้อ ' + st + (snip ? (' 「' + snip + '」') : '') + ' → (' + x + ',' + y + ')';
|
||||
}
|
||||
const next = editorQuizBattleMcqList.find((q) => q.compId > st);
|
||||
if (next) setQuizBattleGateQuestionSelect(next.compId);
|
||||
} else {
|
||||
quizBattlePathGateArea[y][x] = 0;
|
||||
if (statusEl) statusEl.textContent = 'ลบประตูช่อง (' + x + ',' + y + ')';
|
||||
}
|
||||
} else if (drawModeEl.value === 'carryEmbedCountdown') {
|
||||
if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'quiz_carry') return;
|
||||
ensureQuizCarryAreas();
|
||||
@@ -3613,7 +3832,7 @@
|
||||
if (drawModeEl && drawModeEl.value === 'shooterSpawnPaint' && gameType !== 'space_shooter' && gameType !== 'jump_survive') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'balloonBossPlayerPaint' && gameType !== 'balloon_boss') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'balloonBossBossPaint' && gameType !== 'balloon_boss') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && (drawModeEl.value === 'quizBattleDome' || drawModeEl.value === 'quizBattlePath') && gameType !== 'quiz_battle') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && (drawModeEl.value === 'quizBattleDome' || drawModeEl.value === 'quizBattlePath' || drawModeEl.value === 'quizBattlePathGate') && gameType !== 'quiz_battle') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'lobbyPlayerSpawn' && !supportsLobbySpawnPaint(gameType)) drawModeEl.value = 'wall';
|
||||
toggleFroggerUI();
|
||||
syncEditorStackHudMock();
|
||||
@@ -3740,6 +3959,7 @@
|
||||
ensureQuizCarryAreas();
|
||||
ensureQuizBattleDomeArea();
|
||||
ensureQuizBattlePathArea();
|
||||
ensureQuizBattlePathGateArea();
|
||||
ensureJumpSurvivePlatformArea();
|
||||
ensureJumpSurviveHazardArea();
|
||||
ensureShooterSpawnSlots();
|
||||
@@ -3802,6 +4022,10 @@
|
||||
})(),
|
||||
quizBattleDomeArea: gameType === 'quiz_battle' ? quizBattleDomeArea.map(r => r.slice()) : [],
|
||||
quizBattlePathArea: gameType === 'quiz_battle' ? quizBattlePathArea.map(r => r.slice()) : [],
|
||||
quizBattlePathGateArea: gameType === 'quiz_battle' ? quizBattlePathGateArea.map(r => r.slice()) : [],
|
||||
quizBattleCategoryId: gameType === 'quiz_battle' && editorQuizBattleCategoryId
|
||||
? String(editorQuizBattleCategoryId).trim()
|
||||
: undefined,
|
||||
stackReleaseArea: gameType === 'stack' ? stackReleaseArea.map(r => r.slice()) : [],
|
||||
stackLandArea: gameType === 'stack' ? stackLandArea.map(r => r.slice()) : [],
|
||||
jumpSurvivePlatforms: gameType === 'jump_survive' ? jumpSurvivePlatforms.slice() : [],
|
||||
@@ -3927,6 +4151,17 @@
|
||||
const gt = gameTypeEl ? gameTypeEl.value : gameType;
|
||||
bindWrap.style.display = (drawModeEl.value === 'cellImage' && gt === 'quiz_carry') ? 'flex' : 'none';
|
||||
}
|
||||
const gateWrap = document.getElementById('quiz-battle-gate-wrap');
|
||||
if (gateWrap) {
|
||||
const gt = gameTypeEl ? gameTypeEl.value : gameType;
|
||||
const showGate = drawModeEl.value === 'quizBattlePathGate' && gt === 'quiz_battle';
|
||||
gateWrap.style.display = showGate ? 'inline-flex' : 'none';
|
||||
if (showGate && !editorQuizBattleMcqList.length) loadEditorQuizBattleMcqList();
|
||||
}
|
||||
}
|
||||
const gateReloadBtn = document.getElementById('quiz-battle-gate-reload');
|
||||
if (gateReloadBtn) {
|
||||
gateReloadBtn.addEventListener('click', () => { loadEditorQuizBattleMcqList(); });
|
||||
}
|
||||
if (drawModeEl) drawModeEl.addEventListener('change', () => { syncDrawModeAuxUi(); syncLobbySpawnAuxUi(); });
|
||||
syncDrawModeAuxUi();
|
||||
@@ -4119,6 +4354,12 @@
|
||||
: Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
quizBattleDomeArea = m.quizBattleDomeArea && m.quizBattleDomeArea.length ? m.quizBattleDomeArea.map(r => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
quizBattlePathArea = m.quizBattlePathArea && m.quizBattlePathArea.length ? m.quizBattlePathArea.map(r => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
quizBattlePathGateArea = m.quizBattlePathGateArea && m.quizBattlePathGateArea.length ? m.quizBattlePathGateArea.map(r => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
editorQuizBattleCategoryId = m.quizBattleCategoryId
|
||||
? String(m.quizBattleCategoryId).trim()
|
||||
: inferQuizBattleCategoryFromMapId(mapId);
|
||||
rebuildEditorQuizBattleDomeComp();
|
||||
if (m.gameType === 'quiz_battle') loadEditorQuizBattleMcqList();
|
||||
jumpSurvivePlatforms = Array.isArray(m.jumpSurvivePlatforms) ? m.jumpSurvivePlatforms.map((p) => (p && typeof p === 'object' ? { ...p } : null)).filter(Boolean) : [];
|
||||
jumpSurvivePlatformArea = m.jumpSurvivePlatformArea && m.jumpSurvivePlatformArea.length ? m.jumpSurvivePlatformArea.map((r) => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurvivePlatformVariantArea = m.jumpSurvivePlatformVariantArea && m.jumpSurvivePlatformVariantArea.length
|
||||
|
||||
@@ -12541,6 +12541,213 @@
|
||||
md.quizBattleDomeComp = comp;
|
||||
}
|
||||
|
||||
function hasQuizBattleManualPathGatesPlay(md) {
|
||||
const mg = md && md.quizBattlePathGateArea;
|
||||
if (!mg || !Array.isArray(mg)) return false;
|
||||
for (let ty = 0; ty < mg.length; ty++) {
|
||||
const row = mg[ty];
|
||||
if (!row) continue;
|
||||
for (let tx = 0; tx < row.length; tx++) {
|
||||
if ((row[tx] | 0) > 0) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildQuizBattlePathGateFromManualPlay(md) {
|
||||
const w = md.width || 20, h = md.height || 15;
|
||||
const path = md.quizBattlePathArea;
|
||||
const dome = md.quizBattleDomeArea;
|
||||
const manual = md.quizBattlePathGateArea;
|
||||
const gate = Array(h).fill(0).map(() => Array(w).fill(0));
|
||||
const bestReq = Array(h).fill(0).map(() => Array(w).fill(-1));
|
||||
const queue = [];
|
||||
|
||||
function enqueue(tx, ty, req) {
|
||||
if (tx < 0 || ty < 0 || tx >= w || ty >= h) return;
|
||||
const onPath = path[ty] && path[ty][tx] === 1;
|
||||
const onDome = dome && dome[ty] && dome[ty][tx] === 1;
|
||||
if (!onPath && !onDome) return;
|
||||
if (bestReq[ty][tx] >= 0 && req >= bestReq[ty][tx]) return;
|
||||
bestReq[ty][tx] = req;
|
||||
queue.push({ tx, ty, req });
|
||||
}
|
||||
|
||||
const sp = md.spawn || { x: 1, y: 1 };
|
||||
const stx = Math.max(0, Math.min(w - 1, Math.floor(Number(sp.x)) || 0));
|
||||
const sty = Math.max(0, Math.min(h - 1, Math.floor(Number(sp.y)) || 0));
|
||||
enqueue(stx, sty, 0);
|
||||
if (bestReq[sty][stx] < 0) {
|
||||
for (const [dx, dy] of [[0, 0], [1, 0], [-1, 0], [0, 1], [0, -1]]) enqueue(stx + dx, sty + dy, 0);
|
||||
}
|
||||
const sa = md.spawnArea;
|
||||
if (sa && Array.isArray(sa)) {
|
||||
for (let ty = 0; ty < h; ty++) {
|
||||
const row = sa[ty];
|
||||
if (!row) continue;
|
||||
for (let tx = 0; tx < w; tx++) {
|
||||
if (row[tx] === 1) enqueue(tx, ty, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.length) {
|
||||
const cur = queue.shift();
|
||||
const { tx, ty, req } = cur;
|
||||
if (bestReq[ty][tx] !== req) continue;
|
||||
const onDome = dome && dome[ty] && dome[ty][tx] === 1;
|
||||
let enterReq = req;
|
||||
const mg = manual && manual[ty] && manual[ty][tx] ? (manual[ty][tx] | 0) : 0;
|
||||
if (onDome) enterReq = 0;
|
||||
else if (mg > 0) enterReq = Math.max(enterReq, mg);
|
||||
if (path[ty] && path[ty][tx] === 1) gate[ty][tx] = enterReq;
|
||||
let outReq = enterReq;
|
||||
if (mg > 0) outReq = Math.max(outReq, mg);
|
||||
for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
||||
enqueue(tx + dx, ty + dy, outReq);
|
||||
}
|
||||
}
|
||||
md.quizBattlePathGateRequired = gate;
|
||||
}
|
||||
|
||||
/** ประตูบนเลน — เลนหลังโดม C ต้องตอบถูกโดม 1..C ก่อน (ยืนบนโดมได้โดยไม่ต้องตอบโดมนั้น) */
|
||||
function buildQuizBattlePathGateRequiredPlay(md) {
|
||||
if (!md || md.gameType !== 'quiz_battle' || !quizBattlePathModeActive(md)) {
|
||||
if (md) md.quizBattlePathGateRequired = null;
|
||||
return;
|
||||
}
|
||||
if (hasQuizBattleManualPathGatesPlay(md)) {
|
||||
buildQuizBattlePathGateFromManualPlay(md);
|
||||
return;
|
||||
}
|
||||
normalizeQuizBattleDomeInPlay(md);
|
||||
const w = md.width || 20, h = md.height || 15;
|
||||
const path = md.quizBattlePathArea;
|
||||
const dome = md.quizBattleDomeArea;
|
||||
const domeComp = md.quizBattleDomeComp;
|
||||
const gate = Array(h).fill(0).map(() => Array(w).fill(0));
|
||||
const dist = Array(h).fill(0).map(() => Array(w).fill(-1));
|
||||
const queue = [];
|
||||
const sp = md.spawn || { x: 1, y: 1 };
|
||||
const stx = Math.max(0, Math.min(w - 1, Math.floor(Number(sp.x)) || 0));
|
||||
const sty = Math.max(0, Math.min(h - 1, Math.floor(Number(sp.y)) || 0));
|
||||
|
||||
function tryEnqueue(tx, ty, d) {
|
||||
if (tx < 0 || ty < 0 || tx >= w || ty >= h) return;
|
||||
if (dist[ty][tx] >= 0) return;
|
||||
const onPath = path[ty] && path[ty][tx] === 1;
|
||||
const onDome = dome && dome[ty] && dome[ty][tx] === 1;
|
||||
if (!onPath && !onDome) return;
|
||||
dist[ty][tx] = d;
|
||||
queue.push({ tx, ty, d });
|
||||
}
|
||||
|
||||
tryEnqueue(stx, sty, 0);
|
||||
if (dist[sty][stx] < 0) {
|
||||
for (const [dx, dy] of [[0, 0], [1, 0], [-1, 0], [0, 1], [0, -1]]) tryEnqueue(stx + dx, sty + dy, 0);
|
||||
}
|
||||
const sa = md.spawnArea;
|
||||
if (sa && Array.isArray(sa)) {
|
||||
for (let ty = 0; ty < h; ty++) {
|
||||
const row = sa[ty];
|
||||
if (!row) continue;
|
||||
for (let tx = 0; tx < w; tx++) {
|
||||
if (row[tx] === 1) tryEnqueue(tx, ty, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const domeMinDist = {};
|
||||
let maxComp = 0;
|
||||
while (queue.length) {
|
||||
const cur = queue.shift();
|
||||
const { tx, ty, d } = cur;
|
||||
const dc = domeComp[ty] && domeComp[ty][tx] ? domeComp[ty][tx] : 0;
|
||||
if (dc > 0) {
|
||||
maxComp = Math.max(maxComp, dc);
|
||||
if (domeMinDist[dc] == null || d < domeMinDist[dc]) domeMinDist[dc] = d;
|
||||
}
|
||||
for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
||||
const nx = tx + dx, ny = ty + dy;
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
||||
if (dist[ny][nx] >= 0) continue;
|
||||
const onPath = path[ny] && path[ny][nx] === 1;
|
||||
const onDome = dome && dome[ny] && dome[ny][nx] === 1;
|
||||
if (onPath || onDome) tryEnqueue(nx, ny, d + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (let ty = 0; ty < h; ty++) {
|
||||
for (let tx = 0; tx < w; tx++) {
|
||||
if (!path[ty] || path[ty][tx] !== 1) continue;
|
||||
const d = dist[ty][tx];
|
||||
if (d < 0) {
|
||||
gate[ty][tx] = maxComp;
|
||||
continue;
|
||||
}
|
||||
let req = 0;
|
||||
for (let c = 1; c <= maxComp; c++) {
|
||||
if (domeMinDist[c] != null && domeMinDist[c] < d) req = Math.max(req, c);
|
||||
}
|
||||
gate[ty][tx] = req;
|
||||
}
|
||||
}
|
||||
md.quizBattlePathGateRequired = gate;
|
||||
}
|
||||
|
||||
function quizBattlePathGateRequiredAtPlay(md, px, py) {
|
||||
const gate = md && md.quizBattlePathGateRequired;
|
||||
if (!gate || !quizBattlePathModeActive(md)) return 0;
|
||||
const path = md.quizBattlePathArea;
|
||||
const tiles = quizBattleSpriteBoundsTilesPlay(md, px, py);
|
||||
let maxReq = 0;
|
||||
for (const k of tiles) {
|
||||
const p = k.split(',');
|
||||
const tx = +p[0], ty = +p[1];
|
||||
if (!path[ty] || path[ty][tx] !== 1) continue;
|
||||
maxReq = Math.max(maxReq, gate[ty][tx] || 0);
|
||||
}
|
||||
return maxReq;
|
||||
}
|
||||
|
||||
function quizBattleFirstMissingGateCompPlay(md, px, py, solvedSet) {
|
||||
const maxReq = quizBattlePathGateRequiredAtPlay(md, px, py);
|
||||
if (maxReq <= 0) return 0;
|
||||
for (let c = 1; c <= maxReq; c++) {
|
||||
if (!solvedSet.has(c)) return c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function quizBattlePathGateAllowsPlay(md, px, py, solvedSet) {
|
||||
if (quizBattleFootprintOnDomePlay(md, px, py)) return true;
|
||||
return quizBattleFirstMissingGateCompPlay(md, px, py, solvedSet) === 0;
|
||||
}
|
||||
|
||||
let quizBattleGateFlashAt = 0;
|
||||
function quizBattleGateQuestionSnippetPlay(compId) {
|
||||
if (!(compId > 0) || !quizBattleMcqPool.length) return '';
|
||||
const q = quizBattleMcqPool[(compId - 1) % quizBattleMcqPool.length];
|
||||
if (!q || !q.text) return '';
|
||||
const t = String(q.text).trim();
|
||||
return t.length > 42 ? t.slice(0, 42) + '…' : t;
|
||||
}
|
||||
|
||||
function quizBattleFlashGateBlocked(md, px, py, solvedSet) {
|
||||
const need = quizBattleFirstMissingGateCompPlay(md, px, py, solvedSet);
|
||||
if (!need) return;
|
||||
const now = performance.now();
|
||||
if (now - quizBattleGateFlashAt < 1400) return;
|
||||
quizBattleGateFlashAt = now;
|
||||
const snip = quizBattleGateQuestionSnippetPlay(need);
|
||||
flashQuizBattleFeedback(
|
||||
snip
|
||||
? ('ตอบข้อ ' + need + ' ก่อนผ่าน: 「' + snip + '」')
|
||||
: ('ตอบคำถามข้อ ' + need + ' ก่อนผ่านทางนี้'),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/** เส้นทาง Quiz Battle — วาดอย่างน้อย 1 ช่องแล้วจะเดินได้เฉพาะบนเส้นทาง (เปลี่ยนแค่รูปพื้นหลังได้) */
|
||||
function normalizeQuizBattlePathInPlay(md) {
|
||||
if (!md || md.gameType !== 'quiz_battle') return;
|
||||
@@ -12767,6 +12974,41 @@
|
||||
return Math.min.apply(null, arr);
|
||||
}
|
||||
|
||||
function quizBattleFootprintOnDomePlay(md, px, py) {
|
||||
const dome = md && md.quizBattleDomeArea;
|
||||
if (!dome) return false;
|
||||
for (const k of quizTilesFootprintPlay(px, py)) {
|
||||
const p = k.split(',');
|
||||
const tx = +p[0], ty = +p[1];
|
||||
if (dome[ty] && dome[ty][tx] === 1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** โดมใต้เท้า หรือช่องติดกัน (8 ทิศ) — ให้กด E / เด้งคำถามได้แม้ยืนข้างสถานี */
|
||||
function getNearestQuizBattleDomeCompAt(px, py) {
|
||||
const direct = getPrimaryQuizBattleDomeCompAt(px, py);
|
||||
if (direct != null) return direct;
|
||||
if (!mapData || !isQuizBattle()) return null;
|
||||
const dome = mapData.quizBattleDomeArea;
|
||||
const comp = mapData.quizBattleDomeComp;
|
||||
if (!dome) return null;
|
||||
const w = mapData.width || 20, h = mapData.height || 15;
|
||||
let best = null;
|
||||
for (const k of quizTilesFootprintPlay(px, py)) {
|
||||
const p = k.split(',');
|
||||
const tx = +p[0], ty = +p[1];
|
||||
for (const [dx, dy] of [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [-1, 1], [1, -1], [-1, -1]]) {
|
||||
const nx = tx + dx, ny = ty + dy;
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
||||
if (!dome[ny] || dome[ny][nx] !== 1) continue;
|
||||
const cid = comp && comp[ny] && comp[ny][nx] ? comp[ny][nx] : 1;
|
||||
if (best == null || cid < best) best = cid;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
function showQuizBattleMcqModal(q) {
|
||||
const ov = document.getElementById('quiz-battle-mcq-overlay');
|
||||
const textEl = document.getElementById('quiz-battle-mcq-text');
|
||||
@@ -12799,12 +13041,15 @@
|
||||
const ok = Number(q.correctIndex) === Number(idx);
|
||||
if (ok) {
|
||||
quizBattleAnsweredComps.add(compId);
|
||||
if (typeof socket !== 'undefined' && socket && socket.connected) {
|
||||
try { socket.emit('quizbattle-solve', { compId: compId }, function () {}); } catch (e) { /* ignore */ }
|
||||
}
|
||||
if (myId != null) {
|
||||
if (!playLiveQuizScores) playLiveQuizScores = {};
|
||||
playLiveQuizScores[myId] = (playLiveQuizScores[myId] || 0) + 1;
|
||||
renderPlayQuizScoreboard(playLiveQuizScores);
|
||||
}
|
||||
flashQuizBattleFeedback('ถูกต้อง · +1', true);
|
||||
flashQuizBattleFeedback('ถูกต้อง · +1 · ทางไปข้างหน้าเปิดแล้ว', true);
|
||||
} else {
|
||||
flashQuizBattleFeedback('ยังไม่ถูก — ลองใหม่ (กด E อีกครั้ง)', false);
|
||||
}
|
||||
@@ -12812,15 +13057,15 @@
|
||||
hideQuizBattleMcqModal();
|
||||
}
|
||||
|
||||
/** หา comp โดมที่ตัวละครเหยียบอยู่ — เช็คหลายจุด (กลาง/เท้า/หัว/ซ้าย-ขวา) ให้เด้งง่ายเมื่อถึงสถานี */
|
||||
/** หา comp โดมที่ตัวละครเหยียบอยู่ — เช็คหลายจุด + ช่องติดโดม ให้เด้งง่ายเมื่อถึงสถานี */
|
||||
function quizBattleDomeAtMe() {
|
||||
if (!isQuizBattle()) return null;
|
||||
const pts = [[me.x, me.y], [me.x, me.y + 0.45], [me.x, me.y - 0.25], [me.x + 0.3, me.y], [me.x - 0.3, me.y]];
|
||||
const pts = [[me.x, me.y], [me.x, me.y + 0.45], [me.x, me.y - 0.25], [me.x + 0.35, me.y], [me.x - 0.35, me.y]];
|
||||
for (let i = 0; i < pts.length; i++) {
|
||||
const c = getPrimaryQuizBattleDomeCompAt(pts[i][0], pts[i][1]);
|
||||
const c = getNearestQuizBattleDomeCompAt(pts[i][0], pts[i][1]);
|
||||
if (c != null) return c;
|
||||
}
|
||||
return null;
|
||||
return getNearestQuizBattleDomeCompAt(me.x, me.y);
|
||||
}
|
||||
|
||||
/** ZEP-style: เหยียบสถานี (โดม) ที่ยังไม่ตอบ → เปิดคำถามอัตโนมัติ (ครั้งเดียวต่อการเข้า · เดินออกแล้วกลับมาใหม่ถึงเด้งอีก) */
|
||||
@@ -12835,7 +13080,10 @@
|
||||
qbAutoLastComp = compNow;
|
||||
if (quizBattleAnsweredComps.has(compNow)) return;
|
||||
const pool = quizBattleMcqPool;
|
||||
if (!pool.length) return;
|
||||
if (!pool.length) {
|
||||
flashQuizBattleFeedback('ยังไม่มีคำถาม — บันทึกใน Admin → Quiz Battle แล้วรีเฟรช', false);
|
||||
return;
|
||||
}
|
||||
const q = pool[(compNow - 1) % pool.length];
|
||||
if (!q) return;
|
||||
quizBattleModalCompId = compNow;
|
||||
@@ -12894,9 +13142,9 @@
|
||||
|
||||
function tryOpenQuizBattleFromKey() {
|
||||
if (!isQuizBattle() || !mapData || myId == null) return;
|
||||
const compId = getPrimaryQuizBattleDomeCompAt(me.x, me.y);
|
||||
const compId = quizBattleDomeAtMe();
|
||||
if (compId == null) {
|
||||
flashQuizBattleFeedback('ยังไม่ยืนบนโดมคำถาม', false);
|
||||
flashQuizBattleFeedback('เข้าใกล้สถานีคำถาม (หีบ) แล้วกด E', false);
|
||||
return;
|
||||
}
|
||||
if (quizBattleAnsweredComps.has(compId)) {
|
||||
@@ -15797,6 +16045,7 @@
|
||||
if (!mapData.quizBattlePathArea) mapData.quizBattlePathArea = [];
|
||||
normalizeQuizBattlePathInPlay(mapData);
|
||||
normalizeQuizBattleDomeInPlay(mapData);
|
||||
buildQuizBattlePathGateRequiredPlay(mapData);
|
||||
}
|
||||
tileSize = mapData.tileSize || 32;
|
||||
mapBackgroundImg = null;
|
||||
@@ -17074,6 +17323,7 @@
|
||||
if (!mapData.quizBattlePathArea) mapData.quizBattlePathArea = [];
|
||||
normalizeQuizBattlePathInPlay(mapData);
|
||||
normalizeQuizBattleDomeInPlay(mapData);
|
||||
buildQuizBattlePathGateRequiredPlay(mapData);
|
||||
if (quizBattlePathModeActive(mapData)) {
|
||||
const ms = snapPositionOntoQuizBattlePathIfNeeded(me.x, me.y);
|
||||
me.x = ms.x;
|
||||
@@ -17559,6 +17809,10 @@
|
||||
if (isQuizCarry() && quizCarryFootprintOverlapsHub(x, y)) return false;
|
||||
if (isQuizBattle() && quizBattlePathModeActive(mapData)) {
|
||||
if (!quizBattlePositionAllowedPlay(mapData, x, y)) return false;
|
||||
if (!quizBattlePathGateAllowsPlay(mapData, x, y, quizBattleAnsweredComps)) {
|
||||
quizBattleFlashGateBlocked(mapData, x, y, quizBattleAnsweredComps);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -17607,6 +17861,8 @@
|
||||
if (isQuizCarry() && quizCarryFootprintOverlapsHub(x, y)) return false;
|
||||
if (isQuizBattle() && quizBattlePathModeActive(mapData)) {
|
||||
if (!quizBattlePositionAllowedPlay(mapData, x, y)) return false;
|
||||
if (o && isPreviewBotId(o.id || o.peerId)) { /* preview bots ไม่ล็อกประตู */ }
|
||||
else if (!quizBattlePathGateAllowsPlay(mapData, x, y, quizBattleAnsweredComps)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3974,7 +3974,7 @@
|
||||
<script src="/app-base.js?v=2"></script>
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0306"></script>
|
||||
<script src="js/play.js?v=0.006071209"></script>
|
||||
<script src="js/play.js?v=0.00607144417"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3702,6 +3702,204 @@ function normalizeQuizBattleDomeAreaOnMap(m) {
|
||||
m.quizBattleDomeArea = rows;
|
||||
}
|
||||
|
||||
/** โดมติดกัน = 1 ข้อ (comp id 1,2,3…) — ตรงกับ play.js */
|
||||
function normalizeQuizBattleDomeCompOnMap(m) {
|
||||
if (!m || m.gameType !== 'quiz_battle') return;
|
||||
normalizeQuizBattleDomeAreaOnMap(m);
|
||||
const w = m.width || 20, h = m.height || 15;
|
||||
const grid = m.quizBattleDomeArea;
|
||||
const comp = Array(h).fill(0).map(() => Array(w).fill(0));
|
||||
let nextId = 0;
|
||||
for (let y = 0; y < h; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
if (!grid[y] || grid[y][x] !== 1 || comp[y][x]) continue;
|
||||
nextId++;
|
||||
const stack = [[x, y]];
|
||||
comp[y][x] = nextId;
|
||||
while (stack.length) {
|
||||
const c = stack.pop();
|
||||
const cx = c[0], cy = c[1];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const dx = (i === 0 ? 1 : i === 1 ? -1 : 0);
|
||||
const dy = (i === 2 ? 1 : i === 3 ? -1 : 0);
|
||||
const nx = cx + dx, ny = cy + dy;
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
||||
if (!grid[ny] || grid[ny][nx] !== 1 || comp[ny][nx]) continue;
|
||||
comp[ny][nx] = nextId;
|
||||
stack.push([nx, ny]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m.quizBattleDomeComp = comp;
|
||||
}
|
||||
|
||||
/**
|
||||
* ประตูบนเลน — ช่องเลนที่อยู่ "หลัง" โดม C ต้องตอบถูกโดม 1..C ก่อน
|
||||
* ยืนบนโดมได้โดยไม่ต้องตอบ (dist เท่ากัน) · ผ่านจุดหลังโดมต้องตอบก่อน
|
||||
*/
|
||||
function hasQuizBattleManualPathGates(m) {
|
||||
const mg = m && m.quizBattlePathGateArea;
|
||||
if (!mg || !Array.isArray(mg)) return false;
|
||||
for (let ty = 0; ty < mg.length; ty++) {
|
||||
const row = mg[ty];
|
||||
if (!row) continue;
|
||||
for (let tx = 0; tx < row.length; tx++) {
|
||||
if ((row[tx] | 0) > 0) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildQuizBattlePathGateFromManualOnMap(m) {
|
||||
const w = m.width || 20, h = m.height || 15;
|
||||
const path = m.quizBattlePathArea;
|
||||
const dome = m.quizBattleDomeArea;
|
||||
const manual = m.quizBattlePathGateArea;
|
||||
const gate = Array(h).fill(0).map(() => Array(w).fill(0));
|
||||
const bestReq = Array(h).fill(0).map(() => Array(w).fill(-1));
|
||||
const queue = [];
|
||||
|
||||
function enqueue(tx, ty, req) {
|
||||
if (tx < 0 || ty < 0 || tx >= w || ty >= h) return;
|
||||
const onPath = path[ty] && path[ty][tx] === 1;
|
||||
const onDome = dome && dome[ty] && dome[ty][tx] === 1;
|
||||
if (!onPath && !onDome) return;
|
||||
/* เก็บ req ต่ำสุดที่เข้าช่องได้ — กันเส้นทางวนกลับไปยก req ทับ spawn */
|
||||
if (bestReq[ty][tx] >= 0 && req >= bestReq[ty][tx]) return;
|
||||
bestReq[ty][tx] = req;
|
||||
queue.push({ tx, ty, req });
|
||||
}
|
||||
|
||||
const sp = m.spawn || { x: 1, y: 1 };
|
||||
const stx = Math.max(0, Math.min(w - 1, Math.floor(Number(sp.x)) || 0));
|
||||
const sty = Math.max(0, Math.min(h - 1, Math.floor(Number(sp.y)) || 0));
|
||||
enqueue(stx, sty, 0);
|
||||
if (bestReq[sty][stx] < 0) {
|
||||
for (const [dx, dy] of [[0, 0], [1, 0], [-1, 0], [0, 1], [0, -1]]) enqueue(stx + dx, sty + dy, 0);
|
||||
}
|
||||
const sa = m.spawnArea;
|
||||
if (sa && Array.isArray(sa)) {
|
||||
for (let ty = 0; ty < h; ty++) {
|
||||
const row = sa[ty];
|
||||
if (!row) continue;
|
||||
for (let tx = 0; tx < w; tx++) {
|
||||
if (row[tx] === 1) enqueue(tx, ty, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.length) {
|
||||
const cur = queue.shift();
|
||||
const { tx, ty, req } = cur;
|
||||
if (bestReq[ty][tx] !== req) continue;
|
||||
const onDome = dome && dome[ty] && dome[ty][tx] === 1;
|
||||
let enterReq = req;
|
||||
const mg = manual && manual[ty] && manual[ty][tx] ? (manual[ty][tx] | 0) : 0;
|
||||
if (onDome) enterReq = 0;
|
||||
else if (mg > 0) enterReq = Math.max(enterReq, mg);
|
||||
if (path[ty] && path[ty][tx] === 1) gate[ty][tx] = enterReq;
|
||||
let outReq = enterReq;
|
||||
if (mg > 0) outReq = Math.max(outReq, mg);
|
||||
for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
||||
enqueue(tx + dx, ty + dy, outReq);
|
||||
}
|
||||
}
|
||||
m.quizBattlePathGateRequired = gate;
|
||||
}
|
||||
|
||||
function buildQuizBattlePathGateRequiredOnMap(m) {
|
||||
if (!m || m.gameType !== 'quiz_battle' || !quizBattlePathModeActiveServer(m)) {
|
||||
if (m) m.quizBattlePathGateRequired = null;
|
||||
return;
|
||||
}
|
||||
if (hasQuizBattleManualPathGates(m)) {
|
||||
buildQuizBattlePathGateFromManualOnMap(m);
|
||||
return;
|
||||
}
|
||||
normalizeQuizBattleDomeCompOnMap(m);
|
||||
const w = m.width || 20, h = m.height || 15;
|
||||
const path = m.quizBattlePathArea;
|
||||
const dome = m.quizBattleDomeArea;
|
||||
const domeComp = m.quizBattleDomeComp;
|
||||
const gate = Array(h).fill(0).map(() => Array(w).fill(0));
|
||||
const dist = Array(h).fill(0).map(() => Array(w).fill(-1));
|
||||
const queue = [];
|
||||
const sp = m.spawn || { x: 1, y: 1 };
|
||||
const stx = Math.max(0, Math.min(w - 1, Math.floor(Number(sp.x)) || 0));
|
||||
const sty = Math.max(0, Math.min(h - 1, Math.floor(Number(sp.y)) || 0));
|
||||
|
||||
function tryEnqueue(tx, ty, d) {
|
||||
if (tx < 0 || ty < 0 || tx >= w || ty >= h) return;
|
||||
if (dist[ty][tx] >= 0) return;
|
||||
const onPath = path[ty] && path[ty][tx] === 1;
|
||||
const onDome = dome && dome[ty] && dome[ty][tx] === 1;
|
||||
if (!onPath && !onDome) return;
|
||||
dist[ty][tx] = d;
|
||||
queue.push({ tx, ty, d });
|
||||
}
|
||||
|
||||
tryEnqueue(stx, sty, 0);
|
||||
if (dist[sty][stx] < 0) {
|
||||
for (const [dx, dy] of [[0, 0], [1, 0], [-1, 0], [0, 1], [0, -1]]) tryEnqueue(stx + dx, sty + dy, 0);
|
||||
}
|
||||
const sa = m.spawnArea;
|
||||
if (sa && Array.isArray(sa)) {
|
||||
for (let ty = 0; ty < h; ty++) {
|
||||
const row = sa[ty];
|
||||
if (!row) continue;
|
||||
for (let tx = 0; tx < w; tx++) {
|
||||
if (row[tx] === 1) tryEnqueue(tx, ty, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const domeMinDist = {};
|
||||
let maxComp = 0;
|
||||
while (queue.length) {
|
||||
const cur = queue.shift();
|
||||
const { tx, ty, d } = cur;
|
||||
const dc = domeComp[ty] && domeComp[ty][tx] ? domeComp[ty][tx] : 0;
|
||||
if (dc > 0) {
|
||||
maxComp = Math.max(maxComp, dc);
|
||||
if (domeMinDist[dc] == null || d < domeMinDist[dc]) domeMinDist[dc] = d;
|
||||
}
|
||||
for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
||||
const nx = tx + dx, ny = ty + dy;
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
||||
if (dist[ny][nx] >= 0) continue;
|
||||
const onPath = path[ny] && path[ny][nx] === 1;
|
||||
const onDome = dome && dome[ny] && dome[ny][nx] === 1;
|
||||
if (onPath || onDome) tryEnqueue(nx, ny, d + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (let ty = 0; ty < h; ty++) {
|
||||
for (let tx = 0; tx < w; tx++) {
|
||||
if (!path[ty] || path[ty][tx] !== 1) continue;
|
||||
const d = dist[ty][tx];
|
||||
if (d < 0) {
|
||||
gate[ty][tx] = maxComp;
|
||||
continue;
|
||||
}
|
||||
let req = 0;
|
||||
for (let c = 1; c <= maxComp; c++) {
|
||||
if (domeMinDist[c] != null && domeMinDist[c] < d) req = Math.max(req, c);
|
||||
}
|
||||
gate[ty][tx] = req;
|
||||
}
|
||||
}
|
||||
m.quizBattlePathGateRequired = gate;
|
||||
}
|
||||
|
||||
function finalizeQuizBattleMapLayersOnMap(m) {
|
||||
if (!m || m.gameType !== 'quiz_battle') return;
|
||||
normalizeQuizBattleDomeAreaOnMap(m);
|
||||
normalizeQuizBattlePathAreaOnMap(m);
|
||||
normalizeQuizBattleDomeCompOnMap(m);
|
||||
buildQuizBattlePathGateRequiredOnMap(m);
|
||||
}
|
||||
|
||||
function normalizeQuizBattlePathAreaOnMap(m) {
|
||||
if (!m || m.gameType !== 'quiz_battle') return;
|
||||
const w = m.width || 20, h = m.height || 15;
|
||||
@@ -3755,6 +3953,49 @@ function quizBattlePositionAllowedServer(m, px, py) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function quizBattlePlayerSolvedCompsServer(space, socketId) {
|
||||
if (!space || !space.qbwSolved) return new Set();
|
||||
const set = space.qbwSolved.get(socketId);
|
||||
return set || new Set();
|
||||
}
|
||||
|
||||
function quizBattlePathGateRequiredAtServer(m, px, py) {
|
||||
const gate = m && m.quizBattlePathGateRequired;
|
||||
if (!gate || !quizBattlePathModeActiveServer(m)) return 0;
|
||||
const keys = quizBattleSpriteBoundsTileKeys(m, px, py);
|
||||
const path = m.quizBattlePathArea;
|
||||
let maxReq = 0;
|
||||
for (const k of keys) {
|
||||
const [tx, ty] = k.split(',').map(Number);
|
||||
if (!path[ty] || path[ty][tx] !== 1) continue;
|
||||
maxReq = Math.max(maxReq, gate[ty][tx] || 0);
|
||||
}
|
||||
return maxReq;
|
||||
}
|
||||
|
||||
function quizBattleFootprintOnDomeServer(m, px, py) {
|
||||
const dome = m && m.quizBattleDomeArea;
|
||||
if (!dome) return false;
|
||||
const keys = quizBattleSpriteBoundsTileKeys(m, px, py);
|
||||
for (const k of keys) {
|
||||
const [tx, ty] = k.split(',').map(Number);
|
||||
if (dome[ty] && dome[ty][tx] === 1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function quizBattlePathGateAllowsServer(m, space, socketId, px, py) {
|
||||
if (!m || !quizBattlePathModeActiveServer(m)) return true;
|
||||
if (quizBattleFootprintOnDomeServer(m, px, py)) return true;
|
||||
const maxReq = quizBattlePathGateRequiredAtServer(m, px, py);
|
||||
if (maxReq <= 0) return true;
|
||||
const solved = quizBattlePlayerSolvedCompsServer(space, socketId);
|
||||
for (let c = 1; c <= maxReq; c++) {
|
||||
if (!solved.has(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function serverFootprintClearOfWalls(md, px, py) {
|
||||
const w = md.width || 20, h = md.height || 15;
|
||||
const keys = serverWallCollisionTileKeys(md, px, py);
|
||||
@@ -4053,8 +4294,7 @@ function loadMaps() {
|
||||
normalizeJumpSurvivePlatformAreaOnMap(data);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(data);
|
||||
normalizeJumpSurviveHazardAreaOnMap(data);
|
||||
normalizeQuizBattleDomeAreaOnMap(data);
|
||||
normalizeQuizBattlePathAreaOnMap(data);
|
||||
finalizeQuizBattleMapLayersOnMap(data);
|
||||
applyStackTowerMissionMapPlayPolicy(id, data);
|
||||
maps.set(id, data);
|
||||
} catch (e) { console.error('load map', f, e.message); }
|
||||
@@ -4076,8 +4316,7 @@ function rehydrateSingleMapFromDisk(safeId) {
|
||||
normalizeJumpSurvivePlatformAreaOnMap(data);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(data);
|
||||
normalizeJumpSurviveHazardAreaOnMap(data);
|
||||
normalizeQuizBattleDomeAreaOnMap(data);
|
||||
normalizeQuizBattlePathAreaOnMap(data);
|
||||
finalizeQuizBattleMapLayersOnMap(data);
|
||||
applyStackTowerMissionMapPlayPolicy(safeId, data);
|
||||
maps.set(safeId, data);
|
||||
return true;
|
||||
@@ -4185,8 +4424,7 @@ const server = http.createServer((req, res) => {
|
||||
normalizeJumpSurvivePlatformAreaOnMap(m);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(m);
|
||||
normalizeJumpSurviveHazardAreaOnMap(m);
|
||||
normalizeQuizBattleDomeAreaOnMap(m);
|
||||
normalizeQuizBattlePathAreaOnMap(m);
|
||||
finalizeQuizBattleMapLayersOnMap(m);
|
||||
maps.set(id, m);
|
||||
saveMap(id, m);
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
@@ -4229,8 +4467,7 @@ const server = http.createServer((req, res) => {
|
||||
normalizeJumpSurvivePlatformAreaOnMap(m);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(m);
|
||||
normalizeJumpSurviveHazardAreaOnMap(m);
|
||||
normalizeQuizBattleDomeAreaOnMap(m);
|
||||
normalizeQuizBattlePathAreaOnMap(m);
|
||||
finalizeQuizBattleMapLayersOnMap(m);
|
||||
applyStackTowerMissionMapPlayPolicy(id, m);
|
||||
maps.set(id, m);
|
||||
saveMap(id, m);
|
||||
@@ -7514,6 +7751,9 @@ io.on('connection', (socket) => {
|
||||
} else if (quizBattlePathModeActiveServer(md) && !quizBattlePositionAllowedServer(md, txN, tyN)) {
|
||||
nx = p.x;
|
||||
ny = p.y;
|
||||
} else if (quizBattlePathModeActiveServer(md) && !quizBattlePathGateAllowsServer(md, space, socket.id, txN, tyN)) {
|
||||
nx = p.x;
|
||||
ny = p.y;
|
||||
}
|
||||
if (quizBattlePathModeActiveServer(md)) {
|
||||
const sn = snapPositionOntoQuizBattlePathServer(md, Number(nx), Number(ny));
|
||||
|
||||
@@ -29,7 +29,124 @@
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer mg4-time"><img class="mg-time-label" src="../cut/time.png" alt="TIME :"><span class="num">0</span></div><div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
<div class="layer mg4-time"><img class="mg-time-label" src="../cut/time.png" alt="TIME :"><span class="num">0</span></div><div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-content">
|
||||
|
||||
<div class="sm-ranks">
|
||||
<div class="sm-rank is-1st" style="--bg:#ffd96b; --hair:#3a2210; --body:#e09f2a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-1st.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JACKSON</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-2nd" style="--bg:#f5b9c8; --hair:#ec5fa0; --body:#ffffff; --skin:#f7d8c2;">
|
||||
<img class="pos" src="../cut/result-2nd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MONE</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-3rd" style="--bg:#ff7b7b; --hair:#c8252a; --body:#9b1f23; --skin:#f5cba7;">
|
||||
<img class="pos" src="../cut/result-3rd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MICKY</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-4th" style="--bg:#7e84d1; --hair:#2a3580; --body:#3848a8; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-1.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">RAINY</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-5th" style="--bg:#85d28a; --hair:#216a2c; --body:#2f8a4a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-2.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JAME</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-above" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-total">
|
||||
คะแนนรวม (100+100+100+90+90) =<span class="num">480</span>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-below" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-bottom">
|
||||
<section class="sm-col sm-col-grade">
|
||||
<h3 class="head">ระดับความสำเร็จ</h3>
|
||||
<p class="sm-grade" aria-label="เกรด A">A</p>
|
||||
</section>
|
||||
|
||||
<div class="sm-vrule" aria-hidden="true">
|
||||
<img class="sm-line-2" src="../cut/popup-result-Line2.png" alt="">
|
||||
</div>
|
||||
|
||||
<section class="sm-col sm-col-bonus">
|
||||
<div class="sm-bonus">
|
||||
<div class="sm-bonus-left">
|
||||
<h3 class="head">โบนัสพิเศษ</h3>
|
||||
<img class="complete" src="../cut/mission-complete-select.png" alt="ภารกิจสำเร็จ">
|
||||
</div>
|
||||
<div class="sm-bonus-right">
|
||||
<img class="bail-card" src="../../cut/10-Game-1-result/card-5.png" alt="Bail Coin">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>08 · สรุปผลภารกิจ</title>
|
||||
<meta name="viewport" content="width=1920, initial-scale=1">
|
||||
<link rel="stylesheet" href="../assets/css/base.css">
|
||||
<link rel="stylesheet" href="../assets/css/game-hud.css">
|
||||
<link rel="stylesheet" href="../assets/css/game.css">
|
||||
<link rel="stylesheet" href="../assets/css/summary.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="stage-wrap">
|
||||
<div class="stage mg-stage ">
|
||||
<div class="layer mg-bg"><img src="../cut/BG.png" alt="bg"></div>
|
||||
|
||||
<div class="layer mg2-score-bar">
|
||||
<img class="mg2-score-label" src="../cut/score.png" alt="SCORE :">
|
||||
<div class="mg2-players">
|
||||
<div class="mg2-player"><div class="nm">Mone</div><div class="av"></div><div class="sc">100</div></div>
|
||||
<div class="mg2-player"><div class="nm">Jackson</div><div class="av"></div><div class="sc">100</div></div>
|
||||
<div class="mg2-player"><div class="nm">Micky</div><div class="av"></div><div class="sc">100</div></div>
|
||||
<div class="mg2-player"><div class="nm">Rainy</div><div class="av"></div><div class="sc">100</div></div>
|
||||
<div class="mg2-player"><div class="nm">Jame</div><div class="av"></div><div class="sc">100</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer mg-host">
|
||||
<img class="mg-host-frame" src="../cut/Avartar.png" alt="">
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer mg4-time"><img class="mg-time-label" src="../cut/time.png" alt="TIME :"><span class="num">0</span></div><div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 507 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 485 B |
@@ -26,7 +26,124 @@
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer mg5-joy" data-joystick><img class="joy-base" src="../cut/btn-joystick-1.png" alt=""><img class="joy-thumb" src="../cut/btn-joystick-2.png" alt=""></div>
|
||||
<div class="layer mg5-jump btn"><img src="../cut/btn-jump.png" alt="JUMP"></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-content">
|
||||
|
||||
<div class="sm-ranks">
|
||||
<div class="sm-rank is-1st" style="--bg:#ffd96b; --hair:#3a2210; --body:#e09f2a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-1st.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JACKSON</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-2nd" style="--bg:#f5b9c8; --hair:#ec5fa0; --body:#ffffff; --skin:#f7d8c2;">
|
||||
<img class="pos" src="../cut/result-2nd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MONE</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-3rd" style="--bg:#ff7b7b; --hair:#c8252a; --body:#9b1f23; --skin:#f5cba7;">
|
||||
<img class="pos" src="../cut/result-3rd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MICKY</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-4th" style="--bg:#7e84d1; --hair:#2a3580; --body:#3848a8; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-1.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">RAINY</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-5th" style="--bg:#85d28a; --hair:#216a2c; --body:#2f8a4a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-2.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JAME</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-above" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-total">
|
||||
คะแนนรวม (100+100+100+90+90) =<span class="num">480</span>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-below" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-bottom">
|
||||
<section class="sm-col sm-col-grade">
|
||||
<h3 class="head">ระดับความสำเร็จ</h3>
|
||||
<p class="sm-grade" aria-label="เกรด A">A</p>
|
||||
</section>
|
||||
|
||||
<div class="sm-vrule" aria-hidden="true">
|
||||
<img class="sm-line-2" src="../cut/popup-result-Line2.png" alt="">
|
||||
</div>
|
||||
|
||||
<section class="sm-col sm-col-bonus">
|
||||
<div class="sm-bonus">
|
||||
<div class="sm-bonus-left">
|
||||
<h3 class="head">โบนัสพิเศษ</h3>
|
||||
<img class="complete" src="../cut/mission-complete-select.png" alt="ภารกิจสำเร็จ">
|
||||
</div>
|
||||
<div class="sm-bonus-right">
|
||||
<img class="bail-card" src="../../cut/10-Game-1-result/card-5.png" alt="Bail Coin">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>08 · สรุปผลภารกิจ</title>
|
||||
<meta name="viewport" content="width=1920, initial-scale=1">
|
||||
<link rel="stylesheet" href="../assets/css/base.css">
|
||||
<link rel="stylesheet" href="../assets/css/game-hud.css">
|
||||
<link rel="stylesheet" href="../assets/css/game.css">
|
||||
<link rel="stylesheet" href="../assets/css/summary.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="stage-wrap">
|
||||
<div class="stage mg-stage ">
|
||||
<div class="layer mg-bg"><img src="../cut/BG.png" alt="bg"></div>
|
||||
|
||||
|
||||
<div class="layer mg-time mg5-time">
|
||||
<img class="mg-time-label" src="../cut/time.png" alt="TIME :">
|
||||
<span class="num">0</span>
|
||||
</div>
|
||||
<div class="layer mg-host">
|
||||
<img class="mg-host-frame" src="../cut/Avartar.png" alt="">
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer mg5-joy" data-joystick><img class="joy-base" src="../cut/btn-joystick-1.png" alt=""><img class="joy-thumb" src="../cut/btn-joystick-2.png" alt=""></div>
|
||||
<div class="layer mg5-jump btn"><img src="../cut/btn-jump.png" alt="JUMP"></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
<script src="../assets/js/joystick.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 507 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 485 B |
@@ -34,7 +34,124 @@
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-content">
|
||||
|
||||
<div class="sm-ranks">
|
||||
<div class="sm-rank is-1st" style="--bg:#ffd96b; --hair:#3a2210; --body:#e09f2a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-1st.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JACKSON</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-2nd" style="--bg:#f5b9c8; --hair:#ec5fa0; --body:#ffffff; --skin:#f7d8c2;">
|
||||
<img class="pos" src="../cut/result-2nd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MONE</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-3rd" style="--bg:#ff7b7b; --hair:#c8252a; --body:#9b1f23; --skin:#f5cba7;">
|
||||
<img class="pos" src="../cut/result-3rd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MICKY</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-4th" style="--bg:#7e84d1; --hair:#2a3580; --body:#3848a8; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-1.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">RAINY</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-5th" style="--bg:#85d28a; --hair:#216a2c; --body:#2f8a4a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-2.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JAME</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-above" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-total">
|
||||
คะแนนรวม (100+100+100+90+90) =<span class="num">480</span>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-below" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-bottom">
|
||||
<section class="sm-col sm-col-grade">
|
||||
<h3 class="head">ระดับความสำเร็จ</h3>
|
||||
<p class="sm-grade" aria-label="เกรด A">A</p>
|
||||
</section>
|
||||
|
||||
<div class="sm-vrule" aria-hidden="true">
|
||||
<img class="sm-line-2" src="../cut/popup-result-Line2.png" alt="">
|
||||
</div>
|
||||
|
||||
<section class="sm-col sm-col-bonus">
|
||||
<div class="sm-bonus">
|
||||
<div class="sm-bonus-left">
|
||||
<h3 class="head">โบนัสพิเศษ</h3>
|
||||
<img class="complete" src="../cut/mission-complete-select.png" alt="ภารกิจสำเร็จ">
|
||||
</div>
|
||||
<div class="sm-bonus-right">
|
||||
<img class="bail-card" src="../../cut/10-Game-1-result/card-5.png" alt="Bail Coin">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>08 · สรุปผลภารกิจ</title>
|
||||
<meta name="viewport" content="width=1920, initial-scale=1">
|
||||
<link rel="stylesheet" href="../assets/css/base.css">
|
||||
<link rel="stylesheet" href="../assets/css/game-hud.css">
|
||||
<link rel="stylesheet" href="../assets/css/game.css">
|
||||
<link rel="stylesheet" href="../assets/css/summary.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="stage-wrap">
|
||||
<div class="stage mg-stage ">
|
||||
<div class="layer mg-bg"><img src="../cut/BG-2_Loop.png" alt="bg"></div>
|
||||
|
||||
|
||||
<div class="layer mg-score-panel">
|
||||
<div class="mg-score-title">SCORE</div>
|
||||
<div class="mg-score-list">
|
||||
<div class="mg-score-row"><div class="av"></div><div class="meta"><span class="nm">MONE</span><span class="sc">20</span></div></div>
|
||||
<div class="mg-score-row"><div class="av"></div><div class="meta"><span class="nm">JACKSON</span><span class="sc">20</span></div></div>
|
||||
<div class="mg-score-row"><div class="av"></div><div class="meta"><span class="nm">MICKY</span><span class="sc">20</span></div></div>
|
||||
<div class="mg-score-row"><div class="av"></div><div class="meta"><span class="nm">RAINY</span><span class="sc">20</span></div></div>
|
||||
<div class="mg-score-row"><div class="av"></div><div class="meta"><span class="nm">JAME</span><span class="sc">20</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer mg-time">
|
||||
<img class="mg-time-label" src="../cut/time.png" alt="TIME :">
|
||||
<span class="num">0</span>
|
||||
</div>
|
||||
<div class="layer mg-host">
|
||||
<img class="mg-host-frame" src="../cut/Avartar.png" alt="">
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -30,7 +30,124 @@
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-content">
|
||||
|
||||
<div class="sm-ranks">
|
||||
<div class="sm-rank is-1st" style="--bg:#ffd96b; --hair:#3a2210; --body:#e09f2a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-1st.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JACKSON</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-2nd" style="--bg:#f5b9c8; --hair:#ec5fa0; --body:#ffffff; --skin:#f7d8c2;">
|
||||
<img class="pos" src="../cut/result-2nd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MONE</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-3rd" style="--bg:#ff7b7b; --hair:#c8252a; --body:#9b1f23; --skin:#f5cba7;">
|
||||
<img class="pos" src="../cut/result-3rd.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">MICKY</div>
|
||||
<div class="sc">100</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-4th" style="--bg:#7e84d1; --hair:#2a3580; --body:#3848a8; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-1.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">RAINY</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
|
||||
<div class="sm-rank is-5th" style="--bg:#85d28a; --hair:#216a2c; --body:#2f8a4a; --skin:#f5d6b3;">
|
||||
<img class="pos" src="../cut/result-txt-2.png" alt="">
|
||||
<div class="av">
|
||||
<div class="portrait">
|
||||
<div class="chibi">
|
||||
<div class="ch-body"></div>
|
||||
<div class="ch-head"></div>
|
||||
<div class="ch-hair"></div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="frame" src="../cut/player-frame.png" alt="">
|
||||
</div>
|
||||
<div class="nm">JAME</div>
|
||||
<div class="sc">90</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-above" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-total">
|
||||
คะแนนรวม (100+100+100+90+90) =<span class="num">480</span>
|
||||
</div>
|
||||
|
||||
<img class="sm-line sm-line-1 sm-line-below" src="../cut/popup-result-Line1.png" alt="">
|
||||
|
||||
<div class="sm-bottom">
|
||||
<section class="sm-col sm-col-grade">
|
||||
<h3 class="head">ระดับความสำเร็จ</h3>
|
||||
<p class="sm-grade" aria-label="เกรด A">A</p>
|
||||
</section>
|
||||
|
||||
<div class="sm-vrule" aria-hidden="true">
|
||||
<img class="sm-line-2" src="../cut/popup-result-Line2.png" alt="">
|
||||
</div>
|
||||
|
||||
<section class="sm-col sm-col-bonus">
|
||||
<div class="sm-bonus">
|
||||
<div class="sm-bonus-left">
|
||||
<h3 class="head">โบนัสพิเศษ</h3>
|
||||
<img class="complete" src="../cut/mission-complete-select.png" alt="ภารกิจสำเร็จ">
|
||||
</div>
|
||||
<div class="sm-bonus-right">
|
||||
<img class="bail-card" src="../../cut/10-Game-1-result/card-5.png" alt="Bail Coin">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>08 · สรุปผลภารกิจ</title>
|
||||
<meta name="viewport" content="width=1920, initial-scale=1">
|
||||
<link rel="stylesheet" href="../assets/css/base.css">
|
||||
<link rel="stylesheet" href="../assets/css/game-hud.css">
|
||||
<link rel="stylesheet" href="../assets/css/game.css">
|
||||
<link rel="stylesheet" href="../assets/css/summary.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="stage-wrap">
|
||||
<div class="stage mg-stage ">
|
||||
<div class="layer mg-bg"><img src="../cut/BG.png" alt="bg"></div>
|
||||
|
||||
|
||||
<div class="layer mg2-score-bar">
|
||||
<img class="mg2-score-label" src="../cut/score.png" alt="SCORE :">
|
||||
<div class="mg2-players">
|
||||
<div class="mg2-player"><div class="nm">Mone</div><div class="av"></div><div class="sc">200</div></div>
|
||||
<div class="mg2-player"><div class="nm">Jackson</div><div class="av"></div><div class="sc">180</div></div>
|
||||
<div class="mg2-player"><div class="nm">Micky</div><div class="av"></div><div class="sc">160</div></div>
|
||||
<div class="mg2-player"><div class="nm">Rainy</div><div class="av"></div><div class="sc">190</div></div>
|
||||
<div class="mg2-player"><div class="nm">Jame</div><div class="av"></div><div class="sc">150</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layer mg-host">
|
||||
<img class="mg-host-frame" src="../cut/Avartar.png" alt="">
|
||||
<img class="mg-host-face" src="" alt="">
|
||||
</div>
|
||||
<div class="layer mg-mic"><img src="../cut/btn-mic_on.png" alt="mic"></div>
|
||||
<div class="layer sm-overlay"></div><div class="layer sm-popup"><img src="../cut/popup-result.png" alt=""></div><div class="layer sm-btn-done btn"><img src="../cut/btn-done.png" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../assets/js/scale.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||