/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ var ArenaWall = Container.expand(function () { var self = Container.call(this); self.wallGraphics = []; self.createWalls = function (centerX, centerY, size) { // Clear existing walls for (var i = 0; i < self.wallGraphics.length; i++) { self.removeChild(self.wallGraphics[i]); } self.wallGraphics = []; // Calculate positions var halfSize = size / 2; var wallSize = 30; var numWalls = Math.floor(size / wallSize); // Top and bottom walls for (var x = 0; x < numWalls; x++) { var topWall = self.attachAsset('arenaWall', { anchorX: 0.5, anchorY: 0.5, x: centerX - halfSize + (x + 0.5) * wallSize, y: centerY - halfSize }); self.wallGraphics.push(topWall); var bottomWall = self.attachAsset('arenaWall', { anchorX: 0.5, anchorY: 0.5, x: centerX - halfSize + (x + 0.5) * wallSize, y: centerY + halfSize }); self.wallGraphics.push(bottomWall); } // Left and right walls (skip corners) for (var y = 1; y < numWalls - 1; y++) { var leftWall = self.attachAsset('arenaWall', { anchorX: 0.5, anchorY: 0.5, x: centerX - halfSize, y: centerY - halfSize + (y + 0.5) * wallSize }); self.wallGraphics.push(leftWall); var rightWall = self.attachAsset('arenaWall', { anchorX: 0.5, anchorY: 0.5, x: centerX + halfSize, y: centerY - halfSize + (y + 0.5) * wallSize }); self.wallGraphics.push(rightWall); } }; return self; }); var Brawler = Container.expand(function () { var self = Container.call(this); // Character graphic var graphic = self.attachAsset('brawler', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, y: -60 }); // Health bar var healthBar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5, y: -60, x: -40 }); // Properties self.maxHealth = 100; self.health = self.maxHealth; self.attackCooldown = 0; self.attackCooldownMax = 20; self.speed = 5; self.isDragging = false; self.direction = { x: 0, y: 0 }; self.isPlayer = false; self.isEnemy = false; self.attackDamage = 20; self.lastPosition = { x: 0, y: 0 }; self.isDead = false; // Movement AI for enemies self.moveTowards = function (target, speed) { if (!target || self.isDead) { return; } var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.direction.x = dx / distance; self.direction.y = dy / distance; self.x += self.direction.x * speed; self.y += self.direction.y * speed; } }; // Attack method self.attack = function () { if (self.attackCooldown > 0 || self.isDead) { return; } var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.direction = { x: self.direction.x || 1, y: self.direction.y || 0 }; projectile.owner = self; game.addChild(projectile); gameState.projectiles.push(projectile); self.attackCooldown = self.attackCooldownMax; LK.getSound('shoot').play(); }; // Take damage method self.takeDamage = function (amount) { if (self.isDead) { return; } self.health -= amount; // Update health bar healthBar.width = self.health / self.maxHealth * 80; // Flash the character red LK.effects.flashObject(graphic, 0xff0000, 200); LK.getSound('hit').play(); if (self.health <= 0) { self.die(); } }; // Die method self.die = function () { if (self.isDead) { return; } self.isDead = true; tween(self, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { self.markForRemoval = true; } }); if (self.isPlayer) { LK.showGameOver(); } else if (self.isEnemy) { gameState.score += 100; gameState.defeatedEnemies++; scoreTxt.setText("Score: " + gameState.score); if (gameState.defeatedEnemies >= gameState.totalEnemies) { LK.showYouWin(); } } }; // Event handlers self.down = function (x, y, obj) { if (self.isPlayer && !self.isDead) { self.isDragging = true; } }; self.up = function (x, y, obj) { if (self.isPlayer) { self.isDragging = false; } }; self.update = function () { // Store last position self.lastPosition.x = self.x; self.lastPosition.y = self.y; // Update cooldowns if (self.attackCooldown > 0) { self.attackCooldown--; } // Keep within arena bounds var arenaSize = gameState.arenaSize; var halfSize = arenaSize / 2; if (self.x < gameState.arenaCenter.x - halfSize + 50) { self.x = gameState.arenaCenter.x - halfSize + 50; } if (self.x > gameState.arenaCenter.x + halfSize - 50) { self.x = gameState.arenaCenter.x + halfSize - 50; } if (self.y < gameState.arenaCenter.y - halfSize + 50) { self.y = gameState.arenaCenter.y - halfSize + 50; } if (self.y > gameState.arenaCenter.y + halfSize - 50) { self.y = gameState.arenaCenter.y + halfSize - 50; } }; return self; }); var Enemy = Brawler.expand(function () { var self = Brawler.call(this); // Override the default brawler graphic with enemy graphic var graphic = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.isEnemy = true; self.targetUpdateCooldown = 0; self.currentTarget = null; // Override the update method var parentUpdate = self.update; self.update = function () { if (self.isDead) { return; } // Base update from parent parentUpdate(); // Update target every so often if (self.targetUpdateCooldown <= 0) { self.findNewTarget(); self.targetUpdateCooldown = 60; // Update target every 60 frames } else { self.targetUpdateCooldown--; } // Move towards target if (self.currentTarget && !self.currentTarget.isDead) { self.moveTowards(self.currentTarget, self.speed); // Attack if close enough var dx = self.currentTarget.x - self.x; var dy = self.currentTarget.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 300) { // Set direction for projectile self.direction.x = dx / distance; self.direction.y = dy / distance; // Attack with some randomness if (Math.random() < 0.03) { self.attack(); } } } else { // If no target or target is dead, wander randomly if (Math.random() < 0.02) { self.direction.x = Math.random() * 2 - 1; self.direction.y = Math.random() * 2 - 1; var mag = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y); self.direction.x /= mag; self.direction.y /= mag; } self.x += self.direction.x * self.speed / 2; self.y += self.direction.y * self.speed / 2; } }; self.findNewTarget = function () { // Target the player if (gameState.player && !gameState.player.isDead) { self.currentTarget = gameState.player; return; } // No valid targets self.currentTarget = null; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var graphic = self.attachAsset('powerUp', { anchorX: 0.5, anchorY: 0.5 }); self.type = 'health'; // health, damage, speed self.value = 20; // Visual effect - floating animation self.floatAnim = function () { tween(graphic, { y: -5 }, { duration: 1000, easing: tween.sinceOut, onFinish: function onFinish() { tween(graphic, { y: 5 }, { duration: 1000, easing: tween.sinceOut, onFinish: self.floatAnim }); } }); }; self.floatAnim(); return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); var graphic = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.direction = { x: 0, y: 0 }; self.speed = 12; self.damage = 10; self.lifetime = 60; // frames the projectile lives for self.owner = null; self.update = function () { self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; self.lifetime--; if (self.lifetime <= 0) { self.markForRemoval = true; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Game state tracking var gameState = { arenaCenter: { x: 2048 / 2, y: 2732 / 2 }, arenaSize: 1800, minArenaSize: 1000, arenaShrinkRate: 0.2, arenaShrinkStart: 1000, // ticks before arena starts shrinking arenaShrinkTimer: 0, projectiles: [], powerUps: [], enemies: [], player: null, score: 0, level: 1, defeatedEnemies: 0, totalEnemies: 5, gameStarted: false }; // Create arena background var arenaBackground = LK.getAsset('arena', { anchorX: 0.5, anchorY: 0.5, x: gameState.arenaCenter.x, y: gameState.arenaCenter.y, width: gameState.arenaSize, height: gameState.arenaSize, alpha: 0.3 }); game.addChild(arenaBackground); // Create arena walls var arenaWalls = new ArenaWall(); game.addChild(arenaWalls); arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize); // Create player var player = new Brawler(); player.x = gameState.arenaCenter.x; player.y = gameState.arenaCenter.y + 300; player.isPlayer = true; game.addChild(player); gameState.player = player; // Setup GUI elements var scoreTxt = new Text2("Score: 0", { size: 50, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(scoreTxt); scoreTxt.x = -250; scoreTxt.y = 50; var levelTxt = new Text2("Level: 1", { size: 50, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.topRight.addChild(levelTxt); levelTxt.x = -250; levelTxt.y = 120; var arenaSizeTxt = new Text2("Arena Shrinking", { size: 40, fill: 0xFF9900 }); arenaSizeTxt.anchor.set(0.5, 0); arenaSizeTxt.alpha = 0; LK.gui.top.addChild(arenaSizeTxt); arenaSizeTxt.y = 50; // Initialize enemies function spawnEnemies() { // Clear existing enemies for (var i = gameState.enemies.length - 1; i >= 0; i--) { game.removeChild(gameState.enemies[i]); gameState.enemies.splice(i, 1); } // Set number of enemies based on level gameState.totalEnemies = 3 + gameState.level * 2; gameState.defeatedEnemies = 0; // Spawn new enemies for (var i = 0; i < gameState.totalEnemies; i++) { var enemy = new Enemy(); // Position around the arena var angle = i / gameState.totalEnemies * Math.PI * 2; var radius = gameState.arenaSize / 2 - 200; enemy.x = gameState.arenaCenter.x + Math.cos(angle) * radius; enemy.y = gameState.arenaCenter.y + Math.sin(angle) * radius; // Scale difficulty with level enemy.maxHealth = 80 + gameState.level * 20; enemy.health = enemy.maxHealth; enemy.speed = 3 + gameState.level * 0.5; game.addChild(enemy); gameState.enemies.push(enemy); } } // Spawn power-ups periodically function spawnPowerUp() { if (gameState.powerUps.length >= 5) { return; } // Limit number of power-ups var powerUp = new PowerUp(); // Random position within arena var halfSize = gameState.arenaSize / 2 - 100; powerUp.x = gameState.arenaCenter.x + Math.random() * halfSize * 2 - halfSize; powerUp.y = gameState.arenaCenter.y + Math.random() * halfSize * 2 - halfSize; // Random type var typeRoll = Math.random(); if (typeRoll < 0.4) { powerUp.type = 'health'; powerUp.tint = 0x00ff00; } else if (typeRoll < 0.7) { powerUp.type = 'damage'; powerUp.tint = 0xff0000; powerUp.value = 10; } else { powerUp.type = 'speed'; powerUp.tint = 0x0088ff; powerUp.value = 1; } game.addChild(powerUp); gameState.powerUps.push(powerUp); } // Game start function startGame() { // Reset game state gameState.score = 0; gameState.level = 1; gameState.arenaSize = 1800; gameState.arenaShrinkTimer = gameState.arenaShrinkStart; // Reset arena arenaBackground.width = gameState.arenaSize; arenaBackground.height = gameState.arenaSize; arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize); // Reset player player.health = player.maxHealth; player.isDead = false; player.alpha = 1; player.speed = 5; player.attackDamage = 20; // Spawn enemies spawnEnemies(); // Update UI scoreTxt.setText("Score: " + gameState.score); levelTxt.setText("Level: " + gameState.level); // Play music LK.playMusic('battleMusic'); gameState.gameStarted = true; } // Handle touch/mouse events var dragTarget = null; function handleMove(x, y, obj) { if (dragTarget && dragTarget.isDragging) { // Update player position dragTarget.x = x; dragTarget.y = y; // Calculate direction based on movement var dx = x - dragTarget.lastPosition.x; var dy = y - dragTarget.lastPosition.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0.1) { dragTarget.direction.x = dx / dist; dragTarget.direction.y = dy / dist; } } } game.down = function (x, y, obj) { if (!gameState.gameStarted) { startGame(); return; } dragTarget = player; if (!player.isDead) { player.isDragging = true; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { if (dragTarget) { dragTarget.isDragging = false; dragTarget = null; } }; game.move = handleMove; // Main game update loop game.update = function () { if (!gameState.gameStarted) { return; } // Shrink arena over time if (gameState.arenaShrinkTimer > 0) { gameState.arenaShrinkTimer--; if (gameState.arenaShrinkTimer === 300) { // Show warning message tween(arenaSizeTxt, { alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(arenaSizeTxt, { alpha: 0 }, { duration: 300 }); } }); } } else { if (gameState.arenaSize > gameState.minArenaSize) { gameState.arenaSize -= gameState.arenaShrinkRate; // Update arena visuals arenaBackground.width = gameState.arenaSize; arenaBackground.height = gameState.arenaSize; arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize); } } // Spawn power-ups randomly if (Math.random() < 0.005) { spawnPowerUp(); } // Player attack on tap/click/drag release if (player.isDragging === false && dragTarget === null && !player.isDead) { player.attack(); } // Update projectiles and check collisions for (var i = gameState.projectiles.length - 1; i >= 0; i--) { var projectile = gameState.projectiles[i]; // Check if projectile is marked for removal if (projectile.markForRemoval) { game.removeChild(projectile); gameState.projectiles.splice(i, 1); continue; } // Check projectile collisions with enemies if (projectile.owner === player) { for (var j = 0; j < gameState.enemies.length; j++) { var enemy = gameState.enemies[j]; if (!enemy.isDead && projectile.intersects(enemy)) { enemy.takeDamage(player.attackDamage); projectile.markForRemoval = true; break; } } } // Check projectile collisions with player else if (projectile.owner.isEnemy && !player.isDead && projectile.intersects(player)) { player.takeDamage(projectile.owner.attackDamage); projectile.markForRemoval = true; } // Check if projectile is out of arena bounds var halfSize = gameState.arenaSize / 2; if (projectile.x < gameState.arenaCenter.x - halfSize || projectile.x > gameState.arenaCenter.x + halfSize || projectile.y < gameState.arenaCenter.y - halfSize || projectile.y > gameState.arenaCenter.y + halfSize) { projectile.markForRemoval = true; } } // Check power-up collisions with player for (var i = gameState.powerUps.length - 1; i >= 0; i--) { var powerUp = gameState.powerUps[i]; if (!player.isDead && powerUp.intersects(player)) { // Apply power-up effect if (powerUp.type === 'health') { player.health = Math.min(player.maxHealth, player.health + powerUp.value); // Update health bar var healthBar = player.getChildAt(2); // Health bar is at index 2 healthBar.width = player.health / player.maxHealth * 80; } else if (powerUp.type === 'damage') { player.attackDamage += powerUp.value; } else if (powerUp.type === 'speed') { player.speed += powerUp.value; } // Remove power-up game.removeChild(powerUp); gameState.powerUps.splice(i, 1); // Play sound and add score LK.getSound('powerup').play(); gameState.score += 50; scoreTxt.setText("Score: " + gameState.score); } // Remove power-ups outside arena bounds var halfSize = gameState.arenaSize / 2; if (powerUp.x < gameState.arenaCenter.x - halfSize || powerUp.x > gameState.arenaCenter.x + halfSize || powerUp.y < gameState.arenaCenter.y - halfSize || powerUp.y > gameState.arenaCenter.y + halfSize) { game.removeChild(powerUp); gameState.powerUps.splice(i, 1); } } // Check for level completion if (gameState.defeatedEnemies >= gameState.totalEnemies) { // Level up gameState.level++; levelTxt.setText("Level: " + gameState.level); // Reset arena gameState.arenaSize = 1800; gameState.arenaShrinkTimer = gameState.arenaShrinkStart; arenaBackground.width = gameState.arenaSize; arenaBackground.height = gameState.arenaSize; arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize); // Spawn new enemies spawnEnemies(); // Bonus score for completing level gameState.score += 500; scoreTxt.setText("Score: " + gameState.score); // Clear old power-ups for (var i = gameState.powerUps.length - 1; i >= 0; i--) { game.removeChild(gameState.powerUps[i]); gameState.powerUps.splice(i, 1); } } // Check for game over (player dead) if (player.isDead) { // Update high score if (gameState.score > storage.highScore) { storage.highScore = gameState.score; } } // Clean up dead enemies for (var i = gameState.enemies.length - 1; i >= 0; i--) { if (gameState.enemies[i].markForRemoval) { game.removeChild(gameState.enemies[i]); gameState.enemies.splice(i, 1); } } }; // Start music LK.playMusic('battleMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); // Initial instruction text var instructionText = new Text2("Tap to start!\nDrag to move, release to shoot", { size: 60, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = gameState.arenaCenter.x; instructionText.y = gameState.arenaCenter.y - 200; game.addChild(instructionText); // Hide instructions when game starts var originalUpdate = game.update; game.update = function () { if (!gameState.gameStarted) { // Make text pulse before game starts instructionText.alpha = 0.5 + Math.sin(LK.ticks / 20) * 0.5; return; } // Remove instruction text once when game starts if (instructionText.parent) { game.removeChild(instructionText); } // Call the original update originalUpdate.call(game); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var ArenaWall = Container.expand(function () {
var self = Container.call(this);
self.wallGraphics = [];
self.createWalls = function (centerX, centerY, size) {
// Clear existing walls
for (var i = 0; i < self.wallGraphics.length; i++) {
self.removeChild(self.wallGraphics[i]);
}
self.wallGraphics = [];
// Calculate positions
var halfSize = size / 2;
var wallSize = 30;
var numWalls = Math.floor(size / wallSize);
// Top and bottom walls
for (var x = 0; x < numWalls; x++) {
var topWall = self.attachAsset('arenaWall', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX - halfSize + (x + 0.5) * wallSize,
y: centerY - halfSize
});
self.wallGraphics.push(topWall);
var bottomWall = self.attachAsset('arenaWall', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX - halfSize + (x + 0.5) * wallSize,
y: centerY + halfSize
});
self.wallGraphics.push(bottomWall);
}
// Left and right walls (skip corners)
for (var y = 1; y < numWalls - 1; y++) {
var leftWall = self.attachAsset('arenaWall', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX - halfSize,
y: centerY - halfSize + (y + 0.5) * wallSize
});
self.wallGraphics.push(leftWall);
var rightWall = self.attachAsset('arenaWall', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX + halfSize,
y: centerY - halfSize + (y + 0.5) * wallSize
});
self.wallGraphics.push(rightWall);
}
};
return self;
});
var Brawler = Container.expand(function () {
var self = Container.call(this);
// Character graphic
var graphic = self.attachAsset('brawler', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
y: -60
});
// Health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
y: -60,
x: -40
});
// Properties
self.maxHealth = 100;
self.health = self.maxHealth;
self.attackCooldown = 0;
self.attackCooldownMax = 20;
self.speed = 5;
self.isDragging = false;
self.direction = {
x: 0,
y: 0
};
self.isPlayer = false;
self.isEnemy = false;
self.attackDamage = 20;
self.lastPosition = {
x: 0,
y: 0
};
self.isDead = false;
// Movement AI for enemies
self.moveTowards = function (target, speed) {
if (!target || self.isDead) {
return;
}
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.direction.x = dx / distance;
self.direction.y = dy / distance;
self.x += self.direction.x * speed;
self.y += self.direction.y * speed;
}
};
// Attack method
self.attack = function () {
if (self.attackCooldown > 0 || self.isDead) {
return;
}
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.direction = {
x: self.direction.x || 1,
y: self.direction.y || 0
};
projectile.owner = self;
game.addChild(projectile);
gameState.projectiles.push(projectile);
self.attackCooldown = self.attackCooldownMax;
LK.getSound('shoot').play();
};
// Take damage method
self.takeDamage = function (amount) {
if (self.isDead) {
return;
}
self.health -= amount;
// Update health bar
healthBar.width = self.health / self.maxHealth * 80;
// Flash the character red
LK.effects.flashObject(graphic, 0xff0000, 200);
LK.getSound('hit').play();
if (self.health <= 0) {
self.die();
}
};
// Die method
self.die = function () {
if (self.isDead) {
return;
}
self.isDead = true;
tween(self, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
self.markForRemoval = true;
}
});
if (self.isPlayer) {
LK.showGameOver();
} else if (self.isEnemy) {
gameState.score += 100;
gameState.defeatedEnemies++;
scoreTxt.setText("Score: " + gameState.score);
if (gameState.defeatedEnemies >= gameState.totalEnemies) {
LK.showYouWin();
}
}
};
// Event handlers
self.down = function (x, y, obj) {
if (self.isPlayer && !self.isDead) {
self.isDragging = true;
}
};
self.up = function (x, y, obj) {
if (self.isPlayer) {
self.isDragging = false;
}
};
self.update = function () {
// Store last position
self.lastPosition.x = self.x;
self.lastPosition.y = self.y;
// Update cooldowns
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Keep within arena bounds
var arenaSize = gameState.arenaSize;
var halfSize = arenaSize / 2;
if (self.x < gameState.arenaCenter.x - halfSize + 50) {
self.x = gameState.arenaCenter.x - halfSize + 50;
}
if (self.x > gameState.arenaCenter.x + halfSize - 50) {
self.x = gameState.arenaCenter.x + halfSize - 50;
}
if (self.y < gameState.arenaCenter.y - halfSize + 50) {
self.y = gameState.arenaCenter.y - halfSize + 50;
}
if (self.y > gameState.arenaCenter.y + halfSize - 50) {
self.y = gameState.arenaCenter.y + halfSize - 50;
}
};
return self;
});
var Enemy = Brawler.expand(function () {
var self = Brawler.call(this);
// Override the default brawler graphic with enemy graphic
var graphic = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.isEnemy = true;
self.targetUpdateCooldown = 0;
self.currentTarget = null;
// Override the update method
var parentUpdate = self.update;
self.update = function () {
if (self.isDead) {
return;
}
// Base update from parent
parentUpdate();
// Update target every so often
if (self.targetUpdateCooldown <= 0) {
self.findNewTarget();
self.targetUpdateCooldown = 60; // Update target every 60 frames
} else {
self.targetUpdateCooldown--;
}
// Move towards target
if (self.currentTarget && !self.currentTarget.isDead) {
self.moveTowards(self.currentTarget, self.speed);
// Attack if close enough
var dx = self.currentTarget.x - self.x;
var dy = self.currentTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 300) {
// Set direction for projectile
self.direction.x = dx / distance;
self.direction.y = dy / distance;
// Attack with some randomness
if (Math.random() < 0.03) {
self.attack();
}
}
} else {
// If no target or target is dead, wander randomly
if (Math.random() < 0.02) {
self.direction.x = Math.random() * 2 - 1;
self.direction.y = Math.random() * 2 - 1;
var mag = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y);
self.direction.x /= mag;
self.direction.y /= mag;
}
self.x += self.direction.x * self.speed / 2;
self.y += self.direction.y * self.speed / 2;
}
};
self.findNewTarget = function () {
// Target the player
if (gameState.player && !gameState.player.isDead) {
self.currentTarget = gameState.player;
return;
}
// No valid targets
self.currentTarget = null;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var graphic = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'health'; // health, damage, speed
self.value = 20;
// Visual effect - floating animation
self.floatAnim = function () {
tween(graphic, {
y: -5
}, {
duration: 1000,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(graphic, {
y: 5
}, {
duration: 1000,
easing: tween.sinceOut,
onFinish: self.floatAnim
});
}
});
};
self.floatAnim();
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var graphic = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = {
x: 0,
y: 0
};
self.speed = 12;
self.damage = 10;
self.lifetime = 60; // frames the projectile lives for
self.owner = null;
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
self.lifetime--;
if (self.lifetime <= 0) {
self.markForRemoval = true;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state tracking
var gameState = {
arenaCenter: {
x: 2048 / 2,
y: 2732 / 2
},
arenaSize: 1800,
minArenaSize: 1000,
arenaShrinkRate: 0.2,
arenaShrinkStart: 1000,
// ticks before arena starts shrinking
arenaShrinkTimer: 0,
projectiles: [],
powerUps: [],
enemies: [],
player: null,
score: 0,
level: 1,
defeatedEnemies: 0,
totalEnemies: 5,
gameStarted: false
};
// Create arena background
var arenaBackground = LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5,
x: gameState.arenaCenter.x,
y: gameState.arenaCenter.y,
width: gameState.arenaSize,
height: gameState.arenaSize,
alpha: 0.3
});
game.addChild(arenaBackground);
// Create arena walls
var arenaWalls = new ArenaWall();
game.addChild(arenaWalls);
arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize);
// Create player
var player = new Brawler();
player.x = gameState.arenaCenter.x;
player.y = gameState.arenaCenter.y + 300;
player.isPlayer = true;
game.addChild(player);
gameState.player = player;
// Setup GUI elements
var scoreTxt = new Text2("Score: 0", {
size: 50,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -250;
scoreTxt.y = 50;
var levelTxt = new Text2("Level: 1", {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x = -250;
levelTxt.y = 120;
var arenaSizeTxt = new Text2("Arena Shrinking", {
size: 40,
fill: 0xFF9900
});
arenaSizeTxt.anchor.set(0.5, 0);
arenaSizeTxt.alpha = 0;
LK.gui.top.addChild(arenaSizeTxt);
arenaSizeTxt.y = 50;
// Initialize enemies
function spawnEnemies() {
// Clear existing enemies
for (var i = gameState.enemies.length - 1; i >= 0; i--) {
game.removeChild(gameState.enemies[i]);
gameState.enemies.splice(i, 1);
}
// Set number of enemies based on level
gameState.totalEnemies = 3 + gameState.level * 2;
gameState.defeatedEnemies = 0;
// Spawn new enemies
for (var i = 0; i < gameState.totalEnemies; i++) {
var enemy = new Enemy();
// Position around the arena
var angle = i / gameState.totalEnemies * Math.PI * 2;
var radius = gameState.arenaSize / 2 - 200;
enemy.x = gameState.arenaCenter.x + Math.cos(angle) * radius;
enemy.y = gameState.arenaCenter.y + Math.sin(angle) * radius;
// Scale difficulty with level
enemy.maxHealth = 80 + gameState.level * 20;
enemy.health = enemy.maxHealth;
enemy.speed = 3 + gameState.level * 0.5;
game.addChild(enemy);
gameState.enemies.push(enemy);
}
}
// Spawn power-ups periodically
function spawnPowerUp() {
if (gameState.powerUps.length >= 5) {
return;
} // Limit number of power-ups
var powerUp = new PowerUp();
// Random position within arena
var halfSize = gameState.arenaSize / 2 - 100;
powerUp.x = gameState.arenaCenter.x + Math.random() * halfSize * 2 - halfSize;
powerUp.y = gameState.arenaCenter.y + Math.random() * halfSize * 2 - halfSize;
// Random type
var typeRoll = Math.random();
if (typeRoll < 0.4) {
powerUp.type = 'health';
powerUp.tint = 0x00ff00;
} else if (typeRoll < 0.7) {
powerUp.type = 'damage';
powerUp.tint = 0xff0000;
powerUp.value = 10;
} else {
powerUp.type = 'speed';
powerUp.tint = 0x0088ff;
powerUp.value = 1;
}
game.addChild(powerUp);
gameState.powerUps.push(powerUp);
}
// Game start
function startGame() {
// Reset game state
gameState.score = 0;
gameState.level = 1;
gameState.arenaSize = 1800;
gameState.arenaShrinkTimer = gameState.arenaShrinkStart;
// Reset arena
arenaBackground.width = gameState.arenaSize;
arenaBackground.height = gameState.arenaSize;
arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize);
// Reset player
player.health = player.maxHealth;
player.isDead = false;
player.alpha = 1;
player.speed = 5;
player.attackDamage = 20;
// Spawn enemies
spawnEnemies();
// Update UI
scoreTxt.setText("Score: " + gameState.score);
levelTxt.setText("Level: " + gameState.level);
// Play music
LK.playMusic('battleMusic');
gameState.gameStarted = true;
}
// Handle touch/mouse events
var dragTarget = null;
function handleMove(x, y, obj) {
if (dragTarget && dragTarget.isDragging) {
// Update player position
dragTarget.x = x;
dragTarget.y = y;
// Calculate direction based on movement
var dx = x - dragTarget.lastPosition.x;
var dy = y - dragTarget.lastPosition.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0.1) {
dragTarget.direction.x = dx / dist;
dragTarget.direction.y = dy / dist;
}
}
}
game.down = function (x, y, obj) {
if (!gameState.gameStarted) {
startGame();
return;
}
dragTarget = player;
if (!player.isDead) {
player.isDragging = true;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (dragTarget) {
dragTarget.isDragging = false;
dragTarget = null;
}
};
game.move = handleMove;
// Main game update loop
game.update = function () {
if (!gameState.gameStarted) {
return;
}
// Shrink arena over time
if (gameState.arenaShrinkTimer > 0) {
gameState.arenaShrinkTimer--;
if (gameState.arenaShrinkTimer === 300) {
// Show warning message
tween(arenaSizeTxt, {
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(arenaSizeTxt, {
alpha: 0
}, {
duration: 300
});
}
});
}
} else {
if (gameState.arenaSize > gameState.minArenaSize) {
gameState.arenaSize -= gameState.arenaShrinkRate;
// Update arena visuals
arenaBackground.width = gameState.arenaSize;
arenaBackground.height = gameState.arenaSize;
arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize);
}
}
// Spawn power-ups randomly
if (Math.random() < 0.005) {
spawnPowerUp();
}
// Player attack on tap/click/drag release
if (player.isDragging === false && dragTarget === null && !player.isDead) {
player.attack();
}
// Update projectiles and check collisions
for (var i = gameState.projectiles.length - 1; i >= 0; i--) {
var projectile = gameState.projectiles[i];
// Check if projectile is marked for removal
if (projectile.markForRemoval) {
game.removeChild(projectile);
gameState.projectiles.splice(i, 1);
continue;
}
// Check projectile collisions with enemies
if (projectile.owner === player) {
for (var j = 0; j < gameState.enemies.length; j++) {
var enemy = gameState.enemies[j];
if (!enemy.isDead && projectile.intersects(enemy)) {
enemy.takeDamage(player.attackDamage);
projectile.markForRemoval = true;
break;
}
}
}
// Check projectile collisions with player
else if (projectile.owner.isEnemy && !player.isDead && projectile.intersects(player)) {
player.takeDamage(projectile.owner.attackDamage);
projectile.markForRemoval = true;
}
// Check if projectile is out of arena bounds
var halfSize = gameState.arenaSize / 2;
if (projectile.x < gameState.arenaCenter.x - halfSize || projectile.x > gameState.arenaCenter.x + halfSize || projectile.y < gameState.arenaCenter.y - halfSize || projectile.y > gameState.arenaCenter.y + halfSize) {
projectile.markForRemoval = true;
}
}
// Check power-up collisions with player
for (var i = gameState.powerUps.length - 1; i >= 0; i--) {
var powerUp = gameState.powerUps[i];
if (!player.isDead && powerUp.intersects(player)) {
// Apply power-up effect
if (powerUp.type === 'health') {
player.health = Math.min(player.maxHealth, player.health + powerUp.value);
// Update health bar
var healthBar = player.getChildAt(2); // Health bar is at index 2
healthBar.width = player.health / player.maxHealth * 80;
} else if (powerUp.type === 'damage') {
player.attackDamage += powerUp.value;
} else if (powerUp.type === 'speed') {
player.speed += powerUp.value;
}
// Remove power-up
game.removeChild(powerUp);
gameState.powerUps.splice(i, 1);
// Play sound and add score
LK.getSound('powerup').play();
gameState.score += 50;
scoreTxt.setText("Score: " + gameState.score);
}
// Remove power-ups outside arena bounds
var halfSize = gameState.arenaSize / 2;
if (powerUp.x < gameState.arenaCenter.x - halfSize || powerUp.x > gameState.arenaCenter.x + halfSize || powerUp.y < gameState.arenaCenter.y - halfSize || powerUp.y > gameState.arenaCenter.y + halfSize) {
game.removeChild(powerUp);
gameState.powerUps.splice(i, 1);
}
}
// Check for level completion
if (gameState.defeatedEnemies >= gameState.totalEnemies) {
// Level up
gameState.level++;
levelTxt.setText("Level: " + gameState.level);
// Reset arena
gameState.arenaSize = 1800;
gameState.arenaShrinkTimer = gameState.arenaShrinkStart;
arenaBackground.width = gameState.arenaSize;
arenaBackground.height = gameState.arenaSize;
arenaWalls.createWalls(gameState.arenaCenter.x, gameState.arenaCenter.y, gameState.arenaSize);
// Spawn new enemies
spawnEnemies();
// Bonus score for completing level
gameState.score += 500;
scoreTxt.setText("Score: " + gameState.score);
// Clear old power-ups
for (var i = gameState.powerUps.length - 1; i >= 0; i--) {
game.removeChild(gameState.powerUps[i]);
gameState.powerUps.splice(i, 1);
}
}
// Check for game over (player dead)
if (player.isDead) {
// Update high score
if (gameState.score > storage.highScore) {
storage.highScore = gameState.score;
}
}
// Clean up dead enemies
for (var i = gameState.enemies.length - 1; i >= 0; i--) {
if (gameState.enemies[i].markForRemoval) {
game.removeChild(gameState.enemies[i]);
gameState.enemies.splice(i, 1);
}
}
};
// Start music
LK.playMusic('battleMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Initial instruction text
var instructionText = new Text2("Tap to start!\nDrag to move, release to shoot", {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = gameState.arenaCenter.x;
instructionText.y = gameState.arenaCenter.y - 200;
game.addChild(instructionText);
// Hide instructions when game starts
var originalUpdate = game.update;
game.update = function () {
if (!gameState.gameStarted) {
// Make text pulse before game starts
instructionText.alpha = 0.5 + Math.sin(LK.ticks / 20) * 0.5;
return;
}
// Remove instruction text once when game starts
if (instructionText.parent) {
game.removeChild(instructionText);
}
// Call the original update
originalUpdate.call(game);
};