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 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) {
<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>
<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>
`);
addPiece('steam-enemy-card', rightControlsSrc, `
const enemyHealthPiece = addHealthPiece('steam-enemy-card', enemyHealthSrc, `
<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>
`);
@ -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;