0.9.5
feat: boss-room loading overlay, drop HUD pressure multiplier, elite Brood Sac 4-6 minis
This commit is contained in:
1 parent
2cf8dc89fe
commit
8991bde943
13 files changed
+101
-34
No files matched your search
+1
-1
@@ -551,7 +551,7 @@ function updateEnemyAI(e, dt) {
|
||||
e.summonT -= dt;
|
||||
if (e.summonT <= 0) {
|
||||
e.summonT = e.elite ? 3 : 4;
|
||||
spawnBroodMinis(e, 2, 0.35);
|
||||
spawnBroodMinis(e, e.elite ? irand(4, 6) : 2, 0.35);
|
||||
e.attackT = 0.5; e.actionT = 0;
|
||||
}
|
||||
} else if (t.ranged) {
|
||||
|
||||
@@ -167,8 +167,6 @@ function drawHUD(ctx) {
|
||||
ctx.fillText('KILLS ' + String(G.kills).padStart(3, '0'), W - 18, H - 67);
|
||||
ctx.fillStyle = '#f0e5d7'; ctx.font = 'bold 11px monospace';
|
||||
ctx.fillText('SCORE ' + String(G.score).padStart(6, '0'), W - 18, H - 46);
|
||||
ctx.fillStyle = '#8f7770'; ctx.font = '8px monospace';
|
||||
ctx.fillText('×' + (G.pressure || 1).toFixed(2), W - 168, H - 46);
|
||||
ctx.textAlign = 'left';
|
||||
const dialTxt = ' D' + (G.pressureDial > 0 ? '+' : '') + G.pressureDial;
|
||||
ctx.fillStyle = G.pressure > 1 ? '#d6aa39' : '#69b8bd'; ctx.font = 'bold 8px monospace';
|
||||
|
||||
+24
@@ -246,6 +246,11 @@ function update(dt) {
|
||||
updateParticles(dt);
|
||||
drawToastsUpdate(dt);
|
||||
break;
|
||||
case 'bossload':
|
||||
updateParticles(dt);
|
||||
if (G.bossLoadT > 0) G.bossLoadT -= dt;
|
||||
if (G.bossLoadT <= 0 && Sprites.warm(G.bossLoadSheet)) G.mode = 'play';
|
||||
break;
|
||||
case 'levelup':
|
||||
updateLevelup(dt);
|
||||
updateParticles(dt);
|
||||
@@ -328,6 +333,7 @@ function draw() {
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
|
||||
if (G.mode === 'menu') { drawMenu(ctx); return; }
|
||||
if (G.mode === 'bossload') { drawBossLoad(ctx); return; }
|
||||
|
||||
updateCamera();
|
||||
|
||||
@@ -378,6 +384,24 @@ function draw() {
|
||||
if (G.devMode) { if (G.debugPin && G.mode !== 'debug') drawDebugPin(ctx); }
|
||||
}
|
||||
|
||||
function drawBossLoad(ctx) {
|
||||
ctx.fillStyle = '#0a0506';
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
ctx.textAlign = 'center';
|
||||
const name = G.boss && G.boss.name ? G.boss.name : 'FRESH MEAT';
|
||||
ctx.fillStyle = '#d9c9bc'; ctx.font = 'bold 28px monospace';
|
||||
ctx.fillText(name, W / 2, H / 2 - 6);
|
||||
ctx.fillStyle = '#8f1f2e'; ctx.font = 'bold 11px monospace';
|
||||
ctx.fillText('FRESH MEAT APPROACHES', W / 2, H / 2 + 16);
|
||||
const pr = Math.min(1, Math.max(0, G.bossLoadT / BOSS_LOAD_MIN));
|
||||
const tier = Math.floor((1 - pr) * 5) % 3;
|
||||
ctx.fillStyle = '#3a1a1e';
|
||||
for (let i = 0; i < 3; i++) ctx.fillRect(W / 2 - 22 + i * 14, H / 2 + 30, 8, 8);
|
||||
ctx.fillStyle = '#d7a934';
|
||||
ctx.fillRect(W / 2 - 22 + tier * 14, H / 2 + 30, 8, 8);
|
||||
ctx.textAlign = 'left';
|
||||
}
|
||||
|
||||
function drawMenu(ctx) {
|
||||
const t = performance.now() / 1000;
|
||||
const bg = ctx.createRadialGradient && ctx.createRadialGradient(W / 2, H * 0.4, 30, W / 2, H * 0.45, 590);
|
||||
|
||||
@@ -187,6 +187,10 @@ function enterRoom(gx, gy) {
|
||||
// boss fights are a pure ammo drain, so stock the room on the way in
|
||||
spawnPickup('ammo', a.cx, a.cy + 140);
|
||||
Music.playBoss(boss.bossKind);
|
||||
G.mode = 'bossload';
|
||||
G.bossLoadSheet = boss.sprite + '_sheet';
|
||||
G.bossLoadT = BOSS_LOAD_MIN;
|
||||
Sprites.warm(G.bossLoadSheet);
|
||||
} else if (room.type === 'item') {
|
||||
spawnItemPedestal(a.cx - 45, a.cy, null, 'room');
|
||||
// item rooms sometimes also stock a weapon
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ const SfxBank = {
|
||||
if (this.buffers.has(name) || this.pending.has(name) || this.failed.has(name)) return;
|
||||
const file = this.FILES[name];
|
||||
if (!file || !this.ctx || typeof fetch !== 'function') return;
|
||||
const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=59')
|
||||
const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=60')
|
||||
.then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.arrayBuffer(); })
|
||||
.then(bytes => this.ctx.decodeAudioData(bytes))
|
||||
.then(buffer => this.buffers.set(name, this.trim(buffer)))
|
||||
|
||||
+19
-2
@@ -88,15 +88,32 @@ const Sprites = {
|
||||
const finish = () => { if (--pending === 0) G.imagesLoaded = true; };
|
||||
img.onload = () => { this.imgs[name] = img; if (typeof img.decode === 'function') img.decode().then(finish, finish); else finish(); };
|
||||
img.onerror = () => {
|
||||
if (hd && !img._sdRetry) { img._sdRetry = true; img.src = 'assets/' + name + '.png?v=59'; return; }
|
||||
if (hd && !img._sdRetry) { img._sdRetry = true; img.src = 'assets/' + name + '.png?v=60'; return; }
|
||||
this.imgs[name] = null;
|
||||
finish();
|
||||
};
|
||||
img.src = hd ? 'assets/hd/' + name + '.webp?v=59' : 'assets/' + name + '.png?v=59';
|
||||
img.src = hd ? 'assets/hd/' + name + '.webp?v=60' : 'assets/' + name + '.png?v=60';
|
||||
}
|
||||
},
|
||||
|
||||
get(name) { return this.imgs[name] || null; },
|
||||
|
||||
forceUpload(img) {
|
||||
if (typeof document === 'undefined' || !document.createElement) return;
|
||||
if (!document.createElement('canvas').getContext) return;
|
||||
if (!this._warmCtx) {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 1; c.height = 1;
|
||||
this._warmCtx = c.getContext('2d');
|
||||
}
|
||||
if (this._warmCtx) this._warmCtx.drawImage(img, 0, 0, 1, 1);
|
||||
},
|
||||
|
||||
warm(name) {
|
||||
if (this.imgs[name]) { this.forceUpload(this.imgs[name]); return true; }
|
||||
return this.imgs[name] !== undefined;
|
||||
},
|
||||
|
||||
// Compact player-only strip: eight forward-facing stride frames.
|
||||
// Runtime rotation is smooth; distance-driven phase keeps contacts from skating.
|
||||
legs(ctx, x, y, facing, phase, targetW) {
|
||||
|
||||
@@ -51,6 +51,8 @@ const G = {
|
||||
hudAlpha: 1,
|
||||
hdRemaster: false,
|
||||
imagesLoaded: false,
|
||||
bossLoadT: 0,
|
||||
bossLoadSheet: null,
|
||||
roomLayer: null,
|
||||
roomLayerKey: '',
|
||||
atmosphereLayer: null,
|
||||
@@ -72,6 +74,7 @@ const PRESSURE_GAIN = PRESSURE_UNIT; // retained: dial 0 == 1 point
|
||||
const PRESSURE_DROP_BASE = 3; // dial 0 == 0.03 relief base
|
||||
const PRESSURE_RELIEF_MUL = 0.5; // global brake: all pressure drops (hit relief + passive decay) are halved
|
||||
const DEATH_LOCK = 3;
|
||||
const BOSS_LOAD_MIN = 1.0;
|
||||
const PRESSURE_DIAL_MIN = -10, PRESSURE_DIAL_MAX = 10;
|
||||
|
||||
function hdScale() { return G.hdRemaster ? 4 : 1; }
|
||||
|
||||
Reference in new issue
Block a user