/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Archer = Container.expand(function () {
var self = Container.call(this);
var archerGraphics = self.attachAsset('archer', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.health = 10;
self.shootTimer = 0;
self.shootRate = 60; // Faster shooting than regular enemies
self.moveToNewPosition = function () {
var newX = Math.random() * (2048 - 400) + 200;
var newY = Math.random() * (2732 - 400) + 200;
// Make sure boss doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(newX - archer.x, 2) + Math.pow(newY - archer.y, 2));
if (distToArcher < 300) {
newX = Math.random() * (2048 - 600) + 300;
if (newX > archer.x - 300 && newX < archer.x + 300) {
newX += 400;
}
}
// Animate movement to new position
tween(self, {
x: newX,
y: newY
}, {
duration: 800,
easing: tween.easeInOut
});
};
self.update = function () {
// Skip shooting if frozen
if (freezeActive) {
return;
}
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shootTimer = 0;
// Shoot at player
var dirX = archer.x - self.x;
var dirY = archer.y - self.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make boss face the shooting direction
self.rotation = Math.atan2(normalY, normalX);
// Boss shoots 3 arrows at once
for (var i = 0; i < 3; i++) {
var spreadAngle = (i - 1) * 0.15; // Reduced from 0.3 to 0.15 for tighter spread
var finalAngle = Math.atan2(normalY, normalX) + spreadAngle;
var finalNormalX = Math.cos(finalAngle);
var finalNormalY = Math.sin(finalAngle);
var enemyArrow = new EnemyArrow();
enemyArrow.x = self.x;
enemyArrow.y = self.y;
enemyArrow.speedX = finalNormalX * 10;
enemyArrow.speedY = finalNormalY * 10;
enemyArrow.rotation = finalAngle;
enemyArrows.push(enemyArrow);
game.addChild(enemyArrow);
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 2; // Small enemies have 2 health
self.shootTimer = 0;
self.shootRate = 120 + Math.random() * 60; // Random shooting interval
// Create health display text
self.healthTxt = new Text2(self.health.toString(), {
size: 50,
fill: 0xff0000
});
self.healthTxt.anchor.set(0.5, 1);
self.addChild(self.healthTxt);
self.healthTxt.y = -60;
self.update = function () {
// Enemies don't move, they stay in place and shoot
// Skip shooting if frozen
if (freezeActive) {
return;
}
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shootTimer = 0;
// Shoot at player
var dirX = archer.x - self.x;
var dirY = archer.y - self.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make enemy face the shooting direction
self.rotation = Math.atan2(normalY, normalX);
var enemyArrow = new EnemyArrow();
enemyArrow.x = self.x;
enemyArrow.y = self.y;
enemyArrow.speedX = normalX * 8;
enemyArrow.speedY = normalY * 8;
enemyArrow.rotation = Math.atan2(normalY, normalX);
enemyArrows.push(enemyArrow);
game.addChild(enemyArrow);
}
};
return self;
});
var EnemyArrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('enemyArrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
};
return self;
});
var MiniBoss = Container.expand(function () {
var self = Container.call(this);
var miniBossGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x800080
});
self.health = 3;
self.shootTimer = 0;
self.shootRate = Math.max(10, 40 - (currentWave - 1) * 3); // Gets faster each wave, minimum 10
// Create health display text
self.healthTxt = new Text2(self.health.toString(), {
size: 60,
fill: 0x800080
});
self.healthTxt.anchor.set(0.5, 1);
self.addChild(self.healthTxt);
self.healthTxt.y = -70;
self.update = function () {
// Skip shooting if frozen
if (freezeActive) {
return;
}
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shootTimer = 0;
// Shoot at player
var dirX = archer.x - self.x;
var dirY = archer.y - self.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make enemy face the shooting direction
self.rotation = Math.atan2(normalY, normalX);
// Shoot 2 arrows at once
for (var i = 0; i < 2; i++) {
var spreadAngle = (i - 0.5) * 0.1; // Reduced from 0.2 to 0.1 for tighter spread
var finalAngle = Math.atan2(normalY, normalX) + spreadAngle;
var finalNormalX = Math.cos(finalAngle);
var finalNormalY = Math.sin(finalAngle);
var enemyArrow = new EnemyArrow();
enemyArrow.x = self.x;
enemyArrow.y = self.y;
enemyArrow.speedX = finalNormalX * 9;
enemyArrow.speedY = finalNormalY * 9;
enemyArrow.rotation = finalAngle;
enemyArrows.push(enemyArrow);
game.addChild(enemyArrow);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var archer = game.addChild(new Archer());
archer.x = 150;
archer.y = 2732 / 2;
var arrows = [];
var enemies = [];
var miniBosses = [];
var enemyArrows = [];
var enemySpawnTimer = 0;
var enemySpawnRate = 120; // frames between spawns
var playerHealth = 10;
var maxEnemies = 1; // Start with 1 enemy, increase over time
var energy = 100;
var energyRegenTimer = 0;
var isShooting = false;
var shootingTimer = 0;
var money = storage.money || 0;
var kills = storage.kills || 0;
var shopVisible = false;
var gamePaused = false;
var energyUpgradeLevel = storage.energyUpgradeLevel || 0;
var speedUpgradeLevel = storage.speedUpgradeLevel || 0;
var damageUpgradeLevel = storage.damageUpgradeLevel || 0;
var healthUpgradeLevel = storage.healthUpgradeLevel || 0;
var rangeUpgradeLevel = storage.rangeUpgradeLevel || 0;
var maxHealth = 10 + healthUpgradeLevel * 2;
var shieldActive = false;
var shieldTimer = 0;
var freezeActive = false;
var freezeTimer = 0;
var explosiveActive = false;
var explosiveTimer = 0;
var doubleMoneyActive = false;
var doubleMoneyTimer = 0;
var moneyTxt = new Text2('Money: 0', {
size: 100,
fill: 0xFFD700
});
moneyTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(moneyTxt);
moneyTxt.setText('Money: ' + money);
var energyTxt = new Text2('Energy: 100', {
size: 80,
fill: 0x00ff00
});
energyTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(energyTxt);
energyTxt.x = -20;
energyTxt.y = 20;
var healthTxt = new Text2('Health: 10', {
size: 80,
fill: 0xff0000
});
healthTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthTxt);
healthTxt.x = 120; // Avoid the platform menu icon
healthTxt.y = 20;
var autoShootTimer = 0;
var autoBattleActive = false;
// Create hack mode button
var autoBattleButton = new Text2('HACK MODE', {
size: 80,
fill: 0xff0000
});
autoBattleButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(autoBattleButton);
autoBattleButton.y = -50;
// Button click handler
autoBattleButton.down = function (x, y, obj) {
autoBattleActive = !autoBattleActive;
if (autoBattleActive) {
autoBattleButton.setText('STOP HACK');
autoBattleButton.tint = 0x00ff00; // Green when active
} else {
autoBattleButton.setText('HACK MODE');
autoBattleButton.tint = 0xff0000; // Red when inactive
}
;
};
// Triple shoot variables
var tripleShootActive = false;
var tripleShootTimer = 0;
var boss = null;
var bossHealth = 10;
var bossSpawnScore = Math.floor(Math.random() * 21) + 30; // Random between 30-50 kills
var currentWave = 1;
var bossesKilled = 0;
var bossHealthTxt = new Text2('', {
size: 80,
fill: 0xff0000
});
bossHealthTxt.anchor.set(0.5, 1);
bossHealthTxt.visible = false;
game.addChild(bossHealthTxt);
// Upgrade system variables
var upgradeLevel = 0; // 0=normal, 1=triple, 2=octa, 3=16-arrow, 4=32-arrow, 5=64-arrow
var upgradeNames = ['NORMAL', 'TRIPLE', 'OCTA', '16-ARROW', '32-ARROW', '64-ARROW'];
var upgradeArrowCounts = [1, 3, 8, 16, 32, 64];
// Create cycling upgrade button
var upgradeButton = new Text2('NORMAL', {
size: 50,
fill: 0x00ff00
});
upgradeButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(upgradeButton);
upgradeButton.y = -250;
// Create reset button
var resetButton = new Text2('RESET', {
size: 60,
fill: 0xff0000
});
resetButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(resetButton);
resetButton.x = -600;
resetButton.y = -50;
// Cycling button handler
upgradeButton.down = function (x, y, obj) {
upgradeLevel = (upgradeLevel + 1) % upgradeNames.length;
upgradeButton.setText(upgradeNames[upgradeLevel]);
// Update triple shoot active state based on upgrade level
tripleShootActive = upgradeLevel > 0;
};
// Reset button handler
resetButton.down = function (x, y, obj) {
// Reset all game variables to initial state
money = 0;
kills = 0;
playerHealth = 10;
energy = 100;
energyUpgradeLevel = 0;
speedUpgradeLevel = 0;
damageUpgradeLevel = 0;
healthUpgradeLevel = 0;
rangeUpgradeLevel = 0;
maxHealth = 10;
currentWave = 1;
bossesKilled = 0;
bossSpawnScore = 200;
maxEnemies = 1;
enemySpawnRate = 120;
// Reset power-up states
shieldActive = false;
shieldTimer = 0;
freezeActive = false;
freezeTimer = 0;
explosiveActive = false;
explosiveTimer = 0;
doubleMoneyActive = false;
doubleMoneyTimer = 0;
superShieldActive = false;
superShieldTimer = 0;
timeSlowActive = false;
timeSlowTimer = 0;
tripleMoneyActive = false;
tripleMoneyTimer = 0;
// Reset upgrade system
upgradeLevel = 0;
tripleShootActive = false;
// Reset auto battle
autoBattleActive = false;
autoBattleButton.setText('HACK MODE');
autoBattleButton.tint = 0xff0000;
// Clear all game objects
for (var i = arrows.length - 1; i >= 0; i--) {
arrows[i].destroy();
arrows.splice(i, 1);
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = miniBosses.length - 1; i >= 0; i--) {
miniBosses[i].destroy();
miniBosses.splice(i, 1);
}
for (var i = enemyArrows.length - 1; i >= 0; i--) {
enemyArrows[i].destroy();
enemyArrows.splice(i, 1);
}
// Clear boss
if (boss) {
boss.destroy();
boss = null;
}
// Remove hacker text if exists
if (game.hackerText) {
game.hackerText.destroy();
game.hackerText = null;
}
// Reset archer appearance
archer.tint = 0xFFFFFF;
archer.x = 150;
archer.y = 2732 / 2;
// Reset all timer displays
shieldTimerTxt.visible = false;
freezeTimerTxt.visible = false;
explosiveTimerTxt.visible = false;
doubleMoneyTimerTxt.visible = false;
superShieldTimerTxt.visible = false;
timeSlowTimerTxt.visible = false;
tripleMoneyTimerTxt.visible = false;
bossHealthTxt.visible = false;
bossCountdownTxt.visible = false;
// Update storage
storage.money = money;
storage.kills = kills;
storage.energyUpgradeLevel = energyUpgradeLevel;
storage.speedUpgradeLevel = speedUpgradeLevel;
storage.damageUpgradeLevel = damageUpgradeLevel;
storage.healthUpgradeLevel = healthUpgradeLevel;
storage.rangeUpgradeLevel = rangeUpgradeLevel;
// Update UI displays
moneyTxt.setText('Money: ' + money);
healthTxt.setText('Health: ' + playerHealth);
energyTxt.setText('Energy: ' + energy);
energyTxt.tint = 0x00ff00;
waveTxt.setText('Wave: ' + currentWave);
upgradeButton.setText('NORMAL');
// Close shop if open
shopVisible = false;
shopContainer.visible = false;
gamePaused = false;
};
// Shop UI elements
var shopContainer = new Container();
var shopBackground = LK.getAsset('shop_bg', {
width: 1400,
height: 2000,
color: 0x444444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
shopContainer.addChild(shopBackground);
var shopTitle = new Text2('SHOP', {
size: 100,
fill: 0xFFFFFF
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.y = -950;
shopContainer.addChild(shopTitle);
// Health Potion button
var healthPotionBtn = new Text2('Health Potion (Cost: 30)', {
size: 60,
fill: 0xFF69B4
});
healthPotionBtn.anchor.set(0.5, 0.5);
healthPotionBtn.y = -800;
shopContainer.addChild(healthPotionBtn);
// Shield button
var shieldBtn = new Text2('Shield 30sec (Cost: 80)', {
size: 60,
fill: 0x4169E1
});
shieldBtn.anchor.set(0.5, 0.5);
shieldBtn.y = -720;
shopContainer.addChild(shieldBtn);
// Freeze Enemies button
var freezeBtn = new Text2('Freeze 10sec (Cost: 60)', {
size: 60,
fill: 0x00FFFF
});
freezeBtn.anchor.set(0.5, 0.5);
freezeBtn.y = -640;
shopContainer.addChild(freezeBtn);
// Explosive Arrows button
var explosiveBtn = new Text2('Explosive 20sec (Cost: 120)', {
size: 60,
fill: 0xFF4500
});
explosiveBtn.anchor.set(0.5, 0.5);
explosiveBtn.y = -560;
shopContainer.addChild(explosiveBtn);
// Double Money button
var doubleMoneyBtn = new Text2('2x Money 60sec (Cost: 150)', {
size: 60,
fill: 0xFFD700
});
doubleMoneyBtn.anchor.set(0.5, 0.5);
doubleMoneyBtn.y = -480;
shopContainer.addChild(doubleMoneyBtn);
// Energy Upgrade button
var energyUpgradeBtn = new Text2('Energy +10 (Cost: 100)', {
size: 60,
fill: 0x00FF00
});
energyUpgradeBtn.anchor.set(0.5, 0.5);
energyUpgradeBtn.y = -400;
energyUpgradeBtn.x = -100;
shopContainer.addChild(energyUpgradeBtn);
// Energy Upgrade buy button
var energyUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x00AA00
});
energyUpgradeBuyBtn.anchor.set(0.5, 0.5);
energyUpgradeBuyBtn.y = -400;
energyUpgradeBuyBtn.x = 200;
shopContainer.addChild(energyUpgradeBuyBtn);
// Speed Upgrade button
var speedUpgradeBtn = new Text2('Speed +1 (Cost: 150)', {
size: 60,
fill: 0x00AAFF
});
speedUpgradeBtn.anchor.set(0.5, 0.5);
speedUpgradeBtn.y = -320;
speedUpgradeBtn.x = -100;
shopContainer.addChild(speedUpgradeBtn);
// Max Energy Upgrade button
// Speed Upgrade buy button
var speedUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x0088CC
});
speedUpgradeBuyBtn.anchor.set(0.5, 0.5);
speedUpgradeBuyBtn.y = -320;
speedUpgradeBuyBtn.x = 200;
shopContainer.addChild(speedUpgradeBuyBtn);
var maxEnergyUpgradeBtn = new Text2('Max Energy +20 (Cost: 300)', {
size: 60,
fill: 0x80FF00
});
maxEnergyUpgradeBtn.anchor.set(0.5, 0.5);
maxEnergyUpgradeBtn.y = -240;
maxEnergyUpgradeBtn.x = -100;
shopContainer.addChild(maxEnergyUpgradeBtn);
// Damage Upgrade button
var damageUpgradeBtn = new Text2('Damage +1 (Cost: 200)', {
size: 60,
fill: 0xFF6600
});
damageUpgradeBtn.anchor.set(0.5, 0.5);
damageUpgradeBtn.y = -160;
damageUpgradeBtn.x = -100;
shopContainer.addChild(damageUpgradeBtn);
// Health Upgrade button
// Max Energy Upgrade buy button
var maxEnergyUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x66CC00
});
maxEnergyUpgradeBuyBtn.anchor.set(0.5, 0.5);
maxEnergyUpgradeBuyBtn.y = -240;
maxEnergyUpgradeBuyBtn.x = 200;
shopContainer.addChild(maxEnergyUpgradeBuyBtn);
var healthUpgradeBtn = new Text2('Max Health +2 (Cost: 250)', {
size: 60,
fill: 0xFF1493
});
healthUpgradeBtn.anchor.set(0.5, 0.5);
healthUpgradeBtn.y = -80;
healthUpgradeBtn.x = -100;
shopContainer.addChild(healthUpgradeBtn);
// Arrow Range Upgrade button
// Damage Upgrade buy button
var damageUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0xCC4400
});
damageUpgradeBuyBtn.anchor.set(0.5, 0.5);
damageUpgradeBuyBtn.y = -160;
damageUpgradeBuyBtn.x = 200;
shopContainer.addChild(damageUpgradeBuyBtn);
// Health Upgrade buy button
var healthUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0xCC1077
});
healthUpgradeBuyBtn.anchor.set(0.5, 0.5);
healthUpgradeBuyBtn.y = -80;
healthUpgradeBuyBtn.x = 200;
shopContainer.addChild(healthUpgradeBuyBtn);
var rangeUpgradeBtn = new Text2('Arrow Range +20% (Cost: 180)', {
size: 60,
fill: 0x9932CC
});
rangeUpgradeBtn.anchor.set(0.5, 0.5);
rangeUpgradeBtn.y = 0;
rangeUpgradeBtn.x = -100;
shopContainer.addChild(rangeUpgradeBtn);
// Range Upgrade buy button
var rangeUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x7722AA
});
rangeUpgradeBuyBtn.anchor.set(0.5, 0.5);
rangeUpgradeBuyBtn.y = 0;
rangeUpgradeBuyBtn.x = 200;
shopContainer.addChild(rangeUpgradeBuyBtn);
// Close shop button
var closeShopBtn = new Text2('CLOSE', {
size: 70,
fill: 0xFF0000
});
closeShopBtn.anchor.set(0.5, 0.5);
closeShopBtn.y = 120;
shopContainer.addChild(closeShopBtn);
shopContainer.x = 2048 / 2;
shopContainer.y = 2732 / 2;
shopContainer.visible = false;
// Create shop button
var shopButton = new Text2('SHOP', {
size: 60,
fill: 0xFFD700
});
shopButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(shopButton);
shopButton.x = -300;
shopButton.y = -50;
// Removed navigation button - all items now in main shop
// Removed advanced shop variables - all items now in main shop
// All shop items now in main shop - removed advanced shop container
// Advanced shop power-up variables
var superShieldActive = false;
var superShieldTimer = 0;
var timeSlowActive = false;
var timeSlowTimer = 0;
var tripleMoneyActive = false;
var tripleMoneyTimer = 0;
// Timer display texts for power-ups
var shieldTimerTxt = new Text2('', {
size: 60,
fill: 0x4169E1
});
shieldTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(shieldTimerTxt);
shieldTimerTxt.x = 120;
shieldTimerTxt.y = 120;
shieldTimerTxt.visible = false;
var freezeTimerTxt = new Text2('', {
size: 60,
fill: 0x00FFFF
});
freezeTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(freezeTimerTxt);
freezeTimerTxt.x = 120;
freezeTimerTxt.y = 180;
freezeTimerTxt.visible = false;
var explosiveTimerTxt = new Text2('', {
size: 60,
fill: 0xFF4500
});
explosiveTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(explosiveTimerTxt);
explosiveTimerTxt.x = 120;
explosiveTimerTxt.y = 240;
explosiveTimerTxt.visible = false;
var doubleMoneyTimerTxt = new Text2('', {
size: 60,
fill: 0xFFD700
});
doubleMoneyTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(doubleMoneyTimerTxt);
doubleMoneyTimerTxt.x = 120;
doubleMoneyTimerTxt.y = 300;
doubleMoneyTimerTxt.visible = false;
var superShieldTimerTxt = new Text2('', {
size: 60,
fill: 0x8000FF
});
superShieldTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(superShieldTimerTxt);
superShieldTimerTxt.x = 120;
superShieldTimerTxt.y = 360;
superShieldTimerTxt.visible = false;
var timeSlowTimerTxt = new Text2('', {
size: 60,
fill: 0x80FF80
});
timeSlowTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(timeSlowTimerTxt);
timeSlowTimerTxt.x = 120;
timeSlowTimerTxt.y = 420;
timeSlowTimerTxt.visible = false;
var tripleMoneyTimerTxt = new Text2('', {
size: 60,
fill: 0xFFFF00
});
tripleMoneyTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(tripleMoneyTimerTxt);
tripleMoneyTimerTxt.x = 120;
tripleMoneyTimerTxt.y = 480;
tripleMoneyTimerTxt.visible = false;
// Boss countdown timer display
var bossCountdownTxt = new Text2('', {
size: 80,
fill: 0xff8800
});
bossCountdownTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bossCountdownTxt);
bossCountdownTxt.y = 120;
// Wave display
var waveTxt = new Text2('Wave: 1', {
size: 90,
fill: 0x00ff88
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 200;
// Shop button handler
shopButton.down = function (x, y, obj) {
shopVisible = true;
shopContainer.visible = true;
// Update upgrade button costs
energyUpgradeBtn.setText('Energy +10 (Cost: ' + (100 + energyUpgradeLevel * 20) + ')');
speedUpgradeBtn.setText('Speed +1 (Cost: ' + (150 + speedUpgradeLevel * 30) + ')');
maxEnergyUpgradeBtn.setText('Max Energy +20 (Cost: ' + (300 + energyUpgradeLevel * 50) + ')');
damageUpgradeBtn.setText('Damage +1 (Cost: ' + (200 + damageUpgradeLevel * 40) + ')');
healthUpgradeBtn.setText('Max Health +2 (Cost: ' + (250 + healthUpgradeLevel * 50) + ')');
rangeUpgradeBtn.setText('Arrow Range +20% (Cost: ' + (180 + rangeUpgradeLevel * 35) + ')');
// Pause the game when shop opens
gamePaused = true;
// Clear all enemies when shop opens
for (var e = enemies.length - 1; e >= 0; e--) {
enemies[e].destroy();
enemies.splice(e, 1);
}
// Clear all mini bosses when shop opens
for (var mb = miniBosses.length - 1; mb >= 0; mb--) {
miniBosses[mb].destroy();
miniBosses.splice(mb, 1);
}
// Clear all enemy arrows when shop opens
for (var a = enemyArrows.length - 1; a >= 0; a--) {
enemyArrows[a].destroy();
enemyArrows.splice(a, 1);
}
// Clear boss when shop opens
if (boss) {
boss.destroy();
boss = null;
}
};
// Removed navigation handler - all items now in main shop
// Add shop event handlers
healthPotionBtn.down = function (x, y, obj) {
if (money >= 30) {
money -= 30;
playerHealth += 3;
if (playerHealth > maxHealth) playerHealth = maxHealth;
storage.money = money;
moneyTxt.setText('Money: ' + money);
healthTxt.setText('Health: ' + playerHealth);
}
};
shieldBtn.down = function (x, y, obj) {
if (money >= 80 && !shieldActive) {
money -= 80;
shieldActive = true;
shieldTimer = 1800; // 30 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
archer.tint = 0x4169E1; // Blue tint for shield
}
};
freezeBtn.down = function (x, y, obj) {
if (money >= 60 && !freezeActive) {
money -= 60;
freezeActive = true;
freezeTimer = 600; // 10 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
}
};
explosiveBtn.down = function (x, y, obj) {
if (money >= 120 && !explosiveActive) {
money -= 120;
explosiveActive = true;
explosiveTimer = 1200; // 20 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
}
;
};
doubleMoneyBtn.down = function (x, y, obj) {
if (money >= 150 && !doubleMoneyActive) {
money -= 150;
doubleMoneyActive = true;
doubleMoneyTimer = 3600; // 60 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
}
;
};
energyUpgradeBuyBtn.down = function (x, y, obj) {
if (money >= 100 + energyUpgradeLevel * 20) {
var cost = 100 + energyUpgradeLevel * 20;
money -= cost;
energyUpgradeLevel++;
energy += 10; // Give immediate energy boost
var maxEnergy = 100 + energyUpgradeLevel * 10;
if (energy > maxEnergy) energy = maxEnergy;
storage.money = money;
storage.energyUpgradeLevel = energyUpgradeLevel;
moneyTxt.setText('Money: ' + money);
energyTxt.setText('Energy: ' + energy);
energyUpgradeBtn.setText('Energy +10 (Cost: ' + (100 + energyUpgradeLevel * 20) + ')');
}
};
// Speed upgrade handler
speedUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 150 + speedUpgradeLevel * 30;
if (money >= cost) {
money -= cost;
speedUpgradeLevel++;
storage.money = money;
storage.speedUpgradeLevel = speedUpgradeLevel;
moneyTxt.setText('Money: ' + money);
speedUpgradeBtn.setText('Speed +1 (Cost: ' + (150 + speedUpgradeLevel * 30) + ')');
}
};
// Max energy upgrade handler
maxEnergyUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 300 + energyUpgradeLevel * 50;
if (money >= cost) {
money -= cost;
energyUpgradeLevel += 2;
energy += 20; // Give immediate energy boost
var maxEnergy = 100 + energyUpgradeLevel * 10;
if (energy > maxEnergy) energy = maxEnergy;
storage.money = money;
storage.energyUpgradeLevel = energyUpgradeLevel;
moneyTxt.setText('Money: ' + money);
energyTxt.setText('Energy: ' + energy);
maxEnergyUpgradeBtn.setText('Max Energy +20 (Cost: ' + (300 + energyUpgradeLevel * 50) + ')');
}
};
// Damage upgrade handler
damageUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 200 + damageUpgradeLevel * 40;
if (money >= cost) {
money -= cost;
damageUpgradeLevel++;
storage.money = money;
storage.damageUpgradeLevel = damageUpgradeLevel;
moneyTxt.setText('Money: ' + money);
damageUpgradeBtn.setText('Damage +1 (Cost: ' + (200 + damageUpgradeLevel * 40) + ')');
}
};
// Health upgrade handler
healthUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 250 + healthUpgradeLevel * 50;
if (money >= cost) {
money -= cost;
healthUpgradeLevel++;
maxHealth = 10 + healthUpgradeLevel * 2;
playerHealth += 2; // Give immediate health boost
if (playerHealth > maxHealth) playerHealth = maxHealth;
storage.money = money;
storage.healthUpgradeLevel = healthUpgradeLevel;
moneyTxt.setText('Money: ' + money);
healthTxt.setText('Health: ' + playerHealth);
healthUpgradeBtn.setText('Max Health +2 (Cost: ' + (250 + healthUpgradeLevel * 50) + ')');
}
};
// Range upgrade handler
rangeUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 180 + rangeUpgradeLevel * 35;
if (money >= cost) {
money -= cost;
rangeUpgradeLevel++;
storage.money = money;
storage.rangeUpgradeLevel = rangeUpgradeLevel;
moneyTxt.setText('Money: ' + money);
rangeUpgradeBtn.setText('Arrow Range +20% (Cost: ' + (180 + rangeUpgradeLevel * 35) + ')');
}
};
closeShopBtn.down = function (x, y, obj) {
shopVisible = false;
shopContainer.visible = false;
// Resume the game when shop closes
gamePaused = false;
// Increase difficulty slightly each time shop closes
if (maxEnemies < 8) {
maxEnemies++;
}
if (enemySpawnRate > 20) {
enemySpawnRate -= 2;
}
};
// Removed advanced shop event handlers - items moved to main shop
game.addChild(shopContainer);
// Manual shooting - touch screen to shoot
game.down = function (x, y, obj) {
// Only allow manual shooting when hack mode is not active
if (!autoBattleActive && energy > 0) {
// Convert touch position to game coordinates
// Check if obj and obj.parent exist before calling toGlobal
if (obj && obj.parent && obj.parent.toGlobal) {
var gamePos = game.toLocal(obj.parent.toGlobal({
x: x,
y: y
}));
shootArrowAtTarget(gamePos.x, gamePos.y);
} else {
// Fallback: use x, y directly as they should already be in the correct coordinate system
shootArrowAtTarget(x, y);
}
}
};
function shootArrowAtTarget(targetX, targetY) {
// Check if player has enough energy
if (energy <= 0) {
return;
}
// Calculate direction from archer to target point
var dirX = targetX - archer.x;
var dirY = targetY - archer.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
// Normalize direction
var normalX = dirX / distance;
var normalY = dirY / distance;
// Calculate base angle for arrow direction
var baseAngle = Math.atan2(normalY, normalX);
// Make archer face the shooting direction
archer.rotation = baseAngle;
// Fire arrows based on upgrade level
var arrowCount = upgradeArrowCounts[upgradeLevel];
var energyCost = upgradeLevel === 0 ? 2 : upgradeLevel === 1 ? 6 : upgradeLevel === 2 ? 12 : upgradeLevel === 3 ? 20 : upgradeLevel === 4 ? 35 : 50; // Normal: 2, Triple: 6, Octa: 12, 16-Arrow: 20, 32-Arrow: 35, 64-Arrow: 50
// Check if we have enough energy for the shot type
if (energy < energyCost) {
return;
}
for (var i = 0; i < arrowCount; i++) {
// Add spread based on upgrade level
var spreadAngle = 0;
if (upgradeLevel === 1) {
// Triple shot
spreadAngle = (i - 1) * 0.2; // -0.2, 0, 0.2 radians spread
} else if (upgradeLevel === 2) {
// Octa shot
spreadAngle = (i - 3.5) * 0.15; // Spread 8 arrows in fan pattern
} else if (upgradeLevel === 3) {
// 16-arrow shot
spreadAngle = (i - 7.5) * 0.12; // Spread 16 arrows in wide fan pattern
} else if (upgradeLevel === 4) {
// 32-arrow shot
spreadAngle = (i - 15.5) * 0.10; // Spread 32 arrows in very wide fan pattern
} else if (upgradeLevel === 5) {
// 64-arrow shot
spreadAngle = (i - 31.5) * 0.08; // Spread 64 arrows in extremely wide fan pattern
}
var finalAngle = baseAngle + spreadAngle;
var finalNormalX = Math.cos(finalAngle);
var finalNormalY = Math.sin(finalAngle);
// Create arrow
var newArrow = new Arrow();
newArrow.x = archer.x;
newArrow.y = archer.y;
var arrowSpeed = 15 + speedUpgradeLevel * 3;
newArrow.speedX = finalNormalX * arrowSpeed;
newArrow.speedY = finalNormalY * arrowSpeed;
// Rotate arrow to face direction
newArrow.rotation = finalAngle;
arrows.push(newArrow);
game.addChild(newArrow);
}
LK.getSound('shoot').play();
// Decrease energy
energy -= energyCost;
// Set shooting state
isShooting = true;
shootingTimer = 0;
// Update energy display
energyTxt.setText('Energy: ' + energy);
// Change color based on energy level
if (energy <= 20) {
energyTxt.tint = 0xff0000; // Red when low
} else if (energy <= 50) {
energyTxt.tint = 0xffff00; // Yellow when medium
} else {
energyTxt.tint = 0x00ff00; // Green when high
}
}
game.update = function () {
// Skip all game logic when game is paused (shop is open)
if (gamePaused) {
return;
}
// Update power-up timers
if (shieldActive) {
shieldTimer--;
var seconds = Math.ceil(shieldTimer / 60);
shieldTimerTxt.setText('Shield: ' + seconds + 's');
shieldTimerTxt.visible = true;
if (shieldTimer <= 0) {
shieldActive = false;
shieldTimerTxt.visible = false;
if (!superShieldActive) {
archer.tint = 0xFFFFFF; // Reset archer color only if super shield not active
}
}
}
if (superShieldActive) {
superShieldTimer--;
var seconds = Math.ceil(superShieldTimer / 60);
superShieldTimerTxt.setText('Super Shield: ' + seconds + 's');
superShieldTimerTxt.visible = true;
if (superShieldTimer <= 0) {
superShieldActive = false;
superShieldTimerTxt.visible = false;
if (!shieldActive) {
archer.tint = 0xFFFFFF; // Reset archer color only if regular shield not active
}
}
}
if (timeSlowActive) {
timeSlowTimer--;
var seconds = Math.ceil(timeSlowTimer / 60);
timeSlowTimerTxt.setText('Time Slow: ' + seconds + 's');
timeSlowTimerTxt.visible = true;
if (timeSlowTimer <= 0) {
timeSlowActive = false;
timeSlowTimerTxt.visible = false;
}
}
if (tripleMoneyActive) {
tripleMoneyTimer--;
var seconds = Math.ceil(tripleMoneyTimer / 60);
tripleMoneyTimerTxt.setText('3x Money: ' + seconds + 's');
tripleMoneyTimerTxt.visible = true;
if (tripleMoneyTimer <= 0) {
tripleMoneyActive = false;
tripleMoneyTimerTxt.visible = false;
}
}
if (freezeActive) {
freezeTimer--;
var seconds = Math.ceil(freezeTimer / 60);
freezeTimerTxt.setText('Freeze: ' + seconds + 's');
freezeTimerTxt.visible = true;
if (freezeTimer <= 0) {
freezeActive = false;
freezeTimerTxt.visible = false;
}
}
if (explosiveActive) {
explosiveTimer--;
var seconds = Math.ceil(explosiveTimer / 60);
explosiveTimerTxt.setText('Explosive: ' + seconds + 's');
explosiveTimerTxt.visible = true;
if (explosiveTimer <= 0) {
explosiveActive = false;
explosiveTimerTxt.visible = false;
}
}
if (doubleMoneyActive) {
doubleMoneyTimer--;
var seconds = Math.ceil(doubleMoneyTimer / 60);
doubleMoneyTimerTxt.setText('2x Money: ' + seconds + 's');
doubleMoneyTimerTxt.visible = true;
if (doubleMoneyTimer <= 0) {
doubleMoneyActive = false;
doubleMoneyTimerTxt.visible = false;
}
}
// Automatic shooting logic - only when hack mode is active
if (autoBattleActive) {
autoShootTimer++;
if (autoShootTimer >= 15) {
// Shoot every 0.25 seconds (faster rate for hack mode)
autoShootTimer = 0;
// Shoot at boss first if present, then mini bosses, then regular enemies
var targets = [];
if (boss) {
targets.push(boss);
}
for (var m = 0; m < miniBosses.length; m++) {
if (miniBosses[m]) {
targets.push(miniBosses[m]);
}
}
for (var e = 0; e < enemies.length; e++) {
if (enemies[e]) {
targets.push(enemies[e]);
}
}
for (var t = 0; t < targets.length; t++) {
var target = targets[t];
// Calculate direction from archer to target
var dirX = target.x - archer.x;
var dirY = target.y - archer.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
// Normalize direction
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make archer face the shooting direction
archer.rotation = Math.atan2(normalY, normalX);
// Create arrow without energy cost
var newArrow = new Arrow();
newArrow.x = archer.x;
newArrow.y = archer.y;
newArrow.speedX = normalX * 15;
newArrow.speedY = normalY * 15;
// Rotate arrow to face direction
newArrow.rotation = Math.atan2(normalY, normalX);
arrows.push(newArrow);
game.addChild(newArrow);
}
// Play shoot sound if we shot any arrows
if (targets.length > 0) {
LK.getSound('shoot').play();
}
}
}
// Update shooting timer
if (isShooting) {
shootingTimer++;
if (shootingTimer >= 30) {
// Stop shooting state after 0.5 seconds
isShooting = false;
}
}
// Update energy regeneration only when not shooting (every 60 frames = 1 second at 60 FPS)
if (!isShooting) {
energyRegenTimer++;
if (energyRegenTimer >= 60) {
energyRegenTimer = 0;
var maxEnergy = 100 + energyUpgradeLevel * 10;
if (energy < maxEnergy) {
energy += 1;
// Update energy display
energyTxt.setText('Energy: ' + energy);
// Change color based on energy level
if (energy <= 20) {
energyTxt.tint = 0xff0000; // Red when low
} else if (energy <= 50) {
energyTxt.tint = 0xffff00; // Yellow when medium
} else {
energyTxt.tint = 0x00ff00; // Green when high
}
}
}
} else {
energyRegenTimer = 0; // Reset regen timer when shooting
}
// Spawn enemies randomly on screen when needed (only when no boss is active)
if (!boss && enemies.length + miniBosses.length < maxEnemies) {
enemySpawnTimer++;
var currentSpawnRate = timeSlowActive ? enemySpawnRate * 2 : enemySpawnRate;
if (enemySpawnTimer >= currentSpawnRate) {
enemySpawnTimer = 0;
// 30% chance to spawn mini boss, 70% chance for regular enemy
var spawnMiniBoss = Math.random() < 0.3;
if (spawnMiniBoss && miniBosses.length < 2) {
// Spawn mini boss
var newMiniBoss = new MiniBoss();
// Scale mini boss health with wave
var baseHealth = 3;
var waveHealthBonus = Math.floor((currentWave - 1) / 2);
newMiniBoss.health = baseHealth + waveHealthBonus;
newMiniBoss.healthTxt.setText(newMiniBoss.health.toString());
newMiniBoss.x = Math.random() * (2048 - 200) + 100;
newMiniBoss.y = Math.random() * (2732 - 200) + 100;
// Make sure mini boss doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(newMiniBoss.x - archer.x, 2) + Math.pow(newMiniBoss.y - archer.y, 2));
if (distToArcher < 200) {
newMiniBoss.x = Math.random() * (2048 - 400) + 200;
if (newMiniBoss.x > archer.x - 200 && newMiniBoss.x < archer.x + 200) {
newMiniBoss.x += 300;
}
}
miniBosses.push(newMiniBoss);
game.addChild(newMiniBoss);
} else {
// Spawn regular enemy
var newEnemy = new Enemy();
// Scale enemy health with wave
var baseHealth = 2;
var waveHealthBonus = Math.floor((currentWave - 1) / 2);
newEnemy.health = baseHealth + waveHealthBonus;
newEnemy.healthTxt.setText(newEnemy.health.toString());
newEnemy.x = Math.random() * (2048 - 200) + 100;
newEnemy.y = Math.random() * (2732 - 200) + 100;
// Make sure enemy doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(newEnemy.x - archer.x, 2) + Math.pow(newEnemy.y - archer.y, 2));
if (distToArcher < 200) {
// Respawn farther away
newEnemy.x = Math.random() * (2048 - 400) + 200;
if (newEnemy.x > archer.x - 200 && newEnemy.x < archer.x + 200) {
newEnemy.x += 300;
}
}
enemies.push(newEnemy);
game.addChild(newEnemy);
}
}
}
// Update arrows
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Remove arrows that hit walls or go off screen
if (arrow.x >= 2048 || arrow.x <= 0 || arrow.y >= 2732 || arrow.y <= 0) {
arrow.destroy();
arrows.splice(i, 1);
continue;
}
// Check arrow-enemy arrow collisions
var arrowDestroyed = false;
for (var k = enemyArrows.length - 1; k >= 0; k--) {
var enemyArrow = enemyArrows[k];
if (arrow && enemyArrow && arrow.intersects(enemyArrow)) {
// Both arrows destroy each other
arrow.destroy();
arrows.splice(i, 1);
enemyArrow.destroy();
enemyArrows.splice(k, 1);
arrowDestroyed = true;
break;
}
}
// Skip enemy collision check if arrow was destroyed by enemy arrow
if (arrowDestroyed) {
continue;
}
// Check arrow-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (enemy && arrow && arrow.intersects(enemy)) {
// Hit enemy - reduce health
enemy.health -= 1 + damageUpgradeLevel;
enemy.healthTxt.setText(enemy.health.toString());
LK.getSound('hit').play();
LK.effects.flashObject(enemy, explosiveActive ? 0xFF4500 : 0xff0000, 200);
// Check if enemy is defeated
if (enemy.health <= 0) {
// Give money with bonuses
var moneyGain = tripleMoneyActive ? 15 : doubleMoneyActive ? 10 : 5;
money += moneyGain;
kills++;
storage.money = money;
storage.kills = kills;
moneyTxt.setText('Money: ' + money);
// Explosive arrows destroy nearby enemies
if (explosiveActive) {
for (var k = enemies.length - 1; k >= 0; k--) {
if (k !== j && enemies[k]) {
var dist = Math.sqrt(Math.pow(enemies[k].x - enemy.x, 2) + Math.pow(enemies[k].y - enemy.y, 2));
if (dist < 150) {
// Explosion radius
var bonusGain = tripleMoneyActive ? 15 : doubleMoneyActive ? 10 : 5;
money += bonusGain;
kills++;
storage.money = money;
storage.kills = kills;
LK.effects.flashObject(enemies[k], 0xFF4500, 300);
enemies[k].destroy();
enemies.splice(k, 1);
if (k < j) j--; // Adjust index if we removed an enemy before current one
}
}
}
moneyTxt.setText('Money: ' + money);
}
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
}
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
// Check arrow-mini boss collisions
for (var mb = miniBosses.length - 1; mb >= 0; mb--) {
var miniBoss = miniBosses[mb];
if (miniBoss && arrow && arrow.intersects(miniBoss)) {
// Hit mini boss - reduce health
miniBoss.health -= 1 + damageUpgradeLevel;
miniBoss.healthTxt.setText(miniBoss.health.toString());
LK.getSound('hit').play();
LK.effects.flashObject(miniBoss, explosiveActive ? 0xFF4500 : 0x800080, 200);
// Check if mini boss is defeated
if (miniBoss.health <= 0) {
// Give more money for mini boss
var moneyGain = tripleMoneyActive ? 30 : doubleMoneyActive ? 20 : 10;
money += moneyGain;
kills += 3; // Mini boss counts as 3 kills
storage.money = money;
storage.kills = kills;
moneyTxt.setText('Money: ' + money);
// Explosive arrows destroy nearby enemies
if (explosiveActive) {
// Check explosion damage to regular enemies
for (var k = enemies.length - 1; k >= 0; k--) {
if (enemies[k]) {
var dist = Math.sqrt(Math.pow(enemies[k].x - miniBoss.x, 2) + Math.pow(enemies[k].y - miniBoss.y, 2));
if (dist < 150) {
var bonusGain = tripleMoneyActive ? 15 : doubleMoneyActive ? 10 : 5;
money += bonusGain;
kills++;
storage.money = money;
storage.kills = kills;
LK.effects.flashObject(enemies[k], 0xFF4500, 300);
enemies[k].destroy();
enemies.splice(k, 1);
}
}
}
// Check explosion damage to other mini bosses
for (var k = miniBosses.length - 1; k >= 0; k--) {
if (k !== mb && miniBosses[k]) {
var dist = Math.sqrt(Math.pow(miniBosses[k].x - miniBoss.x, 2) + Math.pow(miniBosses[k].y - miniBoss.y, 2));
if (dist < 150) {
miniBosses[k].health--;
miniBosses[k].healthTxt.setText(miniBosses[k].health.toString());
LK.effects.flashObject(miniBosses[k], 0xFF4500, 300);
if (miniBosses[k].health <= 0) {
var bonusGain = tripleMoneyActive ? 30 : doubleMoneyActive ? 20 : 10;
money += bonusGain;
kills += 3;
storage.money = money;
storage.kills = kills;
miniBosses[k].destroy();
miniBosses.splice(k, 1);
if (k < mb) mb--;
}
}
}
}
moneyTxt.setText('Money: ' + money);
}
// Remove mini boss
miniBoss.destroy();
miniBosses.splice(mb, 1);
}
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
// Check arrow-boss collision
if (boss && arrow && arrow.intersects(boss)) {
// Hit boss
boss.health--;
LK.getSound('hit').play();
LK.effects.flashObject(boss, 0xff0000, 300);
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
// Boss moves to new position when hit
boss.moveToNewPosition();
// Check if boss is defeated
if (boss.health <= 0) {
money += 50; // Bonus money for defeating boss
kills += 10; // Count boss as 10 kills
bossesKilled++;
currentWave++;
// Increase difficulty with each wave
maxEnemies = Math.min(10, 1 + Math.floor(currentWave / 2));
enemySpawnRate = Math.max(15, 120 - currentWave * 8);
// Enemies get stronger each wave
if (currentWave >= 3) {
// Starting from wave 3, some enemies have more health
var healthBonus = Math.floor((currentWave - 2) / 2);
// This will be applied when spawning enemies
}
storage.money = money;
storage.kills = kills;
moneyTxt.setText('Money: ' + money);
waveTxt.setText('Wave: ' + currentWave);
boss.destroy();
boss = null;
bossSpawnScore = kills + Math.floor(Math.random() * 21) + 30; // Next boss spawns after random 30-50 more kills
}
break;
}
}
// Update enemy arrows
for (var m = enemyArrows.length - 1; m >= 0; m--) {
var enemyArrow = enemyArrows[m];
// Remove arrows that hit walls or go off screen
if (enemyArrow.x >= 2048 || enemyArrow.x <= 0 || enemyArrow.y >= 2732 || enemyArrow.y <= 0) {
enemyArrow.destroy();
enemyArrows.splice(m, 1);
continue;
}
// Check if enemy arrow hits player
if (enemyArrow.intersects(archer)) {
if (!shieldActive && !superShieldActive) {
playerHealth--;
healthTxt.setText('Health: ' + playerHealth);
LK.getSound('playerHit').play();
LK.effects.flashObject(archer, 0xff0000, 300);
// Check if player is dead
if (playerHealth <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
} else {
// Shield blocks the arrow
var shieldColor = superShieldActive ? 0x8000FF : 0x4169E1;
LK.effects.flashObject(archer, shieldColor, 200);
}
enemyArrow.destroy();
enemyArrows.splice(m, 1);
continue;
}
}
// Check if hack mode is active and score exceeds 200 to show HACKER text
if (autoBattleActive && LK.getScore() > 200) {
// Create red HACKER text if it doesn't exist
if (!game.hackerText) {
game.hackerText = new Text2('HACKER', {
size: 200,
fill: 0xff0000
});
game.hackerText.anchor.set(0.5, 0.5);
game.hackerText.x = 2048 / 2;
game.hackerText.y = 2732 / 2;
game.addChild(game.hackerText);
// Apply pulsing red glow effect
tween(game.hackerText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(game.hackerText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the pulsing effect
if (game.hackerText) {
tween(game.hackerText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
}
} else if (game.hackerText) {
// Remove HACKER text if conditions not met
game.hackerText.destroy();
game.hackerText = null;
}
// Update boss health display
if (boss) {
bossHealthTxt.setText('Boss Health: ' + boss.health);
bossHealthTxt.x = boss.x;
bossHealthTxt.y = boss.y - 80;
bossHealthTxt.visible = true;
} else {
bossHealthTxt.visible = false;
}
// Update boss countdown display
if (boss === null) {
var killsUntilBoss = bossSpawnScore - kills;
if (killsUntilBoss > 0) {
bossCountdownTxt.setText('Boss in: ' + killsUntilBoss + ' kills');
bossCountdownTxt.visible = true;
} else {
bossCountdownTxt.visible = false;
}
} else {
bossCountdownTxt.visible = false;
}
// Spawn boss every 20 kills
if (boss === null && kills >= bossSpawnScore) {
// Clear all existing enemies when boss spawns
for (var e = enemies.length - 1; e >= 0; e--) {
enemies[e].destroy();
enemies.splice(e, 1);
}
// Clear all mini bosses when boss spawns
for (var mb = miniBosses.length - 1; mb >= 0; mb--) {
miniBosses[mb].destroy();
miniBosses.splice(mb, 1);
}
boss = new Boss();
boss.x = Math.random() * (2048 - 400) + 200;
boss.y = Math.random() * (2732 - 400) + 200;
// Make sure boss doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(boss.x - archer.x, 2) + Math.pow(boss.y - archer.y, 2));
if (distToArcher < 300) {
boss.x = Math.random() * (2048 - 600) + 300;
if (boss.x > archer.x - 300 && boss.x < archer.x + 300) {
boss.x += 400;
}
}
// Scale boss health with wave
var baseHealth = 10;
var waveHealthBonus = Math.floor((currentWave - 1) / 3) * 5; // +5 health every 3 waves
boss.health = baseHealth + waveHealthBonus;
game.addChild(boss);
}
// When enemy dies, increase max enemies and spawn rate for difficulty
if (LK.getScore() > 0 && LK.getScore() % 50 === 0) {
if (maxEnemies < 5) {
maxEnemies++;
}
if (enemySpawnRate > 30) {
enemySpawnRate -= 5;
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Archer = Container.expand(function () {
var self = Container.call(this);
var archerGraphics = self.attachAsset('archer', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.health = 10;
self.shootTimer = 0;
self.shootRate = 60; // Faster shooting than regular enemies
self.moveToNewPosition = function () {
var newX = Math.random() * (2048 - 400) + 200;
var newY = Math.random() * (2732 - 400) + 200;
// Make sure boss doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(newX - archer.x, 2) + Math.pow(newY - archer.y, 2));
if (distToArcher < 300) {
newX = Math.random() * (2048 - 600) + 300;
if (newX > archer.x - 300 && newX < archer.x + 300) {
newX += 400;
}
}
// Animate movement to new position
tween(self, {
x: newX,
y: newY
}, {
duration: 800,
easing: tween.easeInOut
});
};
self.update = function () {
// Skip shooting if frozen
if (freezeActive) {
return;
}
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shootTimer = 0;
// Shoot at player
var dirX = archer.x - self.x;
var dirY = archer.y - self.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make boss face the shooting direction
self.rotation = Math.atan2(normalY, normalX);
// Boss shoots 3 arrows at once
for (var i = 0; i < 3; i++) {
var spreadAngle = (i - 1) * 0.15; // Reduced from 0.3 to 0.15 for tighter spread
var finalAngle = Math.atan2(normalY, normalX) + spreadAngle;
var finalNormalX = Math.cos(finalAngle);
var finalNormalY = Math.sin(finalAngle);
var enemyArrow = new EnemyArrow();
enemyArrow.x = self.x;
enemyArrow.y = self.y;
enemyArrow.speedX = finalNormalX * 10;
enemyArrow.speedY = finalNormalY * 10;
enemyArrow.rotation = finalAngle;
enemyArrows.push(enemyArrow);
game.addChild(enemyArrow);
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 2; // Small enemies have 2 health
self.shootTimer = 0;
self.shootRate = 120 + Math.random() * 60; // Random shooting interval
// Create health display text
self.healthTxt = new Text2(self.health.toString(), {
size: 50,
fill: 0xff0000
});
self.healthTxt.anchor.set(0.5, 1);
self.addChild(self.healthTxt);
self.healthTxt.y = -60;
self.update = function () {
// Enemies don't move, they stay in place and shoot
// Skip shooting if frozen
if (freezeActive) {
return;
}
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shootTimer = 0;
// Shoot at player
var dirX = archer.x - self.x;
var dirY = archer.y - self.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make enemy face the shooting direction
self.rotation = Math.atan2(normalY, normalX);
var enemyArrow = new EnemyArrow();
enemyArrow.x = self.x;
enemyArrow.y = self.y;
enemyArrow.speedX = normalX * 8;
enemyArrow.speedY = normalY * 8;
enemyArrow.rotation = Math.atan2(normalY, normalX);
enemyArrows.push(enemyArrow);
game.addChild(enemyArrow);
}
};
return self;
});
var EnemyArrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('enemyArrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
};
return self;
});
var MiniBoss = Container.expand(function () {
var self = Container.call(this);
var miniBossGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x800080
});
self.health = 3;
self.shootTimer = 0;
self.shootRate = Math.max(10, 40 - (currentWave - 1) * 3); // Gets faster each wave, minimum 10
// Create health display text
self.healthTxt = new Text2(self.health.toString(), {
size: 60,
fill: 0x800080
});
self.healthTxt.anchor.set(0.5, 1);
self.addChild(self.healthTxt);
self.healthTxt.y = -70;
self.update = function () {
// Skip shooting if frozen
if (freezeActive) {
return;
}
self.shootTimer++;
if (self.shootTimer >= self.shootRate) {
self.shootTimer = 0;
// Shoot at player
var dirX = archer.x - self.x;
var dirY = archer.y - self.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make enemy face the shooting direction
self.rotation = Math.atan2(normalY, normalX);
// Shoot 2 arrows at once
for (var i = 0; i < 2; i++) {
var spreadAngle = (i - 0.5) * 0.1; // Reduced from 0.2 to 0.1 for tighter spread
var finalAngle = Math.atan2(normalY, normalX) + spreadAngle;
var finalNormalX = Math.cos(finalAngle);
var finalNormalY = Math.sin(finalAngle);
var enemyArrow = new EnemyArrow();
enemyArrow.x = self.x;
enemyArrow.y = self.y;
enemyArrow.speedX = finalNormalX * 9;
enemyArrow.speedY = finalNormalY * 9;
enemyArrow.rotation = finalAngle;
enemyArrows.push(enemyArrow);
game.addChild(enemyArrow);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var archer = game.addChild(new Archer());
archer.x = 150;
archer.y = 2732 / 2;
var arrows = [];
var enemies = [];
var miniBosses = [];
var enemyArrows = [];
var enemySpawnTimer = 0;
var enemySpawnRate = 120; // frames between spawns
var playerHealth = 10;
var maxEnemies = 1; // Start with 1 enemy, increase over time
var energy = 100;
var energyRegenTimer = 0;
var isShooting = false;
var shootingTimer = 0;
var money = storage.money || 0;
var kills = storage.kills || 0;
var shopVisible = false;
var gamePaused = false;
var energyUpgradeLevel = storage.energyUpgradeLevel || 0;
var speedUpgradeLevel = storage.speedUpgradeLevel || 0;
var damageUpgradeLevel = storage.damageUpgradeLevel || 0;
var healthUpgradeLevel = storage.healthUpgradeLevel || 0;
var rangeUpgradeLevel = storage.rangeUpgradeLevel || 0;
var maxHealth = 10 + healthUpgradeLevel * 2;
var shieldActive = false;
var shieldTimer = 0;
var freezeActive = false;
var freezeTimer = 0;
var explosiveActive = false;
var explosiveTimer = 0;
var doubleMoneyActive = false;
var doubleMoneyTimer = 0;
var moneyTxt = new Text2('Money: 0', {
size: 100,
fill: 0xFFD700
});
moneyTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(moneyTxt);
moneyTxt.setText('Money: ' + money);
var energyTxt = new Text2('Energy: 100', {
size: 80,
fill: 0x00ff00
});
energyTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(energyTxt);
energyTxt.x = -20;
energyTxt.y = 20;
var healthTxt = new Text2('Health: 10', {
size: 80,
fill: 0xff0000
});
healthTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthTxt);
healthTxt.x = 120; // Avoid the platform menu icon
healthTxt.y = 20;
var autoShootTimer = 0;
var autoBattleActive = false;
// Create hack mode button
var autoBattleButton = new Text2('HACK MODE', {
size: 80,
fill: 0xff0000
});
autoBattleButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(autoBattleButton);
autoBattleButton.y = -50;
// Button click handler
autoBattleButton.down = function (x, y, obj) {
autoBattleActive = !autoBattleActive;
if (autoBattleActive) {
autoBattleButton.setText('STOP HACK');
autoBattleButton.tint = 0x00ff00; // Green when active
} else {
autoBattleButton.setText('HACK MODE');
autoBattleButton.tint = 0xff0000; // Red when inactive
}
;
};
// Triple shoot variables
var tripleShootActive = false;
var tripleShootTimer = 0;
var boss = null;
var bossHealth = 10;
var bossSpawnScore = Math.floor(Math.random() * 21) + 30; // Random between 30-50 kills
var currentWave = 1;
var bossesKilled = 0;
var bossHealthTxt = new Text2('', {
size: 80,
fill: 0xff0000
});
bossHealthTxt.anchor.set(0.5, 1);
bossHealthTxt.visible = false;
game.addChild(bossHealthTxt);
// Upgrade system variables
var upgradeLevel = 0; // 0=normal, 1=triple, 2=octa, 3=16-arrow, 4=32-arrow, 5=64-arrow
var upgradeNames = ['NORMAL', 'TRIPLE', 'OCTA', '16-ARROW', '32-ARROW', '64-ARROW'];
var upgradeArrowCounts = [1, 3, 8, 16, 32, 64];
// Create cycling upgrade button
var upgradeButton = new Text2('NORMAL', {
size: 50,
fill: 0x00ff00
});
upgradeButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(upgradeButton);
upgradeButton.y = -250;
// Create reset button
var resetButton = new Text2('RESET', {
size: 60,
fill: 0xff0000
});
resetButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(resetButton);
resetButton.x = -600;
resetButton.y = -50;
// Cycling button handler
upgradeButton.down = function (x, y, obj) {
upgradeLevel = (upgradeLevel + 1) % upgradeNames.length;
upgradeButton.setText(upgradeNames[upgradeLevel]);
// Update triple shoot active state based on upgrade level
tripleShootActive = upgradeLevel > 0;
};
// Reset button handler
resetButton.down = function (x, y, obj) {
// Reset all game variables to initial state
money = 0;
kills = 0;
playerHealth = 10;
energy = 100;
energyUpgradeLevel = 0;
speedUpgradeLevel = 0;
damageUpgradeLevel = 0;
healthUpgradeLevel = 0;
rangeUpgradeLevel = 0;
maxHealth = 10;
currentWave = 1;
bossesKilled = 0;
bossSpawnScore = 200;
maxEnemies = 1;
enemySpawnRate = 120;
// Reset power-up states
shieldActive = false;
shieldTimer = 0;
freezeActive = false;
freezeTimer = 0;
explosiveActive = false;
explosiveTimer = 0;
doubleMoneyActive = false;
doubleMoneyTimer = 0;
superShieldActive = false;
superShieldTimer = 0;
timeSlowActive = false;
timeSlowTimer = 0;
tripleMoneyActive = false;
tripleMoneyTimer = 0;
// Reset upgrade system
upgradeLevel = 0;
tripleShootActive = false;
// Reset auto battle
autoBattleActive = false;
autoBattleButton.setText('HACK MODE');
autoBattleButton.tint = 0xff0000;
// Clear all game objects
for (var i = arrows.length - 1; i >= 0; i--) {
arrows[i].destroy();
arrows.splice(i, 1);
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = miniBosses.length - 1; i >= 0; i--) {
miniBosses[i].destroy();
miniBosses.splice(i, 1);
}
for (var i = enemyArrows.length - 1; i >= 0; i--) {
enemyArrows[i].destroy();
enemyArrows.splice(i, 1);
}
// Clear boss
if (boss) {
boss.destroy();
boss = null;
}
// Remove hacker text if exists
if (game.hackerText) {
game.hackerText.destroy();
game.hackerText = null;
}
// Reset archer appearance
archer.tint = 0xFFFFFF;
archer.x = 150;
archer.y = 2732 / 2;
// Reset all timer displays
shieldTimerTxt.visible = false;
freezeTimerTxt.visible = false;
explosiveTimerTxt.visible = false;
doubleMoneyTimerTxt.visible = false;
superShieldTimerTxt.visible = false;
timeSlowTimerTxt.visible = false;
tripleMoneyTimerTxt.visible = false;
bossHealthTxt.visible = false;
bossCountdownTxt.visible = false;
// Update storage
storage.money = money;
storage.kills = kills;
storage.energyUpgradeLevel = energyUpgradeLevel;
storage.speedUpgradeLevel = speedUpgradeLevel;
storage.damageUpgradeLevel = damageUpgradeLevel;
storage.healthUpgradeLevel = healthUpgradeLevel;
storage.rangeUpgradeLevel = rangeUpgradeLevel;
// Update UI displays
moneyTxt.setText('Money: ' + money);
healthTxt.setText('Health: ' + playerHealth);
energyTxt.setText('Energy: ' + energy);
energyTxt.tint = 0x00ff00;
waveTxt.setText('Wave: ' + currentWave);
upgradeButton.setText('NORMAL');
// Close shop if open
shopVisible = false;
shopContainer.visible = false;
gamePaused = false;
};
// Shop UI elements
var shopContainer = new Container();
var shopBackground = LK.getAsset('shop_bg', {
width: 1400,
height: 2000,
color: 0x444444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
shopContainer.addChild(shopBackground);
var shopTitle = new Text2('SHOP', {
size: 100,
fill: 0xFFFFFF
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.y = -950;
shopContainer.addChild(shopTitle);
// Health Potion button
var healthPotionBtn = new Text2('Health Potion (Cost: 30)', {
size: 60,
fill: 0xFF69B4
});
healthPotionBtn.anchor.set(0.5, 0.5);
healthPotionBtn.y = -800;
shopContainer.addChild(healthPotionBtn);
// Shield button
var shieldBtn = new Text2('Shield 30sec (Cost: 80)', {
size: 60,
fill: 0x4169E1
});
shieldBtn.anchor.set(0.5, 0.5);
shieldBtn.y = -720;
shopContainer.addChild(shieldBtn);
// Freeze Enemies button
var freezeBtn = new Text2('Freeze 10sec (Cost: 60)', {
size: 60,
fill: 0x00FFFF
});
freezeBtn.anchor.set(0.5, 0.5);
freezeBtn.y = -640;
shopContainer.addChild(freezeBtn);
// Explosive Arrows button
var explosiveBtn = new Text2('Explosive 20sec (Cost: 120)', {
size: 60,
fill: 0xFF4500
});
explosiveBtn.anchor.set(0.5, 0.5);
explosiveBtn.y = -560;
shopContainer.addChild(explosiveBtn);
// Double Money button
var doubleMoneyBtn = new Text2('2x Money 60sec (Cost: 150)', {
size: 60,
fill: 0xFFD700
});
doubleMoneyBtn.anchor.set(0.5, 0.5);
doubleMoneyBtn.y = -480;
shopContainer.addChild(doubleMoneyBtn);
// Energy Upgrade button
var energyUpgradeBtn = new Text2('Energy +10 (Cost: 100)', {
size: 60,
fill: 0x00FF00
});
energyUpgradeBtn.anchor.set(0.5, 0.5);
energyUpgradeBtn.y = -400;
energyUpgradeBtn.x = -100;
shopContainer.addChild(energyUpgradeBtn);
// Energy Upgrade buy button
var energyUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x00AA00
});
energyUpgradeBuyBtn.anchor.set(0.5, 0.5);
energyUpgradeBuyBtn.y = -400;
energyUpgradeBuyBtn.x = 200;
shopContainer.addChild(energyUpgradeBuyBtn);
// Speed Upgrade button
var speedUpgradeBtn = new Text2('Speed +1 (Cost: 150)', {
size: 60,
fill: 0x00AAFF
});
speedUpgradeBtn.anchor.set(0.5, 0.5);
speedUpgradeBtn.y = -320;
speedUpgradeBtn.x = -100;
shopContainer.addChild(speedUpgradeBtn);
// Max Energy Upgrade button
// Speed Upgrade buy button
var speedUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x0088CC
});
speedUpgradeBuyBtn.anchor.set(0.5, 0.5);
speedUpgradeBuyBtn.y = -320;
speedUpgradeBuyBtn.x = 200;
shopContainer.addChild(speedUpgradeBuyBtn);
var maxEnergyUpgradeBtn = new Text2('Max Energy +20 (Cost: 300)', {
size: 60,
fill: 0x80FF00
});
maxEnergyUpgradeBtn.anchor.set(0.5, 0.5);
maxEnergyUpgradeBtn.y = -240;
maxEnergyUpgradeBtn.x = -100;
shopContainer.addChild(maxEnergyUpgradeBtn);
// Damage Upgrade button
var damageUpgradeBtn = new Text2('Damage +1 (Cost: 200)', {
size: 60,
fill: 0xFF6600
});
damageUpgradeBtn.anchor.set(0.5, 0.5);
damageUpgradeBtn.y = -160;
damageUpgradeBtn.x = -100;
shopContainer.addChild(damageUpgradeBtn);
// Health Upgrade button
// Max Energy Upgrade buy button
var maxEnergyUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x66CC00
});
maxEnergyUpgradeBuyBtn.anchor.set(0.5, 0.5);
maxEnergyUpgradeBuyBtn.y = -240;
maxEnergyUpgradeBuyBtn.x = 200;
shopContainer.addChild(maxEnergyUpgradeBuyBtn);
var healthUpgradeBtn = new Text2('Max Health +2 (Cost: 250)', {
size: 60,
fill: 0xFF1493
});
healthUpgradeBtn.anchor.set(0.5, 0.5);
healthUpgradeBtn.y = -80;
healthUpgradeBtn.x = -100;
shopContainer.addChild(healthUpgradeBtn);
// Arrow Range Upgrade button
// Damage Upgrade buy button
var damageUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0xCC4400
});
damageUpgradeBuyBtn.anchor.set(0.5, 0.5);
damageUpgradeBuyBtn.y = -160;
damageUpgradeBuyBtn.x = 200;
shopContainer.addChild(damageUpgradeBuyBtn);
// Health Upgrade buy button
var healthUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0xCC1077
});
healthUpgradeBuyBtn.anchor.set(0.5, 0.5);
healthUpgradeBuyBtn.y = -80;
healthUpgradeBuyBtn.x = 200;
shopContainer.addChild(healthUpgradeBuyBtn);
var rangeUpgradeBtn = new Text2('Arrow Range +20% (Cost: 180)', {
size: 60,
fill: 0x9932CC
});
rangeUpgradeBtn.anchor.set(0.5, 0.5);
rangeUpgradeBtn.y = 0;
rangeUpgradeBtn.x = -100;
shopContainer.addChild(rangeUpgradeBtn);
// Range Upgrade buy button
var rangeUpgradeBuyBtn = new Text2('BUY', {
size: 70,
fill: 0x7722AA
});
rangeUpgradeBuyBtn.anchor.set(0.5, 0.5);
rangeUpgradeBuyBtn.y = 0;
rangeUpgradeBuyBtn.x = 200;
shopContainer.addChild(rangeUpgradeBuyBtn);
// Close shop button
var closeShopBtn = new Text2('CLOSE', {
size: 70,
fill: 0xFF0000
});
closeShopBtn.anchor.set(0.5, 0.5);
closeShopBtn.y = 120;
shopContainer.addChild(closeShopBtn);
shopContainer.x = 2048 / 2;
shopContainer.y = 2732 / 2;
shopContainer.visible = false;
// Create shop button
var shopButton = new Text2('SHOP', {
size: 60,
fill: 0xFFD700
});
shopButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(shopButton);
shopButton.x = -300;
shopButton.y = -50;
// Removed navigation button - all items now in main shop
// Removed advanced shop variables - all items now in main shop
// All shop items now in main shop - removed advanced shop container
// Advanced shop power-up variables
var superShieldActive = false;
var superShieldTimer = 0;
var timeSlowActive = false;
var timeSlowTimer = 0;
var tripleMoneyActive = false;
var tripleMoneyTimer = 0;
// Timer display texts for power-ups
var shieldTimerTxt = new Text2('', {
size: 60,
fill: 0x4169E1
});
shieldTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(shieldTimerTxt);
shieldTimerTxt.x = 120;
shieldTimerTxt.y = 120;
shieldTimerTxt.visible = false;
var freezeTimerTxt = new Text2('', {
size: 60,
fill: 0x00FFFF
});
freezeTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(freezeTimerTxt);
freezeTimerTxt.x = 120;
freezeTimerTxt.y = 180;
freezeTimerTxt.visible = false;
var explosiveTimerTxt = new Text2('', {
size: 60,
fill: 0xFF4500
});
explosiveTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(explosiveTimerTxt);
explosiveTimerTxt.x = 120;
explosiveTimerTxt.y = 240;
explosiveTimerTxt.visible = false;
var doubleMoneyTimerTxt = new Text2('', {
size: 60,
fill: 0xFFD700
});
doubleMoneyTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(doubleMoneyTimerTxt);
doubleMoneyTimerTxt.x = 120;
doubleMoneyTimerTxt.y = 300;
doubleMoneyTimerTxt.visible = false;
var superShieldTimerTxt = new Text2('', {
size: 60,
fill: 0x8000FF
});
superShieldTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(superShieldTimerTxt);
superShieldTimerTxt.x = 120;
superShieldTimerTxt.y = 360;
superShieldTimerTxt.visible = false;
var timeSlowTimerTxt = new Text2('', {
size: 60,
fill: 0x80FF80
});
timeSlowTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(timeSlowTimerTxt);
timeSlowTimerTxt.x = 120;
timeSlowTimerTxt.y = 420;
timeSlowTimerTxt.visible = false;
var tripleMoneyTimerTxt = new Text2('', {
size: 60,
fill: 0xFFFF00
});
tripleMoneyTimerTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(tripleMoneyTimerTxt);
tripleMoneyTimerTxt.x = 120;
tripleMoneyTimerTxt.y = 480;
tripleMoneyTimerTxt.visible = false;
// Boss countdown timer display
var bossCountdownTxt = new Text2('', {
size: 80,
fill: 0xff8800
});
bossCountdownTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bossCountdownTxt);
bossCountdownTxt.y = 120;
// Wave display
var waveTxt = new Text2('Wave: 1', {
size: 90,
fill: 0x00ff88
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 200;
// Shop button handler
shopButton.down = function (x, y, obj) {
shopVisible = true;
shopContainer.visible = true;
// Update upgrade button costs
energyUpgradeBtn.setText('Energy +10 (Cost: ' + (100 + energyUpgradeLevel * 20) + ')');
speedUpgradeBtn.setText('Speed +1 (Cost: ' + (150 + speedUpgradeLevel * 30) + ')');
maxEnergyUpgradeBtn.setText('Max Energy +20 (Cost: ' + (300 + energyUpgradeLevel * 50) + ')');
damageUpgradeBtn.setText('Damage +1 (Cost: ' + (200 + damageUpgradeLevel * 40) + ')');
healthUpgradeBtn.setText('Max Health +2 (Cost: ' + (250 + healthUpgradeLevel * 50) + ')');
rangeUpgradeBtn.setText('Arrow Range +20% (Cost: ' + (180 + rangeUpgradeLevel * 35) + ')');
// Pause the game when shop opens
gamePaused = true;
// Clear all enemies when shop opens
for (var e = enemies.length - 1; e >= 0; e--) {
enemies[e].destroy();
enemies.splice(e, 1);
}
// Clear all mini bosses when shop opens
for (var mb = miniBosses.length - 1; mb >= 0; mb--) {
miniBosses[mb].destroy();
miniBosses.splice(mb, 1);
}
// Clear all enemy arrows when shop opens
for (var a = enemyArrows.length - 1; a >= 0; a--) {
enemyArrows[a].destroy();
enemyArrows.splice(a, 1);
}
// Clear boss when shop opens
if (boss) {
boss.destroy();
boss = null;
}
};
// Removed navigation handler - all items now in main shop
// Add shop event handlers
healthPotionBtn.down = function (x, y, obj) {
if (money >= 30) {
money -= 30;
playerHealth += 3;
if (playerHealth > maxHealth) playerHealth = maxHealth;
storage.money = money;
moneyTxt.setText('Money: ' + money);
healthTxt.setText('Health: ' + playerHealth);
}
};
shieldBtn.down = function (x, y, obj) {
if (money >= 80 && !shieldActive) {
money -= 80;
shieldActive = true;
shieldTimer = 1800; // 30 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
archer.tint = 0x4169E1; // Blue tint for shield
}
};
freezeBtn.down = function (x, y, obj) {
if (money >= 60 && !freezeActive) {
money -= 60;
freezeActive = true;
freezeTimer = 600; // 10 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
}
};
explosiveBtn.down = function (x, y, obj) {
if (money >= 120 && !explosiveActive) {
money -= 120;
explosiveActive = true;
explosiveTimer = 1200; // 20 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
}
;
};
doubleMoneyBtn.down = function (x, y, obj) {
if (money >= 150 && !doubleMoneyActive) {
money -= 150;
doubleMoneyActive = true;
doubleMoneyTimer = 3600; // 60 seconds at 60 FPS
storage.money = money;
moneyTxt.setText('Money: ' + money);
}
;
};
energyUpgradeBuyBtn.down = function (x, y, obj) {
if (money >= 100 + energyUpgradeLevel * 20) {
var cost = 100 + energyUpgradeLevel * 20;
money -= cost;
energyUpgradeLevel++;
energy += 10; // Give immediate energy boost
var maxEnergy = 100 + energyUpgradeLevel * 10;
if (energy > maxEnergy) energy = maxEnergy;
storage.money = money;
storage.energyUpgradeLevel = energyUpgradeLevel;
moneyTxt.setText('Money: ' + money);
energyTxt.setText('Energy: ' + energy);
energyUpgradeBtn.setText('Energy +10 (Cost: ' + (100 + energyUpgradeLevel * 20) + ')');
}
};
// Speed upgrade handler
speedUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 150 + speedUpgradeLevel * 30;
if (money >= cost) {
money -= cost;
speedUpgradeLevel++;
storage.money = money;
storage.speedUpgradeLevel = speedUpgradeLevel;
moneyTxt.setText('Money: ' + money);
speedUpgradeBtn.setText('Speed +1 (Cost: ' + (150 + speedUpgradeLevel * 30) + ')');
}
};
// Max energy upgrade handler
maxEnergyUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 300 + energyUpgradeLevel * 50;
if (money >= cost) {
money -= cost;
energyUpgradeLevel += 2;
energy += 20; // Give immediate energy boost
var maxEnergy = 100 + energyUpgradeLevel * 10;
if (energy > maxEnergy) energy = maxEnergy;
storage.money = money;
storage.energyUpgradeLevel = energyUpgradeLevel;
moneyTxt.setText('Money: ' + money);
energyTxt.setText('Energy: ' + energy);
maxEnergyUpgradeBtn.setText('Max Energy +20 (Cost: ' + (300 + energyUpgradeLevel * 50) + ')');
}
};
// Damage upgrade handler
damageUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 200 + damageUpgradeLevel * 40;
if (money >= cost) {
money -= cost;
damageUpgradeLevel++;
storage.money = money;
storage.damageUpgradeLevel = damageUpgradeLevel;
moneyTxt.setText('Money: ' + money);
damageUpgradeBtn.setText('Damage +1 (Cost: ' + (200 + damageUpgradeLevel * 40) + ')');
}
};
// Health upgrade handler
healthUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 250 + healthUpgradeLevel * 50;
if (money >= cost) {
money -= cost;
healthUpgradeLevel++;
maxHealth = 10 + healthUpgradeLevel * 2;
playerHealth += 2; // Give immediate health boost
if (playerHealth > maxHealth) playerHealth = maxHealth;
storage.money = money;
storage.healthUpgradeLevel = healthUpgradeLevel;
moneyTxt.setText('Money: ' + money);
healthTxt.setText('Health: ' + playerHealth);
healthUpgradeBtn.setText('Max Health +2 (Cost: ' + (250 + healthUpgradeLevel * 50) + ')');
}
};
// Range upgrade handler
rangeUpgradeBuyBtn.down = function (x, y, obj) {
var cost = 180 + rangeUpgradeLevel * 35;
if (money >= cost) {
money -= cost;
rangeUpgradeLevel++;
storage.money = money;
storage.rangeUpgradeLevel = rangeUpgradeLevel;
moneyTxt.setText('Money: ' + money);
rangeUpgradeBtn.setText('Arrow Range +20% (Cost: ' + (180 + rangeUpgradeLevel * 35) + ')');
}
};
closeShopBtn.down = function (x, y, obj) {
shopVisible = false;
shopContainer.visible = false;
// Resume the game when shop closes
gamePaused = false;
// Increase difficulty slightly each time shop closes
if (maxEnemies < 8) {
maxEnemies++;
}
if (enemySpawnRate > 20) {
enemySpawnRate -= 2;
}
};
// Removed advanced shop event handlers - items moved to main shop
game.addChild(shopContainer);
// Manual shooting - touch screen to shoot
game.down = function (x, y, obj) {
// Only allow manual shooting when hack mode is not active
if (!autoBattleActive && energy > 0) {
// Convert touch position to game coordinates
// Check if obj and obj.parent exist before calling toGlobal
if (obj && obj.parent && obj.parent.toGlobal) {
var gamePos = game.toLocal(obj.parent.toGlobal({
x: x,
y: y
}));
shootArrowAtTarget(gamePos.x, gamePos.y);
} else {
// Fallback: use x, y directly as they should already be in the correct coordinate system
shootArrowAtTarget(x, y);
}
}
};
function shootArrowAtTarget(targetX, targetY) {
// Check if player has enough energy
if (energy <= 0) {
return;
}
// Calculate direction from archer to target point
var dirX = targetX - archer.x;
var dirY = targetY - archer.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
// Normalize direction
var normalX = dirX / distance;
var normalY = dirY / distance;
// Calculate base angle for arrow direction
var baseAngle = Math.atan2(normalY, normalX);
// Make archer face the shooting direction
archer.rotation = baseAngle;
// Fire arrows based on upgrade level
var arrowCount = upgradeArrowCounts[upgradeLevel];
var energyCost = upgradeLevel === 0 ? 2 : upgradeLevel === 1 ? 6 : upgradeLevel === 2 ? 12 : upgradeLevel === 3 ? 20 : upgradeLevel === 4 ? 35 : 50; // Normal: 2, Triple: 6, Octa: 12, 16-Arrow: 20, 32-Arrow: 35, 64-Arrow: 50
// Check if we have enough energy for the shot type
if (energy < energyCost) {
return;
}
for (var i = 0; i < arrowCount; i++) {
// Add spread based on upgrade level
var spreadAngle = 0;
if (upgradeLevel === 1) {
// Triple shot
spreadAngle = (i - 1) * 0.2; // -0.2, 0, 0.2 radians spread
} else if (upgradeLevel === 2) {
// Octa shot
spreadAngle = (i - 3.5) * 0.15; // Spread 8 arrows in fan pattern
} else if (upgradeLevel === 3) {
// 16-arrow shot
spreadAngle = (i - 7.5) * 0.12; // Spread 16 arrows in wide fan pattern
} else if (upgradeLevel === 4) {
// 32-arrow shot
spreadAngle = (i - 15.5) * 0.10; // Spread 32 arrows in very wide fan pattern
} else if (upgradeLevel === 5) {
// 64-arrow shot
spreadAngle = (i - 31.5) * 0.08; // Spread 64 arrows in extremely wide fan pattern
}
var finalAngle = baseAngle + spreadAngle;
var finalNormalX = Math.cos(finalAngle);
var finalNormalY = Math.sin(finalAngle);
// Create arrow
var newArrow = new Arrow();
newArrow.x = archer.x;
newArrow.y = archer.y;
var arrowSpeed = 15 + speedUpgradeLevel * 3;
newArrow.speedX = finalNormalX * arrowSpeed;
newArrow.speedY = finalNormalY * arrowSpeed;
// Rotate arrow to face direction
newArrow.rotation = finalAngle;
arrows.push(newArrow);
game.addChild(newArrow);
}
LK.getSound('shoot').play();
// Decrease energy
energy -= energyCost;
// Set shooting state
isShooting = true;
shootingTimer = 0;
// Update energy display
energyTxt.setText('Energy: ' + energy);
// Change color based on energy level
if (energy <= 20) {
energyTxt.tint = 0xff0000; // Red when low
} else if (energy <= 50) {
energyTxt.tint = 0xffff00; // Yellow when medium
} else {
energyTxt.tint = 0x00ff00; // Green when high
}
}
game.update = function () {
// Skip all game logic when game is paused (shop is open)
if (gamePaused) {
return;
}
// Update power-up timers
if (shieldActive) {
shieldTimer--;
var seconds = Math.ceil(shieldTimer / 60);
shieldTimerTxt.setText('Shield: ' + seconds + 's');
shieldTimerTxt.visible = true;
if (shieldTimer <= 0) {
shieldActive = false;
shieldTimerTxt.visible = false;
if (!superShieldActive) {
archer.tint = 0xFFFFFF; // Reset archer color only if super shield not active
}
}
}
if (superShieldActive) {
superShieldTimer--;
var seconds = Math.ceil(superShieldTimer / 60);
superShieldTimerTxt.setText('Super Shield: ' + seconds + 's');
superShieldTimerTxt.visible = true;
if (superShieldTimer <= 0) {
superShieldActive = false;
superShieldTimerTxt.visible = false;
if (!shieldActive) {
archer.tint = 0xFFFFFF; // Reset archer color only if regular shield not active
}
}
}
if (timeSlowActive) {
timeSlowTimer--;
var seconds = Math.ceil(timeSlowTimer / 60);
timeSlowTimerTxt.setText('Time Slow: ' + seconds + 's');
timeSlowTimerTxt.visible = true;
if (timeSlowTimer <= 0) {
timeSlowActive = false;
timeSlowTimerTxt.visible = false;
}
}
if (tripleMoneyActive) {
tripleMoneyTimer--;
var seconds = Math.ceil(tripleMoneyTimer / 60);
tripleMoneyTimerTxt.setText('3x Money: ' + seconds + 's');
tripleMoneyTimerTxt.visible = true;
if (tripleMoneyTimer <= 0) {
tripleMoneyActive = false;
tripleMoneyTimerTxt.visible = false;
}
}
if (freezeActive) {
freezeTimer--;
var seconds = Math.ceil(freezeTimer / 60);
freezeTimerTxt.setText('Freeze: ' + seconds + 's');
freezeTimerTxt.visible = true;
if (freezeTimer <= 0) {
freezeActive = false;
freezeTimerTxt.visible = false;
}
}
if (explosiveActive) {
explosiveTimer--;
var seconds = Math.ceil(explosiveTimer / 60);
explosiveTimerTxt.setText('Explosive: ' + seconds + 's');
explosiveTimerTxt.visible = true;
if (explosiveTimer <= 0) {
explosiveActive = false;
explosiveTimerTxt.visible = false;
}
}
if (doubleMoneyActive) {
doubleMoneyTimer--;
var seconds = Math.ceil(doubleMoneyTimer / 60);
doubleMoneyTimerTxt.setText('2x Money: ' + seconds + 's');
doubleMoneyTimerTxt.visible = true;
if (doubleMoneyTimer <= 0) {
doubleMoneyActive = false;
doubleMoneyTimerTxt.visible = false;
}
}
// Automatic shooting logic - only when hack mode is active
if (autoBattleActive) {
autoShootTimer++;
if (autoShootTimer >= 15) {
// Shoot every 0.25 seconds (faster rate for hack mode)
autoShootTimer = 0;
// Shoot at boss first if present, then mini bosses, then regular enemies
var targets = [];
if (boss) {
targets.push(boss);
}
for (var m = 0; m < miniBosses.length; m++) {
if (miniBosses[m]) {
targets.push(miniBosses[m]);
}
}
for (var e = 0; e < enemies.length; e++) {
if (enemies[e]) {
targets.push(enemies[e]);
}
}
for (var t = 0; t < targets.length; t++) {
var target = targets[t];
// Calculate direction from archer to target
var dirX = target.x - archer.x;
var dirY = target.y - archer.y;
var distance = Math.sqrt(dirX * dirX + dirY * dirY);
// Normalize direction
var normalX = dirX / distance;
var normalY = dirY / distance;
// Make archer face the shooting direction
archer.rotation = Math.atan2(normalY, normalX);
// Create arrow without energy cost
var newArrow = new Arrow();
newArrow.x = archer.x;
newArrow.y = archer.y;
newArrow.speedX = normalX * 15;
newArrow.speedY = normalY * 15;
// Rotate arrow to face direction
newArrow.rotation = Math.atan2(normalY, normalX);
arrows.push(newArrow);
game.addChild(newArrow);
}
// Play shoot sound if we shot any arrows
if (targets.length > 0) {
LK.getSound('shoot').play();
}
}
}
// Update shooting timer
if (isShooting) {
shootingTimer++;
if (shootingTimer >= 30) {
// Stop shooting state after 0.5 seconds
isShooting = false;
}
}
// Update energy regeneration only when not shooting (every 60 frames = 1 second at 60 FPS)
if (!isShooting) {
energyRegenTimer++;
if (energyRegenTimer >= 60) {
energyRegenTimer = 0;
var maxEnergy = 100 + energyUpgradeLevel * 10;
if (energy < maxEnergy) {
energy += 1;
// Update energy display
energyTxt.setText('Energy: ' + energy);
// Change color based on energy level
if (energy <= 20) {
energyTxt.tint = 0xff0000; // Red when low
} else if (energy <= 50) {
energyTxt.tint = 0xffff00; // Yellow when medium
} else {
energyTxt.tint = 0x00ff00; // Green when high
}
}
}
} else {
energyRegenTimer = 0; // Reset regen timer when shooting
}
// Spawn enemies randomly on screen when needed (only when no boss is active)
if (!boss && enemies.length + miniBosses.length < maxEnemies) {
enemySpawnTimer++;
var currentSpawnRate = timeSlowActive ? enemySpawnRate * 2 : enemySpawnRate;
if (enemySpawnTimer >= currentSpawnRate) {
enemySpawnTimer = 0;
// 30% chance to spawn mini boss, 70% chance for regular enemy
var spawnMiniBoss = Math.random() < 0.3;
if (spawnMiniBoss && miniBosses.length < 2) {
// Spawn mini boss
var newMiniBoss = new MiniBoss();
// Scale mini boss health with wave
var baseHealth = 3;
var waveHealthBonus = Math.floor((currentWave - 1) / 2);
newMiniBoss.health = baseHealth + waveHealthBonus;
newMiniBoss.healthTxt.setText(newMiniBoss.health.toString());
newMiniBoss.x = Math.random() * (2048 - 200) + 100;
newMiniBoss.y = Math.random() * (2732 - 200) + 100;
// Make sure mini boss doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(newMiniBoss.x - archer.x, 2) + Math.pow(newMiniBoss.y - archer.y, 2));
if (distToArcher < 200) {
newMiniBoss.x = Math.random() * (2048 - 400) + 200;
if (newMiniBoss.x > archer.x - 200 && newMiniBoss.x < archer.x + 200) {
newMiniBoss.x += 300;
}
}
miniBosses.push(newMiniBoss);
game.addChild(newMiniBoss);
} else {
// Spawn regular enemy
var newEnemy = new Enemy();
// Scale enemy health with wave
var baseHealth = 2;
var waveHealthBonus = Math.floor((currentWave - 1) / 2);
newEnemy.health = baseHealth + waveHealthBonus;
newEnemy.healthTxt.setText(newEnemy.health.toString());
newEnemy.x = Math.random() * (2048 - 200) + 100;
newEnemy.y = Math.random() * (2732 - 200) + 100;
// Make sure enemy doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(newEnemy.x - archer.x, 2) + Math.pow(newEnemy.y - archer.y, 2));
if (distToArcher < 200) {
// Respawn farther away
newEnemy.x = Math.random() * (2048 - 400) + 200;
if (newEnemy.x > archer.x - 200 && newEnemy.x < archer.x + 200) {
newEnemy.x += 300;
}
}
enemies.push(newEnemy);
game.addChild(newEnemy);
}
}
}
// Update arrows
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Remove arrows that hit walls or go off screen
if (arrow.x >= 2048 || arrow.x <= 0 || arrow.y >= 2732 || arrow.y <= 0) {
arrow.destroy();
arrows.splice(i, 1);
continue;
}
// Check arrow-enemy arrow collisions
var arrowDestroyed = false;
for (var k = enemyArrows.length - 1; k >= 0; k--) {
var enemyArrow = enemyArrows[k];
if (arrow && enemyArrow && arrow.intersects(enemyArrow)) {
// Both arrows destroy each other
arrow.destroy();
arrows.splice(i, 1);
enemyArrow.destroy();
enemyArrows.splice(k, 1);
arrowDestroyed = true;
break;
}
}
// Skip enemy collision check if arrow was destroyed by enemy arrow
if (arrowDestroyed) {
continue;
}
// Check arrow-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (enemy && arrow && arrow.intersects(enemy)) {
// Hit enemy - reduce health
enemy.health -= 1 + damageUpgradeLevel;
enemy.healthTxt.setText(enemy.health.toString());
LK.getSound('hit').play();
LK.effects.flashObject(enemy, explosiveActive ? 0xFF4500 : 0xff0000, 200);
// Check if enemy is defeated
if (enemy.health <= 0) {
// Give money with bonuses
var moneyGain = tripleMoneyActive ? 15 : doubleMoneyActive ? 10 : 5;
money += moneyGain;
kills++;
storage.money = money;
storage.kills = kills;
moneyTxt.setText('Money: ' + money);
// Explosive arrows destroy nearby enemies
if (explosiveActive) {
for (var k = enemies.length - 1; k >= 0; k--) {
if (k !== j && enemies[k]) {
var dist = Math.sqrt(Math.pow(enemies[k].x - enemy.x, 2) + Math.pow(enemies[k].y - enemy.y, 2));
if (dist < 150) {
// Explosion radius
var bonusGain = tripleMoneyActive ? 15 : doubleMoneyActive ? 10 : 5;
money += bonusGain;
kills++;
storage.money = money;
storage.kills = kills;
LK.effects.flashObject(enemies[k], 0xFF4500, 300);
enemies[k].destroy();
enemies.splice(k, 1);
if (k < j) j--; // Adjust index if we removed an enemy before current one
}
}
}
moneyTxt.setText('Money: ' + money);
}
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
}
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
// Check arrow-mini boss collisions
for (var mb = miniBosses.length - 1; mb >= 0; mb--) {
var miniBoss = miniBosses[mb];
if (miniBoss && arrow && arrow.intersects(miniBoss)) {
// Hit mini boss - reduce health
miniBoss.health -= 1 + damageUpgradeLevel;
miniBoss.healthTxt.setText(miniBoss.health.toString());
LK.getSound('hit').play();
LK.effects.flashObject(miniBoss, explosiveActive ? 0xFF4500 : 0x800080, 200);
// Check if mini boss is defeated
if (miniBoss.health <= 0) {
// Give more money for mini boss
var moneyGain = tripleMoneyActive ? 30 : doubleMoneyActive ? 20 : 10;
money += moneyGain;
kills += 3; // Mini boss counts as 3 kills
storage.money = money;
storage.kills = kills;
moneyTxt.setText('Money: ' + money);
// Explosive arrows destroy nearby enemies
if (explosiveActive) {
// Check explosion damage to regular enemies
for (var k = enemies.length - 1; k >= 0; k--) {
if (enemies[k]) {
var dist = Math.sqrt(Math.pow(enemies[k].x - miniBoss.x, 2) + Math.pow(enemies[k].y - miniBoss.y, 2));
if (dist < 150) {
var bonusGain = tripleMoneyActive ? 15 : doubleMoneyActive ? 10 : 5;
money += bonusGain;
kills++;
storage.money = money;
storage.kills = kills;
LK.effects.flashObject(enemies[k], 0xFF4500, 300);
enemies[k].destroy();
enemies.splice(k, 1);
}
}
}
// Check explosion damage to other mini bosses
for (var k = miniBosses.length - 1; k >= 0; k--) {
if (k !== mb && miniBosses[k]) {
var dist = Math.sqrt(Math.pow(miniBosses[k].x - miniBoss.x, 2) + Math.pow(miniBosses[k].y - miniBoss.y, 2));
if (dist < 150) {
miniBosses[k].health--;
miniBosses[k].healthTxt.setText(miniBosses[k].health.toString());
LK.effects.flashObject(miniBosses[k], 0xFF4500, 300);
if (miniBosses[k].health <= 0) {
var bonusGain = tripleMoneyActive ? 30 : doubleMoneyActive ? 20 : 10;
money += bonusGain;
kills += 3;
storage.money = money;
storage.kills = kills;
miniBosses[k].destroy();
miniBosses.splice(k, 1);
if (k < mb) mb--;
}
}
}
}
moneyTxt.setText('Money: ' + money);
}
// Remove mini boss
miniBoss.destroy();
miniBosses.splice(mb, 1);
}
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
// Check arrow-boss collision
if (boss && arrow && arrow.intersects(boss)) {
// Hit boss
boss.health--;
LK.getSound('hit').play();
LK.effects.flashObject(boss, 0xff0000, 300);
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
// Boss moves to new position when hit
boss.moveToNewPosition();
// Check if boss is defeated
if (boss.health <= 0) {
money += 50; // Bonus money for defeating boss
kills += 10; // Count boss as 10 kills
bossesKilled++;
currentWave++;
// Increase difficulty with each wave
maxEnemies = Math.min(10, 1 + Math.floor(currentWave / 2));
enemySpawnRate = Math.max(15, 120 - currentWave * 8);
// Enemies get stronger each wave
if (currentWave >= 3) {
// Starting from wave 3, some enemies have more health
var healthBonus = Math.floor((currentWave - 2) / 2);
// This will be applied when spawning enemies
}
storage.money = money;
storage.kills = kills;
moneyTxt.setText('Money: ' + money);
waveTxt.setText('Wave: ' + currentWave);
boss.destroy();
boss = null;
bossSpawnScore = kills + Math.floor(Math.random() * 21) + 30; // Next boss spawns after random 30-50 more kills
}
break;
}
}
// Update enemy arrows
for (var m = enemyArrows.length - 1; m >= 0; m--) {
var enemyArrow = enemyArrows[m];
// Remove arrows that hit walls or go off screen
if (enemyArrow.x >= 2048 || enemyArrow.x <= 0 || enemyArrow.y >= 2732 || enemyArrow.y <= 0) {
enemyArrow.destroy();
enemyArrows.splice(m, 1);
continue;
}
// Check if enemy arrow hits player
if (enemyArrow.intersects(archer)) {
if (!shieldActive && !superShieldActive) {
playerHealth--;
healthTxt.setText('Health: ' + playerHealth);
LK.getSound('playerHit').play();
LK.effects.flashObject(archer, 0xff0000, 300);
// Check if player is dead
if (playerHealth <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
} else {
// Shield blocks the arrow
var shieldColor = superShieldActive ? 0x8000FF : 0x4169E1;
LK.effects.flashObject(archer, shieldColor, 200);
}
enemyArrow.destroy();
enemyArrows.splice(m, 1);
continue;
}
}
// Check if hack mode is active and score exceeds 200 to show HACKER text
if (autoBattleActive && LK.getScore() > 200) {
// Create red HACKER text if it doesn't exist
if (!game.hackerText) {
game.hackerText = new Text2('HACKER', {
size: 200,
fill: 0xff0000
});
game.hackerText.anchor.set(0.5, 0.5);
game.hackerText.x = 2048 / 2;
game.hackerText.y = 2732 / 2;
game.addChild(game.hackerText);
// Apply pulsing red glow effect
tween(game.hackerText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(game.hackerText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the pulsing effect
if (game.hackerText) {
tween(game.hackerText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
}
} else if (game.hackerText) {
// Remove HACKER text if conditions not met
game.hackerText.destroy();
game.hackerText = null;
}
// Update boss health display
if (boss) {
bossHealthTxt.setText('Boss Health: ' + boss.health);
bossHealthTxt.x = boss.x;
bossHealthTxt.y = boss.y - 80;
bossHealthTxt.visible = true;
} else {
bossHealthTxt.visible = false;
}
// Update boss countdown display
if (boss === null) {
var killsUntilBoss = bossSpawnScore - kills;
if (killsUntilBoss > 0) {
bossCountdownTxt.setText('Boss in: ' + killsUntilBoss + ' kills');
bossCountdownTxt.visible = true;
} else {
bossCountdownTxt.visible = false;
}
} else {
bossCountdownTxt.visible = false;
}
// Spawn boss every 20 kills
if (boss === null && kills >= bossSpawnScore) {
// Clear all existing enemies when boss spawns
for (var e = enemies.length - 1; e >= 0; e--) {
enemies[e].destroy();
enemies.splice(e, 1);
}
// Clear all mini bosses when boss spawns
for (var mb = miniBosses.length - 1; mb >= 0; mb--) {
miniBosses[mb].destroy();
miniBosses.splice(mb, 1);
}
boss = new Boss();
boss.x = Math.random() * (2048 - 400) + 200;
boss.y = Math.random() * (2732 - 400) + 200;
// Make sure boss doesn't spawn too close to archer
var distToArcher = Math.sqrt(Math.pow(boss.x - archer.x, 2) + Math.pow(boss.y - archer.y, 2));
if (distToArcher < 300) {
boss.x = Math.random() * (2048 - 600) + 300;
if (boss.x > archer.x - 300 && boss.x < archer.x + 300) {
boss.x += 400;
}
}
// Scale boss health with wave
var baseHealth = 10;
var waveHealthBonus = Math.floor((currentWave - 1) / 3) * 5; // +5 health every 3 waves
boss.health = baseHealth + waveHealthBonus;
game.addChild(boss);
}
// When enemy dies, increase max enemies and spawn rate for difficulty
if (LK.getScore() > 0 && LK.getScore() % 50 === 0) {
if (maxEnemies < 5) {
maxEnemies++;
}
if (enemySpawnRate > 30) {
enemySpawnRate -= 5;
}
}
};