User prompt
Make me move and make it so easy
User prompt
Make it a easy game
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'playerTank.score')' in or related to this line: 'scoreTxt.setText("Score: " + playerTank.score);' Line Number: 752
Code edit (1 edits merged)
Please save this source code
User prompt
Tank Commander: Battle Arena
Initial prompt
A tank shooting game
/**** * 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 + 30, tank.maxHealth); break; case 'speed': tank.speedBoostTime = 300; // 5 seconds at 60fps tank.speed = tank.baseSpeed * 1.5; break; case 'damage': tank.damageBoostTime = 300; // 5 seconds at 60fps tank.bulletDamage = tank.baseBulletDamage * 1.5; 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 = 100; self.health = 100; 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 = 50; self.health = 50; self.baseSpeed = 3; self.speed = 3; self.reloadTime = 30; self.pointValue = 10; self.body.tint = 0x8B0000; // Dark red self.turret.tint = 0x580000; // Even darker red break; case 'fast': self.maxHealth = 30; self.health = 30; self.baseSpeed = 6; self.speed = 6; self.reloadTime = 25; self.pointValue = 15; self.body.tint = 0xFFA500; // Orange self.turret.tint = 0xFF8C00; // Dark orange break; case 'heavy': self.maxHealth = 100; self.health = 100; self.baseSpeed = 2; self.speed = 2; self.reloadTime = 40; self.baseBulletDamage = 20; self.bulletDamage = 20; 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 (20%) if (Math.random() < 0.2) { 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 + 2, 10); var numFast = Math.floor(level / 2); var numHeavy = Math.floor(level / 3); 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 * 2, 15); 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() { 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() { // 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();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,946 @@
-/****
+/****
+* 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 + 30, tank.maxHealth);
+ break;
+ case 'speed':
+ tank.speedBoostTime = 300; // 5 seconds at 60fps
+ tank.speed = tank.baseSpeed * 1.5;
+ break;
+ case 'damage':
+ tank.damageBoostTime = 300; // 5 seconds at 60fps
+ tank.bulletDamage = tank.baseBulletDamage * 1.5;
+ 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 = 100;
+ self.health = 100;
+ 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 = 50;
+ self.health = 50;
+ self.baseSpeed = 3;
+ self.speed = 3;
+ self.reloadTime = 30;
+ self.pointValue = 10;
+ self.body.tint = 0x8B0000; // Dark red
+ self.turret.tint = 0x580000; // Even darker red
+ break;
+ case 'fast':
+ self.maxHealth = 30;
+ self.health = 30;
+ self.baseSpeed = 6;
+ self.speed = 6;
+ self.reloadTime = 25;
+ self.pointValue = 15;
+ self.body.tint = 0xFFA500; // Orange
+ self.turret.tint = 0xFF8C00; // Dark orange
+ break;
+ case 'heavy':
+ self.maxHealth = 100;
+ self.health = 100;
+ self.baseSpeed = 2;
+ self.speed = 2;
+ self.reloadTime = 40;
+ self.baseBulletDamage = 20;
+ self.bulletDamage = 20;
+ 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 (20%)
+ if (Math.random() < 0.2) {
+ 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: 0x000000
-});
\ No newline at end of file
+ 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 + 2, 10);
+ var numFast = Math.floor(level / 2);
+ var numHeavy = Math.floor(level / 3);
+ 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 * 2, 15);
+ 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() {
+ 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() {
+ // 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();
\ No newline at end of file