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.
This commit is contained in:
+261
@@ -0,0 +1,261 @@
|
||||
/* PRIMAL RAMPAGE — game data: weapons, heroes, skins, beasts, alphas, cards.
|
||||
Theme: prehistoric tribe survival vs dinosaurs. Original content.
|
||||
NOTE: ids stay stable so existing saved profiles keep working;
|
||||
`kind` selects the drawn sprite in js/art.js */
|
||||
(function () {
|
||||
'use strict';
|
||||
var US = (window.US = window.US || {});
|
||||
|
||||
/* ---------------- Weapons ---------------- */
|
||||
US.WEAPONS = {
|
||||
pistol: {
|
||||
id: 'pistol', name: 'Slingshot', price: 0, kind: 'slingshot',
|
||||
dmg: 12, rof: 3.2, speed: 620, pellets: 1, spread: 0.04, pierce: 0,
|
||||
color: '#c9bda6', size: 4, sfx: 'shoot'
|
||||
},
|
||||
smg: {
|
||||
id: 'smg', name: 'Bone Blowpipe', price: 400, kind: 'blowpipe',
|
||||
dmg: 7, rof: 8.5, speed: 700, pellets: 1, spread: 0.13, pierce: 0,
|
||||
color: '#caa96a', size: 3.5, sfx: 'smg'
|
||||
},
|
||||
shotgun: {
|
||||
id: 'shotgun', name: 'Spear Burst', price: 750, kind: 'spears',
|
||||
dmg: 8, rof: 1.5, speed: 560, pellets: 6, spread: 0.38, pierce: 0,
|
||||
color: '#a8814f', size: 4, sfx: 'shotgun', knock: 120
|
||||
},
|
||||
rail: {
|
||||
id: 'rail', name: 'Obsidian Lance', price: 1200, kind: 'obelisk',
|
||||
dmg: 46, rof: 1.1, speed: 1500, pellets: 1, spread: 0, pierce: 3,
|
||||
color: '#7b6fa0', size: 5, sfx: 'rail'
|
||||
},
|
||||
launcher: {
|
||||
id: 'launcher', name: 'Fire Bomb Thrower', price: 1800, kind: 'firebomb',
|
||||
dmg: 30, rof: 0.9, speed: 430, pellets: 1, spread: 0.05, pierce: 0,
|
||||
color: '#e04b3a', size: 7, sfx: 'rocket', aoe: 90
|
||||
}
|
||||
};
|
||||
|
||||
/* ---------------- Heroes ---------------- */
|
||||
US.HEROES = {
|
||||
recruit: {
|
||||
id: 'recruit', name: 'Raider', price: 0,
|
||||
hp: 100, speed: 250, dmgMul: 1,
|
||||
talent: '+0 — cân bằng / balanced',
|
||||
tint: '#e04b3a'
|
||||
},
|
||||
scout: {
|
||||
id: 'scout', name: 'Trail Runner', price: 500,
|
||||
hp: 75, speed: 320, dmgMul: 0.9,
|
||||
talent: '+28% tốc độ / speed — mỏng máu / frail',
|
||||
tint: '#58a55c'
|
||||
},
|
||||
brute: {
|
||||
id: 'brute', name: 'Bear-Clan Brute', price: 900,
|
||||
hp: 160, speed: 205, dmgMul: 1.25,
|
||||
talent: 'Trâu bò, đòn nặng, chậm / tanky, heavy, slow',
|
||||
tint: '#ffb938'
|
||||
},
|
||||
gunslinger: {
|
||||
id: 'gunslinger', name: 'Spirit Hunter', price: 1400,
|
||||
hp: 90, speed: 265, dmgMul: 1.05,
|
||||
talent: '+15% chí mạng ×2.5 / crit chance',
|
||||
crit: 0.15, critMul: 2.5,
|
||||
tint: '#c56cf0'
|
||||
}
|
||||
};
|
||||
|
||||
/* ---------------- Skins (war-paint palettes) ---------------- */
|
||||
US.SKINS = {
|
||||
default: { id: 'default', name: 'Clay', price: 0, body: '#e04b3a', trim: '#e8dcc4' },
|
||||
ember: { id: 'ember', name: 'Ember', price: 300, body: '#ff8a3d', trim: '#ffd9a0' },
|
||||
frost: { id: 'frost', name: 'Glacier', price: 300, body: '#9fd8ff', trim: '#e8fbff' },
|
||||
void: { id: 'void', name: 'Night Shade', price: 600, body: '#a86bff', trim: '#e2ccff' },
|
||||
gold: { id: 'gold', name: 'Sun Chief', price: 900, body: '#ffb938', trim: '#fff3c4' },
|
||||
crimson: { id: 'crimson', name: 'Blood Moon', price: 900, body: '#c22f45', trim: '#ffc2c8' },
|
||||
// --- premium tier, bought with Ngọn Lửa (hard currency) ---
|
||||
ashfang: { id: 'ashfang', name: 'Ashfang Chief', price: 0, gems: 15, body: '#5a4a3c', trim: '#ff6b35', premium: true,
|
||||
vi: 'Thủ Lĩnh Tro Tàn — hào quang núi lửa' },
|
||||
jade: { id: 'jade', name: 'Jade Serpent', price: 0, gems: 25, body: '#2fae7a', trim: '#d9ffe9', premium: true,
|
||||
vi: 'Trăn Bích Ngọc — da rắn thần rừng' },
|
||||
solaris: { id: 'solaris', name: 'Solaris', price: 0, gems: 40, body: '#ffd24a', trim: '#fff8dd', premium: true, glow: true,
|
||||
vi: 'Thái Dương — ánh sáng nguyên thủy' }
|
||||
};
|
||||
|
||||
/* ---------------- Beasts ---------------- */
|
||||
// contact = dps on touch; value feeds heat/score; cost feeds wave budget
|
||||
US.ENEMIES = {
|
||||
grunt: {
|
||||
id: 'grunt', art: 'raptor', hp: 30, speed: 95, radius: 14, contact: 12, value: 1, cost: 10,
|
||||
coin: [1, 2], color: '#5f8f3e', score: 10, fromWave: 1
|
||||
},
|
||||
runner: {
|
||||
id: 'runner', art: 'compy', hp: 16, speed: 185, radius: 11, contact: 9, value: 1, cost: 12,
|
||||
coin: [1, 2], color: '#d98e2b', score: 12, fromWave: 2
|
||||
},
|
||||
spitter: {
|
||||
id: 'spitter', art: 'dilo', hp: 34, speed: 70, radius: 13, contact: 8, value: 2, cost: 22,
|
||||
coin: [2, 3], color: '#8ac74a', score: 20, fromWave: 3,
|
||||
ranged: { dmg: 10, speed: 240, rof: 1.6, range: 380 }
|
||||
},
|
||||
tank: {
|
||||
id: 'tank', art: 'trike', hp: 130, speed: 55, radius: 22, contact: 22, value: 3, cost: 40,
|
||||
coin: [4, 7], color: '#8a7a5c', score: 35, fromWave: 4
|
||||
},
|
||||
stego: {
|
||||
id: 'stego', art: 'stego', hp: 90, speed: 60, radius: 19, contact: 18, value: 2, cost: 34,
|
||||
coin: [3, 5], color: '#6f7d44', score: 30, fromWave: 7
|
||||
},
|
||||
pachy: {
|
||||
id: 'pachy', art: 'pachy', hp: 45, speed: 120, radius: 13, contact: 14, value: 2, cost: 26,
|
||||
coin: [2, 4], color: '#b5893e', score: 28, fromWave: 5,
|
||||
dash: { speed: 340, dur: 0.45, cd: 2.6 }
|
||||
},
|
||||
splitter: {
|
||||
id: 'splitter', art: 'ptero', hp: 60, speed: 85, radius: 18, contact: 14, value: 2, cost: 30,
|
||||
coin: [2, 4], color: '#7ba3a8', score: 25, fromWave: 6,
|
||||
splits: { into: 'mini', count: 3 }
|
||||
},
|
||||
mini: {
|
||||
id: 'mini', art: 'pteroChick', hp: 14, speed: 165, radius: 9, contact: 7, value: 0.5, cost: 0,
|
||||
coin: [1, 1], color: '#93b7bb', score: 6, noSpawn: true
|
||||
},
|
||||
eliteGrunt: {
|
||||
id: 'eliteGrunt', art: 'alphaRaptor', hp: 110, speed: 105, radius: 17, contact: 16, value: 3, cost: 45,
|
||||
coin: [6, 9], color: '#b0432e', score: 50, elite: true, fromWave: 8
|
||||
}
|
||||
};
|
||||
|
||||
US.BOSSES = [
|
||||
{
|
||||
id: 'bruiser', art: 'rex', name: 'REX THE ELDER',
|
||||
hp: 1600, speed: 80, radius: 44, contact: 30,
|
||||
color: '#7e4022', score: 500, coin: [40, 60],
|
||||
patterns: ['charge', 'spawn']
|
||||
},
|
||||
{
|
||||
id: 'artillery', art: 'spino', name: 'SPINO OF THE REEDS',
|
||||
hp: 2000, speed: 55, radius: 46, contact: 24,
|
||||
color: '#4f6f52', score: 650, coin: [50, 75],
|
||||
patterns: ['radial', 'aimed', 'spiral']
|
||||
},
|
||||
{
|
||||
id: 'hydra', art: 'giga', name: 'GIGA BROODMOTHER',
|
||||
hp: 2600, speed: 70, radius: 42, contact: 26,
|
||||
color: '#55514e', score: 800, coin: [60, 90],
|
||||
patterns: ['split', 'charge']
|
||||
}
|
||||
];
|
||||
|
||||
/* ---------------- Upgrade cards ---------------- */
|
||||
// apply(p) mutates player stats; stack limits where noted.
|
||||
US.CARDS = [
|
||||
{ id: 'dmg', rarity: 'common', max: 99, icon: '🦷', title: 'Sharpened Fangs', desc: '+25% damage / sát thương', apply: function (s) { s.dmgMul += 0.25; } },
|
||||
{ id: 'rof', rarity: 'common', max: 99, icon: '🖐', title: 'Quick Hands', desc: '+20% fire rate / tốc độ bắn', apply: function (s) { s.rofMul += 0.2; } },
|
||||
{ id: 'hp', rarity: 'common', max: 99, icon: '🦴', title: 'Thick Hide', desc: '+25 max HP & heal 50 / hồi 50', apply: function (s) { s.maxHp += 25; }, onPick: function (pl) { pl.hp = Math.min(pl.stats.maxHp, pl.hp + 50); } },
|
||||
{ id: 'speed', rarity: 'common', max: 5, icon: '👣', title: 'Fleet Foot', desc: '+15% move speed / di chuyển', apply: function (s) { s.speedMul += 0.15; } },
|
||||
{ id: 'multi', rarity: 'epic', max: 4, icon: '🔱', title: 'Twin Arms', desc: '+1 projectile / thêm 1 phát', apply: function (s) { s.pellets += 1; } },
|
||||
{ id: 'pierce', rarity: 'rare', max: 4, icon: '➶', title: 'Skewer', desc: 'Bullets pierce +1 / xuyên thêm 1', apply: function (s) { s.pierce += 1; } },
|
||||
{ id: 'crit', rarity: 'rare', max: 6, icon: '✦', title: 'Deadeye', desc: '+10% crit chance / chí mạng', apply: function (s) { s.crit += 0.10; } },
|
||||
{ id: 'critdmg', rarity: 'rare', max: 4, icon: '✸', title: 'Executioner', desc: '+50% crit damage / sát thương chí mạng', apply: function (s) { s.critMul += 0.5; } },
|
||||
{ id: 'leech', rarity: 'epic', max: 4, icon: '🩸', title: 'Red Harvest', desc: 'Heal 3% of damage dealt / hút máu', apply: function (s) { s.lifesteal += 0.03; } },
|
||||
{ id: 'bspd', rarity: 'common', max: 4, icon: '💪', title: 'Strong Arm', desc: '+30% bullet speed / đạn bay nhanh', apply: function (s) { s.bulletSpeedMul += 0.3; } },
|
||||
{ id: 'magnet', rarity: 'common', max: 4, icon: '🧲', title: 'Hoarder', desc: '+80 pickup range / hút vật phẩm', apply: function (s) { s.magnet += 80; } },
|
||||
{ id: 'boom', rarity: 'epic', max: 3, icon: '🔥', title: 'Fire Bombs', desc: 'Bullets explode on hit / đạn nổ', apply: function (s) { s.explosive += 1; } },
|
||||
{ id: 'rico', rarity: 'epic', max: 2, icon: '🔄', title: 'Ricochet', desc: 'Bullets bounce to a new target / đạn nảy', apply: function (s) { s.ricochet += 1; } },
|
||||
{ id: 'bomb', rarity: 'rare', max: 99, icon: '💣', title: 'Bundle of Fire', desc: '+2 bombs now / nhận 2 bom', apply: function () {}, onPick: function (pl) { pl.bombs += 2; } },
|
||||
{ id: 'ultcharge', rarity: 'rare', max: 4, icon: '🌟', title: 'Ancestor Spirit', desc: '+40% ultimate charge rate / nạp chiêu nhanh', apply: function (s) { s.ultRate += 0.4; } },
|
||||
{ id: 'ultnow', rarity: 'epic', max: 99, icon: '☄', title: 'Spirit Call', desc: 'Ultimate fully charged / chiêu cuối đầy', apply: function () {}, onPick: function (pl) { pl.ult = 100; } },
|
||||
{ id: 'greed', rarity: 'common', max: 4, icon: '🪙', title: 'Shiny Things', desc: '+35% coins earned / nhiều vàng hơn', apply: function (s) { s.coinMul += 0.35; } },
|
||||
{ id: 'regen', rarity: 'rare', max: 4, icon: '🌿', title: "Healer's Ritual", desc: 'Regenerate +1.5 HP/s / hồi máu', apply: function (s) { s.regen += 1.5; } },
|
||||
{ id: 'thorns', rarity: 'rare', max: 3, icon: '🌵', title: 'Spiky Hide', desc: 'Damages enemies that touch you / gai phản đòn', apply: function (s) { s.thorns += 25; } },
|
||||
{ id: 'bigbullets', rarity: 'rare', max: 3, icon: '⬤', title: 'Boulder Shots', desc: '+40% bullet size & +10% damage / đạn to', apply: function (s) { s.bulletSizeMul += 0.4; s.dmgMul += 0.1; } }
|
||||
];
|
||||
|
||||
/* ---------------- Permanent upgrades (spend amber, keep forever) ------ */
|
||||
US.UPGRADES = {
|
||||
hp: { id: 'hp', max: 5, base: 120, per: '+10 máu tối đa / cấp',
|
||||
vi: 'Tim Sói Đầu', en: 'Wolf-Heart', apply: function (st, lv) { st.maxHp += lv * 10; } },
|
||||
dmg: { id: 'dmg', max: 5, base: 180, per: '+6% sát thương / cấp',
|
||||
vi: 'Nọc Rắn Hổ', en: 'Viper Fangs', apply: function (st, lv) { st.dmgMul += lv * 0.06; } },
|
||||
speed: { id: 'speed', max: 5, base: 150, per: '+3% tốc chạy / cấp',
|
||||
vi: 'Gót Gió', en: 'Wind Heels', apply: function (st, lv) { st.speedMul += lv * 0.03; } },
|
||||
ult: { id: 'ult', max: 5, base: 200, per: '+10% hồi chiêu ultimate / cấp',
|
||||
vi: 'Mặt Trời Trẻ', en: 'Young Sun', apply: function (st, lv) { st.ultRate = (st.ultRate || 1) + lv * 0.1; } },
|
||||
coin: { id: 'coin', max: 5, base: 160, per: '+5% hổ phách nhặt được / cấp',
|
||||
vi: 'Mắt Kền Kền', en: 'Vulture Eye', apply: function (st, lv) { st.coinMul = (st.coinMul || 1) + lv * 0.05; } },
|
||||
bombs: { id: 'bombs', max: 2, base: 400, per: '+1 bom khởi đầu / cấp',
|
||||
vi: 'Túi Phù Thủy', en: 'Shaman Pouch', apply: function (st, lv) { st.startBombs = (st.startBombs || 0) + lv; } }
|
||||
};
|
||||
|
||||
/* ---------------- Bestiary: every beast of the valley ------------------ */
|
||||
// goal kills to complete an entry; reward paid in ngọn lửa (gems)
|
||||
US.BESTIARY = {
|
||||
grunt: { name: { en: 'Green Raptor', vi: 'Raptor Xanh Lục' }, goal: 15, gem: 1,
|
||||
lore: { en: 'Fast pack hunter. Never alone.', vi: 'Săn theo bầy, nhanh như gió. Không bao giờ đi một mình.' } },
|
||||
runner: { name: { en: 'Compy Scout', vi: 'Compy Dothám' }, goal: 20, gem: 1,
|
||||
lore: { en: 'Small, curious, bites your ankle.', vi: 'Nhỏ mà tinh, chuyên cắn gót chân.' } },
|
||||
spitter: { name: { en: 'Dilophosaurus', vi: 'Khủng Long Có Mào' }, goal: 12, gem: 1,
|
||||
lore: { en: 'Spits venom from beyond spear range.', vi: 'Phun nọc từ xa hơn tầm giáo ném.' } },
|
||||
tank: { name: { en: 'Triceratops', vi: 'Triceratops' }, goal: 8, gem: 2,
|
||||
lore: { en: 'A living battering ram with three horns.', vi: 'Con đập sống ba sừng, tường thành di động.' } },
|
||||
stego: { name: { en: 'Stegosaurus', vi: 'Stegosaurus' }, goal: 8, gem: 2,
|
||||
lore: { en: 'Its tail spikes end arguments.', vi: 'Đuôi chùy bốn gai — kết thúc mọi tranh luận.' } },
|
||||
pachy: { name: { en: 'Pachycephalosaurus', vi: 'Khủng Long Đầu Đội' }, goal: 10, gem: 2,
|
||||
lore: { en: 'Charges head-first. The skull is a weapon.', vi: 'Lao đầu như búa đá — hộp sọ là vũ khí.' } },
|
||||
splitter: { name: { en: 'Pteranodon', vi: 'Pteranodon' }, goal: 10, gem: 2,
|
||||
lore: { en: 'Dive-bombers. Break one, three scream back.', vi: 'Chúng lao xuống từ trời. Đánh một, ba con gào lại.' } },
|
||||
mini: { name: { en: 'Ptero Chick', vi: 'P Con' }, goal: 25, gem: 1,
|
||||
lore: { en: 'Hatched angry. Flies angrier.', vi: 'Nở ra đã giận, bay lên còn giận hơn.' } },
|
||||
eliteGrunt: { name: { en: 'Alpha Raptor', vi: 'Raptor Đầu Đàn' }, goal: 6, gem: 3,
|
||||
lore: { en: 'Scarred leader of the pack.', vi: 'Thủ lĩnh đầy sẹo của bầy xanh.' } },
|
||||
bruiser: { name: { en: 'REX THE ELDER', vi: 'REX GIÀ' }, goal: 3, gem: 5,
|
||||
lore: { en: 'The valley remembers his footsteps.', vi: 'Cả thung lũng nhớ tiếng bước chân của hắn.' } },
|
||||
artillery: { name: { en: 'SPINO THE TIDE', vi: 'SPINO THỦY TRIỀU' }, goal: 3, gem: 5,
|
||||
lore: { en: 'A sail that blocks out the sun.', vi: 'Chiếc buồng chặn cả mặt trời.' } },
|
||||
hydra: { name: { en: 'GIGA THE BROOD', vi: 'GIGA MA VẬT CHÚNG' }, goal: 3, gem: 8,
|
||||
lore: { en: 'Kill it and it multiplies. Kill those too.', vi: 'Giết nó thì nó sinh thêm. Giết nốt những đứa sinh ra.' } }
|
||||
};
|
||||
|
||||
/* ---------------- Daily quests (pick 3/day by date seed) --------------- */
|
||||
// type hooks fired by the game: kill, collect, wave, ult, boss
|
||||
US.QUEST_POOL = [
|
||||
{ id: 'kill30', type: 'kill', goal: 30, gem: 1, coins: 80,
|
||||
text: { en: 'Slay 30 beasts', vi: 'Hạ 30 con thú' } },
|
||||
{ id: 'kill80', type: 'kill', goal: 80, gem: 2, coins: 200,
|
||||
text: { en: 'Slay 80 beasts', vi: 'Hạ 80 con thú' } },
|
||||
{ id: 'amber12', type: 'collect', goal: 12, gem: 1, coins: 60,
|
||||
text: { en: 'Collect 12 amber nuggets', vi: 'Nhặt 12 viên hổ phách' } },
|
||||
{ id: 'wave5', type: 'wave', goal: 5, gem: 2, coins: 150,
|
||||
text: { en: 'Reach wave 5 in one run', vi: 'Đạt wave 5 trong một trận' } },
|
||||
{ id: 'ult3', type: 'ult', goal: 3, gem: 1, coins: 70,
|
||||
text: { en: 'Unleash 3 ultimates', vi: 'Phóng 3 lần chiêu cuối' } },
|
||||
{ id: 'boss1', type: 'boss', goal: 1, gem: 3, coins: 250,
|
||||
text: { en: 'Defeat a boss', vi: 'Hạ 1 trùm khủng long' } }
|
||||
];
|
||||
|
||||
/* ---------------- Achievements ----------------------------------------- */
|
||||
US.ACHIEVEMENTS = {
|
||||
firstBlood: { gem: 1, test: function (sv, g) { return sv.stats.totalKills >= 1; },
|
||||
text: { en: 'First Blood — slay your first beast', vi: 'Máu Đầu — hạ con thú đầu tiên' } },
|
||||
slayer100: { gem: 2, test: function (sv) { return sv.stats.totalKills >= 100; },
|
||||
text: { en: 'Beast Slayer — 100 kills total', vi: 'Thợ Săn — tổng 100 mạng' } },
|
||||
slayer500: { gem: 4, test: function (sv) { return sv.stats.totalKills >= 500; },
|
||||
text: { en: 'Apex Hunter — 500 kills total', vi: 'Thợ Săn Bậc Nhất — tổng 500 mạng' } },
|
||||
wave5: { gem: 1, test: function (sv) { return sv.bestWave >= 5; },
|
||||
text: { en: 'Face the Elder — reach wave 5', vi: 'Đối mặt Cố Nhân — đạt wave 5' } },
|
||||
wave10: { gem: 2, test: function (sv) { return sv.bestWave >= 10; },
|
||||
text: { en: 'Deep Jungle — reach wave 10', vi: 'Rừng Sâu — đạt wave 10' } },
|
||||
wave25: { gem: 5, test: function (sv) { return sv.bestWave >= 25; },
|
||||
text: { en: 'Valley Conqueror — finish wave 25', vi: 'Chinh Phục Thung Lũng — qua wave 25' } },
|
||||
combo20: { gem: 2, test: function (g) { return g && g.bestCombo >= 20; },
|
||||
text: { en: 'On Fire Master — combo ×20', vi: 'Bậc Thầy Bùng Cháy — combo ×20' } },
|
||||
rich: { gem: 2, test: function (sv) { return sv.stats.totalAmber >= 300; },
|
||||
text: { en: 'Amber Hoard — gather 300 amber', vi: 'Kho Hổ Phách — gom 300 hổ phách' } },
|
||||
petParent: { gem: 3, test: function (sv) { return !!sv.pet; },
|
||||
text: { en: 'Pack Parent — hatch a raptor egg', vi: 'Cha Mẹ Bầy Đàn — ấp trứng raptor' } },
|
||||
endless10: { gem: 4, test: function (sv) { return sv.bestWave >= 30; },
|
||||
text: { en: 'Beyond the Endless — wave 30+', vi: 'Vượt Vô Tận — wave 30+' } }
|
||||
};
|
||||
|
||||
US.RARITY_COLOR = { common: '#9c8a72', rare: '#58a55c', epic: '#ff8a3d' };
|
||||
})();
|
||||
Reference in New Issue
Block a user