diff --git a/assets/sfx/plr_revive.mp3 b/assets/sfx/plr_revive.mp3 new file mode 100644 index 0000000..4171965 Binary files /dev/null and b/assets/sfx/plr_revive.mp3 differ diff --git a/assets/sfx/plr_step1.mp3 b/assets/sfx/plr_step1.mp3 deleted file mode 100644 index 07b52b0..0000000 Binary files a/assets/sfx/plr_step1.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step2.mp3 b/assets/sfx/plr_step2.mp3 deleted file mode 100644 index ce3bc0f..0000000 Binary files a/assets/sfx/plr_step2.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step3.mp3 b/assets/sfx/plr_step3.mp3 deleted file mode 100644 index 3b9e761..0000000 Binary files a/assets/sfx/plr_step3.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step4.mp3 b/assets/sfx/plr_step4.mp3 deleted file mode 100644 index 79aeb62..0000000 Binary files a/assets/sfx/plr_step4.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step_wet1.mp3 b/assets/sfx/plr_step_wet1.mp3 deleted file mode 100644 index de200f7..0000000 Binary files a/assets/sfx/plr_step_wet1.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step_wet2.mp3 b/assets/sfx/plr_step_wet2.mp3 deleted file mode 100644 index 3ba9a76..0000000 Binary files a/assets/sfx/plr_step_wet2.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step_wet3.mp3 b/assets/sfx/plr_step_wet3.mp3 deleted file mode 100644 index 0dcd47f..0000000 Binary files a/assets/sfx/plr_step_wet3.mp3 and /dev/null differ diff --git a/assets/sfx/plr_step_wet4.mp3 b/assets/sfx/plr_step_wet4.mp3 deleted file mode 100644 index 4fabd9b..0000000 Binary files a/assets/sfx/plr_step_wet4.mp3 and /dev/null differ diff --git a/assets/sfx/ui_active.mp3 b/assets/sfx/ui_active.mp3 new file mode 100644 index 0000000..b4d1da4 Binary files /dev/null and b/assets/sfx/ui_active.mp3 differ diff --git a/assets/sfx/ui_active_empty.mp3 b/assets/sfx/ui_active_empty.mp3 new file mode 100644 index 0000000..c787b5d Binary files /dev/null and b/assets/sfx/ui_active_empty.mp3 differ diff --git a/assets/sfx/ui_curse.mp3 b/assets/sfx/ui_curse.mp3 new file mode 100644 index 0000000..7e53d4a Binary files /dev/null and b/assets/sfx/ui_curse.mp3 differ diff --git a/assets/sfx/wpn_bonepopper.mp3 b/assets/sfx/wpn_bonepopper.mp3 index c7e50e5..56e2411 100644 Binary files a/assets/sfx/wpn_bonepopper.mp3 and b/assets/sfx/wpn_bonepopper.mp3 differ diff --git a/assets/sfx/wpn_cleaver.mp3 b/assets/sfx/wpn_cleaver.mp3 index 0af4eac..39529b2 100644 Binary files a/assets/sfx/wpn_cleaver.mp3 and b/assets/sfx/wpn_cleaver.mp3 differ diff --git a/assets/sfx/wpn_eye.mp3 b/assets/sfx/wpn_eye.mp3 index 467491c..9ed49ac 100644 Binary files a/assets/sfx/wpn_eye.mp3 and b/assets/sfx/wpn_eye.mp3 differ diff --git a/assets/sfx/wpn_fleshmasher.mp3 b/assets/sfx/wpn_fleshmasher.mp3 index 9bc2af6..23f55c8 100644 Binary files a/assets/sfx/wpn_fleshmasher.mp3 and b/assets/sfx/wpn_fleshmasher.mp3 differ diff --git a/assets/sfx/wpn_guthook.mp3 b/assets/sfx/wpn_guthook.mp3 index 1ce9c36..7c3c95d 100644 Binary files a/assets/sfx/wpn_guthook.mp3 and b/assets/sfx/wpn_guthook.mp3 differ diff --git a/assets/sfx/wpn_hemophage.mp3 b/assets/sfx/wpn_hemophage.mp3 index 5f6ca01..e2a2fc1 100644 Binary files a/assets/sfx/wpn_hemophage.mp3 and b/assets/sfx/wpn_hemophage.mp3 differ diff --git a/assets/sfx/wpn_marrow.mp3 b/assets/sfx/wpn_marrow.mp3 index 6d5a0f5..6db3536 100644 Binary files a/assets/sfx/wpn_marrow.mp3 and b/assets/sfx/wpn_marrow.mp3 differ diff --git a/assets/sfx/wpn_repeater.mp3 b/assets/sfx/wpn_repeater.mp3 index f2b6095..4b2608f 100644 Binary files a/assets/sfx/wpn_repeater.mp3 and b/assets/sfx/wpn_repeater.mp3 differ diff --git a/assets/sfx/wpn_saw.mp3 b/assets/sfx/wpn_saw.mp3 index 937aae5..59b7206 100644 Binary files a/assets/sfx/wpn_saw.mp3 and b/assets/sfx/wpn_saw.mp3 differ diff --git a/assets/sfx/wpn_spinaltap_fire.mp3 b/assets/sfx/wpn_spinaltap_fire.mp3 index b82d82a..8feaf04 100644 Binary files a/assets/sfx/wpn_spinaltap_fire.mp3 and b/assets/sfx/wpn_spinaltap_fire.mp3 differ diff --git a/assets/sfx/wpn_swarmjar.mp3 b/assets/sfx/wpn_swarmjar.mp3 index bdc784a..de702ef 100644 Binary files a/assets/sfx/wpn_swarmjar.mp3 and b/assets/sfx/wpn_swarmjar.mp3 differ diff --git a/assets/sfx/wpn_tenderizer.mp3 b/assets/sfx/wpn_tenderizer.mp3 index 1192b64..b76dcec 100644 Binary files a/assets/sfx/wpn_tenderizer.mp3 and b/assets/sfx/wpn_tenderizer.mp3 differ diff --git a/assets/sfx/wpn_trapqueen.mp3 b/assets/sfx/wpn_trapqueen.mp3 index fd24ec5..18b7d86 100644 Binary files a/assets/sfx/wpn_trapqueen.mp3 and b/assets/sfx/wpn_trapqueen.mp3 differ diff --git a/assets/sfx/wpnloop_bile.mp3 b/assets/sfx/wpnloop_bile.mp3 index 78ece91..32f6173 100644 Binary files a/assets/sfx/wpnloop_bile.mp3 and b/assets/sfx/wpnloop_bile.mp3 differ diff --git a/assets/sfx/wpnloop_cauterizer.mp3 b/assets/sfx/wpnloop_cauterizer.mp3 index 90e5775..3122094 100644 Binary files a/assets/sfx/wpnloop_cauterizer.mp3 and b/assets/sfx/wpnloop_cauterizer.mp3 differ diff --git a/assets/sfx/wpnloop_redhand.mp3 b/assets/sfx/wpnloop_redhand.mp3 index 66e88e6..a7a75fc 100644 Binary files a/assets/sfx/wpnloop_redhand.mp3 and b/assets/sfx/wpnloop_redhand.mp3 differ diff --git a/assets/sfx/wpnloop_spinaltap_charge.mp3 b/assets/sfx/wpnloop_spinaltap_charge.mp3 index acd1260..678b8d5 100644 Binary files a/assets/sfx/wpnloop_spinaltap_charge.mp3 and b/assets/sfx/wpnloop_spinaltap_charge.mp3 differ diff --git a/index.html b/index.html index cee3268..0a2f7cb 100644 --- a/index.html +++ b/index.html @@ -6,31 +6,31 @@ MEATSLICER - +
- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/js/items.js b/js/items.js index 301f73e..3fd9aa9 100644 --- a/js/items.js +++ b/js/items.js @@ -176,9 +176,9 @@ function giveItem(iid) { spawnText(p.x, p.y - 14, 'UPGRADED!', '#ffd060'); } else { addToast(item.name, item.desc); - spawnText(p.x, p.y - 14, item.name.toUpperCase(), '#c9a227'); + spawnText(p.x, p.y - 14, item.name.toUpperCase(), ITEM_RARITY[item.rarity].color); } - Sfx.item(); + if (item.rarity === 'legendary') Sfx.curse(); else Sfx.item(); refreshOrbitals(p); } diff --git a/js/player.js b/js/player.js index b33f122..99be8d9 100644 --- a/js/player.js +++ b/js/player.js @@ -5,7 +5,7 @@ function initPlayer() { x: W / 2, y: H / 2, r: 20, hp: 6, shieldHp: 0, aim: 0, bodyFacing: 0, fireT: 0, charge: 0, invT: 0, frenzyT: 0, - step: 0, stepTick: 0, moveBlend: 0, recoil: 0, muzzleT: 0, + step: 0, moveBlend: 0, recoil: 0, muzzleT: 0, animT: 0, attackT: 0, actionT: 0, hitT: 0, deathT: 0, weapon: { id: 'bonepopper', ammo: Infinity }, holstered: null, // a special weapon set aside while the pistol is out @@ -76,15 +76,11 @@ function updatePlayer(dt) { p.x += (mx / len) * spd * dt; p.y += (my / len) * spd * dt; p.step += dt * 9 * Math.min(st.speedMul, 1.8); - const stepTick = Math.floor(p.step / Math.PI); - if (stepTick !== p.stepTick && p.hp > 0) Sfx.footstep(p.moveBlend, onGore(p.x, p.y)); - p.stepTick = stepTick; } else { // Finish on a planted contact frame instead of freezing halfway through a stride. const planted = Math.round(p.step / Math.PI) * Math.PI; p.step = lerp(p.step, planted, clamp(dt * 10, 0, 1)); - p.stepTick = Math.round(p.step / Math.PI); } p.moveBlend = lerp(p.moveBlend, mx || my ? 1 : 0, clamp(dt * 12, 0, 1)); p.recoil = Math.max(0, p.recoil - dt * 7); @@ -339,7 +335,7 @@ function hurtPlayer(dmg, ang, attacker) { addShake(10); spawnText(p.x, p.y - 20, 'SECOND SKIN', '#e2472f'); addToast('SECOND SKIN', 'you refuse to die — once per floor'); - Sfx.shieldUp(); + Sfx.revive(); return; } p.hp = 0; diff --git a/js/sfx.js b/js/sfx.js index 6c1374f..4c68c69 100644 --- a/js/sfx.js +++ b/js/sfx.js @@ -3,12 +3,12 @@ const Sfx = { ctx: null, master: null, comp: null, glue: null, limiter: null, muted: false, mixBus: null, musicBus: null, musicMute: null, duckGain: null, saturators: {}, buses: {}, active: [], procedural: [], lastPlayed: new Map(), currentWeaponLoop: null, - footstepN: 0, fleshN: 0, wallN: 0, goreN: 0, + fleshN: 0, wallN: 0, goreN: 0, lastPunch: -999, lastPunchLevel: 0, maxVoices: 24, cooldowns: { hit: 0.035, crit: 0.07, ricochet: 0.05, wallHit: 0.045, sawHit: 0.09, gem: 0.025, - enemyDie: 0.04, goreBurst: 0.055, footstep: 0.10, lifesteal: 0.12, + enemyDie: 0.04, goreBurst: 0.055, lifesteal: 0.12, loop_wpnloop_bile: 0.12, loop_wpnloop_cauterizer: 0.12, loop_wpnloop_redhand: 0.11, loop_wpnloop_spinaltap_charge: 0.12, }, @@ -302,36 +302,6 @@ const Sfx = { this.blip(260, .14, 'sine', .2, 180, 'voices'); }, playerDeath() { this.stopAllLoops(); if (this.sample('plr_death', 'voices', null, 1, 'playerDeath')) return; this.thump(80, .65, .7, 25, 'voices'); this.noise(.7, .45, 350, 'voices'); }, - footstep(strength, wet) { - if (!this.canPlay('footstep')) return; - this.footstepN = (this.footstepN % 4) + 1; - const dryName = 'plr_step' + this.footstepN; - const wanted = wet ? 'plr_step_wet' + this.footstepN : dryName; - const force = strength === undefined ? 1 : clamp(strength, .55, 1); - const side = this.footstepN % 2 ? .96 : 1.04; - const gain = .66 * force * side; - let sampled = false; - if (typeof SfxBank !== 'undefined' && SfxBank.buffers.has(wanted)) { - this.enforceVoiceLimit(); - sampled = SfxBank.play(wanted, this.output('impacts', null), { - gain: gain * rand(.96, 1.04), rate: rand(.965, 1.035), - }); - } else if (typeof SfxBank !== 'undefined') { - SfxBank.want(wanted); - // Keep the gait audible while a wet variant is still decoding. - if (wet && SfxBank.buffers.has(dryName)) { - this.enforceVoiceLimit(); - sampled = SfxBank.play(dryName, this.output('impacts', null), { gain: gain * .9, rate: rand(.97, 1.03) }); - } - } - this.punch(.18 * force * side); - if (!sampled) { - if (wet) { - this.noise(.08, .17 * force, 360, 'impacts'); - this.noise(.025, .05 * force, 1500, 'impacts', null, true); - } else this.noise(.055, .13 * force, 420, 'impacts'); - } - }, ricochet(pos) { if (this.sample('imp_ricochet', 'impacts', pos, .5, 'ricochet')) return; this.blip(rand(1400, 1900), .045, 'triangle', .12, -500, 'impacts', pos); }, wallHit(pos) { @@ -376,8 +346,10 @@ const Sfx = { ammo() { this.ui('ammo', () => { this.click(.14, 1600, 'ui'); this.blip(520, .08, 'square', .12, -80, 'ui'); }); }, weapon() { this.ui('weapon', () => { this.thump(120, .14, .4, 50, 'ui'); this.blip(300, .1, 'square', .25, 200, 'ui'); this.blip(450, .12, 'square', .2, 250, 'ui'); }); }, item() { this.ui('item', () => { this.thump(100, .2, .4, 45, 'ui'); this.blip(350, .15, 'triangle', .3, 150, 'ui'); this.blip(525, .2, 'triangle', .25, 200, 'ui'); }); }, - active() { this.thump(140, .22, .5, 60, 'ui'); this.blip(700, .12, 'sawtooth', .28, 500, 'ui'); this.noise(.1, .14, 2400, 'ui', null, true); }, - activeEmpty() { this.blip(220, .12, 'square', .18, -90, 'ui'); }, + active() { this.ui('active', () => { this.thump(140, .22, .5, 60, 'ui'); this.blip(700, .12, 'sawtooth', .28, 500, 'ui'); this.noise(.1, .14, 2400, 'ui', null, true); }); }, + activeEmpty() { this.ui('active_empty', () => this.blip(220, .12, 'square', .18, -90, 'ui')); }, + curse() { this.ui('curse', () => { this.thump(60, .5, .5, 30, 'ui'); this.blip(180, .4, 'sawtooth', .22, -60, 'ui'); }); }, + revive() { if (this.sample('plr_revive', 'voices', null, .85, 'revive')) return; this.blip(500, .3, 'sine', .25, 300, 'ui'); this.thump(100, .3, .4, 50, 'ui'); }, levelup() { this.ui('levelup', () => { this.blip(400, .12, 'square', .25, 200, 'ui'); setTimeout(() => this.blip(600, .12, 'square', .25, 200, 'ui'), 100); setTimeout(() => this.blip(800, .2, 'square', .25, 200, 'ui'), 200); }); }, perk() { this.ui('perk', () => this.blip(500, .1, 'square', .25, 300, 'ui')); }, menu() { this.ui('menu', () => this.blip(620, .055, 'square', .11, 120, 'ui')); }, diff --git a/js/sfxbank.js b/js/sfxbank.js index 3d48503..d3f1852 100644 --- a/js/sfxbank.js +++ b/js/sfxbank.js @@ -79,6 +79,10 @@ const SfxBank = { ui_perk: 'ui_perk.mp3', ui_stairs: 'ui_stairs.mp3', ui_menu: 'ui_menu.mp3', + ui_active: 'ui_active.mp3', + ui_active_empty: 'ui_active_empty.mp3', + ui_curse: 'ui_curse.mp3', + plr_revive: 'plr_revive.mp3', imp_crit: 'imp_crit.mp3', imp_ricochet: 'imp_ricochet.mp3', @@ -88,14 +92,6 @@ const SfxBank = { plr_death: 'plr_death.mp3', plr_shield_break: 'plr_shield_break.mp3', plr_shield_up: 'plr_shield_up.mp3', - plr_step1: 'plr_step1.mp3', - plr_step2: 'plr_step2.mp3', - plr_step3: 'plr_step3.mp3', - plr_step4: 'plr_step4.mp3', - plr_step_wet1: 'plr_step_wet1.mp3', - plr_step_wet2: 'plr_step_wet2.mp3', - plr_step_wet3: 'plr_step_wet3.mp3', - plr_step_wet4: 'plr_step_wet4.mp3', imp_flesh1: 'imp_flesh1.mp3', imp_flesh2: 'imp_flesh2.mp3', imp_flesh3: 'imp_flesh3.mp3', @@ -115,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=41') + const job = fetch('/assets/sfx/' + encodeURIComponent(file) + '?v=42') .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))) diff --git a/js/sprites.js b/js/sprites.js index 3983939..3482d18 100644 --- a/js/sprites.js +++ b/js/sprites.js @@ -84,7 +84,7 @@ const Sprites = { const img = new Image(); img.onload = () => { this.imgs[name] = img; if (--pending === 0) G.imagesLoaded = true; }; img.onerror = () => { if (--pending === 0) G.imagesLoaded = true; }; - img.src = 'assets/' + name + '.png?v=41'; + img.src = 'assets/' + name + '.png?v=42'; } }, diff --git a/test/smoke.js b/test/smoke.js index 5b1b8f4..91070a6 100644 --- a/test/smoke.js +++ b/test/smoke.js @@ -180,21 +180,14 @@ step(12, 16); const aimDeltaBeforeIdle = Math.abs(Math.atan2(Math.sin(ctx.G.player.aim - idleBodyFacing), Math.cos(ctx.G.player.aim - idleBodyFacing))); const aimDeltaAfterIdle = Math.abs(Math.atan2(Math.sin(ctx.G.player.aim - ctx.G.player.bodyFacing), Math.cos(ctx.G.player.aim - ctx.G.player.bodyFacing))); check('legs rotate back toward aim after movement stops', aimDeltaAfterIdle < aimDeltaBeforeIdle); -let footstepCalls = 0, wetFootsteps = 0; -const realFootstep = ctx.Sfx.footstep; const decalStart = ctx.G.cur.decals.length; for (const offset of [45, 90, 135, 180]) { ctx.G.cur.decals.push({ x: ctx.G.player.x + offset, y: ctx.G.player.y, s: 1.6, rot: 0, img: 'decal_blood1' }); } check('gore proximity query detects blood underfoot', ctx.onGore(ctx.G.player.x + 45, ctx.G.player.y)); -ctx.Sfx.footstep = (_strength, wet) => { footstepCalls++; if (wet) wetFootsteps++; }; press('d'); step(60, 16); release('d'); -const movingFootsteps = footstepCalls; step(30, 16); -ctx.Sfx.footstep = realFootstep; ctx.G.cur.decals.splice(decalStart); -check('stride-9 cycle emits synchronized footsteps', movingFootsteps >= 2 && footstepCalls === movingFootsteps); -check('blood decals select wet footstep events', wetFootsteps >= 1); ctx.Input.mdown = true; ctx.Input.mx = 700; ctx.Input.my = 300; step(30, 16); check('bullets fired', ctx.G.bullets.length > 0 || ctx.G.kills >= 0); // may have hit nothing