Files
primal-rampage/js/main.js
T
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

186 lines
6.5 KiB
JavaScript

/* Neon Rampage — bootstrap & input (keyboard, mouse, touch sticks). */
(function () {
'use strict';
var US = (window.US = window.US || {});
var input = {
up: false, down: false, left: false, right: false,
firing: false,
aimX: 640, aimY: 360,
moveVec: null // touch stick vector {x,y}
};
US.input = input;
var canvas, touchUI = {};
var stickMove = null, stickAim = null; // active touches
function toWorld(e) {
var r = canvas.getBoundingClientRect();
var g = US.game.get();
if (!g) return { x: input.aimX, y: input.aimY };
var scale = Math.min(r.width / g.W, r.height / g.H);
var ox = (r.width - g.W * scale) / 2;
var oy = (r.height - g.H * scale) / 2;
return {
x: (e.clientX - r.left - ox) / scale,
y: (e.clientY - r.top - oy) / scale
};
}
function bindKeyboard() {
window.addEventListener('keydown', function (e) {
switch (e.code) {
case 'KeyW': case 'ArrowUp': input.up = true; break;
case 'KeyS': case 'ArrowDown': input.down = true; break;
case 'KeyA': case 'ArrowLeft': input.left = true; break;
case 'KeyD': case 'ArrowRight': input.right = true; break;
case 'Space': input.firing = true; e.preventDefault(); break;
case 'KeyR': US.game.castUlt(); break;
case 'KeyF': {
var on2 = US.game.toggleAutoAim();
var t3 = document.getElementById('toast');
if (t3) {
t3.textContent = on2 ? '🎯 Auto-aim ON' : '✋ Auto-aim OFF';
t3.classList.remove('hidden');
clearTimeout(t3._tt);
t3._tt = setTimeout(function () { t3.classList.add('hidden'); }, 1400);
}
break;
}
case 'KeyQ': US.game.useBomb(); break;
case 'KeyP': case 'Escape':
if (!togglePause()) {
// if paused via overlay button, resume
if (US.game.resume()) document.getElementById('overlay-pause').classList.add('hidden');
}
break;
}
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].indexOf(e.code) >= 0) e.preventDefault();
});
window.addEventListener('keyup', function (e) {
switch (e.code) {
case 'KeyW': case 'ArrowUp': input.up = false; break;
case 'KeyS': case 'ArrowDown': input.down = false; break;
case 'KeyA': case 'ArrowLeft': input.left = false; break;
case 'KeyD': case 'ArrowRight': input.right = false; break;
case 'Space': input.firing = false; break;
}
});
}
function togglePause() {
if (US.game.pause()) {
document.getElementById('overlay-pause').classList.remove('hidden');
return true;
}
return false;
}
function bindMouse() {
canvas.addEventListener('mousemove', function (e) {
var w = toWorld(e);
input.aimX = w.x; input.aimY = w.y;
});
canvas.addEventListener('mousedown', function (e) {
if (e.button === 0) {
var w = toWorld(e);
input.aimX = w.x; input.aimY = w.y;
input.firing = true;
}
US.audio.resume();
});
window.addEventListener('mouseup', function () { input.firing = false; });
canvas.addEventListener('contextmenu', function (e) { e.preventDefault(); });
}
/* ---------------- touch: twin virtual sticks ---------------- */
function makeStick(el) {
var knob = el.querySelector('.stick-knob');
var base = el.getBoundingClientRect();
var id = null, vec = { x: 0, y: 0 };
function setVec(t) {
var r = el.getBoundingClientRect();
var cx = r.left + r.width / 2, cy = r.top + r.height / 2;
var dx = t.clientX - cx, dy = t.clientY - cy;
var max = r.width / 2;
var d = Math.hypot(dx, dy);
if (d > max) { dx *= max / d; dy *= max / d; }
vec.x = dx / max; vec.y = dy / max;
knob.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
}
el.addEventListener('touchstart', function (e) {
e.preventDefault();
var t = e.changedTouches[0];
id = t.identifier;
setVec(t);
}, { passive: false });
el.addEventListener('touchmove', function (e) {
e.preventDefault();
for (var i = 0; i < e.changedTouches.length; i++) {
if (e.changedTouches[i].identifier === id) setVec(e.changedTouches[i]);
}
}, { passive: false });
function end(e) {
for (var i = 0; i < e.changedTouches.length; i++) {
if (e.changedTouches[i].identifier === id) {
id = null;
vec.x = 0; vec.y = 0;
knob.style.transform = '';
}
}
}
el.addEventListener('touchend', end);
el.addEventListener('touchcancel', end);
return vec;
}
function isTouchDevice() {
return ('ontouchstart' in window) || navigator.maxTouchPoints > 0;
}
function bindTouch() {
if (!isTouchDevice()) return;
var root = document.getElementById('touch-ui');
root.classList.remove('hidden');
touchUI.fireBtn = document.getElementById('tc-fire');
touchUI.ultBtn = document.getElementById('tc-ult');
touchUI.bombBtn = document.getElementById('tc-bomb');
stickMove = makeStick(document.getElementById('stick-move'));
stickAim = makeStick(document.getElementById('stick-aim'));
// live vector refs — game reads these directly each frame (no polling)
input.moveVec = stickMove;
input.aimVec = stickAim;
touchUI.fireBtn.addEventListener('touchstart', function (e) { e.preventDefault(); input.firing = true; }, { passive: false });
touchUI.fireBtn.addEventListener('touchend', function () { input.firing = false; });
touchUI.ultBtn.addEventListener('touchstart', function (e) { e.preventDefault(); US.game.castUlt(); }, { passive: false });
touchUI.bombBtn.addEventListener('touchstart', function (e) { e.preventDefault(); US.game.useBomb(); }, { passive: false });
}
/* ---------------- boot ---------------- */
window.addEventListener('DOMContentLoaded', function () {
canvas = document.getElementById('game');
US.i18n.setLang(US.save.get().lang);
US.ui.init();
US.game.mount(canvas);
bindKeyboard();
bindMouse();
bindTouch();
// default aim ahead of player
input.aimX = 800; input.aimY = 360;
input.moveVec = null; // replaced by live stick ref on touch devices
// unlock audio on first interaction anywhere
['pointerdown', 'keydown', 'touchstart'].forEach(function (ev) {
window.addEventListener(ev, function once() {
US.audio.resume();
window.removeEventListener(ev, once);
});
});
});
})();