User prompt
son değişikliği iptal et
User prompt
arka planı minecraft görseli yaparmısın
User prompt
checkpointblou1 kendi etrafında dönmesin
User prompt
checkpoint açma kapama tuşunu siyah yap ve biraz büyüt
User prompt
devam et tuşunu kaldır
User prompt
bomba oluşma sıklığını arttır
User prompt
checkpoint kapalıyken checkpoint sayacını gizle ve checkpointblou1 i gizle
User prompt
checkpoint aldıktan sonra ölürsek devam et butonu göster
User prompt
checkpointi açıp kapatma tuşu koy
User prompt
kuş checkpointblou1 e değerse ve sonrasında ölürse checkpointblou1 in olduğu yerden başlasın
User prompt
her level için ayrı tohum kullan ama oyun yeniden başladığında tohum aynı kalsın
User prompt
oyun baştan başladığında seedi değiştirme
User prompt
yukarı checkpoint sayacı ekleyelim
User prompt
blokun adını "checkpoint bloğu1 olarak değiştir
User prompt
blok kalplrden 100 piksel ilerde olsun
User prompt
blok 0 can arttırsın
User prompt
kalplerle aynı mantıkta çalışan bir blok ekle
User prompt
checkpoint sistemini kaldır
User prompt
oyuncu checkpoint alırsa puanı sıfırlanmasın ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
eğer oyuncu checkpoint alırsa oyun en sonki checkpointi aldığı yerden başlasın ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
checkpointe kuş değerse yeni oyunu checkpointin olduğu yerden başlasın ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
checkpoint simgesi oluştur
User prompt
game over ekranına tekrar oyna düğmesinin yanına eğer checkpoint açıksa "Kaldığın Yerden Başla" diye nir düğme koy ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
game over ekranına eğer çhekpoint açıksa "Kaldığın Yerden Başla" diye bir buton koy ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
chekpoint her engelden sonra kalplerin yakınında dursun ↪💡 Consider importing and using the following plugins: @upit/storage.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { currentLevel: 1, unlockedLevels: 1, totalScore: 0, levelScore: 0, checkpointEnabled: true }); /**** * Classes ****/ var BackgroundShape = Container.expand(function () { var self = Container.call(this); var shapeTypes = ['bgCircle', 'bgSquare', 'bgTriangle', 'bgPentagon', 'bgHexagon', 'bgHeptagon', 'bgOctagon']; var randomType = shapeTypes[Math.floor(Math.random() * shapeTypes.length)]; var shapeGraphics = self.attachAsset(randomType, { anchorX: 0.5, anchorY: 0.5 }); self.speed = -1 - Math.random() * 2; // Random speed between -1 and -3 self.rotationSpeed = (Math.random() - 0.5) * 0.02; // Random rotation self.pulseScale = 1; self.pulseDirection = 1; self.alpha = 0.3 + Math.random() * 0.4; // Random transparency between 0.3-0.7 shapeGraphics.alpha = self.alpha; self.update = function () { self.x += self.speed; // Add rotation shapeGraphics.rotation += self.rotationSpeed; // Add subtle pulsing self.pulseScale += 0.003 * self.pulseDirection; if (self.pulseScale > 1.1) self.pulseDirection = -1; if (self.pulseScale < 0.9) self.pulseDirection = 1; shapeGraphics.scaleX = self.pulseScale; shapeGraphics.scaleY = self.pulseScale; }; return self; }); var Bird = Container.expand(function () { var self = Container.call(this); var birdGraphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); // Ensure bird is visible birdGraphics.alpha = 1.0; birdGraphics.visible = true; self.velocityY = 0; self.gravity = 0.8; self.flapPower = -12; self.rotation = 0; self.rotationVelocity = 0; self.isFlipping = false; self.completedFlips = 0; self.lastUpright = true; self.flap = function () { self.velocityY = self.flapPower; LK.getSound('flap').play(); // No score bonus for flapping - score only from obstacles }; self.update = function () { // Apply gravity self.velocityY += self.gravity; self.y += self.velocityY; // Keep bird in bounds horizontally if (self.x < 40) self.x = 40; if (self.x > 2008) self.x = 2008; }; self.isUpright = function () { var normalizedRotation = self.rotation % (Math.PI * 2); return normalizedRotation < 0.3 || normalizedRotation > Math.PI * 2 - 0.3; }; return self; }); var Bomb = Container.expand(function () { var self = Container.call(this); var bombGraphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; self.passed = false; self.collected = false; self.isBomb = true; self.pulseScale = 1; self.pulseDirection = 1; self.rotationSpeed = 0.05; self.update = function () { self.x += self.speed; // Pulsing effect for bombs self.pulseScale += 0.015 * self.pulseDirection; if (self.pulseScale > 1.3) self.pulseDirection = -1; if (self.pulseScale < 0.8) self.pulseDirection = 1; bombGraphics.scaleX = self.pulseScale; bombGraphics.scaleY = self.pulseScale; }; return self; }); var Checkpoint = Container.expand(function () { var self = Container.call(this); var checkpointGraphics = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3 }); self.speed = -3; self.activated = false; self.checkpointIndex = 0; self.obstacleIndex = 0; // Track which obstacle count this checkpoint is for self.pulseScale = 1; self.pulseDirection = 1; // Purple tint for checkpoint checkpointGraphics.tint = 0x9966ff; self.update = function () { self.x += self.speed; // Pulsing effect self.pulseScale += 0.015 * self.pulseDirection; if (self.pulseScale > 1.4) self.pulseDirection = -1; if (self.pulseScale < 0.8) self.pulseDirection = 1; checkpointGraphics.scaleX = self.pulseScale * 3; checkpointGraphics.scaleY = self.pulseScale * 3; // Rotation effect checkpointGraphics.rotation += 0.02; }; return self; }); var Heart = Container.expand(function () { var self = Container.call(this); var heartGraphics = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); self.speed = -3; self.collected = false; self.pulseScale = 1; self.pulseDirection = 1; self.floatOffset = 0; // Add pulsing and floating animation self.update = function () { self.x += self.speed; // Pulsing effect self.pulseScale += 0.01 * self.pulseDirection; if (self.pulseScale > 1.2) self.pulseDirection = -1; if (self.pulseScale < 0.8) self.pulseDirection = 1; heartGraphics.scaleX = self.pulseScale; heartGraphics.scaleY = self.pulseScale; // Floating effect self.floatOffset += 0.05; heartGraphics.y = Math.sin(self.floatOffset) * 10; }; return self; }); var Laser = Container.expand(function () { var self = Container.call(this); var laserGraphics = self.attachAsset('laser', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -24; self.passed = false; self.collected = false; self.isLaser = true; self.pulseScale = 1; self.pulseDirection = 1; self.horizontalSpeed = 6; self.horizontalDirection = 1; self.maxHorizontalRange = 300; self.startX = 0; // Laser visual effects - bright red pulsing laserGraphics.tint = 0xff0000; self.update = function () { self.x -= self.speed; // Changed to move in opposite direction (positive X, away from bird) // Horizontal oscillation movement self.x += self.horizontalSpeed * self.horizontalDirection; // Reverse direction if reached maximum range if (Math.abs(self.x - self.startX) > self.maxHorizontalRange) { self.horizontalDirection *= -1; } // Intense pulsing effect for laser self.pulseScale += 0.02 * self.pulseDirection; if (self.pulseScale > 1.4) self.pulseDirection = -1; if (self.pulseScale < 0.7) self.pulseDirection = 1; laserGraphics.scaleX = self.pulseScale; laserGraphics.scaleY = self.pulseScale; // Add rotation for visual effect laserGraphics.rotation += 0.03; }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); // Choose obstacle type from level-specific sequence var randomType = 'block'; // Default fallback if (levelObstacleTypes.length > 0) { var typeIndex = obstacleCounter % levelObstacleTypes.length; randomType = levelObstacleTypes[typeIndex]; } var obstacleGraphics = self.attachAsset(randomType, { anchorX: 0.5, anchorY: 0.5 }); self.speed = -3; self.passed = false; self.obstacleType = randomType; // Add visual effects for Geometry Dash style self.pulseScale = 1; self.pulseDirection = 1; self.rotationSpeed = 0; self.isStationary = false; // Set rotation speed for certain types if (randomType === 'spike' || randomType === 'block') { self.rotationSpeed = 0.02; } // Set rest platform specific properties if (randomType === 'restPlatform') { self.speed = 0; // Rest platforms don't move horizontally self.isStationary = true; self.isSafe = true; // Mark as safe platform } // Set door specific properties if (randomType === 'door') { self.speed = -1; // Doors move slower self.isDoor = true; // Mark as door for special collision handling self.pulseScale = 1.1; // Start with larger pulse for visibility } self.update = function () { // Only move if not stationary if (!self.isStationary) { self.x += self.speed; } // Add pulsing effect only for non-rest platform obstacles if (self.obstacleType !== 'restPlatform') { self.pulseScale += 0.005 * self.pulseDirection; if (self.pulseScale > 1.05) self.pulseDirection = -1; if (self.pulseScale < 0.98) self.pulseDirection = 1; obstacleGraphics.scaleX = self.pulseScale; obstacleGraphics.scaleY = self.pulseScale; } // Add rotation for certain obstacles if (self.rotationSpeed > 0) { obstacleGraphics.rotation += self.rotationSpeed; } }; return self; }); var Portal = Container.expand(function () { var self = Container.call(this); var portalGraphics = self.attachAsset('door', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); self.speed = -2; self.passed = false; self.isPortal = true; self.pulseScale = 1; self.pulseDirection = 1; self.rotationSpeed = 0.01; // Portal specific visual effects portalGraphics.tint = 0x9966ff; // Purple tint for portal self.update = function () { self.x += self.speed; // Enhanced pulsing effect for portal self.pulseScale += 0.008 * self.pulseDirection; if (self.pulseScale > 1.3) self.pulseDirection = -1; if (self.pulseScale < 0.8) self.pulseDirection = 1; portalGraphics.scaleX = self.pulseScale * 1.5; portalGraphics.scaleY = self.pulseScale * 1.5; // Portal rotation removed - portal no longer spins }; return self; }); var Spike = Container.expand(function () { var self = Container.call(this); var spikeGraphics = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; self.passed = false; self.collected = false; self.isSpike = true; self.pulseScale = 1; self.pulseDirection = 1; self.rotationSpeed = 0; // Spike visual effects - look like hearts (no tint) self.update = function () { self.x += self.speed; // Pulsing effect for spikes self.pulseScale += 0.01 * self.pulseDirection; if (self.pulseScale > 1.15) self.pulseDirection = -1; if (self.pulseScale < 0.9) self.pulseDirection = 1; spikeGraphics.scaleX = self.pulseScale; spikeGraphics.scaleY = self.pulseScale; // No rotation for spikes }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var bird; var obstacles = []; var hearts = []; var backgroundShapes = []; var portals = []; var spikes = []; var bombs = []; var lasers = []; var ground; var gameStarted = false; var lastObstacle = 0; var obstacleSpacing = 400; var currentLevel = 1; var unlockedLevels = 1; var totalScore = 0; var levelScoreThreshold = 10; // Base score needed to unlock next level // Load persistent scores from storage totalScore = storage.totalScore || 0; var levelScore = storage.levelScore || 0; LK.setScore(levelScore); // Checkpoint system variables var checkpoints = []; // Array to store checkpoint positions var lastCheckpointIndex = -1; // Track the last checkpoint reached var checkpointSpacing = 1000; // Distance between checkpoints var nextCheckpointX = 2500; // Position of next checkpoint var obstacleCounter = 0; // Counter for obstacle generation var levelObstacleTypes = []; // Array to store shuffled obstacle types for current level var playerHealth = 3; // Player starts with 3 lives var lastBackgroundShape = 0; var backgroundShapeSpacing = 400; // Spacing between background shapes - increased for more space between shapes var missedHearts = 0; // Track how many hearts were missed var portalSpawned = false; // Track if portal has been spawned for current level var currentObstacleArrayPassed = false; // Track if current obstacle array has been passed var lastObstacleArrayX = 0; // Track the X position of the last obstacle array // Death music is already implemented in the following scenarios: // 1. Ground collision - line 43: LK.getSound('death').play(); // 2. Spike collision - line 5F: LK.getSound('death').play(); // 3. Bomb collision when health is 1 - line 5S: LK.getSound('death').play(); // 4. When health reaches 0 in takeDamage - line 36: LK.getSound('death').play(); // Checkpoint toggle functionality var checkpointEnabled = storage.checkpointEnabled !== false; // Default to true var checkpointToggleButton; LK.setScore(0); // Create score display var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 100; // Reset storage values to initial state storage.currentLevel = 1; storage.unlockedLevels = 1; storage.totalScore = 0; // Create level display var levelTxt = new Text2('Seviye ' + currentLevel, { size: 70, fill: 0xFFD700 }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.y = 180; // Create level progress display var progressTxt = new Text2('İlerleme: 0/' + levelScoreThreshold, { size: 40, fill: 0xFFFFFF }); progressTxt.anchor.set(0.5, 0); LK.gui.top.addChild(progressTxt); progressTxt.y = 280; // Create health display var healthTxt = new Text2('Can: ' + playerHealth, { size: 50, fill: 0xff0000 }); healthTxt.anchor.set(0.5, 0); LK.gui.top.addChild(healthTxt); healthTxt.y = 340; // Create checkpoint toggle button checkpointToggleButton = new Text2(checkpointEnabled ? 'Checkpoint: AÇIK' : 'Checkpoint: KAPALI', { size: 45, fill: checkpointEnabled ? 0x00ff00 : 0xff0000 }); checkpointToggleButton.anchor.set(0.5, 0); LK.gui.top.addChild(checkpointToggleButton); checkpointToggleButton.y = 400; // Create instructions var instructionTxt = new Text2('DOKUN VE UÇ\nENGELLERDEN ZİPLA!', { size: 60, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0.5); game.addChild(instructionTxt); instructionTxt.x = 1024; instructionTxt.y = 1000; // Create ground ground = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0 })); ground.x = 0; ground.y = 2632; // Create bird bird = game.addChild(new Bird()); bird.x = 300; bird.y = 800; // Generate initial obstacle sequence for current level generateLevelObstacleSequence(); // Create initial obstacles at game start createObstacle(); // Create initial background shapes for (var i = 0; i < 15; i++) { createBackgroundShape(); } function generateLevelObstacleSequence() { levelObstacleTypes = []; var baseTypes = ['spike', 'block', 'tallBlock', 'platform', 'restPlatform', 'restPlatform', 'restPlatform', 'laser']; // Door obstacle removed completely var sequenceLength = 50; // Generate enough types for a full level var seed = currentLevel * 12345 + Date.now(); // Level-based seed plus current time for different patterns each time for (var i = 0; i < sequenceLength; i++) { seed = (seed * 9301 + 49297) % 233280; var randomIndex = Math.floor(seed / 233280 * baseTypes.length); levelObstacleTypes.push(baseTypes[randomIndex]); } } function updateLevelProgress() { var levelProgress = LK.getScore(); var progressNeeded = levelScoreThreshold + (currentLevel - 1) * 10; // Update progress display progressTxt.setText('İlerleme: ' + levelProgress + '/' + progressNeeded); } function updateDifficultyForLevel() { // Each level increases difficulty significantly var difficultyMultiplier = 1 + (currentLevel - 1) * 0.4; // Adjust obstacle spacing based on level - gets much tighter obstacleSpacing = Math.max(150, 400 - (currentLevel - 1) * 30); // Adjust bird physics progressively for higher levels if (currentLevel > 2) { bird.gravity = 0.8 + (currentLevel - 2) * 0.15; // Stronger gravity earlier } // Increase bird flap power requirement for higher levels if (currentLevel > 3) { bird.flapPower = Math.max(-18, -12 - (currentLevel - 3) * 1.5); // Stronger flap needed } // Adjust obstacle speed for higher levels var obstacleSpeedMultiplier = 1 + (currentLevel - 1) * 0.2; // Apply speed multiplier to all moving obstacles for (var i = 0; i < obstacles.length; i++) { if (obstacles[i] && !obstacles[i].isStationary) { obstacles[i].speed = -3 * obstacleSpeedMultiplier; } } for (var h = 0; h < hearts.length; h++) { if (hearts[h]) { hearts[h].speed = -3 * obstacleSpeedMultiplier; } } for (var s = 0; s < spikes.length; s++) { if (spikes[s]) { spikes[s].speed = -2 * obstacleSpeedMultiplier; } } for (var b = 0; b < bombs.length; b++) { if (bombs[b]) { bombs[b].speed = -2 * obstacleSpeedMultiplier; } } for (var l = 0; l < lasers.length; l++) { if (lasers[l]) { lasers[l].speed = -24 * obstacleSpeedMultiplier; } } } function restoreFromCheckpoint() { // Check if checkpoints are enabled if (!checkpointEnabled) { return false; // Checkpoints disabled } // Load checkpoint data from storage var checkpointIndex = storage.lastCheckpointIndex || -1; if (checkpointIndex >= 0) { // Restore from checkpoint currentLevel = storage.checkpointLevel || 1; LK.setScore(storage.checkpointScore || 0); playerHealth = storage.checkpointHealth || 3; obstacleCounter = storage.checkpointObstacleCounter || 0; missedHearts = storage.checkpointMissedHearts || 0; portalSpawned = storage.checkpointPortalSpawned || false; // Update displays levelTxt.setText('Seviye ' + currentLevel); scoreTxt.setText(LK.getScore()); healthTxt.setText('Can: ' + playerHealth); // Restore bird position bird.x = storage.checkpointBirdX || 300; bird.y = storage.checkpointBirdY || 800; bird.velocityY = 0; // Clear all game objects clearAllGameObjects(); // Regenerate level generateLevelObstacleSequence(); updateDifficultyForLevel(); updateLevelProgress(); // Start the game if it's not started if (!gameStarted) { gameStarted = true; if (instructionTxt && instructionTxt.parent) { game.removeChild(instructionTxt); } } // Restart music LK.playMusic('hfu2'); // Position bird at checkpoint position bird.x = storage.checkpointBirdX || 300; bird.y = storage.checkpointBirdY || 800; // Create initial obstacles for the checkpoint level createObstacle(); return true; // Checkpoint restored } return false; // No checkpoint available } function clearAllGameObjects() { // Clear all obstacles for (var i = obstacles.length - 1; i >= 0; i--) { obstacles[i].destroy(); obstacles.splice(i, 1); } // Clear all checkpoints for (var c = checkpoints.length - 1; c >= 0; c--) { checkpoints[c].destroy(); checkpoints.splice(c, 1); } // Clear all hearts for (var h = hearts.length - 1; h >= 0; h--) { hearts[h].destroy(); hearts.splice(h, 1); } // Clear all portals for (var p = portals.length - 1; p >= 0; p--) { portals[p].destroy(); portals.splice(p, 1); } // Clear all spikes for (var s = spikes.length - 1; s >= 0; s--) { spikes[s].destroy(); spikes.splice(s, 1); } // Clear all bombs for (var b = bombs.length - 1; b >= 0; b--) { bombs[b].destroy(); bombs.splice(b, 1); } // Clear all lasers for (var l = lasers.length - 1; l >= 0; l--) { lasers[l].destroy(); lasers.splice(l, 1); } // Clear all background shapes for (var bg = backgroundShapes.length - 1; bg >= 0; bg--) { backgroundShapes[bg].destroy(); backgroundShapes.splice(bg, 1); } // Reset counters obstacleCounter = 0; missedHearts = 0; portalSpawned = false; currentObstacleArrayPassed = false; lastObstacleArrayX = 0; nextCheckpointX = 2500; } function resetToLevelStart() { // Reset to level 1 when game over occurs currentLevel = 1; storage.currentLevel = 1; levelTxt.setText('Seviye ' + currentLevel); // Reset score to level start LK.setScore(0); scoreTxt.setText(0); // Reset health playerHealth = 3; healthTxt.setText('Can: ' + playerHealth); // Clear checkpoint data when resetting to level start storage.lastCheckpointIndex = -1; storage.checkpointScore = 0; storage.checkpointLevel = 1; storage.checkpointHealth = 3; storage.checkpointBirdX = 300; storage.checkpointBirdY = 800; storage.checkpointObstacleCounter = 0; storage.checkpointMissedHearts = 0; storage.checkpointPortalSpawned = false; // Restart music when game resets LK.playMusic('hfu2'); // Clear all obstacles for (var i = obstacles.length - 1; i >= 0; i--) { obstacles[i].destroy(); obstacles.splice(i, 1); } // Clear all background shapes for (var j = backgroundShapes.length - 1; j >= 0; j--) { backgroundShapes[j].destroy(); backgroundShapes.splice(j, 1); } // Clear all hearts for (var k = hearts.length - 1; k >= 0; k--) { hearts[k].destroy(); hearts.splice(k, 1); } // Clear all portals for (var p = portals.length - 1; p >= 0; p--) { portals[p].destroy(); portals.splice(p, 1); } // Clear all spikes for (var s = spikes.length - 1; s >= 0; s--) { spikes[s].destroy(); spikes.splice(s, 1); } // Clear all bombs for (var b = bombs.length - 1; b >= 0; b--) { bombs[b].destroy(); bombs.splice(b, 1); } // Clear all lasers for (var l = lasers.length - 1; l >= 0; l--) { lasers[l].destroy(); lasers.splice(l, 1); } // Reset obstacle counter obstacleCounter = 0; // Reset missed hearts counter missedHearts = 0; // Reset portal spawned flag portalSpawned = false; // Reset obstacle array tracking currentObstacleArrayPassed = false; lastObstacleArrayX = 0; // Reset bird position bird.x = 300; bird.y = 800; bird.velocityY = 0; bird.completedFlips = 0; // Regenerate obstacle sequence for current level generateLevelObstacleSequence(); // Update displays updateLevelProgress(); // Update difficulty for level 1 updateDifficultyForLevel(); // Create initial obstacle createObstacle(); } function takeDamage() { playerHealth--; healthTxt.setText('Can: ' + playerHealth); // Flash screen red when taking damage tween(game, { tint: 0xff4444 }, { duration: 200, onFinish: function onFinish() { tween(game, { tint: 0xffffff }, { duration: 200 }); } }); // Check if game over if (playerHealth <= 0) { // Play death music LK.getSound('death').play(); // Play death sound LK.getSound('deathSound').play(); // Try to restore from checkpoint first only if enabled if (checkpointEnabled && !restoreFromCheckpoint()) { // No checkpoint available, show game over screen showGameOverWithLevelButton(); } else if (!checkpointEnabled) { // Checkpoints disabled, go straight to game over showGameOverWithLevelButton(); } } } function resetHealth() { playerHealth = 3; healthTxt.setText('Can: ' + playerHealth); } function startGame() { if (!gameStarted) { gameStarted = true; game.removeChild(instructionTxt); // Start playing music when game begins - restart music for new game LK.playMusic('hfu2'); // Reset obstacle counter for consistent level maps obstacleCounter = 0; // Generate obstacle sequence for current level generateLevelObstacleSequence(); resetHealth(); // Reset health when starting game updateDifficultyForLevel(); updateLevelProgress(); } } function createBackgroundShape() { var bgShape = new BackgroundShape(); bgShape.x = 2200 + Math.random() * 200; // Start off-screen right bgShape.y = 200 + Math.random() * 2300; // Random Y position // Scale background shapes to 3 times their original size bgShape.scaleX = 3; bgShape.scaleY = 3; // Check distance from existing background shapes to ensure spacing var minDistance = 400; // Minimum distance between shapes var validPosition = true; for (var i = 0; i < backgroundShapes.length; i++) { var existingShape = backgroundShapes[i]; var distanceX = bgShape.x - existingShape.x; var distanceY = bgShape.y - existingShape.y; var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); if (distance < minDistance) { validPosition = false; break; } } // Only add the shape if it has enough spacing if (validPosition) { backgroundShapes.push(bgShape); game.addChild(bgShape); } else { // Destroy the shape if position is invalid bgShape.destroy(); } } function createHeart() { var heart = new Heart(); heart.x = 2200; // Start off-screen right heart.y = 300 + Math.random() * 2000; // Random Y position within playable area hearts.push(heart); game.addChild(heart); } function createSpike() { // Use seed based on current level and time for randomness var spikeSeed = (currentLevel * 7777 + LK.ticks + Date.now()) % 99991; var spikeRandom = (spikeSeed * 9301 + 49297) % 233280 / 233280; // Only create spike if random chance is met (roughly 15% chance) if (spikeRandom < 0.15) { var spike = new Spike(); spike.x = 2200 + Math.random() * 100; // Start off-screen right spike.y = 300 + spikeRandom * 2000; // Random Y position spikes.push(spike); game.addChild(spike); } } function createCheckpoint() { var checkpoint = new Checkpoint(); checkpoint.x = 2200; // Start off-screen right like other objects // Position checkpoint near hearts - find the last created heart's Y position var heartY = 1366; // Default center position if (hearts.length > 0) { // Get the Y position of the most recently created heart var lastHeart = hearts[hearts.length - 1]; heartY = lastHeart.y; } checkpoint.y = heartY + 100; // Position checkpoint slightly below the heart checkpoint.checkpointIndex = checkpoints.length; checkpoint.obstacleIndex = obstacleCounter; // Track which obstacle count this checkpoint is for checkpoints.push(checkpoint); game.addChild(checkpoint); // Save checkpoint data to storage - only save essential data as literals storage.lastCheckpointX = checkpoint.x; storage.lastCheckpointY = checkpoint.y; storage.lastCheckpointObstacleIndex = checkpoint.obstacleIndex; } function createObstacle() { // Create walls that cover full screen with a gap for the bird var baseX = 2148; var gapSize = 600; // Size of the gap for bird to pass through // Progressive gap size reduction per level for increased difficulty if (currentLevel === 1) { gapSize = 650; // Easier start for level 1 } else if (currentLevel === 2) { gapSize = 580; // Slightly smaller for level 2 } else if (currentLevel === 3) { gapSize = 520; // Even smaller for level 3 } else if (currentLevel >= 4) { // Much more challenging gaps for level 4+ var baseLevelGap = Math.max(350, 500 - (currentLevel - 4) * 25); // Starts at 500, reduces by 25 per level, minimum 350 var obstacleReduction = Math.max(0, obstacleCounter * 3); // Reduce by 3 per obstacle within level gapSize = Math.max(300, baseLevelGap - obstacleReduction); // Minimum gap of 300 } // Use seeded random based on current level, obstacle counter and current time for different maps each time var seed = (currentLevel * 1000 + obstacleCounter + Date.now()) % 9973; // Use prime number for better distribution plus time var seededRandom = (seed * 9301 + 49297) % 233280 / 233280; // Linear congruential generator var gapPosition = seededRandom * (2200 - gapSize) + 300; // Seeded gap position between 300-2200 var obstacleHeight = 320; // Height of each obstacle segment (reduced from 400) // Reduce obstacle height for Level 4 to create more segments and fill gaps if (currentLevel >= 4) { obstacleHeight = 240; // Smaller segments create more obstacles (reduced from 300) } var numSegments = Math.ceil(2732 / obstacleHeight); // Number of segments to cover full height for (var i = 0; i < numSegments; i++) { var segmentY = i * obstacleHeight + obstacleHeight / 2; // Skip creating obstacles in the gap area if (segmentY + obstacleHeight / 2 > gapPosition && segmentY - obstacleHeight / 2 < gapPosition + gapSize) { continue; } var obstacle = new Obstacle(); obstacle.x = baseX; obstacle.y = segmentY; obstacles.push(obstacle); game.addChild(obstacle); } // Add extra obstacles to fill empty spaces - more for higher levels var extraObstacles = Math.min(15, 4 + currentLevel + Math.floor(seededRandom * (2 + currentLevel))); // Scales with level: 5-7 for level 1, up to 15 for high levels for (var j = 0; j < extraObstacles; j++) { var extraSeed = (seed + j * 777) % 9973; var extraRandom = (extraSeed * 9301 + 49297) % 233280 / 233280; var obstacleX = baseX + 100 + extraRandom * (400 + currentLevel * 50); // Wider spread for higher levels var obstacleY = 150 + extraRandom * 2300; // Cover more vertical space // Make sure it's not in the main gap area - tighter constraints for higher levels var gapBuffer = Math.max(30, 80 - currentLevel * 5); // Smaller buffer for higher levels if (obstacleY < gapPosition - gapBuffer || obstacleY > gapPosition + gapSize + gapBuffer) { var extraObstacle = new Obstacle(); extraObstacle.x = obstacleX; extraObstacle.y = obstacleY; obstacles.push(extraObstacle); game.addChild(extraObstacle); } } // Add obstacles in the middle area - much more for higher levels var middleObstacles = Math.min(12, 3 + currentLevel * 2 + Math.floor(seededRandom * currentLevel)); // Scales dramatically: 4-6 for level 1, up to 12 for high levels for (var m = 0; m < middleObstacles; m++) { var middleSeed = (seed + m * 333 + 111) % 9973; var middleRandom = (middleSeed * 9301 + 49297) % 233280 / 233280; var middleX = baseX - 300 - middleRandom * (600 + currentLevel * 100); // Wider area coverage for higher levels var middleY = 250 + middleRandom * 2100; // Random Y position // Ensure obstacles are within screen bounds - tighter placement for higher levels var minX = Math.max(400, 600 - currentLevel * 20); // Allow closer placement for higher levels if (middleX > minX && middleY > 150 && middleY < 2500) { var middleObstacle = new Obstacle(); middleObstacle.x = middleX; middleObstacle.y = middleY; obstacles.push(middleObstacle); game.addChild(middleObstacle); } } // Add additional scattered obstacles for Level 4, but fewer at the beginning if (currentLevel >= 4 && obstacleCounter > 3) { // Create 2-3 additional obstacles in random positions, but only after first few obstacles var additionalObstacles = 2 + Math.floor(seededRandom * 2); // 2-3 obstacles for (var j = 0; j < additionalObstacles; j++) { var additionalSeed = (seed + j * 1000) % 9973; var additionalRandom = (additionalSeed * 9301 + 49297) % 233280 / 233280; var obstacleX = baseX + 200 + additionalRandom * 400; // Spread them out var obstacleY = 400 + additionalRandom * 1800; // Random Y position // Make sure it's not in the main gap area if (obstacleY < gapPosition || obstacleY > gapPosition + gapSize) { var additionalObstacle = new Obstacle(); additionalObstacle.x = obstacleX; additionalObstacle.y = obstacleY; obstacles.push(additionalObstacle); game.addChild(additionalObstacle); } } // Add obstacles specifically in character's current area for Level 4, but fewer at the beginning var characterAreaObstacles = obstacleCounter > 2 ? 3 + Math.floor(seededRandom * 2) : 1 + Math.floor(seededRandom * 1); // Start with 1-2 obstacles, then 3-4 for (var k = 0; k < characterAreaObstacles; k++) { var charAreaSeed = (seed + k * 2000 + 500) % 9973; var charAreaRandom = (charAreaSeed * 9301 + 49297) % 233280 / 233280; // Place obstacles in character's vicinity (around x=300, y=800) var charObstacleX = baseX + 100 + charAreaRandom * 300; // Close to current obstacles var charObstacleY = bird.y - 400 + charAreaRandom * 800; // Around character's Y position // Ensure obstacles are within screen bounds and not in gap if (charObstacleY > 100 && charObstacleY < 2500 && (charObstacleY < gapPosition || charObstacleY > gapPosition + gapSize)) { var charAreaObstacle = new Obstacle(); charAreaObstacle.x = charObstacleX; charAreaObstacle.y = charObstacleY; obstacles.push(charAreaObstacle); game.addChild(charAreaObstacle); } } } obstacleCounter++; lastObstacle = LK.ticks; // Update the last obstacle array position and reset completion flag lastObstacleArrayX = baseX; currentObstacleArrayPassed = false; // Create rest platform every 2 obstacles if (obstacleCounter % 2 === 0) { var restPlatform = new Obstacle(); restPlatform.obstacleType = 'restPlatform'; restPlatform.speed = 0; restPlatform.isStationary = true; restPlatform.isSafe = true; restPlatform.x = baseX + 300; // Position rest platform after obstacles restPlatform.y = gapPosition + gapSize / 2; // Center in gap area obstacles.push(restPlatform); game.addChild(restPlatform); } // Create hearts based on missed hearts count (minimum 1, add 1 for each missed) var heartsToCreate = 1 + missedHearts; for (var heartIndex = 0; heartIndex < heartsToCreate; heartIndex++) { var heart = new Heart(); heart.x = baseX + 30; // Position heart 30 pixels ahead of obstacle // Spread multiple hearts vertically in the gap area if (heartsToCreate === 1) { heart.y = gapPosition + gapSize / 2; // Center single heart in the gap area } else { // Distribute multiple hearts evenly across the gap var heartSpacing = gapSize / (heartsToCreate + 1); heart.y = gapPosition + heartSpacing * (heartIndex + 1); } hearts.push(heart); game.addChild(heart); } // Create spikes based on level - more spikes for higher levels with strategic positioning var spikesToCreate = Math.min(5, Math.floor(currentLevel / 2) + 1); // 1 spike for levels 1-2, up to 5 for high levels for (var spikeIndex = 0; spikeIndex < spikesToCreate; spikeIndex++) { var spike = new Spike(); spike.x = baseX + 40 + spikeIndex * 25; // Position spikes closer together // Strategic spike positioning for higher levels if (spikesToCreate === 1) { spike.y = gapPosition + gapSize / 2 + 120; // Single spike offset from heart position } else if (currentLevel >= 3) { // For level 3+: place spikes to create challenging patterns if (spikeIndex === 0) { spike.y = gapPosition - 50; // Above gap } else if (spikeIndex === 1) { spike.y = gapPosition + gapSize + 50; // Below gap } else { // Additional spikes within gap area for extreme difficulty var innerSpacing = gapSize / (spikesToCreate - 1); spike.y = gapPosition + innerSpacing * (spikeIndex - 2); } } else { // For levels 1-2: distribute spikes more safely var spikeSpacing = (gapSize + 200) / spikesToCreate; spike.y = gapPosition - 50 + spikeSpacing * spikeIndex; } spikes.push(spike); game.addChild(spike); } // Create bombs based on level - much more frequent and challenging for higher levels var bombFrequency = Math.max(1, 4 - currentLevel); // Level 1: every 3 obstacles, Level 2: every 2, Level 3+: every obstacle if (obstacleCounter % bombFrequency === 0) { // Create multiple bombs for higher levels var bombCount = Math.min(4, Math.floor(currentLevel / 2) + 1); // 1 bomb for levels 1-2, 2 for 3-4, 3 for 5-6, 4 for 7+ for (var bombIndex = 0; bombIndex < bombCount; bombIndex++) { var bomb = new Bomb(); bomb.x = baseX + 50 + bombIndex * 40; // Spread bombs horizontally // Position bombs at different heights for higher levels if (bombCount === 1) { bomb.y = gapPosition + gapSize / 2 - 100; // Single bomb position } else { // Distribute multiple bombs vertically in and around the gap var bombSpacing = (gapSize + 200) / bombCount; bomb.y = gapPosition - 100 + bombSpacing * bombIndex; } bombs.push(bomb); game.addChild(bomb); } } // Create exactly 3 lasers with each obstacle if (currentLevel >= 1) { var laserCount = 3; // Always create exactly 3 lasers for (var laserIndex = 0; laserIndex < laserCount; laserIndex++) { var laser = new Laser(); laser.x = baseX + 100 + laserIndex * 80; // Spread lasers horizontally laser.startX = laser.x; // Set starting position for oscillation // Distribute 3 lasers vertically around the gap var laserSpacing = (gapSize + 300) / laserCount; laser.y = gapPosition - 150 + laserSpacing * laserIndex; lasers.push(laser); game.addChild(laser); } } // Create rest platforms above and below the gap (door area) var topRestPlatform = new Obstacle(); topRestPlatform.obstacleType = 'restPlatform'; topRestPlatform.speed = 0; topRestPlatform.isStationary = true; topRestPlatform.isSafe = true; topRestPlatform.x = baseX; topRestPlatform.y = gapPosition - 100; // Position above the gap obstacles.push(topRestPlatform); game.addChild(topRestPlatform); var bottomRestPlatform = new Obstacle(); bottomRestPlatform.obstacleType = 'restPlatform'; bottomRestPlatform.speed = 0; bottomRestPlatform.isStationary = true; bottomRestPlatform.isSafe = true; bottomRestPlatform.x = baseX; bottomRestPlatform.y = gapPosition + gapSize + 100; // Position below the gap obstacles.push(bottomRestPlatform); game.addChild(bottomRestPlatform); } function checkCollisions() { // Check ground collision - game over when bird touches ground if (bird.y + 60 >= ground.y) { // Play death music LK.getSound('death').play(); // Play death sound LK.getSound('deathSound').play(); // Show game over screen with level button showGameOverWithLevelButton(); return; } // Check obstacle collisions - bounce off obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obstacle = obstacles[i]; // Skip if obstacle is undefined if (!obstacle) { obstacles.splice(i, 1); continue; } if (bird.intersects(obstacle)) { // Check if it's a rest platform - safe area if (obstacle.obstacleType === 'restPlatform') { // Safe collision - just bounce without damage var distanceX = bird.x - obstacle.x; var distanceY = bird.y - obstacle.y; var totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); if (totalDistance > 0) { var normalX = distanceX / totalDistance; var normalY = distanceY / totalDistance; // Gentle push away from platform var pushForce = 3; bird.x += normalX * pushForce; bird.y += normalY * pushForce; // Apply gentle bounce velocity bird.velocityY = normalY * 3; // Visual feedback for landing on safe platform LK.effects.flashObject(obstacle, 0x00ff88, 300); } continue; // Skip damage dealing } // Only take damage if not already damaged from this obstacle if (!obstacle.damagedBird) { takeDamage(); obstacle.damagedBird = true; // Mark this obstacle as having caused damage } // Calculate collision direction var distanceX = bird.x - obstacle.x; var distanceY = bird.y - obstacle.y; var totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); // Normalize collision direction if (totalDistance > 0) { var normalX = distanceX / totalDistance; var normalY = distanceY / totalDistance; // Push bird away from obstacle var pushForce = 8; bird.x += normalX * pushForce; bird.y += normalY * pushForce; // Apply bounce velocity var bounceForce = 5; bird.velocityY = normalY * bounceForce; // Play flap sound for bounce feedback LK.getSound('flap').play(); } } // Check if bird passed obstacle (mark individual obstacles as passed but don't award points yet) if (!obstacle.passed && obstacle.x + 100 < bird.x) { obstacle.passed = true; } ; // Remove off-screen obstacles (but keep stationary gears and rest platforms) if (obstacle.x < -100 && !obstacle.isStationary) { // Add flash effect when obstacle disappears LK.effects.flashObject(obstacle, 0xffffff, 200); obstacle.destroy(); obstacles.splice(i, 1); } } // Check heart collections for (var h = hearts.length - 1; h >= 0; h--) { var heart = hearts[h]; // Skip if heart is undefined if (!heart) { hearts.splice(h, 1); continue; } // Check if bird collected the heart if (bird.intersects(heart) && !heart.collected) { heart.collected = true; // Reset missed hearts counter when collecting a heart missedHearts = 0; // Always increase health (no maximum limit) playerHealth++; healthTxt.setText('Can: ' + playerHealth); // Increase score by 2 when collecting heart LK.setScore(LK.getScore() + 2); storage.levelScore = LK.getScore(); // Save to storage scoreTxt.setText(LK.getScore()); updateLevelProgress(); // Visual feedback for collecting heart tween(heart, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { heart.destroy(); } }); // Flash screen green briefly LK.effects.flashScreen(0x00ff00, 200); // Play score sound as feedback LK.getSound('score').play(); hearts.splice(h, 1); continue; } // Remove off-screen hearts if (heart.x < -100) { // Track that this heart was missed missedHearts++; heart.destroy(); hearts.splice(h, 1); } } // Check spike collisions - instant death for (var s = spikes.length - 1; s >= 0; s--) { var spike = spikes[s]; // Skip if spike is undefined if (!spike) { spikes.splice(s, 1); continue; } // Check if bird touched the spike - instant death if (bird.intersects(spike)) { // Play death music LK.getSound('death').play(); // Play death sound LK.getSound('deathSound').play(); // Flash screen red to indicate death LK.effects.flashScreen(0xff0000, 500); // Show game over screen immediately showGameOverWithLevelButton(); return; // Exit collision check immediately } // Remove off-screen spikes if (spike.x < -150) { spike.destroy(); spikes.splice(s, 1); } } // Check bomb collisions - reduce health to 1 and remove all hearts for (var b = bombs.length - 1; b >= 0; b--) { var bomb = bombs[b]; // Skip if bomb is undefined if (!bomb) { bombs.splice(b, 1); continue; } // Check if bird touched the bomb if (bird.intersects(bomb) && !bomb.collected) { bomb.collected = true; // Play bomb explosion sound LK.getSound('bombExplosion').play(); // Create explosion effect - scale up and fade out tween(bomb, { scaleX: 3, scaleY: 3, alpha: 0, rotation: Math.PI * 2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { bomb.destroy(); } }); // Remove all hearts from the game first for (var hRemove = hearts.length - 1; hRemove >= 0; hRemove--) { hearts[hRemove].destroy(); hearts.splice(hRemove, 1); } // Check if player already had 1 health (would die from bomb) if (playerHealth <= 1) { // Play death music LK.getSound('death').play(); // Play death sound LK.getSound('deathSound').play(); // Show game over screen immediately showGameOverWithLevelButton(); return; // Exit collision check immediately } // Reduce health to 1 playerHealth = 1; healthTxt.setText('Can: ' + playerHealth); // Flash screen red to indicate bomb effect LK.effects.flashScreen(0xff4444, 800); // Remove the bomb from array but don't destroy immediately (explosion handles destruction) bombs.splice(b, 1); continue; } // Remove off-screen bombs if (bomb.x < -150) { bomb.destroy(); bombs.splice(b, 1); } } // Check laser collisions - damage player and apply knockback for (var l = lasers.length - 1; l >= 0; l--) { var laser = lasers[l]; // Skip if laser is undefined if (!laser) { lasers.splice(l, 1); continue; } // Check if bird touched the laser if (bird.intersects(laser) && !laser.collected) { laser.collected = true; // Play laser sound LK.getSound('laserSound').play(); // Take damage takeDamage(); // Apply knockback effect var knockbackForce = 15; var distanceX = bird.x - laser.x; var distanceY = bird.y - laser.y; var totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); if (totalDistance > 0) { var normalX = distanceX / totalDistance; var normalY = distanceY / totalDistance; // Push bird away from laser bird.x += normalX * knockbackForce; bird.y += normalY * knockbackForce; // Apply strong bounce velocity bird.velocityY = normalY * 8; } // Flash screen red to indicate laser hit LK.effects.flashScreen(0xff0000, 400); // Create destruction effect for laser tween(laser, { scaleX: 0, scaleY: 0, alpha: 0, rotation: Math.PI * 4 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { laser.destroy(); } }); lasers.splice(l, 1); continue; } // Remove off-screen lasers (now check right side since they move right) if (laser.x > 2300) { laser.destroy(); lasers.splice(l, 1); } } // Check portal collisions for (var p = portals.length - 1; p >= 0; p--) { var portal = portals[p]; // Skip if portal is undefined if (!portal) { portals.splice(p, 1); continue; } if (bird.intersects(portal)) { // Flash screen purple to indicate portal entry LK.effects.flashScreen(0x9966ff, 800); // Add current level score to total and preserve it totalScore += LK.getScore(); storage.totalScore = totalScore; storage.levelScore = LK.getScore(); // Save current level score // Unlock next level if not already unlocked if (currentLevel >= unlockedLevels) { unlockedLevels = currentLevel + 1; storage.unlockedLevels = unlockedLevels; } // Advance to next level currentLevel++; storage.currentLevel = currentLevel; levelTxt.setText('Seviye ' + currentLevel); // Keep current score when advancing levels (don't reset to 0) // Score persists to next level scoreTxt.setText(LK.getScore()); // Reset obstacle counter for consistent level maps obstacleCounter = 0; // Clear all obstacles for (var j = obstacles.length - 1; j >= 0; j--) { obstacles[j].destroy(); obstacles.splice(j, 1); } // Clear all portals for (var pt = portals.length - 1; pt >= 0; pt--) { portals[pt].destroy(); portals.splice(pt, 1); } // Generate new obstacle sequence for the new level generateLevelObstacleSequence(); updateLevelProgress(); // Increase difficulty for new level updateDifficultyForLevel(); // Reset bird position bird.x = 300; bird.y = 800; bird.velocityY = 0; bird.completedFlips = 0; // Reset health resetHealth(); // Reset portal spawned flag portalSpawned = false; // Create initial obstacle for new level createObstacle(); return; // Exit collision check to avoid other collision handling } // Remove off-screen portals if (portal.x < -200) { portal.destroy(); portals.splice(p, 1); } } // Check checkpoint collisions for (var c = checkpoints.length - 1; c >= 0; c--) { var checkpoint = checkpoints[c]; // Skip if checkpoint is undefined if (!checkpoint) { checkpoints.splice(c, 1); continue; } if (bird.intersects(checkpoint) && !checkpoint.activated) { checkpoint.activated = true; lastCheckpointIndex = checkpoint.checkpointIndex; // Save checkpoint progress to storage storage.lastCheckpointIndex = lastCheckpointIndex; storage.checkpointBirdX = bird.x; storage.checkpointBirdY = bird.y; storage.checkpointScore = LK.getScore(); storage.checkpointLevel = currentLevel; storage.checkpointHealth = playerHealth; storage.checkpointObstacleCounter = obstacleCounter; storage.checkpointMissedHearts = missedHearts; storage.checkpointPortalSpawned = portalSpawned; // Visual feedback for checkpoint activation LK.effects.flashScreen(0x9966ff, 500); LK.getSound('score').play(); // Scale up and fade checkpoint tween(checkpoint, { scaleX: 6, scaleY: 6, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { checkpoint.destroy(); } }); checkpoints.splice(c, 1); continue; } // Remove off-screen checkpoints if (checkpoint.x < -200) { checkpoint.destroy(); checkpoints.splice(c, 1); } } // Check ceiling collision if (bird.y < 50) { bird.y = 50; bird.velocityY = 0; } } // Add checkpoint toggle button click handler checkpointToggleButton.down = function (x, y, obj) { // Toggle checkpoint setting checkpointEnabled = !checkpointEnabled; storage.checkpointEnabled = checkpointEnabled; // Update button appearance checkpointToggleButton.setText(checkpointEnabled ? 'Checkpoint: AÇIK' : 'Checkpoint: KAPALI'); checkpointToggleButton.tint = checkpointEnabled ? 0x00ff00 : 0xff0000; }; game.down = function (x, y, obj) { if (!gameStarted) { startGame(); } bird.flap(); }; game.update = function () { if (!gameStarted) return; // Create obstacles if (LK.ticks - lastObstacle > obstacleSpacing) { createObstacle(); } // Create checkpoints after every obstacle near hearts if (obstacleCounter > 0) { // Only create if we don't already have a checkpoint for this obstacle count var checkpointExists = false; for (var cp = 0; cp < checkpoints.length; cp++) { if (checkpoints[cp].obstacleIndex === obstacleCounter) { checkpointExists = true; break; } } if (!checkpointExists) { createCheckpoint(); } } // Create background shapes if (LK.ticks - lastBackgroundShape > backgroundShapeSpacing) { createBackgroundShape(); lastBackgroundShape = LK.ticks; } // Spikes are now created after obstacles, no random creation needed // Hearts are now created after each obstacle, so no timer-based spawning needed // Update bird physics bird.update(); // Update obstacles for (var i = 0; i < obstacles.length; i++) { obstacles[i].update(); } // Update hearts for (var h = 0; h < hearts.length; h++) { hearts[h].update(); } // Update portals for (var p = 0; p < portals.length; p++) { portals[p].update(); } // Update spikes for (var s = 0; s < spikes.length; s++) { spikes[s].update(); } // Update bombs for (var b = 0; b < bombs.length; b++) { bombs[b].update(); } // Update lasers for (var l = 0; l < lasers.length; l++) { lasers[l].update(); } // Update checkpoints for (var cp = 0; cp < checkpoints.length; cp++) { checkpoints[cp].update(); } // Update background shapes for (var j = backgroundShapes.length - 1; j >= 0; j--) { var bgShape = backgroundShapes[j]; bgShape.update(); // Remove off-screen background shapes if (bgShape.x < -200) { bgShape.destroy(); backgroundShapes.splice(j, 1); } } // Check collisions checkCollisions(); // Check individual obstacle passing for score increment for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; // Check if bird passed obstacle and hasn't scored from it yet if (!obstacle.passed && !obstacle.scoredFrom && obstacle.x + 100 < bird.x) { obstacle.passed = true; obstacle.scoredFrom = true; // Mark as scored to prevent duplicate scoring LK.setScore(LK.getScore() + 1); storage.levelScore = LK.getScore(); // Save to storage scoreTxt.setText(LK.getScore()); LK.getSound('score').play(); updateLevelProgress(); } } // Create portal when score reaches level threshold (10 + (level-1)*10) var currentLevelThreshold = levelScoreThreshold + (currentLevel - 1) * 10; if (LK.getScore() >= currentLevelThreshold && !portalSpawned) { var portal = new Portal(); portal.x = 2200; // Start off-screen right portal.y = 1366; // Center of screen vertically portals.push(portal); game.addChild(portal); // Hide all existing obstacles when portal is created for (var hideIndex = 0; hideIndex < obstacles.length; hideIndex++) { obstacles[hideIndex].visible = false; } // Hide all hearts when portal is created for (var heartHideIndex = 0; heartHideIndex < hearts.length; heartHideIndex++) { hearts[heartHideIndex].visible = false; } // Hide all spikes when portal is created for (var spikeHideIndex = 0; spikeHideIndex < spikes.length; spikeHideIndex++) { spikes[spikeHideIndex].visible = false; } // Hide all bombs when portal is created for (var bombHideIndex = 0; bombHideIndex < bombs.length; bombHideIndex++) { bombs[bombHideIndex].visible = false; } // Hide all lasers when portal is created for (var laserHideIndex = 0; laserHideIndex < lasers.length; laserHideIndex++) { lasers[laserHideIndex].visible = false; } portalSpawned = true; } // No time-based score increment - score only from obstacles // Level-based difficulty progression var baseSpacing = 400 - (currentLevel - 1) * 20; var minSpacing = Math.max(200, 300 - (currentLevel - 1) * 10); var reductionRate = 1 + currentLevel; // Increase reduction rate with level var currentSpacing = baseSpacing - LK.getScore() * reductionRate; obstacleSpacing = Math.max(minSpacing, currentSpacing); }; function showGameOverWithLevelButton() { // Check if checkpoint is available var hasCheckpoint = (storage.lastCheckpointIndex || -1) >= 0; if (hasCheckpoint) { // Show checkpoint restore option before resetting var shouldRestore = restoreFromCheckpoint(); if (shouldRestore) { return; // Don't show game over if checkpoint was restored } } resetToLevelStart(); LK.showGameOver(); }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
unlockedLevels: 1,
totalScore: 0,
levelScore: 0,
checkpointEnabled: true
});
/****
* Classes
****/
var BackgroundShape = Container.expand(function () {
var self = Container.call(this);
var shapeTypes = ['bgCircle', 'bgSquare', 'bgTriangle', 'bgPentagon', 'bgHexagon', 'bgHeptagon', 'bgOctagon'];
var randomType = shapeTypes[Math.floor(Math.random() * shapeTypes.length)];
var shapeGraphics = self.attachAsset(randomType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -1 - Math.random() * 2; // Random speed between -1 and -3
self.rotationSpeed = (Math.random() - 0.5) * 0.02; // Random rotation
self.pulseScale = 1;
self.pulseDirection = 1;
self.alpha = 0.3 + Math.random() * 0.4; // Random transparency between 0.3-0.7
shapeGraphics.alpha = self.alpha;
self.update = function () {
self.x += self.speed;
// Add rotation
shapeGraphics.rotation += self.rotationSpeed;
// Add subtle pulsing
self.pulseScale += 0.003 * self.pulseDirection;
if (self.pulseScale > 1.1) self.pulseDirection = -1;
if (self.pulseScale < 0.9) self.pulseDirection = 1;
shapeGraphics.scaleX = self.pulseScale;
shapeGraphics.scaleY = self.pulseScale;
};
return self;
});
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Ensure bird is visible
birdGraphics.alpha = 1.0;
birdGraphics.visible = true;
self.velocityY = 0;
self.gravity = 0.8;
self.flapPower = -12;
self.rotation = 0;
self.rotationVelocity = 0;
self.isFlipping = false;
self.completedFlips = 0;
self.lastUpright = true;
self.flap = function () {
self.velocityY = self.flapPower;
LK.getSound('flap').play();
// No score bonus for flapping - score only from obstacles
};
self.update = function () {
// Apply gravity
self.velocityY += self.gravity;
self.y += self.velocityY;
// Keep bird in bounds horizontally
if (self.x < 40) self.x = 40;
if (self.x > 2008) self.x = 2008;
};
self.isUpright = function () {
var normalizedRotation = self.rotation % (Math.PI * 2);
return normalizedRotation < 0.3 || normalizedRotation > Math.PI * 2 - 0.3;
};
return self;
});
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.passed = false;
self.collected = false;
self.isBomb = true;
self.pulseScale = 1;
self.pulseDirection = 1;
self.rotationSpeed = 0.05;
self.update = function () {
self.x += self.speed;
// Pulsing effect for bombs
self.pulseScale += 0.015 * self.pulseDirection;
if (self.pulseScale > 1.3) self.pulseDirection = -1;
if (self.pulseScale < 0.8) self.pulseDirection = 1;
bombGraphics.scaleX = self.pulseScale;
bombGraphics.scaleY = self.pulseScale;
};
return self;
});
var Checkpoint = Container.expand(function () {
var self = Container.call(this);
var checkpointGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
self.speed = -3;
self.activated = false;
self.checkpointIndex = 0;
self.obstacleIndex = 0; // Track which obstacle count this checkpoint is for
self.pulseScale = 1;
self.pulseDirection = 1;
// Purple tint for checkpoint
checkpointGraphics.tint = 0x9966ff;
self.update = function () {
self.x += self.speed;
// Pulsing effect
self.pulseScale += 0.015 * self.pulseDirection;
if (self.pulseScale > 1.4) self.pulseDirection = -1;
if (self.pulseScale < 0.8) self.pulseDirection = 1;
checkpointGraphics.scaleX = self.pulseScale * 3;
checkpointGraphics.scaleY = self.pulseScale * 3;
// Rotation effect
checkpointGraphics.rotation += 0.02;
};
return self;
});
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.speed = -3;
self.collected = false;
self.pulseScale = 1;
self.pulseDirection = 1;
self.floatOffset = 0;
// Add pulsing and floating animation
self.update = function () {
self.x += self.speed;
// Pulsing effect
self.pulseScale += 0.01 * self.pulseDirection;
if (self.pulseScale > 1.2) self.pulseDirection = -1;
if (self.pulseScale < 0.8) self.pulseDirection = 1;
heartGraphics.scaleX = self.pulseScale;
heartGraphics.scaleY = self.pulseScale;
// Floating effect
self.floatOffset += 0.05;
heartGraphics.y = Math.sin(self.floatOffset) * 10;
};
return self;
});
var Laser = Container.expand(function () {
var self = Container.call(this);
var laserGraphics = self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -24;
self.passed = false;
self.collected = false;
self.isLaser = true;
self.pulseScale = 1;
self.pulseDirection = 1;
self.horizontalSpeed = 6;
self.horizontalDirection = 1;
self.maxHorizontalRange = 300;
self.startX = 0;
// Laser visual effects - bright red pulsing
laserGraphics.tint = 0xff0000;
self.update = function () {
self.x -= self.speed; // Changed to move in opposite direction (positive X, away from bird)
// Horizontal oscillation movement
self.x += self.horizontalSpeed * self.horizontalDirection;
// Reverse direction if reached maximum range
if (Math.abs(self.x - self.startX) > self.maxHorizontalRange) {
self.horizontalDirection *= -1;
}
// Intense pulsing effect for laser
self.pulseScale += 0.02 * self.pulseDirection;
if (self.pulseScale > 1.4) self.pulseDirection = -1;
if (self.pulseScale < 0.7) self.pulseDirection = 1;
laserGraphics.scaleX = self.pulseScale;
laserGraphics.scaleY = self.pulseScale;
// Add rotation for visual effect
laserGraphics.rotation += 0.03;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Choose obstacle type from level-specific sequence
var randomType = 'block'; // Default fallback
if (levelObstacleTypes.length > 0) {
var typeIndex = obstacleCounter % levelObstacleTypes.length;
randomType = levelObstacleTypes[typeIndex];
}
var obstacleGraphics = self.attachAsset(randomType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -3;
self.passed = false;
self.obstacleType = randomType;
// Add visual effects for Geometry Dash style
self.pulseScale = 1;
self.pulseDirection = 1;
self.rotationSpeed = 0;
self.isStationary = false;
// Set rotation speed for certain types
if (randomType === 'spike' || randomType === 'block') {
self.rotationSpeed = 0.02;
}
// Set rest platform specific properties
if (randomType === 'restPlatform') {
self.speed = 0; // Rest platforms don't move horizontally
self.isStationary = true;
self.isSafe = true; // Mark as safe platform
}
// Set door specific properties
if (randomType === 'door') {
self.speed = -1; // Doors move slower
self.isDoor = true; // Mark as door for special collision handling
self.pulseScale = 1.1; // Start with larger pulse for visibility
}
self.update = function () {
// Only move if not stationary
if (!self.isStationary) {
self.x += self.speed;
}
// Add pulsing effect only for non-rest platform obstacles
if (self.obstacleType !== 'restPlatform') {
self.pulseScale += 0.005 * self.pulseDirection;
if (self.pulseScale > 1.05) self.pulseDirection = -1;
if (self.pulseScale < 0.98) self.pulseDirection = 1;
obstacleGraphics.scaleX = self.pulseScale;
obstacleGraphics.scaleY = self.pulseScale;
}
// Add rotation for certain obstacles
if (self.rotationSpeed > 0) {
obstacleGraphics.rotation += self.rotationSpeed;
}
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
var portalGraphics = self.attachAsset('door', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = -2;
self.passed = false;
self.isPortal = true;
self.pulseScale = 1;
self.pulseDirection = 1;
self.rotationSpeed = 0.01;
// Portal specific visual effects
portalGraphics.tint = 0x9966ff; // Purple tint for portal
self.update = function () {
self.x += self.speed;
// Enhanced pulsing effect for portal
self.pulseScale += 0.008 * self.pulseDirection;
if (self.pulseScale > 1.3) self.pulseDirection = -1;
if (self.pulseScale < 0.8) self.pulseDirection = 1;
portalGraphics.scaleX = self.pulseScale * 1.5;
portalGraphics.scaleY = self.pulseScale * 1.5;
// Portal rotation removed - portal no longer spins
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.passed = false;
self.collected = false;
self.isSpike = true;
self.pulseScale = 1;
self.pulseDirection = 1;
self.rotationSpeed = 0;
// Spike visual effects - look like hearts (no tint)
self.update = function () {
self.x += self.speed;
// Pulsing effect for spikes
self.pulseScale += 0.01 * self.pulseDirection;
if (self.pulseScale > 1.15) self.pulseDirection = -1;
if (self.pulseScale < 0.9) self.pulseDirection = 1;
spikeGraphics.scaleX = self.pulseScale;
spikeGraphics.scaleY = self.pulseScale;
// No rotation for spikes
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var bird;
var obstacles = [];
var hearts = [];
var backgroundShapes = [];
var portals = [];
var spikes = [];
var bombs = [];
var lasers = [];
var ground;
var gameStarted = false;
var lastObstacle = 0;
var obstacleSpacing = 400;
var currentLevel = 1;
var unlockedLevels = 1;
var totalScore = 0;
var levelScoreThreshold = 10; // Base score needed to unlock next level
// Load persistent scores from storage
totalScore = storage.totalScore || 0;
var levelScore = storage.levelScore || 0;
LK.setScore(levelScore);
// Checkpoint system variables
var checkpoints = []; // Array to store checkpoint positions
var lastCheckpointIndex = -1; // Track the last checkpoint reached
var checkpointSpacing = 1000; // Distance between checkpoints
var nextCheckpointX = 2500; // Position of next checkpoint
var obstacleCounter = 0; // Counter for obstacle generation
var levelObstacleTypes = []; // Array to store shuffled obstacle types for current level
var playerHealth = 3; // Player starts with 3 lives
var lastBackgroundShape = 0;
var backgroundShapeSpacing = 400; // Spacing between background shapes - increased for more space between shapes
var missedHearts = 0; // Track how many hearts were missed
var portalSpawned = false; // Track if portal has been spawned for current level
var currentObstacleArrayPassed = false; // Track if current obstacle array has been passed
var lastObstacleArrayX = 0; // Track the X position of the last obstacle array
// Death music is already implemented in the following scenarios:
// 1. Ground collision - line 43: LK.getSound('death').play();
// 2. Spike collision - line 5F: LK.getSound('death').play();
// 3. Bomb collision when health is 1 - line 5S: LK.getSound('death').play();
// 4. When health reaches 0 in takeDamage - line 36: LK.getSound('death').play();
// Checkpoint toggle functionality
var checkpointEnabled = storage.checkpointEnabled !== false; // Default to true
var checkpointToggleButton;
LK.setScore(0);
// Create score display
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
// Reset storage values to initial state
storage.currentLevel = 1;
storage.unlockedLevels = 1;
storage.totalScore = 0;
// Create level display
var levelTxt = new Text2('Seviye ' + currentLevel, {
size: 70,
fill: 0xFFD700
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 180;
// Create level progress display
var progressTxt = new Text2('İlerleme: 0/' + levelScoreThreshold, {
size: 40,
fill: 0xFFFFFF
});
progressTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(progressTxt);
progressTxt.y = 280;
// Create health display
var healthTxt = new Text2('Can: ' + playerHealth, {
size: 50,
fill: 0xff0000
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
healthTxt.y = 340;
// Create checkpoint toggle button
checkpointToggleButton = new Text2(checkpointEnabled ? 'Checkpoint: AÇIK' : 'Checkpoint: KAPALI', {
size: 45,
fill: checkpointEnabled ? 0x00ff00 : 0xff0000
});
checkpointToggleButton.anchor.set(0.5, 0);
LK.gui.top.addChild(checkpointToggleButton);
checkpointToggleButton.y = 400;
// Create instructions
var instructionTxt = new Text2('DOKUN VE UÇ\nENGELLERDEN ZİPLA!', {
size: 60,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
game.addChild(instructionTxt);
instructionTxt.x = 1024;
instructionTxt.y = 1000;
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = 2632;
// Create bird
bird = game.addChild(new Bird());
bird.x = 300;
bird.y = 800;
// Generate initial obstacle sequence for current level
generateLevelObstacleSequence();
// Create initial obstacles at game start
createObstacle();
// Create initial background shapes
for (var i = 0; i < 15; i++) {
createBackgroundShape();
}
function generateLevelObstacleSequence() {
levelObstacleTypes = [];
var baseTypes = ['spike', 'block', 'tallBlock', 'platform', 'restPlatform', 'restPlatform', 'restPlatform', 'laser']; // Door obstacle removed completely
var sequenceLength = 50; // Generate enough types for a full level
var seed = currentLevel * 12345 + Date.now(); // Level-based seed plus current time for different patterns each time
for (var i = 0; i < sequenceLength; i++) {
seed = (seed * 9301 + 49297) % 233280;
var randomIndex = Math.floor(seed / 233280 * baseTypes.length);
levelObstacleTypes.push(baseTypes[randomIndex]);
}
}
function updateLevelProgress() {
var levelProgress = LK.getScore();
var progressNeeded = levelScoreThreshold + (currentLevel - 1) * 10;
// Update progress display
progressTxt.setText('İlerleme: ' + levelProgress + '/' + progressNeeded);
}
function updateDifficultyForLevel() {
// Each level increases difficulty significantly
var difficultyMultiplier = 1 + (currentLevel - 1) * 0.4;
// Adjust obstacle spacing based on level - gets much tighter
obstacleSpacing = Math.max(150, 400 - (currentLevel - 1) * 30);
// Adjust bird physics progressively for higher levels
if (currentLevel > 2) {
bird.gravity = 0.8 + (currentLevel - 2) * 0.15; // Stronger gravity earlier
}
// Increase bird flap power requirement for higher levels
if (currentLevel > 3) {
bird.flapPower = Math.max(-18, -12 - (currentLevel - 3) * 1.5); // Stronger flap needed
}
// Adjust obstacle speed for higher levels
var obstacleSpeedMultiplier = 1 + (currentLevel - 1) * 0.2;
// Apply speed multiplier to all moving obstacles
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] && !obstacles[i].isStationary) {
obstacles[i].speed = -3 * obstacleSpeedMultiplier;
}
}
for (var h = 0; h < hearts.length; h++) {
if (hearts[h]) {
hearts[h].speed = -3 * obstacleSpeedMultiplier;
}
}
for (var s = 0; s < spikes.length; s++) {
if (spikes[s]) {
spikes[s].speed = -2 * obstacleSpeedMultiplier;
}
}
for (var b = 0; b < bombs.length; b++) {
if (bombs[b]) {
bombs[b].speed = -2 * obstacleSpeedMultiplier;
}
}
for (var l = 0; l < lasers.length; l++) {
if (lasers[l]) {
lasers[l].speed = -24 * obstacleSpeedMultiplier;
}
}
}
function restoreFromCheckpoint() {
// Check if checkpoints are enabled
if (!checkpointEnabled) {
return false; // Checkpoints disabled
}
// Load checkpoint data from storage
var checkpointIndex = storage.lastCheckpointIndex || -1;
if (checkpointIndex >= 0) {
// Restore from checkpoint
currentLevel = storage.checkpointLevel || 1;
LK.setScore(storage.checkpointScore || 0);
playerHealth = storage.checkpointHealth || 3;
obstacleCounter = storage.checkpointObstacleCounter || 0;
missedHearts = storage.checkpointMissedHearts || 0;
portalSpawned = storage.checkpointPortalSpawned || false;
// Update displays
levelTxt.setText('Seviye ' + currentLevel);
scoreTxt.setText(LK.getScore());
healthTxt.setText('Can: ' + playerHealth);
// Restore bird position
bird.x = storage.checkpointBirdX || 300;
bird.y = storage.checkpointBirdY || 800;
bird.velocityY = 0;
// Clear all game objects
clearAllGameObjects();
// Regenerate level
generateLevelObstacleSequence();
updateDifficultyForLevel();
updateLevelProgress();
// Start the game if it's not started
if (!gameStarted) {
gameStarted = true;
if (instructionTxt && instructionTxt.parent) {
game.removeChild(instructionTxt);
}
}
// Restart music
LK.playMusic('hfu2');
// Position bird at checkpoint position
bird.x = storage.checkpointBirdX || 300;
bird.y = storage.checkpointBirdY || 800;
// Create initial obstacles for the checkpoint level
createObstacle();
return true; // Checkpoint restored
}
return false; // No checkpoint available
}
function clearAllGameObjects() {
// Clear all obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear all checkpoints
for (var c = checkpoints.length - 1; c >= 0; c--) {
checkpoints[c].destroy();
checkpoints.splice(c, 1);
}
// Clear all hearts
for (var h = hearts.length - 1; h >= 0; h--) {
hearts[h].destroy();
hearts.splice(h, 1);
}
// Clear all portals
for (var p = portals.length - 1; p >= 0; p--) {
portals[p].destroy();
portals.splice(p, 1);
}
// Clear all spikes
for (var s = spikes.length - 1; s >= 0; s--) {
spikes[s].destroy();
spikes.splice(s, 1);
}
// Clear all bombs
for (var b = bombs.length - 1; b >= 0; b--) {
bombs[b].destroy();
bombs.splice(b, 1);
}
// Clear all lasers
for (var l = lasers.length - 1; l >= 0; l--) {
lasers[l].destroy();
lasers.splice(l, 1);
}
// Clear all background shapes
for (var bg = backgroundShapes.length - 1; bg >= 0; bg--) {
backgroundShapes[bg].destroy();
backgroundShapes.splice(bg, 1);
}
// Reset counters
obstacleCounter = 0;
missedHearts = 0;
portalSpawned = false;
currentObstacleArrayPassed = false;
lastObstacleArrayX = 0;
nextCheckpointX = 2500;
}
function resetToLevelStart() {
// Reset to level 1 when game over occurs
currentLevel = 1;
storage.currentLevel = 1;
levelTxt.setText('Seviye ' + currentLevel);
// Reset score to level start
LK.setScore(0);
scoreTxt.setText(0);
// Reset health
playerHealth = 3;
healthTxt.setText('Can: ' + playerHealth);
// Clear checkpoint data when resetting to level start
storage.lastCheckpointIndex = -1;
storage.checkpointScore = 0;
storage.checkpointLevel = 1;
storage.checkpointHealth = 3;
storage.checkpointBirdX = 300;
storage.checkpointBirdY = 800;
storage.checkpointObstacleCounter = 0;
storage.checkpointMissedHearts = 0;
storage.checkpointPortalSpawned = false;
// Restart music when game resets
LK.playMusic('hfu2');
// Clear all obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear all background shapes
for (var j = backgroundShapes.length - 1; j >= 0; j--) {
backgroundShapes[j].destroy();
backgroundShapes.splice(j, 1);
}
// Clear all hearts
for (var k = hearts.length - 1; k >= 0; k--) {
hearts[k].destroy();
hearts.splice(k, 1);
}
// Clear all portals
for (var p = portals.length - 1; p >= 0; p--) {
portals[p].destroy();
portals.splice(p, 1);
}
// Clear all spikes
for (var s = spikes.length - 1; s >= 0; s--) {
spikes[s].destroy();
spikes.splice(s, 1);
}
// Clear all bombs
for (var b = bombs.length - 1; b >= 0; b--) {
bombs[b].destroy();
bombs.splice(b, 1);
}
// Clear all lasers
for (var l = lasers.length - 1; l >= 0; l--) {
lasers[l].destroy();
lasers.splice(l, 1);
}
// Reset obstacle counter
obstacleCounter = 0;
// Reset missed hearts counter
missedHearts = 0;
// Reset portal spawned flag
portalSpawned = false;
// Reset obstacle array tracking
currentObstacleArrayPassed = false;
lastObstacleArrayX = 0;
// Reset bird position
bird.x = 300;
bird.y = 800;
bird.velocityY = 0;
bird.completedFlips = 0;
// Regenerate obstacle sequence for current level
generateLevelObstacleSequence();
// Update displays
updateLevelProgress();
// Update difficulty for level 1
updateDifficultyForLevel();
// Create initial obstacle
createObstacle();
}
function takeDamage() {
playerHealth--;
healthTxt.setText('Can: ' + playerHealth);
// Flash screen red when taking damage
tween(game, {
tint: 0xff4444
}, {
duration: 200,
onFinish: function onFinish() {
tween(game, {
tint: 0xffffff
}, {
duration: 200
});
}
});
// Check if game over
if (playerHealth <= 0) {
// Play death music
LK.getSound('death').play();
// Play death sound
LK.getSound('deathSound').play();
// Try to restore from checkpoint first only if enabled
if (checkpointEnabled && !restoreFromCheckpoint()) {
// No checkpoint available, show game over screen
showGameOverWithLevelButton();
} else if (!checkpointEnabled) {
// Checkpoints disabled, go straight to game over
showGameOverWithLevelButton();
}
}
}
function resetHealth() {
playerHealth = 3;
healthTxt.setText('Can: ' + playerHealth);
}
function startGame() {
if (!gameStarted) {
gameStarted = true;
game.removeChild(instructionTxt);
// Start playing music when game begins - restart music for new game
LK.playMusic('hfu2');
// Reset obstacle counter for consistent level maps
obstacleCounter = 0;
// Generate obstacle sequence for current level
generateLevelObstacleSequence();
resetHealth(); // Reset health when starting game
updateDifficultyForLevel();
updateLevelProgress();
}
}
function createBackgroundShape() {
var bgShape = new BackgroundShape();
bgShape.x = 2200 + Math.random() * 200; // Start off-screen right
bgShape.y = 200 + Math.random() * 2300; // Random Y position
// Scale background shapes to 3 times their original size
bgShape.scaleX = 3;
bgShape.scaleY = 3;
// Check distance from existing background shapes to ensure spacing
var minDistance = 400; // Minimum distance between shapes
var validPosition = true;
for (var i = 0; i < backgroundShapes.length; i++) {
var existingShape = backgroundShapes[i];
var distanceX = bgShape.x - existingShape.x;
var distanceY = bgShape.y - existingShape.y;
var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
if (distance < minDistance) {
validPosition = false;
break;
}
}
// Only add the shape if it has enough spacing
if (validPosition) {
backgroundShapes.push(bgShape);
game.addChild(bgShape);
} else {
// Destroy the shape if position is invalid
bgShape.destroy();
}
}
function createHeart() {
var heart = new Heart();
heart.x = 2200; // Start off-screen right
heart.y = 300 + Math.random() * 2000; // Random Y position within playable area
hearts.push(heart);
game.addChild(heart);
}
function createSpike() {
// Use seed based on current level and time for randomness
var spikeSeed = (currentLevel * 7777 + LK.ticks + Date.now()) % 99991;
var spikeRandom = (spikeSeed * 9301 + 49297) % 233280 / 233280;
// Only create spike if random chance is met (roughly 15% chance)
if (spikeRandom < 0.15) {
var spike = new Spike();
spike.x = 2200 + Math.random() * 100; // Start off-screen right
spike.y = 300 + spikeRandom * 2000; // Random Y position
spikes.push(spike);
game.addChild(spike);
}
}
function createCheckpoint() {
var checkpoint = new Checkpoint();
checkpoint.x = 2200; // Start off-screen right like other objects
// Position checkpoint near hearts - find the last created heart's Y position
var heartY = 1366; // Default center position
if (hearts.length > 0) {
// Get the Y position of the most recently created heart
var lastHeart = hearts[hearts.length - 1];
heartY = lastHeart.y;
}
checkpoint.y = heartY + 100; // Position checkpoint slightly below the heart
checkpoint.checkpointIndex = checkpoints.length;
checkpoint.obstacleIndex = obstacleCounter; // Track which obstacle count this checkpoint is for
checkpoints.push(checkpoint);
game.addChild(checkpoint);
// Save checkpoint data to storage - only save essential data as literals
storage.lastCheckpointX = checkpoint.x;
storage.lastCheckpointY = checkpoint.y;
storage.lastCheckpointObstacleIndex = checkpoint.obstacleIndex;
}
function createObstacle() {
// Create walls that cover full screen with a gap for the bird
var baseX = 2148;
var gapSize = 600; // Size of the gap for bird to pass through
// Progressive gap size reduction per level for increased difficulty
if (currentLevel === 1) {
gapSize = 650; // Easier start for level 1
} else if (currentLevel === 2) {
gapSize = 580; // Slightly smaller for level 2
} else if (currentLevel === 3) {
gapSize = 520; // Even smaller for level 3
} else if (currentLevel >= 4) {
// Much more challenging gaps for level 4+
var baseLevelGap = Math.max(350, 500 - (currentLevel - 4) * 25); // Starts at 500, reduces by 25 per level, minimum 350
var obstacleReduction = Math.max(0, obstacleCounter * 3); // Reduce by 3 per obstacle within level
gapSize = Math.max(300, baseLevelGap - obstacleReduction); // Minimum gap of 300
}
// Use seeded random based on current level, obstacle counter and current time for different maps each time
var seed = (currentLevel * 1000 + obstacleCounter + Date.now()) % 9973; // Use prime number for better distribution plus time
var seededRandom = (seed * 9301 + 49297) % 233280 / 233280; // Linear congruential generator
var gapPosition = seededRandom * (2200 - gapSize) + 300; // Seeded gap position between 300-2200
var obstacleHeight = 320; // Height of each obstacle segment (reduced from 400)
// Reduce obstacle height for Level 4 to create more segments and fill gaps
if (currentLevel >= 4) {
obstacleHeight = 240; // Smaller segments create more obstacles (reduced from 300)
}
var numSegments = Math.ceil(2732 / obstacleHeight); // Number of segments to cover full height
for (var i = 0; i < numSegments; i++) {
var segmentY = i * obstacleHeight + obstacleHeight / 2;
// Skip creating obstacles in the gap area
if (segmentY + obstacleHeight / 2 > gapPosition && segmentY - obstacleHeight / 2 < gapPosition + gapSize) {
continue;
}
var obstacle = new Obstacle();
obstacle.x = baseX;
obstacle.y = segmentY;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Add extra obstacles to fill empty spaces - more for higher levels
var extraObstacles = Math.min(15, 4 + currentLevel + Math.floor(seededRandom * (2 + currentLevel))); // Scales with level: 5-7 for level 1, up to 15 for high levels
for (var j = 0; j < extraObstacles; j++) {
var extraSeed = (seed + j * 777) % 9973;
var extraRandom = (extraSeed * 9301 + 49297) % 233280 / 233280;
var obstacleX = baseX + 100 + extraRandom * (400 + currentLevel * 50); // Wider spread for higher levels
var obstacleY = 150 + extraRandom * 2300; // Cover more vertical space
// Make sure it's not in the main gap area - tighter constraints for higher levels
var gapBuffer = Math.max(30, 80 - currentLevel * 5); // Smaller buffer for higher levels
if (obstacleY < gapPosition - gapBuffer || obstacleY > gapPosition + gapSize + gapBuffer) {
var extraObstacle = new Obstacle();
extraObstacle.x = obstacleX;
extraObstacle.y = obstacleY;
obstacles.push(extraObstacle);
game.addChild(extraObstacle);
}
}
// Add obstacles in the middle area - much more for higher levels
var middleObstacles = Math.min(12, 3 + currentLevel * 2 + Math.floor(seededRandom * currentLevel)); // Scales dramatically: 4-6 for level 1, up to 12 for high levels
for (var m = 0; m < middleObstacles; m++) {
var middleSeed = (seed + m * 333 + 111) % 9973;
var middleRandom = (middleSeed * 9301 + 49297) % 233280 / 233280;
var middleX = baseX - 300 - middleRandom * (600 + currentLevel * 100); // Wider area coverage for higher levels
var middleY = 250 + middleRandom * 2100; // Random Y position
// Ensure obstacles are within screen bounds - tighter placement for higher levels
var minX = Math.max(400, 600 - currentLevel * 20); // Allow closer placement for higher levels
if (middleX > minX && middleY > 150 && middleY < 2500) {
var middleObstacle = new Obstacle();
middleObstacle.x = middleX;
middleObstacle.y = middleY;
obstacles.push(middleObstacle);
game.addChild(middleObstacle);
}
}
// Add additional scattered obstacles for Level 4, but fewer at the beginning
if (currentLevel >= 4 && obstacleCounter > 3) {
// Create 2-3 additional obstacles in random positions, but only after first few obstacles
var additionalObstacles = 2 + Math.floor(seededRandom * 2); // 2-3 obstacles
for (var j = 0; j < additionalObstacles; j++) {
var additionalSeed = (seed + j * 1000) % 9973;
var additionalRandom = (additionalSeed * 9301 + 49297) % 233280 / 233280;
var obstacleX = baseX + 200 + additionalRandom * 400; // Spread them out
var obstacleY = 400 + additionalRandom * 1800; // Random Y position
// Make sure it's not in the main gap area
if (obstacleY < gapPosition || obstacleY > gapPosition + gapSize) {
var additionalObstacle = new Obstacle();
additionalObstacle.x = obstacleX;
additionalObstacle.y = obstacleY;
obstacles.push(additionalObstacle);
game.addChild(additionalObstacle);
}
}
// Add obstacles specifically in character's current area for Level 4, but fewer at the beginning
var characterAreaObstacles = obstacleCounter > 2 ? 3 + Math.floor(seededRandom * 2) : 1 + Math.floor(seededRandom * 1); // Start with 1-2 obstacles, then 3-4
for (var k = 0; k < characterAreaObstacles; k++) {
var charAreaSeed = (seed + k * 2000 + 500) % 9973;
var charAreaRandom = (charAreaSeed * 9301 + 49297) % 233280 / 233280;
// Place obstacles in character's vicinity (around x=300, y=800)
var charObstacleX = baseX + 100 + charAreaRandom * 300; // Close to current obstacles
var charObstacleY = bird.y - 400 + charAreaRandom * 800; // Around character's Y position
// Ensure obstacles are within screen bounds and not in gap
if (charObstacleY > 100 && charObstacleY < 2500 && (charObstacleY < gapPosition || charObstacleY > gapPosition + gapSize)) {
var charAreaObstacle = new Obstacle();
charAreaObstacle.x = charObstacleX;
charAreaObstacle.y = charObstacleY;
obstacles.push(charAreaObstacle);
game.addChild(charAreaObstacle);
}
}
}
obstacleCounter++;
lastObstacle = LK.ticks;
// Update the last obstacle array position and reset completion flag
lastObstacleArrayX = baseX;
currentObstacleArrayPassed = false;
// Create rest platform every 2 obstacles
if (obstacleCounter % 2 === 0) {
var restPlatform = new Obstacle();
restPlatform.obstacleType = 'restPlatform';
restPlatform.speed = 0;
restPlatform.isStationary = true;
restPlatform.isSafe = true;
restPlatform.x = baseX + 300; // Position rest platform after obstacles
restPlatform.y = gapPosition + gapSize / 2; // Center in gap area
obstacles.push(restPlatform);
game.addChild(restPlatform);
}
// Create hearts based on missed hearts count (minimum 1, add 1 for each missed)
var heartsToCreate = 1 + missedHearts;
for (var heartIndex = 0; heartIndex < heartsToCreate; heartIndex++) {
var heart = new Heart();
heart.x = baseX + 30; // Position heart 30 pixels ahead of obstacle
// Spread multiple hearts vertically in the gap area
if (heartsToCreate === 1) {
heart.y = gapPosition + gapSize / 2; // Center single heart in the gap area
} else {
// Distribute multiple hearts evenly across the gap
var heartSpacing = gapSize / (heartsToCreate + 1);
heart.y = gapPosition + heartSpacing * (heartIndex + 1);
}
hearts.push(heart);
game.addChild(heart);
}
// Create spikes based on level - more spikes for higher levels with strategic positioning
var spikesToCreate = Math.min(5, Math.floor(currentLevel / 2) + 1); // 1 spike for levels 1-2, up to 5 for high levels
for (var spikeIndex = 0; spikeIndex < spikesToCreate; spikeIndex++) {
var spike = new Spike();
spike.x = baseX + 40 + spikeIndex * 25; // Position spikes closer together
// Strategic spike positioning for higher levels
if (spikesToCreate === 1) {
spike.y = gapPosition + gapSize / 2 + 120; // Single spike offset from heart position
} else if (currentLevel >= 3) {
// For level 3+: place spikes to create challenging patterns
if (spikeIndex === 0) {
spike.y = gapPosition - 50; // Above gap
} else if (spikeIndex === 1) {
spike.y = gapPosition + gapSize + 50; // Below gap
} else {
// Additional spikes within gap area for extreme difficulty
var innerSpacing = gapSize / (spikesToCreate - 1);
spike.y = gapPosition + innerSpacing * (spikeIndex - 2);
}
} else {
// For levels 1-2: distribute spikes more safely
var spikeSpacing = (gapSize + 200) / spikesToCreate;
spike.y = gapPosition - 50 + spikeSpacing * spikeIndex;
}
spikes.push(spike);
game.addChild(spike);
}
// Create bombs based on level - much more frequent and challenging for higher levels
var bombFrequency = Math.max(1, 4 - currentLevel); // Level 1: every 3 obstacles, Level 2: every 2, Level 3+: every obstacle
if (obstacleCounter % bombFrequency === 0) {
// Create multiple bombs for higher levels
var bombCount = Math.min(4, Math.floor(currentLevel / 2) + 1); // 1 bomb for levels 1-2, 2 for 3-4, 3 for 5-6, 4 for 7+
for (var bombIndex = 0; bombIndex < bombCount; bombIndex++) {
var bomb = new Bomb();
bomb.x = baseX + 50 + bombIndex * 40; // Spread bombs horizontally
// Position bombs at different heights for higher levels
if (bombCount === 1) {
bomb.y = gapPosition + gapSize / 2 - 100; // Single bomb position
} else {
// Distribute multiple bombs vertically in and around the gap
var bombSpacing = (gapSize + 200) / bombCount;
bomb.y = gapPosition - 100 + bombSpacing * bombIndex;
}
bombs.push(bomb);
game.addChild(bomb);
}
}
// Create exactly 3 lasers with each obstacle
if (currentLevel >= 1) {
var laserCount = 3; // Always create exactly 3 lasers
for (var laserIndex = 0; laserIndex < laserCount; laserIndex++) {
var laser = new Laser();
laser.x = baseX + 100 + laserIndex * 80; // Spread lasers horizontally
laser.startX = laser.x; // Set starting position for oscillation
// Distribute 3 lasers vertically around the gap
var laserSpacing = (gapSize + 300) / laserCount;
laser.y = gapPosition - 150 + laserSpacing * laserIndex;
lasers.push(laser);
game.addChild(laser);
}
}
// Create rest platforms above and below the gap (door area)
var topRestPlatform = new Obstacle();
topRestPlatform.obstacleType = 'restPlatform';
topRestPlatform.speed = 0;
topRestPlatform.isStationary = true;
topRestPlatform.isSafe = true;
topRestPlatform.x = baseX;
topRestPlatform.y = gapPosition - 100; // Position above the gap
obstacles.push(topRestPlatform);
game.addChild(topRestPlatform);
var bottomRestPlatform = new Obstacle();
bottomRestPlatform.obstacleType = 'restPlatform';
bottomRestPlatform.speed = 0;
bottomRestPlatform.isStationary = true;
bottomRestPlatform.isSafe = true;
bottomRestPlatform.x = baseX;
bottomRestPlatform.y = gapPosition + gapSize + 100; // Position below the gap
obstacles.push(bottomRestPlatform);
game.addChild(bottomRestPlatform);
}
function checkCollisions() {
// Check ground collision - game over when bird touches ground
if (bird.y + 60 >= ground.y) {
// Play death music
LK.getSound('death').play();
// Play death sound
LK.getSound('deathSound').play();
// Show game over screen with level button
showGameOverWithLevelButton();
return;
}
// Check obstacle collisions - bounce off obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Skip if obstacle is undefined
if (!obstacle) {
obstacles.splice(i, 1);
continue;
}
if (bird.intersects(obstacle)) {
// Check if it's a rest platform - safe area
if (obstacle.obstacleType === 'restPlatform') {
// Safe collision - just bounce without damage
var distanceX = bird.x - obstacle.x;
var distanceY = bird.y - obstacle.y;
var totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
if (totalDistance > 0) {
var normalX = distanceX / totalDistance;
var normalY = distanceY / totalDistance;
// Gentle push away from platform
var pushForce = 3;
bird.x += normalX * pushForce;
bird.y += normalY * pushForce;
// Apply gentle bounce velocity
bird.velocityY = normalY * 3;
// Visual feedback for landing on safe platform
LK.effects.flashObject(obstacle, 0x00ff88, 300);
}
continue; // Skip damage dealing
}
// Only take damage if not already damaged from this obstacle
if (!obstacle.damagedBird) {
takeDamage();
obstacle.damagedBird = true; // Mark this obstacle as having caused damage
}
// Calculate collision direction
var distanceX = bird.x - obstacle.x;
var distanceY = bird.y - obstacle.y;
var totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
// Normalize collision direction
if (totalDistance > 0) {
var normalX = distanceX / totalDistance;
var normalY = distanceY / totalDistance;
// Push bird away from obstacle
var pushForce = 8;
bird.x += normalX * pushForce;
bird.y += normalY * pushForce;
// Apply bounce velocity
var bounceForce = 5;
bird.velocityY = normalY * bounceForce;
// Play flap sound for bounce feedback
LK.getSound('flap').play();
}
}
// Check if bird passed obstacle (mark individual obstacles as passed but don't award points yet)
if (!obstacle.passed && obstacle.x + 100 < bird.x) {
obstacle.passed = true;
}
;
// Remove off-screen obstacles (but keep stationary gears and rest platforms)
if (obstacle.x < -100 && !obstacle.isStationary) {
// Add flash effect when obstacle disappears
LK.effects.flashObject(obstacle, 0xffffff, 200);
obstacle.destroy();
obstacles.splice(i, 1);
}
}
// Check heart collections
for (var h = hearts.length - 1; h >= 0; h--) {
var heart = hearts[h];
// Skip if heart is undefined
if (!heart) {
hearts.splice(h, 1);
continue;
}
// Check if bird collected the heart
if (bird.intersects(heart) && !heart.collected) {
heart.collected = true;
// Reset missed hearts counter when collecting a heart
missedHearts = 0;
// Always increase health (no maximum limit)
playerHealth++;
healthTxt.setText('Can: ' + playerHealth);
// Increase score by 2 when collecting heart
LK.setScore(LK.getScore() + 2);
storage.levelScore = LK.getScore(); // Save to storage
scoreTxt.setText(LK.getScore());
updateLevelProgress();
// Visual feedback for collecting heart
tween(heart, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
heart.destroy();
}
});
// Flash screen green briefly
LK.effects.flashScreen(0x00ff00, 200);
// Play score sound as feedback
LK.getSound('score').play();
hearts.splice(h, 1);
continue;
}
// Remove off-screen hearts
if (heart.x < -100) {
// Track that this heart was missed
missedHearts++;
heart.destroy();
hearts.splice(h, 1);
}
}
// Check spike collisions - instant death
for (var s = spikes.length - 1; s >= 0; s--) {
var spike = spikes[s];
// Skip if spike is undefined
if (!spike) {
spikes.splice(s, 1);
continue;
}
// Check if bird touched the spike - instant death
if (bird.intersects(spike)) {
// Play death music
LK.getSound('death').play();
// Play death sound
LK.getSound('deathSound').play();
// Flash screen red to indicate death
LK.effects.flashScreen(0xff0000, 500);
// Show game over screen immediately
showGameOverWithLevelButton();
return; // Exit collision check immediately
}
// Remove off-screen spikes
if (spike.x < -150) {
spike.destroy();
spikes.splice(s, 1);
}
}
// Check bomb collisions - reduce health to 1 and remove all hearts
for (var b = bombs.length - 1; b >= 0; b--) {
var bomb = bombs[b];
// Skip if bomb is undefined
if (!bomb) {
bombs.splice(b, 1);
continue;
}
// Check if bird touched the bomb
if (bird.intersects(bomb) && !bomb.collected) {
bomb.collected = true;
// Play bomb explosion sound
LK.getSound('bombExplosion').play();
// Create explosion effect - scale up and fade out
tween(bomb, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
bomb.destroy();
}
});
// Remove all hearts from the game first
for (var hRemove = hearts.length - 1; hRemove >= 0; hRemove--) {
hearts[hRemove].destroy();
hearts.splice(hRemove, 1);
}
// Check if player already had 1 health (would die from bomb)
if (playerHealth <= 1) {
// Play death music
LK.getSound('death').play();
// Play death sound
LK.getSound('deathSound').play();
// Show game over screen immediately
showGameOverWithLevelButton();
return; // Exit collision check immediately
}
// Reduce health to 1
playerHealth = 1;
healthTxt.setText('Can: ' + playerHealth);
// Flash screen red to indicate bomb effect
LK.effects.flashScreen(0xff4444, 800);
// Remove the bomb from array but don't destroy immediately (explosion handles destruction)
bombs.splice(b, 1);
continue;
}
// Remove off-screen bombs
if (bomb.x < -150) {
bomb.destroy();
bombs.splice(b, 1);
}
}
// Check laser collisions - damage player and apply knockback
for (var l = lasers.length - 1; l >= 0; l--) {
var laser = lasers[l];
// Skip if laser is undefined
if (!laser) {
lasers.splice(l, 1);
continue;
}
// Check if bird touched the laser
if (bird.intersects(laser) && !laser.collected) {
laser.collected = true;
// Play laser sound
LK.getSound('laserSound').play();
// Take damage
takeDamage();
// Apply knockback effect
var knockbackForce = 15;
var distanceX = bird.x - laser.x;
var distanceY = bird.y - laser.y;
var totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
if (totalDistance > 0) {
var normalX = distanceX / totalDistance;
var normalY = distanceY / totalDistance;
// Push bird away from laser
bird.x += normalX * knockbackForce;
bird.y += normalY * knockbackForce;
// Apply strong bounce velocity
bird.velocityY = normalY * 8;
}
// Flash screen red to indicate laser hit
LK.effects.flashScreen(0xff0000, 400);
// Create destruction effect for laser
tween(laser, {
scaleX: 0,
scaleY: 0,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
laser.destroy();
}
});
lasers.splice(l, 1);
continue;
}
// Remove off-screen lasers (now check right side since they move right)
if (laser.x > 2300) {
laser.destroy();
lasers.splice(l, 1);
}
}
// Check portal collisions
for (var p = portals.length - 1; p >= 0; p--) {
var portal = portals[p];
// Skip if portal is undefined
if (!portal) {
portals.splice(p, 1);
continue;
}
if (bird.intersects(portal)) {
// Flash screen purple to indicate portal entry
LK.effects.flashScreen(0x9966ff, 800);
// Add current level score to total and preserve it
totalScore += LK.getScore();
storage.totalScore = totalScore;
storage.levelScore = LK.getScore(); // Save current level score
// Unlock next level if not already unlocked
if (currentLevel >= unlockedLevels) {
unlockedLevels = currentLevel + 1;
storage.unlockedLevels = unlockedLevels;
}
// Advance to next level
currentLevel++;
storage.currentLevel = currentLevel;
levelTxt.setText('Seviye ' + currentLevel);
// Keep current score when advancing levels (don't reset to 0)
// Score persists to next level
scoreTxt.setText(LK.getScore());
// Reset obstacle counter for consistent level maps
obstacleCounter = 0;
// Clear all obstacles
for (var j = obstacles.length - 1; j >= 0; j--) {
obstacles[j].destroy();
obstacles.splice(j, 1);
}
// Clear all portals
for (var pt = portals.length - 1; pt >= 0; pt--) {
portals[pt].destroy();
portals.splice(pt, 1);
}
// Generate new obstacle sequence for the new level
generateLevelObstacleSequence();
updateLevelProgress();
// Increase difficulty for new level
updateDifficultyForLevel();
// Reset bird position
bird.x = 300;
bird.y = 800;
bird.velocityY = 0;
bird.completedFlips = 0;
// Reset health
resetHealth();
// Reset portal spawned flag
portalSpawned = false;
// Create initial obstacle for new level
createObstacle();
return; // Exit collision check to avoid other collision handling
}
// Remove off-screen portals
if (portal.x < -200) {
portal.destroy();
portals.splice(p, 1);
}
}
// Check checkpoint collisions
for (var c = checkpoints.length - 1; c >= 0; c--) {
var checkpoint = checkpoints[c];
// Skip if checkpoint is undefined
if (!checkpoint) {
checkpoints.splice(c, 1);
continue;
}
if (bird.intersects(checkpoint) && !checkpoint.activated) {
checkpoint.activated = true;
lastCheckpointIndex = checkpoint.checkpointIndex;
// Save checkpoint progress to storage
storage.lastCheckpointIndex = lastCheckpointIndex;
storage.checkpointBirdX = bird.x;
storage.checkpointBirdY = bird.y;
storage.checkpointScore = LK.getScore();
storage.checkpointLevel = currentLevel;
storage.checkpointHealth = playerHealth;
storage.checkpointObstacleCounter = obstacleCounter;
storage.checkpointMissedHearts = missedHearts;
storage.checkpointPortalSpawned = portalSpawned;
// Visual feedback for checkpoint activation
LK.effects.flashScreen(0x9966ff, 500);
LK.getSound('score').play();
// Scale up and fade checkpoint
tween(checkpoint, {
scaleX: 6,
scaleY: 6,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
checkpoint.destroy();
}
});
checkpoints.splice(c, 1);
continue;
}
// Remove off-screen checkpoints
if (checkpoint.x < -200) {
checkpoint.destroy();
checkpoints.splice(c, 1);
}
}
// Check ceiling collision
if (bird.y < 50) {
bird.y = 50;
bird.velocityY = 0;
}
}
// Add checkpoint toggle button click handler
checkpointToggleButton.down = function (x, y, obj) {
// Toggle checkpoint setting
checkpointEnabled = !checkpointEnabled;
storage.checkpointEnabled = checkpointEnabled;
// Update button appearance
checkpointToggleButton.setText(checkpointEnabled ? 'Checkpoint: AÇIK' : 'Checkpoint: KAPALI');
checkpointToggleButton.tint = checkpointEnabled ? 0x00ff00 : 0xff0000;
};
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
}
bird.flap();
};
game.update = function () {
if (!gameStarted) return;
// Create obstacles
if (LK.ticks - lastObstacle > obstacleSpacing) {
createObstacle();
}
// Create checkpoints after every obstacle near hearts
if (obstacleCounter > 0) {
// Only create if we don't already have a checkpoint for this obstacle count
var checkpointExists = false;
for (var cp = 0; cp < checkpoints.length; cp++) {
if (checkpoints[cp].obstacleIndex === obstacleCounter) {
checkpointExists = true;
break;
}
}
if (!checkpointExists) {
createCheckpoint();
}
}
// Create background shapes
if (LK.ticks - lastBackgroundShape > backgroundShapeSpacing) {
createBackgroundShape();
lastBackgroundShape = LK.ticks;
}
// Spikes are now created after obstacles, no random creation needed
// Hearts are now created after each obstacle, so no timer-based spawning needed
// Update bird physics
bird.update();
// Update obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
// Update hearts
for (var h = 0; h < hearts.length; h++) {
hearts[h].update();
}
// Update portals
for (var p = 0; p < portals.length; p++) {
portals[p].update();
}
// Update spikes
for (var s = 0; s < spikes.length; s++) {
spikes[s].update();
}
// Update bombs
for (var b = 0; b < bombs.length; b++) {
bombs[b].update();
}
// Update lasers
for (var l = 0; l < lasers.length; l++) {
lasers[l].update();
}
// Update checkpoints
for (var cp = 0; cp < checkpoints.length; cp++) {
checkpoints[cp].update();
}
// Update background shapes
for (var j = backgroundShapes.length - 1; j >= 0; j--) {
var bgShape = backgroundShapes[j];
bgShape.update();
// Remove off-screen background shapes
if (bgShape.x < -200) {
bgShape.destroy();
backgroundShapes.splice(j, 1);
}
}
// Check collisions
checkCollisions();
// Check individual obstacle passing for score increment
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
// Check if bird passed obstacle and hasn't scored from it yet
if (!obstacle.passed && !obstacle.scoredFrom && obstacle.x + 100 < bird.x) {
obstacle.passed = true;
obstacle.scoredFrom = true; // Mark as scored to prevent duplicate scoring
LK.setScore(LK.getScore() + 1);
storage.levelScore = LK.getScore(); // Save to storage
scoreTxt.setText(LK.getScore());
LK.getSound('score').play();
updateLevelProgress();
}
}
// Create portal when score reaches level threshold (10 + (level-1)*10)
var currentLevelThreshold = levelScoreThreshold + (currentLevel - 1) * 10;
if (LK.getScore() >= currentLevelThreshold && !portalSpawned) {
var portal = new Portal();
portal.x = 2200; // Start off-screen right
portal.y = 1366; // Center of screen vertically
portals.push(portal);
game.addChild(portal);
// Hide all existing obstacles when portal is created
for (var hideIndex = 0; hideIndex < obstacles.length; hideIndex++) {
obstacles[hideIndex].visible = false;
}
// Hide all hearts when portal is created
for (var heartHideIndex = 0; heartHideIndex < hearts.length; heartHideIndex++) {
hearts[heartHideIndex].visible = false;
}
// Hide all spikes when portal is created
for (var spikeHideIndex = 0; spikeHideIndex < spikes.length; spikeHideIndex++) {
spikes[spikeHideIndex].visible = false;
}
// Hide all bombs when portal is created
for (var bombHideIndex = 0; bombHideIndex < bombs.length; bombHideIndex++) {
bombs[bombHideIndex].visible = false;
}
// Hide all lasers when portal is created
for (var laserHideIndex = 0; laserHideIndex < lasers.length; laserHideIndex++) {
lasers[laserHideIndex].visible = false;
}
portalSpawned = true;
}
// No time-based score increment - score only from obstacles
// Level-based difficulty progression
var baseSpacing = 400 - (currentLevel - 1) * 20;
var minSpacing = Math.max(200, 300 - (currentLevel - 1) * 10);
var reductionRate = 1 + currentLevel; // Increase reduction rate with level
var currentSpacing = baseSpacing - LK.getScore() * reductionRate;
obstacleSpacing = Math.max(minSpacing, currentSpacing);
};
function showGameOverWithLevelButton() {
// Check if checkpoint is available
var hasCheckpoint = (storage.lastCheckpointIndex || -1) >= 0;
if (hasCheckpoint) {
// Show checkpoint restore option before resetting
var shouldRestore = restoreFromCheckpoint();
if (shouldRestore) {
return; // Don't show game over if checkpoint was restored
}
}
resetToLevelStart();
LK.showGameOver();
}
kırmızı bir kalp . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
portal. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
bomba. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
minecraft papağan. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
bayrak . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat