/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
totalWins: 0,
totalMatches: 0
});
/****
* Classes
****/
var Entity = Container.expand(function () {
var self = Container.call(this);
self.speed = 0;
self.health = 100;
self.maxHealth = 100;
self.size = 100;
self.isDead = false;
self.init = function (x, y, color) {
self.x = x;
self.y = y;
};
self.takeDamage = function (amount) {
if (self.isDead) {
return;
}
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
self.die();
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 300);
};
self.die = function () {
if (self.isDead) {
return;
}
self.isDead = true;
tween(self, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.isOutOfBounds = function (bounds) {
return self.x < bounds.x || self.x > bounds.x + bounds.width || self.y < bounds.y || self.y > bounds.y + bounds.height;
};
self.keepInBounds = function (bounds) {
if (self.x < bounds.x) {
self.x = bounds.x;
} else if (self.x > bounds.x + bounds.width) {
self.x = bounds.x + bounds.width;
}
if (self.y < bounds.y) {
self.y = bounds.y;
} else if (self.y > bounds.y + bounds.height) {
self.y = bounds.y + bounds.height;
}
};
return self;
});
var Player = Entity.expand(function () {
var self = Entity.call(this);
self.speed = 5;
self.cooldown = 0;
self.shootCooldownMax = 30; // half a second at 60fps
self.score = 0;
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0,
y: -80
});
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0,
y: -80,
x: -100
});
self.init = function (x, y) {
self.x = x;
self.y = y;
self.health = self.maxHealth;
self.score = 0;
self.isDead = false;
self.alpha = 1;
self.cooldown = 0;
self.updateHealthBar();
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
healthBar.width = 200 * healthPercentage;
};
self.takeDamage = function (amount) {
Entity.prototype.takeDamage.call(self, amount);
self.updateHealthBar();
};
self.update = function () {
if (self.isDead) {
return;
}
if (self.cooldown > 0) {
self.cooldown--;
}
self.updateHealthBar();
};
self.shoot = function () {
if (self.cooldown > 0 || self.isDead) {
return null;
}
var projectile = new Projectile();
projectile.init(self.x, self.y, 'up');
self.cooldown = self.shootCooldownMax;
LK.getSound('projectileShoot').play();
return projectile;
};
self.addScore = function (points) {
self.score += points;
if (self.score > storage.highScore) {
storage.highScore = self.score;
}
};
return self;
});
var Enemy = Entity.expand(function () {
var self = Entity.call(this);
self.speed = 2;
self.attackCooldown = 0;
self.attackRate = 120; // 2 seconds at 60fps
self.scoreValue = 10;
self.targetPlayer = null;
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, targetPlayer) {
self.x = x;
self.y = y;
self.health = 50;
self.maxHealth = 50;
self.isDead = false;
self.alpha = 1;
self.targetPlayer = targetPlayer;
self.attackCooldown = Math.floor(Math.random() * self.attackRate);
};
self.update = function () {
if (self.isDead || !self.targetPlayer) {
return;
}
// Move toward player
var dx = self.targetPlayer.x - self.x;
var dy = self.targetPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Attack logic
if (self.attackCooldown > 0) {
self.attackCooldown--;
} else if (distance < 150) {
self.attackPlayer();
}
};
self.attackPlayer = function () {
if (self.isDead || self.targetPlayer.isDead) {
return null;
}
// Attack directly by touching
if (self.intersects(self.targetPlayer)) {
self.targetPlayer.takeDamage(10);
LK.getSound('hit').play();
}
// Sometimes shoot projectiles
if (Math.random() < 0.3) {
var projectile = new Projectile();
var angle = Math.atan2(self.targetPlayer.y - self.y, self.targetPlayer.x - self.x);
projectile.init(self.x, self.y, 'custom', angle);
self.attackCooldown = self.attackRate;
return projectile;
}
self.attackCooldown = self.attackRate / 2;
return null;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = 'health'; // Default type
self.active = true;
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, type) {
self.x = x;
self.y = y;
self.type = type || (Math.random() < 0.7 ? 'health' : 'speed');
self.active = true;
self.alpha = 1;
// Different colors for different power-up types
if (self.type === 'health') {
powerUpGraphics.tint = 0x2ecc71; // Green
} else if (self.type === 'speed') {
powerUpGraphics.tint = 0x3498db; // Blue
}
// Add a pulsing animation
self.pulse();
};
self.pulse = function () {
tween(powerUpGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(powerUpGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.sinceIn,
onFinish: function onFinish() {
if (self.active) {
self.pulse();
}
}
});
}
});
};
self.collect = function (player) {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('powerUpCollect').play();
if (self.type === 'health') {
player.health = Math.min(player.maxHealth, player.health + 25);
} else if (self.type === 'speed') {
var originalSpeed = player.speed;
player.speed += 2;
// Speed boost is temporary
LK.setTimeout(function () {
if (!player.isDead) {
player.speed = originalSpeed;
}
}, 5000);
}
// Fade out and destroy
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 10;
self.direction = 'up';
self.angleRad = 0;
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, direction, angleRad) {
self.x = x;
self.y = y;
self.direction = direction || 'up';
self.angleRad = angleRad || 0;
};
self.update = function () {
if (self.direction === 'up') {
self.y -= self.speed;
} else if (self.direction === 'down') {
self.y += self.speed;
} else if (self.direction === 'custom' && self.angleRad !== undefined) {
self.x += Math.cos(self.angleRad) * self.speed;
self.y += Math.sin(self.angleRad) * self.speed;
}
};
return self;
});
var Trap = Container.expand(function () {
var self = Container.call(this);
self.damage = 15;
self.active = true;
var trapGraphics = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, damage) {
self.x = x;
self.y = y;
self.damage = damage || 15;
self.active = true;
self.alpha = 1;
// Add a rotating animation
self.rotate();
};
self.rotate = function () {
tween(trapGraphics, {
rotation: Math.PI * 2
}, {
duration: 3000,
onFinish: function onFinish() {
trapGraphics.rotation = 0;
if (self.active) {
self.rotate();
}
}
});
};
self.trigger = function (entity) {
if (!self.active) {
return;
}
LK.getSound('trapHit').play();
entity.takeDamage(self.damage);
// Visual feedback
LK.effects.flashObject(self, 0xff0000, 300);
// Deactivate temporarily
self.active = false;
trapGraphics.alpha = 0.5;
// Reactivate after a delay
LK.setTimeout(function () {
if (self.parent) {
// Check if still in the scene
self.active = true;
trapGraphics.alpha = 1;
}
}, 3000);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Arena setup
var arena = LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5
});
arena.x = 2048 / 2;
arena.y = 2732 / 2;
game.addChild(arena);
// Game state variables
var player = null;
var enemies = [];
var powerUps = [];
var traps = [];
var projectiles = [];
var dragTarget = null;
var gameActive = false;
var waveNumber = 0;
var enemiesDefeated = 0;
var isGameStarting = false;
// Game boundaries
var bounds = {
x: arena.x - arena.width / 2,
y: arena.y - arena.height / 2,
width: arena.width,
height: arena.height
};
// UI elements
var scoreText = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var waveText = new Text2('Wave 1', {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.y = 80;
LK.gui.top.addChild(waveText);
var highScoreText = new Text2('High Score: ' + storage.highScore, {
size: 40,
fill: 0xFFFFFF
});
highScoreText.anchor.set(1, 0);
highScoreText.x = -20;
highScoreText.y = 20;
LK.gui.topRight.addChild(highScoreText);
// Play music
LK.playMusic('battleMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Initialize the game
function startGame() {
if (isGameStarting) {
return;
}
isGameStarting = true;
// Reset game state
resetGameState();
// Create player
player = new Player();
player.init(arena.x, arena.y + 300);
game.addChild(player);
// Create initial traps
for (var i = 0; i < 5; i++) {
createTrap();
}
// Create initial power-ups
for (var i = 0; i < 3; i++) {
createPowerUp();
}
// Start first wave
startNextWave();
// Update UI
updateScoreDisplay();
gameActive = true;
isGameStarting = false;
}
function resetGameState() {
// Remove existing entities
if (player && !player.isDead) {
player.die();
}
// Clean up arrays
cleanupArrays();
// Reset game variables
waveNumber = 0;
enemiesDefeated = 0;
LK.setScore(0);
}
function cleanupArrays() {
// Clean up enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].parent) {
enemies[i].destroy();
}
enemies.splice(i, 1);
}
// Clean up power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
if (powerUps[i].parent) {
powerUps[i].destroy();
}
powerUps.splice(i, 1);
}
// Clean up traps
for (var i = traps.length - 1; i >= 0; i--) {
if (traps[i].parent) {
traps[i].destroy();
}
traps.splice(i, 1);
}
// Clean up projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
if (projectiles[i].parent) {
projectiles[i].destroy();
}
projectiles.splice(i, 1);
}
}
function startNextWave() {
waveNumber++;
waveText.setText('Wave ' + waveNumber);
// Display wave announcement
var waveAnnouncement = new Text2('Wave ' + waveNumber, {
size: 120,
fill: 0xFFFFFF
});
waveAnnouncement.anchor.set(0.5, 0.5);
LK.gui.center.addChild(waveAnnouncement);
// Fade in and out
waveAnnouncement.alpha = 0;
tween(waveAnnouncement, {
alpha: 1
}, {
duration: 800,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(waveAnnouncement, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
waveAnnouncement.destroy();
}
});
}, 1000);
}
});
// Spawn enemies for this wave
var enemiesToSpawn = Math.min(3 + waveNumber, 10);
for (var i = 0; i < enemiesToSpawn; i++) {
LK.setTimeout(function () {
if (gameActive && player && !player.isDead) {
spawnEnemy();
}
}, i * 800);
}
// Add a new trap and powerup with each wave
if (waveNumber > 1) {
createTrap();
if (waveNumber % 2 === 0) {
createPowerUp();
}
}
}
function spawnEnemy() {
if (!player || player.isDead) {
return;
}
var enemy = new Enemy();
var edge = Math.floor(Math.random() * 4);
var x, y;
// Spawn from edges of the arena
switch (edge) {
case 0:
// Top
x = bounds.x + Math.random() * bounds.width;
y = bounds.y;
break;
case 1:
// Right
x = bounds.x + bounds.width;
y = bounds.y + Math.random() * bounds.height;
break;
case 2:
// Bottom
x = bounds.x + Math.random() * bounds.width;
y = bounds.y + bounds.height;
break;
case 3:
// Left
x = bounds.x;
y = bounds.y + Math.random() * bounds.height;
break;
}
enemy.init(x, y, player);
game.addChild(enemy);
enemies.push(enemy);
}
function createPowerUp() {
var powerUp = new PowerUp();
var x = bounds.x + 100 + Math.random() * (bounds.width - 200);
var y = bounds.y + 100 + Math.random() * (bounds.height - 200);
powerUp.init(x, y);
game.addChild(powerUp);
powerUps.push(powerUp);
}
function createTrap() {
var trap = new Trap();
var x = bounds.x + 100 + Math.random() * (bounds.width - 200);
var y = bounds.y + 100 + Math.random() * (bounds.height - 200);
trap.init(x, y);
game.addChild(trap);
traps.push(trap);
}
function updateScoreDisplay() {
scoreText.setText('Score: ' + LK.getScore());
highScoreText.setText('High Score: ' + storage.highScore);
}
function checkCollisions() {
if (!player || player.isDead) {
return;
}
// Player-Enemy collisions
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.isDead) {
continue;
}
if (player.intersects(enemy)) {
player.takeDamage(5);
LK.getSound('hit').play();
}
}
// Player-PowerUp collisions
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (!powerUp.active) {
continue;
}
if (player.intersects(powerUp)) {
powerUp.collect(player);
powerUps.splice(i, 1);
}
}
// Player-Trap collisions
for (var i = traps.length - 1; i >= 0; i--) {
var trap = traps[i];
if (!trap.active) {
continue;
}
if (player.intersects(trap)) {
trap.trigger(player);
}
}
// Projectile-Enemy collisions
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
var hit = false;
// Check if projectile is out of bounds
if (projectile.isOutOfBounds(bounds)) {
projectile.destroy();
projectiles.splice(i, 1);
continue;
}
// Check hits against enemies (only if shot by player)
if (projectile.direction === 'up') {
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (enemy.isDead) {
continue;
}
if (projectile.intersects(enemy)) {
enemy.takeDamage(projectile.damage);
LK.getSound('hit').play();
if (enemy.health <= 0) {
player.addScore(enemy.scoreValue);
LK.setScore(player.score);
updateScoreDisplay();
enemies.splice(j, 1);
enemiesDefeated++;
// Check if wave is complete
if (enemies.length === 0) {
LK.setTimeout(startNextWave, 2000);
}
}
hit = true;
break;
}
}
}
// Check hits against player (only if shot by enemy)
else if (projectile.direction === 'custom' && player && !player.isDead) {
if (projectile.intersects(player)) {
player.takeDamage(projectile.damage);
LK.getSound('hit').play();
hit = true;
}
}
if (hit) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
}
// Game input handlers
game.down = function (x, y, obj) {
if (!gameActive) {
startGame();
return;
}
if (player && !player.isDead) {
dragTarget = player;
player.x = x;
player.y = y;
player.keepInBounds(bounds);
}
};
game.up = function (x, y, obj) {
dragTarget = null;
// Shoot on release if game is active
if (gameActive && player && !player.isDead) {
var projectile = player.shoot();
if (projectile) {
game.addChild(projectile);
projectiles.push(projectile);
}
}
};
game.move = function (x, y, obj) {
if (dragTarget === player && player && !player.isDead) {
player.x = x;
player.y = y;
player.keepInBounds(bounds);
}
};
// Main game update loop
game.update = function () {
if (!gameActive) {
// Show start game message if not already started
if (!isGameStarting && (!player || player.isDead)) {
var _pulseMessage = function pulseMessage() {
tween(startMessage, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(startMessage, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: function onFinish() {
if (startMessage.parent) {
_pulseMessage();
}
}
});
}
});
};
var startMessage = new Text2('Tap to Start', {
size: 80,
fill: 0xFFFFFF
});
startMessage.anchor.set(0.5, 0.5);
LK.gui.center.addChild(startMessage);
// Pulse animation for the start message
_pulseMessage();
gameActive = true; // Set to true so message only shows once
}
return;
}
// If player is dead, show game over
if (player && player.isDead) {
storage.totalMatches++;
LK.showGameOver();
return;
}
// Update all game entities
if (player) {
player.update();
}
for (var i = 0; i < enemies.length; i++) {
if (!enemies[i].isDead) {
enemies[i].update();
// Chance for enemy to shoot
if (Math.random() < 0.005) {
var enemyProjectile = enemies[i].attackPlayer();
if (enemyProjectile) {
game.addChild(enemyProjectile);
projectiles.push(enemyProjectile);
}
}
}
}
for (var i = 0; i < projectiles.length; i++) {
projectiles[i].update();
}
// Check various collisions
checkCollisions();
// Spawn additional power-ups randomly
if (Math.random() < 0.001 && powerUps.length < 5) {
createPowerUp();
}
// Spawn additional traps randomly in later waves
if (waveNumber > 2 && Math.random() < 0.0005 && traps.length < waveNumber + 3) {
createTrap();
}
};
// Initial screen
var titleText = new Text2('Arena Rivals', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -200;
LK.gui.center.addChild(titleText);
var instructionText = new Text2('Tap and drag to move\nRelease to shoot', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.y = 100;
LK.gui.center.addChild(instructionText);
// Start game message
var startMessage = new Text2('Tap to Start', {
size: 80,
fill: 0xFFFFFF
});
startMessage.anchor.set(0.5, 0.5);
startMessage.y = 300;
LK.gui.center.addChild(startMessage);
// Pulse animation for the start message
function pulseStartMessage() {
tween(startMessage, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(startMessage, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: pulseStartMessage
});
}
});
}
pulseStartMessage(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
totalWins: 0,
totalMatches: 0
});
/****
* Classes
****/
var Entity = Container.expand(function () {
var self = Container.call(this);
self.speed = 0;
self.health = 100;
self.maxHealth = 100;
self.size = 100;
self.isDead = false;
self.init = function (x, y, color) {
self.x = x;
self.y = y;
};
self.takeDamage = function (amount) {
if (self.isDead) {
return;
}
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
self.die();
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 300);
};
self.die = function () {
if (self.isDead) {
return;
}
self.isDead = true;
tween(self, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.isOutOfBounds = function (bounds) {
return self.x < bounds.x || self.x > bounds.x + bounds.width || self.y < bounds.y || self.y > bounds.y + bounds.height;
};
self.keepInBounds = function (bounds) {
if (self.x < bounds.x) {
self.x = bounds.x;
} else if (self.x > bounds.x + bounds.width) {
self.x = bounds.x + bounds.width;
}
if (self.y < bounds.y) {
self.y = bounds.y;
} else if (self.y > bounds.y + bounds.height) {
self.y = bounds.y + bounds.height;
}
};
return self;
});
var Player = Entity.expand(function () {
var self = Entity.call(this);
self.speed = 5;
self.cooldown = 0;
self.shootCooldownMax = 30; // half a second at 60fps
self.score = 0;
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0,
y: -80
});
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0,
y: -80,
x: -100
});
self.init = function (x, y) {
self.x = x;
self.y = y;
self.health = self.maxHealth;
self.score = 0;
self.isDead = false;
self.alpha = 1;
self.cooldown = 0;
self.updateHealthBar();
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
healthBar.width = 200 * healthPercentage;
};
self.takeDamage = function (amount) {
Entity.prototype.takeDamage.call(self, amount);
self.updateHealthBar();
};
self.update = function () {
if (self.isDead) {
return;
}
if (self.cooldown > 0) {
self.cooldown--;
}
self.updateHealthBar();
};
self.shoot = function () {
if (self.cooldown > 0 || self.isDead) {
return null;
}
var projectile = new Projectile();
projectile.init(self.x, self.y, 'up');
self.cooldown = self.shootCooldownMax;
LK.getSound('projectileShoot').play();
return projectile;
};
self.addScore = function (points) {
self.score += points;
if (self.score > storage.highScore) {
storage.highScore = self.score;
}
};
return self;
});
var Enemy = Entity.expand(function () {
var self = Entity.call(this);
self.speed = 2;
self.attackCooldown = 0;
self.attackRate = 120; // 2 seconds at 60fps
self.scoreValue = 10;
self.targetPlayer = null;
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, targetPlayer) {
self.x = x;
self.y = y;
self.health = 50;
self.maxHealth = 50;
self.isDead = false;
self.alpha = 1;
self.targetPlayer = targetPlayer;
self.attackCooldown = Math.floor(Math.random() * self.attackRate);
};
self.update = function () {
if (self.isDead || !self.targetPlayer) {
return;
}
// Move toward player
var dx = self.targetPlayer.x - self.x;
var dy = self.targetPlayer.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Attack logic
if (self.attackCooldown > 0) {
self.attackCooldown--;
} else if (distance < 150) {
self.attackPlayer();
}
};
self.attackPlayer = function () {
if (self.isDead || self.targetPlayer.isDead) {
return null;
}
// Attack directly by touching
if (self.intersects(self.targetPlayer)) {
self.targetPlayer.takeDamage(10);
LK.getSound('hit').play();
}
// Sometimes shoot projectiles
if (Math.random() < 0.3) {
var projectile = new Projectile();
var angle = Math.atan2(self.targetPlayer.y - self.y, self.targetPlayer.x - self.x);
projectile.init(self.x, self.y, 'custom', angle);
self.attackCooldown = self.attackRate;
return projectile;
}
self.attackCooldown = self.attackRate / 2;
return null;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = 'health'; // Default type
self.active = true;
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, type) {
self.x = x;
self.y = y;
self.type = type || (Math.random() < 0.7 ? 'health' : 'speed');
self.active = true;
self.alpha = 1;
// Different colors for different power-up types
if (self.type === 'health') {
powerUpGraphics.tint = 0x2ecc71; // Green
} else if (self.type === 'speed') {
powerUpGraphics.tint = 0x3498db; // Blue
}
// Add a pulsing animation
self.pulse();
};
self.pulse = function () {
tween(powerUpGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(powerUpGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.sinceIn,
onFinish: function onFinish() {
if (self.active) {
self.pulse();
}
}
});
}
});
};
self.collect = function (player) {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('powerUpCollect').play();
if (self.type === 'health') {
player.health = Math.min(player.maxHealth, player.health + 25);
} else if (self.type === 'speed') {
var originalSpeed = player.speed;
player.speed += 2;
// Speed boost is temporary
LK.setTimeout(function () {
if (!player.isDead) {
player.speed = originalSpeed;
}
}, 5000);
}
// Fade out and destroy
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 10;
self.direction = 'up';
self.angleRad = 0;
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, direction, angleRad) {
self.x = x;
self.y = y;
self.direction = direction || 'up';
self.angleRad = angleRad || 0;
};
self.update = function () {
if (self.direction === 'up') {
self.y -= self.speed;
} else if (self.direction === 'down') {
self.y += self.speed;
} else if (self.direction === 'custom' && self.angleRad !== undefined) {
self.x += Math.cos(self.angleRad) * self.speed;
self.y += Math.sin(self.angleRad) * self.speed;
}
};
return self;
});
var Trap = Container.expand(function () {
var self = Container.call(this);
self.damage = 15;
self.active = true;
var trapGraphics = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y, damage) {
self.x = x;
self.y = y;
self.damage = damage || 15;
self.active = true;
self.alpha = 1;
// Add a rotating animation
self.rotate();
};
self.rotate = function () {
tween(trapGraphics, {
rotation: Math.PI * 2
}, {
duration: 3000,
onFinish: function onFinish() {
trapGraphics.rotation = 0;
if (self.active) {
self.rotate();
}
}
});
};
self.trigger = function (entity) {
if (!self.active) {
return;
}
LK.getSound('trapHit').play();
entity.takeDamage(self.damage);
// Visual feedback
LK.effects.flashObject(self, 0xff0000, 300);
// Deactivate temporarily
self.active = false;
trapGraphics.alpha = 0.5;
// Reactivate after a delay
LK.setTimeout(function () {
if (self.parent) {
// Check if still in the scene
self.active = true;
trapGraphics.alpha = 1;
}
}, 3000);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Arena setup
var arena = LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5
});
arena.x = 2048 / 2;
arena.y = 2732 / 2;
game.addChild(arena);
// Game state variables
var player = null;
var enemies = [];
var powerUps = [];
var traps = [];
var projectiles = [];
var dragTarget = null;
var gameActive = false;
var waveNumber = 0;
var enemiesDefeated = 0;
var isGameStarting = false;
// Game boundaries
var bounds = {
x: arena.x - arena.width / 2,
y: arena.y - arena.height / 2,
width: arena.width,
height: arena.height
};
// UI elements
var scoreText = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var waveText = new Text2('Wave 1', {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.y = 80;
LK.gui.top.addChild(waveText);
var highScoreText = new Text2('High Score: ' + storage.highScore, {
size: 40,
fill: 0xFFFFFF
});
highScoreText.anchor.set(1, 0);
highScoreText.x = -20;
highScoreText.y = 20;
LK.gui.topRight.addChild(highScoreText);
// Play music
LK.playMusic('battleMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Initialize the game
function startGame() {
if (isGameStarting) {
return;
}
isGameStarting = true;
// Reset game state
resetGameState();
// Create player
player = new Player();
player.init(arena.x, arena.y + 300);
game.addChild(player);
// Create initial traps
for (var i = 0; i < 5; i++) {
createTrap();
}
// Create initial power-ups
for (var i = 0; i < 3; i++) {
createPowerUp();
}
// Start first wave
startNextWave();
// Update UI
updateScoreDisplay();
gameActive = true;
isGameStarting = false;
}
function resetGameState() {
// Remove existing entities
if (player && !player.isDead) {
player.die();
}
// Clean up arrays
cleanupArrays();
// Reset game variables
waveNumber = 0;
enemiesDefeated = 0;
LK.setScore(0);
}
function cleanupArrays() {
// Clean up enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].parent) {
enemies[i].destroy();
}
enemies.splice(i, 1);
}
// Clean up power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
if (powerUps[i].parent) {
powerUps[i].destroy();
}
powerUps.splice(i, 1);
}
// Clean up traps
for (var i = traps.length - 1; i >= 0; i--) {
if (traps[i].parent) {
traps[i].destroy();
}
traps.splice(i, 1);
}
// Clean up projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
if (projectiles[i].parent) {
projectiles[i].destroy();
}
projectiles.splice(i, 1);
}
}
function startNextWave() {
waveNumber++;
waveText.setText('Wave ' + waveNumber);
// Display wave announcement
var waveAnnouncement = new Text2('Wave ' + waveNumber, {
size: 120,
fill: 0xFFFFFF
});
waveAnnouncement.anchor.set(0.5, 0.5);
LK.gui.center.addChild(waveAnnouncement);
// Fade in and out
waveAnnouncement.alpha = 0;
tween(waveAnnouncement, {
alpha: 1
}, {
duration: 800,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(waveAnnouncement, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
waveAnnouncement.destroy();
}
});
}, 1000);
}
});
// Spawn enemies for this wave
var enemiesToSpawn = Math.min(3 + waveNumber, 10);
for (var i = 0; i < enemiesToSpawn; i++) {
LK.setTimeout(function () {
if (gameActive && player && !player.isDead) {
spawnEnemy();
}
}, i * 800);
}
// Add a new trap and powerup with each wave
if (waveNumber > 1) {
createTrap();
if (waveNumber % 2 === 0) {
createPowerUp();
}
}
}
function spawnEnemy() {
if (!player || player.isDead) {
return;
}
var enemy = new Enemy();
var edge = Math.floor(Math.random() * 4);
var x, y;
// Spawn from edges of the arena
switch (edge) {
case 0:
// Top
x = bounds.x + Math.random() * bounds.width;
y = bounds.y;
break;
case 1:
// Right
x = bounds.x + bounds.width;
y = bounds.y + Math.random() * bounds.height;
break;
case 2:
// Bottom
x = bounds.x + Math.random() * bounds.width;
y = bounds.y + bounds.height;
break;
case 3:
// Left
x = bounds.x;
y = bounds.y + Math.random() * bounds.height;
break;
}
enemy.init(x, y, player);
game.addChild(enemy);
enemies.push(enemy);
}
function createPowerUp() {
var powerUp = new PowerUp();
var x = bounds.x + 100 + Math.random() * (bounds.width - 200);
var y = bounds.y + 100 + Math.random() * (bounds.height - 200);
powerUp.init(x, y);
game.addChild(powerUp);
powerUps.push(powerUp);
}
function createTrap() {
var trap = new Trap();
var x = bounds.x + 100 + Math.random() * (bounds.width - 200);
var y = bounds.y + 100 + Math.random() * (bounds.height - 200);
trap.init(x, y);
game.addChild(trap);
traps.push(trap);
}
function updateScoreDisplay() {
scoreText.setText('Score: ' + LK.getScore());
highScoreText.setText('High Score: ' + storage.highScore);
}
function checkCollisions() {
if (!player || player.isDead) {
return;
}
// Player-Enemy collisions
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.isDead) {
continue;
}
if (player.intersects(enemy)) {
player.takeDamage(5);
LK.getSound('hit').play();
}
}
// Player-PowerUp collisions
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (!powerUp.active) {
continue;
}
if (player.intersects(powerUp)) {
powerUp.collect(player);
powerUps.splice(i, 1);
}
}
// Player-Trap collisions
for (var i = traps.length - 1; i >= 0; i--) {
var trap = traps[i];
if (!trap.active) {
continue;
}
if (player.intersects(trap)) {
trap.trigger(player);
}
}
// Projectile-Enemy collisions
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
var hit = false;
// Check if projectile is out of bounds
if (projectile.isOutOfBounds(bounds)) {
projectile.destroy();
projectiles.splice(i, 1);
continue;
}
// Check hits against enemies (only if shot by player)
if (projectile.direction === 'up') {
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (enemy.isDead) {
continue;
}
if (projectile.intersects(enemy)) {
enemy.takeDamage(projectile.damage);
LK.getSound('hit').play();
if (enemy.health <= 0) {
player.addScore(enemy.scoreValue);
LK.setScore(player.score);
updateScoreDisplay();
enemies.splice(j, 1);
enemiesDefeated++;
// Check if wave is complete
if (enemies.length === 0) {
LK.setTimeout(startNextWave, 2000);
}
}
hit = true;
break;
}
}
}
// Check hits against player (only if shot by enemy)
else if (projectile.direction === 'custom' && player && !player.isDead) {
if (projectile.intersects(player)) {
player.takeDamage(projectile.damage);
LK.getSound('hit').play();
hit = true;
}
}
if (hit) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
}
// Game input handlers
game.down = function (x, y, obj) {
if (!gameActive) {
startGame();
return;
}
if (player && !player.isDead) {
dragTarget = player;
player.x = x;
player.y = y;
player.keepInBounds(bounds);
}
};
game.up = function (x, y, obj) {
dragTarget = null;
// Shoot on release if game is active
if (gameActive && player && !player.isDead) {
var projectile = player.shoot();
if (projectile) {
game.addChild(projectile);
projectiles.push(projectile);
}
}
};
game.move = function (x, y, obj) {
if (dragTarget === player && player && !player.isDead) {
player.x = x;
player.y = y;
player.keepInBounds(bounds);
}
};
// Main game update loop
game.update = function () {
if (!gameActive) {
// Show start game message if not already started
if (!isGameStarting && (!player || player.isDead)) {
var _pulseMessage = function pulseMessage() {
tween(startMessage, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(startMessage, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: function onFinish() {
if (startMessage.parent) {
_pulseMessage();
}
}
});
}
});
};
var startMessage = new Text2('Tap to Start', {
size: 80,
fill: 0xFFFFFF
});
startMessage.anchor.set(0.5, 0.5);
LK.gui.center.addChild(startMessage);
// Pulse animation for the start message
_pulseMessage();
gameActive = true; // Set to true so message only shows once
}
return;
}
// If player is dead, show game over
if (player && player.isDead) {
storage.totalMatches++;
LK.showGameOver();
return;
}
// Update all game entities
if (player) {
player.update();
}
for (var i = 0; i < enemies.length; i++) {
if (!enemies[i].isDead) {
enemies[i].update();
// Chance for enemy to shoot
if (Math.random() < 0.005) {
var enemyProjectile = enemies[i].attackPlayer();
if (enemyProjectile) {
game.addChild(enemyProjectile);
projectiles.push(enemyProjectile);
}
}
}
}
for (var i = 0; i < projectiles.length; i++) {
projectiles[i].update();
}
// Check various collisions
checkCollisions();
// Spawn additional power-ups randomly
if (Math.random() < 0.001 && powerUps.length < 5) {
createPowerUp();
}
// Spawn additional traps randomly in later waves
if (waveNumber > 2 && Math.random() < 0.0005 && traps.length < waveNumber + 3) {
createTrap();
}
};
// Initial screen
var titleText = new Text2('Arena Rivals', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -200;
LK.gui.center.addChild(titleText);
var instructionText = new Text2('Tap and drag to move\nRelease to shoot', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.y = 100;
LK.gui.center.addChild(instructionText);
// Start game message
var startMessage = new Text2('Tap to Start', {
size: 80,
fill: 0xFFFFFF
});
startMessage.anchor.set(0.5, 0.5);
startMessage.y = 300;
LK.gui.center.addChild(startMessage);
// Pulse animation for the start message
function pulseStartMessage() {
tween(startMessage, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(startMessage, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: pulseStartMessage
});
}
});
}
pulseStartMessage();