Render player health from crisp individual frames
This commit is contained in:
parent
3612b502bb
commit
7395b5b125
@ -1,5 +1,7 @@
|
|||||||
import topHudSrc from './assets/controller/top-hud.js';
|
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 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';
|
||||||
@ -78,16 +80,17 @@ if (!isDesktopPointer.matches) {
|
|||||||
.steam-enemy-card {
|
.steam-enemy-card {
|
||||||
top: clamp(80px, 10.3vh, 90px);
|
top: clamp(80px, 10.3vh, 90px);
|
||||||
aspect-ratio: 360 / 130;
|
aspect-ratio: 360 / 130;
|
||||||
|
width: min(31vw, 120px);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 0 0;
|
background-position: center;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
image-rendering: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.steam-player-card {
|
.steam-player-card {
|
||||||
left: -1%;
|
left: 1%;
|
||||||
width: 53%;
|
background-size: 100% 100%;
|
||||||
background-size: 400% 300%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.steam-player-card.steam-hit {
|
.steam-player-card.steam-hit {
|
||||||
@ -102,8 +105,7 @@ if (!isDesktopPointer.matches) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.steam-enemy-card {
|
.steam-enemy-card {
|
||||||
right: -1%;
|
right: 1%;
|
||||||
width: 46%;
|
|
||||||
background-size: 400% 300%;
|
background-size: 400% 300%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,12 +443,12 @@ if (!isDesktopPointer.matches) {
|
|||||||
|
|
||||||
.steam-player-card {
|
.steam-player-card {
|
||||||
left: max(12px, env(safe-area-inset-left));
|
left: max(12px, env(safe-area-inset-left));
|
||||||
width: min(34vw, 360px);
|
width: min(18vw, 180px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.steam-enemy-card {
|
.steam-enemy-card {
|
||||||
right: max(12px, env(safe-area-inset-right));
|
right: max(12px, env(safe-area-inset-right));
|
||||||
width: min(30vw, 330px);
|
width: min(18vw, 180px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.steam-card-name {
|
.steam-card-name {
|
||||||
@ -564,7 +566,7 @@ if (!isDesktopPointer.matches) {
|
|||||||
return piece;
|
return piece;
|
||||||
};
|
};
|
||||||
|
|
||||||
const playerHealthPiece = addHealthPiece('steam-player-card', playerHealthSrc, `
|
const playerHealthPiece = addHealthPiece('steam-player-card', playerHealthFullSrc, `
|
||||||
<span class="steam-card-name">SPARROW</span>
|
<span class="steam-card-name">SPARROW</span>
|
||||||
<span class="steam-card-value" data-steam="player-hull">100/100</span>
|
<span class="steam-card-value" data-steam="player-hull">100/100</span>
|
||||||
`);
|
`);
|
||||||
@ -657,6 +659,17 @@ if (!isDesktopPointer.matches) {
|
|||||||
piece.style.backgroundPosition = `${(col / 3) * 100}% ${(row / 2) * 100}%`;
|
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 = []) => {
|
const updateRadar = (contacts = []) => {
|
||||||
radarDots.forEach((dot, index) => {
|
radarDots.forEach((dot, index) => {
|
||||||
const contact = contacts[index];
|
const contact = contacts[index];
|
||||||
@ -706,7 +719,7 @@ if (!isDesktopPointer.matches) {
|
|||||||
fields['enemy-name'].textContent = target?.name || 'PIRATE';
|
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';
|
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(
|
setHealthFrame(
|
||||||
enemyHealthPiece,
|
enemyHealthPiece,
|
||||||
target ? healthFrame(target.hull || 0, target.maxHull || 120) : 11,
|
target ? healthFrame(target.hull || 0, target.maxHull || 120) : 11,
|
||||||
|
|||||||
@ -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, /addHealthPiece\('steam-enemy-card', enemyHealthSrc/);
|
||||||
assert.match(source, /const healthFrame = \(current, max\) =>/);
|
assert.match(source, /const healthFrame = \(current, max\) =>/);
|
||||||
assert.match(source, /const setHealthFrame = \(piece, frame\) =>/);
|
assert.match(source, /const setHealthFrame = \(piece, frame\) =>/);
|
||||||
assert.match(source, /\.steam-player-card \{[\s\S]*?background-size: 400% 300%/);
|
assert.match(source, /\.steam-player-card \{[\s\S]*?background-size: 100% 100%/);
|
||||||
assert.match(source, /setHealthFrame\(playerHealthPiece, healthFrame\(playerHull, playerMaxHull\)\)/);
|
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.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-cover/);
|
||||||
assert.doesNotMatch(source, /player-health-tint/);
|
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, /if \(!shot\?\.active \|\| shot === this\.player\)/);
|
||||||
assert.match(source, /shot !== this\.player[\s\S]*?projectileOwner'\) === 'pirate'[\s\S]*?shot\.destroy\(\)/);
|
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/);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user