User prompt
delete the characters bullets and the bullet items
User prompt
remove the character's bullet squeeze and remove the items that depend on it and replace them with other items, do not make any other changes
User prompt
split so that the character can hit more slowly
User prompt
as long as the character is moving, the character will start firing bullets at the nearest monster (character), not in a sweeping manner, but one by one in an intermittent manner
User prompt
as long as the character moves, the nearest monster will start firing bullets, not in a sweeping manner, but one by one, intermittently
User prompt
as long as the character moves, the monster closest to the character should start firing bullets, not in a sweeping pattern but in long, long bursts
User prompt
make the character bullets single, not like scanned
User prompt
make character bullets to hit one by one by one
User prompt
as long as I move the character, the bullets will automatically go to the nearest enemy and deal damage
User prompt
the character's bullets do damage for an automatic duration
User prompt
I think there's still a problem. I want to character to make the automatic shoot monsters for a while.
User prompt
I think there's still a problem. I want you to make the automatic shoot monsters for a while.
User prompt
I think there is a problem with the bullet mechanics, can you fix it? When I stop holding down, it squeezes the bullets.
User prompt
Can you get your market gui at the top of the middle, and I want you to make the special appearance and item names of the items a little more beautiful
User prompt
Can you fix the shops gui
User prompt
Set the Spitter to fire its bullets slower and the bullets will be slower
User prompt
İncrease the characters range
User prompt
Make it rarer for people other than the chaser to spawn
User prompt
The sword is not damage
User prompt
Can you make spitters spawn more rare
User prompt
Make an animation on the sword because I couldn't see if I was hitting the zombies or not, bidet slow down the spitter's bullets
User prompt
Endlessly point the sword at the zombies
User prompt
Reduce spawning of zombies and lower bullet speeds and zombie speeds
User prompt
Can you give the main character sword and a animation so ı can see the zombis get damages
User prompt
Make the bullets mooore slow
/****
* 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 = 2.2;
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 = 1.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
});
// Add sword asset
var swordGfx = self.attachAsset('herosword', {
anchorX: 0.1,
anchorY: 0.5,
x: heroGfx.width * 0.5 + 30,
y: 0,
rotation: 0
});
swordGfx.rotation = 0;
swordGfx.visible = true;
self.addChild(swordGfx);
self.radius = heroGfx.width / 2;
self.speed = 22;
self.shootCooldown = 0;
self.shootInterval = 18; // frames
self.invuln = 0;
self.hp = 3;
// Sword swing state
self.swordSwing = 0; // 0 = idle, >0 = animating
self.swordSwingDir = 1; // 1 = right, -1 = left
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
if (self.invuln > 0) self.invuln--;
// Always point sword at nearest monster, animate a subtle swing
var nearest = null;
var minDist = 999999;
for (var i = 0; i < monsters.length; ++i) {
var m = monsters[i];
if (m.type === 'burrower' && m.burrowed) continue;
var dx = m.x - self.x;
var dy = m.y - self.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
nearest = m;
}
}
if (nearest) {
var dx = nearest.x - self.x;
var dy = nearest.y - self.y;
var angle = Math.atan2(dy, dx);
// Animate a subtle swing: oscillate +/- 0.18 radians
var swing = Math.sin(LK.ticks / 10) * 0.18;
// Sword hit animation: if recently hit, flash sword
if (self.swordHitAnim && self.swordHitAnim > 0) {
// Animate sword: scale up and flash color
swordGfx.scaleX = 1.3 + 0.3 * Math.sin(self.swordHitAnim * Math.PI / 6);
swordGfx.scaleY = 1.3 + 0.3 * Math.sin(self.swordHitAnim * Math.PI / 6);
swordGfx.tint = 0xffe066;
self.swordHitAnim--;
if (self.swordHitAnim === 0) {
swordGfx.scaleX = 1;
swordGfx.scaleY = 1;
swordGfx.tint = 0xf5e663;
}
} else {
swordGfx.scaleX = 1;
swordGfx.scaleY = 1;
swordGfx.tint = 0xf5e663;
}
swordGfx.rotation = angle + swing;
// Position sword at edge of hero
var dist = heroGfx.width * 0.5 + 30;
swordGfx.x = Math.cos(angle) * dist;
swordGfx.y = Math.sin(angle) * dist;
} else {
// No monsters: point up, subtle swing
var angle = -Math.PI / 2;
var swing = Math.sin(LK.ticks / 10) * 0.18;
swordGfx.rotation = angle + swing;
var dist = heroGfx.width * 0.5 + 30;
swordGfx.x = Math.cos(angle) * dist;
swordGfx.y = Math.sin(angle) * dist;
}
};
// Flash when hit
self.flash = function () {
tween(heroGfx, {
alpha: 0.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(heroGfx, {
alpha: 1
}, {
duration: 80
});
}
});
};
// Sword swing trigger
self.swingSword = function (dir) {
self.swordSwing = self.swordSwingMax = 10;
self.swordSwingDir = dir || 1;
swordGfx.rotation = 0;
};
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 = 3.5;
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 = 7;
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 = 1.1;
self.hp = 2;
self.type = 'spitter';
self.shootCooldown = 0;
self.shootInterval = 110 + Math.floor(Math.random() * 40); // fire much slower
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 = 3.2; // even slower bullet speed
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;
// Add a little sparkle/emoji to the name for beauty
var prettyName = "★ " + p.name + " ★";
btn.txt.setText(prettyName);
btn.shadow.setText(prettyName);
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 center, beautified)
for (var i = 0; i < 3; ++i) {
var btn = new Container();
var bg = btn.attachAsset('storebtn', {
anchorX: 0.5,
anchorY: 0
});
// Center horizontally, stack vertically with spacing
btn.x = 2048 / 2;
btn.y = 160 + i * 160;
// Add a subtle drop shadow effect by layering a slightly offset, darker text
btn.shadow = new Text2('', {
size: 64,
fill: 0x222222
});
btn.shadow.anchor.set(0.5, 0);
btn.shadow.x = 0 + 3;
btn.shadow.y = 10 + 3;
btn.addChild(btn.shadow);
// Beautified item name: larger, bold, gold color, centered
btn.txt = new Text2('', {
size: 64,
fill: 0xFFE066,
font: "Impact, 'Arial Black', Tahoma"
});
btn.txt.anchor.set(0.5, 0);
btn.txt.x = 0;
btn.txt.y = 10;
btn.addChild(btn.txt);
// Description: slightly larger, italic, silver color, centered
btn.desc = new Text2('', {
size: 40,
fill: 0xCCCCCC,
font: "italic 36px Tahoma"
});
btn.desc.anchor.set(0.5, 0);
btn.desc.x = 0;
btn.desc.y = 80;
btn.addChild(btn.desc);
// Price: gold coin color, bold, left side
btn.price = new Text2('5', {
size: 54,
fill: 0xFFD700,
font: "bold 48px Tahoma"
});
btn.price.anchor.set(0, 0);
btn.price.x = -180;
btn.price.y = 40;
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;
// Make Spitter and Burrower much more rare: Chaser 80%, Spitter 7%, Burrower 13%
if (t < 0.8) {
m = new Chaser();
} else if (t < 0.87) {
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();
// --- Sword swing animation and sword hit detection ---
if (typeof hero.swingSword === "function") {
// Direction: right if aiming right, left if aiming left
hero.swingSword(dx >= 0 ? 1 : -1);
// Sword hitbox: check monsters in a 180px arc in front of hero
var swordRange = 180;
var swordArc = Math.PI / 2; // 90 deg arc
var heroAngle = Math.atan2(dy, dx);
for (var j = monsters.length - 1; j >= 0; --j) {
var m = monsters[j];
if (m.type === 'burrower' && m.burrowed) continue;
var mx = m.x - hero.x,
my = m.y - hero.y;
var mdist = Math.sqrt(mx * mx + my * my);
if (mdist < swordRange) {
var mangle = Math.atan2(my, mx);
var diff = Math.abs((mangle - heroAngle + Math.PI * 3) % (Math.PI * 2) - Math.PI);
if (diff < swordArc / 2) {
m.hp -= 1;
m.flash();
LK.getSound('hit').play();
// --- Sword hit animation trigger ---
if (typeof hero.swordHitAnim === "undefined" || hero.swordHitAnim === 0) {
hero.swordHitAnim = 8; // frames of animation
} else {
hero.swordHitAnim = 8;
}
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);
}
}
}
}
}
}
// 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;
}
}
}
// --- Track hero movement for monster firing logic ---
if (typeof hero.lastX === "undefined") hero.lastX = hero.x;
if (typeof hero.lastY === "undefined") hero.lastY = hero.y;
var heroIsMoving = Math.abs(hero.x - hero.lastX) > 0.1 || Math.abs(hero.y - hero.lastY) > 0.1;
// Find nearest monster to hero
var nearestMonster = null;
var nearestDist = 999999;
for (var mi = 0; mi < monsters.length; ++mi) {
var mtest = monsters[mi];
if (mtest.type === 'burrower' && mtest.burrowed) continue;
var dx = mtest.x - hero.x;
var dy = mtest.y - hero.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < nearestDist) {
nearestDist = d;
nearestMonster = mtest;
}
}
// Nearest monster fires intermittently if hero is moving
if (heroIsMoving && nearestMonster) {
if (typeof nearestMonster.fireCooldown === "undefined") nearestMonster.fireCooldown = 0;
if (nearestMonster.fireCooldown > 0) {
nearestMonster.fireCooldown--;
} else {
// Only fire if not burrowed (for burrower), and not dead
if (!(nearestMonster.type === 'burrower' && nearestMonster.burrowed)) {
// Fire a bullet at hero
var spit = new MonsterBullet();
spit.x = nearestMonster.x;
spit.y = nearestMonster.y;
var tx = hero.x - nearestMonster.x;
var ty = hero.y - nearestMonster.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 = 3.2; // slow bullet
spit.radius = 32;
monsterBullets.push(spit);
game.addChild(spit);
// Set cooldown for intermittent fire (e.g. 36 frames = 0.6s)
nearestMonster.fireCooldown = 36 + Math.floor(Math.random() * 12);
}
}
} else if (nearestMonster && typeof nearestMonster.fireCooldown !== "undefined") {
// Reset cooldown if hero stops moving
nearestMonster.fireCooldown = 0;
}
// Update hero last position for next frame
hero.lastX = hero.x;
hero.lastY = hero.y;
// 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;
}
}
// --- Sword hit detection in update ---
if (m.type !== 'burrower' || !m.burrowed) {
// Sword hitbox: check monsters in a 180px arc in front of hero
var swordRange = 180;
var swordArc = Math.PI / 2; // 90 deg arc
// Find sword angle (same as in Hero.update)
var nearest = null;
var minDist = 999999;
for (var mi = 0; mi < monsters.length; ++mi) {
var mm = monsters[mi];
if (mm.type === 'burrower' && mm.burrowed) continue;
var dx = mm.x - hero.x;
var dy = mm.y - hero.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < minDist) {
minDist = d;
nearest = mm;
}
}
var swordAngle = -Math.PI / 2;
if (nearest) {
swordAngle = Math.atan2(nearest.y - hero.x, nearest.x - hero.x);
// Correction: should be (nearest.y - hero.y, nearest.x - hero.x)
swordAngle = Math.atan2(nearest.y - hero.y, nearest.x - hero.x);
}
var mx = m.x - hero.x,
my = m.y - hero.y;
var mdist = Math.sqrt(mx * mx + my * my);
if (mdist < swordRange) {
var mangle = Math.atan2(my, mx);
var diff = Math.abs((mangle - swordAngle + Math.PI * 3) % (Math.PI * 2) - Math.PI);
if (diff < swordArc / 2) {
m.hp -= 1;
m.flash();
LK.getSound('hit').play();
// --- Sword hit animation trigger ---
if (typeof hero.swordHitAnim === "undefined" || hero.swordHitAnim === 0) {
hero.swordHitAnim = 8; // frames of animation
} else {
hero.swordHitAnim = 8;
}
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(i, 1);
continue;
}
}
}
}
}
// 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;
// Only show highlight if pointer is over the button
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
});
// Always ensure highlight is above the background but below text
btn.setChildIndex(btn.hl, 1);
}
} else {
if (btn.hl) {
btn.hl.destroy();
btn.hl = null;
}
}
}
// Spawn monsters
if (LK.ticks % 120 === 0 && monsters.length < 7) {
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
@@ -337,18 +337,18 @@
/****
* Game Code
****/
-// Global variables
-// Hero
-// Chaser monster
-// Spitter monster
-// Burrower monster
-// Hero bullet
-// Gold coin
-// Store button background
-// Store button highlight
// 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 = [];
@@ -623,10 +623,8 @@
}
// Drag hero
dragNode = hero;
handleMove(x, y, obj);
- // Prevent shooting when starting drag (fixes bullet squeeze)
- hero.shootCooldown = hero.shootInterval;
};
game.up = function (x, y, obj) {
dragNode = null;
};
@@ -737,82 +735,8 @@
hpTxt.setText(s);
}
// Main update
game.update = function () {
- // --- Closest monster burst fire logic ---
- // Track if hero is moving (by comparing last position)
- if (typeof hero.lastX === "undefined") hero.lastX = hero.x;
- if (typeof hero.lastY === "undefined") hero.lastY = hero.y;
- var heroMoving = Math.abs(hero.x - hero.lastX) > 0.5 || Math.abs(hero.y - hero.lastY) > 0.5;
- // Find closest monster
- var closestMonster = null;
- var closestDist = 999999;
- for (var i = 0; i < monsters.length; ++i) {
- var m = monsters[i];
- if (m.type === 'burrower' && m.burrowed) continue;
- var dx = m.x - hero.x;
- var dy = m.y - hero.y;
- var d = Math.sqrt(dx * dx + dy * dy);
- if (d < closestDist) {
- closestDist = d;
- closestMonster = m;
- }
- }
- // Only one monster can burst fire at a time, and only if hero is moving
- if (closestMonster && heroMoving) {
- // Attach burst fire state to monster if not present
- if (typeof closestMonster.burstState === "undefined") {
- closestMonster.burstState = {
- burstTicks: 0,
- burstCooldown: 0
- };
- }
- var state = closestMonster.burstState;
- // If not currently bursting, start a long burst (e.g. 60-120 frames)
- if (state.burstTicks <= 0 && state.burstCooldown <= 0) {
- state.burstTicks = 90 + Math.floor(Math.random() * 60); // 1.5-2.5s burst
- state.burstCooldown = 90 + Math.floor(Math.random() * 60); // cooldown after burst
- }
- // If in burst, fire every 8 frames
- if (state.burstTicks > 0) {
- if (typeof state.burstFireTimer === "undefined") state.burstFireTimer = 0;
- state.burstFireTimer--;
- if (state.burstFireTimer <= 0) {
- // Fire a bullet at hero
- var spit = new MonsterBullet();
- spit.x = closestMonster.x;
- spit.y = closestMonster.y;
- var tx = hero.x - closestMonster.x;
- var ty = hero.y - closestMonster.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 = 5.2; // faster than spitter, but not too fast
- spit.radius = 32;
- monsterBullets.push(spit);
- game.addChild(spit);
- state.burstFireTimer = 8; // fire every 8 frames
- }
- state.burstTicks--;
- if (state.burstTicks <= 0) {
- state.burstCooldown = 90 + Math.floor(Math.random() * 60);
- }
- } else if (state.burstCooldown > 0) {
- state.burstCooldown--;
- }
- } else {
- // If hero is not moving, clear burst state for all monsters
- for (var i = 0; i < monsters.length; ++i) {
- if (typeof monsters[i].burstState !== "undefined") {
- monsters[i].burstState.burstTicks = 0;
- monsters[i].burstState.burstCooldown = 0;
- monsters[i].burstState.burstFireTimer = 0;
- }
- }
- }
- // Update hero last position
- hero.lastX = hero.x;
- hero.lastY = hero.y;
// Hero update
hero.update();
// Regen passive
for (var i = 0; i < passives.length; ++i) {
@@ -898,8 +822,58 @@
return;
}
}
}
+ // --- Track hero movement for monster firing logic ---
+ if (typeof hero.lastX === "undefined") hero.lastX = hero.x;
+ if (typeof hero.lastY === "undefined") hero.lastY = hero.y;
+ var heroIsMoving = Math.abs(hero.x - hero.lastX) > 0.1 || Math.abs(hero.y - hero.lastY) > 0.1;
+ // Find nearest monster to hero
+ var nearestMonster = null;
+ var nearestDist = 999999;
+ for (var mi = 0; mi < monsters.length; ++mi) {
+ var mtest = monsters[mi];
+ if (mtest.type === 'burrower' && mtest.burrowed) continue;
+ var dx = mtest.x - hero.x;
+ var dy = mtest.y - hero.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ if (d < nearestDist) {
+ nearestDist = d;
+ nearestMonster = mtest;
+ }
+ }
+ // Nearest monster fires intermittently if hero is moving
+ if (heroIsMoving && nearestMonster) {
+ if (typeof nearestMonster.fireCooldown === "undefined") nearestMonster.fireCooldown = 0;
+ if (nearestMonster.fireCooldown > 0) {
+ nearestMonster.fireCooldown--;
+ } else {
+ // Only fire if not burrowed (for burrower), and not dead
+ if (!(nearestMonster.type === 'burrower' && nearestMonster.burrowed)) {
+ // Fire a bullet at hero
+ var spit = new MonsterBullet();
+ spit.x = nearestMonster.x;
+ spit.y = nearestMonster.y;
+ var tx = hero.x - nearestMonster.x;
+ var ty = hero.y - nearestMonster.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 = 3.2; // slow bullet
+ spit.radius = 32;
+ monsterBullets.push(spit);
+ game.addChild(spit);
+ // Set cooldown for intermittent fire (e.g. 36 frames = 0.6s)
+ nearestMonster.fireCooldown = 36 + Math.floor(Math.random() * 12);
+ }
+ }
+ } else if (nearestMonster && typeof nearestMonster.fireCooldown !== "undefined") {
+ // Reset cooldown if hero stops moving
+ nearestMonster.fireCooldown = 0;
+ }
+ // Update hero last position for next frame
+ hero.lastX = hero.x;
+ hero.lastY = hero.y;
// Monsters
for (var i = monsters.length - 1; i >= 0; --i) {
var m = monsters[i];
m.update();
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