diff --git a/src/mobile-hud.js b/src/mobile-hud.js index f7804e2..20e0aac 100644 --- a/src/mobile-hud.js +++ b/src/mobile-hud.js @@ -1,5 +1,7 @@ import topHudSrc from './assets/controller/top-hud.js'; -import playerHealthSrc from './assets/controller/player-health.js'; +import playerHealthFullSrc from './assets/controller/player-health-frame-v2-00.js'; +import playerHealthMidSrc from './assets/controller/player-health-frame-v2-02.js'; +import playerHealthLowSrc from './assets/controller/player-health-frame-v2-03.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'; @@ -78,16 +80,17 @@ if (!isDesktopPointer.matches) { .steam-enemy-card { top: clamp(80px, 10.3vh, 90px); aspect-ratio: 360 / 130; + width: min(31vw, 120px); background-repeat: no-repeat; - background-position: 0 0; + background-position: center; background-color: transparent; overflow: visible; + image-rendering: auto; } .steam-player-card { - left: -1%; - width: 53%; - background-size: 400% 300%; + left: 1%; + background-size: 100% 100%; } .steam-player-card.steam-hit { @@ -102,8 +105,7 @@ if (!isDesktopPointer.matches) { } .steam-enemy-card { - right: -1%; - width: 46%; + right: 1%; background-size: 400% 300%; } @@ -441,12 +443,12 @@ if (!isDesktopPointer.matches) { .steam-player-card { left: max(12px, env(safe-area-inset-left)); - width: min(34vw, 360px); + width: min(18vw, 180px); } .steam-enemy-card { right: max(12px, env(safe-area-inset-right)); - width: min(30vw, 330px); + width: min(18vw, 180px); } .steam-card-name { @@ -564,7 +566,7 @@ if (!isDesktopPointer.matches) { return piece; }; - const playerHealthPiece = addHealthPiece('steam-player-card', playerHealthSrc, ` + const playerHealthPiece = addHealthPiece('steam-player-card', playerHealthFullSrc, ` SPARROW 100/100 `); @@ -657,6 +659,17 @@ if (!isDesktopPointer.matches) { piece.style.backgroundPosition = `${(col / 3) * 100}% ${(row / 2) * 100}%`; }; + const setPlayerHealthImage = (current, max) => { + const pct = max > 0 ? clamp01(current / max) : 0; + const src = pct > 0.66 + ? playerHealthFullSrc + : pct > 0.33 + ? playerHealthMidSrc + : playerHealthLowSrc; + playerHealthPiece.style.backgroundImage = `url("${src}")`; + playerHealthPiece.style.backgroundPosition = 'center'; + }; + const updateRadar = (contacts = []) => { radarDots.forEach((dot, index) => { const contact = contacts[index]; @@ -706,7 +719,7 @@ if (!isDesktopPointer.matches) { fields['enemy-name'].textContent = target?.name || 'PIRATE'; fields['enemy-hull'].textContent = target ? `${Math.max(0, target.hull || 0)}/${Math.max(1, target.maxHull || 120)}` : 'CLEAR'; - setHealthFrame(playerHealthPiece, healthFrame(playerHull, playerMaxHull)); + setPlayerHealthImage(playerHull, playerMaxHull); setHealthFrame( enemyHealthPiece, target ? healthFrame(target.hull || 0, target.maxHull || 120) : 11, diff --git a/tests/wiring.test.mjs b/tests/wiring.test.mjs index 9088727..f1f0b9b 100644 --- a/tests/wiring.test.mjs +++ b/tests/wiring.test.mjs @@ -136,8 +136,9 @@ test('mobile HUD uses uploaded player and enemy health sprite sheets', async () assert.match(source, /addHealthPiece\('steam-enemy-card', enemyHealthSrc/); assert.match(source, /const healthFrame = \(current, max\) =>/); assert.match(source, /const setHealthFrame = \(piece, frame\) =>/); - assert.match(source, /\.steam-player-card \{[\s\S]*?background-size: 400% 300%/); - assert.match(source, /setHealthFrame\(playerHealthPiece, healthFrame\(playerHull, playerMaxHull\)\)/); + assert.match(source, /\.steam-player-card \{[\s\S]*?background-size: 100% 100%/); + assert.match(source, /width: min\(31vw, 120px\)/); + assert.match(source, /setPlayerHealthImage\(playerHull, playerMaxHull\)/); assert.match(source, /setHealthFrame\([\s\S]*?enemyHealthPiece,[\s\S]*?target \? healthFrame\(target\.hull \|\| 0, target\.maxHull \|\| 120\) : 11/); assert.doesNotMatch(source, /player-health-cover/); assert.doesNotMatch(source, /player-health-tint/); @@ -248,3 +249,14 @@ test('player overlap resolves an explicitly tagged pirate projectile before queu assert.match(source, /if \(!shot\?\.active \|\| shot === this\.player\)/); assert.match(source, /shot !== this\.player[\s\S]*?projectileOwner'\) === 'pirate'[\s\S]*?shot\.destroy\(\)/); }); + + +test('player health uses discrete full-resolution frame images instead of sprite-sheet magnification', async () => { + const source = await read('src/mobile-hud.js'); + + assert.match(source, /player-health-frame-v2-00\.js/); + assert.match(source, /player-health-frame-v2-02\.js/); + assert.match(source, /player-health-frame-v2-03\.js/); + assert.match(source, /const setPlayerHealthImage = \(current, max\)/); + assert.doesNotMatch(source, /setHealthFrame\(playerHealthPiece/); +});