diff --git a/src/mobile-hud.js b/src/mobile-hud.js index 9b2ee34..a5912eb 100644 --- a/src/mobile-hud.js +++ b/src/mobile-hud.js @@ -1,7 +1,7 @@ import Phaser from 'phaser'; import topHudSrc from './assets/controller/top-hud.js'; -import leftStatusSrc from './assets/controller/left-status.js'; -import rightControlsSrc from './assets/controller/right-controls.js'; +import playerHealthSrc from './assets/controller/player-health.js'; +import enemyHealthSrc from './assets/controller/enemy-health.js'; import cannonRoundelSrc from './assets/controller/cannon-roundel.js'; import helmFrameSrc from './assets/controller/helm-frame.js'; import helmKnobSrc from './assets/controller/helm-knob.js'; @@ -51,8 +51,9 @@ if (!isDesktopPointer.matches) { left: 0; top: 0; width: 100%; - height: min(21.1vw, 95px); - overflow: hidden; + height: auto; + aspect-ratio: 960 / 246; + overflow: visible; } .steam-top-value { @@ -74,15 +75,24 @@ if (!isDesktopPointer.matches) { .steam-kills { left: 78.2%; } .steam-range { left: 91.1%; } + .steam-player-card, + .steam-enemy-card { + top: clamp(80px, 10.3vh, 90px); + aspect-ratio: 300 / 108; + background-repeat: no-repeat; + background-size: 400% 300%; + background-position: 0 0; + background-color: transparent; + overflow: visible; + } + .steam-player-card { left: -1%; - top: clamp(80px, 10.3vh, 90px); width: 53%; } .steam-enemy-card { right: -1%; - top: clamp(80px, 10.3vh, 90px); width: 46%; } @@ -92,68 +102,36 @@ if (!isDesktopPointer.matches) { transform: translate(-50%, -50%); white-space: nowrap; text-align: center; + pointer-events: none; } .steam-card-name { + top: 28%; color: #f6d58e; font-size: clamp(8px, 2.2vw, 11px); letter-spacing: .06em; } .steam-player-card .steam-card-name { - left: 58%; - top: 31%; + left: 61%; } .steam-enemy-card .steam-card-name { - left: 51%; - top: 12%; + left: 39%; } .steam-card-value { + top: 67%; color: #fff0bd; font-size: clamp(8px, 2.4vw, 12px); } .steam-player-card .steam-card-value { left: 61%; - top: 67%; } .steam-enemy-card .steam-card-value { - left: 38%; - top: 32%; - } - - .steam-bar { - position: absolute; - overflow: hidden; - border-radius: 999px; - pointer-events: none; - } - - .steam-player-card .steam-bar { - left: 32.3%; - top: 56.2%; - width: 57%; - height: 20%; - } - - .steam-enemy-card .steam-bar { - left: 7.5%; - top: 24.8%; - width: 60.5%; - height: 16%; - } - - .steam-bar-fill { - width: 100%; - height: 100%; - transform-origin: left center; - border-radius: inherit; - background: linear-gradient(180deg, #ffb445 0%, #d43e2f 42%, #821b1c 100%); - box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 0 5px rgba(219,63,40,.45); - opacity: .9; + left: 39%; } .steam-radar-wrap { @@ -414,7 +392,7 @@ if (!isDesktopPointer.matches) { left: 50%; width: min(64vw, 560px); height: auto; - aspect-ratio: 4.74 / 1; + aspect-ratio: 960 / 246; transform: translateX(-50%); } @@ -537,15 +515,28 @@ if (!isDesktopPointer.matches) { -- `); - addPiece('steam-player-card', leftStatusSrc, ` + const addHealthPiece = (className, src, inner = '') => { + const piece = document.createElement('div'); + piece.className = `steam-piece ${className}`; + piece.style.backgroundImage = `url("${src}")`; + + const probe = new Image(); + probe.addEventListener('load', () => airshipLog('hud:health-sheet-loaded', { component: className })); + probe.addEventListener('error', () => airshipLog('hud:health-sheet-error', { component: className }, 'error')); + probe.src = src; + + if (inner) piece.insertAdjacentHTML('beforeend', inner); + hud.appendChild(piece); + return piece; + }; + + const playerHealthPiece = addHealthPiece('steam-player-card', playerHealthSrc, ` SPARROW -
100/100 `); - addPiece('steam-enemy-card', rightControlsSrc, ` + const enemyHealthPiece = addHealthPiece('steam-enemy-card', enemyHealthSrc, ` PIRATE -
CLEAR `); @@ -644,6 +635,17 @@ if (!isDesktopPointer.matches) { const clamp01 = (value) => Math.max(0, Math.min(1, value)); + const healthFrame = (current, max) => { + const pct = max > 0 ? clamp01(current / max) : 0; + return Math.max(0, Math.min(11, Math.round((1 - pct) * 11))); + }; + + const setHealthFrame = (piece, frame) => { + const col = frame % 4; + const row = Math.floor(frame / 4); + piece.style.backgroundPosition = `${(col / 3) * 100}% ${(row / 2) * 100}%`; + }; + const updateRadar = (scene) => { const pirates = scene.pirates?.getChildren?.().filter((pirate) => pirate?.active) || []; @@ -690,8 +692,8 @@ if (!isDesktopPointer.matches) { fields.range.textContent = Number.isFinite(range) ? `${Math.round(range)}m` : '--'; fields['player-hull'].textContent = `${playerHull}/100`; fields['enemy-hull'].textContent = pirate ? `${enemyHull}/120` : 'CLEAR'; - fields['player-fill'].style.transform = `scaleX(${clamp01(playerHull / 100)})`; - fields['enemy-fill'].style.transform = `scaleX(${pirate ? clamp01(enemyHull / 120) : 0})`; + setHealthFrame(playerHealthPiece, healthFrame(playerHull, 100)); + setHealthFrame(enemyHealthPiece, pirate ? healthFrame(enemyHull, 120) : 11); const statusText = scene.status?.text?.trim?.() || ''; fields.status.textContent = statusText;