โชว์การ์ดพิเศษที่ได้ + ปุ่มไปต่อ

This commit is contained in:
2026-06-02 06:45:39 +00:00
parent 7737555c27
commit 18cad3e9f6
27 changed files with 114 additions and 11 deletions
+63 -10
View File
@@ -396,6 +396,14 @@
title.appendChild(document.createElement('br'));
title.appendChild(gradeEl);
}
var freeBonus = Math.max(0, Number(res.freeEvidenceCount) || 0);
if (freeBonus > 0) {
var bonusEl = document.createElement('div');
bonusEl.style.cssText = 'font:800 15px/1.3 Kanit,system-ui,sans-serif;color:#9ece6a;margin-top:4px';
bonusEl.textContent = '⭐ การ์ด Free Evidence: ทุกคนรับหลักฐานฟรี +' + freeBonus + ' ใบ';
title.appendChild(document.createElement('br'));
title.appendChild(bonusEl);
}
var row = document.createElement('div');
row.style.cssText = 'display:flex;gap:18px;flex-wrap:wrap;justify-content:center;align-items:center;max-width:92vw';
awarded.forEach(function (c, i) {
@@ -11766,6 +11774,9 @@
if (coins > 0 && card.effectKey === 'fund') {
addOwnCoinsViaApi(coins);
}
/* ถ้า overlay ควิซยังเปิด (การ์ดเพิ่งได้จากควิซ) — แผง «เล่นต่อ» โชว์การ์ดอยู่แล้ว ไม่ต้องเด้งซ้ำ */
const sqOv = specialQuizOverlayEl();
if (sqOv && !sqOv.classList.contains('is-hidden')) return;
showSpecialCardUseAnimation(card, { addSec: addSec, coins: coins });
}
@@ -11835,24 +11846,66 @@
}, 2600);
}
/** ปิด overlay + คืนเวลาเล่นต่อ (เรียกตอนกด «เล่นต่อ» หรือ safety timeout) */
function specialQuizContinueAndResume() {
clearTimeout(endSpecialQuiz._safety);
endSpecialQuiz._safety = 0;
const ov = specialQuizOverlayEl();
const award = document.getElementById('sq-ov-award');
if (award) award.classList.add('is-hidden');
if (ov) ov.classList.add('is-hidden');
/* แจ้ง server ให้เล่นรอบ quiz (mng8a80o) ต่อ — มินิเกมอื่น freeze ฝั่ง client พอ */
try { socket.emit('special-quiz-continue', {}, function () {}); } catch (e) { /* ignore */ }
endSpecialQuizFreeze();
}
function endSpecialQuiz(data) {
cancelAnimationFrame(specialQuizTimerRaf);
specialQuizActiveQuestion = null;
specialQuizAnswered = false;
endSpecialQuizFreeze();
if (data && data.success && data.card) specialQuizAwardedCard = data.card;
const ov = specialQuizOverlayEl();
const success = !!(data && data.success && data.card);
const card = success ? data.card : null;
if (success) specialQuizAwardedCard = card;
const status = document.getElementById('sq-ov-status');
if (status) status.textContent = '';
const result = document.getElementById('sq-ov-result');
if (result) {
result.classList.remove('is-hidden');
result.textContent = (data && data.success)
? 'ได้รับการ์ดพิเศษ! ดูในสรุปผลภารกิจ'
: 'รอบนี้ไม่ได้การ์ดพิเศษ';
result.className = 'sq-ov-result ' + ((data && data.success) ? 'sq-ov-result--ok' : 'sq-ov-result--fail');
result.textContent = success ? 'ได้รับการ์ดพิเศษ!' : 'รอบนี้ไม่ได้การ์ดพิเศษ';
result.className = 'sq-ov-result ' + (success ? 'sq-ov-result--ok' : 'sq-ov-result--fail');
}
const status = document.getElementById('sq-ov-status');
if (status) status.textContent = '';
if (ov) setTimeout(function () { ov.classList.add('is-hidden'); }, 1900);
/* แผงโชว์การ์ด + ปุ่ม «เล่นต่อ» — เกมหยุดค้างจนกว่าจะกด */
const award = document.getElementById('sq-ov-award');
const titleEl = document.getElementById('sq-ov-award-title');
const imgEl = document.getElementById('sq-ov-award-img');
const nameEl = document.getElementById('sq-ov-award-name');
const fxEl = document.getElementById('sq-ov-award-effect');
const btn = document.getElementById('sq-ov-award-continue');
if (award) {
if (success) {
if (titleEl) titleEl.textContent = 'ได้รับการ์ดพิเศษ!';
if (imgEl) {
imgEl.removeAttribute('hidden');
imgEl.src = card.imageUrl || (card.cardId ? '/Game/img/special-cards/card-' + card.cardId + '.png' : '');
}
if (nameEl) nameEl.textContent = card.en || card.th || ('การ์ด #' + (card.cardId || ''));
if (fxEl) fxEl.textContent = card.desc || card.th || '';
} else {
if (titleEl) titleEl.textContent = 'รอบนี้ไม่ได้การ์ดพิเศษ';
if (imgEl) { imgEl.setAttribute('hidden', ''); imgEl.removeAttribute('src'); }
if (nameEl) nameEl.textContent = '';
if (fxEl) fxEl.textContent = 'ตอบถูกครบทุกคนเพื่อรับการ์ดพิเศษในครั้งหน้า';
}
if (btn) btn.onclick = specialQuizContinueAndResume;
award.classList.remove('is-hidden');
} else {
/* ไม่มี element สำรอง — เล่นต่อทันที */
specialQuizContinueAndResume();
return;
}
/* กันค้าง: ถ้าไม่กดภายใน 30 วิ ให้ไปต่อเอง */
clearTimeout(endSpecialQuiz._safety);
endSpecialQuiz._safety = setTimeout(specialQuizContinueAndResume, 30000);
}
function showGauntletCrownMissionOverlay(mission) {
+18 -1
View File
@@ -3899,6 +3899,16 @@
#special-quiz-overlay .sq-ov-result.is-hidden { display: none; }
#special-quiz-overlay .sq-ov-result--ok { color: #0e1226; background: #9ece6a; }
#special-quiz-overlay .sq-ov-result--fail { color: #fff; background: rgba(247,118,142,0.9); }
#special-quiz-overlay .sq-ov-award { margin-top: 16px; display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
#special-quiz-overlay .sq-ov-award.is-hidden { display: none; }
#special-quiz-overlay .sq-ov-award-title { font-size: 20px; font-weight: 900; color: #ffe7a3; text-shadow: 0 0 14px rgba(255,214,102,0.55); }
#special-quiz-overlay .sq-ov-award-img { width: 170px; max-width: 60vw; height: auto; border-radius: 14px; box-shadow: 0 0 34px rgba(255,200,90,0.55), 0 14px 40px rgba(0,0,0,0.55); animation: sqAwardPop .55s cubic-bezier(.2,.9,.3,1.45); }
#special-quiz-overlay .sq-ov-award-img[hidden] { display: none; }
@keyframes sqAwardPop { from { opacity: 0; transform: translateY(22px) scale(.82) rotate(-5deg); } to { opacity: 1; transform: none; } }
#special-quiz-overlay .sq-ov-award-name { font-size: 18px; font-weight: 800; color: #fff; }
#special-quiz-overlay .sq-ov-award-effect { font-size: 14px; color: #c0caf5; max-width: 90%; line-height: 1.4; }
#special-quiz-overlay .sq-ov-award-btn { margin-top: 6px; cursor: pointer; border: none; border-radius: 999px; padding: 13px 40px; font-size: 17px; font-weight: 800; color: #0e1226; background: linear-gradient(180deg, #ffd666, #f0b429); box-shadow: 0 10px 28px rgba(240,180,41,0.45); transition: filter .12s, transform .08s; }
#special-quiz-overlay .sq-ov-award-btn:hover { filter: brightness(1.06); transform: translateY(-1px); }
#special-quiz-overlay .sq-ov-members-head { margin-top: 16px; font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: #7aa2f7; font-weight: 700; }
#special-quiz-overlay .sq-ov-members { margin-top: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
@media (max-width: 560px) { #special-quiz-overlay .sq-ov-members { grid-template-columns: 1fr; } }
@@ -3949,12 +3959,19 @@
<div class="sq-ov-members-head">สมาชิกในรอบนี้ — ต้องตอบถูกครบทุกคน</div>
<div id="sq-ov-members" class="sq-ov-members"></div>
<div id="sq-ov-result" class="sq-ov-result is-hidden"></div>
<div id="sq-ov-award" class="sq-ov-award is-hidden">
<div id="sq-ov-award-title" class="sq-ov-award-title">ได้รับการ์ดพิเศษ!</div>
<img id="sq-ov-award-img" class="sq-ov-award-img" alt="" />
<div id="sq-ov-award-name" class="sq-ov-award-name"></div>
<div id="sq-ov-award-effect" class="sq-ov-award-effect"></div>
<button type="button" id="sq-ov-award-continue" class="sq-ov-award-btn">เล่นต่อ ▶</button>
</div>
</div>
</div>
<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.0508"></script>
<script src="js/play.js?v=0.0509"></script>
<div class="version-tag">v —</div>
</body>
</html>
+33
View File
@@ -494,6 +494,11 @@ function clearSpecialQuizTimers(space) {
space.specialQuizTimers.forEach((t) => clearTimeout(t));
}
space.specialQuizTimers = [];
if (space.specialQuizContinueTimer) {
clearTimeout(space.specialQuizContinueTimer);
space.specialQuizContinueTimer = null;
}
space.specialQuizAwaitContinue = false;
}
/** payload ไอคอนสำหรับ client (null = ไม่มี/ถูกทริกแล้ว) */
@@ -775,6 +780,24 @@ function endSpecialQuizSession(sid, space, success) {
applySpecialCardImmediate(sid, space, sq.card);
}
io.to(sid).emit('special-quiz-ended', { success: !!success, card: cardOut });
/* client «» quiz ()
safety timeout นคางถาไมใครกด */
space.specialQuizAwaitContinue = true;
if (space.specialQuizContinueTimer) clearTimeout(space.specialQuizContinueTimer);
space.specialQuizContinueTimer = setTimeout(() => {
space.specialQuizContinueTimer = null;
finishSpecialQuizContinue(sid, space);
}, 32000);
}
/** เล่นรอบ quiz ต่อหลังผู้เล่นกด «เล่นต่อ» (เรียกครั้งเดียว / หรือ safety timeout) */
function finishSpecialQuizContinue(sid, space) {
if (!space || !space.specialQuizAwaitContinue) return;
space.specialQuizAwaitContinue = false;
if (space.specialQuizContinueTimer) {
clearTimeout(space.specialQuizContinueTimer);
space.specialQuizContinueTimer = null;
}
resumeQuizAfterSpecialQuiz(sid, space);
}
@@ -5930,6 +5953,16 @@ io.on('connection', (socket) => {
maybeResolveSpecialQuizAllAnswered(sid, space);
});
/** ผู้เล่นกด «เล่นต่อ» หลังโชว์การ์ดพิเศษ — เล่นรอบ quiz ต่อ (คนแรกที่กดพอ) */
socket.on('special-quiz-continue', (_data, cb) => {
const reply = typeof cb === 'function' ? cb : () => {};
const sid = socket.data.spaceId;
const space = sid ? spaces.get(sid) : null;
if (!space || !space.peers.has(socket.id)) return reply({ ok: false });
finishSpecialQuizContinue(sid, space);
reply({ ok: true });
});
/** เปลี่ยนสีเสื้อ/ผิวใน lobby — ห้ามเลือกสีที่คนอื่นใช้แล้ว */
socket.on('lobby-tint-update', (data, cb) => {
const reply = typeof cb === 'function' ? cb : () => {};
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB