User prompt
The game could be better if you added a collectible heart-like item that increases your health.
User prompt
The game could be better if you added a collectible heart-like item that increases your health.
Code edit (1 edits merged)
Please save this source code
User prompt
add sound effects and music for each event
User prompt
Enemy tanks should come in a more aligned manner from above and below the game over text.
User prompt
animate the losing screen. Tanks and enemies come. Red from right and left, sometimes it says game over, it should be very dynamic and cool. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Well done, make the counter look better.
User prompt
We understand the problem, the problem is that when you press the game started text once, it starts, but when you press it again, the menu returns, this should not be the problem. Make sure the game is running smoothly.
User prompt
We understand the problem, the problem is that when you press the game started text once, it starts, but when you press it again, the menu returns, this should not be the problem. Make sure the game is running smoothly.
User prompt
We understand the problem, the problem is that when you press the game started text once, it starts, but when you press it again, the menu returns, this should not be the problem. Make sure the game is running smoothly.
User prompt
Say tap to satart and the menu should go and the game should start.
User prompt
Say tap to satart and the menu should go and the game should start.
User prompt
It would be better if the tank image in the main menu was lower than it is now. Also, there are a lot of problems with starting the game.
User prompt
when the game starts the tank image leaves a picture that stays where it is, fix this. also there should be an animated intro, i.e. at first a blue tank clears 3 red tanks and then the menu comes, it would be cool. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make an intro. then click and the game will start.
User prompt
No, when you say color, you don't mean like this, you add things like objects, depth and similar items.
User prompt
Add some color to the playground, make it like a real tank battlefield.
User prompt
Move the lives a little more to the left.
User prompt
Move the lives a little more to the right.Move the lives a little more to the right.
User prompt
Write "Glaud" in small orange in the upper right corner.
User prompt
enemies should fire slower.
User prompt
enemies should fire slower.enemies should fire slower.enemies should fire slower.
User prompt
enemies should move slower.enemies should move slower.enemies should move slower.enemies should move slower.
User prompt
enemies should move slower.
User prompt
the controlled tank should be faster
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Animated Intro Class var AnimatedIntro = Container.expand(function () { var self = Container.call(this); // Create battlefield var field = self.attachAsset('battlefieldFloor', { anchorX: 0.5, anchorY: 0.5 }); field.x = GAME_W / 2; field.y = GAME_H / 2; // Create tanks for animation var blueTank = self.attachAsset('playerTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); blueTank.x = GAME_W / 2; blueTank.y = GAME_H - 400; // Create enemy tanks var redTanks = []; for (var i = 0; i < 3; i++) { var redTank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); redTank.x = 600 + i * 400; redTank.y = 500; redTanks.push(redTank); self.addChild(redTank); } // Animation state self.state = 'ready'; self.timer = 0; self.explosions = []; // Animation sequence self.update = function () { self.timer++; if (self.state === 'ready' && self.timer > 60) { // Start battle animation self.state = 'fire1'; // Animate blue tank firing var shell = self.attachAsset('playerShell', { anchorX: 0.5, anchorY: 0.5 }); shell.x = blueTank.x; shell.y = blueTank.y - 100; // Play fire sound LK.getSound('fire').play(); // Animate shell movement tween(shell, { x: redTanks[0].x, y: redTanks[0].y }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { // Create explosion var exp = new Explosion(); exp.x = redTanks[0].x; exp.y = redTanks[0].y; self.addChild(exp); self.explosions.push(exp); LK.getSound('explode').play(); // Remove tank and shell redTanks[0].destroy(); shell.destroy(); // Next state self.state = 'fire2'; self.timer = 0; } }); } else if (self.state === 'fire2' && self.timer > 30) { // Second shot var shell = self.attachAsset('playerShell', { anchorX: 0.5, anchorY: 0.5 }); shell.x = blueTank.x; shell.y = blueTank.y - 100; tween(shell, { x: redTanks[1].x, y: redTanks[1].y }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { // Create explosion var exp = new Explosion(); exp.x = redTanks[1].x; exp.y = redTanks[1].y; self.addChild(exp); self.explosions.push(exp); // Remove tank and shell redTanks[1].destroy(); shell.destroy(); // Next state self.state = 'fire3'; self.timer = 0; } }); } else if (self.state === 'fire3' && self.timer > 30) { // Third shot var shell = self.attachAsset('playerShell', { anchorX: 0.5, anchorY: 0.5 }); shell.x = blueTank.x; shell.y = blueTank.y - 100; tween(shell, { x: redTanks[2].x, y: redTanks[2].y }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { // Create explosion var exp = new Explosion(); exp.x = redTanks[2].x; exp.y = redTanks[2].y; self.addChild(exp); self.explosions.push(exp); // Remove tank and shell redTanks[2].destroy(); shell.destroy(); // Next state self.state = 'victory'; self.timer = 0; } }); } else if (self.state === 'victory' && self.timer > 90) { // Show intro screen self.finish(); } }; // Complete intro and show menu self.finish = function () { // Clean up all explosions for (var i = 0; i < self.explosions.length; i++) { if (self.explosions[i].parent) { self.explosions[i].destroy(); } } // Fade out and destroy tween(self, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); showIntroScreen(); } }); }; return self; }); // Enemy Shell Class var EnemyShell = Container.expand(function () { var self = Container.call(this); var shell = self.attachAsset('enemyShell', { anchorX: 0.5, anchorY: 0.5 }); self.width = shell.width; self.height = shell.height; self.vx = 0; self.vy = 8; // Reduced speed self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // Enemy Tank Class var EnemyTank = Container.expand(function () { var self = Container.call(this); var tank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); self.width = tank.width; self.height = tank.height; // Movement self.speed = 1.5 + Math.random() * 0.5 + Math.floor(LK.getScore() / 30); // Decreased base speed and score scaling // Firing self.fireInterval = 240; // 4 seconds at 60fps (Increased fire interval further) self.fireCounter = 0; self.update = function () { self.y += self.speed; // Move downwards self.fireCounter++; if (self.fireCounter >= self.fireInterval) { self.fireCounter = 0; self.fire(); } }; self.fire = function () { var shell = new EnemyShell(); shell.x = self.x; shell.y = self.y + self.height / 2 + shell.height / 2; // Start below the enemy tank // Aim at player var dx = player.x - shell.x; var dy = player.y - shell.y; var mag = Math.sqrt(dx * dx + dy * dy); // Rotate tank to face direction of fire var angle = Math.atan2(dy, dx); self.rotation = angle + Math.PI / 2; // +90 degrees since tank points up // Lowered speed for enemy shells var shellSpeed = 5 + Math.floor(LK.getScore() / 30); shell.vx = dx / mag * shellSpeed; shell.vy = dy / mag * shellSpeed; enemyShells.push(shell); game.addChild(shell); LK.getSound('enemyFire').play(); }; return self; }); // Explosion effect (for destroyed tanks) var Explosion = Container.expand(function () { var self = Container.call(this); var exp = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1 }); exp.alpha = 0.9; // Animate explosion growth and fade tween(exp, { scaleX: 2.5, scaleY: 2.5, alpha: 0.7 }, { duration: 150, easing: tween.easeOut }); // Animate fade out and destroy tween(exp, { alpha: 0 }, { duration: 450, easing: tween.linear, onFinish: function onFinish() { self.destroy(); } }); return self; }); // Game Over Animation Class var GameOverAnimator = Container.expand(function () { var self = Container.call(this); // Create battlefield background var field = self.attachAsset('battlefieldFloor', { anchorX: 0.5, anchorY: 0.5 }); field.x = GAME_W / 2; field.y = GAME_H / 2; // Game Over text with animation var gameOverText = new Text2('GAME OVER', { size: 250, fill: 0xFF0000 }); gameOverText.anchor.set(0.5, 0.5); gameOverText.x = GAME_W / 2; gameOverText.y = GAME_H / 2; gameOverText.alpha = 0; self.addChild(gameOverText); // Animation elements self.explosions = []; self.tanks = []; self.shells = []; self.animationTimer = 0; // Initialize animation self.initialize = function () { // Prepare text for animation (start invisible and small) gameOverText.alpha = 0; gameOverText.scaleX = 0.5; gameOverText.scaleY = 0.5; // Dramatic entrance for game over text tween(gameOverText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.elasticOut, onStart: function onStart() { // Big explosion at the center when text appears var centerExplosion = new Explosion(); centerExplosion.x = GAME_W / 2; centerExplosion.y = GAME_H / 2; self.addChild(centerExplosion); // Play explosion sound LK.getSound('explode').play(); // Screen flash LK.effects.flashScreen(0xff0000, 500); } }); // Create enemy tanks coming from all sides self.createTanks(); // Add initial explosions in a circle around game over text var explosionCount = 6; for (var i = 0; i < explosionCount; i++) { var angle = i / explosionCount * Math.PI * 2; var radius = 400; var exp = new Explosion(); exp.x = GAME_W / 2 + Math.cos(angle) * radius; exp.y = GAME_H / 2 + Math.sin(angle) * radius; self.addChild(exp); self.explosions.push(exp); } // Schedule more random explosions LK.setTimeout(function () { self.createRandomExplosion(); }, 300); }; // Create tanks for animation self.createTanks = function () { // Create tanks coming from top and bottom of the game over text // Create top tanks (facing down) for (var i = 0; i < 4; i++) { var topTank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); topTank.x = GAME_W / 2 - 500 + i * 300; // Spread across horizontally, centered around text topTank.y = -200; // Start above screen topTank.rotation = Math.PI; // Face down self.tanks.push({ sprite: topTank, vx: 0, vy: 6 + Math.random() * 3, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } // Create bottom tanks (facing up) for (var i = 0; i < 4; i++) { var bottomTank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); bottomTank.x = GAME_W / 2 - 350 + i * 300; // Staggered positioning from top tanks bottomTank.y = GAME_H + 200; // Start below screen bottomTank.rotation = 0; // Face up self.tanks.push({ sprite: bottomTank, vx: 0, vy: -6 - Math.random() * 3, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } // Create side tanks for more dynamic feel for (var i = 0; i < 3; i++) { // Left side tank var leftTank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); leftTank.x = -200; leftTank.y = GAME_H / 2 - 300 + i * 300; leftTank.rotation = Math.PI / 2; // Face right self.tanks.push({ sprite: leftTank, vx: 7 + Math.random() * 3, vy: 0, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); // Right side tank var rightTank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); rightTank.x = GAME_W + 200; rightTank.y = GAME_H / 2 - 150 + i * 300; rightTank.rotation = -Math.PI / 2; // Face left self.tanks.push({ sprite: rightTank, vx: -7 - Math.random() * 3, vy: 0, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } }; // Create a random explosion self.createRandomExplosion = function () { var exp = new Explosion(); exp.x = Math.random() * GAME_W; exp.y = Math.random() * GAME_H; self.addChild(exp); self.explosions.push(exp); // Schedule next explosion var nextExplosionTime = 30 + Math.floor(Math.random() * 60); LK.setTimeout(function () { self.createRandomExplosion(); }, nextExplosionTime * 16); // Convert frames to ms (approx) }; // Create a shell from a tank self.createShell = function (tank) { var shell = self.attachAsset('enemyShell', { anchorX: 0.5, anchorY: 0.5 }); shell.x = tank.sprite.x; shell.y = tank.sprite.y; // Random direction but mostly toward center var targetX = GAME_W / 2 + (Math.random() * 600 - 300); var targetY = GAME_H / 2 + (Math.random() * 600 - 300); var dx = targetX - shell.x; var dy = targetY - shell.y; var mag = Math.sqrt(dx * dx + dy * dy); var shellSpeed = 8 + Math.random() * 5; self.shells.push({ sprite: shell, vx: dx / mag * shellSpeed, vy: dy / mag * shellSpeed }); }; // Main update function self.update = function () { self.animationTimer++; // Update tanks for (var i = 0; i < self.tanks.length; i++) { var tank = self.tanks[i]; tank.sprite.x += tank.vx; tank.sprite.y += tank.vy; // Fire shells randomly tank.lastFired++; if (tank.lastFired > tank.fireRate) { tank.lastFired = 0; tank.fireRate = 30 + Math.floor(Math.random() * 60); self.createShell(tank); // Play fire sound occasionally if (Math.random() < 0.3) { LK.getSound('enemyFire').play(); } } // Remove tanks that go off screen and create new ones var offScreen = false; if (tank.vx > 0 && tank.sprite.x > GAME_W + 200 || tank.vx < 0 && tank.sprite.x < -200 || tank.vy > 0 && tank.sprite.y > GAME_H + 200 || tank.vy < 0 && tank.sprite.y < -200) { offScreen = true; } if (offScreen) { tank.sprite.destroy(); self.tanks.splice(i, 1); i--; // Create a new tank from the opposite side with a delay LK.setTimeout(function () { // Determine spawn location (top, bottom, left, right) var spawnLocation = Math.floor(Math.random() * 4); // 0=top, 1=right, 2=bottom, 3=left var newTank = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); if (spawnLocation === 0) { // Top newTank.x = GAME_W / 2 - 400 + Math.random() * 800; newTank.y = -200; newTank.rotation = Math.PI; // Face down self.tanks.push({ sprite: newTank, vx: 0, vy: 6 + Math.random() * 3, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } else if (spawnLocation === 1) { newTank.x = GAME_W + 200; newTank.y = GAME_H / 2 - 400 + Math.random() * 800; newTank.rotation = -Math.PI / 2; // Face left self.tanks.push({ sprite: newTank, vx: -7 - Math.random() * 3, vy: 0, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } else if (spawnLocation === 2) { newTank.x = GAME_W / 2 - 400 + Math.random() * 800; newTank.y = GAME_H + 200; newTank.rotation = 0; // Face up self.tanks.push({ sprite: newTank, vx: 0, vy: -6 - Math.random() * 3, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } else { // Left newTank.x = -200; newTank.y = GAME_H / 2 - 400 + Math.random() * 800; newTank.rotation = Math.PI / 2; // Face right self.tanks.push({ sprite: newTank, vx: 7 + Math.random() * 3, vy: 0, lastFired: 0, fireRate: 30 + Math.floor(Math.random() * 60) }); } }, Math.random() * 800); } } // Update shells for (var i = 0; i < self.shells.length; i++) { var shell = self.shells[i]; shell.sprite.x += shell.vx; shell.sprite.y += shell.vy; // Remove shells that go off screen if (shell.sprite.x < -50 || shell.sprite.x > GAME_W + 50 || shell.sprite.y < -50 || shell.sprite.y > GAME_H + 50) { shell.sprite.destroy(); self.shells.splice(i, 1); i--; continue; } // Create explosion if shells collide for (var j = i + 1; j < self.shells.length; j++) { var otherShell = self.shells[j]; var dx = shell.sprite.x - otherShell.sprite.x; var dy = shell.sprite.y - otherShell.sprite.y; var distSq = dx * dx + dy * dy; if (distSq < 50 * 50) { // If shells are close enough var exp = new Explosion(); exp.x = shell.sprite.x; exp.y = shell.sprite.y; self.addChild(exp); self.explosions.push(exp); // Play explosion sound occasionally if (Math.random() < 0.5) { LK.getSound('explode').play(); } shell.sprite.destroy(); otherShell.sprite.destroy(); self.shells.splice(j, 1); self.shells.splice(i, 1); i--; break; } } } // Pulse text animation if (self.animationTimer % 60 < 30) { gameOverText.scale.x = 1.1 + Math.sin(self.animationTimer / 15) * 0.1; gameOverText.scale.y = 1.1 + Math.sin(self.animationTimer / 15) * 0.1; } }; return self; }); // Health Power-up Class var Health = Container.expand(function () { var self = Container.call(this); // Create heart graphic var heart = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); // Set heart color to red heart.tint = 0xFF0000; // Size properties self.width = heart.width * 0.7; self.height = heart.height * 0.7; // Animation properties self.floatOffset = Math.random() * Math.PI * 2; self.rotationSpeed = 0.01 + Math.random() * 0.01; self.lastY = 0; // Update function to animate the heart self.update = function () { self.lastY = self.y; // Floating animation self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 0.5; self.rotation += self.rotationSpeed; // Pulsing animation var scale = 0.7 + Math.sin(LK.ticks * 0.1) * 0.05; heart.scaleX = scale; heart.scaleY = scale; }; // Apply pick-up animation self.collect = function () { // Flash effect tween(heart, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, easing: tween.easeOut }); }; return self; }); // Intro Screen Class var IntroScreen = Container.expand(function () { var self = Container.call(this); // Create title text var titleText = new Text2('TANK BATTLE', { size: 200, fill: 0xFF8800 }); titleText.anchor.set(0.5, 0.5); titleText.x = GAME_W / 2; titleText.y = GAME_H / 3; self.addChild(titleText); // Create subtitle text var subtitleText = new Text2('Dodge enemy attacks and destroy enemy tanks', { size: 80, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = GAME_W / 2; subtitleText.y = GAME_H / 3 + 200; self.addChild(subtitleText); // Create start button var startButton = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5 }); startButton.x = GAME_W / 2; startButton.y = GAME_H / 3 + 400; startButton.tint = 0x44aa44; // Create start text var startText = new Text2('TAP TO START', { size: 100, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startText.x = GAME_W / 2; startText.y = GAME_H / 3 + 400; self.addChild(startText); // Create tank decoration var tankIcon = self.attachAsset('playerTank', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); tankIcon.x = GAME_W / 2; tankIcon.y = GAME_H / 3 + 900; // Lower the tank position // Animate button self.update = function () { // Pulse animation for button startButton.scaleX = 3 + Math.sin(LK.ticks / 20) * 0.2; startButton.scaleY = 1.5 + Math.sin(LK.ticks / 20) * 0.1; }; // Add interaction self.down = function (x, y, obj) { startGame(); }; return self; }); // Player Shell Class var PlayerShell = Container.expand(function () { var self = Container.call(this); var shell = self.attachAsset('playerShell', { anchorX: 0.5, anchorY: 0.5 }); self.width = shell.width; self.height = shell.height; self.vx = 0; self.vy = -32; // Default upwards self.lastX = 0; self.lastY = 0; self.update = function () { self.lastX = self.x; self.lastY = self.y; self.x += self.vx; self.y += self.vy; }; return self; }); // Player Tank Class var PlayerTank = Container.expand(function () { var self = Container.call(this); var tank = self.attachAsset('playerTank', { anchorX: 0.5, anchorY: 0.5 }); self.width = tank.width; self.height = tank.height; // Fire cooldown self.canFire = true; self.fireCooldown = 18; // frames (0.3s) self.cooldownCounter = 0; // Movement physics self.targetX = 0; self.targetY = 0; self.velocityX = 0; self.velocityY = 0; self.acceleration = 0.4; // Increased acceleration self.maxSpeed = 60; // Increased max speed self.friction = 0.94; // Slightly less friction for smoother movement self.isMoving = false; self.reachedTarget = true; self.rotationSpeed = 0.3; // Faster rotation self.tracksFX = null; // Move to a position with physics self.moveTo = function (x, y) { self.targetX = x; self.targetY = y; self.isMoving = true; self.reachedTarget = false; // Create dust/tracks effect if (!self.tracksFX) { self.createTracksFX(); } }; // Create track/dust effect self.createTracksFX = function () { if (self.tracksFX) { // Remove old track FX if it exists if (self.tracksFX.parent) { self.tracksFX.parent.removeChild(self.tracksFX); } self.tracksFX = null; } // Create new track effects self.tracksFX = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); self.tracksFX.y = self.height / 3; self.tracksFX.alpha = 0.3; self.tracksFX.tint = 0xCCCCCC; }; // Stop movement self.stopMoving = function () { self.isMoving = false; if (self.tracksFX) { tween(self.tracksFX, { alpha: 0 }, { duration: 300, easing: tween.easeOut }); } }; self.update = function () { // Handle fire cooldown if (!self.canFire) { self.cooldownCounter++; if (self.cooldownCounter >= self.fireCooldown) { self.canFire = true; self.cooldownCounter = 0; } } // Handle physics-based movement if (self.isMoving) { // Calculate distance and direction to target var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distSq = dx * dx + dy * dy; // Calculate desired direction var targetAngle = Math.atan2(dy, dx) + Math.PI / 2; // Smoothly rotate toward target direction var angleDiff = targetAngle - self.rotation; // Normalize angle difference to -PI to PI while (angleDiff > Math.PI) { angleDiff -= Math.PI * 2; } while (angleDiff < -Math.PI) { angleDiff += Math.PI * 2; } self.rotation += angleDiff * self.rotationSpeed; // If we're close to target, slow down if (distSq < 10000) { // 100*100 self.velocityX *= 0.9; self.velocityY *= 0.9; if (distSq < 100) { // 10*10 self.reachedTarget = true; self.velocityX = 0; self.velocityY = 0; self.stopMoving(); } } else { // Accelerate toward target based on tank's orientation var facingX = Math.sin(self.rotation); var facingY = -Math.cos(self.rotation); // Only accelerate if facing approximately the right direction var dotProduct = facingX * dx + facingY * dy; if (dotProduct > 0 || distSq > 90000) { // Allow movement if far away regardless of direction self.velocityX += facingX * self.acceleration; self.velocityY += facingY * self.acceleration; } } // Apply friction and speed limits self.velocityX *= self.friction; self.velocityY *= self.friction; // Limit max speed var speed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY); if (speed > self.maxSpeed) { self.velocityX = self.velocityX / speed * self.maxSpeed; self.velocityY = self.velocityY / speed * self.maxSpeed; } // Apply velocity self.x += self.velocityX; self.y += self.velocityY; // Animate tracks effect if (self.tracksFX && speed > 0.5) { self.tracksFX.rotation = Math.random() * 0.2 - 0.1; self.tracksFX.alpha = Math.min(0.2 + speed / 30, 0.7); self.tracksFX.scaleX = 0.3 + Math.random() * 0.1; self.tracksFX.scaleY = 0.3 + Math.random() * 0.1; } } }; // Fire a shell self.fire = function (targetX, targetY) { if (self.canFire) { var shell = new PlayerShell(); shell.x = self.x; shell.y = self.y - self.height / 3; // Offset slightly to start from tank cannon // Calculate direction if (targetX !== undefined && targetY !== undefined) { var dx = targetX - shell.x; var dy = targetY - shell.y; var mag = Math.sqrt(dx * dx + dy * dy); var shellSpeed = 32; // Shell speed shell.vx = dx / mag * shellSpeed; shell.vy = dy / mag * shellSpeed; // Rotate tank to face direction of fire var angle = Math.atan2(dy, dx); self.rotation = angle + Math.PI / 2; // +90 degrees since tank points up } else { // Default upward if no target shell.vx = 0; shell.vy = -32; self.rotation = 0; } playerShells.push(shell); game.addChild(shell); LK.getSound('fire').play(); self.canFire = false; shell.lastX = shell.x; shell.lastY = shell.y; // Animate recoil effect on firing var originalY = self.y; tween(self, { y: self.y + 20 }, { duration: 50, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { y: originalY }, { duration: 150, easing: tween.elasticOut }); } }); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x22272b }); /**** * Game Code ****/ // Play background music LK.playMusic('bgmusic', { loop: true, fade: { start: 0, end: 1, duration: 1000 } }); // Sound effects // Explosion: white ellipse (for flash effect) // Enemy shell: orange ellipse // Enemy tank: red box // Player shell: yellow ellipse // Player tank: green box // Game area var GAME_W = 2048; var GAME_H = 2732; // Game state var gameStarted = false; var introScreen = null; var animatedIntro = null; var introShown = false; // Game elements - will be initialized when game starts var player = null; var playerShells = []; var enemyTanks = []; var enemyShells = []; var healthPowerUps = []; var healthPowerUps = []; // Create animated intro function showAnimatedIntro() { animatedIntro = new AnimatedIntro(); game.addChild(animatedIntro); } // Create intro screen function showIntroScreen() { // Only show intro screen if game hasn't started if (gameStarted || introShown) { return; } introScreen = new IntroScreen(); game.addChild(introScreen); introShown = true; } // Start the actual game function startGame() { // Remove intro screen if (introScreen) { introScreen.destroy(); introScreen = null; introShown = false; } // Initialize game elements gameStarted = true; introScreen = null; animatedIntro = null; // Create player player = new PlayerTank(); // Make sure track effects are properly reset player.tracksFX = null; player.x = GAME_W / 2; player.y = GAME_H - 350; game.addChild(player); // Reset arrays playerShells = []; enemyTanks = []; enemyShells = []; healthPowerUps = []; healthPowerUps = []; // Reset score and health LK.setScore(0); scoreTxt.setText('0'); playerHealth = 5; var hearts = ''; for (var h = 0; h < playerHealth; h++) { hearts += '❤'; } healthTxt.setText(hearts); } // Start with animated intro showAnimatedIntro(); // Score display var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Player health var playerHealth = 5; var healthTxt = new Text2('❤❤❤❤❤', { size: 100, fill: 0xFF4444 }); healthTxt.anchor.set(0.5, 0); LK.gui.top.addChild(healthTxt); healthTxt.x = 400; healthTxt.y = 0; // "Glaud" text var glaudTxt = new Text2('Glaud', { size: 40, fill: 0xFFA500 // Orange color }); glaudTxt.anchor.set(1, 0); // Anchor to the top right LK.gui.topRight.addChild(glaudTxt); // Dragging var dragNode = null; // Spawn enemy timer var enemySpawnInterval = 90; // frames (1.5s) var enemySpawnCounter = 0; // Start at 0 to spawn immediately // Difficulty escalation function getEnemySpawnInterval() { var s = LK.getScore(); if (s < 10) { return 90; } if (s < 25) { return 70; } if (s < 50) { return 55; } return 40; } // Move handler (physics-based tank movement) function handleMove(x, y, obj) { if (dragNode) { // Calculate target position with bounds checking var minX = dragNode.width / 2; var maxX = GAME_W - dragNode.width / 2; var minY = 100 + dragNode.height / 2; var maxY = GAME_H - dragNode.height / 2; var targetX = Math.max(minX, Math.min(maxX, x)); var targetY = Math.max(minY, Math.min(maxY, y)); // If it's the player tank, use the physics system if (dragNode === player) { player.moveTo(targetX, targetY); } else { // For other objects, use direct positioning dragNode.x = targetX; dragNode.y = targetY; } } else if (isFiring) { // Update firing target if holding down fireTarget.x = x; fireTarget.y = y; // Rotate tank to face direction var dx = x - player.x; var dy = y - player.y; var angle = Math.atan2(dy, dx); player.rotation = angle + Math.PI / 2; // +90 degrees since tank points up } } game.move = handleMove; game.down = function (x, y, obj) { // Only drag if touch is on player tank var local = player.toLocal(game.toGlobal({ x: x, y: y })); if (local.x > -player.width / 2 && local.x < player.width / 2 && local.y > -player.height / 2 && local.y < player.height / 2) { dragNode = player; } }; game.up = function (x, y, obj) { // If we were dragging the player, let it continue to its last target if (dragNode === player) { // Already set target in handleMove, just release drag indicator } dragNode = null; isFiring = false; autoFireCounter = 0; }; // Tap to move and fire game.tap = function (x, y, obj) { // If animated intro is playing, skip to intro screen if (animatedIntro) { animatedIntro.finish(); return; } // Only start the game if it hasn't started yet and introScreen is showing if (!gameStarted && introScreen) { startGame(); introScreen = null; return; } // Avoid firing if already dragging if (!dragNode) { // Move the tank to the target position (using physics) player.moveTo(x, y); // Fire toward target player.fire(x, y); } }; // Variables for firing control var isFiring = false; var fireTarget = { x: 0, y: 0 }; var autoFireRate = 10; // Fire every 10 frames while holding var autoFireCounter = 0; // Handle both movement and firing game.down = function (x, y, obj) { // Skip animated intro on tap if (animatedIntro) { animatedIntro.finish(); return; } // Only handle tank interactions if player exists if (!player) { return; } var local = player.toLocal(game.toGlobal({ x: x, y: y })); if (local.x > -player.width / 2 && local.x < player.width / 2 && local.y > -player.height / 2 && local.y < player.height / 2) { dragNode = player; // When clicking on tank, start listening for drag } else { // When clicking elsewhere, move tank there and fire // Set firing target and start firing fireTarget.x = x; fireTarget.y = y; isFiring = true; autoFireCounter = 0; // Move the tank to the target position (using physics) player.moveTo(x, y); // Fire toward target player.fire(fireTarget.x, fireTarget.y); } }; // Game over animation var gameOverAnimation = null; // Main update loop game.update = function () { // Update game over animation if active if (gameOverAnimation) { gameOverAnimation.update(); return; } // Update animated intro or intro screen if game hasn't started if (!gameStarted) { if (animatedIntro) { animatedIntro.update(); return; } if (introScreen) { introScreen.update(); } return; // Don't run game logic until game starts } // Update player player.update(); // Handle continuous firing while holding down if (isFiring) { autoFireCounter++; if (autoFireCounter >= autoFireRate && player.canFire) { player.fire(fireTarget.x, fireTarget.y); autoFireCounter = 0; } } // Update player shells for (var i = playerShells.length - 1; i >= 0; i--) { var shell = playerShells[i]; shell.update(); // Remove if off screen if (shell.y < -shell.height / 2) { shell.destroy(); playerShells.splice(i, 1); continue; } // Check collision with enemy tanks for (var j = enemyTanks.length - 1; j >= 0; j--) { var enemy = enemyTanks[j]; if (shell.intersects(enemy)) { // Explosion var exp = new Explosion(); exp.x = enemy.x; exp.y = enemy.y; game.addChild(exp); LK.getSound('explode').play(); // Remove both shell.destroy(); playerShells.splice(i, 1); enemy.destroy(); enemyTanks.splice(j, 1); // Score LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); break; } } } // Update enemy tanks for (var i = enemyTanks.length - 1; i >= 0; i--) { var enemy = enemyTanks[i]; enemy.update(); // Remove if off screen if (enemy.y > GAME_H + enemy.height / 2) { enemy.destroy(); enemyTanks.splice(i, 1); continue; } // Check collision with player if (enemy.intersects(player)) { // Explosion var exp = new Explosion(); exp.x = player.x; exp.y = player.y; game.addChild(exp); LK.getSound('explode').play(); LK.effects.flashScreen(0xff0000, 800); // Decrease health playerHealth--; // Update health display var hearts = ''; for (var h = 0; h < playerHealth; h++) { hearts += '❤'; } healthTxt.setText(hearts); if (playerHealth <= 0) { // Create animated game over screen gameOverAnimation = new GameOverAnimator(); game.addChild(gameOverAnimation); gameOverAnimation.initialize(); // Delay the actual game over to allow animation to play LK.setTimeout(function () { LK.showGameOver(); }, 5000); // Show game over after 5 seconds of animation return; } // Animate player hit effect tween.stop(player, { rotation: true, scaleX: true, scaleY: true, alpha: true }); tween(player, { scaleX: 1.3, scaleY: 1.3, alpha: 0.6 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(player, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300, easing: tween.elasticOut }); } }); // Remove enemy tank enemy.destroy(); enemyTanks.splice(i, 1); continue; } } // Update enemy shells for (var i = enemyShells.length - 1; i >= 0; i--) { var shell = enemyShells[i]; shell.update(); // Remove if off screen if (shell.y > GAME_H + shell.height / 2 || shell.x < -shell.width / 2 || shell.x > GAME_W + shell.width / 2) { shell.destroy(); enemyShells.splice(i, 1); continue; } // Check collision with player if (shell.intersects(player)) { // Explosion var exp = new Explosion(); exp.x = shell.x; exp.y = shell.y; game.addChild(exp); LK.getSound('explode').play(); LK.effects.flashScreen(0xff0000, 800); // Decrease health playerHealth--; // Update health display var hearts = ''; for (var h = 0; h < playerHealth; h++) { hearts += '❤'; } healthTxt.setText(hearts); if (playerHealth <= 0) { // Create animated game over screen gameOverAnimation = new GameOverAnimator(); game.addChild(gameOverAnimation); gameOverAnimation.initialize(); // Delay the actual game over to allow animation to play LK.setTimeout(function () { LK.showGameOver(); }, 5000); // Show game over after 5 seconds of animation return; } // Animate player hit by shell tween.stop(player, { rotation: true, alpha: true }); // Push player slightly in the direction of the hit var pushX = (player.x - shell.x) * 0.05; var pushY = (player.y - shell.y) * 0.05; var origX = player.x; var origY = player.y; tween(player, { x: player.x + pushX, y: player.y + pushY, alpha: 0.7 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(player, { x: origX, y: origY, alpha: 1 }, { duration: 280, easing: tween.easeInOut }); } }); // Remove shell shell.destroy(); enemyShells.splice(i, 1); continue; } } // Update health power-ups for (var i = healthPowerUps.length - 1; i >= 0; i--) { var heart = healthPowerUps[i]; heart.update(); // Check if player collected the heart if (player.intersects(heart)) { // Only collect if health is less than maximum if (playerHealth < 5) { // Play collect sound LK.getSound('enemyFire').play(); // Increase health playerHealth++; // Update health display var hearts = ''; for (var h = 0; h < playerHealth; h++) { hearts += '❤'; } healthTxt.setText(hearts); // Animation heart.collect(); // Remove heart LK.setTimeout(function () { heart.destroy(); }, 300); healthPowerUps.splice(i, 1); // Flash effect LK.effects.flashScreen(0x00ff00, 300); } } // Remove if off screen if (heart.y > GAME_H + heart.height / 2) { heart.destroy(); healthPowerUps.splice(i, 1); } } // Update health power-ups for (var i = healthPowerUps.length - 1; i >= 0; i--) { var heart = healthPowerUps[i]; heart.update(); // Check if player collected the heart if (player.intersects(heart)) { // Only collect if health is less than maximum if (playerHealth < 5) { // Play collect sound LK.getSound('enemyFire').play(); // Increase health playerHealth++; // Update health display var hearts = ''; for (var h = 0; h < playerHealth; h++) { hearts += '❤'; } healthTxt.setText(hearts); // Animation heart.collect(); // Remove heart LK.setTimeout(function () { heart.destroy(); }, 300); healthPowerUps.splice(i, 1); // Flash effect LK.effects.flashScreen(0x00ff00, 300); } } // Remove if off screen if (heart.y > GAME_H + heart.height / 2) { heart.destroy(); healthPowerUps.splice(i, 1); } } // Spawn enemies enemySpawnCounter++; if (enemySpawnCounter >= getEnemySpawnInterval()) { enemySpawnCounter = 0; // Randomly spawn health power-up (around 10% chance, only if player's health is not full) if (Math.random() < 0.1 && playerHealth < 5 && healthPowerUps.length < 2) { var healthPowerUp = new Health(); var margin = 300; healthPowerUp.x = margin + Math.random() * (GAME_W - margin * 2); healthPowerUp.y = -healthPowerUp.height; healthPowerUp.lastY = healthPowerUp.y; // Add to game healthPowerUps.push(healthPowerUp); game.addChild(healthPowerUp); // Apply slow falling movement tween(healthPowerUp, { y: GAME_H + healthPowerUp.height }, { duration: 20000, // Slow fall over 20 seconds easing: tween.linear }); } // Randomly spawn health power-up (around 10% chance, only if player's health is not full) if (Math.random() < 0.1 && playerHealth < 5 && healthPowerUps.length < 2) { var healthPowerUp = new Health(); var margin = 300; healthPowerUp.x = margin + Math.random() * (GAME_W - margin * 2); healthPowerUp.y = -healthPowerUp.height; healthPowerUp.lastY = healthPowerUp.y; // Add to game healthPowerUps.push(healthPowerUp); game.addChild(healthPowerUp); // Apply slow falling movement tween(healthPowerUp, { y: GAME_H + healthPowerUp.height }, { duration: 20000, // Slow fall over 20 seconds easing: tween.linear }); } // Only spawn an enemy if there are not too many already if (enemyTanks.length < 5 + Math.floor(LK.getScore() / 10)) { var enemy = new EnemyTank(); // Position enemies at the top, across the width of the screen var margin = 200; enemy.x = margin / 2 + Math.random() * (GAME_W - margin); enemy.y = -enemy.height / 2; // Start just off-screen at the top // Rotate to face down enemy.rotation = Math.PI; // 180 degrees to face down enemyTanks.push(enemy); game.addChild(enemy); // Add spawn animation enemy.alpha = 0; enemy.scaleX = 0.2; enemy.scaleY = 0.2; tween(enemy, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 400, easing: tween.elasticOut }); console.log("Spawned enemy tank from top"); } } }; // Game initialization happens in startGame function
===================================================================
--- original.js
+++ change.js
@@ -927,8 +927,9 @@
var playerShells = [];
var enemyTanks = [];
var enemyShells = [];
var healthPowerUps = [];
+var healthPowerUps = [];
// Create animated intro
function showAnimatedIntro() {
animatedIntro = new AnimatedIntro();
game.addChild(animatedIntro);
@@ -966,8 +967,9 @@
playerShells = [];
enemyTanks = [];
enemyShells = [];
healthPowerUps = [];
+ healthPowerUps = [];
// Reset score and health
LK.setScore(0);
scoreTxt.setText('0');
playerHealth = 5;
@@ -1368,8 +1370,43 @@
heart.destroy();
healthPowerUps.splice(i, 1);
}
}
+ // Update health power-ups
+ for (var i = healthPowerUps.length - 1; i >= 0; i--) {
+ var heart = healthPowerUps[i];
+ heart.update();
+ // Check if player collected the heart
+ if (player.intersects(heart)) {
+ // Only collect if health is less than maximum
+ if (playerHealth < 5) {
+ // Play collect sound
+ LK.getSound('enemyFire').play();
+ // Increase health
+ playerHealth++;
+ // Update health display
+ var hearts = '';
+ for (var h = 0; h < playerHealth; h++) {
+ hearts += '❤';
+ }
+ healthTxt.setText(hearts);
+ // Animation
+ heart.collect();
+ // Remove heart
+ LK.setTimeout(function () {
+ heart.destroy();
+ }, 300);
+ healthPowerUps.splice(i, 1);
+ // Flash effect
+ LK.effects.flashScreen(0x00ff00, 300);
+ }
+ }
+ // Remove if off screen
+ if (heart.y > GAME_H + heart.height / 2) {
+ heart.destroy();
+ healthPowerUps.splice(i, 1);
+ }
+ }
// Spawn enemies
enemySpawnCounter++;
if (enemySpawnCounter >= getEnemySpawnInterval()) {
enemySpawnCounter = 0;
@@ -1391,8 +1428,27 @@
// Slow fall over 20 seconds
easing: tween.linear
});
}
+ // Randomly spawn health power-up (around 10% chance, only if player's health is not full)
+ if (Math.random() < 0.1 && playerHealth < 5 && healthPowerUps.length < 2) {
+ var healthPowerUp = new Health();
+ var margin = 300;
+ healthPowerUp.x = margin + Math.random() * (GAME_W - margin * 2);
+ healthPowerUp.y = -healthPowerUp.height;
+ healthPowerUp.lastY = healthPowerUp.y;
+ // Add to game
+ healthPowerUps.push(healthPowerUp);
+ game.addChild(healthPowerUp);
+ // Apply slow falling movement
+ tween(healthPowerUp, {
+ y: GAME_H + healthPowerUp.height
+ }, {
+ duration: 20000,
+ // Slow fall over 20 seconds
+ easing: tween.linear
+ });
+ }
// Only spawn an enemy if there are not too many already
if (enemyTanks.length < 5 + Math.floor(LK.getScore() / 10)) {
var enemy = new EnemyTank();
// Position enemies at the top, across the width of the screen