/**** * 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();