minigame 2 update design

This commit is contained in:
2026-06-09 09:05:48 +00:00
parent 5d4efcc822
commit 8f94f6e54b
3 changed files with 437 additions and 52 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

+200 -28
View File
@@ -960,6 +960,7 @@
return alignWorld;
}
/**
* คอลมนบนกรดทเส FINISH องแนบ (map world px)
* gauntletCrownRunwayBg.finishAlignMapTileX | finishAlignMapTileFrac าไม ใชตำแหนงตวนำ
@@ -9589,7 +9590,7 @@
if (mg2Jump) {
const hud = document.getElementById('play-cyber-hud');
if (hud && hud.classList.contains('play-cyber-hud--quiz-tf-mock')) updateQuizTfMockScale(hud);
else syncMg2MockJumpButtonBoundsPlay();
syncMg2MockJumpButtonBoundsPlay();
} else {
btn.classList.remove('mg2-jump');
}
@@ -11134,13 +11135,80 @@
const jm = Math.max(4, jumpTicksMax || 16);
const a = Math.max(0, Math.min(jm, Number(air) || 0));
const t = a / jm;
const peakAt = 0.28;
if (t >= peakAt) {
const span = 1 - peakAt;
const u = span > 0 ? (t - peakAt) / span : 0;
return Math.max(0, 1 - u * u * 1.2);
/* design (rn-jump): sine ease-in-out peak
เด: ขาข quadratic / ขาลง linear (อสมมาตร peak ~72% เวลา ขาลงกระต) */
const p = Math.max(0, Math.min(1, 1 - t)); /* 0 = เริ่มกระโดด, 1 = แตะพื้น */
return Math.sin(Math.PI * p);
}
/* ===== Minigame-2: + + -10 DOM overlay (sync worldscreen) canvas =====
เรยกทกเฟรมจาก draw() ( worldToScreen/zDraw/tileSize เขามา) container map bufferdisplay วย transform:scale */
const gauntletDomLabelPool = new Map(); /* id -> .gnt-label (pool ไว้ reuse) */
const gauntletPenaltySeenById = new Map(); /* id -> ts penalty ล่าสุด (กัน spawn float ซ้ำ) */
function syncGauntletNameDom(worldToScreenFn, zDrawVal, tileSizePx) {
const ov = document.getElementById('gauntlet-name-overlay');
if (!ov) return;
const live = isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live';
if (!live || typeof worldToScreenFn !== 'function') {
if (!ov.classList.contains('is-hidden')) {
ov.classList.add('is-hidden');
gauntletDomLabelPool.forEach((el) => { if (el.parentNode) el.parentNode.removeChild(el); });
gauntletDomLabelPool.clear();
}
return;
}
return Math.min(1, t / peakAt);
ov.classList.remove('is-hidden');
const canvas = document.getElementById('game-canvas');
const stage = document.getElementById('play-canvas-stage');
if (!canvas || !stage) return;
const cr = canvas.getBoundingClientRect();
const srect = stage.getBoundingClientRect();
const dispScale = canvas.width > 0 ? cr.width / canvas.width : 1;
ov.style.left = Math.round(cr.left - srect.left) + 'px';
ov.style.top = Math.round(cr.top - srect.top) + 'px';
ov.style.width = canvas.width + 'px';
ov.style.height = canvas.height + 'px';
ov.style.transform = 'scale(' + dispScale + ')';
const fp = (typeof getCharacterFootprintWH === 'function') ? getCharacterFootprintWH(mapData) : { cw: 1, ch: 1 };
const cwv = fp.cw || 1, chv = fp.ch || 1;
function v(n) { const x = Number(n); return Number.isFinite(x) ? x : 0; }
const seen = new Set();
function place(id, ent, isMe) {
if (!ent) return;
if (typeof shouldGauntletCrownHeistSkipAvatarDrawPlay === 'function'
&& shouldGauntletCrownHeistSkipAvatarDrawPlay(ent, isMe, 0, 0)) return;
seen.add(id);
const air = isMe ? (meGauntletJumpVis || 0) : ((ent.gauntletJumpVis != null ? ent.gauntletJumpVis : ent.gauntletJumpTicks) || 0);
const liftWorldY = air > 0 ? gauntletLiftHeightNorm(air, gauntletRuntimeJumpTicks) * tileSizePx * 0.52 : 0;
const cxWorld = (v(ent.x) + cwv * 0.5) * tileSizePx;
const cyBottomWorld = (v(ent.y) + chv) * tileSizePx - liftWorldY;
const scr = worldToScreenFn(cxWorld, cyBottomWorld);
const sx = scr[0], sy = scr[1];
/* /: mock mock + ""
(นดเหมอน 2 layer บก) คงไวเฉพาะ -10 penalty float mock */
const pen = Number(ent.gauntletCrownPenaltyFxUntil) || 0;
/* -10 float ต้องอยู่ "ใต้แถบคะแนนบน" เท่านั้น — กันผู้เล่นที่จุดเริ่ม (project ใกล้แถบคะแนน เช่น AFK) ทำ float โผล่ทับ SCORE label */
var sbEl = document.querySelector('.play-cyber-scoreboard');
var sbBottomBuf = sbEl ? ((sbEl.getBoundingClientRect().bottom - cr.top) / (dispScale || 1) + 6) : 0;
if (pen > (gauntletPenaltySeenById.get(id) || 0) && (sy - 28) > sbBottomBuf) {
gauntletPenaltySeenById.set(id, pen);
/* -10 float = design asset score-.png (รูป "-10" สำเร็จรูป) ไม่ใช่ text สไตล์เอง */
const pf = document.createElement('img');
pf.className = 'gnt-pen';
pf.src = GAUNTLET_CROWN_SCORE_POP_URL;
pf.alt = '-10';
pf.decoding = 'async';
pf.style.left = Math.round(sx) + 'px';
pf.style.top = Math.round(sy - 28) + 'px';
ov.appendChild(pf);
setTimeout(function () { if (pf.parentNode) pf.parentNode.removeChild(pf); }, 900);
} else if (pen > (gauntletPenaltySeenById.get(id) || 0)) {
/* อยู่บริเวณแถบคะแนน — บันทึกว่าเห็นแล้ว ไม่ต้องสร้าง float (กัน spawn ซ้ำตอนเลื่อนลงมา) */
gauntletPenaltySeenById.set(id, pen);
}
}
if (others && typeof others.forEach === 'function') others.forEach((o, id) => place(id, o, false));
place('__me', me, true);
}
function getGauntletObsDrawPositionsAt(nowMs) {
@@ -11454,6 +11522,42 @@
if (socket && socket.connected) socket.emit('gauntlet-crown-lobby-ready', { ready: true });
}
/** Minigame-2 mno9kb07 — HOW TO PLAY ใช้ layout mock 01-howto (canvas = mg-bg) */
function gauntletCrownHowtoUsesMg2LayoutPlay() {
return !!isGauntletCrownHeistMapPlay();
}
function getGauntletCrownHowtoStatusEl() {
if (gauntletCrownHowtoUsesMg2LayoutPlay()) {
return document.getElementById('gauntlet-crown-howto-status-mg2');
}
return document.getElementById('gauntlet-crown-howto-status');
}
function getGauntletCrownHowtoBtnEl() {
if (gauntletCrownHowtoUsesMg2LayoutPlay()) {
return document.getElementById('btn-gch-ready-mg2');
}
return document.getElementById('btn-gch-ready');
}
function setPlayMg2HowtoLivePlay(on) {
document.documentElement.classList.toggle('play-mg2-howto-live', !!on);
}
function clearGchMg2OverlayBoundsPlay(ov) {
ov = ov || document.getElementById('gauntlet-crown-howto-overlay');
if (!ov) return;
['position', 'left', 'top', 'width', 'height', 'right', 'bottom', 'transform'].forEach((p) => {
ov.style.removeProperty(p);
});
}
function dismissGauntletCrownHowtoOverlayPlay() {
const howto = document.getElementById('gauntlet-crown-howto-overlay');
if (howto) {
howto.classList.add('is-hidden');
setPlayMg2HowtoLivePlay(false);
clearGchMg2OverlayBoundsPlay(howto);
}
gauntletCrownHowtoVisible = false;
}
/** ภารกิจคำถาม mng8a80o — Ready Status / ปุ่ม แบบเดียวกับ Mega Virus (gauntlet-crown-lobby-sync) */
function updateQuizQuestionMissionHowtoHud() {
if (!isQuizQuestionMissionUiMapPlay() || quizQuestionMissionPhase !== 'howto') return;
@@ -11476,8 +11580,8 @@
}
function updateGauntletCrownHowtoHud() {
const st = document.getElementById('gauntlet-crown-howto-status');
const btn = document.getElementById('btn-gch-ready');
const st = getGauntletCrownHowtoStatusEl();
const btn = getGauntletCrownHowtoBtnEl();
if (!btn || !usesCrownLobbyShellPlay() || gauntletCrownPregamePhase !== 'howto') return;
const humans = quizCarryPregameHumanIds();
const tot = Math.max(1, quizCarryPregameTotalPlayers());
@@ -11488,6 +11592,15 @@
}
const humansReady = humans.length > 0 && humans.every((id) => !!gauntletCrownLobbyReadyMap[id]);
btn.classList.toggle('is-start-phase', humansReady);
if (gauntletCrownHowtoUsesMg2LayoutPlay()) {
const btnImg = document.getElementById('btn-gch-ready-mg2-img');
if (btnImg) {
btnImg.src = humansReady
? (missionMockHudAssetUrl('btn-start.png') || (BASE + '/img/gauntlet-assets/btn-start.png'))
: (missionMockHudAssetUrl('btn-ready.png') || (BASE + '/img/gauntlet-assets/btn-ready.png'));
btnImg.alt = humansReady ? 'START' : 'READY';
}
}
btn.classList.toggle('is-read-only', !isMePlayHost());
btn.disabled = !isMePlayHost();
btn.setAttribute('aria-pressed', humansReady ? 'false' : ((myId && gauntletCrownLobbyReadyMap[String(myId)]) ? 'true' : 'false'));
@@ -11507,9 +11620,7 @@
applyGauntletPreviewSpawnLayout(false);
}
trySnapGauntletCrownRunwayScrollToSpawnsPlay();
const howto = document.getElementById('gauntlet-crown-howto-overlay');
if (howto) howto.classList.add('is-hidden');
gauntletCrownHowtoVisible = false;
dismissGauntletCrownHowtoOverlayPlay();
const cd = document.getElementById('gauntlet-crown-countdown');
const numEl = document.getElementById('gauntlet-crown-countdown-num');
function applyMegaBalloonLiveStartPlay() {
@@ -11600,6 +11711,11 @@
if (!ov) return;
gauntletCrownHowtoVisible = true;
ov.classList.remove('is-hidden');
if (isGauntletCrownHeistMapPlay()) {
setPlayMg2HowtoLivePlay(true);
updateGchTfMockScale(ov);
trySnapGauntletCrownRunwayScrollToSpawnsPlay();
}
if (socket && socket.connected) socket.emit('gauntlet-crown-lobby-sync-request');
gauntletCrownSyncGuestReadyIfNeeded();
updateGauntletCrownHowtoHud();
@@ -11683,7 +11799,7 @@
if (nick !== 0) return nick;
return String(a.id).localeCompare(String(b.id));
});
const top = baseRows.slice(0, 5);
const top = baseRows.slice(0, 6); /* Last Light รองรับ 6 ผู้เล่น — summary ต้องครบ 6 */
const ranked = top.map((row, idx) => {
const pos = idx + 1;
const rankBonus = gauntletCrownRankBonusLocal(pos);
@@ -11757,6 +11873,17 @@
eliminated: !!o.gauntletEliminated,
});
});
/* รวมตัวเรา (human) ถ้ายังไม่อยู่ — บาง mission (preview/จาก server) มีแต่บอท ทำให้ตัวเราตกหายจาก summary */
if (myId != null && !seen.has(String(myId))) {
seen.add(String(myId));
baseRows.push({
id: myId,
nickname: (me.nickname || nick || 'คุณ').trim() || 'คุณ',
characterId: me.characterId ?? null,
baseScore: me.gauntletEliminated ? 0 : Math.max(0, Number(me.gauntletScore) | 0),
eliminated: !!me.gauntletEliminated,
});
}
baseRows.sort((a, b) => {
if (b.baseScore !== a.baseScore) return b.baseScore - a.baseScore;
@@ -11764,7 +11891,7 @@
if (nick !== 0) return nick;
return String(a.id).localeCompare(String(b.id));
});
const top = baseRows.slice(0, 5);
const top = baseRows.slice(0, 6); /* Last Light รองรับ 6 ผู้เล่น — summary ต้องครบ 6 รวมตัวเรา (เดิม 5 ตัดคนคะแนนต่ำสุด/ตายออก) */
const ranked = top.map((row, idx) => {
const pos = idx + 1;
const rankBonus = gauntletCrownRankBonusLocal(pos);
@@ -19075,6 +19202,10 @@
/* ตัวละครที่ไม่มีไฟล์เลเยอร์สี (เช่น defaultChar) — compose คืน Image ดิบ → crop หน้าจากรูปดิบตรง ๆ กันช่องว่าง */
let drawSrc = srcCanvas;
if (!drawSrc && raw && raw.complete && raw.naturalWidth > 0 && raw.naturalHeight > 0) {
/* (tintable) composite API characters
'' ให applyCyberHudScoreAvatarImgWithRetry retry รอเลเยอรโหลด (avatar bot จะไมกรบจนกวาจะเป data URL อมสแล)
อย fallback raw "ไม่มีสี" ไม avatar กคนเหมอนก = "สีไม่ขึ้น"; ใช raw เฉพาะตวท API นยนวาไมเลเยอร (defaultChar) */
if (playCharLayerApi.status !== 'done' || playCharLayerFromApi(cid) === true) return '';
drawSrc = raw;
}
if (!drawSrc) return '';
@@ -19336,6 +19467,7 @@
if (Math.abs(s - _quizTfScaleLast) < 0.0005
&& root.dataset.qtfStackW === String(Math.round(cw))
&& root.dataset.qtfStackH === String(Math.round(ch))) {
syncMg2MockJumpButtonBoundsPlay(r, s);
return;
}
_quizTfScaleLast = s;
@@ -19406,10 +19538,33 @@
const s = missionMockOverlayScalePlay();
if (s > 0 && isFinite(s)) ov.style.setProperty('--gcm-tf-scale', String(s));
}
/* popup HOW TO PLAY (01-howto) — scale stage 1920×1080 ตามสูตรเดียวกัน */
/* popup HOW TO PLAY (01-howto) — scale stage 1920×1080 · MG2 ยึด #play-canvas-stack เหมือน HUD */
function updateGchTfMockScale(ov) {
ov = ov || document.getElementById('gauntlet-crown-howto-overlay');
if (!ov) return;
const mg2 = ov.classList.contains('gch-mg2-mock');
if (mg2) {
const stack = document.getElementById('play-canvas-stack');
if (!stack) return;
const r = stack.getBoundingClientRect();
const cw = Math.max(1, r.width || stack.clientWidth || 0);
const ch = Math.max(1, r.height || stack.clientHeight || 0);
const s = Math.min(cw / 1920, ch / 1080);
if (!(s > 0 && isFinite(s))) return;
ov.style.position = 'fixed';
ov.style.left = Math.round(r.left) + 'px';
ov.style.top = Math.round(r.top) + 'px';
ov.style.width = Math.round(cw) + 'px';
ov.style.height = Math.round(ch) + 'px';
ov.style.right = 'auto';
ov.style.bottom = 'auto';
ov.style.transform = 'none';
ov.style.setProperty('--gch-tf-scale', String(s));
const stage = ov.querySelector('.gch-mg2-stage');
if (stage) stage.setAttribute('aria-hidden', 'false');
return;
}
clearGchMg2OverlayBoundsPlay(ov);
const s = missionMockOverlayScalePlay();
if (s > 0 && isFinite(s)) ov.style.setProperty('--gch-tf-scale', String(s));
}
@@ -19503,7 +19658,8 @@
/** แถบ SCORE แนวนอน (mno9kb07) — ใช้รูปแบบเดียวกับ quiz_carry / พรีวิว editor */
root.classList.toggle(
'play-cyber-hud--gauntlet-crown-strip',
!!(on && (isGauntletCrownHeistMapPlay() || isQuizCarry())),
!!(on && (isGauntletCrownHeistMapPlay() || isQuizCarry())
&& !(missionMockOn && getMissionMockHudSkinPlay() === 'gauntlet_crown')),
);
root.classList.toggle('play-cyber-hud--score-flush-left', !!(on && isQuizCarry()));
root.classList.toggle('play-cyber-hud--jump-mission-hide-score', !!isJumpSurviveMissionUiMapPlay());
@@ -19895,7 +20051,9 @@
return a.y - b.y;
});
const cap = stripScoreHud ? (isQuizCarry() ? 8 : 6) : 8;
/* Minigame-2 6 (GAUNTLET_MAX_PLAYERS) 6
(เด cap 5 ตาม mockup วาดตวอยาง 5 คน ทำใหวเราคะแนนตำสดถกตดออก) */
const cap = mg2MockHud ? 6 : (stripScoreHud ? (isQuizCarry() ? 8 : 6) : 8);
let hudRows = rows.slice(0, Math.min(cap, rows.length));
if (stripScoreHud) {
const mi = hudRows.findIndex((row) => row.isMe);
@@ -20719,11 +20877,17 @@
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
const nameBase = name || '';
/* gauntlet (Last Light) live: ชื่อ/คะแนน/-10 ใช้ DOM overlay (syncGauntletNameDom) — ไม่วาดบน canvas */
/* crown map (Minigame-2): //-10 canvas "" ( mock )
เดมเชคเฉพาะ phase==='live' ตอน summary/howto canvas วาดชอกลบมา บแถบคะแนน (เหมอน avatar อน) */
const gntDomLabel = isGauntletCrownHeistMapPlay();
const withScore = isGauntlet() && typeof gauntletScoreLabel === 'number' && gauntletScoreLabel >= 0
? (nameBase + ' · ' + gauntletScoreLabel)
: nameBase;
ctx.fillText(withScore, sx, sy + 10);
if (isGauntletCrownHeistMapPlay()
if (!gntDomLabel) ctx.fillText(withScore, sx, sy + 10);
if (!gntDomLabel
&& isGauntletCrownHeistMapPlay()
&& gauntletCrownPregamePhase === 'live'
&& typeof crownPenaltyFxUntil === 'number'
&& crownPenaltyFxUntil > timeMs) {
const penImg = ensureGauntletCrownScorePenaltyImgPlay();
@@ -20847,7 +21011,10 @@
const ga = _quizGhostScreenAnchorPlay(axO, ayO, worldToScreen, zDraw);
drawQuizWrongGhostOverlayPlay(ga.sx, ga.sy, ga.sizeH, id, peerName);
} else {
drawAvatar(axO, ayO, false, peerName, o.characterId, faceDirOther, otherWalk, ot, (o.gauntletJumpVis != null ? o.gauntletJumpVis : o.gauntletJumpTicks) || 0, o.gauntletScore || 0, quizCarrySignForEntity(o), isGauntletCrownHeistMapPlay() ? (o.gauntletCrownPenaltyFxUntil || 0) : 0);
drawAvatar(axO, ayO, false, peerName, o.characterId, faceDirOther, otherWalk, ot, (o.gauntletJumpVis != null ? o.gauntletJumpVis : o.gauntletJumpTicks) || 0,
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (o.gauntletScore || 0) : undefined,
quizCarrySignForEntity(o),
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (o.gauntletCrownPenaltyFxUntil || 0) : 0);
}
if (botOut) ctx.restore();
} else {
@@ -20863,11 +21030,16 @@
const gam = _quizGhostScreenAnchorPlay(axMe, ayMe, worldToScreen, zDraw);
drawQuizWrongGhostOverlayPlay(gam.sx, gam.sy, gam.sizeH, myId, me.nickname + meTag);
} else {
drawAvatar(axMe, ayMe, true, me.nickname + meTag, me.characterId, faceDirMe, meWalking, mt, meGauntletJumpVis, me.gauntletScore || 0, quizCarrySignForEntity(me), isGauntletCrownHeistMapPlay() ? (me.gauntletCrownPenaltyFxUntil || 0) : 0);
drawAvatar(axMe, ayMe, true, me.nickname + meTag, me.characterId, faceDirMe, meWalking, mt, meGauntletJumpVis,
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (me.gauntletScore || 0) : undefined,
quizCarrySignForEntity(me),
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (me.gauntletCrownPenaltyFxUntil || 0) : 0);
}
if (isJumpSurvive() && jumpSurviveEliminated) ctx.restore();
}
});
/* Minigame-2: อัปเดต DOM overlay (ชื่อ/คะแนน/-10) ให้ตรงตำแหน่งตัวละครบน canvas */
try { syncGauntletNameDom(worldToScreen, zDraw, tileSize); } catch (eGntDom) { /* ignore */ }
}
/* ไอคอนคำถามพิเศษในฉาก (วาดทับ map/ผู้เล่น) + ตรวจการเดินชน */
if (specialQuizIcon) {
@@ -20896,10 +21068,8 @@
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
if (isGauntletCrownHeistMapPlay()) {
ctx.fillText('พรมแดงสุดท้าย | คะแนน ' + (me.gauntletScore || 0) + ' · ชน -10 · ขอบซ้ายตาย · สูงสุด 6 คน', 10, 24);
ctx.font = '12px sans-serif';
ctx.fillStyle = '#c0caf5';
ctx.fillText('Space / W / ↑ = กระโดด · เลน + เลเซอร์ · จบเกมมีโบนัสอันดับ + เกรดทีม', 10, 42);
/* Minigame-2 (Last Light): cyber HUD + DOM overlay debug text ( · -10 · ) canvas
(เดมโผลมซายบนท SCORE label เวลา useCyberPlayHud() = false ใน preview embed) */
} else {
ctx.fillText('พรมแดงสุดท้าย | คะแนน: ' + (me.gauntletScore || 0) + ' (ข้าม 1 ชิ้น +1) · สูงสุด 6 คน', 10, 24);
ctx.font = '12px sans-serif';
@@ -21508,9 +21678,7 @@
})();
(function wireGauntletCrownHowtoPrimary() {
const btn = document.getElementById('btn-gch-ready');
if (!btn) return;
btn.addEventListener('click', () => {
function onHowtoPrimaryClick() {
if (isJumpSurviveMissionUiMapPlay()) {
if (jumpSurviveMissionPhase !== 'howto') return;
const humans = quizCarryPregameHumanIds();
@@ -21654,6 +21822,10 @@
gauntletCrownLobbyReadyMap[sid] = next;
updateGauntletCrownHowtoHud();
socket.emit('gauntlet-crown-lobby-ready', { ready: next });
}
['btn-gch-ready', 'btn-gch-ready-mg2'].forEach((id) => {
const btn = document.getElementById(id);
if (btn) btn.addEventListener('click', onHowtoPrimaryClick);
});
})();
+237 -24
View File
@@ -21,6 +21,9 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;800&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css?v=20260506-stack-tower-cover">
<!-- Minigame-2 HOW TO PLAY mock (01-howto) — layer positions / popup / status / btn -->
<link rel="stylesheet" href="/onlydesign/Cut%20[Justice%20Game]_full1.1/Minigame-2/assets/css/game-hud.css">
<link rel="stylesheet" href="/onlydesign/Cut%20[Justice%20Game]_full1.1/Minigame-2/assets/css/howto.css">
<style>
html, body {
height: 100%;
@@ -124,6 +127,52 @@
justify-content: center;
box-sizing: border-box;
}
/* Minigame-2 DOM overlay — ชื่อ/คะแนนใต้ตัวละคร + เลข -10 (sync world→screen)
container map buffer→display ด้วย transform:scale(dispScale) (set จาก JS) → ข้างในใช้ buffer coords ตรง ๆ */
#gauntlet-name-overlay {
position: absolute;
left: 0;
top: 0;
transform-origin: 0 0;
pointer-events: none;
z-index: 6;
overflow: hidden;
}
#gauntlet-name-overlay.is-hidden { display: none !important; }
#gauntlet-name-overlay .gnt-label {
position: absolute;
transform: translate(-50%, 0);
white-space: nowrap;
font-family: "NotoSansThai", sans-serif;
font-weight: 700;
font-size: 11px;
line-height: 1;
color: #e8f4ff;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
text-align: center;
}
#gauntlet-name-overlay .gnt-label .gnt-sc {
color: #ffd96b;
font-weight: 900;
margin-left: 5px;
}
#gauntlet-name-overlay .gnt-label.is-out { opacity: 0.45; }
/* เลข -10 ลอยขึ้น+จาง (penalty) */
#gauntlet-name-overlay .gnt-pen {
position: absolute;
transform: translate(-50%, 0);
width: 72px;
height: auto;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8));
animation: gntPenFloat 0.85s ease-out forwards;
will-change: transform, opacity;
pointer-events: none;
}
@keyframes gntPenFloat {
0% { opacity: 0; transform: translate(-50%, 6px) scale(0.8); }
18% { opacity: 1; transform: translate(-50%, 0) scale(1.1); }
100% { opacity: 0; transform: translate(-50%, -40px) scale(1); }
}
#play-canvas-stage #game-canvas {
flex: none;
display: block;
@@ -3299,7 +3348,7 @@
height: 104px;
overflow: hidden;
border-radius: 8px;
z-index: 3;
z-index: 1;
}
#play-cyber-hud.play-cyber-hud--quiz-tf-mock .play-cyber-profile-frame .play-cyber-portrait-img,
#play-cyber-hud.play-cyber-hud--quiz-tf-mock.play-cyber-hud--question-mission .play-cyber-profile-frame .play-cyber-portrait-img {
@@ -3359,7 +3408,7 @@
transform-origin: center center;
}
/* popup-Howto.png 1451×851 → mockup โชว์ 1500×880 @ (50%, 50px) */
#gauntlet-crown-howto-overlay.gch-quiz-tf-mock .gch-bg {
#gauntlet-crown-howto-overlay.gch-quiz-tf-mock:not(.gch-mg2-mock) .gch-bg {
position: absolute;
left: 50%;
top: 50px;
@@ -3444,22 +3493,164 @@
#gauntlet-crown-howto-overlay.gch-quiz-tf-mock .gch-inner.gch-inner--art .btn-gch-ready:active {
transform: translateY(2px);
}
/* Minigame-2 mno9kb07 — HOW TO PLAY ใช้ปุ่ม/ฉากจาก gauntlet-assets */
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-backdrop {
/* Minigame-2 mno9kb07 — HOW TO PLAY บน canvas (แทน mg-bg ใน mock 01-howto) */
#gauntlet-crown-howto-overlay.gch-mg2-mock {
display: block;
align-items: stretch;
justify-content: stretch;
padding: 0;
pointer-events: none;
/* ตรง design 01-howto: HUD (score bar/host/mic = z8) อยู่ "หลัง" overlay(z9)+popup(z10)
base .play-cyber-hud คือ z55 → ต้องยก overlay เหนือ HUD ไม่งั้นแถบคะแนน/โปรไฟล์ลอยทับหัว HOW TO PLAY */
z-index: 56;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock:not(.is-hidden) ~ #play-cyber-hud,
html.play-mg2-howto-live #play-cyber-hud {
z-index: 55;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock > .gch-backdrop.gch-legacy-backdrop {
display: none !important;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-legacy-only {
display: none !important;
}
#gauntlet-crown-howto-overlay:not(.gch-mg2-mock) .gch-mg2-only {
display: none !important;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-shell {
position: absolute;
left: 50%;
top: 50%;
width: 1920px !important;
height: 1080px !important;
min-width: 1920px !important;
min-height: 1080px !important;
max-width: none !important;
max-height: none !important;
margin: 0;
padding: 0;
transform: translate(-50%, -50%) scale(var(--gch-tf-scale, 1));
transform-origin: center center;
pointer-events: none;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .layer {
position: absolute;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .mg-overlay {
left: 0;
top: 0;
width: 1920px;
height: 1080px;
background: rgba(4, 6, 18, 0.45);
backdrop-filter: none;
z-index: 1;
pointer-events: none;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-inner.gch-inner--art .btn-gch-ready {
background-image: var(--mmh-btn-ready, url('/Game/img/gauntlet-assets/btn-ready.png'));
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-popup {
left: 50%;
top: 50px;
transform: translateX(-50%);
width: 1500px;
height: 880px;
z-index: 2;
pointer-events: none;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-inner.gch-inner--art .btn-gch-ready.is-start-phase {
background-image: var(--mmh-btn-start, url('/Game/img/gauntlet-assets/btn-start.png'));
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-popup img {
width: 100%;
height: 100%;
max-height: none; /* ยกเลิก cap min(88vh,600px) จาก .gch-bg — ไม่งั้นรูป popup ถูกบีบเหลือ 600/880 (สเกลแล้ว ~400px) ป๊อปอัพเตี้ย READY หลุดกรอบ */
object-fit: fill;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-status {
left: 50%;
top: 808px;
transform: translateX(-50%);
z-index: 3;
pointer-events: none;
font-family: "NotoSansThai", sans-serif;
font-weight: 700;
font-size: 22px;
color: #f067e8;
text-shadow:
0 0 10px rgba(240, 103, 232, 0.6),
0 0 22px rgba(237, 86, 224, 0.35);
letter-spacing: 0.5px;
text-align: center;
white-space: nowrap;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-status .gch-status {
position: static;
transform: none;
margin: 0;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-status .gch-status.is-hidden {
display: none !important;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-btn.btn-gch-ready {
left: 957px;
top: 885px;
transform: translate(-50%, -50%);
z-index: 4;
pointer-events: auto;
cursor: pointer;
border: none;
padding: 0;
background: transparent !important;
background-image: none !important;
font-size: 0;
color: transparent;
width: auto;
height: 120px;
filter: none;
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-btn.btn-gch-ready img {
height: 100%;
width: auto;
display: block;
filter: drop-shadow(0 0 18px rgba(120, 220, 255, 0.65));
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-btn.btn-gch-ready:hover {
transform: translate(-50%, calc(-50% - 3px));
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-btn.btn-gch-ready:hover img {
filter: drop-shadow(0 0 28px rgba(120, 220, 255, 0.95));
}
#gauntlet-crown-howto-overlay.gch-mg2-mock .gch-mg2-stage .howto-btn.btn-gch-ready:active {
transform: translate(-50%, calc(-50% + 2px));
}
/* ====== Minigame-2 mno9kb07 — HUD แนวนอน (mg2-score-bar) fix 1920×1080 ====== */
/* selector ต้องชนะ .play-cyber-hud--quiz-tf-mock ทั่วไป (score-bar แนวตั้ง 156×433 + overflow:hidden) */
html #play-cyber-hud.play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"] {
display: block;
padding: 0 !important;
grid-template: none !important;
}
html #play-cyber-hud.play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"].play-cyber-hud--gauntlet-crown-strip {
display: block !important;
grid-template: none !important;
padding: 0 !important;
}
html #play-cyber-hud.play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"] .play-cyber-self {
position: absolute !important;
left: 1729px !important;
top: 21px !important;
right: auto !important;
width: 201px !important;
display: block !important;
grid-area: unset !important;
justify-self: unset !important;
align-self: unset !important;
margin: 0 !important;
}
html #play-cyber-hud.play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"] .play-cyber-profile-frame-overlay {
display: block !important;
z-index: 2 !important;
}
html #play-cyber-hud.play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"].play-cyber-hud--gauntlet-crown-strip {
display: block;
@@ -3568,14 +3759,14 @@
content: '';
position: absolute;
inset: 0;
z-index: 2;
z-index: 1; /* frame score-avartar.png = "พื้นหลัง" (กล่องดำ+ขอบ cyan ทึบ) → ต้องอยู่หลัง avatar ไม่งั้นบัง avatar มืด */
pointer-events: none;
background: var(--mmh-score-avatar, url('/Game/img/gauntlet-assets/score-avartar.png')) no-repeat center / contain;
}
#play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"] .play-cyber-mg2-av-clip {
position: absolute;
inset: 4px;
z-index: 3;
inset: 6px; /* นั่งในขอบ cyan ของ frame (avatar บนสุด z2) */
z-index: 2;
overflow: hidden;
border-radius: 6px;
}
@@ -3585,9 +3776,10 @@
width: 100%;
height: 100%;
object-fit: cover;
object-position: center 30%;
object-position: 50% 32%;
border-radius: 6px;
transform: scale(0.86) translateY(-5px);
/* portrait 120×120 หัวอยู่บน → ดันหัวให้อยู่กลางกรอบ ตรงกับ avatar ในกล่อง summary (gcm-av) ที่หัวกลางสวย */
transform: scale(0.9) translateY(-3px);
transform-origin: center center;
}
#play-cyber-hud.play-cyber-hud--quiz-tf-mock[data-mission-mock-skin="gauntlet_crown"] .play-cyber-mg2-score {
@@ -3657,16 +3849,18 @@
#gauntlet-crown-mission-overlay.gcm-quiz-tf-mock .gcm-header-tab {
display: none !important;
}
/* ranks: 5 ช่อง ×137 gap30 @ (323,168) width805 (mockup .sm-ranks) */
/* ranks: รองรับ 56 ช่อง ×137 gap30 — กว้าง 980 (6 ช่อง = 6×137+5×30 = 972) จัดกึ่งกลางพาเนล (center 725.5)
เดิม width805 พอดีแค่ 5 ช่อง → คนที่ 6 wrap ลงแถว 2 (ตำแหน่งเพี้ยน) */
#gauntlet-crown-mission-overlay.gcm-quiz-tf-mock .gcm-rank-row {
position: absolute;
left: 323px;
left: 235px;
top: 168px;
width: 805px;
width: 980px;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: flex-start;
justify-content: center;
gap: 30px;
@@ -4063,8 +4257,11 @@
filter: drop-shadow(0 0 14px rgba(120, 220, 255, 0.55));
}
#gauntlet-crown-mission-overlay.gcm-mg2-mock.gcm-quiz-tf-mock .btn-gcm-done {
left: 784px !important;
top: 818px !important;
/* .gcm-shell = ตัวพาเนล popup-result (1451×851) ไม่ใช่ stage 1920×1080
design sm-btn-done อยู่ stage(784,818) แต่ panel อยู่ stage@234,114 → พิกัดในพาเนล = (784-234, 818-114) = (550,704) = กลาง-ล่างพาเนล
เดิมใส่ 784/818 (พิกัด stage) ตรง ๆ ทำให้ปุ่มหลุดไปขวา-ล่างเลยกรอบ */
left: 550px !important;
top: 704px !important;
width: 351px;
height: 141px;
background-image: url('/Game/img/gauntlet-assets/btn-done.png');
@@ -4164,6 +4361,8 @@
<div class="play-canvas-stack" id="play-canvas-stack">
<div id="play-canvas-stage" class="play-canvas-stage">
<canvas id="game-canvas"></canvas>
<!-- Minigame-2: ชื่อ+คะแนนใต้ตัวละคร + เลข -10 → DOM overlay (sync world→screen) แทนวาดบน canvas เพื่อให้ตรง mock -->
<div id="gauntlet-name-overlay" aria-hidden="true"></div>
</div>
<button type="button" id="quiz-carry-grab-btn" class="is-hidden" aria-label="หยิบหรือส่งคำตอบ (Grab)" title="หยิบ / ส่งคำตอบ — เหมือนกด F">
<img src="/Game/img/quiz-carry/btn-grab.png" alt="" width="256" height="256" decoding="async" />
@@ -4307,15 +4506,29 @@
</div>
</div>
<div id="gauntlet-crown-howto-overlay" class="is-hidden" role="dialog" aria-modal="true" aria-label="HOW TO PLAY — Last Light">
<div class="gch-backdrop" aria-hidden="true"></div>
<div class="gch-backdrop gch-legacy-backdrop" aria-hidden="true"></div>
<div class="gch-shell">
<img class="gch-bg" src="/Game/img/gauntlet-assets/popup-Howto.png" alt="" width="920" height="520" decoding="async" />
<div class="gch-inner gch-inner--art">
<!-- legacy (Mega Virus / แมปอื่น) -->
<img class="gch-bg gch-legacy-only" src="/Game/img/gauntlet-assets/popup-Howto.png" alt="" width="920" height="520" decoding="async" />
<div class="gch-inner gch-inner--art gch-legacy-only">
<div class="gch-art-footer">
<p id="gauntlet-crown-howto-status" class="gch-status is-hidden" aria-live="polite"></p>
<button type="button" class="btn-gch-ready" id="btn-gch-ready" title="READY"><span class="sr-only">READY</span></button>
<button type="button" class="btn-gch-ready gch-legacy-only" id="btn-gch-ready" title="READY"><span class="sr-only">READY</span></button>
</div>
</div>
<!-- Minigame-2 mno9kb07 — mock 01-howto (canvas = mg-bg อยู่ใต้ stack) -->
<div class="gch-mg2-stage gch-mg2-only" aria-hidden="true">
<div class="layer mg-overlay" aria-hidden="true"></div>
<div class="layer howto-popup">
<img class="gch-bg" src="/Game/img/gauntlet-assets/popup-Howto.png" alt="HOW TO PLAY" width="1500" height="880" decoding="async" />
</div>
<div class="layer howto-status">
<p id="gauntlet-crown-howto-status-mg2" class="gch-status is-hidden" aria-live="polite"></p>
</div>
<button type="button" class="layer howto-btn btn btn-gch-ready" id="btn-gch-ready-mg2" title="READY">
<img id="btn-gch-ready-mg2-img" src="/Game/img/gauntlet-assets/btn-ready.png" alt="READY" width="299" height="120" decoding="async" />
</button>
</div>
</div>
</div>
<div id="gauntlet-crown-countdown" class="is-hidden" role="alert" aria-live="assertive" aria-atomic="true">
@@ -4494,7 +4707,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.00610144433"></script>
<script src="js/play.js?v=0.00610200004"></script>
<div class="version-tag">v —</div>
</body>
</html>