Code edit (1 edits merged)
Please save this source code
User prompt
Arena Brawlers: Ultimate Showdown
Initial prompt
Subreddit for Brawl Stars, the free multiplayer mobile arena fighter/party brawler/shoot 'em up game from Supercell. Fast-paced 3v3 and Battle Royale matches 🔥 Collect unique Brawlers. Play with your friends. Be the last one standing!
/****
* 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);
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,692 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ 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);
+};
\ No newline at end of file