/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
unlockedWeapons: [0]
});
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphic = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.damage = 10;
self.attackCooldown = 60; // frames
self.attackTimer = 0;
self.scoreValue = 10;
self.healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
y: -80
});
self.healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
y: -80
});
self.healthBar.width = self.health / self.maxHealth * 100;
self.takeDamage = function (amount) {
self.health -= amount;
self.healthBar.width = self.health / self.maxHealth * 100;
// Flash red when hit
tween(enemyGraphic, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphic, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.health <= 0) {
LK.getSound('enemyDeath').play();
var randomChance = Math.random();
if (randomChance < 0.2) {
// 20% chance to drop powerup
var powerup = new PowerUp();
powerup.x = self.x;
powerup.y = self.y;
game.addChild(powerup);
powerups.push(powerup);
}
self.readyToRemove = true;
return true;
}
return false;
};
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.update = function () {
if (self.attackTimer > 0) {
self.attackTimer--;
}
};
return self;
});
var BossEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace the enemy graphic with a boss graphic
self.removeChild(self.children[0]); // Remove regular enemy graphic
var bossGraphic = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 150;
self.maxHealth = 150;
self.speed = 1.5;
self.damage = 20;
self.attackCooldown = 120;
self.scoreValue = 50;
// Update health bar position and size for boss
self.healthBar.y = -120;
self.healthBarBg.y = -120;
self.healthBar.width = 200;
self.healthBarBg.width = 200;
// Special boss attack pattern
var attackPhase = 0;
self.update = function () {
Enemy.prototype.update.call(self); // Call the parent update method
if (self.attackTimer === 0) {
// Boss can fire projectiles
attackPhase = (attackPhase + 1) % 3;
switch (attackPhase) {
case 0:
// Fire in cardinal directions
for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 2) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.setDirection(Math.cos(angle), Math.sin(angle));
projectile.damage = 15;
projectile.isEnemyProjectile = true;
game.addChild(projectile);
enemyProjectiles.push(projectile);
}
break;
case 1:
// Fire at player
if (warrior) {
var dx = warrior.x - self.x;
var dy = warrior.y - self.y;
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.setDirection(dx, dy);
projectile.damage = 15;
projectile.isEnemyProjectile = true;
game.addChild(projectile);
enemyProjectiles.push(projectile);
}
break;
case 2:
// Spawn minions
var minion = new Enemy();
minion.x = self.x + (Math.random() * 200 - 100);
minion.y = self.y + (Math.random() * 200 - 100);
minion.health = 15;
minion.maxHealth = 15;
minion.speed = 3;
game.addChild(minion);
enemies.push(minion);
break;
}
self.attackTimer = self.attackCooldown;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphic = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.floor(Math.random() * 3); // 0: health, 1: speed, 2: damage
// Different colors for different powerup types
switch (self.type) {
case 0:
// Health
powerupGraphic.tint = 0x33cc33; // Green
break;
case 1:
// Speed
powerupGraphic.tint = 0x3366ff; // Blue
break;
case 2:
// Damage
powerupGraphic.tint = 0xff3333; // Red
break;
}
self.lifespan = 300; // frames the powerup will stay
self.age = 0;
// Pulsing animation
self.update = function () {
self.age++;
var scale = 1 + 0.1 * Math.sin(self.age * 0.1);
powerupGraphic.scale.set(scale, scale);
if (self.age >= self.lifespan) {
self.readyToRemove = true;
}
};
self.applyEffect = function (warrior) {
LK.getSound('powerupCollect').play();
switch (self.type) {
case 0:
// Health
warrior.health = Math.min(warrior.health + 30, warrior.maxHealth);
warrior.updateHealthBar();
break;
case 1:
// Speed
warrior.speedBoost = 300; // Number of frames for speed boost
break;
case 2:
// Damage
warrior.damageBoost = 300; // Number of frames for damage boost
break;
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphic = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.damage = 10;
self.direction = {
x: 0,
y: 0
};
self.lifespan = 120; // frames the projectile will live
self.age = 0;
self.setDirection = function (dirX, dirY) {
// Normalize direction vector
var length = Math.sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
self.direction.x = dirX / length;
self.direction.y = dirY / length;
}
};
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
self.age++;
if (self.age >= self.lifespan) {
self.readyToRemove = true;
}
};
return self;
});
var Warrior = Container.expand(function () {
var self = Container.call(this);
var warriorGraphic = self.attachAsset('warrior', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 5;
self.damage = 20;
self.attackCooldown = 20; // frames
self.attackTimer = 0;
self.invulnerable = false;
self.invulnerableTime = 0;
self.speedBoost = 0;
self.damageBoost = 0;
// Health bar displayed at the top of the screen
self.healthBar = null;
self.healthBarBg = null;
self.initializeHealthBar = function () {
self.healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 30
});
self.healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
width: 300,
height: 30
});
LK.gui.top.addChild(self.healthBarBg);
self.healthBarBg.y = 50;
LK.gui.top.addChild(self.healthBar);
self.healthBar.y = 50;
self.healthBar.x = self.healthBarBg.x - self.healthBarBg.width / 2;
self.updateHealthBar();
};
self.updateHealthBar = function () {
if (self.healthBar) {
self.healthBar.width = self.health / self.maxHealth * 300;
}
};
self.takeDamage = function (amount) {
if (self.invulnerable) {
return false;
}
self.health -= amount;
self.updateHealthBar();
LK.getSound('playerHit').play();
// Flash and set invulnerable briefly
self.invulnerable = true;
self.invulnerableTime = 60; // 1 second of invulnerability
tween(warriorGraphic, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorGraphic, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.health <= 0) {
LK.getSound('gameOver').play();
LK.showGameOver();
return true;
}
return false;
};
self.fire = function (dirX, dirY) {
if (self.attackTimer === 0) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.setDirection(dirX, dirY);
// Apply damage boost if active
if (self.damageBoost > 0) {
projectile.damage = self.damage * 1.5;
projectile.tint = 0xff0000; // Red tint for boosted projectiles
} else {
projectile.damage = self.damage;
}
game.addChild(projectile);
playerProjectiles.push(projectile);
self.attackTimer = self.attackCooldown;
}
};
self.update = function () {
if (self.attackTimer > 0) {
self.attackTimer--;
}
if (self.invulnerable) {
self.invulnerableTime--;
warriorGraphic.alpha = 0.5 + 0.5 * Math.sin(LK.ticks * 0.2);
if (self.invulnerableTime <= 0) {
self.invulnerable = false;
warriorGraphic.alpha = 1;
}
}
// Handle boosts
if (self.speedBoost > 0) {
self.speedBoost--;
warriorGraphic.tint = 0x3366ff; // Blue tint during speed boost
} else {
warriorGraphic.tint = 0xffffff; // Reset tint
}
if (self.damageBoost > 0) {
self.damageBoost--;
if (!self.speedBoost) {
// Only tint red if not speed boosted
warriorGraphic.tint = 0xff3333; // Red tint during damage boost
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state variables
var warrior = null;
var enemies = [];
var playerProjectiles = [];
var enemyProjectiles = [];
var powerups = [];
var waveNumber = 1;
var enemiesPerWave = 5;
var score = 0;
var waveClearTimeBonus = 5000; // Base time bonus for clearing waves
var waveStartTime = 0;
var gameActive = false;
var arena = null;
var arenaSize = 1800;
var waveText = null;
var scoreText = null;
var dragging = false;
var lastMoveX = 0;
var lastMoveY = 0;
// Initialize the game
function initGame() {
// Reset game state
warrior = null;
enemies = [];
playerProjectiles = [];
enemyProjectiles = [];
powerups = [];
waveNumber = 1;
enemiesPerWave = 5;
score = 0;
gameActive = true;
// Set score for LK
LK.setScore(score);
// Create arena
arena = LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(arena);
arena.x = 2048 / 2;
arena.y = 2732 / 2;
arena.alpha = 0.5;
// Create warrior
warrior = new Warrior();
warrior.x = 2048 / 2;
warrior.y = 2732 / 2;
game.addChild(warrior);
warrior.initializeHealthBar();
// Create UI elements
createUI();
// Start first wave
startWave();
// Play background music
LK.playMusic('battleMusic');
}
function createUI() {
// Score text
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreText);
scoreText.x = -20;
scoreText.y = 20;
// Wave text
waveText = new Text2('Wave 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0, 0);
LK.gui.topRight.addChild(waveText);
waveText.x = -20;
waveText.y = 80;
}
function updateUI() {
scoreText.setText('Score: ' + score);
waveText.setText('Wave ' + waveNumber);
}
function startWave() {
waveStartTime = Date.now();
// Create enemies for this wave
var enemiesToSpawn = enemiesPerWave + waveNumber;
// Every 3rd wave has a boss
if (waveNumber % 3 === 0) {
var boss = new BossEnemy();
placeEnemyOutsideArena(boss);
game.addChild(boss);
enemies.push(boss);
enemiesToSpawn -= 3; // Boss counts as 3 regular enemies
}
for (var i = 0; i < enemiesToSpawn; i++) {
var enemy = new Enemy();
// Scale difficulty with wave number
enemy.health = 30 + waveNumber * 5;
enemy.maxHealth = enemy.health;
enemy.speed = 2 + waveNumber * 0.1;
enemy.damage = 10 + waveNumber * 2;
placeEnemyOutsideArena(enemy);
game.addChild(enemy);
enemies.push(enemy);
}
updateUI();
}
function placeEnemyOutsideArena(enemy) {
var angle = Math.random() * Math.PI * 2;
var distance = arenaSize / 2 + 100 + Math.random() * 300; // Spawn outside arena
enemy.x = 2048 / 2 + Math.cos(angle) * distance;
enemy.y = 2732 / 2 + Math.sin(angle) * distance;
}
function waveCompleted() {
// Calculate time bonus
var timeElapsed = Date.now() - waveStartTime;
var timeBonus = Math.max(0, waveClearTimeBonus - Math.floor(timeElapsed / 100));
score += timeBonus;
LK.setScore(score);
// Update high score
if (score > storage.highScore) {
storage.highScore = score;
}
// Play victory sound
LK.getSound('waveComplete').play();
// Flash screen
LK.effects.flashScreen(0x33cc33, 500);
// Next wave
waveNumber++;
enemiesPerWave += 2;
// Brief delay before next wave
LK.setTimeout(function () {
startWave();
}, 2000);
}
// Game update function
game.update = function () {
if (!gameActive) {
return;
}
// Update warrior
if (warrior) {
warrior.update();
// Move warrior if dragging
if (dragging) {
// Calculate actual speed based on speed boost
var actualSpeed = warrior.speed;
if (warrior.speedBoost > 0) {
actualSpeed *= 1.5;
}
// Calculate direction to move
var dx = lastMoveX - warrior.x;
var dy = lastMoveY - warrior.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > actualSpeed) {
warrior.x += dx / distance * actualSpeed;
warrior.y += dy / distance * actualSpeed;
} else {
warrior.x = lastMoveX;
warrior.y = lastMoveY;
}
// Keep warrior inside the arena
var arenaRadius = arenaSize / 2;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var dx = warrior.x - centerX;
var dy = warrior.y - centerY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > arenaRadius) {
warrior.x = centerX + dx / distance * arenaRadius;
warrior.y = centerY + dy / distance * arenaRadius;
}
}
// Check for powerup collisions
for (var i = powerups.length - 1; i >= 0; i--) {
if (warrior.intersects(powerups[i])) {
powerups[i].applyEffect(warrior);
powerups[i].destroy();
powerups.splice(i, 1);
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Move towards warrior
if (warrior) {
enemy.moveTowards(warrior.x, warrior.y);
// Check for collision with warrior
if (warrior.intersects(enemy) && enemy.attackTimer === 0) {
warrior.takeDamage(enemy.damage);
enemy.attackTimer = enemy.attackCooldown;
}
}
// Remove dead enemies
if (enemy.readyToRemove) {
score += enemy.scoreValue;
LK.setScore(score);
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update player projectiles
for (var i = playerProjectiles.length - 1; i >= 0; i--) {
var proj = playerProjectiles[i];
proj.update();
// Check for enemy hits
for (var j = enemies.length - 1; j >= 0; j--) {
if (proj.intersects(enemies[j])) {
LK.getSound('hit').play();
var killed = enemies[j].takeDamage(proj.damage);
if (killed) {
score += enemies[j].scoreValue;
LK.setScore(score);
}
proj.readyToRemove = true;
break;
}
}
// Remove projectiles that are off-screen or have hit something
if (proj.readyToRemove || proj.x < 0 || proj.x > 2048 || proj.y < 0 || proj.y > 2732) {
proj.destroy();
playerProjectiles.splice(i, 1);
}
}
// Update enemy projectiles
for (var i = enemyProjectiles.length - 1; i >= 0; i--) {
var proj = enemyProjectiles[i];
proj.update();
// Check for warrior hit
if (warrior && proj.intersects(warrior)) {
warrior.takeDamage(proj.damage);
proj.readyToRemove = true;
}
// Remove projectiles that are off-screen or have hit something
if (proj.readyToRemove || proj.x < 0 || proj.x > 2048 || proj.y < 0 || proj.y > 2732) {
proj.destroy();
enemyProjectiles.splice(i, 1);
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].update();
if (powerups[i].readyToRemove) {
powerups[i].destroy();
powerups.splice(i, 1);
}
}
// Check if wave is completed
if (enemies.length === 0) {
waveCompleted();
}
// Update UI
updateUI();
};
// Input handling
function handleMove(x, y, obj) {
if (dragging && warrior) {
lastMoveX = x;
lastMoveY = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (warrior) {
dragging = true;
lastMoveX = x;
lastMoveY = y;
// Fire in the direction opposite to the touch
var dx = x - warrior.x;
var dy = y - warrior.y;
warrior.fire(dx, dy);
}
};
game.up = function (x, y, obj) {
dragging = false;
};
// Initialize game when script loads
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
unlockedWeapons: [0]
});
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphic = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.damage = 10;
self.attackCooldown = 60; // frames
self.attackTimer = 0;
self.scoreValue = 10;
self.healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
y: -80
});
self.healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
y: -80
});
self.healthBar.width = self.health / self.maxHealth * 100;
self.takeDamage = function (amount) {
self.health -= amount;
self.healthBar.width = self.health / self.maxHealth * 100;
// Flash red when hit
tween(enemyGraphic, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphic, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.health <= 0) {
LK.getSound('enemyDeath').play();
var randomChance = Math.random();
if (randomChance < 0.2) {
// 20% chance to drop powerup
var powerup = new PowerUp();
powerup.x = self.x;
powerup.y = self.y;
game.addChild(powerup);
powerups.push(powerup);
}
self.readyToRemove = true;
return true;
}
return false;
};
self.moveTowards = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.update = function () {
if (self.attackTimer > 0) {
self.attackTimer--;
}
};
return self;
});
var BossEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace the enemy graphic with a boss graphic
self.removeChild(self.children[0]); // Remove regular enemy graphic
var bossGraphic = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 150;
self.maxHealth = 150;
self.speed = 1.5;
self.damage = 20;
self.attackCooldown = 120;
self.scoreValue = 50;
// Update health bar position and size for boss
self.healthBar.y = -120;
self.healthBarBg.y = -120;
self.healthBar.width = 200;
self.healthBarBg.width = 200;
// Special boss attack pattern
var attackPhase = 0;
self.update = function () {
Enemy.prototype.update.call(self); // Call the parent update method
if (self.attackTimer === 0) {
// Boss can fire projectiles
attackPhase = (attackPhase + 1) % 3;
switch (attackPhase) {
case 0:
// Fire in cardinal directions
for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 2) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.setDirection(Math.cos(angle), Math.sin(angle));
projectile.damage = 15;
projectile.isEnemyProjectile = true;
game.addChild(projectile);
enemyProjectiles.push(projectile);
}
break;
case 1:
// Fire at player
if (warrior) {
var dx = warrior.x - self.x;
var dy = warrior.y - self.y;
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.setDirection(dx, dy);
projectile.damage = 15;
projectile.isEnemyProjectile = true;
game.addChild(projectile);
enemyProjectiles.push(projectile);
}
break;
case 2:
// Spawn minions
var minion = new Enemy();
minion.x = self.x + (Math.random() * 200 - 100);
minion.y = self.y + (Math.random() * 200 - 100);
minion.health = 15;
minion.maxHealth = 15;
minion.speed = 3;
game.addChild(minion);
enemies.push(minion);
break;
}
self.attackTimer = self.attackCooldown;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphic = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.floor(Math.random() * 3); // 0: health, 1: speed, 2: damage
// Different colors for different powerup types
switch (self.type) {
case 0:
// Health
powerupGraphic.tint = 0x33cc33; // Green
break;
case 1:
// Speed
powerupGraphic.tint = 0x3366ff; // Blue
break;
case 2:
// Damage
powerupGraphic.tint = 0xff3333; // Red
break;
}
self.lifespan = 300; // frames the powerup will stay
self.age = 0;
// Pulsing animation
self.update = function () {
self.age++;
var scale = 1 + 0.1 * Math.sin(self.age * 0.1);
powerupGraphic.scale.set(scale, scale);
if (self.age >= self.lifespan) {
self.readyToRemove = true;
}
};
self.applyEffect = function (warrior) {
LK.getSound('powerupCollect').play();
switch (self.type) {
case 0:
// Health
warrior.health = Math.min(warrior.health + 30, warrior.maxHealth);
warrior.updateHealthBar();
break;
case 1:
// Speed
warrior.speedBoost = 300; // Number of frames for speed boost
break;
case 2:
// Damage
warrior.damageBoost = 300; // Number of frames for damage boost
break;
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphic = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.damage = 10;
self.direction = {
x: 0,
y: 0
};
self.lifespan = 120; // frames the projectile will live
self.age = 0;
self.setDirection = function (dirX, dirY) {
// Normalize direction vector
var length = Math.sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
self.direction.x = dirX / length;
self.direction.y = dirY / length;
}
};
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
self.age++;
if (self.age >= self.lifespan) {
self.readyToRemove = true;
}
};
return self;
});
var Warrior = Container.expand(function () {
var self = Container.call(this);
var warriorGraphic = self.attachAsset('warrior', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 5;
self.damage = 20;
self.attackCooldown = 20; // frames
self.attackTimer = 0;
self.invulnerable = false;
self.invulnerableTime = 0;
self.speedBoost = 0;
self.damageBoost = 0;
// Health bar displayed at the top of the screen
self.healthBar = null;
self.healthBarBg = null;
self.initializeHealthBar = function () {
self.healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 30
});
self.healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
width: 300,
height: 30
});
LK.gui.top.addChild(self.healthBarBg);
self.healthBarBg.y = 50;
LK.gui.top.addChild(self.healthBar);
self.healthBar.y = 50;
self.healthBar.x = self.healthBarBg.x - self.healthBarBg.width / 2;
self.updateHealthBar();
};
self.updateHealthBar = function () {
if (self.healthBar) {
self.healthBar.width = self.health / self.maxHealth * 300;
}
};
self.takeDamage = function (amount) {
if (self.invulnerable) {
return false;
}
self.health -= amount;
self.updateHealthBar();
LK.getSound('playerHit').play();
// Flash and set invulnerable briefly
self.invulnerable = true;
self.invulnerableTime = 60; // 1 second of invulnerability
tween(warriorGraphic, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorGraphic, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.health <= 0) {
LK.getSound('gameOver').play();
LK.showGameOver();
return true;
}
return false;
};
self.fire = function (dirX, dirY) {
if (self.attackTimer === 0) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.setDirection(dirX, dirY);
// Apply damage boost if active
if (self.damageBoost > 0) {
projectile.damage = self.damage * 1.5;
projectile.tint = 0xff0000; // Red tint for boosted projectiles
} else {
projectile.damage = self.damage;
}
game.addChild(projectile);
playerProjectiles.push(projectile);
self.attackTimer = self.attackCooldown;
}
};
self.update = function () {
if (self.attackTimer > 0) {
self.attackTimer--;
}
if (self.invulnerable) {
self.invulnerableTime--;
warriorGraphic.alpha = 0.5 + 0.5 * Math.sin(LK.ticks * 0.2);
if (self.invulnerableTime <= 0) {
self.invulnerable = false;
warriorGraphic.alpha = 1;
}
}
// Handle boosts
if (self.speedBoost > 0) {
self.speedBoost--;
warriorGraphic.tint = 0x3366ff; // Blue tint during speed boost
} else {
warriorGraphic.tint = 0xffffff; // Reset tint
}
if (self.damageBoost > 0) {
self.damageBoost--;
if (!self.speedBoost) {
// Only tint red if not speed boosted
warriorGraphic.tint = 0xff3333; // Red tint during damage boost
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state variables
var warrior = null;
var enemies = [];
var playerProjectiles = [];
var enemyProjectiles = [];
var powerups = [];
var waveNumber = 1;
var enemiesPerWave = 5;
var score = 0;
var waveClearTimeBonus = 5000; // Base time bonus for clearing waves
var waveStartTime = 0;
var gameActive = false;
var arena = null;
var arenaSize = 1800;
var waveText = null;
var scoreText = null;
var dragging = false;
var lastMoveX = 0;
var lastMoveY = 0;
// Initialize the game
function initGame() {
// Reset game state
warrior = null;
enemies = [];
playerProjectiles = [];
enemyProjectiles = [];
powerups = [];
waveNumber = 1;
enemiesPerWave = 5;
score = 0;
gameActive = true;
// Set score for LK
LK.setScore(score);
// Create arena
arena = LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(arena);
arena.x = 2048 / 2;
arena.y = 2732 / 2;
arena.alpha = 0.5;
// Create warrior
warrior = new Warrior();
warrior.x = 2048 / 2;
warrior.y = 2732 / 2;
game.addChild(warrior);
warrior.initializeHealthBar();
// Create UI elements
createUI();
// Start first wave
startWave();
// Play background music
LK.playMusic('battleMusic');
}
function createUI() {
// Score text
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreText);
scoreText.x = -20;
scoreText.y = 20;
// Wave text
waveText = new Text2('Wave 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0, 0);
LK.gui.topRight.addChild(waveText);
waveText.x = -20;
waveText.y = 80;
}
function updateUI() {
scoreText.setText('Score: ' + score);
waveText.setText('Wave ' + waveNumber);
}
function startWave() {
waveStartTime = Date.now();
// Create enemies for this wave
var enemiesToSpawn = enemiesPerWave + waveNumber;
// Every 3rd wave has a boss
if (waveNumber % 3 === 0) {
var boss = new BossEnemy();
placeEnemyOutsideArena(boss);
game.addChild(boss);
enemies.push(boss);
enemiesToSpawn -= 3; // Boss counts as 3 regular enemies
}
for (var i = 0; i < enemiesToSpawn; i++) {
var enemy = new Enemy();
// Scale difficulty with wave number
enemy.health = 30 + waveNumber * 5;
enemy.maxHealth = enemy.health;
enemy.speed = 2 + waveNumber * 0.1;
enemy.damage = 10 + waveNumber * 2;
placeEnemyOutsideArena(enemy);
game.addChild(enemy);
enemies.push(enemy);
}
updateUI();
}
function placeEnemyOutsideArena(enemy) {
var angle = Math.random() * Math.PI * 2;
var distance = arenaSize / 2 + 100 + Math.random() * 300; // Spawn outside arena
enemy.x = 2048 / 2 + Math.cos(angle) * distance;
enemy.y = 2732 / 2 + Math.sin(angle) * distance;
}
function waveCompleted() {
// Calculate time bonus
var timeElapsed = Date.now() - waveStartTime;
var timeBonus = Math.max(0, waveClearTimeBonus - Math.floor(timeElapsed / 100));
score += timeBonus;
LK.setScore(score);
// Update high score
if (score > storage.highScore) {
storage.highScore = score;
}
// Play victory sound
LK.getSound('waveComplete').play();
// Flash screen
LK.effects.flashScreen(0x33cc33, 500);
// Next wave
waveNumber++;
enemiesPerWave += 2;
// Brief delay before next wave
LK.setTimeout(function () {
startWave();
}, 2000);
}
// Game update function
game.update = function () {
if (!gameActive) {
return;
}
// Update warrior
if (warrior) {
warrior.update();
// Move warrior if dragging
if (dragging) {
// Calculate actual speed based on speed boost
var actualSpeed = warrior.speed;
if (warrior.speedBoost > 0) {
actualSpeed *= 1.5;
}
// Calculate direction to move
var dx = lastMoveX - warrior.x;
var dy = lastMoveY - warrior.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > actualSpeed) {
warrior.x += dx / distance * actualSpeed;
warrior.y += dy / distance * actualSpeed;
} else {
warrior.x = lastMoveX;
warrior.y = lastMoveY;
}
// Keep warrior inside the arena
var arenaRadius = arenaSize / 2;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var dx = warrior.x - centerX;
var dy = warrior.y - centerY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > arenaRadius) {
warrior.x = centerX + dx / distance * arenaRadius;
warrior.y = centerY + dy / distance * arenaRadius;
}
}
// Check for powerup collisions
for (var i = powerups.length - 1; i >= 0; i--) {
if (warrior.intersects(powerups[i])) {
powerups[i].applyEffect(warrior);
powerups[i].destroy();
powerups.splice(i, 1);
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Move towards warrior
if (warrior) {
enemy.moveTowards(warrior.x, warrior.y);
// Check for collision with warrior
if (warrior.intersects(enemy) && enemy.attackTimer === 0) {
warrior.takeDamage(enemy.damage);
enemy.attackTimer = enemy.attackCooldown;
}
}
// Remove dead enemies
if (enemy.readyToRemove) {
score += enemy.scoreValue;
LK.setScore(score);
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update player projectiles
for (var i = playerProjectiles.length - 1; i >= 0; i--) {
var proj = playerProjectiles[i];
proj.update();
// Check for enemy hits
for (var j = enemies.length - 1; j >= 0; j--) {
if (proj.intersects(enemies[j])) {
LK.getSound('hit').play();
var killed = enemies[j].takeDamage(proj.damage);
if (killed) {
score += enemies[j].scoreValue;
LK.setScore(score);
}
proj.readyToRemove = true;
break;
}
}
// Remove projectiles that are off-screen or have hit something
if (proj.readyToRemove || proj.x < 0 || proj.x > 2048 || proj.y < 0 || proj.y > 2732) {
proj.destroy();
playerProjectiles.splice(i, 1);
}
}
// Update enemy projectiles
for (var i = enemyProjectiles.length - 1; i >= 0; i--) {
var proj = enemyProjectiles[i];
proj.update();
// Check for warrior hit
if (warrior && proj.intersects(warrior)) {
warrior.takeDamage(proj.damage);
proj.readyToRemove = true;
}
// Remove projectiles that are off-screen or have hit something
if (proj.readyToRemove || proj.x < 0 || proj.x > 2048 || proj.y < 0 || proj.y > 2732) {
proj.destroy();
enemyProjectiles.splice(i, 1);
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].update();
if (powerups[i].readyToRemove) {
powerups[i].destroy();
powerups.splice(i, 1);
}
}
// Check if wave is completed
if (enemies.length === 0) {
waveCompleted();
}
// Update UI
updateUI();
};
// Input handling
function handleMove(x, y, obj) {
if (dragging && warrior) {
lastMoveX = x;
lastMoveY = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (warrior) {
dragging = true;
lastMoveX = x;
lastMoveY = y;
// Fire in the direction opposite to the touch
var dx = x - warrior.x;
var dy = y - warrior.y;
warrior.fire(dx, dy);
}
};
game.up = function (x, y, obj) {
dragging = false;
};
// Initialize game when script loads
initGame();