User prompt
The game should not be lost unless either the tower’s HP or the character’s HP reaches 0. Add an HP bar below the character that follows them and displays their current health. Also, increase the HP of all towers by 3 times their original value.
User prompt
Advance to the next wave after 30 enemies have been defeated.
User prompt
Enemy attacks should come in waves. After each wave passes, all enemies’ HP should increase by 10. At wave 5, a boss attacks. Waves 6, 7, 8, and 9 continue with regular enemy attacks. At wave 10, two bosses attack together. Waves 11, 12, 13, and 14 continue with regular enemy attacks. At wave 15, both regular enemies and bosses attack together.
User prompt
Make the enemy attacks continue nonstop without any pauses or breaks.
User prompt
When the boss’s HP reaches 0, remove the boss from the game until the next scheduled boss attack wave occurs.
User prompt
Make the character attack the boss enemy.
User prompt
The character should immediately stop attacking arrows as soon as the boss dies. Ensure the attack logic checks the boss’s alive status before continuing to shoot.
User prompt
After the boss attack ends, wave 6 should start automatically and regular enemy attacks should continue without stopping.
User prompt
When the boss dies, the character should stop shooting arrows, and wave 6 of enemy attacks should begin, continuing up to wave 9. At wave 10, two bosses should appear and attack alongside other enemies. Wave 11 should continue with normal enemy attacks. At wave 15, three bosses along with other enemies should attack. The game continues this way until wave 100. At wave 100, the main character appears as an enemy with 10,000 HP.
User prompt
When the character dies, stop shooting arrows and trigger the game over state.
User prompt
Remove all flower visuals from the game. They should no longer appear in the environment or on the map.
User prompt
Enemies should not die upon touching the main building; they can only be defeated by arrow attacks.
User prompt
Increase the selectable area of the character to be 4 times larger than its original size.
User prompt
Allow the character to be selectable not just from its center point, but from any part of the area it visually occupies on the screen.
User prompt
Double the HP of all regular enemies. When any enemy touches the main building, reduce the building’s HP by 2 per second as long as the enemy is in contact. Increase the boss enemy’s HP to 10 times its original value.
User prompt
Replace the red troll that currently appears as the boss with the actual boss asset from the assets panel. The correct boss asset should be used during boss waves instead of the placeholder red troll.
User prompt
Make sure the boss enemy also spawns and joins the attack like other enemies during wave 5 and every multiple of 5. The game should not stop at wave 5 — the boss must appear and participate in the attack as an active enemy unit.
User prompt
When a speed option is selected, the entire game should accelerate accordingly — enemy movement, attack frequency, and all game actions should adjust to the selected speed (1.5x, 2x, 3x, or 5x). ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: LK.setTimeScale is not a function' in or related to this line: 'LK.setTimeScale(gameSpeed);' Line Number: 1229
User prompt
Add a game speed button at the bottom-left corner of the screen with options to speed up the game at 1.5x, 2x, 3x, and 5x speeds.
User prompt
The boss should launch solo attack waves at wave 5 and at every multiple of 5 thereafter (e.g., 5, 10, 15, 20, etc.).
User prompt
Add the boss enemy to the group of attacking enemies so it participates in attacks alongside them.
User prompt
After each attack round, enemy health should increase to make the game progressively harder, and skeleton enemies’ walking speed should gradually increase over time. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Here’s a clear prompt for creating a boss enemy:
User prompt
After every 20 enemy attacks, all attacks should stop and a single main boss should appear. The boss’s attacks should repeat in multiples of 20 (e.g., at 20, 40, 60 attacks, and so on).
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
self.health = Math.floor(8000 * difficultyScaling.healthMultiplier);
self.maxHealth = Math.floor(8000 * difficultyScaling.healthMultiplier);
self.speed = 0.6;
self.goldValue = 100;
self.currentTarget = null;
self.lastX = 0;
self.lastY = 0;
self.dustTimer = 0;
self.motionEffects = [];
var graphics = self.attachAsset('boos', {
anchorX: 0.5,
anchorY: 1
});
// Scale boss to be larger
graphics.scaleX = 1.5;
graphics.scaleY = 1.5;
graphics.tint = 0x800080; // Purple tint for boss
// Add glowing red eyes
self.redEyes = self.attachAsset('redEyes', {
anchorX: 0.5,
anchorY: 0.5
});
self.redEyes.y = -graphics.height * 0.8;
self.redEyes.scaleX = 1.5;
self.redEyes.scaleY = 1.5;
// Make eyes glow with pulsing effect
self.eyeGlowDirection = 1;
self.eyeGlowAlpha = 0.8;
self.targetX = 1024;
self.targetY = 1366;
self.findNearestTarget = function () {
var nearestTarget = null;
var shortestDistance = Infinity;
// Check crystal tower
var dx = crystalTower.x - self.x;
var dy = crystalTower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
nearestTarget = crystalTower;
shortestDistance = distance;
// Check hero
dx = hero.x - self.x;
dy = hero.y - self.y;
distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
nearestTarget = hero;
shortestDistance = distance;
}
// Check towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
dx = tower.x - self.x;
dy = tower.y - self.y;
distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
nearestTarget = tower;
shortestDistance = distance;
}
}
return nearestTarget;
};
self.createDustCloud = function () {
var dust = LK.getAsset('dustCloud', {
anchorX: 0.5,
anchorY: 0.5
});
dust.x = self.x + (Math.random() - 0.5) * 30;
dust.y = self.y + (Math.random() - 0.5) * 20;
dust.alpha = 0.6;
game.addChild(dust);
// Animate dust cloud
tween(dust, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: dust.y - 20
}, {
duration: Math.floor(800 / gameSpeed),
easing: tween.easeOut,
onFinish: function onFinish() {
dust.destroy();
}
});
};
self.createMotionLine = function () {
var motionLine = LK.getAsset('motionLine', {
anchorX: 0,
anchorY: 0.5
});
motionLine.x = self.x - 40;
motionLine.y = self.y - 20;
motionLine.alpha = 0.7;
// Calculate motion direction for line orientation
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
if (dx !== 0 || dy !== 0) {
motionLine.rotation = Math.atan2(dy, dx);
}
game.addChild(motionLine);
self.motionEffects.push(motionLine);
// Fade out motion line
tween(motionLine, {
alpha: 0,
scaleX: 0.5
}, {
duration: Math.floor(600 / gameSpeed),
easing: tween.easeOut,
onFinish: function onFinish() {
for (var i = self.motionEffects.length - 1; i >= 0; i--) {
if (self.motionEffects[i] === motionLine) {
self.motionEffects.splice(i, 1);
break;
}
}
motionLine.destroy();
}
});
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Clean up motion effects
for (var i = 0; i < self.motionEffects.length; i++) {
self.motionEffects[i].destroy();
}
var coin = new GoldCoin();
coin.x = self.x;
coin.y = self.y;
coin.value = self.goldValue;
game.addChild(coin);
goldCoins.push(coin);
LK.getSound('enemyDeath').play();
enemyKills++; // Increment kill counter when boss dies
// Continue wave progression after boss death and re-enable hero shooting
bossActive = false;
heroCanShoot = true;
// Remove boss from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
self.update = function () {
// Store last position for motion effects
self.lastX = self.x;
self.lastY = self.y;
// Update glowing eyes effect
self.eyeGlowAlpha += self.eyeGlowDirection * 0.05 * gameSpeed;
if (self.eyeGlowAlpha >= 1) {
self.eyeGlowDirection = -1;
} else if (self.eyeGlowAlpha <= 0.5) {
self.eyeGlowDirection = 1;
}
self.redEyes.alpha = self.eyeGlowAlpha;
// Find nearest target and charge toward it
self.currentTarget = self.findNearestTarget();
var targetX = self.currentTarget.x;
var targetY = self.currentTarget.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move toward target with charging behavior
var moveX = dx / distance * self.speed * gameSpeed;
var moveY = dy / distance * self.speed * gameSpeed;
self.x += moveX;
self.y += moveY;
// Create dust clouds periodically while moving
self.dustTimer += gameSpeed;
if (self.dustTimer >= 15) {
self.createDustCloud();
self.dustTimer = 0;
}
// Create motion lines for charging effect
if (LK.ticks % Math.max(1, Math.floor(8 / gameSpeed)) === 0) {
self.createMotionLine();
}
} else {
// Attack the target (boss deals more damage)
if (self.currentTarget === crystalTower) {
crystalTower.takeDamage(40);
enemyAttacks++;
} else if (self.currentTarget === hero) {
// Flash hero red when attacked
LK.effects.flashObject(hero, 0xff0000, 500);
// Damage hero
heroHealth -= 50;
enemyAttacks++;
if (heroHealth <= 0 && !heroCollapsed) {
heroCollapsed = true;
// Hero collapse effect
tween(hero, {
alpha: 0.3,
scaleX: 0.8,
scaleY: 0.4,
rotation: Math.PI / 2
}, {
duration: Math.floor(1000 / gameSpeed),
easing: tween.easeOut
});
// Add smoke effect
var smoke = new SmokeEffect();
smoke.x = hero.x;
smoke.y = hero.y;
game.addChild(smoke);
}
} else {
// Check if target is a tower
for (var i = 0; i < towers.length; i++) {
if (towers[i] === self.currentTarget) {
self.currentTarget.takeDamage();
enemyAttacks++;
break;
}
}
}
}
};
return self;
});
var CrystalTower = Container.expand(function () {
var self = Container.call(this);
self.health = 1500;
self.maxHealth = 1500;
self.range = 200;
self.damage = 40;
self.fireRate = 30;
self.lastShot = 0;
self.hits = 0;
self.maxHits = 10;
// Create magic aura for attack radius visualization
self.magicAura = new MagicAura();
self.addChild(self.magicAura);
// Scale aura to match tower range (range 200 = diameter 400)
self.magicAura.scaleX = self.range * 2 / 400;
self.magicAura.scaleY = self.range * 2 / 400;
var graphics = self.attachAsset('crystalTower', {
anchorX: 0.5,
anchorY: 1
});
self.takeDamage = function (damage) {
self.health -= damage;
self.hits++;
// Tower blinks red when taking damage
tween(graphics, {
tint: 0xff0000
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xffffff
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xff0000
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xffffff
}, {
duration: 150,
easing: tween.easeOut
});
}
});
}
});
}
});
LK.effects.flashObject(self, 0xff0000, 500);
// Add damage effects based on health percentage
var healthPercent = self.health / self.maxHealth;
if (healthPercent < 0.7) {
// Add cracks
var crack = LK.getAsset('crackEffect', {
anchorX: 0.5,
anchorY: 0.5
});
crack.x = self.x + (Math.random() - 0.5) * 100;
crack.y = self.y - Math.random() * 150;
crack.rotation = Math.random() * Math.PI;
crack.alpha = 0.8;
game.addChild(crack);
}
if (healthPercent < 0.5) {
// Add smoke effects
var smoke = new SmokeEffect();
smoke.x = self.x + (Math.random() - 0.5) * 80;
smoke.y = self.y - 100 - Math.random() * 50;
game.addChild(smoke);
}
if (healthPercent < 0.3) {
// Add red alert glow
LK.effects.flashScreen(0xff0000, 300);
}
if (self.hits >= self.maxHits) {
// Create massive explosion effect
for (var i = 0; i < 5; i++) {
var explosion = new ExplosionEffect();
explosion.x = self.x + (Math.random() - 0.5) * 150;
explosion.y = self.y - Math.random() * 200;
game.addChild(explosion);
}
// Play destruction sound
LK.getSound('towerDestroy').play();
// Flash screen red for dramatic effect
LK.effects.flashScreen(0xff0000, 1500);
LK.showGameOver();
}
};
self.findTarget = function () {
var closest = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closest = enemy;
closestDistance = distance;
}
}
return closest;
};
self.shoot = function (target) {
if (LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed)) {
var projectile = new TowerProjectile();
projectile.x = self.x;
projectile.y = self.y - 60;
projectile.target = target;
projectile.damage = self.damage;
game.addChild(projectile);
towerProjectiles.push(projectile);
self.lastShot = LK.ticks;
// Trigger magical attack aura effect
self.magicAura.showAttackPulse();
// Add magical flash effect to tower
LK.effects.flashObject(graphics, 0x4169e1, 500);
// Add muzzle flash
var flash = new MuzzleFlash();
flash.x = self.x;
flash.y = self.y - 80;
game.addChild(flash);
LK.getSound('towerShoot').play();
}
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.enemyType = type || 'goblin';
self.health = 100;
self.maxHealth = 100;
self.speed = 1;
self.goldValue = 10;
self.currentTarget = null;
self.lastX = 0;
self.lastY = 0;
self.dustTimer = 0;
self.motionEffects = [];
if (self.enemyType === 'goblin') {
self.health = Math.floor(160 * difficultyScaling.healthMultiplier);
self.maxHealth = Math.floor(160 * difficultyScaling.healthMultiplier);
self.speed = 1.5;
self.goldValue = 15;
var graphics = self.attachAsset('goblin', {
anchorX: 0.5,
anchorY: 1
});
} else if (self.enemyType === 'troll') {
self.health = Math.floor(400 * difficultyScaling.healthMultiplier);
self.maxHealth = Math.floor(400 * difficultyScaling.healthMultiplier);
self.speed = 0.8;
self.goldValue = 30;
var graphics = self.attachAsset('troll', {
anchorX: 0.5,
anchorY: 1
});
} else if (self.enemyType === 'skeleton') {
self.health = Math.floor(240 * difficultyScaling.healthMultiplier);
self.maxHealth = Math.floor(240 * difficultyScaling.healthMultiplier);
self.speed = 1.2 * difficultyScaling.skeletonSpeedMultiplier;
self.goldValue = 20;
var graphics = self.attachAsset('skeleton', {
anchorX: 0.5,
anchorY: 1
});
}
// Add glowing red eyes
self.redEyes = self.attachAsset('redEyes', {
anchorX: 0.5,
anchorY: 0.5
});
self.redEyes.y = -graphics.height * 0.8;
// Make eyes glow with pulsing effect
self.eyeGlowDirection = 1;
self.eyeGlowAlpha = 0.8;
self.targetX = 1024;
self.targetY = 1366;
self.findNearestTarget = function () {
var nearestTarget = null;
var shortestDistance = Infinity;
// Check crystal tower
var dx = crystalTower.x - self.x;
var dy = crystalTower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
nearestTarget = crystalTower;
shortestDistance = distance;
// Check hero
dx = hero.x - self.x;
dy = hero.y - self.y;
distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
nearestTarget = hero;
shortestDistance = distance;
}
// Check towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
dx = tower.x - self.x;
dy = tower.y - self.y;
distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
nearestTarget = tower;
shortestDistance = distance;
}
}
return nearestTarget;
};
self.createDustCloud = function () {
var dust = LK.getAsset('dustCloud', {
anchorX: 0.5,
anchorY: 0.5
});
dust.x = self.x + (Math.random() - 0.5) * 30;
dust.y = self.y + (Math.random() - 0.5) * 20;
dust.alpha = 0.6;
game.addChild(dust);
// Animate dust cloud
tween(dust, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: dust.y - 20
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
dust.destroy();
}
});
};
self.createMotionLine = function () {
var motionLine = LK.getAsset('motionLine', {
anchorX: 0,
anchorY: 0.5
});
motionLine.x = self.x - 40;
motionLine.y = self.y - 20;
motionLine.alpha = 0.7;
// Calculate motion direction for line orientation
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
if (dx !== 0 || dy !== 0) {
motionLine.rotation = Math.atan2(dy, dx);
}
game.addChild(motionLine);
self.motionEffects.push(motionLine);
// Fade out motion line
tween(motionLine, {
alpha: 0,
scaleX: 0.5
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
for (var i = self.motionEffects.length - 1; i >= 0; i--) {
if (self.motionEffects[i] === motionLine) {
self.motionEffects.splice(i, 1);
break;
}
}
motionLine.destroy();
}
});
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Clean up motion effects
for (var i = 0; i < self.motionEffects.length; i++) {
self.motionEffects[i].destroy();
}
var coin = new GoldCoin();
coin.x = self.x;
coin.y = self.y;
coin.value = self.goldValue;
game.addChild(coin);
goldCoins.push(coin);
LK.getSound('enemyDeath').play();
enemyKills++; // Increment kill counter when enemy dies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
self.update = function () {
// Store last position for motion effects
self.lastX = self.x;
self.lastY = self.y;
// Update glowing eyes effect
self.eyeGlowAlpha += self.eyeGlowDirection * 0.05 * gameSpeed;
if (self.eyeGlowAlpha >= 1) {
self.eyeGlowDirection = -1;
} else if (self.eyeGlowAlpha <= 0.5) {
self.eyeGlowDirection = 1;
}
self.redEyes.alpha = self.eyeGlowAlpha;
// Find nearest target and charge toward it
self.currentTarget = self.findNearestTarget();
var targetX = self.currentTarget.x;
var targetY = self.currentTarget.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move toward target with charging behavior
var moveX = dx / distance * self.speed * gameSpeed;
var moveY = dy / distance * self.speed * gameSpeed;
self.x += moveX;
self.y += moveY;
// Create dust clouds periodically while moving
self.dustTimer += gameSpeed;
if (self.dustTimer >= 15) {
self.createDustCloud();
self.dustTimer = 0;
}
// Create motion lines for charging effect
if (LK.ticks % Math.max(1, Math.floor(8 / gameSpeed)) === 0) {
self.createMotionLine();
}
} else {
// Attack the target
if (self.currentTarget === crystalTower) {
crystalTower.takeDamage(20);
enemyAttacks++;
} else if (self.currentTarget === hero) {
// Flash hero red when attacked
LK.effects.flashObject(hero, 0xff0000, 500);
// Damage hero
heroHealth -= 25;
enemyAttacks++;
if (heroHealth <= 0 && !heroCollapsed) {
heroCollapsed = true;
// Hero collapse effect
tween(hero, {
alpha: 0.3,
scaleX: 0.8,
scaleY: 0.4,
rotation: Math.PI / 2
}, {
duration: Math.floor(1000 / gameSpeed),
easing: tween.easeOut
});
// Add smoke effect
var smoke = new SmokeEffect();
smoke.x = hero.x;
smoke.y = hero.y;
game.addChild(smoke);
}
} else {
// Check if target is a tower
for (var i = 0; i < towers.length; i++) {
if (towers[i] === self.currentTarget) {
self.currentTarget.takeDamage();
enemyAttacks++;
break;
}
}
}
}
};
return self;
});
var ExplosionEffect = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.alpha = 0.8;
self.scaleX = 0.3;
self.scaleY = 0.3;
// Animate explosion
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var GoldCoin = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('goldCoin', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 10;
self.collected = false;
self.collect = function () {
if (!self.collected) {
self.collected = true;
gold += self.value;
goldText.setText('Gold: ' + gold);
LK.getSound('coinCollect').play();
for (var i = goldCoins.length - 1; i >= 0; i--) {
if (goldCoins[i] === self) {
goldCoins.splice(i, 1);
break;
}
}
self.destroy();
}
};
self.down = function (x, y, obj) {
self.collect();
};
return self;
});
var GrassPatch = Container.expand(function (grassType) {
var self = Container.call(this);
self.grassType = grassType || 'grassPatch';
self.swayAmount = 0.1 + Math.random() * 0.15; // Random sway intensity
self.swaySpeed = 2000 + Math.random() * 1000; // Random sway duration
var graphics = self.attachAsset(self.grassType, {
anchorX: 0.5,
anchorY: 1
});
// Start initial sway animation
self.startSway = function () {
var targetRotation = self.swayAmount * (Math.random() > 0.5 ? 1 : -1);
tween(self, {
rotation: targetRotation
}, {
duration: self.swaySpeed,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Reverse sway direction
var reverseRotation = -targetRotation * (0.8 + Math.random() * 0.4);
tween(self, {
rotation: reverseRotation
}, {
duration: self.swaySpeed * (0.8 + Math.random() * 0.4),
easing: tween.easeInOut,
onFinish: function onFinish() {
self.startSway(); // Continue swaying indefinitely
}
});
}
});
};
// Start swaying with random delay to create natural effect
LK.setTimeout(function () {
self.startSway();
}, Math.random() * 2000);
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 1
});
self.speed = 3;
self.range = 450;
self.damage = 35;
self.fireRate = 20;
self.lastShot = 0;
self.findTarget = function () {
var closest = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closest = enemy;
closestDistance = distance;
}
}
return closest;
};
self.shoot = function (target) {
if (LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed)) {
var projectile = new HeroProjectile();
projectile.x = self.x;
projectile.y = self.y - 25;
projectile.target = target;
projectile.damage = self.damage;
game.addChild(projectile);
heroProjectiles.push(projectile);
self.lastShot = LK.ticks;
}
};
self.update = function () {
// Only shoot if hero is not collapsed (dead) and is allowed to shoot
if (!heroCollapsed && heroCanShoot) {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
}
};
return self;
});
var HeroProjectile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('heroProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.target = null;
self.damage = 35;
self.update = function () {
if (!self.target || self.target.destroyed) {
self.destroy();
for (var i = heroProjectiles.length - 1; i >= 0; i--) {
if (heroProjectiles[i] === self) {
heroProjectiles.splice(i, 1);
break;
}
}
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
LK.getSound('arrowImpact').play();
self.destroy();
for (var i = heroProjectiles.length - 1; i >= 0; i--) {
if (heroProjectiles[i] === self) {
heroProjectiles.splice(i, 1);
break;
}
}
} else {
self.x += dx / distance * self.speed * gameSpeed;
self.y += dy / distance * self.speed * gameSpeed;
}
};
return self;
});
var MagicAura = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('magicAura', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.alpha = 0.2;
self.pulseDirection = 1;
self.basePulse = 0.15;
self.currentPulse = self.basePulse;
self.update = function () {
// Create pulsing effect
self.currentPulse += self.pulseDirection * 0.003;
if (self.currentPulse >= 0.4) {
self.pulseDirection = -1;
} else if (self.currentPulse <= self.basePulse) {
self.pulseDirection = 1;
}
graphics.alpha = self.currentPulse;
// Slight rotation for magical effect
graphics.rotation += 0.01;
};
self.showAttackPulse = function () {
// Flash brighter when tower attacks
tween(graphics, {
alpha: 0.7,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
alpha: self.currentPulse,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
};
return self;
});
var MuzzleFlash = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('muzzleFlash', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.alpha = 0.9;
self.scaleX = 0.5;
self.scaleY = 0.5;
// Quick flash effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var SmokeEffect = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('smokeCloud', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.alpha = 0.4;
self.scaleX = 0.8;
self.scaleY = 0.8;
// Rising smoke animation
tween(self, {
y: self.y - 100,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
self.towerType = type || 'archer';
self.range = 150;
self.damage = 25;
self.fireRate = 60;
self.lastShot = 0;
self.hits = 0;
self.maxHits = 4;
if (self.towerType === 'archer') {
self.range = 180;
self.damage = 30;
self.fireRate = 45;
var graphics = self.attachAsset('archerTower', {
anchorX: 0.5,
anchorY: 1
});
} else if (self.towerType === 'cannon') {
self.range = 120;
self.damage = 60;
self.fireRate = 90;
var graphics = self.attachAsset('cannonTower', {
anchorX: 0.5,
anchorY: 1
});
}
self.takeDamage = function () {
self.hits++;
// Tower blinks red when taking damage
tween(graphics, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeOut
});
}
});
if (self.hits >= self.maxHits) {
// Create explosion effect
var explosion = new ExplosionEffect();
explosion.x = self.x;
explosion.y = self.y;
game.addChild(explosion);
// Play destruction sound
LK.getSound('towerDestroy').play();
// Remove from towers array
for (var i = towers.length - 1; i >= 0; i--) {
if (towers[i] === self) {
towers.splice(i, 1);
break;
}
}
self.destroy();
}
};
self.findTarget = function () {
var closest = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closest = enemy;
closestDistance = distance;
}
}
return closest;
};
self.shoot = function (target) {
if (LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed)) {
var projectile = new TowerProjectile();
projectile.x = self.x;
projectile.y = self.y - 40;
projectile.target = target;
projectile.damage = self.damage;
game.addChild(projectile);
towerProjectiles.push(projectile);
self.lastShot = LK.ticks;
LK.getSound('towerShoot').play();
// Add muzzle flash effect
var flash = new MuzzleFlash();
flash.x = self.x;
flash.y = self.y - 30;
game.addChild(flash);
}
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
return self;
});
var TowerProjectile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('towerProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.target = null;
self.damage = 25;
self.update = function () {
if (!self.target || self.target.destroyed) {
self.destroy();
for (var i = towerProjectiles.length - 1; i >= 0; i--) {
if (towerProjectiles[i] === self) {
towerProjectiles.splice(i, 1);
break;
}
}
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.target.takeDamage(self.damage);
self.destroy();
for (var i = towerProjectiles.length - 1; i >= 0; i--) {
if (towerProjectiles[i] === self) {
towerProjectiles.splice(i, 1);
break;
}
}
} else {
self.x += dx / distance * self.speed * gameSpeed;
self.y += dy / distance * self.speed * gameSpeed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a4d1a
});
/****
* Game Code
****/
var enemies = [];
var towers = [];
var towerProjectiles = [];
var heroProjectiles = [];
var goldCoins = [];
var crystalTower;
var hero;
var gold = 100;
var wave = 1;
var enemiesInWave = 5;
var enemySpawnTimer = 0;
var minEnemiesOnField = 3; // Always maintain minimum enemies
var enemiesSpawned = 0;
var waveComplete = false;
var draggedHero = false;
var heroCollapsed = false;
var heroHealth = 100;
var heroMaxHealth = 100;
var enemyAttacks = 0;
var enemyKills = 0;
var bossActive = false;
var difficultyScaling = {
healthMultiplier: 1.0,
skeletonSpeedMultiplier: 1.0
};
// Control hero shooting behavior
var heroCanShoot = true;
// Track enemies in contact with crystal tower for continuous damage
var enemiesInContact = [];
var contactDamageTimer = 0;
// Game speed control
var gameSpeed = 1.0;
var gameSpeedOptions = [1.0, 1.5, 2.0, 3.0, 5.0];
var currentSpeedIndex = 0;
// Grass background removed - clean battlefield environment
var grassPatches = [];
// Create crystal tower at center
crystalTower = new CrystalTower();
crystalTower.x = 1024;
crystalTower.y = 1366;
game.addChild(crystalTower);
// Create hero
hero = new Hero();
hero.x = 900;
hero.y = 1200;
game.addChild(hero);
// Create hero HP bar that follows hero
var heroHealthBar = LK.getAsset('uiPanel', {
anchorX: 0.5,
anchorY: 0.5
});
heroHealthBar.scaleX = 1.5;
heroHealthBar.scaleY = 0.3;
heroHealthBar.tint = 0x404040;
game.addChild(heroHealthBar);
var heroHealthFill = LK.getAsset('uiPanel', {
anchorX: 0,
anchorY: 0.5
});
heroHealthFill.scaleX = 1.5;
heroHealthFill.scaleY = 0.25;
heroHealthFill.tint = 0x00ff00;
heroHealthBar.addChild(heroHealthFill);
heroHealthFill.x = -150;
heroHealthFill.y = 0;
var heroHealthText = new Text2(heroHealth + '/' + heroMaxHealth, {
size: 20,
fill: 0xFFFFFF
});
heroHealthText.anchor.set(0.5, 0.5);
heroHealthBar.addChild(heroHealthText);
// UI Elements
var goldText = new Text2('Gold: ' + gold, {
size: 60,
fill: 0xFFD700
});
goldText.anchor.set(0, 0);
LK.gui.topRight.addChild(goldText);
goldText.x = -200;
goldText.y = 20;
var waveText = new Text2('Wave: ' + wave, {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0, 0);
LK.gui.top.addChild(waveText);
waveText.x = -100;
waveText.y = 20;
var healthText = new Text2('Tower HP: ' + crystalTower.health, {
size: 50,
fill: 0x00FFFF
});
healthText.anchor.set(0, 0);
LK.gui.bottom.addChild(healthText);
healthText.x = -150;
healthText.y = -80;
// Boss health bar (initially hidden)
var bossHealthBar = LK.getAsset('uiPanel', {
anchorX: 0.5,
anchorY: 0.5
});
bossHealthBar.scaleX = 4;
bossHealthBar.scaleY = 0.5;
bossHealthBar.tint = 0x800080;
bossHealthBar.visible = false;
LK.gui.top.addChild(bossHealthBar);
bossHealthBar.x = 0;
bossHealthBar.y = 100;
var bossHealthFill = LK.getAsset('uiPanel', {
anchorX: 0,
anchorY: 0.5
});
bossHealthFill.scaleX = 4;
bossHealthFill.scaleY = 0.4;
bossHealthFill.tint = 0xff0000;
bossHealthFill.visible = false;
bossHealthBar.addChild(bossHealthFill);
bossHealthFill.x = -400;
bossHealthFill.y = 0;
var bossHealthText = new Text2('BOSS', {
size: 40,
fill: 0xFFFFFF
});
bossHealthText.anchor.set(0.5, 0.5);
bossHealthText.visible = false;
LK.gui.top.addChild(bossHealthText);
bossHealthText.x = 0;
bossHealthText.y = 60;
// Tower placement UI
var archerButton = LK.getAsset('uiPanel', {
anchorX: 0.5,
anchorY: 0.5
});
archerButton.x = 150;
archerButton.y = -150;
LK.gui.bottom.addChild(archerButton);
var archerText = new Text2('Archer\n50g', {
size: 30,
fill: 0xFFFFFF
});
archerText.anchor.set(0.5, 0.5);
archerButton.addChild(archerText);
var cannonButton = LK.getAsset('uiPanel', {
anchorX: 0.5,
anchorY: 0.5
});
cannonButton.x = 400;
cannonButton.y = -150;
LK.gui.bottom.addChild(cannonButton);
var cannonText = new Text2('Cannon\n80g', {
size: 30,
fill: 0xFFFFFF
});
cannonText.anchor.set(0.5, 0.5);
cannonButton.addChild(cannonText);
// Game speed button
var speedButton = LK.getAsset('uiPanel', {
anchorX: 0.5,
anchorY: 0.5
});
speedButton.x = 150;
speedButton.y = -50;
speedButton.scaleX = 1.2;
speedButton.scaleY = 0.8;
speedButton.tint = 0x4169e1;
LK.gui.bottomLeft.addChild(speedButton);
var speedText = new Text2('Speed: 1.0x', {
size: 28,
fill: 0xFFFFFF
});
speedText.anchor.set(0.5, 0.5);
speedButton.addChild(speedText);
// Speed button handler
speedButton.down = function (x, y, obj) {
currentSpeedIndex = (currentSpeedIndex + 1) % gameSpeedOptions.length;
gameSpeed = gameSpeedOptions[currentSpeedIndex];
speedText.setText('Speed: ' + gameSpeed + 'x');
// Note: Game speed functionality not available in LK engine
};
// Tower placement handlers
archerButton.down = function (x, y, obj) {
if (gold >= 50) {
placingTowerType = 'archer';
placingTowerCost = 50;
}
};
cannonButton.down = function (x, y, obj) {
if (gold >= 80) {
placingTowerType = 'cannon';
placingTowerCost = 80;
}
};
var placingTowerType = null;
var placingTowerCost = 0;
function spawnEnemy() {
var enemyTypes = ['goblin', 'goblin', 'skeleton'];
if (wave >= 3) enemyTypes.push('troll');
if (wave >= 5) enemyTypes.push('troll', 'troll');
var randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy = new Enemy(randomType);
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
if (side === 0) {
// Top
enemy.x = Math.random() * 2048;
enemy.y = 0;
} else if (side === 1) {
// Right
enemy.x = 2048;
enemy.y = Math.random() * 2732;
} else if (side === 2) {
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2732;
} else {
// Left
enemy.x = 0;
enemy.y = Math.random() * 2732;
}
game.addChild(enemy);
enemies.push(enemy);
enemiesSpawned++;
}
function checkWaveComplete() {
if (enemyKills >= 30) {
// Start next wave immediately after 30 kills
wave++;
// Increase all enemy HP by 10 after each wave
difficultyScaling.healthMultiplier += 10 / 160; // Base goblin health is 160, so this adds roughly 10 HP
enemiesInWave += 2;
enemiesSpawned = 0;
enemyKills = 0; // Reset kill counter for next wave
bossActive = false; // Reset boss active flag for next wave
// Re-enable hero shooting for wave 6 and beyond (after first boss death)
if (wave >= 6) {
heroCanShoot = true;
}
waveText.setText('Wave: ' + wave);
if (wave > 100) {
LK.showYouWin();
}
}
}
game.down = function (x, y, obj) {
if (placingTowerType) {
// Check if position is valid (not too close to crystal tower or other towers)
var tooClose = false;
var dx = x - crystalTower.x;
var dy = y - crystalTower.y;
if (Math.sqrt(dx * dx + dy * dy) < 200) {
tooClose = true;
}
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var dx = x - tower.x;
var dy = y - tower.y;
if (Math.sqrt(dx * dx + dy * dy) < 120) {
tooClose = true;
break;
}
}
if (!tooClose) {
var newTower = new Tower(placingTowerType);
newTower.x = x;
newTower.y = y;
game.addChild(newTower);
towers.push(newTower);
gold -= placingTowerCost;
goldText.setText('Gold: ' + gold);
LK.getSound('towerPlace').play();
}
placingTowerType = null;
placingTowerCost = 0;
} else {
// Check if clicking on hero to start drag - expanded hitbox 4x larger than original visual area
var dx = x - hero.x;
var dy = y - hero.y;
// Hero asset is 125x156, expand hitbox to 4x the size (500x624 total area)
var heroWidth = 125 * 2; // 4x area means 2x width/height
var heroHeight = 156 * 2;
if (Math.abs(dx) < heroWidth / 2 && Math.abs(dy) < heroHeight / 2) {
draggedHero = true;
}
}
};
game.move = function (x, y, obj) {
if (draggedHero) {
hero.x = x;
hero.y = y;
}
};
game.up = function (x, y, obj) {
draggedHero = false;
};
game.update = function () {
// Determine boss spawn requirements based on wave
var shouldSpawnBoss = false;
var bossCount = 0;
var shouldSpawnRegularEnemies = true;
if (wave === 5 && !bossActive && enemiesSpawned === 0) {
shouldSpawnBoss = true;
bossCount = 1;
shouldSpawnRegularEnemies = false; // Only boss on wave 5
} else if (wave === 10 && !bossActive && enemiesSpawned === 0) {
shouldSpawnBoss = true;
bossCount = 2;
shouldSpawnRegularEnemies = false; // Only bosses on wave 10
} else if (wave === 15 && !bossActive && enemiesSpawned === 0) {
shouldSpawnBoss = true;
bossCount = 1; // One boss plus regular enemies
shouldSpawnRegularEnemies = true; // Both bosses and regular enemies
} else if (wave === 100 && !bossActive && enemiesSpawned === 0) {
// Special wave 100: spawn main character as boss
shouldSpawnBoss = true;
bossCount = 1;
shouldSpawnRegularEnemies = false;
}
if (shouldSpawnBoss) {
for (var bossIndex = 0; bossIndex < bossCount; bossIndex++) {
var boss;
if (wave === 100) {
// Create main character as enemy with 10,000 HP
boss = new Boss();
boss.health = 10000;
boss.maxHealth = 10000;
boss.goldValue = 1000;
// Make it look like the hero
boss.removeChild(boss.attachedAssets[0]); // Remove boss graphics
var heroGraphics = boss.attachAsset('hero', {
anchorX: 0.5,
anchorY: 1
});
heroGraphics.tint = 0x800000; // Dark red tint to show it's evil
heroGraphics.scaleX = 2; // Make it larger
heroGraphics.scaleY = 2;
} else {
boss = new Boss();
}
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
if (side === 0) {
// Top
boss.x = Math.random() * 2048;
boss.y = 0;
} else if (side === 1) {
// Right
boss.x = 2048;
boss.y = Math.random() * 2732;
} else if (side === 2) {
// Bottom
boss.x = Math.random() * 2048;
boss.y = 2732;
} else {
// Left
boss.x = 0;
boss.y = Math.random() * 2732;
}
game.addChild(boss);
enemies.push(boss);
enemiesSpawned++; // Count boss as spawned enemy
}
bossActive = true;
// Flash screen to indicate boss spawn
LK.effects.flashScreen(0x800080, 1000);
// Increase difficulty scaling after each boss round
difficultyScaling.healthMultiplier += 0.15; // 15% health increase per round
difficultyScaling.skeletonSpeedMultiplier += 0.08; // 8% speed increase for skeletons per round
}
// Spawn regular enemies continuously - always keep enemies on the field
// Only spawn regular enemies if it's not a boss-only wave (5, 10) or if it's a mixed wave (15)
var shouldSpawnRegularNow = true;
if ((wave === 5 || wave === 10) && !bossActive) {
shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves before boss spawns
} else if ((wave === 5 || wave === 10) && bossActive) {
shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves after boss spawns
}
if (shouldSpawnRegularNow && (enemiesSpawned < enemiesInWave || enemies.length < 3)) {
enemySpawnTimer += gameSpeed;
if (enemySpawnTimer >= 60) {
// Spawn every 1 second for continuous pressure
spawnEnemy();
enemySpawnTimer = 0;
}
}
// Update health display
healthText.setText('Tower HP: ' + crystalTower.health);
// Update boss health bar
var currentBoss = null;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].health === undefined || enemies[i].maxHealth === undefined) continue;
if (enemies[i].maxHealth >= 800) {
// Boss has 800+ max health
currentBoss = enemies[i];
break;
}
}
if (currentBoss && bossActive) {
bossHealthBar.visible = true;
bossHealthFill.visible = true;
bossHealthText.visible = true;
var healthPercent = currentBoss.health / currentBoss.maxHealth;
bossHealthFill.scaleX = 4 * healthPercent;
bossHealthText.setText('BOSS - ' + currentBoss.health + '/' + currentBoss.maxHealth);
} else {
bossHealthBar.visible = false;
bossHealthFill.visible = false;
bossHealthText.visible = false;
}
// Check for enemies in contact with crystal tower for continuous damage
var currentEnemiesInContact = [];
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - crystalTower.x;
var dy = enemy.y - crystalTower.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
// Contact threshold
currentEnemiesInContact.push(enemy);
}
}
// Apply contact damage every second (60 ticks)
contactDamageTimer += gameSpeed;
if (contactDamageTimer >= 60 && currentEnemiesInContact.length > 0) {
crystalTower.takeDamage(2 * currentEnemiesInContact.length);
contactDamageTimer = 0;
}
// Update enemies in contact array
enemiesInContact = currentEnemiesInContact;
// Check for hero collecting coins
for (var i = goldCoins.length - 1; i >= 0; i--) {
var coin = goldCoins[i];
var dx = coin.x - hero.x;
var dy = coin.y - hero.y;
if (Math.sqrt(dx * dx + dy * dy) < 40) {
coin.collect();
}
}
// Add random battlefield explosions during intense moments
if (enemies.length > 8 && LK.ticks % Math.max(1, Math.floor(180 / gameSpeed)) === 0) {
var explosion = new ExplosionEffect();
explosion.x = 200 + Math.random() * 1648;
explosion.y = 200 + Math.random() * 2332;
game.addChild(explosion);
}
// Add red alert glow when tower health is critical
if (crystalTower.health < crystalTower.maxHealth * 0.2 && LK.ticks % Math.max(1, Math.floor(60 / gameSpeed)) === 0) {
LK.effects.flashScreen(0x8b0000, 200);
}
// Hero can continue fighting even when collapsed - no game over from hero death
// Update hero HP bar position to follow hero
heroHealthBar.x = hero.x;
heroHealthBar.y = hero.y - 80; // Position above hero
// Update hero HP bar display
var heroHealthPercent = Math.max(0, heroHealth) / heroMaxHealth;
heroHealthFill.scaleX = 1.5 * heroHealthPercent;
heroHealthText.setText(Math.max(0, heroHealth) + '/' + heroMaxHealth);
// Change HP bar color based on health
if (heroHealthPercent > 0.6) {
heroHealthFill.tint = 0x00ff00; // Green
} else if (heroHealthPercent > 0.3) {
heroHealthFill.tint = 0xffff00; // Yellow
} else {
heroHealthFill.tint = 0xff0000; // Red
}
checkWaveComplete();
}; ===================================================================
--- original.js
+++ change.js
@@ -235,10 +235,10 @@
return self;
});
var CrystalTower = Container.expand(function () {
var self = Container.call(this);
- self.health = 500;
- self.maxHealth = 500;
+ self.health = 1500;
+ self.maxHealth = 1500;
self.range = 200;
self.damage = 40;
self.fireRate = 30;
self.lastShot = 0;
@@ -1084,8 +1084,33 @@
hero = new Hero();
hero.x = 900;
hero.y = 1200;
game.addChild(hero);
+// Create hero HP bar that follows hero
+var heroHealthBar = LK.getAsset('uiPanel', {
+ anchorX: 0.5,
+ anchorY: 0.5
+});
+heroHealthBar.scaleX = 1.5;
+heroHealthBar.scaleY = 0.3;
+heroHealthBar.tint = 0x404040;
+game.addChild(heroHealthBar);
+var heroHealthFill = LK.getAsset('uiPanel', {
+ anchorX: 0,
+ anchorY: 0.5
+});
+heroHealthFill.scaleX = 1.5;
+heroHealthFill.scaleY = 0.25;
+heroHealthFill.tint = 0x00ff00;
+heroHealthBar.addChild(heroHealthFill);
+heroHealthFill.x = -150;
+heroHealthFill.y = 0;
+var heroHealthText = new Text2(heroHealth + '/' + heroMaxHealth, {
+ size: 20,
+ fill: 0xFFFFFF
+});
+heroHealthText.anchor.set(0.5, 0.5);
+heroHealthBar.addChild(heroHealthText);
// UI Elements
var goldText = new Text2('Gold: ' + gold, {
size: 60,
fill: 0xFFD700
@@ -1461,11 +1486,22 @@
// Add red alert glow when tower health is critical
if (crystalTower.health < crystalTower.maxHealth * 0.2 && LK.ticks % Math.max(1, Math.floor(60 / gameSpeed)) === 0) {
LK.effects.flashScreen(0x8b0000, 200);
}
- // Check if hero is dead and trigger game over
- if (heroCollapsed) {
- LK.showGameOver();
- return; // Stop further game updates
+ // Hero can continue fighting even when collapsed - no game over from hero death
+ // Update hero HP bar position to follow hero
+ heroHealthBar.x = hero.x;
+ heroHealthBar.y = hero.y - 80; // Position above hero
+ // Update hero HP bar display
+ var heroHealthPercent = Math.max(0, heroHealth) / heroMaxHealth;
+ heroHealthFill.scaleX = 1.5 * heroHealthPercent;
+ heroHealthText.setText(Math.max(0, heroHealth) + '/' + heroMaxHealth);
+ // Change HP bar color based on health
+ if (heroHealthPercent > 0.6) {
+ heroHealthFill.tint = 0x00ff00; // Green
+ } else if (heroHealthPercent > 0.3) {
+ heroHealthFill.tint = 0xffff00; // Yellow
+ } else {
+ heroHealthFill.tint = 0xff0000; // Red
}
checkWaveComplete();
};
\ No newline at end of file
izometric cannon tower. In-Game asset. 2d. High contrast. No shadows. izometric
archerTower. In-Game asset. 2d. High contrast. No shadows
goblin. In-Game asset. 2d. High contrast. No shadows
goldCoin. In-Game asset. 2d. High contrast. No shadows
Archer hero. In-Game asset. 2d. High contrast. No shadows
skeleton. In-Game asset. 2d. High contrast. No shadows
troll. In-Game asset. 2d. High contrast. No shadows
single arrow image. In-Game asset. 2d. High contrast. No shadows
Create a flying dragon enemy with the following features:. In-Game asset. 2d. High contrast. No shadows
Here’s a prompt for a very distant, wide-angle view of a tree-free forest floor: **Prompt:** A very distant, wide-angle aerial view of a tree-free forest floor, showing expansive grassy plains with patches of dirt, scattered rocks, and low vegetation. The terrain stretches far into the horizon with subtle color variations and soft natural lighting, creating a vast, open, and serene natural environment without any trees.. In-Game asset. 2d. High contrast. No shadows
A stylized full-body illustration of a small hobbit holding a glowing ring in one hand, viewed from a 45-degree angle. The hobbit has curly hair, bare feet, and wears rustic, earth-toned clothing with detailed textures. The scene has warm, soft lighting emphasizing the character’s expressive face and the shining ring. The art style is cartoonish with rich colors, smooth shading, and a fantasy atmosphere.. In-Game asset. 2d. High contrast. No shadows
A magical yellow light glowing softly, with radiant beams and sparkling particles floating around. The light has a warm, enchanting aura that illuminates its surroundings with a golden hue. The atmosphere feels mystical and inviting, perfect for fantasy scenes or magical effects.. In-Game asset. 2d. High contrast. No shadows
A full-body stylized illustration of Smeagol (Gollum), showing his thin, hunched frame and large expressive eyes. He is barefoot and shirtless, wearing ragged shorts, with exaggerated cartoonish features that highlight his creepy yet pitiful nature. He clutches a glowing precious ring tightly in one hand. The art style is dark fantasy with vibrant colors, detailed skin textures, and a shadowy, mysterious background to enhance the eerie atmosphere. Perfect for full-character concept art or game design.. In-Game asset. 2d. High contrast. No shadows
A full-body stylized illustration of an orc warrior, standing in a dynamic pose. The orc has green or grayish skin, muscular build, tusks, and tribal armor made of bone, leather, and metal. The style is fantasy-themed with bold lines, exaggerated proportions, and detailed textures. The lighting is dramatic, emphasizing the orc’s strength and menace. Background is minimal or softly blurred to keep focus on the character. Suitable for fantasy RPG game concept art.. In-Game asset. 2d. High contrast. No shadows
A stylized fantasy axe with a broad, curved blade and intricate engravings. The handle is wrapped in worn leather, and the metal has a slightly weathered look, giving it a battle-worn feel. The design is bold and exaggerated, suitable for an orc warrior, with a glowing rune etched into the blade. The style is high-fantasy with clean lines, vibrant highlights, and a dramatic shadow for depth. Perfect for 2D game assets or concept art.. In-Game asset. 2d. High contrast. No shadows