/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Boss class
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2,
tint: 0x6fd36f // zombie green
});
self.radius = 120;
self.speed = 2.2;
self.dirX = 0;
self.dirY = 0;
self.hp = 5;
self.maxHp = 5;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Cowboy (player) class
var Cowboy = Container.expand(function () {
var self = Container.call(this);
var cowboySprite = self.attachAsset('cowboy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 60; // for collision
self.whipCooldown = 0;
self.revolverCooldown = 0;
self.pumpUnlocked = false;
self.upgrades = 0;
self.revolverBaseCooldown = 600; // 10 seconds at 60fps
self.revolverCooldownTime = self.revolverBaseCooldown;
self.pumpCooldown = 0;
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x8fd38f // lighter zombie green
});
self.radius = 50;
self.speed = 2;
self.dirX = 0;
self.dirY = 0;
self.hp = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Pump-action bullet class (stronger, wider)
var PumpBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('pump_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 20;
self.speed = 22;
self.dirX = 0;
self.dirY = 0;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Revolver bullet class
var RevolverBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('revolver_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 15;
self.speed = 18;
self.dirX = 0;
self.dirY = 0;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Upgrade drop class
var UpgradeDrop = Container.expand(function () {
var self = Container.call(this);
var upgradeSprite = self.attachAsset('upgrade', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x9e2e2e // blood red for zombie theme
});
self.radius = 30;
self.update = function () {};
return self;
});
// Whip attack class (longer range, short-lived)
var WhipAttack = Container.expand(function () {
var self = Container.call(this);
var whipSprite = self.attachAsset('whip', {
anchorX: 0.0,
anchorY: 0.5
});
self.radius = 120; // longer range
self.dirX = 0;
self.dirY = 0;
self.lifetime = 10; // frames
self.update = function () {
self.lifetime--;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2e7d32 // Forest green
});
/****
* Game Code
****/
// Add a western zombie-themed background image (stretched to fit game area)
var bg = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: GAME_W / 100,
scaleY: GAME_H / 100,
tint: 0x6b4f2c // brownish tint for western feel
});
bg.x = GAME_W / 2;
bg.y = GAME_H / 2;
bg.alpha = 0.45; // subtle, not too strong
game.addChildAt(bg, 0);
// Play background music file number 17
LK.playMusic('17');
// Game area
// Cowboy (player)
// Whip (short range attack)
// Revolver bullet
// Enemy
// Upgrade drop
// Pump-action bullet
var GAME_W = 2048;
var GAME_H = 2732;
// Center position
var centerX = GAME_W / 2;
var centerY = GAME_H / 2;
// Player (fixed in center)
var cowboy = new Cowboy();
cowboy.x = centerX;
cowboy.y = centerY;
game.addChild(cowboy);
// Arrays for game objects
var enemies = [];
var bullets = [];
var whipAttacks = [];
var upgrades = [];
var boss = null;
var bossActive = false;
var bossSpawnTimer = 60 * 60; // 1 minute
// Score and UI
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Game time UI
var timeTxt = new Text2('Time: 0:00', {
size: 60,
fill: "#fff"
});
timeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timeTxt);
timeTxt.y = 70;
// Weapon cooldown UI
var revolverTxt = new Text2('Revolver: Ready', {
size: 60,
fill: "#fff"
});
revolverTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(revolverTxt);
revolverTxt.y = 130;
// Upgrade UI
var upgradeTxt = new Text2('Upgrades: 0/5', {
size: 60,
fill: 0xFFD700
});
upgradeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(upgradeTxt);
upgradeTxt.y = 200;
// Difficulty
var wave = 1;
var spawnTimer = 0;
var spawnInterval = 90; // frames
var enemySpeed = 2;
// Dragging
var dragNode = null;
// Joystick state
var joystick = {
active: false,
baseX: 0,
baseY: 0,
stickX: 0,
stickY: 0,
radius: 120,
dx: 0,
dy: 0
};
// Joystick graphics (simple circles)
var joystickBase = new Container();
var baseCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0x4e2e0e // dark brown for western theme
});
joystickBase.addChild(baseCircle);
joystickBase.alpha = 0.4;
joystickBase.visible = false;
game.addChild(joystickBase);
var joystickStick = new Container();
var stickCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7,
tint: 0x9e7b53 // lighter brown for stick
});
joystickStick.addChild(stickCircle);
joystickStick.alpha = 0.7;
joystickStick.visible = false;
game.addChild(joystickStick);
// Helper: distance
function dist(ax, ay, bx, by) {
var dx = ax - bx;
var dy = ay - by;
return Math.sqrt(dx * dx + dy * dy);
}
// Helper: clamp
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Helper: get direction vector
function getDir(ax, ay, bx, by) {
var dx = bx - ax;
var dy = by - ay;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return {
x: 0,
y: 0
};
return {
x: dx / len,
y: dy / len
};
}
// Helper: spawn enemy at random edge
function spawnEnemy() {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var ex, ey;
if (edge === 0) {
// top
ex = Math.random() * (GAME_W - 200) + 100;
ey = -80;
} else if (edge === 1) {
// right
ex = GAME_W + 80;
ey = Math.random() * (GAME_H - 200) + 100;
} else if (edge === 2) {
// bottom
ex = Math.random() * (GAME_W - 200) + 100;
ey = GAME_H + 80;
} else {
// left
ex = -80;
ey = Math.random() * (GAME_H - 200) + 100;
}
var enemy = new Enemy();
enemy.x = ex;
enemy.y = ey;
var dir = getDir(ex, ey, cowboy.x, cowboy.y);
enemy.dirX = dir.x;
enemy.dirY = dir.y;
enemy.speed = enemySpeed + Math.random() * 0.5 + (wave - 1) * 0.2;
enemy.hp = 1 + Math.floor(wave / 5);
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: spawn upgrade at (x, y)
function spawnUpgrade(x, y) {
var upg = new UpgradeDrop();
upg.x = x;
upg.y = y;
upgrades.push(upg);
game.addChild(upg);
}
// Handle movement (dragging cowboy)
function handleMove(x, y, obj) {
// No movement, cowboy is fixed in center
}
game.move = handleMove;
game.down = function (x, y, obj) {
// No movement, cowboy is fixed in center
};
game.up = function (x, y, obj) {
// No movement, cowboy is fixed in center
};
// Handle tap/attack (whip or revolver)
game.tap = function (x, y, obj) {
// Not used, as LK does not have tap event
};
// Touch: whip (short range, no cooldown)
game.down2 = function (x, y, obj) {
// Not used, as LK does not have down2 event
};
// Touch: fire revolver (if ready)
game.doubletap = function (x, y, obj) {
// Not used, as LK does not have doubletap event
};
// Whip attack: on quick tap (simulate with up event if not dragging)
game.up = function (x, y, obj) {
if (!dragNode) {
// Whip attack in direction of tap, no cooldown, longer range
var dir = getDir(cowboy.x, cowboy.y, x, y);
var whip = new WhipAttack();
whip.x = cowboy.x + dir.x * 120; // longer range
whip.y = cowboy.y + dir.y * 120;
whip.dirX = dir.x;
whip.dirY = dir.y;
whip.rotation = Math.atan2(dir.y, dir.x);
whipAttacks.push(whip);
game.addChild(whip);
// Play whip sound (file 11)
LK.getSound('11').play();
// Animate whip
tween(whip, {
alpha: 0
}, {
duration: 200,
easing: tween.linear,
onFinish: function onFinish() {
whip.alpha = 1;
}
});
}
dragNode = null;
};
// Fire revolver: on long press (simulate with two-finger tap or hold, but here use a button)
var fireBtn = new Text2('REVOLVER', {
size: 80,
fill: "#fff"
});
fireBtn.anchor.set(0.5, 0.5);
LK.gui.bottom.addChild(fireBtn);
fireBtn.y = -120;
fireBtn.x = 0;
fireBtn.interactive = true;
fireBtn.down = function (x, y, obj) {
if (cowboy.pumpUnlocked) {
// Fire pump-action (spread shot)
if (cowboy.pumpCooldown <= 0) {
for (var i = -1; i <= 1; i++) {
var dir = getDir(cowboy.x, cowboy.y, cowboy.x + Math.cos(game.lastAimAngle + i * 0.18) * 100, cowboy.y + Math.sin(game.lastAimAngle + i * 0.18) * 100);
var bullet = new PumpBullet();
bullet.x = cowboy.x + dir.x * 70;
bullet.y = cowboy.y + dir.y * 70;
bullet.dirX = dir.x;
bullet.dirY = dir.y;
bullet.rotation = Math.atan2(dir.y, dir.x);
bullets.push(bullet);
game.addChild(bullet);
}
cowboy.pumpCooldown = 90; // 1.5s
}
} else {
// Fire revolver
if (cowboy.revolverCooldown <= 0) {
var angle, dir;
// If boss is present, always aim at boss
if (bossActive && boss) {
var dx = boss.x - cowboy.x;
var dy = boss.y - cowboy.y;
angle = Math.atan2(dy, dx);
dir = {
x: Math.cos(angle),
y: Math.sin(angle)
};
} else {
// Aim at last direction of movement or default right
angle = game.lastAimAngle || 0;
dir = {
x: Math.cos(angle),
y: Math.sin(angle)
};
}
var bullet = new RevolverBullet();
bullet.x = cowboy.x + dir.x * 70;
bullet.y = cowboy.y + dir.y * 70;
bullet.dirX = dir.x;
bullet.dirY = dir.y;
bullet.rotation = Math.atan2(dir.y, dir.x);
bullets.push(bullet);
game.addChild(bullet);
// Play revolver sound (file 16)
LK.getSound('16').play();
cowboy.revolverCooldown = cowboy.revolverCooldownTime;
}
}
};
// Track last aim direction (for revolver)
game.lastAimAngle = 0;
game.move = function (x, y, obj) {
// Only update aim angle
var dx = x - cowboy.x;
var dy = y - cowboy.y;
if (dx * dx + dy * dy > 10 * 10) {
game.lastAimAngle = Math.atan2(dy, dx);
}
};
// Main update loop
game.update = function () {
// Cooldowns
if (cowboy.whipCooldown > 0) cowboy.whipCooldown--;
if (cowboy.revolverCooldown > 0) cowboy.revolverCooldown--;
if (cowboy.pumpCooldown > 0) cowboy.pumpCooldown--;
// No movement, cowboy is fixed in center
// Auto-aim revolver at nearest enemy
var nearestEnemy = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
var d = dist(cowboy.x, cowboy.y, e.x, e.y);
if (d < minDist) {
minDist = d;
nearestEnemy = e;
}
}
if (nearestEnemy) {
var aimDx = nearestEnemy.x - cowboy.x;
var aimDy = nearestEnemy.y - cowboy.y;
game.lastAimAngle = Math.atan2(aimDy, aimDx);
}
// Update UI
scoreTxt.setText(score);
// Update time UI
var totalSeconds = Math.floor(LK.ticks / 60);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var secondsStr = seconds < 10 ? "0" + seconds : "" + seconds;
timeTxt.setText("Time: " + minutes + ":" + secondsStr);
if (cowboy.pumpUnlocked) {
revolverTxt.setText('Pump-Action: ' + (cowboy.pumpCooldown > 0 ? (cowboy.pumpCooldown / 60).toFixed(1) + 's' : 'Ready'));
} else {
revolverTxt.setText('Revolver: ' + (cowboy.revolverCooldown > 0 ? (cowboy.revolverCooldown / 60).toFixed(1) + 's' : 'Ready'));
}
upgradeTxt.setText('Upgrades: ' + cowboy.upgrades + '/5');
// Spawn enemies
spawnTimer--;
if (spawnTimer <= 0) {
spawnEnemy();
spawnTimer = Math.max(30, spawnInterval - wave * 2);
}
// Boss spawn logic
if (!bossActive && LK.ticks > 0 && LK.ticks % (60 * 60) === 0) {
// Spawn boss at random edge
var edge = Math.floor(Math.random() * 4);
var bx, by;
if (edge === 0) {
bx = Math.random() * (GAME_W - 400) + 200;
by = -160;
} else if (edge === 1) {
bx = GAME_W + 160;
by = Math.random() * (GAME_H - 400) + 200;
} else if (edge === 2) {
bx = Math.random() * (GAME_W - 400) + 200;
by = GAME_H + 160;
} else {
bx = -160;
by = Math.random() * (GAME_H - 400) + 200;
}
boss = new Boss();
boss.x = bx;
boss.y = by;
var dir = getDir(boss.x, boss.y, cowboy.x, cowboy.y);
boss.dirX = dir.x;
boss.dirY = dir.y;
boss.hp = boss.maxHp = 10 + Math.floor(wave * 2.5);
boss.speed = enemySpeed + 0.7 + (wave - 1) * 0.25;
bossActive = true;
game.addChild(boss);
}
// Increase difficulty
if (LK.ticks % (60 * 60) === 0 && LK.ticks > 0) {
// every 1 minute
wave++;
enemySpeed += 0.3;
}
// No random upgrade spawn every 2 minutes; upgrades only from bosses
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Re-aim at player
var dir = getDir(e.x, e.y, cowboy.x, cowboy.y);
e.dirX = dir.x;
e.dirY = dir.y;
// Out of bounds
if (e.x < -200 || e.x > GAME_W + 200 || e.y < -200 || e.y > GAME_H + 200) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Collide with player
if (dist(e.x, e.y, cowboy.x, cowboy.y) < e.radius + cowboy.radius - 10) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
// Update boss if active
if (bossActive && boss) {
boss.update();
// Re-aim at player
var bdir = getDir(boss.x, boss.y, cowboy.x, cowboy.y);
boss.dirX = bdir.x;
boss.dirY = bdir.y;
// Out of bounds
if (boss.x < -300) boss.x = -300;
if (boss.x > GAME_W + 300) boss.x = GAME_W + 300;
if (boss.y < -300) boss.y = -300;
if (boss.y > GAME_H + 300) boss.y = GAME_H + 300;
// Collide with player
if (dist(boss.x, boss.y, cowboy.x, cowboy.y) < boss.radius + cowboy.radius - 20) {
LK.effects.flashScreen(0xff0000, 1200);
LK.showGameOver();
return;
}
// Boss defeat check is handled in bullet/whip collision
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Out of bounds
if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Hit enemy
var isRevolverBullet = b instanceof RevolverBullet;
var isPumpBullet = b instanceof PumpBullet;
var bossHit = false;
// Boss priority: if boss is present and bullet is revolver, check boss first
if (bossActive && boss && isRevolverBullet && dist(b.x, b.y, boss.x, boss.y) < b.radius + boss.radius) {
boss.hp--;
b.destroy();
bullets.splice(i, 1);
bossHit = true;
if (boss.hp <= 0) {
boss.destroy();
boss = null;
bossActive = false;
// Increase game speed
enemySpeed += 0.7;
wave++;
}
}
// Only allow revolver bullets to hit boss, and only if not already hit this frame
if (!bossHit) {
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (dist(b.x, b.y, e.x, e.y) < b.radius + e.radius) {
// Always die in one hit
// No upgrade drop from regular enemies
e.destroy();
enemies.splice(j, 1);
score++;
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// If boss is present and bullet is NOT a revolver bullet, skip boss damage
if (!bossHit && bossActive && boss && isPumpBullet && dist(b.x, b.y, boss.x, boss.y) < b.radius + boss.radius) {
// Pump bullets do NOT damage boss, just ignore
b.destroy();
bullets.splice(i, 1);
}
}
// Update whip attacks
for (var i = whipAttacks.length - 1; i >= 0; i--) {
var w = whipAttacks[i];
w.update();
// Hit enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (dist(w.x + w.dirX * 40, w.y + w.dirY * 40, e.x, e.y) < w.radius + e.radius) {
// Always die in one hit
e.destroy();
enemies.splice(j, 1);
score++;
}
}
// Hit boss
if (bossActive && boss && dist(w.x + w.dirX * 40, w.y + w.dirY * 40, boss.x, boss.y) < w.radius + boss.radius) {
// Whip damages boss (1 hp per hit), and pushes boss back
boss.hp--;
var pushStrength = 120;
boss.x += w.dirX * pushStrength;
boss.y += w.dirY * pushStrength;
// Optionally, add a little visual feedback (flash)
LK.effects.flashObject(boss, 0x00ffff, 200);
// If boss dies from whip, drop upgrades and remove boss
if (boss.hp <= 0) {
boss.destroy();
boss = null;
bossActive = false;
// Increase game speed
enemySpeed += 0.7;
wave++;
}
}
// Lifetime
if (w.lifetime <= 0) {
w.destroy();
whipAttacks.splice(i, 1);
}
}
// Update upgrades
for (var i = upgrades.length - 1; i >= 0; i--) {
var u = upgrades[i];
// Move toward player always
var dir = getDir(u.x, u.y, cowboy.x, cowboy.y);
u.x += dir.x * 10;
u.y += dir.y * 10;
// Collect
if (dist(u.x, u.y, cowboy.x, cowboy.y) < u.radius + cowboy.radius) {
cowboy.upgrades++;
// Each upgrade reduces revolver cooldown by 1.5s, min 4s (240 frames)
cowboy.revolverCooldownTime = Math.max(240, 600 - cowboy.upgrades * 90);
if (cowboy.upgrades >= 5) {
cowboy.pumpUnlocked = true;
}
u.destroy();
upgrades.splice(i, 1);
}
}
// Timed upgrade drop every 2 minutes (120s)
if (typeof upgradeTimer === "undefined") {
upgradeTimer = 60 * 120;
}
upgradeTimer--;
if (upgradeTimer <= 0) {
spawnUpgrade(cowboy.x, cowboy.y - 200); // spawn above player, flies to player
upgradeTimer = 60 * 120;
}
// Win condition: survive 5 minutes or reach 100 score
if (score >= 100 || LK.ticks > 60 * 60 * 5) {
LK.showYouWin();
return;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Boss class
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2,
tint: 0x6fd36f // zombie green
});
self.radius = 120;
self.speed = 2.2;
self.dirX = 0;
self.dirY = 0;
self.hp = 5;
self.maxHp = 5;
self.lastX = 0;
self.lastY = 0;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Cowboy (player) class
var Cowboy = Container.expand(function () {
var self = Container.call(this);
var cowboySprite = self.attachAsset('cowboy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 60; // for collision
self.whipCooldown = 0;
self.revolverCooldown = 0;
self.pumpUnlocked = false;
self.upgrades = 0;
self.revolverBaseCooldown = 600; // 10 seconds at 60fps
self.revolverCooldownTime = self.revolverBaseCooldown;
self.pumpCooldown = 0;
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x8fd38f // lighter zombie green
});
self.radius = 50;
self.speed = 2;
self.dirX = 0;
self.dirY = 0;
self.hp = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Pump-action bullet class (stronger, wider)
var PumpBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('pump_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 20;
self.speed = 22;
self.dirX = 0;
self.dirY = 0;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Revolver bullet class
var RevolverBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('revolver_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 15;
self.speed = 18;
self.dirX = 0;
self.dirY = 0;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
};
return self;
});
// Upgrade drop class
var UpgradeDrop = Container.expand(function () {
var self = Container.call(this);
var upgradeSprite = self.attachAsset('upgrade', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x9e2e2e // blood red for zombie theme
});
self.radius = 30;
self.update = function () {};
return self;
});
// Whip attack class (longer range, short-lived)
var WhipAttack = Container.expand(function () {
var self = Container.call(this);
var whipSprite = self.attachAsset('whip', {
anchorX: 0.0,
anchorY: 0.5
});
self.radius = 120; // longer range
self.dirX = 0;
self.dirY = 0;
self.lifetime = 10; // frames
self.update = function () {
self.lifetime--;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2e7d32 // Forest green
});
/****
* Game Code
****/
// Add a western zombie-themed background image (stretched to fit game area)
var bg = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: GAME_W / 100,
scaleY: GAME_H / 100,
tint: 0x6b4f2c // brownish tint for western feel
});
bg.x = GAME_W / 2;
bg.y = GAME_H / 2;
bg.alpha = 0.45; // subtle, not too strong
game.addChildAt(bg, 0);
// Play background music file number 17
LK.playMusic('17');
// Game area
// Cowboy (player)
// Whip (short range attack)
// Revolver bullet
// Enemy
// Upgrade drop
// Pump-action bullet
var GAME_W = 2048;
var GAME_H = 2732;
// Center position
var centerX = GAME_W / 2;
var centerY = GAME_H / 2;
// Player (fixed in center)
var cowboy = new Cowboy();
cowboy.x = centerX;
cowboy.y = centerY;
game.addChild(cowboy);
// Arrays for game objects
var enemies = [];
var bullets = [];
var whipAttacks = [];
var upgrades = [];
var boss = null;
var bossActive = false;
var bossSpawnTimer = 60 * 60; // 1 minute
// Score and UI
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Game time UI
var timeTxt = new Text2('Time: 0:00', {
size: 60,
fill: "#fff"
});
timeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timeTxt);
timeTxt.y = 70;
// Weapon cooldown UI
var revolverTxt = new Text2('Revolver: Ready', {
size: 60,
fill: "#fff"
});
revolverTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(revolverTxt);
revolverTxt.y = 130;
// Upgrade UI
var upgradeTxt = new Text2('Upgrades: 0/5', {
size: 60,
fill: 0xFFD700
});
upgradeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(upgradeTxt);
upgradeTxt.y = 200;
// Difficulty
var wave = 1;
var spawnTimer = 0;
var spawnInterval = 90; // frames
var enemySpeed = 2;
// Dragging
var dragNode = null;
// Joystick state
var joystick = {
active: false,
baseX: 0,
baseY: 0,
stickX: 0,
stickY: 0,
radius: 120,
dx: 0,
dy: 0
};
// Joystick graphics (simple circles)
var joystickBase = new Container();
var baseCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0x4e2e0e // dark brown for western theme
});
joystickBase.addChild(baseCircle);
joystickBase.alpha = 0.4;
joystickBase.visible = false;
game.addChild(joystickBase);
var joystickStick = new Container();
var stickCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7,
tint: 0x9e7b53 // lighter brown for stick
});
joystickStick.addChild(stickCircle);
joystickStick.alpha = 0.7;
joystickStick.visible = false;
game.addChild(joystickStick);
// Helper: distance
function dist(ax, ay, bx, by) {
var dx = ax - bx;
var dy = ay - by;
return Math.sqrt(dx * dx + dy * dy);
}
// Helper: clamp
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Helper: get direction vector
function getDir(ax, ay, bx, by) {
var dx = bx - ax;
var dy = by - ay;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return {
x: 0,
y: 0
};
return {
x: dx / len,
y: dy / len
};
}
// Helper: spawn enemy at random edge
function spawnEnemy() {
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var ex, ey;
if (edge === 0) {
// top
ex = Math.random() * (GAME_W - 200) + 100;
ey = -80;
} else if (edge === 1) {
// right
ex = GAME_W + 80;
ey = Math.random() * (GAME_H - 200) + 100;
} else if (edge === 2) {
// bottom
ex = Math.random() * (GAME_W - 200) + 100;
ey = GAME_H + 80;
} else {
// left
ex = -80;
ey = Math.random() * (GAME_H - 200) + 100;
}
var enemy = new Enemy();
enemy.x = ex;
enemy.y = ey;
var dir = getDir(ex, ey, cowboy.x, cowboy.y);
enemy.dirX = dir.x;
enemy.dirY = dir.y;
enemy.speed = enemySpeed + Math.random() * 0.5 + (wave - 1) * 0.2;
enemy.hp = 1 + Math.floor(wave / 5);
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: spawn upgrade at (x, y)
function spawnUpgrade(x, y) {
var upg = new UpgradeDrop();
upg.x = x;
upg.y = y;
upgrades.push(upg);
game.addChild(upg);
}
// Handle movement (dragging cowboy)
function handleMove(x, y, obj) {
// No movement, cowboy is fixed in center
}
game.move = handleMove;
game.down = function (x, y, obj) {
// No movement, cowboy is fixed in center
};
game.up = function (x, y, obj) {
// No movement, cowboy is fixed in center
};
// Handle tap/attack (whip or revolver)
game.tap = function (x, y, obj) {
// Not used, as LK does not have tap event
};
// Touch: whip (short range, no cooldown)
game.down2 = function (x, y, obj) {
// Not used, as LK does not have down2 event
};
// Touch: fire revolver (if ready)
game.doubletap = function (x, y, obj) {
// Not used, as LK does not have doubletap event
};
// Whip attack: on quick tap (simulate with up event if not dragging)
game.up = function (x, y, obj) {
if (!dragNode) {
// Whip attack in direction of tap, no cooldown, longer range
var dir = getDir(cowboy.x, cowboy.y, x, y);
var whip = new WhipAttack();
whip.x = cowboy.x + dir.x * 120; // longer range
whip.y = cowboy.y + dir.y * 120;
whip.dirX = dir.x;
whip.dirY = dir.y;
whip.rotation = Math.atan2(dir.y, dir.x);
whipAttacks.push(whip);
game.addChild(whip);
// Play whip sound (file 11)
LK.getSound('11').play();
// Animate whip
tween(whip, {
alpha: 0
}, {
duration: 200,
easing: tween.linear,
onFinish: function onFinish() {
whip.alpha = 1;
}
});
}
dragNode = null;
};
// Fire revolver: on long press (simulate with two-finger tap or hold, but here use a button)
var fireBtn = new Text2('REVOLVER', {
size: 80,
fill: "#fff"
});
fireBtn.anchor.set(0.5, 0.5);
LK.gui.bottom.addChild(fireBtn);
fireBtn.y = -120;
fireBtn.x = 0;
fireBtn.interactive = true;
fireBtn.down = function (x, y, obj) {
if (cowboy.pumpUnlocked) {
// Fire pump-action (spread shot)
if (cowboy.pumpCooldown <= 0) {
for (var i = -1; i <= 1; i++) {
var dir = getDir(cowboy.x, cowboy.y, cowboy.x + Math.cos(game.lastAimAngle + i * 0.18) * 100, cowboy.y + Math.sin(game.lastAimAngle + i * 0.18) * 100);
var bullet = new PumpBullet();
bullet.x = cowboy.x + dir.x * 70;
bullet.y = cowboy.y + dir.y * 70;
bullet.dirX = dir.x;
bullet.dirY = dir.y;
bullet.rotation = Math.atan2(dir.y, dir.x);
bullets.push(bullet);
game.addChild(bullet);
}
cowboy.pumpCooldown = 90; // 1.5s
}
} else {
// Fire revolver
if (cowboy.revolverCooldown <= 0) {
var angle, dir;
// If boss is present, always aim at boss
if (bossActive && boss) {
var dx = boss.x - cowboy.x;
var dy = boss.y - cowboy.y;
angle = Math.atan2(dy, dx);
dir = {
x: Math.cos(angle),
y: Math.sin(angle)
};
} else {
// Aim at last direction of movement or default right
angle = game.lastAimAngle || 0;
dir = {
x: Math.cos(angle),
y: Math.sin(angle)
};
}
var bullet = new RevolverBullet();
bullet.x = cowboy.x + dir.x * 70;
bullet.y = cowboy.y + dir.y * 70;
bullet.dirX = dir.x;
bullet.dirY = dir.y;
bullet.rotation = Math.atan2(dir.y, dir.x);
bullets.push(bullet);
game.addChild(bullet);
// Play revolver sound (file 16)
LK.getSound('16').play();
cowboy.revolverCooldown = cowboy.revolverCooldownTime;
}
}
};
// Track last aim direction (for revolver)
game.lastAimAngle = 0;
game.move = function (x, y, obj) {
// Only update aim angle
var dx = x - cowboy.x;
var dy = y - cowboy.y;
if (dx * dx + dy * dy > 10 * 10) {
game.lastAimAngle = Math.atan2(dy, dx);
}
};
// Main update loop
game.update = function () {
// Cooldowns
if (cowboy.whipCooldown > 0) cowboy.whipCooldown--;
if (cowboy.revolverCooldown > 0) cowboy.revolverCooldown--;
if (cowboy.pumpCooldown > 0) cowboy.pumpCooldown--;
// No movement, cowboy is fixed in center
// Auto-aim revolver at nearest enemy
var nearestEnemy = null;
var minDist = 999999;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
var d = dist(cowboy.x, cowboy.y, e.x, e.y);
if (d < minDist) {
minDist = d;
nearestEnemy = e;
}
}
if (nearestEnemy) {
var aimDx = nearestEnemy.x - cowboy.x;
var aimDy = nearestEnemy.y - cowboy.y;
game.lastAimAngle = Math.atan2(aimDy, aimDx);
}
// Update UI
scoreTxt.setText(score);
// Update time UI
var totalSeconds = Math.floor(LK.ticks / 60);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var secondsStr = seconds < 10 ? "0" + seconds : "" + seconds;
timeTxt.setText("Time: " + minutes + ":" + secondsStr);
if (cowboy.pumpUnlocked) {
revolverTxt.setText('Pump-Action: ' + (cowboy.pumpCooldown > 0 ? (cowboy.pumpCooldown / 60).toFixed(1) + 's' : 'Ready'));
} else {
revolverTxt.setText('Revolver: ' + (cowboy.revolverCooldown > 0 ? (cowboy.revolverCooldown / 60).toFixed(1) + 's' : 'Ready'));
}
upgradeTxt.setText('Upgrades: ' + cowboy.upgrades + '/5');
// Spawn enemies
spawnTimer--;
if (spawnTimer <= 0) {
spawnEnemy();
spawnTimer = Math.max(30, spawnInterval - wave * 2);
}
// Boss spawn logic
if (!bossActive && LK.ticks > 0 && LK.ticks % (60 * 60) === 0) {
// Spawn boss at random edge
var edge = Math.floor(Math.random() * 4);
var bx, by;
if (edge === 0) {
bx = Math.random() * (GAME_W - 400) + 200;
by = -160;
} else if (edge === 1) {
bx = GAME_W + 160;
by = Math.random() * (GAME_H - 400) + 200;
} else if (edge === 2) {
bx = Math.random() * (GAME_W - 400) + 200;
by = GAME_H + 160;
} else {
bx = -160;
by = Math.random() * (GAME_H - 400) + 200;
}
boss = new Boss();
boss.x = bx;
boss.y = by;
var dir = getDir(boss.x, boss.y, cowboy.x, cowboy.y);
boss.dirX = dir.x;
boss.dirY = dir.y;
boss.hp = boss.maxHp = 10 + Math.floor(wave * 2.5);
boss.speed = enemySpeed + 0.7 + (wave - 1) * 0.25;
bossActive = true;
game.addChild(boss);
}
// Increase difficulty
if (LK.ticks % (60 * 60) === 0 && LK.ticks > 0) {
// every 1 minute
wave++;
enemySpeed += 0.3;
}
// No random upgrade spawn every 2 minutes; upgrades only from bosses
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Re-aim at player
var dir = getDir(e.x, e.y, cowboy.x, cowboy.y);
e.dirX = dir.x;
e.dirY = dir.y;
// Out of bounds
if (e.x < -200 || e.x > GAME_W + 200 || e.y < -200 || e.y > GAME_H + 200) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Collide with player
if (dist(e.x, e.y, cowboy.x, cowboy.y) < e.radius + cowboy.radius - 10) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
// Update boss if active
if (bossActive && boss) {
boss.update();
// Re-aim at player
var bdir = getDir(boss.x, boss.y, cowboy.x, cowboy.y);
boss.dirX = bdir.x;
boss.dirY = bdir.y;
// Out of bounds
if (boss.x < -300) boss.x = -300;
if (boss.x > GAME_W + 300) boss.x = GAME_W + 300;
if (boss.y < -300) boss.y = -300;
if (boss.y > GAME_H + 300) boss.y = GAME_H + 300;
// Collide with player
if (dist(boss.x, boss.y, cowboy.x, cowboy.y) < boss.radius + cowboy.radius - 20) {
LK.effects.flashScreen(0xff0000, 1200);
LK.showGameOver();
return;
}
// Boss defeat check is handled in bullet/whip collision
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Out of bounds
if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Hit enemy
var isRevolverBullet = b instanceof RevolverBullet;
var isPumpBullet = b instanceof PumpBullet;
var bossHit = false;
// Boss priority: if boss is present and bullet is revolver, check boss first
if (bossActive && boss && isRevolverBullet && dist(b.x, b.y, boss.x, boss.y) < b.radius + boss.radius) {
boss.hp--;
b.destroy();
bullets.splice(i, 1);
bossHit = true;
if (boss.hp <= 0) {
boss.destroy();
boss = null;
bossActive = false;
// Increase game speed
enemySpeed += 0.7;
wave++;
}
}
// Only allow revolver bullets to hit boss, and only if not already hit this frame
if (!bossHit) {
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (dist(b.x, b.y, e.x, e.y) < b.radius + e.radius) {
// Always die in one hit
// No upgrade drop from regular enemies
e.destroy();
enemies.splice(j, 1);
score++;
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// If boss is present and bullet is NOT a revolver bullet, skip boss damage
if (!bossHit && bossActive && boss && isPumpBullet && dist(b.x, b.y, boss.x, boss.y) < b.radius + boss.radius) {
// Pump bullets do NOT damage boss, just ignore
b.destroy();
bullets.splice(i, 1);
}
}
// Update whip attacks
for (var i = whipAttacks.length - 1; i >= 0; i--) {
var w = whipAttacks[i];
w.update();
// Hit enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (dist(w.x + w.dirX * 40, w.y + w.dirY * 40, e.x, e.y) < w.radius + e.radius) {
// Always die in one hit
e.destroy();
enemies.splice(j, 1);
score++;
}
}
// Hit boss
if (bossActive && boss && dist(w.x + w.dirX * 40, w.y + w.dirY * 40, boss.x, boss.y) < w.radius + boss.radius) {
// Whip damages boss (1 hp per hit), and pushes boss back
boss.hp--;
var pushStrength = 120;
boss.x += w.dirX * pushStrength;
boss.y += w.dirY * pushStrength;
// Optionally, add a little visual feedback (flash)
LK.effects.flashObject(boss, 0x00ffff, 200);
// If boss dies from whip, drop upgrades and remove boss
if (boss.hp <= 0) {
boss.destroy();
boss = null;
bossActive = false;
// Increase game speed
enemySpeed += 0.7;
wave++;
}
}
// Lifetime
if (w.lifetime <= 0) {
w.destroy();
whipAttacks.splice(i, 1);
}
}
// Update upgrades
for (var i = upgrades.length - 1; i >= 0; i--) {
var u = upgrades[i];
// Move toward player always
var dir = getDir(u.x, u.y, cowboy.x, cowboy.y);
u.x += dir.x * 10;
u.y += dir.y * 10;
// Collect
if (dist(u.x, u.y, cowboy.x, cowboy.y) < u.radius + cowboy.radius) {
cowboy.upgrades++;
// Each upgrade reduces revolver cooldown by 1.5s, min 4s (240 frames)
cowboy.revolverCooldownTime = Math.max(240, 600 - cowboy.upgrades * 90);
if (cowboy.upgrades >= 5) {
cowboy.pumpUnlocked = true;
}
u.destroy();
upgrades.splice(i, 1);
}
}
// Timed upgrade drop every 2 minutes (120s)
if (typeof upgradeTimer === "undefined") {
upgradeTimer = 60 * 120;
}
upgradeTimer--;
if (upgradeTimer <= 0) {
spawnUpgrade(cowboy.x, cowboy.y - 200); // spawn above player, flies to player
upgradeTimer = 60 * 120;
}
// Win condition: survive 5 minutes or reach 100 score
if (score >= 100 || LK.ticks > 60 * 60 * 5) {
LK.showYouWin();
return;
}
};
There is a + on the gun. In-Game asset. 2d. High contrast. No shadows
Zombie. In-Game asset. 2d. High contrast. No shadows
revovler. In-Game asset. 2d. High contrast. No shadows
Pump bullet. In-Game asset. 2d. High contrast. No shadows
Cowboy. In-Game asset. 2d. High contrast. No shadows
whip. In-Game asset. 2d. High contrast. No shadows
center circle. In-Game asset. 2d. High contrast. No shadows