Doom-style weapon audio batch + remove footsteps
- Regenerated all 13 weapon fire one-shots and 4 weapon loops via the audio specialist (ElevenLabs) with a 1993 Doom style brief: chunky, bass-heavy, crunchy 90s FPS sounds - New clips wired into SfxBank: ui_active, ui_active_empty (active items), ui_curse (legendary pickups, replaces the normal item jingle), plr_revive (Second Skin revive) - Footstep system removed entirely (never worked right): Sfx.footstep, 8 plr_step* samples and bank entries, player.js call site, smoke test block - Cache v=42
This commit is contained in:
1 parent
4dad22d087
commit
64ac53434b
36 files changed
+37
-80
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+21
-21
@@ -6,31 +6,31 @@
|
||||
<meta name="theme-color" content="#0a0305">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 64 64%22><text y=%2252%22 x=%2250%22 text-anchor=%22middle%22 dominant-baseline=%22middle%22 font-size=%2252%22>🔪</text></svg>">
|
||||
<title>MEATSLICER</title>
|
||||
<link rel="stylesheet" href="css/style.css?v=41">
|
||||
<link rel="stylesheet" href="css/style.css?v=42">
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrap">
|
||||
<canvas id="game" width="960" height="640"></canvas>
|
||||
</div>
|
||||
<script src="js/utils.js?v=41"></script>
|
||||
<script src="js/state.js?v=41"></script>
|
||||
<script src="js/input.js?v=41"></script>
|
||||
<script src="js/sfx.js?v=41"></script>
|
||||
<script src="js/sfxbank.js?v=41"></script>
|
||||
<script src="js/audio.js?v=41"></script>
|
||||
<script src="js/sprites.js?v=41"></script>
|
||||
<script src="js/particles.js?v=41"></script>
|
||||
<script src="js/weapons.js?v=41"></script>
|
||||
<script src="js/bullets.js?v=41"></script>
|
||||
<script src="js/pickups.js?v=41"></script>
|
||||
<script src="js/items.js?v=41"></script>
|
||||
<script src="js/perks.js?v=41"></script>
|
||||
<script src="js/enemies.js?v=41"></script>
|
||||
<script src="js/bosses.js?v=41"></script>
|
||||
<script src="js/rooms.js?v=41"></script>
|
||||
<script src="js/player.js?v=41"></script>
|
||||
<script src="js/hud.js?v=41"></script>
|
||||
<script src="js/help.js?v=41"></script>
|
||||
<script src="js/main.js?v=41"></script>
|
||||
<script src="js/utils.js?v=42"></script>
|
||||
<script src="js/state.js?v=42"></script>
|
||||
<script src="js/input.js?v=42"></script>
|
||||
<script src="js/sfx.js?v=42"></script>
|
||||
<script src="js/sfxbank.js?v=42"></script>
|
||||
<script src="js/audio.js?v=42"></script>
|
||||
<script src="js/sprites.js?v=42"></script>
|
||||
<script src="js/particles.js?v=42"></script>
|
||||
<script src="js/weapons.js?v=42"></script>
|
||||
<script src="js/bullets.js?v=42"></script>
|
||||
<script src="js/pickups.js?v=42"></script>
|
||||
<script src="js/items.js?v=42"></script>
|
||||
<script src="js/perks.js?v=42"></script>
|
||||
<script src="js/enemies.js?v=42"></script>
|
||||
<script src="js/bosses.js?v=42"></script>
|
||||
<script src="js/rooms.js?v=42"></script>
|
||||
<script src="js/player.js?v=42"></script>
|
||||
<script src="js/hud.js?v=42"></script>
|
||||
<script src="js/help.js?v=42"></script>
|
||||
<script src="js/main.js?v=42"></script>
|
||||
</body>
|
||||
</html>
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-6
@@ -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;
|
||||
|
||||
@@ -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')); },
|
||||
|
||||
+5
-9
@@ -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)))
|
||||
|
||||
+1
-1
@@ -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';
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user