User prompt
Make the zombies and the bullet more slow
Code edit (1 edits merged)
Please save this source code
User prompt
Monster Gold Rush
Initial prompt
Make a game, let there be 3 types of monsters, let them follow me, each of them has different features, ranged, rangeless, attacking from underground, and so on, so that I can kill them and collect gold from them, I can buy something from the store at the top right with the gold that comes out, and the store mechanics are like this, if I buy one of those three items, the other items will go randomly, make the passives of the items yourself, but be unique.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Burrower monster
var Burrower = Container.expand(function () {
var self = Container.call(this);
var burrowerGfx = self.attachAsset('burrower', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = burrowerGfx.width / 2;
self.speed = 10;
self.hp = 1;
self.type = 'burrower';
self.burrowTimer = 0;
self.burrowInterval = 90 + Math.floor(Math.random() * 60);
self.burrowed = false;
self.update = function () {
if (self.burrowed) {
self.burrowTimer--;
if (self.burrowTimer <= 0) {
// Unburrow
self.burrowed = false;
burrowerGfx.alpha = 1;
self.burrowTimer = self.burrowInterval;
}
// Move fast toward hero
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > 0.1) {
self.x += dx / d * (self.speed * 2.2);
self.y += dy / d * (self.speed * 2.2);
}
} else {
self.burrowTimer--;
if (self.burrowTimer <= 0) {
// Burrow
self.burrowed = true;
burrowerGfx.alpha = 0.3;
self.burrowTimer = 36 + Math.floor(Math.random() * 24);
}
// Move toward hero
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > 0.1) {
self.x += dx / d * self.speed;
self.y += dy / d * self.speed;
}
}
};
self.flash = function () {
tween(burrowerGfx, {
alpha: 0.1
}, {
duration: 40,
onFinish: function onFinish() {
tween(burrowerGfx, {
alpha: self.burrowed ? 0.3 : 1
}, {
duration: 40
});
}
});
};
return self;
});
// Chaser monster
var Chaser = Container.expand(function () {
var self = Container.call(this);
var chaserGfx = self.attachAsset('chaser', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = chaserGfx.width / 2;
self.speed = 7.5;
self.hp = 2;
self.type = 'chaser';
self.update = function () {
// Move toward hero
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > 0.1) {
self.x += dx / d * self.speed;
self.y += dy / d * self.speed;
}
};
self.flash = function () {
tween(chaserGfx, {
alpha: 0.3
}, {
duration: 60,
onFinish: function onFinish() {
tween(chaserGfx, {
alpha: 1
}, {
duration: 60
});
}
});
};
return self;
});
// Gold coin
var Gold = Container.expand(function () {
var self = Container.call(this);
var goldGfx = self.attachAsset('gold', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = goldGfx.width / 2;
self.vx = 0;
self.vy = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Slow down
self.vx *= 0.92;
self.vy *= 0.92;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGfx = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = heroGfx.width / 2;
self.speed = 22;
self.shootCooldown = 0;
self.shootInterval = 18; // frames
self.invuln = 0;
self.hp = 3;
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
if (self.invuln > 0) self.invuln--;
};
// Flash when hit
self.flash = function () {
tween(heroGfx, {
alpha: 0.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(heroGfx, {
alpha: 1
}, {
duration: 80
});
}
});
};
return self;
});
// Hero bullet
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('herobullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletGfx.width / 2;
self.speed = 38;
self.dirX = 0;
self.dirY = -1;
self.damage = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Monster bullet (spit)
var MonsterBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('herobullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGfx.tint = 0x7cfd3a;
self.radius = bulletGfx.width / 2;
self.speed = 16;
self.dirX = 0;
self.dirY = 1;
self.damage = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Spitter monster
var Spitter = Container.expand(function () {
var self = Container.call(this);
var spitterGfx = self.attachAsset('spitter', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = spitterGfx.width / 2;
self.speed = 4.5;
self.hp = 2;
self.type = 'spitter';
self.shootCooldown = 0;
self.shootInterval = 60 + Math.floor(Math.random() * 30);
self.update = function () {
// Keep distance from hero, shoot
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > 400) {
self.x += dx / d * self.speed;
self.y += dy / d * self.speed;
} else if (d < 320) {
self.x -= dx / d * self.speed;
self.y -= dy / d * self.speed;
}
if (self.shootCooldown > 0) self.shootCooldown--;else {
// Spit at hero
var spit = new MonsterBullet();
spit.x = self.x;
spit.y = self.y;
var tx = hero.x - self.x;
var ty = hero.y - self.y;
var td = Math.sqrt(tx * tx + ty * ty);
spit.dirX = td > 0.1 ? tx / td : 0;
spit.dirY = td > 0.1 ? ty / td : 0;
spit.speed = 16;
spit.radius = 32;
monsterBullets.push(spit);
game.addChild(spit);
self.shootCooldown = self.shootInterval;
}
};
self.flash = function () {
tween(spitterGfx, {
alpha: 0.3
}, {
duration: 60,
onFinish: function onFinish() {
tween(spitterGfx, {
alpha: 1
}, {
duration: 60
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Sound effects
// Store button highlight
// Store button background
// Gold coin
// Hero bullet
// Burrower monster
// Spitter monster
// Chaser monster
// Hero
// Global variables
var hero = new Hero();
var heroBullets = [];
var monsters = [];
var monsterBullets = [];
var golds = [];
var gold = 0;
var storeBtns = [];
var passives = [];
var availablePassives = [{
id: 'firerate',
name: 'Rapid Fire',
desc: 'Shoot much faster.',
apply: function apply() {
hero.shootInterval = Math.max(6, hero.shootInterval - 8);
}
}, {
id: 'pierce',
name: 'Piercing Shots',
desc: 'Bullets pierce through 1 extra enemy.',
apply: function apply() {
passives.push({
id: 'pierce',
stacks: 1
});
}
}, {
id: 'lifesteal',
name: 'Lifesteal',
desc: 'Gain 1 HP every 8 gold picked up.',
apply: function apply() {
passives.push({
id: 'lifesteal',
stacks: 1,
counter: 0
});
}
}, {
id: 'spread',
name: 'Spread Shot',
desc: 'Shoot 3 bullets in a spread.',
apply: function apply() {
passives.push({
id: 'spread',
stacks: 1
});
}
}, {
id: 'regen',
name: 'Regeneration',
desc: 'Regenerate 1 HP every 12 seconds.',
apply: function apply() {
passives.push({
id: 'regen',
stacks: 1,
timer: 0
});
}
}, {
id: 'speed',
name: 'Speed Boost',
desc: 'Move much faster.',
apply: function apply() {
hero.speed += 7;
}
}, {
id: 'bigshot',
name: 'Big Bullets',
desc: 'Bullets are much larger.',
apply: function apply() {
passives.push({
id: 'bigshot',
stacks: 1
});
}
}, {
id: 'goldboost',
name: 'Gold Magnet',
desc: 'Gold is attracted to you.',
apply: function apply() {
passives.push({
id: 'goldboost',
stacks: 1
});
}
}, {
id: 'maxhp',
name: 'Max HP Up',
desc: 'Increase max HP by 1.',
apply: function apply() {
hero.hp++;
}
}];
// Store state
var storeChoices = [];
function getRandomPassive(excludeIds) {
var pool = [];
for (var i = 0; i < availablePassives.length; ++i) {
var id = availablePassives[i].id;
var already = false;
for (var j = 0; j < excludeIds.length; ++j) {
if (excludeIds[j] === id) {
already = true;
break;
}
}
if (!already) pool.push(availablePassives[i]);
}
if (pool.length === 0) pool = availablePassives;
return pool[Math.floor(Math.random() * pool.length)];
}
function rerollStore() {
storeChoices = [];
var exclude = [];
for (var i = 0; i < 3; ++i) {
var p = getRandomPassive(exclude);
storeChoices.push(p);
exclude.push(p.id);
}
updateStoreBtns();
}
function updateStoreBtns() {
for (var i = 0; i < storeBtns.length; ++i) {
var btn = storeBtns[i];
var p = storeChoices[i];
btn.passive = p;
btn.txt.setText(p.name);
btn.desc.setText(p.desc);
btn.price.setText("5");
}
}
// GUI
var goldTxt = new Text2('0', {
size: 90,
fill: 0xFFE066
});
goldTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(goldTxt);
var hpTxt = new Text2('â¤â¤â¤', {
size: 90,
fill: 0xFF3A3A
});
hpTxt.anchor.set(0, 0);
LK.gui.top.addChild(hpTxt);
// Store buttons (top right, below gold)
for (var i = 0; i < 3; ++i) {
var btn = new Container();
var bg = btn.attachAsset('storebtn', {
anchorX: 1,
anchorY: 0
});
btn.x = 2048 - 40;
btn.y = 160 + i * 140;
btn.txt = new Text2('', {
size: 54,
fill: 0xFFE066
});
btn.txt.anchor.set(1, 0);
btn.txt.x = 380;
btn.txt.y = 12;
btn.addChild(btn.txt);
btn.desc = new Text2('', {
size: 36,
fill: 0xCCCCCC
});
btn.desc.anchor.set(1, 0);
btn.desc.x = 380;
btn.desc.y = 62;
btn.addChild(btn.desc);
btn.price = new Text2('5', {
size: 48,
fill: 0xFFD700
});
btn.price.anchor.set(0, 0);
btn.price.x = 20;
btn.price.y = 30;
btn.addChild(btn.price);
btn.index = i;
storeBtns.push(btn);
game.addChild(btn);
}
// Place hero in center
hero.x = 2048 / 2;
hero.y = 2732 / 2;
game.addChild(hero);
// Initial monsters
function spawnMonster() {
var edge = Math.floor(Math.random() * 4);
var x, y;
if (edge === 0) {
// top
x = 200 + Math.random() * (2048 - 400);
y = -80;
} else if (edge === 1) {
// bottom
x = 200 + Math.random() * (2048 - 400);
y = 2732 + 80;
} else if (edge === 2) {
// left
x = -80;
y = 200 + Math.random() * (2732 - 400);
} else {
// right
x = 2048 + 80;
y = 200 + Math.random() * (2732 - 400);
}
var t = Math.random();
var m;
if (t < 0.4) m = new Chaser();else if (t < 0.7) m = new Spitter();else m = new Burrower();
m.x = x;
m.y = y;
monsters.push(m);
game.addChild(m);
}
for (var i = 0; i < 5; ++i) spawnMonster();
// Store logic
rerollStore();
// Dragging
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to screen
var r = hero.radius;
dragNode.x = Math.max(r, Math.min(2048 - r, x));
dragNode.y = Math.max(r, Math.min(2732 - r, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Check store buttons first
for (var i = 0; i < storeBtns.length; ++i) {
var btn = storeBtns[i];
var bx = btn.x - 420,
by = btn.y,
bw = 420,
bh = 120;
if (x >= bx && x <= bx + bw && y >= by && y <= by + bh) {
// Buy
if (gold >= 5) {
gold -= 5;
goldTxt.setText(gold);
btn.passive.apply();
LK.getSound('buy').play();
rerollStore();
}
return;
}
}
// Drag hero
dragNode = hero;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Shoot logic
function shootAt(tx, ty) {
if (hero.shootCooldown > 0) return;
hero.shootCooldown = hero.shootInterval;
var dx = tx - hero.x;
var dy = ty - hero.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < 0.1) {
dx = 0;
dy = -1;
d = 1;
}
var spread = 0;
var n = 1;
var big = false;
for (var i = 0; i < passives.length; ++i) {
if (passives[i].id === 'spread') n = 3;
if (passives[i].id === 'bigshot') big = true;
}
for (var i = 0; i < n; ++i) {
var b = new HeroBullet();
b.x = hero.x;
b.y = hero.y;
var angle = Math.atan2(dy, dx);
if (n > 1) angle += (i - 1) * 0.18;
b.dirX = Math.cos(angle);
b.dirY = Math.sin(angle);
if (big) {
b.scaleX = b.scaleY = 1.7;
b.radius *= 1.7;
}
heroBullets.push(b);
game.addChild(b);
}
LK.getSound('shoot').play();
}
// Touch anywhere to shoot
game.on('down', function (x, y, obj) {
// Don't shoot if clicking store
for (var i = 0; i < storeBtns.length; ++i) {
var btn = storeBtns[i];
var bx = btn.x - 420,
by = btn.y,
bw = 420,
bh = 120;
if (x >= bx && x <= bx + bw && y >= by && y <= by + bh) return;
}
shootAt(x, y);
});
// Helper: collision
function circlesIntersect(a, b) {
var dx = a.x - b.x,
dy = a.y - b.y;
var r = (a.radius || 40) + (b.radius || 40);
return dx * dx + dy * dy < r * r;
}
// Update GUI
function updateHP() {
var s = '';
for (var i = 0; i < hero.hp; ++i) s += 'â¤';
hpTxt.setText(s);
}
// Main update
game.update = function () {
// Hero update
hero.update();
// Regen passive
for (var i = 0; i < passives.length; ++i) {
if (passives[i].id === 'regen') {
passives[i].timer++;
if (passives[i].timer >= 60 * 12) {
hero.hp++;
updateHP();
passives[i].timer = 0;
}
}
}
// Hero bullets
for (var i = heroBullets.length - 1; i >= 0; --i) {
var b = heroBullets[i];
b.update();
// Offscreen
if (b.x < -60 || b.x > 2048 + 60 || b.y < -60 || b.y > 2732 + 60) {
b.destroy();
heroBullets.splice(i, 1);
continue;
}
// Hit monster
var pierced = false;
for (var j = monsters.length - 1; j >= 0; --j) {
var m = monsters[j];
if (m.type === 'burrower' && m.burrowed) continue;
if (circlesIntersect(b, m)) {
m.hp -= b.damage;
m.flash();
LK.getSound('hit').play();
if (m.hp <= 0) {
// Drop gold
var g = new Gold();
g.x = m.x;
g.y = m.y;
g.vx = (Math.random() - 0.5) * 8;
g.vy = (Math.random() - 0.5) * 8;
golds.push(g);
game.addChild(g);
LK.getSound('monsterdie').play();
m.destroy();
monsters.splice(j, 1);
}
// Pierce?
var pierce = 0;
for (var k = 0; k < passives.length; ++k) if (passives[k].id === 'pierce') pierce++;
if (pierce > 0 && !pierced) {
b.damage--;
pierced = true;
if (b.damage <= 0) {
b.destroy();
heroBullets.splice(i, 1);
}
} else {
b.destroy();
heroBullets.splice(i, 1);
}
break;
}
}
}
// Monster bullets
for (var i = monsterBullets.length - 1; i >= 0; --i) {
var b = monsterBullets[i];
b.update();
if (b.x < -60 || b.x > 2048 + 60 || b.y < -60 || b.y > 2732 + 60) {
b.destroy();
monsterBullets.splice(i, 1);
continue;
}
// Hit hero
if (hero.invuln === 0 && circlesIntersect(b, hero)) {
hero.hp--;
updateHP();
hero.invuln = 40;
hero.flash();
b.destroy();
monsterBullets.splice(i, 1);
if (hero.hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
// Monsters
for (var i = monsters.length - 1; i >= 0; --i) {
var m = monsters[i];
m.update();
// Hit hero
if (hero.invuln === 0 && circlesIntersect(m, hero)) {
hero.hp--;
updateHP();
hero.invuln = 40;
hero.flash();
if (hero.hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
// Gold
for (var i = golds.length - 1; i >= 0; --i) {
var g = golds[i];
g.update();
// Magnet passive
for (var j = 0; j < passives.length; ++j) {
if (passives[j].id === 'goldboost') {
var dx = hero.x - g.x,
dy = hero.y - g.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < 600) {
g.vx += dx / d * 2.5;
g.vy += dy / d * 2.5;
}
}
}
// Pick up
if (circlesIntersect(g, hero)) {
gold++;
goldTxt.setText(gold);
LK.getSound('pickup').play();
// Lifesteal
for (var j = 0; j < passives.length; ++j) {
if (passives[j].id === 'lifesteal') {
passives[j].counter++;
if (passives[j].counter >= 8) {
hero.hp++;
updateHP();
passives[j].counter = 0;
}
}
}
g.destroy();
golds.splice(i, 1);
}
}
// Store button highlight
for (var i = 0; i < storeBtns.length; ++i) {
var btn = storeBtns[i];
var bx = btn.x - 420,
by = btn.y,
bw = 420,
bh = 120;
if (game.lastDown && game.lastDown.x >= bx && game.lastDown.x <= bx + bw && game.lastDown.y >= by && game.lastDown.y <= by + bh) {
if (!btn.hl) {
btn.hl = btn.attachAsset('storebtnhl', {
anchorX: 1,
anchorY: 0
});
btn.setChildIndex(btn.hl, 0);
}
} else {
if (btn.hl) {
btn.hl.destroy();
btn.hl = null;
}
}
}
// Spawn monsters
if (LK.ticks % 60 === 0 && monsters.length < 12) {
spawnMonster();
}
};
// Track last down for store highlight
game.lastDown = null;
game.on('down', function (x, y, obj) {
game.lastDown = {
x: x,
y: y
};
});
game.on('up', function (x, y, obj) {
game.lastDown = null;
}); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,775 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Burrower monster
+var Burrower = Container.expand(function () {
+ var self = Container.call(this);
+ var burrowerGfx = self.attachAsset('burrower', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = burrowerGfx.width / 2;
+ self.speed = 10;
+ self.hp = 1;
+ self.type = 'burrower';
+ self.burrowTimer = 0;
+ self.burrowInterval = 90 + Math.floor(Math.random() * 60);
+ self.burrowed = false;
+ self.update = function () {
+ if (self.burrowed) {
+ self.burrowTimer--;
+ if (self.burrowTimer <= 0) {
+ // Unburrow
+ self.burrowed = false;
+ burrowerGfx.alpha = 1;
+ self.burrowTimer = self.burrowInterval;
+ }
+ // Move fast toward hero
+ var dx = hero.x - self.x;
+ var dy = hero.y - self.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d > 0.1) {
+ self.x += dx / d * (self.speed * 2.2);
+ self.y += dy / d * (self.speed * 2.2);
+ }
+ } else {
+ self.burrowTimer--;
+ if (self.burrowTimer <= 0) {
+ // Burrow
+ self.burrowed = true;
+ burrowerGfx.alpha = 0.3;
+ self.burrowTimer = 36 + Math.floor(Math.random() * 24);
+ }
+ // Move toward hero
+ var dx = hero.x - self.x;
+ var dy = hero.y - self.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d > 0.1) {
+ self.x += dx / d * self.speed;
+ self.y += dy / d * self.speed;
+ }
+ }
+ };
+ self.flash = function () {
+ tween(burrowerGfx, {
+ alpha: 0.1
+ }, {
+ duration: 40,
+ onFinish: function onFinish() {
+ tween(burrowerGfx, {
+ alpha: self.burrowed ? 0.3 : 1
+ }, {
+ duration: 40
+ });
+ }
+ });
+ };
+ return self;
+});
+// Chaser monster
+var Chaser = Container.expand(function () {
+ var self = Container.call(this);
+ var chaserGfx = self.attachAsset('chaser', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = chaserGfx.width / 2;
+ self.speed = 7.5;
+ self.hp = 2;
+ self.type = 'chaser';
+ self.update = function () {
+ // Move toward hero
+ var dx = hero.x - self.x;
+ var dy = hero.y - self.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d > 0.1) {
+ self.x += dx / d * self.speed;
+ self.y += dy / d * self.speed;
+ }
+ };
+ self.flash = function () {
+ tween(chaserGfx, {
+ alpha: 0.3
+ }, {
+ duration: 60,
+ onFinish: function onFinish() {
+ tween(chaserGfx, {
+ alpha: 1
+ }, {
+ duration: 60
+ });
+ }
+ });
+ };
+ return self;
+});
+// Gold coin
+var Gold = Container.expand(function () {
+ var self = Container.call(this);
+ var goldGfx = self.attachAsset('gold', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = goldGfx.width / 2;
+ self.vx = 0;
+ self.vy = 0;
+ self.update = function () {
+ self.x += self.vx;
+ self.y += self.vy;
+ // Slow down
+ self.vx *= 0.92;
+ self.vy *= 0.92;
+ };
+ return self;
+});
+// Hero class
+var Hero = Container.expand(function () {
+ var self = Container.call(this);
+ var heroGfx = self.attachAsset('hero', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = heroGfx.width / 2;
+ self.speed = 22;
+ self.shootCooldown = 0;
+ self.shootInterval = 18; // frames
+ self.invuln = 0;
+ self.hp = 3;
+ self.update = function () {
+ if (self.shootCooldown > 0) self.shootCooldown--;
+ if (self.invuln > 0) self.invuln--;
+ };
+ // Flash when hit
+ self.flash = function () {
+ tween(heroGfx, {
+ alpha: 0.3
+ }, {
+ duration: 80,
+ onFinish: function onFinish() {
+ tween(heroGfx, {
+ alpha: 1
+ }, {
+ duration: 80
+ });
+ }
+ });
+ };
+ return self;
+});
+// Hero bullet
+var HeroBullet = Container.expand(function () {
+ var self = Container.call(this);
+ var bulletGfx = self.attachAsset('herobullet', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = bulletGfx.width / 2;
+ self.speed = 38;
+ self.dirX = 0;
+ self.dirY = -1;
+ self.damage = 1;
+ self.update = function () {
+ self.x += self.dirX * self.speed;
+ self.y += self.dirY * self.speed;
+ };
+ return self;
+});
+// Monster bullet (spit)
+var MonsterBullet = Container.expand(function () {
+ var self = Container.call(this);
+ var bulletGfx = self.attachAsset('herobullet', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ bulletGfx.tint = 0x7cfd3a;
+ self.radius = bulletGfx.width / 2;
+ self.speed = 16;
+ self.dirX = 0;
+ self.dirY = 1;
+ self.damage = 1;
+ self.update = function () {
+ self.x += self.dirX * self.speed;
+ self.y += self.dirY * self.speed;
+ };
+ return self;
+});
+// Spitter monster
+var Spitter = Container.expand(function () {
+ var self = Container.call(this);
+ var spitterGfx = self.attachAsset('spitter', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = spitterGfx.width / 2;
+ self.speed = 4.5;
+ self.hp = 2;
+ self.type = 'spitter';
+ self.shootCooldown = 0;
+ self.shootInterval = 60 + Math.floor(Math.random() * 30);
+ self.update = function () {
+ // Keep distance from hero, shoot
+ var dx = hero.x - self.x;
+ var dy = hero.y - self.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d > 400) {
+ self.x += dx / d * self.speed;
+ self.y += dy / d * self.speed;
+ } else if (d < 320) {
+ self.x -= dx / d * self.speed;
+ self.y -= dy / d * self.speed;
+ }
+ if (self.shootCooldown > 0) self.shootCooldown--;else {
+ // Spit at hero
+ var spit = new MonsterBullet();
+ spit.x = self.x;
+ spit.y = self.y;
+ var tx = hero.x - self.x;
+ var ty = hero.y - self.y;
+ var td = Math.sqrt(tx * tx + ty * ty);
+ spit.dirX = td > 0.1 ? tx / td : 0;
+ spit.dirY = td > 0.1 ? ty / td : 0;
+ spit.speed = 16;
+ spit.radius = 32;
+ monsterBullets.push(spit);
+ game.addChild(spit);
+ self.shootCooldown = self.shootInterval;
+ }
+ };
+ self.flash = function () {
+ tween(spitterGfx, {
+ alpha: 0.3
+ }, {
+ duration: 60,
+ onFinish: function onFinish() {
+ tween(spitterGfx, {
+ alpha: 1
+ }, {
+ duration: 60
+ });
+ }
+ });
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
+ backgroundColor: 0x181818
+});
+
+/****
+* Game Code
+****/
+// Sound effects
+// Store button highlight
+// Store button background
+// Gold coin
+// Hero bullet
+// Burrower monster
+// Spitter monster
+// Chaser monster
+// Hero
+// Global variables
+var hero = new Hero();
+var heroBullets = [];
+var monsters = [];
+var monsterBullets = [];
+var golds = [];
+var gold = 0;
+var storeBtns = [];
+var passives = [];
+var availablePassives = [{
+ id: 'firerate',
+ name: 'Rapid Fire',
+ desc: 'Shoot much faster.',
+ apply: function apply() {
+ hero.shootInterval = Math.max(6, hero.shootInterval - 8);
+ }
+}, {
+ id: 'pierce',
+ name: 'Piercing Shots',
+ desc: 'Bullets pierce through 1 extra enemy.',
+ apply: function apply() {
+ passives.push({
+ id: 'pierce',
+ stacks: 1
+ });
+ }
+}, {
+ id: 'lifesteal',
+ name: 'Lifesteal',
+ desc: 'Gain 1 HP every 8 gold picked up.',
+ apply: function apply() {
+ passives.push({
+ id: 'lifesteal',
+ stacks: 1,
+ counter: 0
+ });
+ }
+}, {
+ id: 'spread',
+ name: 'Spread Shot',
+ desc: 'Shoot 3 bullets in a spread.',
+ apply: function apply() {
+ passives.push({
+ id: 'spread',
+ stacks: 1
+ });
+ }
+}, {
+ id: 'regen',
+ name: 'Regeneration',
+ desc: 'Regenerate 1 HP every 12 seconds.',
+ apply: function apply() {
+ passives.push({
+ id: 'regen',
+ stacks: 1,
+ timer: 0
+ });
+ }
+}, {
+ id: 'speed',
+ name: 'Speed Boost',
+ desc: 'Move much faster.',
+ apply: function apply() {
+ hero.speed += 7;
+ }
+}, {
+ id: 'bigshot',
+ name: 'Big Bullets',
+ desc: 'Bullets are much larger.',
+ apply: function apply() {
+ passives.push({
+ id: 'bigshot',
+ stacks: 1
+ });
+ }
+}, {
+ id: 'goldboost',
+ name: 'Gold Magnet',
+ desc: 'Gold is attracted to you.',
+ apply: function apply() {
+ passives.push({
+ id: 'goldboost',
+ stacks: 1
+ });
+ }
+}, {
+ id: 'maxhp',
+ name: 'Max HP Up',
+ desc: 'Increase max HP by 1.',
+ apply: function apply() {
+ hero.hp++;
+ }
+}];
+// Store state
+var storeChoices = [];
+function getRandomPassive(excludeIds) {
+ var pool = [];
+ for (var i = 0; i < availablePassives.length; ++i) {
+ var id = availablePassives[i].id;
+ var already = false;
+ for (var j = 0; j < excludeIds.length; ++j) {
+ if (excludeIds[j] === id) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) pool.push(availablePassives[i]);
+ }
+ if (pool.length === 0) pool = availablePassives;
+ return pool[Math.floor(Math.random() * pool.length)];
+}
+function rerollStore() {
+ storeChoices = [];
+ var exclude = [];
+ for (var i = 0; i < 3; ++i) {
+ var p = getRandomPassive(exclude);
+ storeChoices.push(p);
+ exclude.push(p.id);
+ }
+ updateStoreBtns();
+}
+function updateStoreBtns() {
+ for (var i = 0; i < storeBtns.length; ++i) {
+ var btn = storeBtns[i];
+ var p = storeChoices[i];
+ btn.passive = p;
+ btn.txt.setText(p.name);
+ btn.desc.setText(p.desc);
+ btn.price.setText("5");
+ }
+}
+// GUI
+var goldTxt = new Text2('0', {
+ size: 90,
+ fill: 0xFFE066
+});
+goldTxt.anchor.set(1, 0);
+LK.gui.topRight.addChild(goldTxt);
+var hpTxt = new Text2('â¤â¤â¤', {
+ size: 90,
+ fill: 0xFF3A3A
+});
+hpTxt.anchor.set(0, 0);
+LK.gui.top.addChild(hpTxt);
+// Store buttons (top right, below gold)
+for (var i = 0; i < 3; ++i) {
+ var btn = new Container();
+ var bg = btn.attachAsset('storebtn', {
+ anchorX: 1,
+ anchorY: 0
+ });
+ btn.x = 2048 - 40;
+ btn.y = 160 + i * 140;
+ btn.txt = new Text2('', {
+ size: 54,
+ fill: 0xFFE066
+ });
+ btn.txt.anchor.set(1, 0);
+ btn.txt.x = 380;
+ btn.txt.y = 12;
+ btn.addChild(btn.txt);
+ btn.desc = new Text2('', {
+ size: 36,
+ fill: 0xCCCCCC
+ });
+ btn.desc.anchor.set(1, 0);
+ btn.desc.x = 380;
+ btn.desc.y = 62;
+ btn.addChild(btn.desc);
+ btn.price = new Text2('5', {
+ size: 48,
+ fill: 0xFFD700
+ });
+ btn.price.anchor.set(0, 0);
+ btn.price.x = 20;
+ btn.price.y = 30;
+ btn.addChild(btn.price);
+ btn.index = i;
+ storeBtns.push(btn);
+ game.addChild(btn);
+}
+// Place hero in center
+hero.x = 2048 / 2;
+hero.y = 2732 / 2;
+game.addChild(hero);
+// Initial monsters
+function spawnMonster() {
+ var edge = Math.floor(Math.random() * 4);
+ var x, y;
+ if (edge === 0) {
+ // top
+ x = 200 + Math.random() * (2048 - 400);
+ y = -80;
+ } else if (edge === 1) {
+ // bottom
+ x = 200 + Math.random() * (2048 - 400);
+ y = 2732 + 80;
+ } else if (edge === 2) {
+ // left
+ x = -80;
+ y = 200 + Math.random() * (2732 - 400);
+ } else {
+ // right
+ x = 2048 + 80;
+ y = 200 + Math.random() * (2732 - 400);
+ }
+ var t = Math.random();
+ var m;
+ if (t < 0.4) m = new Chaser();else if (t < 0.7) m = new Spitter();else m = new Burrower();
+ m.x = x;
+ m.y = y;
+ monsters.push(m);
+ game.addChild(m);
+}
+for (var i = 0; i < 5; ++i) spawnMonster();
+// Store logic
+rerollStore();
+// Dragging
+var dragNode = null;
+function handleMove(x, y, obj) {
+ if (dragNode) {
+ // Clamp to screen
+ var r = hero.radius;
+ dragNode.x = Math.max(r, Math.min(2048 - r, x));
+ dragNode.y = Math.max(r, Math.min(2732 - r, y));
+ }
+}
+game.move = handleMove;
+game.down = function (x, y, obj) {
+ // Check store buttons first
+ for (var i = 0; i < storeBtns.length; ++i) {
+ var btn = storeBtns[i];
+ var bx = btn.x - 420,
+ by = btn.y,
+ bw = 420,
+ bh = 120;
+ if (x >= bx && x <= bx + bw && y >= by && y <= by + bh) {
+ // Buy
+ if (gold >= 5) {
+ gold -= 5;
+ goldTxt.setText(gold);
+ btn.passive.apply();
+ LK.getSound('buy').play();
+ rerollStore();
+ }
+ return;
+ }
+ }
+ // Drag hero
+ dragNode = hero;
+ handleMove(x, y, obj);
+};
+game.up = function (x, y, obj) {
+ dragNode = null;
+};
+// Shoot logic
+function shootAt(tx, ty) {
+ if (hero.shootCooldown > 0) return;
+ hero.shootCooldown = hero.shootInterval;
+ var dx = tx - hero.x;
+ var dy = ty - hero.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d < 0.1) {
+ dx = 0;
+ dy = -1;
+ d = 1;
+ }
+ var spread = 0;
+ var n = 1;
+ var big = false;
+ for (var i = 0; i < passives.length; ++i) {
+ if (passives[i].id === 'spread') n = 3;
+ if (passives[i].id === 'bigshot') big = true;
+ }
+ for (var i = 0; i < n; ++i) {
+ var b = new HeroBullet();
+ b.x = hero.x;
+ b.y = hero.y;
+ var angle = Math.atan2(dy, dx);
+ if (n > 1) angle += (i - 1) * 0.18;
+ b.dirX = Math.cos(angle);
+ b.dirY = Math.sin(angle);
+ if (big) {
+ b.scaleX = b.scaleY = 1.7;
+ b.radius *= 1.7;
+ }
+ heroBullets.push(b);
+ game.addChild(b);
+ }
+ LK.getSound('shoot').play();
+}
+// Touch anywhere to shoot
+game.on('down', function (x, y, obj) {
+ // Don't shoot if clicking store
+ for (var i = 0; i < storeBtns.length; ++i) {
+ var btn = storeBtns[i];
+ var bx = btn.x - 420,
+ by = btn.y,
+ bw = 420,
+ bh = 120;
+ if (x >= bx && x <= bx + bw && y >= by && y <= by + bh) return;
+ }
+ shootAt(x, y);
+});
+// Helper: collision
+function circlesIntersect(a, b) {
+ var dx = a.x - b.x,
+ dy = a.y - b.y;
+ var r = (a.radius || 40) + (b.radius || 40);
+ return dx * dx + dy * dy < r * r;
+}
+// Update GUI
+function updateHP() {
+ var s = '';
+ for (var i = 0; i < hero.hp; ++i) s += 'â¤';
+ hpTxt.setText(s);
+}
+// Main update
+game.update = function () {
+ // Hero update
+ hero.update();
+ // Regen passive
+ for (var i = 0; i < passives.length; ++i) {
+ if (passives[i].id === 'regen') {
+ passives[i].timer++;
+ if (passives[i].timer >= 60 * 12) {
+ hero.hp++;
+ updateHP();
+ passives[i].timer = 0;
+ }
+ }
+ }
+ // Hero bullets
+ for (var i = heroBullets.length - 1; i >= 0; --i) {
+ var b = heroBullets[i];
+ b.update();
+ // Offscreen
+ if (b.x < -60 || b.x > 2048 + 60 || b.y < -60 || b.y > 2732 + 60) {
+ b.destroy();
+ heroBullets.splice(i, 1);
+ continue;
+ }
+ // Hit monster
+ var pierced = false;
+ for (var j = monsters.length - 1; j >= 0; --j) {
+ var m = monsters[j];
+ if (m.type === 'burrower' && m.burrowed) continue;
+ if (circlesIntersect(b, m)) {
+ m.hp -= b.damage;
+ m.flash();
+ LK.getSound('hit').play();
+ if (m.hp <= 0) {
+ // Drop gold
+ var g = new Gold();
+ g.x = m.x;
+ g.y = m.y;
+ g.vx = (Math.random() - 0.5) * 8;
+ g.vy = (Math.random() - 0.5) * 8;
+ golds.push(g);
+ game.addChild(g);
+ LK.getSound('monsterdie').play();
+ m.destroy();
+ monsters.splice(j, 1);
+ }
+ // Pierce?
+ var pierce = 0;
+ for (var k = 0; k < passives.length; ++k) if (passives[k].id === 'pierce') pierce++;
+ if (pierce > 0 && !pierced) {
+ b.damage--;
+ pierced = true;
+ if (b.damage <= 0) {
+ b.destroy();
+ heroBullets.splice(i, 1);
+ }
+ } else {
+ b.destroy();
+ heroBullets.splice(i, 1);
+ }
+ break;
+ }
+ }
+ }
+ // Monster bullets
+ for (var i = monsterBullets.length - 1; i >= 0; --i) {
+ var b = monsterBullets[i];
+ b.update();
+ if (b.x < -60 || b.x > 2048 + 60 || b.y < -60 || b.y > 2732 + 60) {
+ b.destroy();
+ monsterBullets.splice(i, 1);
+ continue;
+ }
+ // Hit hero
+ if (hero.invuln === 0 && circlesIntersect(b, hero)) {
+ hero.hp--;
+ updateHP();
+ hero.invuln = 40;
+ hero.flash();
+ b.destroy();
+ monsterBullets.splice(i, 1);
+ if (hero.hp <= 0) {
+ LK.effects.flashScreen(0xff0000, 1000);
+ LK.showGameOver();
+ return;
+ }
+ }
+ }
+ // Monsters
+ for (var i = monsters.length - 1; i >= 0; --i) {
+ var m = monsters[i];
+ m.update();
+ // Hit hero
+ if (hero.invuln === 0 && circlesIntersect(m, hero)) {
+ hero.hp--;
+ updateHP();
+ hero.invuln = 40;
+ hero.flash();
+ if (hero.hp <= 0) {
+ LK.effects.flashScreen(0xff0000, 1000);
+ LK.showGameOver();
+ return;
+ }
+ }
+ }
+ // Gold
+ for (var i = golds.length - 1; i >= 0; --i) {
+ var g = golds[i];
+ g.update();
+ // Magnet passive
+ for (var j = 0; j < passives.length; ++j) {
+ if (passives[j].id === 'goldboost') {
+ var dx = hero.x - g.x,
+ dy = hero.y - g.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d < 600) {
+ g.vx += dx / d * 2.5;
+ g.vy += dy / d * 2.5;
+ }
+ }
+ }
+ // Pick up
+ if (circlesIntersect(g, hero)) {
+ gold++;
+ goldTxt.setText(gold);
+ LK.getSound('pickup').play();
+ // Lifesteal
+ for (var j = 0; j < passives.length; ++j) {
+ if (passives[j].id === 'lifesteal') {
+ passives[j].counter++;
+ if (passives[j].counter >= 8) {
+ hero.hp++;
+ updateHP();
+ passives[j].counter = 0;
+ }
+ }
+ }
+ g.destroy();
+ golds.splice(i, 1);
+ }
+ }
+ // Store button highlight
+ for (var i = 0; i < storeBtns.length; ++i) {
+ var btn = storeBtns[i];
+ var bx = btn.x - 420,
+ by = btn.y,
+ bw = 420,
+ bh = 120;
+ if (game.lastDown && game.lastDown.x >= bx && game.lastDown.x <= bx + bw && game.lastDown.y >= by && game.lastDown.y <= by + bh) {
+ if (!btn.hl) {
+ btn.hl = btn.attachAsset('storebtnhl', {
+ anchorX: 1,
+ anchorY: 0
+ });
+ btn.setChildIndex(btn.hl, 0);
+ }
+ } else {
+ if (btn.hl) {
+ btn.hl.destroy();
+ btn.hl = null;
+ }
+ }
+ }
+ // Spawn monsters
+ if (LK.ticks % 60 === 0 && monsters.length < 12) {
+ spawnMonster();
+ }
+};
+// Track last down for store highlight
+game.lastDown = null;
+game.on('down', function (x, y, obj) {
+ game.lastDown = {
+ x: x,
+ y: y
+ };
+});
+game.on('up', function (x, y, obj) {
+ game.lastDown = null;
});
\ No newline at end of file
a zombie but its half phantom. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a zombie. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a gold. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a spitter zombie. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a green zombi spitt. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a man with green tshirt and blue pant. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a man with green tshirt and blue pant walking up. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a sword . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat