Use transparent health sprite sheets in mobile HUD

This commit is contained in:
Admin 2026-09-18 02:18:50 +00:00
parent 1473bf80df
commit 9ae626d5f8

View File

@ -1,7 +1,7 @@
import Phaser from 'phaser'; import Phaser from 'phaser';
import topHudSrc from './assets/controller/top-hud.js'; import topHudSrc from './assets/controller/top-hud.js';
import leftStatusSrc from './assets/controller/left-status.js'; import playerHealthSrc from './assets/controller/player-health.js';
import rightControlsSrc from './assets/controller/right-controls.js'; import enemyHealthSrc from './assets/controller/enemy-health.js';
import cannonRoundelSrc from './assets/controller/cannon-roundel.js'; import cannonRoundelSrc from './assets/controller/cannon-roundel.js';
import helmFrameSrc from './assets/controller/helm-frame.js'; import helmFrameSrc from './assets/controller/helm-frame.js';
import helmKnobSrc from './assets/controller/helm-knob.js'; import helmKnobSrc from './assets/controller/helm-knob.js';
@ -51,8 +51,9 @@ if (!isDesktopPointer.matches) {
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: min(21.1vw, 95px); height: auto;
overflow: hidden; aspect-ratio: 960 / 246;
overflow: visible;
} }
.steam-top-value { .steam-top-value {
@ -74,15 +75,24 @@ if (!isDesktopPointer.matches) {
.steam-kills { left: 78.2%; } .steam-kills { left: 78.2%; }
.steam-range { left: 91.1%; } .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 { .steam-player-card {
left: -1%; left: -1%;
top: clamp(80px, 10.3vh, 90px);
width: 53%; width: 53%;
} }
.steam-enemy-card { .steam-enemy-card {
right: -1%; right: -1%;
top: clamp(80px, 10.3vh, 90px);
width: 46%; width: 46%;
} }
@ -92,68 +102,36 @@ if (!isDesktopPointer.matches) {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
pointer-events: none;
} }
.steam-card-name { .steam-card-name {
top: 28%;
color: #f6d58e; color: #f6d58e;
font-size: clamp(8px, 2.2vw, 11px); font-size: clamp(8px, 2.2vw, 11px);
letter-spacing: .06em; letter-spacing: .06em;
} }
.steam-player-card .steam-card-name { .steam-player-card .steam-card-name {
left: 58%; left: 61%;
top: 31%;
} }
.steam-enemy-card .steam-card-name { .steam-enemy-card .steam-card-name {
left: 51%; left: 39%;
top: 12%;
} }
.steam-card-value { .steam-card-value {
top: 67%;
color: #fff0bd; color: #fff0bd;
font-size: clamp(8px, 2.4vw, 12px); font-size: clamp(8px, 2.4vw, 12px);
} }
.steam-player-card .steam-card-value { .steam-player-card .steam-card-value {
left: 61%; left: 61%;
top: 67%;
} }
.steam-enemy-card .steam-card-value { .steam-enemy-card .steam-card-value {
left: 38%; left: 39%;
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;
} }
.steam-radar-wrap { .steam-radar-wrap {
@ -414,7 +392,7 @@ if (!isDesktopPointer.matches) {
left: 50%; left: 50%;
width: min(64vw, 560px); width: min(64vw, 560px);
height: auto; height: auto;
aspect-ratio: 4.74 / 1; aspect-ratio: 960 / 246;
transform: translateX(-50%); transform: translateX(-50%);
} }
@ -537,15 +515,28 @@ if (!isDesktopPointer.matches) {
<span class="steam-top-value steam-range" data-steam="range">--</span> <span class="steam-top-value steam-range" data-steam="range">--</span>
`); `);
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, `
<span class="steam-card-name">SPARROW</span> <span class="steam-card-name">SPARROW</span>
<div class="steam-bar"><div class="steam-bar-fill" data-steam="player-fill"></div></div>
<span class="steam-card-value" data-steam="player-hull">100/100</span> <span class="steam-card-value" data-steam="player-hull">100/100</span>
`); `);
addPiece('steam-enemy-card', rightControlsSrc, ` const enemyHealthPiece = addHealthPiece('steam-enemy-card', enemyHealthSrc, `
<span class="steam-card-name">PIRATE</span> <span class="steam-card-name">PIRATE</span>
<div class="steam-bar"><div class="steam-bar-fill" data-steam="enemy-fill"></div></div>
<span class="steam-card-value" data-steam="enemy-hull">CLEAR</span> <span class="steam-card-value" data-steam="enemy-hull">CLEAR</span>
`); `);
@ -644,6 +635,17 @@ if (!isDesktopPointer.matches) {
const clamp01 = (value) => Math.max(0, Math.min(1, value)); 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 updateRadar = (scene) => {
const pirates = scene.pirates?.getChildren?.().filter((pirate) => pirate?.active) || []; 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.range.textContent = Number.isFinite(range) ? `${Math.round(range)}m` : '--';
fields['player-hull'].textContent = `${playerHull}/100`; fields['player-hull'].textContent = `${playerHull}/100`;
fields['enemy-hull'].textContent = pirate ? `${enemyHull}/120` : 'CLEAR'; fields['enemy-hull'].textContent = pirate ? `${enemyHull}/120` : 'CLEAR';
fields['player-fill'].style.transform = `scaleX(${clamp01(playerHull / 100)})`; setHealthFrame(playerHealthPiece, healthFrame(playerHull, 100));
fields['enemy-fill'].style.transform = `scaleX(${pirate ? clamp01(enemyHull / 120) : 0})`; setHealthFrame(enemyHealthPiece, pirate ? healthFrame(enemyHull, 120) : 11);
const statusText = scene.status?.text?.trim?.() || ''; const statusText = scene.status?.text?.trim?.() || '';
fields.status.textContent = statusText; fields.status.textContent = statusText;