/**** * 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