diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index 22cbf0f..2e6b38f 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -641,6 +641,10 @@ let stackTowerLifeBarImg = null; let stackTowerLifeHitImg = null; let stackTowerLifeFullImg = null; + /** Space Shooter (violent_crime): life HUD โหลดจาก /img/ViolentCrime/ แยกจาก TowerBlock ของ Stack */ + let violentCrimeLifeBarImg = null; + let violentCrimeLifeHitImg = null; + let violentCrimeLifeFullImg = null; /** @type {{ until: number } | null} */ let stackTowerHeartMinusFx = null; const STACK_TOWER_HEART_MINUS_FX_MS = 1500; @@ -745,7 +749,9 @@ } ctx.save(); - ctx.imageSmoothingEnabled = false; + /* BG อวกาศเป็นภาพ illustration (loop 1024w สเกลขึ้น ~1.9x) — เปิด smoothing ไม่ให้แตกเป็นบล็อก (stack tower BG กว้าง ~1920 จึงคงปิดไว้) */ + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = 'high'; ctx.beginPath(); ctx.rect(0, 0, cwR, chR); ctx.clip(); @@ -5756,6 +5762,26 @@ } } + /** Space Shooter (mnpz6rkp): โหลด life HUD จาก /img/ViolentCrime/ แล้วคืน image set ให้ drawStackTowerLifeIntegrityBarPlay */ + function ensureViolentCrimeLifeHudImagesPlay() { + if (!violentCrimeLifeBarImg) { + violentCrimeLifeBarImg = new Image(); + violentCrimeLifeBarImg.decoding = 'async'; + violentCrimeLifeBarImg.src = violentCrimeAssetUrl('life-bar.png'); + } + if (!violentCrimeLifeHitImg) { + violentCrimeLifeHitImg = new Image(); + violentCrimeLifeHitImg.decoding = 'async'; + violentCrimeLifeHitImg.src = violentCrimeAssetUrl('life-hit.png'); + } + if (!violentCrimeLifeFullImg) { + violentCrimeLifeFullImg = new Image(); + violentCrimeLifeFullImg.decoding = 'async'; + violentCrimeLifeFullImg.src = violentCrimeAssetUrl('life-full.png'); + } + return { bar: violentCrimeLifeBarImg, full: violentCrimeLifeFullImg, hit: violentCrimeLifeHitImg }; + } + /** * สัดส่วนสูงของ life-bar.png ที่เป็นข้อความ "SYSTEM INTEGRITY" (ที่เหลือ = แถวหัวใจแยกวาดเอง ไม่ทับขอบ `[` `]` ใน PNG) */ @@ -5764,14 +5790,16 @@ /** * วาดหัวข้อจาก life-bar.png (ครอปบน) + แถวหัวใจ life-full / life-hit กึ่งกลาง — คืนความสูงที่ใช้จริง */ - function drawStackTowerLifeIntegrityBarPlay(ctx, destX, destY, maxW, maxH, lifeSlots, livesRem) { - ensureStackTowerLifeHudImagesPlay(); - const bar = stackTowerLifeBarImg; - const fullIm = (stackTowerLifeFullImg && stackTowerLifeFullImg.complete && stackTowerLifeFullImg.naturalWidth > 0) - ? stackTowerLifeFullImg + function drawStackTowerLifeIntegrityBarPlay(ctx, destX, destY, maxW, maxH, lifeSlots, livesRem, opts) { + const imgs = (opts && opts.images) + ? opts.images + : (ensureStackTowerLifeHudImagesPlay(), { bar: stackTowerLifeBarImg, full: stackTowerLifeFullImg, hit: stackTowerLifeHitImg }); + const bar = imgs.bar; + const fullIm = (imgs.full && imgs.full.complete && imgs.full.naturalWidth > 0) + ? imgs.full : ensureStackTowerHeartMinusImgPlay(); - const hitIm = stackTowerLifeHitImg && stackTowerLifeHitImg.complete && stackTowerLifeHitImg.naturalWidth > 0 - ? stackTowerLifeHitImg + const hitIm = imgs.hit && imgs.hit.complete && imgs.hit.naturalWidth > 0 + ? imgs.hit : null; const slots = Math.max(1, Math.min(20, Math.floor(lifeSlots) || 1)); const lives = Math.max(0, Math.min(slots, Math.floor(livesRem) || 0)); @@ -6371,6 +6399,7 @@ return; } cd.classList.remove('is-hidden'); + syncGauntletCrownCountdownMg2ModePlay(cd); let n = 3; setCountdown321QuestionAssetGraphic(numEl, n); const step = function () { @@ -6691,6 +6720,7 @@ return; } cd.classList.remove('is-hidden'); + syncGauntletCrownCountdownMg2ModePlay(cd); let n = 3; setCountdown321QuestionAssetGraphic(numEl, n); const step = function () { @@ -7088,6 +7118,7 @@ return; } cd.classList.remove('is-hidden'); + syncGauntletCrownCountdownMg2ModePlay(cd); let n = 3; setCountdown321QuestionAssetGraphic(numEl, n); const step = function () { @@ -7622,6 +7653,7 @@ return; } cd.classList.remove('is-hidden'); + syncGauntletCrownCountdownMg2ModePlay(cd); let n = 3; setCountdown321QuestionAssetGraphic(numEl, n); const step = function () { @@ -9571,6 +9603,19 @@ function syncGauntletCrownJumpButton() { const btn = document.getElementById('gauntlet-crown-jump-btn'); if (!btn) return; + /* MG5 jump_survive (jumper): โชว์ปุ่ม JUMP มุมขวาล่าง (design .mg5-jump 1648/818/231) — ให้ touch กระโดดได้ (เดิม keyboard-only) */ + if (mapData && isJumpSurviveMissionHudActivePlay()) { + btn.classList.remove('is-hidden'); + btn.setAttribute('aria-hidden', 'false'); + btn.style.pointerEvents = ''; + btn.classList.add('mg2-jump'); + const jimg = btn.querySelector('img'); + if (jimg && jimg.src.indexOf('/Jumper/') < 0) jimg.src = jumperAssetUrl('btn-jump.png'); + const hudJ = document.getElementById('play-cyber-hud'); + if (hudJ && hudJ.classList.contains('play-cyber-hud--quiz-tf-mock')) updateQuizTfMockScale(hudJ); + syncMg2MockJumpButtonBoundsPlay(); + return; + } const pregameUi = gauntletCrownPregamePhase === 'howto' || gauntletCrownPregamePhase === 'countdown'; const liveUi = gauntletCrownPregamePhase === 'live' && gauntletCrownRunwayAvatarRunAllowedPlay(); const show = !!(mapData && isGauntletCrownHeistMapPlay() && !me.gauntletEliminated && (pregameUi || liveUi)); @@ -9620,6 +9665,175 @@ btn.style.pointerEvents = ok ? '' : 'none'; } + /** MG3 (mnn93hpi): ผู้เล่นตาปัจจุบัน (active node) สำหรับ host portrait ใน DOM HUD */ + function mg3CurrentSeatRowPlay() { + const seat = getStackPreviewCurrentSeat(); + const order = stackPreviewTurnOrder; + let entry = null; + if (order && order.length) entry = order.find((e) => e.seat === seat) || null; + if (!entry || entry.kind === 'human') { + return { + id: myId, + characterId: me.characterId || getPlayCharacterId(), + isMe: true, + playTint: me.playTint || resolvePlayTintForPeer(String(myId != null ? myId : 'me')), + }; + } + const o = entry.botId ? others.get(entry.botId) : null; + return { + id: entry.botId, + characterId: o ? o.characterId : null, + isMe: false, + playTint: o ? (o.playTint || resolvePlayTintForPeer(entry.botId)) : null, + }; + } + + /** + * MG3 Stack Tower live HUD → DOM overlay (#mg3-tower-hud) — canvas object-fit:cover ครอป HUD ขอบจอ + * จึงย้าย life bar (ขวาบน) + progress (ซ้ายล่าง) + code terminal + host frame มาเป็น DOM สเกลตาม stage + */ + function syncStackTowerMissionDomHudPlay() { + const root = document.getElementById('mg3-tower-hud'); + if (!root) return; + const on = !!(mapData && isStack() && stackMini && isStackTowerMissionHudActivePlay()); + root.classList.toggle('is-hidden', !on); + root.setAttribute('aria-hidden', on ? 'false' : 'true'); + if (!on) return; + + if (!root.dataset.mg3Assets) { + const setSrc = (sel, file) => { const el = root.querySelector(sel); if (el) el.src = stackTowerAssetUrl(file); }; + setSrc('.mg3-life-bar', 'life-bar.png'); + setSrc('.mg3-host-frame', 'Avartar.png'); + setSrc('.mg3-terminal-bg', 'code-bg.png'); + setSrc('.mg3-progress-label', 'progress-txt.png'); + setSrc('.mg3-progress-track', 'progress-bg.png'); + setSrc('.mg3-progress-fill', 'progress.png'); + root.dataset.mg3Assets = '1'; + } + + /* วาง design plane 1920×1080 → scale แบบ contain + จัดกึ่งกลางใน stage (HUD เห็นครบ ไม่โดน object-fit:cover ครอป) */ + const stack = document.getElementById('play-canvas-stack'); + if (stack) { + const r = stack.getBoundingClientRect(); + const cw = Math.max(1, r.width || 0); + const ch = Math.max(1, r.height || 0); + const s = Math.min(cw / 1920, ch / 1080); + if (s > 0 && isFinite(s)) { + const left = Math.round(r.left + (cw - 1920 * s) / 2); + const top = Math.round(r.top + (ch - 1080 * s) / 2); + const key = left + ',' + top + ',' + s.toFixed(4); + if (root.dataset.mg3Box !== key) { + root.dataset.mg3Box = key; + root.style.left = left + 'px'; + root.style.top = top + 'px'; + root.style.transform = 'scale(' + s + ')'; + } + } + } + + const slots = Math.max(1, Math.min(20, Math.floor(Number(playStackTeamMissesMax) || 3))); + const lives = Math.max(0, Math.min(slots, Math.floor(Number( + stackMini.teamMissesLeft != null ? stackMini.teamMissesLeft : (stackMini.lives != null ? stackMini.lives : 3), + ) || 0))); + const heartsEl = document.getElementById('mg3-life-hearts'); + if (heartsEl) { + const hk = slots + '/' + lives; + if (heartsEl.dataset.hk !== hk) { + heartsEl.dataset.hk = hk; + heartsEl.innerHTML = ''; + for (let i = 0; i < slots; i++) { + const im = document.createElement('img'); + im.src = stackTowerAssetUrl(i < lives ? 'life-full.png' : 'life-hit.png'); + im.alt = ''; + heartsEl.appendChild(im); + } + } + } + + const pct = Math.max(0, Math.min(100, Math.round(Number(stackTowerDecryptPctPlay()) || 0))); + if (root.dataset.mg3Pct !== String(pct)) { + root.dataset.mg3Pct = String(pct); + /* fill เป็น px ในระนาบ design (track ภายในจาก left:30 กว้างสุด ~358px) */ + const fillEl = document.getElementById('mg3-progress-fill'); + if (fillEl) fillEl.style.width = Math.round(358 * pct / 100) + 'px'; + const pctEl = document.getElementById('mg3-progress-pct'); + if (pctEl) pctEl.textContent = '[ ' + pct + '% ]'; + } + + const hostAv = document.getElementById('mg3-host-av'); + if (hostAv) { + const row = mg3CurrentSeatRowPlay(); + const hk = (row.id != null ? row.id : 'me') + '|' + (row.characterId || ''); + if (hostAv.dataset.hk !== hk) { + hostAv.dataset.hk = hk; + setCyberHudScoreAvatarImg(hostAv, row); + } + } + + const logEl = document.getElementById('mg3-terminal-log'); + if (logEl) { + const lines = stackPreviewHudLog && stackPreviewHudLog.length + ? stackPreviewHudLog.slice(-7) + : ['>>> DECRYPT UPLINK READY', '>>> SPACE / CLICK = DROP (ตาคุณ)']; + const txt = lines.join('\n'); + if (logEl.textContent !== txt) logEl.textContent = txt; + } + } + + /** + * MG6 Space Shooter life → overlay #mg6-life-hud (ระนาบ design 1920×1080 scale ตาม stage แบบเดียวกับ MG3) + * life-bar.png + หัวใจ 3 ดวง (ViolentCrime) ตำแหน่ง/ขนาดเท่า MG3 — ไม่พึ่ง flex ของ cyber HUD (กันบีบ/ล้นขอบ) + */ + function syncSpaceShooterLifeDomHudPlay() { + const root = document.getElementById('mg6-life-hud'); + if (!root) return; + const on = !!(mapData && isSpaceShooter() && isSpaceShooterMissionHudActivePlay()); + root.classList.toggle('is-hidden', !on); + root.setAttribute('aria-hidden', on ? 'false' : 'true'); + if (!on) return; + if (!root.dataset.assets) { + const bar = root.querySelector('.mg6-life-bar'); + if (bar) bar.src = violentCrimeAssetUrl('life-bar.png'); + root.dataset.assets = '1'; + } + const stack = document.getElementById('play-canvas-stack'); + if (stack) { + const r = stack.getBoundingClientRect(); + const cw = Math.max(1, r.width || 0); + const ch = Math.max(1, r.height || 0); + const s = Math.min(cw / 1920, ch / 1080); + if (s > 0 && isFinite(s)) { + const left = Math.round(r.left + (cw - 1920 * s) / 2); + const top = Math.round(r.top + (ch - 1080 * s) / 2); + const key = left + ',' + top + ',' + s.toFixed(4); + if (root.dataset.box !== key) { + root.dataset.box = key; + root.style.left = left + 'px'; + root.style.top = top + 'px'; + root.style.transform = 'scale(' + s + ')'; + } + } + } + const slots = SPACE_SHOOTER_MISSION_MAX_ASTEROID_HITS; + const hits = Math.max(0, Math.min(slots, Number(me.spaceShooterHits) || 0)); + const rem = slots - hits; + const heartsEl = document.getElementById('mg6-life-hearts'); + if (heartsEl) { + const hk = slots + '/' + rem; + if (heartsEl.dataset.hk !== hk) { + heartsEl.dataset.hk = hk; + heartsEl.innerHTML = ''; + for (let i = 0; i < slots; i++) { + const im = document.createElement('img'); + im.src = violentCrimeAssetUrl(i < rem ? 'life-full.png' : 'life-hit.png'); + im.alt = ''; + im.decoding = 'async'; + heartsEl.appendChild(im); + } + } + } + } + function resetQuizCarryPlayState() { cancelQuizCarrySessionCompleteResultToSummary(); const qcmo = document.getElementById('quiz-carry-mission-overlay'); @@ -16240,6 +16454,11 @@ const jq = jumpSurviveJumpQueued; jumpSurviveJumpQueued = false; + /* virtual joystick (touch) — แกน x ป้อนซ้าย/ขวาเหมือนคีย์ A/D (deadzone จัดการในตัว update แล้ว) */ + const joyX = isJumpSurviveMissionHudActivePlay() ? (Number(quizQuestionMissionJoyVecX) || 0) : 0; + const moveLeftIn = !!(keys['ArrowLeft'] || keys['KeyA']) || joyX < -0.001; + const moveRightIn = !!(keys['ArrowRight'] || keys['KeyD']) || joyX > 0.001; + const r = jumpSurvivePhysicsIntegrate(md, { dt, cw, @@ -16249,8 +16468,8 @@ vy0: jumpSurviveVy, wasOnGround: jumpSurviveOnGround, jumpQueued: jq, - moveLeft: !!(keys['ArrowLeft'] || keys['KeyA']), - moveRight: !!(keys['ArrowRight'] || keys['KeyD']), + moveLeft: moveLeftIn, + moveRight: moveRightIn, }); if (r.died) { @@ -16267,9 +16486,9 @@ stepJumpSurvivePreviewBots(dt, halfViewH); if (!jumpSurviveEliminated) { - me.isWalking = Math.abs(jumpSurviveVy) > 40 || !!(keys['ArrowLeft'] || keys['KeyA'] || keys['ArrowRight'] || keys['KeyD']); - if (keys['ArrowRight'] || keys['KeyD']) me.direction = 'right'; - else if (keys['ArrowLeft'] || keys['KeyA']) me.direction = 'left'; + me.isWalking = Math.abs(jumpSurviveVy) > 40 || moveLeftIn || moveRightIn; + if (moveRightIn) me.direction = 'right'; + else if (moveLeftIn) me.direction = 'left'; if (Date.now() - jumpSurviveLastEmitT > 80) { jumpSurviveLastEmitT = Date.now(); @@ -18728,6 +18947,8 @@ const mono = '11px ui-monospace, "Cascadia Mono", Consolas, monospace'; const monoSm = '9px ui-monospace, Consolas, monospace'; const towerMapHud = isStackTowerMissionUiMapPlay(); + /* MG3 live mission: HUD ย้ายไป DOM (#mg3-tower-hud) แล้ว — ไม่วาดบน canvas (object-fit:cover ครอปขอบหาย) */ + if (towerMapHud) return; const lifeSlots = towerMapHud ? Math.max(1, Math.min(20, Math.floor(Number(playStackTeamMissesMax) || 3))) : 3; @@ -19469,9 +19690,24 @@ function syncQuizQuestionMissionJoystickPlay() { const root = document.getElementById('quiz-question-mission-joystick'); if (!root) return; - const on = !!(isQuizQuestionMissionHudActivePlay() && !isChatFocused()); + const jumperOn = isJumpSurviveMissionHudActivePlay(); + const on = !!((isQuizQuestionMissionHudActivePlay() || jumperOn) && !isChatFocused()); root.classList.toggle('is-hidden', !on); root.setAttribute('aria-hidden', on ? 'false' : 'true'); + if (on) { + /* สลับรูป joystick ตามฉาก: jumper → /img/Jumper/, quiz คำถาม → /img/QUESTION/ (เปลี่ยน src เฉพาะตอน skin เปลี่ยน) */ + const skin = jumperOn ? 'jumper' : 'question'; + const bg = document.getElementById('quiz-q-mission-joystick-bg'); + const knobImg = document.getElementById('quiz-q-mission-joystick-knob-img'); + if (bg && bg.dataset.joySkin !== skin) { + bg.src = jumperOn ? jumperAssetUrl('btn-joystick-1.png') : questionMissionAssetUrl('btn-joystick-1.png'); + bg.dataset.joySkin = skin; + } + if (knobImg && knobImg.dataset.joySkin !== skin) { + knobImg.src = jumperOn ? jumperAssetUrl('btn-joystick-2.png') : questionMissionAssetUrl('btn-joystick-2.png'); + knobImg.dataset.joySkin = skin; + } + } if (!on) { const base = document.getElementById('quiz-q-mission-joystick-base'); if (base && quizQuestionMissionJoyPointerId != null) { @@ -19524,7 +19760,8 @@ function syncMg2MockJumpButtonBoundsPlay(stackRect, scale) { const jumpBtn = document.getElementById('gauntlet-crown-jump-btn'); const hud = document.getElementById('play-cyber-hud'); - if (!jumpBtn || !hud || hud.dataset.missionMockSkin !== 'gauntlet_crown') { + const _jbSkin = hud ? hud.dataset.missionMockSkin : ''; + if (!jumpBtn || !hud || (_jbSkin !== 'gauntlet_crown' && _jbSkin !== 'jumper')) { if (jumpBtn) { jumpBtn.style.removeProperty('left'); jumpBtn.style.removeProperty('top'); @@ -19850,45 +20087,8 @@ } if (statusEl) { if (isSpaceShooterMissionHudActivePlay()) { - ensureStackTowerLifeHudImagesPlay(); - const nn = String((me.nickname || nick || 'PILOT')).trim().slice(0, 24); - const hits = Math.max(0, Math.min(SPACE_SHOOTER_MISSION_MAX_ASTEROID_HITS, Number(me.spaceShooterHits) || 0)); - const rem = SPACE_SHOOTER_MISSION_MAX_ASTEROID_HITS - hits; - const esc = playCyberHudEsc(nn); - const W = 220; - const H = 58; - const dpr = Math.min(2, Math.max(1, typeof window !== 'undefined' && window.devicePixelRatio ? window.devicePixelRatio : 1)); - let canv = statusEl.querySelector('canvas.play-cyber-ss-integrity-canvas'); - let metaEl = statusEl.querySelector('.play-cyber-ss-meta'); - if (!canv || !metaEl) { - statusEl.textContent = ''; - canv = document.createElement('canvas'); - canv.className = 'play-cyber-ss-integrity-canvas'; - canv.setAttribute('aria-hidden', 'true'); - metaEl = document.createElement('div'); - metaEl.className = 'play-cyber-ss-meta'; - statusEl.appendChild(canv); - statusEl.appendChild(metaEl); - } - canv.width = Math.max(1, Math.floor(W * dpr)); - canv.height = Math.max(1, Math.floor(H * dpr)); - canv.style.width = W + 'px'; - canv.style.height = H + 'px'; - const ctxSs = canv.getContext('2d'); - if (ctxSs) { - ctxSs.setTransform(dpr, 0, 0, dpr, 0, 0); - ctxSs.clearRect(0, 0, W + 2, H + 2); - drawStackTowerLifeIntegrityBarPlay( - ctxSs, - 2, - 2, - W - 4, - H - 4, - SPACE_SHOOTER_MISSION_MAX_ASTEROID_HITS, - rem, - ); - } - metaEl.textContent = 'P1 · ' + esc; /* ตัด 'TEAM 0 · COMBO x0' ปลอม (ค่าไม่เปลี่ยน ไม่ตรง design) */ + /* MG6 life ย้ายไป overlay เฉพาะ #mg6-life-hud (syncSpaceShooterLifeDomHudPlay) — คุมตำแหน่ง/ขนาดเท่า MG3 */ + statusEl.textContent = ''; } else if (isQuizQuestionMissionHudActivePlay()) { statusEl.textContent = 'QUIZ LINK · ประลองความรู้'; } else if (isStackTowerMissionHudActivePlay()) { @@ -21203,6 +21403,8 @@ syncQuizCarryGrabButton(); syncGauntletCrownJumpButton(); syncStackTowerDropButtonPlay(); + syncStackTowerMissionDomHudPlay(); + syncSpaceShooterLifeDomHudPlay(); } document.addEventListener('keydown', (e) => { @@ -21654,7 +21856,10 @@ if (!btn) return; btn.addEventListener('click', (ev) => { ev.preventDefault(); - if (!mapData || !isGauntlet() || isChatFocused()) return; + if (isChatFocused()) return; + /* MG5 jumper: click = queue jump (mirror keyboard Space/W/↑) */ + if (mapData && isJumpSurviveMissionHudActivePlay()) { jumpSurviveJumpQueued = true; return; } + if (!mapData || !isGauntlet()) return; tryRequestGauntletJumpPlay(); }); })(); @@ -21677,6 +21882,7 @@ if (!base) return; if (bg) { bg.src = questionMissionAssetUrl('btn-joystick-1.png'); + bg.dataset.joySkin = 'question'; bg.onerror = function () { this.onerror = null; this.removeAttribute('src'); @@ -21684,6 +21890,7 @@ } if (knobImg) { knobImg.src = questionMissionAssetUrl('btn-joystick-2.png'); + knobImg.dataset.joySkin = 'question'; knobImg.onerror = function () { this.onerror = null; this.removeAttribute('src'); @@ -21698,7 +21905,7 @@ quizQuestionMissionJoystickResetVisual(); } base.addEventListener('pointerdown', (e) => { - if (!isQuizQuestionMissionHudActivePlay() || isChatFocused()) return; + if (!(isQuizQuestionMissionHudActivePlay() || isJumpSurviveMissionHudActivePlay()) || isChatFocused()) return; if (e.button != null && e.button !== 0) return; e.preventDefault(); try { diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index 73b6614..34127a7 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -1424,6 +1424,10 @@ #gauntlet-crown-mission-overlay.is-hidden, #gauntlet-crown-countdown.is-hidden, #stack-tower-result-flash.is-hidden { display: none !important; } + /* นับถอยหลังใช้ .gcc-num{display:block} ทับ — ต้องมี rule ให้ is-hidden ซ่อนเลขข้อความจริง (เดิมไม่มี → "3" รั่วในเกมที่ใช้รูปกราฟิก) */ + #gauntlet-crown-countdown .gcc-num.is-hidden { display: none !important; } + /* สรุปผล: เส้นแบ่ง vrule แสดงเฉพาะ Last Light (mg2) — non-mg2 มี is-hidden แต่ไม่มี rule ซ่อน → ไปกินช่อง grid ขวา ดัน "โบนัสพิเศษ" ตกลงคอลัมน์ซ้าย */ + #gcm-vrule.is-hidden { display: none !important; } .gcc-panel { text-align: center; padding: 0; @@ -2089,6 +2093,36 @@ image-rendering: pixelated; margin-bottom: 0.28rem; } + /* MG6 life (DOM) — life-bar.png เต็มใบ + หัวใจ 3 ดวงในวงเล็บ [ ] เหมือน MG3 */ + .play-cyber-ss-life { + position: relative; + width: 100%; + margin-bottom: 0.28rem; + } + .play-cyber-ss-lifebar { width: 100%; display: block; image-rendering: pixelated; } + .play-cyber-ss-hearts { + position: absolute; + left: 24.3%; top: 50%; height: 46%; + display: flex; align-items: center; gap: 4%; + } + .play-cyber-ss-hearts img { height: 100%; width: auto; image-rendering: pixelated; } + /* MG6 Space Shooter life overlay — ระนาบ design 1920×1080 scale ตาม stage (coords/หัวใจเท่า MG3) */ + .mg6-life-hud { + position: fixed; + width: 1920px; height: 1080px; + transform-origin: 0 0; + pointer-events: none; + z-index: 54; + } + .mg6-life-hud.is-hidden { display: none !important; } + .mg6-life-hud img { image-rendering: pixelated; display: block; } + .mg6-life-hud .mg6-life { position: absolute; left: 1410px; top: 63px; width: 341px; height: 111px; } + .mg6-life-hud .mg6-life-bar { width: 341px; height: 111px; display: block; } + .mg6-life-hud .mg6-life-hearts { + position: absolute; left: 83px; top: 56px; height: 52px; + display: flex; align-items: center; gap: 10px; + } + .mg6-life-hud .mg6-life-hearts img { height: 52px; width: auto; } #play-cyber-hud.play-cyber-hud--question-mission .play-cyber-ss-meta { font-size: 0.5rem; letter-spacing: 0.06em; @@ -2105,8 +2139,9 @@ max-width: min(96vw, 380px); } #play-cyber-hud.play-cyber-hud--ss-self-integrity-left .play-cyber-self-status { - order: 1; - flex: 1 1 auto; + order: 3; + flex: 0 0 auto; + width: auto; min-width: 0; } #play-cyber-hud.play-cyber-hud--ss-self-integrity-left .play-cyber-profile-frame { @@ -2125,6 +2160,65 @@ #play-cyber-hud.play-cyber-hud--stack-tower-canvas-hud .play-cyber-self { display: none !important; } + /* ===== MG3 Stack Tower live HUD — DOM overlay: 1920×1080 design plane scale+center ตาม stage (coords ตรง design game.css/game-hud.css) ===== */ + .mg3-tower-hud { + position: fixed; + width: 1920px; height: 1080px; + transform-origin: 0 0; + pointer-events: none; + z-index: 54; + } + .mg3-tower-hud.is-hidden { display: none !important; } + .mg3-tower-hud > div { position: absolute; } + .mg3-tower-hud img { image-rendering: pixelated; display: block; } + /* SYSTEM INTEGRITY life bar (design .mg3-life left:1410 top:63 w:341 h:111) */ + .mg3-life { left: 1410px; top: 63px; width: 341px; height: 111px; } + .mg3-life-bar { width: 341px; height: 111px; display: block; } + .mg3-life-hearts { + position: absolute; left: 83px; top: 56px; + display: flex; align-items: center; gap: 10px; height: 52px; + } + .mg3-life-hearts img { height: 52px; width: auto; } + /* host portrait frame (design .mg-host left:1729 top:21 w/h:201; face left:49 top:49 104×104) */ + .mg3-host { left: 1729px; top: 21px; width: 201px; height: 201px; } + .mg3-host-frame { position: absolute; inset: 0; width: 100%; height: 100%; } + .mg3-host-face { + position: absolute; left: 49px; top: 49px; width: 104px; height: 104px; + border-radius: 8px; object-fit: cover; + } + /* code terminal (design .mg3-terminal left:40 bottom:170 w:420) */ + .mg3-terminal { left: 40px; bottom: 170px; width: 420px; } + .mg3-terminal-bg { width: 100%; height: auto; } + .mg3-terminal-log { + position: absolute; left: 9%; right: 9%; top: 10%; bottom: 10%; + overflow: hidden; + font: 22px/1.4 ui-monospace, "Cascadia Mono", Consolas, monospace; + color: #8fe9ff; + text-shadow: 0 0 6px rgba(0, 200, 255, 0.5); + white-space: pre-wrap; word-break: break-word; + } + /* progress (design .mg3-progress left:43 bottom:40 w:418 h:151) — label+pct heading แล้ว track+fill */ + .mg3-progress { + left: 43px; bottom: 40px; width: 418px; height: 151px; + display: flex; flex-direction: row; flex-wrap: wrap; gap: 0; + } + .mg3-progress-heading { + display: flex; align-items: center; justify-content: space-between; + width: 100%; flex: 0 0 75%; gap: 3px; + } + .mg3-progress-label { width: auto; height: 58px; display: block; flex: 0 0 auto; } + .mg3-progress-pct { + font-family: "NotoSansThai", monospace, sans-serif; font-weight: 700; + font-size: 28px; line-height: 44px; color: #8fe8ff; letter-spacing: 0.5px; + white-space: nowrap; text-shadow: 0 0 8px rgba(120, 220, 255, 0.6); + } + .mg3-progress-bar { position: relative; width: 100%; flex: 0 0 100%; } + .mg3-progress-track { width: 100%; height: auto; display: block; } + .mg3-progress-fill { + position: absolute; left: 30px; top: 30px; height: 30px; width: 0; + object-fit: cover; object-position: left center; + transition: width 0.25s ease; + } /* Last Light (mno9kb07): แถบคะแนนแนวนอน — score.png แล้วตามด้วยสูงสุด 6 ช่อง (อวาตาร์ | ชื่อบน / คะแนนล่าง) */ #play-cyber-hud.play-cyber-hud--gauntlet-crown-strip .play-cyber-hint { display: none !important; @@ -3352,7 +3446,8 @@ height: 104px; overflow: hidden; border-radius: 8px; - z-index: 1; + /* avatar ต้องอยู่บนกรอบ Avartar.png (กรอบมีพื้นกลางทึบ ทับหน้า avatar = "ถูกบัง") — เหมือน MG3 ที่หน้าอยู่บนสุด */ + z-index: 3; } #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 { @@ -3385,6 +3480,7 @@ #play-cyber-hud.play-cyber-hud--quiz-tf-mock .play-cyber-op-widgets { display: none !important; } + /* Space Shooter (MG6): life ย้ายไป overlay #mg6-life-hud แล้ว — แผง self-status คงซ่อน (ตาม rule quiz-tf-mock) */ /* mic 121px @ (1796,205) — เทียบ mg-mic */ #play-cyber-hud.play-cyber-hud--quiz-tf-mock .play-cyber-mic-img { display: block; @@ -4490,6 +4586,38 @@ + +
+ +