/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.damage = 10;
self.direction = 0;
self.maxDistance = 1000;
self.distanceTraveled = 0;
self.sourceType = 'player'; // 'player' or 'enemy'
self.update = function () {
var deltaX = Math.sin(self.direction) * self.speed;
var deltaY = -Math.cos(self.direction) * self.speed;
self.x += deltaX;
self.y += deltaY;
self.distanceTraveled += Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Remove if traveled too far
if (self.distanceTraveled > self.maxDistance) {
self.destroy();
return;
}
// Check if out of bounds
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.destroy();
return;
}
};
self.destroy = function () {
self.parent.removeChild(self);
if (self.sourceType === 'player') {
var bulletIndex = playerBullets.indexOf(self);
if (bulletIndex !== -1) {
playerBullets.splice(bulletIndex, 1);
}
} else {
var bulletIndex = enemyBullets.indexOf(self);
if (bulletIndex !== -1) {
enemyBullets.splice(bulletIndex, 1);
}
}
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 30; // frames
self.counter = 0;
self.update = function () {
self.counter++;
if (self.counter >= self.lifeTime) {
self.destroy();
return;
}
var scale = 1 + self.counter / self.lifeTime * 1.5;
self.graphics.scale.set(scale);
self.graphics.alpha = 1 - self.counter / self.lifeTime;
};
self.destroy = function () {
self.parent.removeChild(self);
var explosionIndex = explosions.indexOf(self);
if (explosionIndex !== -1) {
explosions.splice(explosionIndex, 1);
}
};
return self;
});
var HealthBar = Container.expand(function () {
var self = Container.call(this);
self.background = new Container();
self.background.graphics = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
tint: 0x333333,
width: 100
});
self.addChild(self.background);
self.foreground = new Container();
self.foreground.graphics = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
width: 100
});
self.addChild(self.foreground);
self.setHealth = function (current, max) {
var percentage = current / max;
percentage = Math.max(0, Math.min(1, percentage));
self.foreground.graphics.width = self.background.graphics.width * percentage;
// Change color based on health
if (percentage > 0.6) {
self.foreground.graphics.tint = 0x00FF00; // Green
} else if (percentage > 0.3) {
self.foreground.graphics.tint = 0xFFFF00; // Yellow
} else {
self.foreground.graphics.tint = 0xFF0000; // Red
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = 'health'; // default type
self.graphics = null;
self.init = function (type) {
self.type = type;
switch (type) {
case 'health':
self.graphics = self.attachAsset('healthPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'speed':
self.graphics = self.attachAsset('speedPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'damage':
self.graphics = self.attachAsset('damagePowerup', {
anchorX: 0.5,
anchorY: 0.5
});
break;
}
// Add pulsing animation
self.pulseCounter = 0;
};
self.update = function () {
// Make power-ups pulse
self.pulseCounter += 0.05;
var pulseFactor = 0.2 * Math.sin(self.pulseCounter) + 1;
self.graphics.scale.set(pulseFactor);
};
self.applyEffect = function (tank) {
switch (self.type) {
case 'health':
tank.health = Math.min(tank.health + 100, tank.maxHealth); // More health
break;
case 'speed':
tank.speedBoostTime = 600; // 10 seconds at 60fps
tank.speed = tank.baseSpeed * 2.0; // Faster boost
break;
case 'damage':
tank.damageBoostTime = 600; // 10 seconds at 60fps
tank.bulletDamage = tank.baseBulletDamage * 2.0; // More damage
break;
}
LK.getSound('powerup').play();
self.destroy();
};
self.destroy = function () {
self.parent.removeChild(self);
var powerupIndex = powerups.indexOf(self);
if (powerupIndex !== -1) {
powerups.splice(powerupIndex, 1);
}
};
return self;
});
var Tank = Container.expand(function () {
var self = Container.call(this);
// Tank components
self.body = self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
self.turret = LK.getAsset('tankTurret', {
anchorX: 0.5,
anchorY: 0.7 // Offset to position at the base of the turret
});
self.addChild(self.turret);
// Tank properties
self.maxHealth = 100;
self.health = 100;
self.baseSpeed = 5;
self.speed = 5;
self.direction = 0; // Radians
self.turretDirection = 0; // Radians
self.baseBulletDamage = 10;
self.bulletDamage = 10;
self.reloadTime = 15; // frames
self.reloadCounter = 0;
self.speedBoostTime = 0;
self.damageBoostTime = 0;
// Health bar
self.healthBar = new HealthBar();
self.healthBar.y = -80;
self.addChild(self.healthBar);
self.update = function () {
// Update health bar
self.healthBar.setHealth(self.health, self.maxHealth);
// Handle timed boosts
if (self.speedBoostTime > 0) {
self.speedBoostTime--;
if (self.speedBoostTime <= 0) {
self.speed = self.baseSpeed;
}
}
if (self.damageBoostTime > 0) {
self.damageBoostTime--;
if (self.damageBoostTime <= 0) {
self.bulletDamage = self.baseBulletDamage;
}
}
// Update reload counter
if (self.reloadCounter > 0) {
self.reloadCounter--;
}
};
self.setBodyDirection = function (direction) {
self.direction = direction;
self.body.rotation = direction;
};
self.setTurretDirection = function (direction) {
self.turretDirection = direction;
self.turret.rotation = direction;
};
self.move = function (dx, dy) {
if (dx === 0 && dy === 0) return;
// Calculate direction from movement
var newDirection = Math.atan2(dx, -dy);
self.setBodyDirection(newDirection);
// Apply movement
var moveX = Math.sin(self.direction) * self.speed;
var moveY = -Math.cos(self.direction) * self.speed;
self.x += moveX;
self.y += moveY;
// Keep tank within bounds
self.x = Math.max(60, Math.min(2048 - 60, self.x));
self.y = Math.max(60, Math.min(2732 - 60, self.y));
};
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.explode();
}
};
self.canShoot = function () {
return self.reloadCounter <= 0;
};
self.shoot = function () {
if (!self.canShoot()) return null;
// Create bullet
var bullet = new Bullet();
bullet.x = self.x + Math.sin(self.turretDirection) * 60;
bullet.y = self.y - Math.cos(self.turretDirection) * 60;
bullet.direction = self.turretDirection;
bullet.damage = self.bulletDamage;
// Reset reload timer
self.reloadCounter = self.reloadTime;
// Play sound
LK.getSound('shoot').play();
return bullet;
};
self.explode = function () {
var explosion = new Explosion();
explosion.x = self.x;
explosion.y = self.y;
game.addChild(explosion);
explosions.push(explosion);
LK.getSound('explosion').play();
};
return self;
});
var PlayerTank = Tank.expand(function () {
var self = Tank.call(this);
self.body.tint = 0x006400; // Dark green
self.turret.tint = 0x004200; // Even darker green
// Player-specific properties
self.score = 0;
self.reloadTime = 10; // Faster reload for player
self.maxHealth = 300;
self.health = 300;
self.baseBulletDamage = 40; // Increase player damage
self.bulletDamage = 40;
self.reloadTime = 5; // Faster reload
var originalExplode = self.explode;
self.explode = function () {
originalExplode();
// Game over when player dies
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
};
return self;
});
var EnemyTank = Tank.expand(function () {
var self = Tank.call(this);
self.body = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
self.turret = LK.getAsset('enemyTurret', {
anchorX: 0.5,
anchorY: 0.7
});
self.addChild(self.turret);
// Enemy-specific properties
self.pointValue = 10;
self.type = 'basic'; // basic, fast, heavy
self.aggroRange = 800;
self.targetX = 0;
self.targetY = 0;
self.aiState = 'patrol'; // patrol, chase, attack
self.patrolCounter = 0;
self.patrolDirection = Math.random() * Math.PI * 2;
self.init = function (type) {
self.type = type;
switch (type) {
case 'basic':
self.maxHealth = 20; // Reduced health
self.health = 20;
self.baseSpeed = 2; // Slower
self.speed = 2;
self.reloadTime = 45; // Longer reload
self.pointValue = 10;
self.body.tint = 0x8B0000; // Dark red
self.turret.tint = 0x580000; // Even darker red
break;
case 'fast':
self.maxHealth = 15; // Reduced health
self.health = 15;
self.baseSpeed = 3; // Slower than before
self.speed = 3;
self.reloadTime = 40; // Longer reload
self.pointValue = 15;
self.body.tint = 0xFFA500; // Orange
self.turret.tint = 0xFF8C00; // Dark orange
break;
case 'heavy':
self.maxHealth = 40; //{2v} // Reduced health
self.health = 40;
self.baseSpeed = 1; // Even slower
self.speed = 1;
self.reloadTime = 60; // Much longer reload
self.baseBulletDamage = 10; // Less damage
self.bulletDamage = 10;
self.pointValue = 20;
self.body.tint = 0x4B0082; // Indigo
self.turret.tint = 0x2E0854; // Darker indigo
break;
}
};
var originalUpdate = self.update;
self.update = function () {
originalUpdate();
if (!playerTank) return;
// Calculate distance to player
var dx = playerTank.x - self.x;
var dy = playerTank.y - self.y;
var distanceToPlayer = Math.sqrt(dx * dx + dy * dy);
// Update AI state
if (distanceToPlayer < self.aggroRange) {
self.aiState = 'chase';
// If close enough, switch to attack
if (distanceToPlayer < 400) {
self.aiState = 'attack';
}
} else {
self.aiState = 'patrol';
}
// Execute AI behavior
switch (self.aiState) {
case 'patrol':
self.patrolCounter++;
if (self.patrolCounter > 60) {
self.patrolCounter = 0;
// Randomly change direction occasionally
if (Math.random() < 0.3) {
self.patrolDirection = Math.random() * Math.PI * 2;
}
}
// Move in patrol direction
var patrolDx = Math.sin(self.patrolDirection);
var patrolDy = -Math.cos(self.patrolDirection);
self.move(patrolDx, patrolDy);
break;
case 'chase':
// Move toward player
var direction = Math.atan2(dx, -dy);
var chaseDx = Math.sin(direction);
var chaseDy = -Math.cos(direction);
self.move(chaseDx, chaseDy);
// Point turret at player
self.setTurretDirection(direction);
break;
case 'attack':
// Stay relatively still but keep distance
if (distanceToPlayer < 300) {
// Back away slightly
var retreatDirection = Math.atan2(-dx, dy);
var retreatDx = Math.sin(retreatDirection) * 0.5;
var retreatDy = -Math.cos(retreatDirection) * 0.5;
self.move(retreatDx, retreatDy);
}
// Point turret at player
var fireDirection = Math.atan2(dx, -dy);
self.setTurretDirection(fireDirection);
// Try to shoot
if (self.canShoot()) {
var bullet = self.shoot();
if (bullet) {
bullet.sourceType = 'enemy';
game.addChild(bullet);
enemyBullets.push(bullet);
}
}
break;
}
};
var originalExplode = self.explode;
self.explode = function () {
originalExplode();
// Add to player score
playerTank.score += self.pointValue;
updateScoreDisplay();
// Chance to drop power-up (50% - higher chance)
if (Math.random() < 0.5) {
var powerupTypes = ['health', 'speed', 'damage'];
var randomType = powerupTypes[Math.floor(Math.random() * powerupTypes.length)];
var powerup = new PowerUp();
powerup.x = self.x;
powerup.y = self.y;
powerup.init(randomType);
game.addChild(powerup);
powerups.push(powerup);
}
// Remove from enemies array
var index = enemies.indexOf(self);
if (index !== -1) {
enemies.splice(index, 1);
}
// Check if all enemies are destroyed
if (enemies.length === 0) {
levelComplete();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E8B57
});
/****
* Game Code
****/
// Game variables
var playerTank;
var enemies = [];
var obstacles = [];
var playerBullets = [];
var enemyBullets = [];
var powerups = [];
var explosions = [];
var currentLevel = storage.level || 1;
var waveDelay = 180; // 3 seconds at 60fps
var waveCounter = 0;
var joystickRadius = 120;
var joystickActive = false;
var joystickCenter = {
x: 0,
y: 0
};
var joystickCurrent = {
x: 0,
y: 0
};
// UI Elements
var scoreTxt;
var levelTxt;
var joystickBase;
var joystickKnob;
var fireButton;
// Initialize game
function initGame() {
// Play background music
LK.playMusic('battleMusic');
// Create UI elements
createUI();
// Create player tank
playerTank = new PlayerTank();
playerTank.x = 2048 / 2;
playerTank.y = 2732 / 2;
game.addChild(playerTank);
// Initialize level
setupLevel(currentLevel);
// Create joystick control
createJoystick();
// Create fire button
createFireButton();
}
function createUI() {
// Score text
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -20;
scoreTxt.y = 20;
// Level text
levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 20;
updateScoreDisplay();
updateLevelDisplay();
}
function createJoystick() {
// Create joystick base (static circle)
joystickBase = new Container();
var baseGraphics = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0x333333,
alpha: 0.5
});
joystickBase.addChild(baseGraphics);
LK.gui.bottomLeft.addChild(joystickBase);
joystickBase.x = 200;
joystickBase.y = -200;
// Create joystick knob (movable circle)
joystickKnob = new Container();
var knobGraphics = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x666666,
alpha: 0.7
});
joystickKnob.addChild(knobGraphics);
LK.gui.bottomLeft.addChild(joystickKnob);
joystickKnob.x = joystickBase.x;
joystickKnob.y = joystickBase.y;
}
function createFireButton() {
fireButton = new Container();
var fireGraphics = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
tint: 0xFF0000,
alpha: 0.7
});
fireButton.addChild(fireGraphics);
// Add a label
var fireTxt = new Text2('FIRE', {
size: 40,
fill: 0xFFFFFF
});
fireTxt.anchor.set(0.5, 0.5);
fireButton.addChild(fireTxt);
LK.gui.bottomRight.addChild(fireButton);
fireButton.x = -200;
fireButton.y = -200;
// Add tap/click handler for the fire button
fireButton.down = function () {
if (playerTank && playerTank.canShoot()) {
var bullet = playerTank.shoot();
if (bullet) {
bullet.sourceType = 'player';
game.addChild(bullet);
playerBullets.push(bullet);
}
}
};
}
function setupLevel(level) {
// Clear any existing enemies and obstacles
clearLevel();
// Update level text
currentLevel = level;
updateLevelDisplay();
// Create enemies based on level
createEnemies(level);
// Create obstacles
createObstacles(level);
}
function clearLevel() {
// Remove all enemies
for (var i = enemies.length - 1; i >= 0; i--) {
game.removeChild(enemies[i]);
}
enemies = [];
// Remove all obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
game.removeChild(obstacles[i]);
}
obstacles = [];
// Remove all bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
game.removeChild(playerBullets[i]);
}
playerBullets = [];
for (var i = enemyBullets.length - 1; i >= 0; i--) {
game.removeChild(enemyBullets[i]);
}
enemyBullets = [];
// Remove all powerups
for (var i = powerups.length - 1; i >= 0; i--) {
game.removeChild(powerups[i]);
}
powerups = [];
}
function createEnemies(level) {
var numBasic = Math.min(level + 1, 3);
var numFast = Math.floor(level / 3);
var numHeavy = Math.floor(level / 4);
var margin = 200;
var maxX = 2048 - margin;
var maxY = 2732 - margin;
// Create basic enemies
for (var i = 0; i < numBasic; i++) {
createEnemy('basic', margin, margin, maxX, maxY);
}
// Create fast enemies
for (var i = 0; i < numFast; i++) {
createEnemy('fast', margin, margin, maxX, maxY);
}
// Create heavy enemies
for (var i = 0; i < numHeavy; i++) {
createEnemy('heavy', margin, margin, maxX, maxY);
}
}
function createEnemy(type, minX, minY, maxX, maxY) {
var enemy = new EnemyTank();
// Find position not too close to player
var validPosition = false;
var attempts = 0;
var x, y;
while (!validPosition && attempts < 10) {
x = minX + Math.random() * (maxX - minX);
y = minY + Math.random() * (maxY - minY);
// Check distance from player
var dx = x - playerTank.x;
var dy = y - playerTank.y;
var distanceToPlayer = Math.sqrt(dx * dx + dy * dy);
if (distanceToPlayer > 500) {
validPosition = true;
}
attempts++;
}
enemy.x = x;
enemy.y = y;
enemy.init(type);
game.addChild(enemy);
enemies.push(enemy);
return enemy;
}
function createObstacles(level) {
var numObstacles = Math.min(level, 5); // Fewer obstacles
var margin = 200;
var maxX = 2048 - margin;
var maxY = 2732 - margin;
for (var i = 0; i < numObstacles; i++) {
var obstacle = new Obstacle();
// Find valid position
var validPosition = false;
var attempts = 0;
var x, y;
while (!validPosition && attempts < 10) {
x = margin + Math.random() * (maxX - margin);
y = margin + Math.random() * (maxY - margin);
// Check distance from player
var dxPlayer = x - playerTank.x;
var dyPlayer = y - playerTank.y;
var distanceToPlayer = Math.sqrt(dxPlayer * dxPlayer + dyPlayer * dyPlayer);
// Check distance from other obstacles
var tooCloseToObstacle = false;
for (var j = 0; j < obstacles.length; j++) {
var dxObstacle = x - obstacles[j].x;
var dyObstacle = y - obstacles[j].y;
var distanceToObstacle = Math.sqrt(dxObstacle * dxObstacle + dyObstacle * dyObstacle);
if (distanceToObstacle < 300) {
tooCloseToObstacle = true;
break;
}
}
if (distanceToPlayer > 300 && !tooCloseToObstacle) {
validPosition = true;
}
attempts++;
}
obstacle.x = x;
obstacle.y = y;
game.addChild(obstacle);
obstacles.push(obstacle);
}
}
function updateScoreDisplay() {
if (!playerTank) return;
// Make sure playerTank and scoreTxt exist before accessing their properties
if (playerTank && scoreTxt && playerTank.score !== undefined) {
scoreTxt.setText("Score: " + playerTank.score);
// Update high score if needed
if (playerTank.score > storage.highScore) {
storage.highScore = playerTank.score;
}
}
}
function updateLevelDisplay() {
levelTxt.setText("Level: " + currentLevel);
storage.level = currentLevel;
}
function levelComplete() {
// Award bonus points for completing level
if (playerTank) {
playerTank.score += currentLevel * 50; // Bonus points
updateScoreDisplay();
}
// Show level complete message
var levelCompleteTxt = new Text2("Level " + currentLevel + " Complete!", {
size: 100,
fill: 0xFFFFFF
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 2048 / 2;
levelCompleteTxt.y = 2732 / 2;
game.addChild(levelCompleteTxt);
// Start next level after delay
LK.setTimeout(function () {
game.removeChild(levelCompleteTxt);
currentLevel++;
setupLevel(currentLevel);
}, 3000);
}
function updateJoystick() {
if (!joystickActive) {
joystickKnob.x = joystickBase.x;
joystickKnob.y = joystickBase.y;
return;
}
var dx = joystickCurrent.x - joystickCenter.x;
var dy = joystickCurrent.y - joystickCenter.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Limit joystick movement to radius
if (distance > joystickRadius) {
dx = dx * joystickRadius / distance;
dy = dy * joystickRadius / distance;
}
// Move the joystick knob
joystickKnob.x = joystickBase.x + dx;
joystickKnob.y = joystickBase.y + dy;
// Move the tank if joystick is active
if (distance > 10) {
// Add a small deadzone
var normalizedDx = dx / joystickRadius;
var normalizedDy = dy / joystickRadius;
// Move the tank
playerTank.move(normalizedDx, normalizedDy);
}
}
function checkCollisions() {
if (!playerTank) return;
// Check collision between player bullets and enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
// Check collision with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
break;
}
}
// Check collision with enemies
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
if (bullet && bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
bullet.destroy();
break;
}
}
}
// Check collision between enemy bullets and player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check collision with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
break;
}
}
// Check collision with player
if (bullet && bullet.intersects(playerTank)) {
playerTank.takeDamage(bullet.damage);
bullet.destroy();
}
}
// Check collision between tanks and obstacles
if (playerTank) {
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (playerTank.intersects(obstacle)) {
// Push player away from obstacle
var dx = playerTank.x - obstacle.x;
var dy = playerTank.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = playerTank.body.width / 2 + obstacle.graphics.width / 2 - distance;
if (overlap > 0 && distance > 0) {
playerTank.x += dx / distance * overlap;
playerTank.y += dy / distance * overlap;
}
}
}
}
// Check collision between enemies and obstacles
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (enemy.intersects(obstacle)) {
// Push enemy away from obstacle
var dx = enemy.x - obstacle.x;
var dy = enemy.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = enemy.body.width / 2 + obstacle.graphics.width / 2 - distance;
if (overlap > 0 && distance > 0) {
enemy.x += dx / distance * overlap;
enemy.y += dy / distance * overlap;
}
}
}
}
// Check collision between player and powerups
for (var i = 0; i < powerups.length; i++) {
var powerup = powerups[i];
if (playerTank.intersects(powerup)) {
powerup.applyEffect(playerTank);
break;
}
}
}
function updateTurretDirection(x, y) {
if (!playerTank) return;
// Convert screen coordinates to game coordinates
var gamePos = game.toLocal({
x: x,
y: y
});
// Calculate direction from player to touch position
var dx = gamePos.x - playerTank.x;
var dy = gamePos.y - playerTank.y;
var direction = Math.atan2(dx, -dy);
// Set turret direction
playerTank.setTurretDirection(direction);
}
// Event handlers
game.down = function (x, y, obj) {
// Check if the touch/click is near the joystick
var guiPos = LK.gui.bottomLeft.toLocal({
x: x,
y: y
});
var dx = guiPos.x - joystickBase.x;
var dy = guiPos.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < joystickRadius * 2) {
// Start joystick input
joystickActive = true;
joystickCenter = {
x: guiPos.x,
y: guiPos.y
};
joystickCurrent = {
x: guiPos.x,
y: guiPos.y
};
} else {
// Update turret direction
updateTurretDirection(x, y);
}
};
game.move = function (x, y, obj) {
if (joystickActive) {
var guiPos = LK.gui.bottomLeft.toLocal({
x: x,
y: y
});
joystickCurrent = {
x: guiPos.x,
y: guiPos.y
};
} else {
// Update turret direction when moving without joystick
updateTurretDirection(x, y);
}
};
game.up = function (x, y, obj) {
joystickActive = false;
};
// Main game update loop
game.update = function () {
if (!playerTank) return;
// Update joystick
updateJoystick();
// Update all game objects
playerTank.update();
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
for (var i = 0; i < playerBullets.length; i++) {
playerBullets[i].update();
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].update();
}
for (var i = 0; i < powerups.length; i++) {
powerups[i].update();
}
for (var i = 0; i < explosions.length; i++) {
explosions[i].update();
}
// Check all collisions
checkCollisions();
};
// Initialize the game
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.damage = 10;
self.direction = 0;
self.maxDistance = 1000;
self.distanceTraveled = 0;
self.sourceType = 'player'; // 'player' or 'enemy'
self.update = function () {
var deltaX = Math.sin(self.direction) * self.speed;
var deltaY = -Math.cos(self.direction) * self.speed;
self.x += deltaX;
self.y += deltaY;
self.distanceTraveled += Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Remove if traveled too far
if (self.distanceTraveled > self.maxDistance) {
self.destroy();
return;
}
// Check if out of bounds
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.destroy();
return;
}
};
self.destroy = function () {
self.parent.removeChild(self);
if (self.sourceType === 'player') {
var bulletIndex = playerBullets.indexOf(self);
if (bulletIndex !== -1) {
playerBullets.splice(bulletIndex, 1);
}
} else {
var bulletIndex = enemyBullets.indexOf(self);
if (bulletIndex !== -1) {
enemyBullets.splice(bulletIndex, 1);
}
}
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 30; // frames
self.counter = 0;
self.update = function () {
self.counter++;
if (self.counter >= self.lifeTime) {
self.destroy();
return;
}
var scale = 1 + self.counter / self.lifeTime * 1.5;
self.graphics.scale.set(scale);
self.graphics.alpha = 1 - self.counter / self.lifeTime;
};
self.destroy = function () {
self.parent.removeChild(self);
var explosionIndex = explosions.indexOf(self);
if (explosionIndex !== -1) {
explosions.splice(explosionIndex, 1);
}
};
return self;
});
var HealthBar = Container.expand(function () {
var self = Container.call(this);
self.background = new Container();
self.background.graphics = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
tint: 0x333333,
width: 100
});
self.addChild(self.background);
self.foreground = new Container();
self.foreground.graphics = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
width: 100
});
self.addChild(self.foreground);
self.setHealth = function (current, max) {
var percentage = current / max;
percentage = Math.max(0, Math.min(1, percentage));
self.foreground.graphics.width = self.background.graphics.width * percentage;
// Change color based on health
if (percentage > 0.6) {
self.foreground.graphics.tint = 0x00FF00; // Green
} else if (percentage > 0.3) {
self.foreground.graphics.tint = 0xFFFF00; // Yellow
} else {
self.foreground.graphics.tint = 0xFF0000; // Red
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.type = 'health'; // default type
self.graphics = null;
self.init = function (type) {
self.type = type;
switch (type) {
case 'health':
self.graphics = self.attachAsset('healthPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'speed':
self.graphics = self.attachAsset('speedPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
break;
case 'damage':
self.graphics = self.attachAsset('damagePowerup', {
anchorX: 0.5,
anchorY: 0.5
});
break;
}
// Add pulsing animation
self.pulseCounter = 0;
};
self.update = function () {
// Make power-ups pulse
self.pulseCounter += 0.05;
var pulseFactor = 0.2 * Math.sin(self.pulseCounter) + 1;
self.graphics.scale.set(pulseFactor);
};
self.applyEffect = function (tank) {
switch (self.type) {
case 'health':
tank.health = Math.min(tank.health + 100, tank.maxHealth); // More health
break;
case 'speed':
tank.speedBoostTime = 600; // 10 seconds at 60fps
tank.speed = tank.baseSpeed * 2.0; // Faster boost
break;
case 'damage':
tank.damageBoostTime = 600; // 10 seconds at 60fps
tank.bulletDamage = tank.baseBulletDamage * 2.0; // More damage
break;
}
LK.getSound('powerup').play();
self.destroy();
};
self.destroy = function () {
self.parent.removeChild(self);
var powerupIndex = powerups.indexOf(self);
if (powerupIndex !== -1) {
powerups.splice(powerupIndex, 1);
}
};
return self;
});
var Tank = Container.expand(function () {
var self = Container.call(this);
// Tank components
self.body = self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
self.turret = LK.getAsset('tankTurret', {
anchorX: 0.5,
anchorY: 0.7 // Offset to position at the base of the turret
});
self.addChild(self.turret);
// Tank properties
self.maxHealth = 100;
self.health = 100;
self.baseSpeed = 5;
self.speed = 5;
self.direction = 0; // Radians
self.turretDirection = 0; // Radians
self.baseBulletDamage = 10;
self.bulletDamage = 10;
self.reloadTime = 15; // frames
self.reloadCounter = 0;
self.speedBoostTime = 0;
self.damageBoostTime = 0;
// Health bar
self.healthBar = new HealthBar();
self.healthBar.y = -80;
self.addChild(self.healthBar);
self.update = function () {
// Update health bar
self.healthBar.setHealth(self.health, self.maxHealth);
// Handle timed boosts
if (self.speedBoostTime > 0) {
self.speedBoostTime--;
if (self.speedBoostTime <= 0) {
self.speed = self.baseSpeed;
}
}
if (self.damageBoostTime > 0) {
self.damageBoostTime--;
if (self.damageBoostTime <= 0) {
self.bulletDamage = self.baseBulletDamage;
}
}
// Update reload counter
if (self.reloadCounter > 0) {
self.reloadCounter--;
}
};
self.setBodyDirection = function (direction) {
self.direction = direction;
self.body.rotation = direction;
};
self.setTurretDirection = function (direction) {
self.turretDirection = direction;
self.turret.rotation = direction;
};
self.move = function (dx, dy) {
if (dx === 0 && dy === 0) return;
// Calculate direction from movement
var newDirection = Math.atan2(dx, -dy);
self.setBodyDirection(newDirection);
// Apply movement
var moveX = Math.sin(self.direction) * self.speed;
var moveY = -Math.cos(self.direction) * self.speed;
self.x += moveX;
self.y += moveY;
// Keep tank within bounds
self.x = Math.max(60, Math.min(2048 - 60, self.x));
self.y = Math.max(60, Math.min(2732 - 60, self.y));
};
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.explode();
}
};
self.canShoot = function () {
return self.reloadCounter <= 0;
};
self.shoot = function () {
if (!self.canShoot()) return null;
// Create bullet
var bullet = new Bullet();
bullet.x = self.x + Math.sin(self.turretDirection) * 60;
bullet.y = self.y - Math.cos(self.turretDirection) * 60;
bullet.direction = self.turretDirection;
bullet.damage = self.bulletDamage;
// Reset reload timer
self.reloadCounter = self.reloadTime;
// Play sound
LK.getSound('shoot').play();
return bullet;
};
self.explode = function () {
var explosion = new Explosion();
explosion.x = self.x;
explosion.y = self.y;
game.addChild(explosion);
explosions.push(explosion);
LK.getSound('explosion').play();
};
return self;
});
var PlayerTank = Tank.expand(function () {
var self = Tank.call(this);
self.body.tint = 0x006400; // Dark green
self.turret.tint = 0x004200; // Even darker green
// Player-specific properties
self.score = 0;
self.reloadTime = 10; // Faster reload for player
self.maxHealth = 300;
self.health = 300;
self.baseBulletDamage = 40; // Increase player damage
self.bulletDamage = 40;
self.reloadTime = 5; // Faster reload
var originalExplode = self.explode;
self.explode = function () {
originalExplode();
// Game over when player dies
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
};
return self;
});
var EnemyTank = Tank.expand(function () {
var self = Tank.call(this);
self.body = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
self.turret = LK.getAsset('enemyTurret', {
anchorX: 0.5,
anchorY: 0.7
});
self.addChild(self.turret);
// Enemy-specific properties
self.pointValue = 10;
self.type = 'basic'; // basic, fast, heavy
self.aggroRange = 800;
self.targetX = 0;
self.targetY = 0;
self.aiState = 'patrol'; // patrol, chase, attack
self.patrolCounter = 0;
self.patrolDirection = Math.random() * Math.PI * 2;
self.init = function (type) {
self.type = type;
switch (type) {
case 'basic':
self.maxHealth = 20; // Reduced health
self.health = 20;
self.baseSpeed = 2; // Slower
self.speed = 2;
self.reloadTime = 45; // Longer reload
self.pointValue = 10;
self.body.tint = 0x8B0000; // Dark red
self.turret.tint = 0x580000; // Even darker red
break;
case 'fast':
self.maxHealth = 15; // Reduced health
self.health = 15;
self.baseSpeed = 3; // Slower than before
self.speed = 3;
self.reloadTime = 40; // Longer reload
self.pointValue = 15;
self.body.tint = 0xFFA500; // Orange
self.turret.tint = 0xFF8C00; // Dark orange
break;
case 'heavy':
self.maxHealth = 40; //{2v} // Reduced health
self.health = 40;
self.baseSpeed = 1; // Even slower
self.speed = 1;
self.reloadTime = 60; // Much longer reload
self.baseBulletDamage = 10; // Less damage
self.bulletDamage = 10;
self.pointValue = 20;
self.body.tint = 0x4B0082; // Indigo
self.turret.tint = 0x2E0854; // Darker indigo
break;
}
};
var originalUpdate = self.update;
self.update = function () {
originalUpdate();
if (!playerTank) return;
// Calculate distance to player
var dx = playerTank.x - self.x;
var dy = playerTank.y - self.y;
var distanceToPlayer = Math.sqrt(dx * dx + dy * dy);
// Update AI state
if (distanceToPlayer < self.aggroRange) {
self.aiState = 'chase';
// If close enough, switch to attack
if (distanceToPlayer < 400) {
self.aiState = 'attack';
}
} else {
self.aiState = 'patrol';
}
// Execute AI behavior
switch (self.aiState) {
case 'patrol':
self.patrolCounter++;
if (self.patrolCounter > 60) {
self.patrolCounter = 0;
// Randomly change direction occasionally
if (Math.random() < 0.3) {
self.patrolDirection = Math.random() * Math.PI * 2;
}
}
// Move in patrol direction
var patrolDx = Math.sin(self.patrolDirection);
var patrolDy = -Math.cos(self.patrolDirection);
self.move(patrolDx, patrolDy);
break;
case 'chase':
// Move toward player
var direction = Math.atan2(dx, -dy);
var chaseDx = Math.sin(direction);
var chaseDy = -Math.cos(direction);
self.move(chaseDx, chaseDy);
// Point turret at player
self.setTurretDirection(direction);
break;
case 'attack':
// Stay relatively still but keep distance
if (distanceToPlayer < 300) {
// Back away slightly
var retreatDirection = Math.atan2(-dx, dy);
var retreatDx = Math.sin(retreatDirection) * 0.5;
var retreatDy = -Math.cos(retreatDirection) * 0.5;
self.move(retreatDx, retreatDy);
}
// Point turret at player
var fireDirection = Math.atan2(dx, -dy);
self.setTurretDirection(fireDirection);
// Try to shoot
if (self.canShoot()) {
var bullet = self.shoot();
if (bullet) {
bullet.sourceType = 'enemy';
game.addChild(bullet);
enemyBullets.push(bullet);
}
}
break;
}
};
var originalExplode = self.explode;
self.explode = function () {
originalExplode();
// Add to player score
playerTank.score += self.pointValue;
updateScoreDisplay();
// Chance to drop power-up (50% - higher chance)
if (Math.random() < 0.5) {
var powerupTypes = ['health', 'speed', 'damage'];
var randomType = powerupTypes[Math.floor(Math.random() * powerupTypes.length)];
var powerup = new PowerUp();
powerup.x = self.x;
powerup.y = self.y;
powerup.init(randomType);
game.addChild(powerup);
powerups.push(powerup);
}
// Remove from enemies array
var index = enemies.indexOf(self);
if (index !== -1) {
enemies.splice(index, 1);
}
// Check if all enemies are destroyed
if (enemies.length === 0) {
levelComplete();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E8B57
});
/****
* Game Code
****/
// Game variables
var playerTank;
var enemies = [];
var obstacles = [];
var playerBullets = [];
var enemyBullets = [];
var powerups = [];
var explosions = [];
var currentLevel = storage.level || 1;
var waveDelay = 180; // 3 seconds at 60fps
var waveCounter = 0;
var joystickRadius = 120;
var joystickActive = false;
var joystickCenter = {
x: 0,
y: 0
};
var joystickCurrent = {
x: 0,
y: 0
};
// UI Elements
var scoreTxt;
var levelTxt;
var joystickBase;
var joystickKnob;
var fireButton;
// Initialize game
function initGame() {
// Play background music
LK.playMusic('battleMusic');
// Create UI elements
createUI();
// Create player tank
playerTank = new PlayerTank();
playerTank.x = 2048 / 2;
playerTank.y = 2732 / 2;
game.addChild(playerTank);
// Initialize level
setupLevel(currentLevel);
// Create joystick control
createJoystick();
// Create fire button
createFireButton();
}
function createUI() {
// Score text
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -20;
scoreTxt.y = 20;
// Level text
levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 20;
updateScoreDisplay();
updateLevelDisplay();
}
function createJoystick() {
// Create joystick base (static circle)
joystickBase = new Container();
var baseGraphics = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0x333333,
alpha: 0.5
});
joystickBase.addChild(baseGraphics);
LK.gui.bottomLeft.addChild(joystickBase);
joystickBase.x = 200;
joystickBase.y = -200;
// Create joystick knob (movable circle)
joystickKnob = new Container();
var knobGraphics = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x666666,
alpha: 0.7
});
joystickKnob.addChild(knobGraphics);
LK.gui.bottomLeft.addChild(joystickKnob);
joystickKnob.x = joystickBase.x;
joystickKnob.y = joystickBase.y;
}
function createFireButton() {
fireButton = new Container();
var fireGraphics = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
tint: 0xFF0000,
alpha: 0.7
});
fireButton.addChild(fireGraphics);
// Add a label
var fireTxt = new Text2('FIRE', {
size: 40,
fill: 0xFFFFFF
});
fireTxt.anchor.set(0.5, 0.5);
fireButton.addChild(fireTxt);
LK.gui.bottomRight.addChild(fireButton);
fireButton.x = -200;
fireButton.y = -200;
// Add tap/click handler for the fire button
fireButton.down = function () {
if (playerTank && playerTank.canShoot()) {
var bullet = playerTank.shoot();
if (bullet) {
bullet.sourceType = 'player';
game.addChild(bullet);
playerBullets.push(bullet);
}
}
};
}
function setupLevel(level) {
// Clear any existing enemies and obstacles
clearLevel();
// Update level text
currentLevel = level;
updateLevelDisplay();
// Create enemies based on level
createEnemies(level);
// Create obstacles
createObstacles(level);
}
function clearLevel() {
// Remove all enemies
for (var i = enemies.length - 1; i >= 0; i--) {
game.removeChild(enemies[i]);
}
enemies = [];
// Remove all obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
game.removeChild(obstacles[i]);
}
obstacles = [];
// Remove all bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
game.removeChild(playerBullets[i]);
}
playerBullets = [];
for (var i = enemyBullets.length - 1; i >= 0; i--) {
game.removeChild(enemyBullets[i]);
}
enemyBullets = [];
// Remove all powerups
for (var i = powerups.length - 1; i >= 0; i--) {
game.removeChild(powerups[i]);
}
powerups = [];
}
function createEnemies(level) {
var numBasic = Math.min(level + 1, 3);
var numFast = Math.floor(level / 3);
var numHeavy = Math.floor(level / 4);
var margin = 200;
var maxX = 2048 - margin;
var maxY = 2732 - margin;
// Create basic enemies
for (var i = 0; i < numBasic; i++) {
createEnemy('basic', margin, margin, maxX, maxY);
}
// Create fast enemies
for (var i = 0; i < numFast; i++) {
createEnemy('fast', margin, margin, maxX, maxY);
}
// Create heavy enemies
for (var i = 0; i < numHeavy; i++) {
createEnemy('heavy', margin, margin, maxX, maxY);
}
}
function createEnemy(type, minX, minY, maxX, maxY) {
var enemy = new EnemyTank();
// Find position not too close to player
var validPosition = false;
var attempts = 0;
var x, y;
while (!validPosition && attempts < 10) {
x = minX + Math.random() * (maxX - minX);
y = minY + Math.random() * (maxY - minY);
// Check distance from player
var dx = x - playerTank.x;
var dy = y - playerTank.y;
var distanceToPlayer = Math.sqrt(dx * dx + dy * dy);
if (distanceToPlayer > 500) {
validPosition = true;
}
attempts++;
}
enemy.x = x;
enemy.y = y;
enemy.init(type);
game.addChild(enemy);
enemies.push(enemy);
return enemy;
}
function createObstacles(level) {
var numObstacles = Math.min(level, 5); // Fewer obstacles
var margin = 200;
var maxX = 2048 - margin;
var maxY = 2732 - margin;
for (var i = 0; i < numObstacles; i++) {
var obstacle = new Obstacle();
// Find valid position
var validPosition = false;
var attempts = 0;
var x, y;
while (!validPosition && attempts < 10) {
x = margin + Math.random() * (maxX - margin);
y = margin + Math.random() * (maxY - margin);
// Check distance from player
var dxPlayer = x - playerTank.x;
var dyPlayer = y - playerTank.y;
var distanceToPlayer = Math.sqrt(dxPlayer * dxPlayer + dyPlayer * dyPlayer);
// Check distance from other obstacles
var tooCloseToObstacle = false;
for (var j = 0; j < obstacles.length; j++) {
var dxObstacle = x - obstacles[j].x;
var dyObstacle = y - obstacles[j].y;
var distanceToObstacle = Math.sqrt(dxObstacle * dxObstacle + dyObstacle * dyObstacle);
if (distanceToObstacle < 300) {
tooCloseToObstacle = true;
break;
}
}
if (distanceToPlayer > 300 && !tooCloseToObstacle) {
validPosition = true;
}
attempts++;
}
obstacle.x = x;
obstacle.y = y;
game.addChild(obstacle);
obstacles.push(obstacle);
}
}
function updateScoreDisplay() {
if (!playerTank) return;
// Make sure playerTank and scoreTxt exist before accessing their properties
if (playerTank && scoreTxt && playerTank.score !== undefined) {
scoreTxt.setText("Score: " + playerTank.score);
// Update high score if needed
if (playerTank.score > storage.highScore) {
storage.highScore = playerTank.score;
}
}
}
function updateLevelDisplay() {
levelTxt.setText("Level: " + currentLevel);
storage.level = currentLevel;
}
function levelComplete() {
// Award bonus points for completing level
if (playerTank) {
playerTank.score += currentLevel * 50; // Bonus points
updateScoreDisplay();
}
// Show level complete message
var levelCompleteTxt = new Text2("Level " + currentLevel + " Complete!", {
size: 100,
fill: 0xFFFFFF
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = 2048 / 2;
levelCompleteTxt.y = 2732 / 2;
game.addChild(levelCompleteTxt);
// Start next level after delay
LK.setTimeout(function () {
game.removeChild(levelCompleteTxt);
currentLevel++;
setupLevel(currentLevel);
}, 3000);
}
function updateJoystick() {
if (!joystickActive) {
joystickKnob.x = joystickBase.x;
joystickKnob.y = joystickBase.y;
return;
}
var dx = joystickCurrent.x - joystickCenter.x;
var dy = joystickCurrent.y - joystickCenter.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Limit joystick movement to radius
if (distance > joystickRadius) {
dx = dx * joystickRadius / distance;
dy = dy * joystickRadius / distance;
}
// Move the joystick knob
joystickKnob.x = joystickBase.x + dx;
joystickKnob.y = joystickBase.y + dy;
// Move the tank if joystick is active
if (distance > 10) {
// Add a small deadzone
var normalizedDx = dx / joystickRadius;
var normalizedDy = dy / joystickRadius;
// Move the tank
playerTank.move(normalizedDx, normalizedDy);
}
}
function checkCollisions() {
if (!playerTank) return;
// Check collision between player bullets and enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
// Check collision with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
break;
}
}
// Check collision with enemies
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
if (bullet && bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
bullet.destroy();
break;
}
}
}
// Check collision between enemy bullets and player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check collision with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
break;
}
}
// Check collision with player
if (bullet && bullet.intersects(playerTank)) {
playerTank.takeDamage(bullet.damage);
bullet.destroy();
}
}
// Check collision between tanks and obstacles
if (playerTank) {
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (playerTank.intersects(obstacle)) {
// Push player away from obstacle
var dx = playerTank.x - obstacle.x;
var dy = playerTank.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = playerTank.body.width / 2 + obstacle.graphics.width / 2 - distance;
if (overlap > 0 && distance > 0) {
playerTank.x += dx / distance * overlap;
playerTank.y += dy / distance * overlap;
}
}
}
}
// Check collision between enemies and obstacles
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (enemy.intersects(obstacle)) {
// Push enemy away from obstacle
var dx = enemy.x - obstacle.x;
var dy = enemy.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = enemy.body.width / 2 + obstacle.graphics.width / 2 - distance;
if (overlap > 0 && distance > 0) {
enemy.x += dx / distance * overlap;
enemy.y += dy / distance * overlap;
}
}
}
}
// Check collision between player and powerups
for (var i = 0; i < powerups.length; i++) {
var powerup = powerups[i];
if (playerTank.intersects(powerup)) {
powerup.applyEffect(playerTank);
break;
}
}
}
function updateTurretDirection(x, y) {
if (!playerTank) return;
// Convert screen coordinates to game coordinates
var gamePos = game.toLocal({
x: x,
y: y
});
// Calculate direction from player to touch position
var dx = gamePos.x - playerTank.x;
var dy = gamePos.y - playerTank.y;
var direction = Math.atan2(dx, -dy);
// Set turret direction
playerTank.setTurretDirection(direction);
}
// Event handlers
game.down = function (x, y, obj) {
// Check if the touch/click is near the joystick
var guiPos = LK.gui.bottomLeft.toLocal({
x: x,
y: y
});
var dx = guiPos.x - joystickBase.x;
var dy = guiPos.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < joystickRadius * 2) {
// Start joystick input
joystickActive = true;
joystickCenter = {
x: guiPos.x,
y: guiPos.y
};
joystickCurrent = {
x: guiPos.x,
y: guiPos.y
};
} else {
// Update turret direction
updateTurretDirection(x, y);
}
};
game.move = function (x, y, obj) {
if (joystickActive) {
var guiPos = LK.gui.bottomLeft.toLocal({
x: x,
y: y
});
joystickCurrent = {
x: guiPos.x,
y: guiPos.y
};
} else {
// Update turret direction when moving without joystick
updateTurretDirection(x, y);
}
};
game.up = function (x, y, obj) {
joystickActive = false;
};
// Main game update loop
game.update = function () {
if (!playerTank) return;
// Update joystick
updateJoystick();
// Update all game objects
playerTank.update();
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
for (var i = 0; i < playerBullets.length; i++) {
playerBullets[i].update();
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].update();
}
for (var i = 0; i < powerups.length; i++) {
powerups[i].update();
}
for (var i = 0; i < explosions.length; i++) {
explosions[i].update();
}
// Check all collisions
checkCollisions();
};
// Initialize the game
initGame();