Files
Primal Rampage Bot 2024ea48b5 Primal Rampage — prehistoric survival wave shooter
Vanilla JS + Canvas 2D, zero dependencies.

Gameplay: campaign waves vs 12 dino species + 3 bosses, arena mode,
7 weapons, hero talents, pets, ult/bombs, card roguelite picks.
World: day/night cycle, weather fronts (rain+lightning), Blood Moon
weekends, 3 act biomes (jungle -> swamp -> ashlands with lava veins).
Meta: gems economy, upgrade tree, quests, bestiary, achievements,
shop (weapons/heroes/skins incl. premium), revive, ad-cap system,
endless scaling past wave 25, live-painted shop/bestiary icons,
pro main menu, auto-aim toggle (F). Headless smoke suite included.
2026-08-23 04:26:58 +00:00

233 lines
9.0 KiB
JavaScript

/* Neon Rampage — synthesized audio (WebAudio). All sounds generated at runtime. */
(function () {
'use strict';
var US = (window.US = window.US || {});
var ctx = null;
var master = null;
var sfxGain = null;
var musicGain = null;
var musicTimer = null;
var step = 0;
var windSrc = null; // { src, lfo }
function ensure() {
if (ctx) return true;
try {
var AC = window.AudioContext || window.webkitAudioContext;
if (!AC) return false;
ctx = new AC();
master = ctx.createGain();
master.gain.value = 0.9;
master.connect(ctx.destination);
sfxGain = ctx.createGain();
sfxGain.gain.value = US.save.get().settings.sfx;
sfxGain.connect(master);
musicGain = ctx.createGain();
musicGain.gain.value = US.save.get().settings.music * 0.5;
musicGain.connect(master);
return true;
} catch (e) { return false; }
}
function resume() {
if (!ensure()) return;
if (ctx.state === 'suspended') ctx.resume();
}
// Generic one-shot: oscillator with pitch slide + optional noise burst.
function tone(opts) {
if (!ensure() || ctx.state === 'suspended') { if (!resume()) return; }
var t = ctx.currentTime;
var o = ctx.createOscillator();
var g = ctx.createGain();
o.type = opts.type || 'square';
o.frequency.setValueAtTime(opts.f0 || 440, t);
if (opts.f1) o.frequency.exponentialRampToValueAtTime(Math.max(20, opts.f1), t + (opts.dur || 0.15));
g.gain.setValueAtTime(0.0001, t);
g.gain.exponentialRampToValueAtTime(opts.vol || 0.2, t + 0.008);
g.gain.exponentialRampToValueAtTime(0.0001, t + (opts.dur || 0.15));
o.connect(g); g.connect(sfxGain);
o.start(t); o.stop(t + (opts.dur || 0.15) + 0.05);
if (opts.noise) {
var len = Math.floor(ctx.sampleRate * (opts.noiseDur || 0.12));
var buf = ctx.createBuffer(1, len, ctx.sampleRate);
var d = buf.getChannelData(0);
for (var i = 0; i < len; i++) d[i] = (Math.random() * 2 - 1) * (1 - i / len);
var src = ctx.createBufferSource();
src.buffer = buf;
var ng = ctx.createGain();
ng.gain.value = opts.noiseVol || 0.25;
src.connect(ng); ng.connect(sfxGain);
src.start(t);
}
}
var SFX = {
shoot: function () { tone({ type: 'square', f0: 720, f1: 160, dur: 0.07, vol: 0.10 }); },
shotgun: function () { tone({ type: 'sawtooth', f0: 300, f1: 90, dur: 0.16, vol: 0.18, noise: true, noiseVol: 0.2 }); },
rail: function () { tone({ type: 'sawtooth', f0: 1400, f1: 200, dur: 0.22, vol: 0.14 }); },
rocket: function () { tone({ type: 'triangle', f0: 220, f1: 60, dur: 0.3, vol: 0.2, noise: true }); },
smg: function () { tone({ type: 'square', f0: 520, f1: 140, dur: 0.05, vol: 0.07 }); },
hit: function () { tone({ type: 'triangle', f0: 300, f1: 120, dur: 0.05, vol: 0.08 }); },
kill: function () { tone({ type: 'square', f0: 260, f1: 520, dur: 0.09, vol: 0.10 }); },
explode: function () { tone({ type: 'sawtooth', f0: 130, f1: 40, dur: 0.4, vol: 0.28, noise: true, noiseVol: 0.35, noiseDur: 0.3 }); },
hurt: function () { tone({ type: 'sawtooth', f0: 180, f1: 70, dur: 0.18, vol: 0.22 }); },
pickup: function () { tone({ type: 'sine', f0: 620, f1: 1240, dur: 0.09, vol: 0.12 }); },
heal: function () { tone({ type: 'sine', f0: 440, f1: 880, dur: 0.18, vol: 0.14 }); },
card: function () { tone({ type: 'sine', f0: 500, f1: 1000, dur: 0.14, vol: 0.13 }); },
onfire: function () {
[523, 659, 784, 1047].forEach(function (f, i) {
setTimeout(function () { tone({ type: 'square', f0: f, dur: 0.12, vol: 0.16 }); }, i * 70);
});
},
ult: function () { tone({ type: 'sawtooth', f0: 80, f1: 900, dur: 0.5, vol: 0.3, noise: true, noiseVol: 0.3 }); },
bomb: function () { tone({ type: 'sawtooth', f0: 100, f1: 30, dur: 0.6, vol: 0.32, noise: true, noiseVol: 0.45, noiseDur: 0.5 }); },
bossRoar:function () { tone({ type: 'sawtooth', f0: 90, f1: 45, dur: 0.7, vol: 0.3, noise: true, noiseVol: 0.2 }); },
stomp: function (vol) {
var v = vol || 0.18;
tone({ type: 'sine', f0: 85, f1: 26, dur: 0.17, vol: v });
tone({ type: 'triangle', f0: 55, f1: 22, dur: 0.24, vol: v * 0.7 });
},
ui: function () { tone({ type: 'sine', f0: 700, f1: 900, dur: 0.05, vol: 0.08 }); },
buy: function () { [660, 880].forEach(function (f, i) { setTimeout(function () { tone({ type: 'sine', f0: f, dur: 0.1, vol: 0.12 }); }, i * 90); }); },
lose: function () { [392, 330, 262, 196].forEach(function (f, i) { setTimeout(function () { tone({ type: 'triangle', f0: f, dur: 0.22, vol: 0.16 }); }, i * 160); }); },
win: function () { [523, 659, 784, 1047, 1319].forEach(function (f, i) { setTimeout(function () { tone({ type: 'square', f0: f, dur: 0.15, vol: 0.14 }); }, i * 110); }); }
};
/* --- tiny procedural music loop: dark synthwave bass + hats --- */
var BASS = [0, 0, 7, 0, 5, 5, 3, 7]; // semitone offsets from A1
function note(semi) { return 55 * Math.pow(2, semi / 12); }
function musicStep() {
if (!ctx || !musicOn) return;
var t = ctx.currentTime;
// bass
var o = ctx.createOscillator();
var g = ctx.createGain();
o.type = 'sawtooth';
var root = BASS[Math.floor(step / 2) % BASS.length];
var playingBoss = US.game && US.game.isBossWave && US.game.isBossWave();
var mul = playingBoss ? Math.pow(2, 3 / 12) : 1;
o.frequency.value = note(root) * mul;
g.gain.setValueAtTime(0.0001, t);
g.gain.exponentialRampToValueAtTime(0.22, t + 0.02);
g.gain.exponentialRampToValueAtTime(0.0001, t + 0.24);
o.connect(g); g.connect(musicGain);
o.start(t); o.stop(t + 0.3);
// hat every other step
if (step % 2 === 1) {
var len = Math.floor(ctx.sampleRate * 0.03);
var buf = ctx.createBuffer(1, len, ctx.sampleRate);
var d = buf.getChannelData(0);
for (var i = 0; i < len; i++) d[i] = (Math.random() * 2 - 1) * (1 - i / len);
var src = ctx.createBufferSource();
src.buffer = buf;
var hg = ctx.createGain(); hg.gain.value = 0.05;
src.connect(hg); hg.connect(musicGain);
src.start(t);
}
step++;
}
var musicOn = false;
/* --- wind ambience: filtered brown noise with slow gusts --- */
var ambGain = null;
function startWind() {
if (!ctx || windSrc || !musicOn) return;
var len = ctx.sampleRate * 2;
var buf = ctx.createBuffer(1, len, ctx.sampleRate);
var d = buf.getChannelData(0);
var lastV = 0;
for (var i = 0; i < len; i++) {
var wn = Math.random() * 2 - 1;
lastV = (lastV + 0.02 * wn) / 1.02; // brown-ish noise
d[i] = lastV * 3.5;
}
var src = ctx.createBufferSource();
src.buffer = buf;
src.loop = true;
var lp = ctx.createBiquadFilter();
lp.type = 'lowpass';
lp.frequency.value = 300;
lp.Q.value = 0.7;
// gust LFO breathing on the filter
var lfo = ctx.createOscillator();
lfo.frequency.value = 0.11;
var lfoG = ctx.createGain();
lfoG.gain.value = 150;
lfo.connect(lfoG);
lfoG.connect(lp.frequency);
ambGain = ctx.createGain();
ambGain.gain.value = 0;
src.connect(lp);
lp.connect(ambGain);
ambGain.connect(musicGain);
try {
ambGain.gain.linearRampToValueAtTime(0.5, ctx.currentTime + 2.5);
src.start();
lfo.start();
windSrc = { src: src, lfo: lfo };
} catch (e) { /* autoplay quirks */ }
}
function stopWind() {
if (!windSrc) return;
try {
ambGain.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.4);
var w = windSrc;
setTimeout(function () {
try { w.src.stop(); w.lfo.stop(); } catch (e) {}
}, 450);
} catch (e) {}
windSrc = null;
}
US.audio = {
resume: resume,
sfx: function (name) {
if (!ensure()) return;
sfxGain.gain.value = US.save.get().settings.sfx;
if (SFX[name]) SFX[name].apply(null, Array.prototype.slice.call(arguments, 1));
},
setVolumes: function () {
if (!ctx) return;
sfxGain.gain.value = US.save.get().settings.sfx;
musicGain.gain.value = US.save.get().settings.music * 0.5;
},
startMusic: function () {
if (!ensure()) return;
resume();
if (!musicTimer) {
musicOn = true;
musicStep();
musicTimer = setInterval(musicStep, 250);
}
startWind();
},
stopMusic: function () {
musicOn = false;
if (musicTimer) { clearInterval(musicTimer); musicTimer = null; }
stopWind();
},
hoverTick: function () {
if (!ensure()) return;
tone({ type: 'sine', f0: 900, f1: 1100, dur: 0.035, vol: 0.05 });
},
thunder: function () {
if (!ensure()) return;
tone({ type: 'sawtooth', f0: 52, f1: 22, dur: 1.2, vol: 0.3, noise: true, noiseVol: 0.55, noiseDur: 1.3 });
},
distantRoar: function () {
if (!ensure()) return;
tone({ type: 'sawtooth', f0: 120, f1: 36, dur: 1.5, vol: 0.09 });
setTimeout(function () {
tone({ type: 'triangle', f0: 70, f1: 28, dur: 1.9, vol: 0.07 });
}, 180);
}
};
})();