/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Bullet class for weapon projectiles var Bullet = Container.expand(function () { var self = Container.call(this); // Create bullet visual (small circle) var bulletAsset = self.attachAsset('manCircle', { anchorX: 0.5, anchorY: 0.5 }); bulletAsset.width = 20; bulletAsset.height = 20; bulletAsset.tint = 0xFFFF00; // Yellow color for bullet self.speed = 12; self.targetX = 0; self.targetY = 0; // Update method for bullet movement towards target self.update = function () { // Move towards target var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Remove bullet if it reaches target or goes off screen if (distance < 30 || self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { if (self.parent) { self.parent.removeChild(self); } var index = bullets.indexOf(self); if (index !== -1) { bullets.splice(index, 1); } } }; return self; }); // Car class var Car = Container.expand(function () { var self = Container.call(this); // Attach car asset (red box, 200x120) var carAsset = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); // Set size for car asset carAsset.width = 200; carAsset.height = 120; // For touch feedback self.flash = function () { tween(self, { alpha: 0.5 }, { duration: 80, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 120 }); } }); }; return self; }); // DeadlyPole class for special scene var DeadlyPole = Container.expand(function () { var self = Container.call(this); // Attach pole asset (red tinted) var poleAsset = self.attachAsset('pole', { anchorX: 0.5, anchorY: 0.5 }); poleAsset.tint = 0xFF0000; // Red tint for deadly poles self.speed = 24; // Falling speed (3x faster) self.isFollowing = false; self.followSpeed = 4; // Speed when following player (reduced for easier gameplay) // Start following the player self.startFollowing = function () { self.isFollowing = true; }; // Stop following and disappear with tween self.stopFollowing = function () { self.isFollowing = false; tween(self, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); }; // Update method for falling motion or following self.update = function () { if (self.isFollowing && car) { // Move towards car position var dx = car.x - self.x; var dy = car.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.followSpeed; self.y += dy / distance * self.followSpeed; } } else if (self.isFalling) { // Falling pole movement self.y += self.speed; } else { self.y += self.speed; } }; return self; }); // GoldenTree class var GoldenTree = Container.expand(function () { var self = Container.call(this); // Attach tree asset (golden, 140x140 - slightly bigger) var treeAsset = self.attachAsset('tree', { anchorX: 0.5, anchorY: 0.5 }); treeAsset.width = 105; treeAsset.height = 105; treeAsset.tint = 0xFFFFFF; // White color for golden trees self.pointValue = 1000; // Golden tree gives 1000 points // No glow effect for golden trees - they stay white // Simple hit animation for golden trees - scale only, no color change self.hitAnim = function () { tween(self, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); }; return self; }); // Pole class var Pole = Container.expand(function () { var self = Container.call(this); // Attach pole asset (thin metal pole, 30x300) var poleAsset = self.attachAsset('pole', { anchorX: 0.5, anchorY: 0.5 }); self.pointValue = -10; // Pole reduces 10 points self.isRepositioning = false; // Flag to track if pole is being repositioned // Animate on hit (shake effect) self.hitAnim = function () { tween(self, { rotation: 0.2 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { rotation: -0.2 }, { duration: 80, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { rotation: 0 }, { duration: 80, easing: tween.easeIn }); } }); } }); }; return self; }); // PurpleTree class var PurpleTree = Container.expand(function () { var self = Container.call(this); // Attach tree asset (purple, 140x140 - slightly bigger) var treeAsset = self.attachAsset('tree', { anchorX: 0.5, anchorY: 0.5 }); treeAsset.width = 140; treeAsset.height = 140; treeAsset.tint = 0x8A2BE2; // Fixed purple color for 5-point trees self.pointValue = 5; // Purple tree gives 5 points // Add purple glow effect self.glowEffect = function () { tween(self, { alpha: 0.6 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { self.glowEffect(); // Loop the glow effect } }); } }); }; // Start glow effect immediately self.glowEffect(); // Special purple hit animation self.hitAnim = function () { tween(self, { scaleX: 1.6, scaleY: 1.6 }, { duration: 180, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 180, easing: tween.easeIn }); } }); }; return self; }); // ThrowingMan class for special scene var ThrowingMan = Container.expand(function () { var self = Container.call(this); // Create circular shape for the man var manAsset = self.attachAsset('manCircle', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Tree class var Tree = Container.expand(function () { var self = Container.call(this); // Attach tree asset (green ellipse, 120x120) var treeAsset = self.attachAsset('tree', { anchorX: 0.5, anchorY: 0.5 }); treeAsset.width = 120; treeAsset.height = 120; treeAsset.tint = 0x228B22; // Fixed green color for 5-point trees self.pointValue = 5; // Normal tree gives 5 points // Animate on hit self.hitAnim = function () { tween(self, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeIn }); } }); }; return self; }); // Weapon class for special scene var Weapon = Container.expand(function () { var self = Container.call(this); // Create weapon visual using weapon image asset var weaponAsset = self.attachAsset('weapon', { anchorX: 0.5, anchorY: 0.5 }); weaponAsset.rotation = Math.PI / 4; // 45 degree rotation return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // --- Game constants --- var CAR_START_X = 2048 / 2; var CAR_START_Y = 2732 - 350; var TREE_MIN_Y = 350; var TREE_MAX_Y = 2732 - 350; var TREE_MIN_X = 150; var TREE_MAX_X = 2048 - 150; var WIN_SCORE = 100000000; // --- Scene Names --- // Birinci Bölüm: Balloon collection phase (normal gameplay with trees and poles) // İkinci Bölüm: Pole avoidance phase (special scene with deadly poles) // --- Game state --- var car = null; var trees = []; var poles = []; var score = 0; var scoreTxt = null; var dragNode = null; var lastIntersecting = []; var lastIntersectingPoles = []; var goldenTreeSpawned = false; var treesHitThisRound = 0; var purpleTreeCounter = 0; var polesSpawnedThisRound = 0; var countdownTimer = 10; var countdownTxt = null; var lastCarX = 0; var lastCarY = 0; var lastScore = 0; var specialScene = false; var specialSceneInitialized = false; var deadlyPoles = []; var lastIntersectingDeadly = []; var throwTimer = 0; var followingStartTime = null; var followingDuration = 10000; // 10 seconds in milliseconds var allFollowingTreesGone = false; // Flag to track if all following trees have disappeared var bottomLeftCounter = 260; var bottomLeftCounterTxt = null; var fourthScene = false; var fourthSceneInitialized = false; var fourthSceneTrees = []; var lastIntersectingFourthTrees = []; // --- Create and add car --- car = new Car(); car.x = CAR_START_X; car.y = CAR_START_Y; game.addChild(car); // --- Create and add trees --- for (var i = 0; i < 3; i++) { var tree = new Tree(); tree.x = getRandomTreeX(); tree.y = getRandomTreeY(); trees.push(tree); lastIntersecting.push(false); game.addChild(tree); } // --- Create and add poles --- for (var i = 0; i < 4; i++) { var pole = new Pole(); var tries = 0; var validPosition = false; do { pole.x = getRandomTreeX(); pole.y = getRandomTreeY(); tries++; validPosition = true; // Check distance from car if (distance(car.x, car.y, pole.x, pole.y) < 300) { validPosition = false; } // Check distance from all existing trees for (var t = 0; t < trees.length; t++) { if (distance(pole.x, pole.y, trees[t].x, trees[t].y) < 200) { validPosition = false; break; } } // Check distance from all existing poles for (var p = 0; p < poles.length; p++) { if (distance(pole.x, pole.y, poles[p].x, poles[p].y) < 200) { validPosition = false; break; } } } while (!validPosition && tries < 10); poles.push(pole); lastIntersectingPoles.push(false); game.addChild(pole); } // --- Score text --- scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Countdown timer text --- countdownTxt = new Text2('10', { size: 100, fill: 0xFF0000 }); countdownTxt.anchor.set(1, 1); LK.gui.bottomRight.addChild(countdownTxt); // --- Bottom-left counter text --- bottomLeftCounterTxt = new Text2('2600', { size: 100, fill: 0x8B4513 }); bottomLeftCounterTxt.anchor.set(0, 1); LK.gui.bottomLeft.addChild(bottomLeftCounterTxt); // --- Winner asset display --- var winnerAsset = LK.getAsset('Winner', { anchorX: 0.5, anchorY: 0.5 }); winnerAsset.x = 150; // Position on left side winnerAsset.y = 400; // Position in middle-left area winnerAsset.lastIntersecting = false; // Flag to track intersection state winnerAsset.lastLifeGainTime = 0; // Track last time life was gained game.addChild(winnerAsset); // --- Lives text --- livesTxt = new Text2('🐱🐱🐱', { size: 100, fill: 0xFF0000 }); livesTxt.anchor.set(0, 0); livesTxt.x = 150; // Positioned away from the top-left menu icon livesTxt.y = 50; LK.gui.addChild(livesTxt); // --- Helper functions --- function getRandomTreeX() { return TREE_MIN_X + Math.floor(Math.random() * (TREE_MAX_X - TREE_MIN_X)); } function getRandomTreeY() { return TREE_MIN_Y + Math.floor(Math.random() * (TREE_MAX_Y - TREE_MIN_Y)); } function updateScoreText() { scoreTxt.setText(score); } function updateCountdownText() { countdownTxt.setText(countdownTimer); } function updateBottomLeftCounterText() { bottomLeftCounterTxt.setText(bottomLeftCounter); } function updateLivesText() { var catText = ''; for (var i = 0; i < lives; i++) { catText += '🐱'; } livesTxt.setText(catText); } function spawnNewTree() { var newTree; // Spawn purple tree every 40 normal trees (1 in 40 chance) purpleTreeCounter++; if (purpleTreeCounter >= 40) { newTree = new PurpleTree(); purpleTreeCounter = 0; // Reset counter } else if (treesHitThisRound % 5 === 0 && !goldenTreeSpawned) { // Spawn golden tree once every 5 hits if not already spawned this round newTree = new GoldenTree(); goldenTreeSpawned = true; } else { newTree = new Tree(); // Reset golden tree availability after 10 hits if (treesHitThisRound >= 10) { goldenTreeSpawned = false; treesHitThisRound = 0; } } // Position new tree randomly (avoid placing too close to car and existing poles) var tries = 0; var validPosition = false; do { newTree.x = getRandomTreeX(); newTree.y = getRandomTreeY(); tries++; validPosition = true; // Check distance from car if (distance(car.x, car.y, newTree.x, newTree.y) < 300) { validPosition = false; } // Check distance from all existing poles for (var p = 0; p < poles.length; p++) { if (distance(newTree.x, newTree.y, poles[p].x, poles[p].y) < 200) { validPosition = false; break; } } // Check distance from all existing trees for (var t = 0; t < trees.length; t++) { if (distance(newTree.x, newTree.y, trees[t].x, trees[t].y) < 200) { validPosition = false; break; } } } while (!validPosition && tries < 10); trees.push(newTree); lastIntersecting.push(false); game.addChild(newTree); // Calculate current round based on score (every 50 points is a new round) var currentRound = Math.floor(score / 50); var polesPerRound = Math.max(1, 5 - currentRound); // Start with 5, decrease by 1 each round, minimum 1 // Spawn poles (decreasing each round) if (polesSpawnedThisRound < polesPerRound) { var newPole = new Pole(); var tries = 0; var validPolePosition = false; do { newPole.x = getRandomTreeX(); newPole.y = getRandomTreeY(); tries++; validPolePosition = true; // Check distance from car if (distance(car.x, car.y, newPole.x, newPole.y) < 300) { validPolePosition = false; } // Check distance from all existing trees for (var t = 0; t < trees.length; t++) { if (distance(newPole.x, newPole.y, trees[t].x, trees[t].y) < 200) { validPolePosition = false; break; } } // Check distance from all existing poles for (var p = 0; p < poles.length; p++) { if (distance(newPole.x, newPole.y, poles[p].x, poles[p].y) < 200) { validPolePosition = false; break; } } } while (!validPolePosition && tries < 10); poles.push(newPole); lastIntersectingPoles.push(false); game.addChild(newPole); polesSpawnedThisRound++; } // Reset pole counter when round is complete if (polesSpawnedThisRound >= polesPerRound && treesHitThisRound >= 10) { polesSpawnedThisRound = 0; } } // --- Move handler for dragging car --- function handleMove(x, y, obj) { if (dragNode) { var newX = x; var newY = Math.max(200, Math.min(2732 - 100, y)); // Screen wrapping for X axis if (newX > 2048) { newX = 0; // Car exits right, appears on left } else if (newX < 0) { newX = 2048; // Car exits left, appears on right } dragNode.x = newX; dragNode.y = newY; } // Check collision with Winner asset for 1 extra life per touch (with 5-second cooldown) if (winnerAsset) { var currentIntersectingWinner = car.intersects(winnerAsset); if (!winnerAsset.lastIntersecting && currentIntersectingWinner) { // Check if 5 seconds have passed since last life gain var currentTime = Date.now(); if (currentTime - winnerAsset.lastLifeGainTime >= 5000) { // Give 1 extra life per touch (maximum 9 lives) if (lives < 9) { lives += 1; updateLivesText(); } winnerAsset.lastLifeGainTime = currentTime; // Update last gain time // Flash winner asset to show it was activated and add scaling animation tween(winnerAsset, { alpha: 0.3, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, onFinish: function onFinish() { tween(winnerAsset, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); } } winnerAsset.lastIntersecting = currentIntersectingWinner; } // Collision detection for all trees for (var i = 0; i < trees.length; i++) { var tree = trees[i]; var currentIntersecting = car.intersects(tree); if (!lastIntersecting[i] && currentIntersecting) { // Car just hit tree score += tree.pointValue; storage.score = score; // Save score to storage treesHitThisRound++; updateScoreText(); // Update bottom-left counter - decrease by total score earned bottomLeftCounter = Math.max(0, 2600 - score); if (bottomLeftCounter <= 0) { bottomLeftCounter = 0; bottomLeftCounterTxt.visible = false; } updateBottomLeftCounterText(); // Animate car and tree car.flash(); tree.hitAnim(); // Remove the hit tree game.removeChild(tree); trees.splice(i, 1); lastIntersecting.splice(i, 1); i--; // Adjust index since we removed an element // Spawn new tree (normal or golden based on conditions) spawnNewTree(); // Win condition at 2600 points with HAPPY BIRTHDAY message if (score >= 2600) { // Create background HAPPY BIRTHDAY text - much larger and screen-filling var backgroundText = new Text2('HAPPY BIRTHDAY', { size: 240, fill: 0xFFD700, // Golden color alpha: 0.4 // Semi-transparent background }); backgroundText.anchor.set(0.5, 0.5); backgroundText.x = 2048 / 2; backgroundText.y = 2732 / 2; backgroundText.rotation = -0.3; // Slight rotation for background effect game.addChild(backgroundText); // Show winner screen after a brief delay LK.setTimeout(function () { LK.showYouWin(); }, 500); } // Original high score win condition if (score >= WIN_SCORE) { LK.showYouWin(); } } if (i >= 0 && i < lastIntersecting.length) { lastIntersecting[i] = currentIntersecting; } } // Collision detection for all poles (reduce 1 life when touched) for (var j = 0; j < poles.length; j++) { var pole = poles[j]; var currentIntersectingPole = car.intersects(pole); if (!lastIntersectingPoles[j] && currentIntersectingPole) { // Check if 2 seconds have passed since last pole hit var currentTime = Date.now(); if (currentTime - lastPoleHitTime >= 2000) { // Reduce 1 life when touching pole lives--; updateLivesText(); lastPoleHitTime = currentTime; // Update last hit time // Flash car to show damage taken car.flash(); // Animate pole on hit pole.hitAnim(); // Check if player is out of lives if (lives <= 0) { LK.showGameOver(); return; } } } if (j >= 0 && j < lastIntersectingPoles.length) { lastIntersectingPoles[j] = currentIntersectingPole; } } } // --- Utility: distance between two points --- function distance(x1, y1, x2, y2) { var dx = x1 - x2; var dy = y1 - y2; return Math.sqrt(dx * dx + dy * dy); } // --- Touch/drag events --- game.down = function (x, y, obj) { // Only start drag if touch is on car (within 120px radius) var dx = x - car.x; var dy = y - car.y; if (dx * dx + dy * dy < 120 * 120) { dragNode = car; handleMove(x, y, obj); } }; game.move = handleMove; game.up = function (x, y, obj) { dragNode = null; }; // --- Game update loop (not needed for movement, but for future extensibility) --- game.update = function () { // Check for special scene transition when reaching 2600 points if (!specialScene && bottomLeftCounter <= 0) { specialScene = true; specialSceneInitialized = false; } // Initialize special scene (İkinci Bölüm: Pole avoidance phase) if (specialScene && !specialSceneInitialized) { // Clear existing trees and poles for (var t = trees.length - 1; t >= 0; t--) { game.removeChild(trees[t]); } trees = []; lastIntersecting = []; for (var p = poles.length - 1; p >= 0; p--) { game.removeChild(poles[p]); } poles = []; lastIntersectingPoles = []; // Hide countdown timer and bottom-left counter in special scene countdownTxt.visible = false; bottomLeftCounterTxt.visible = false; // Create deadly poles that fall from top for (var dp = 0; dp < 5; dp++) { var deadlyPole = new DeadlyPole(); deadlyPole.x = Math.random() * (2048 - 100) + 50; deadlyPole.y = -200 - Math.random() * 400; // Start above screen deadlyPoles.push(deadlyPole); lastIntersectingDeadly.push(false); game.addChild(deadlyPole); } // Start following behavior after 3 seconds followingStartTime = Date.now() + 3000; specialSceneInitialized = true; } // Special scene logic (İkinci Bölüm) if (specialScene) { // Handle deadly pole behavior for (var dp = deadlyPoles.length - 1; dp >= 0; dp--) { var deadlyPole = deadlyPoles[dp]; // Start following after delay if (followingStartTime && Date.now() >= followingStartTime && !deadlyPole.isFollowing) { deadlyPole.startFollowing(); } // Check if following duration is over if (deadlyPole.isFollowing && followingStartTime && Date.now() >= followingStartTime + followingDuration) { deadlyPole.stopFollowing(); deadlyPoles.splice(dp, 1); lastIntersectingDeadly.splice(dp, 1); continue; } // Remove if off screen (bottom) if (deadlyPole.y > 2832) { game.removeChild(deadlyPole); deadlyPoles.splice(dp, 1); lastIntersectingDeadly.splice(dp, 1); continue; } // Collision detection with car var currentIntersectingDeadly = car.intersects(deadlyPole); if (!lastIntersectingDeadly[dp] && currentIntersectingDeadly) { // Check 2-second cooldown var currentTime = Date.now(); if (currentTime - lastDeadlyPoleHitTime >= 2000) { lives--; updateLivesText(); lastDeadlyPoleHitTime = currentTime; car.flash(); if (lives <= 0) { LK.showGameOver(); return; } } } if (dp < lastIntersectingDeadly.length) { lastIntersectingDeadly[dp] = currentIntersectingDeadly; } } // Check if all deadly poles are gone if (deadlyPoles.length === 0 && followingStartTime && Date.now() >= followingStartTime + followingDuration) { // Special scene complete - show win LK.showYouWin(); return; } // Skip normal game logic during special scene return; } // Only run countdown timer logic if not in special scene if (!specialScene) { // Check if player hit something (score changed) var scoreChanged = score !== lastScore; if (scoreChanged) { // Player hit something, reset countdown countdownTimer = 10; updateCountdownText(); } else { // Player didn't hit anything, countdown every second (60 ticks = 1 second) if (LK.ticks % 60 === 0) { countdownTimer--; updateCountdownText(); if (countdownTimer <= 0) { if (score < 10) { // Show F- grade for low score var gradeText = new Text2('F-', { size: 300, fill: 0xFF0000 }); gradeText.anchor.set(0.5, 0.5); gradeText.x = 2048 / 2; gradeText.y = 2732 / 2; game.addChild(gradeText); // Show grade for 2 seconds before game over LK.setTimeout(function () { LK.showGameOver(); }, 2000); } else if (score >= 10 && score <= 30) { // Show F+ grade for score between 10 and 30 var gradeText = new Text2('F+', { size: 300, fill: 0xFFA500 }); gradeText.anchor.set(0.5, 0.5); gradeText.x = 2048 / 2; gradeText.y = 2732 / 2; game.addChild(gradeText); // Show grade for 2 seconds before game over LK.setTimeout(function () { LK.showGameOver(); }, 2000); } else if (score >= 30 && score <= 50) { // Show D- grade for score between 30 and 50 var gradeText = new Text2('D-', { size: 300, fill: 0x800080 }); gradeText.anchor.set(0.5, 0.5); gradeText.x = 2048 / 2; gradeText.y = 2732 / 2; game.addChild(gradeText); // Show grade for 2 seconds before game over LK.setTimeout(function () { LK.showGameOver(); }, 2000); } else { LK.showGameOver(); } } } } } if (fourthScene && !fourthSceneInitialized) { // Initialize 6 monsters for fourth scene for (var m = 0; m < 6; m++) { var monster = new ThrowingMan(); monster.x = Math.random() * (2048 - 200) + 100; monster.y = Math.random() * (2732 - 400) + 200; monster.health = 8; // Each monster needs 8 shots (reduced from 10) monster.maxHealth = 8; fourthSceneTrees.push(monster); lastIntersectingFourthTrees.push(false); game.addChild(monster); } fourthSceneInitialized = true; } if (fourthScene && fourthSceneTrees.length > 0) { // Check bullet collisions with monsters for (var b = bullets.length - 1; b >= 0; b--) { var bullet = bullets[b]; for (var m = 0; m < fourthSceneTrees.length; m++) { var monster = fourthSceneTrees[m]; if (bullet.intersects && bullet.intersects(monster)) { // Remove bullet game.removeChild(bullet); bullets.splice(b, 1); // Reduce monster health monster.health--; // Flash monster red when hit tween(monster, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(monster, { tint: 0xFFFFFF }, { duration: 200 }); } }); // Check if monster is defeated if (monster.health <= 0) { game.removeChild(monster); fourthSceneTrees.splice(m, 1); lastIntersectingFourthTrees.splice(m, 1); // Check if all monsters are defeated if (fourthSceneTrees.length === 0) { // Clear all remaining bullets for (var clearB = bullets.length - 1; clearB >= 0; clearB--) { game.removeChild(bullets[clearB]); } bullets = []; // Show HAPPY BIRTHDAY message var birthdayText = new Text2('HAPPY BIRTHDAY', { size: 200, fill: 0xFFD700 // Golden color }); birthdayText.anchor.set(0.5, 0.5); birthdayText.x = 2048 / 2; birthdayText.y = 2732 / 2; game.addChild(birthdayText); // Show message for 3 seconds before showing you win LK.setTimeout(function () { LK.showYouWin(); }, 3000); return; } } break; // Exit monster loop since bullet hit one } } } // Check collisions with car (reduce lives instead of instant death) for (var ft = fourthSceneTrees.length - 1; ft >= 0; ft--) { var monster = fourthSceneTrees[ft]; var currentIntersectingFourth = car.intersects(monster); if (!lastIntersectingFourthTrees[ft] && currentIntersectingFourth) { // Reduce car lives lives--; updateLivesText(); car.flash(); // Check if car is out of lives if (lives <= 0) { LK.showGameOver(); return; } } if (ft < lastIntersectingFourthTrees.length) { lastIntersectingFourthTrees[ft] = currentIntersectingFourth; } } } // Handle candle repositioning every 4 seconds (only in normal game, not special scenes) if (!specialScene && !fourthScene) { candleRepositionTimer++; if (candleRepositionTimer >= repositionInterval) { // Reposition all trees (candles) using tween animation for (var t = 0; t < trees.length; t++) { var tree = trees[t]; var newX = getRandomTreeX(); var newY = getRandomTreeY(); // Use tween to smoothly move trees to new positions tween(tree, { x: newX, y: newY }, { duration: 1000, easing: tween.easeInOut }); } candleRepositionTimer = 0; // Reset timer } // Handle pole repositioning every 5 seconds (only in normal game, not special scenes) poleRepositionTimer++; if (poleRepositionTimer >= poleRepositionInterval) { // Reposition all poles using tween animation for (var p = 0; p < poles.length; p++) { var pole = poles[p]; var newX = getRandomTreeX(); var newY = getRandomTreeY(); // Set repositioning flag before starting movement pole.isRepositioning = true; // Use tween to smoothly move poles to new positions tween(pole, { x: newX, y: newY }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Clear repositioning flag when movement completes pole.isRepositioning = false; } }); } poleRepositionTimer = 0; // Reset timer } } // Handle winner asset repositioning every 7 seconds (only in normal game, not special scenes) if (!specialScene && !fourthScene) { winnerRepositionTimer++; if (winnerRepositionTimer >= winnerRepositionInterval) { // Reposition winner asset using tween animation var newX = Math.random() * (2048 - 150) + 75; // Random X within game bounds var newY = Math.random() * (2732 - 300) + 150; // Random Y within game bounds // Use tween to smoothly move winner asset to new position tween(winnerAsset, { x: newX, y: newY }, { duration: 1000, easing: tween.easeInOut }); winnerRepositionTimer = 0; // Reset timer } } // Handle falling poles system (only in normal game, not special scenes) if (!specialScene && !fourthScene) { // Spawn new falling poles with dynamic intervals if (LK.ticks - lastFallingPoleSpawn >= fallingPoleInterval) { // Create two poles with a gap in the middle for character var gapCenterX = Math.random() * (2048 - characterGapSize - 200) + 100 + characterGapSize / 2; // Left pole var leftPole = new Pole(); leftPole.x = Math.random() * (gapCenterX - characterGapSize / 2 - 50) + 25; leftPole.y = -150; leftPole.speed = 8; // Falling speed leftPole.isFalling = true; fallingPoles.push(leftPole); game.addChild(leftPole); // Right pole var rightPole = new Pole(); rightPole.x = Math.random() * (2048 - (gapCenterX + characterGapSize / 2) - 50) + (gapCenterX + characterGapSize / 2) + 25; rightPole.y = -150; rightPole.speed = 8; // Falling speed rightPole.isFalling = true; fallingPoles.push(rightPole); game.addChild(rightPole); lastFallingPoleSpawn = LK.ticks; // Progress to next interval if available if (currentIntervalIndex < fallingPoleIntervals.length - 1) { currentIntervalIndex++; fallingPoleInterval = fallingPoleIntervals[currentIntervalIndex]; } } // Update falling poles for (var fp = fallingPoles.length - 1; fp >= 0; fp--) { var fallingPole = fallingPoles[fp]; fallingPole.y += fallingPole.speed; // Remove poles that have fallen off screen if (fallingPole.y > 2832 + 150) { game.removeChild(fallingPole); fallingPoles.splice(fp, 1); continue; } // Check collision with car if (car.intersects(fallingPole)) { var currentTime = Date.now(); if (currentTime - lastPoleHitTime >= 2000) { lives--; updateLivesText(); lastPoleHitTime = currentTime; car.flash(); fallingPole.hitAnim(); if (lives <= 0) { LK.showGameOver(); return; } } } } } // Update tracking variables lastCarX = car.x; lastCarY = car.y; lastScore = score; }; // --- Initialize score --- score = 0; // Always start from 0 points updateScoreText(); // --- Initialize bottom-left counter --- bottomLeftCounter = 2600; updateBottomLeftCounterText(); // --- Initialize lives display --- lives = 3; updateLivesText(); // --- Initialize tracking variables --- lastCarX = car.x; lastCarY = car.y; lastScore = score; // --- Initialize to always start from first section (Birinci Bölüm) --- specialScene = false; // Start in first section, transition at 2600 points specialSceneInitialized = false; followingStartTime = null; allFollowingTreesGone = false; // Initialize bullets array for special scene var bullets = []; // --- Falling poles system --- var fallingPoles = []; var lastFallingPoleSpawn = 0; var fallingPoleIntervals = [300, 240, 180, 120, 60]; // 5, 4, 3, 2, 1 seconds at 60 FPS var currentIntervalIndex = 0; var fallingPoleInterval = fallingPoleIntervals[0]; // Start with 5 seconds var characterGapSize = 250; // Gap size for character to pass through // --- Candle repositioning timer --- var candleRepositionTimer = 0; var repositionInterval = 240; // 4 seconds at 60 FPS (4 * 60 = 240 ticks) // --- Pole repositioning timer --- var poleRepositionTimer = 0; var poleRepositionInterval = 600; // 10 seconds at 60 FPS (10 * 60 = 600 ticks) var poleCollisionCount = 0; // Track pole collisions for game over var lives = 3; // Character has 3 lives var lastPoleHitTime = 0; // Track last time player was hit by pole for 2-second cooldown var lastDeadlyPoleHitTime = 0; // Track last time player was hit by deadly pole for 2-second cooldown // --- Winner asset repositioning timer --- var winnerRepositionTimer = 0; var winnerRepositionInterval = 420; // 7 seconds at 60 FPS (7 * 60 = 420 ticks);;;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bullet class for weapon projectiles
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Create bullet visual (small circle)
var bulletAsset = self.attachAsset('manCircle', {
anchorX: 0.5,
anchorY: 0.5
});
bulletAsset.width = 20;
bulletAsset.height = 20;
bulletAsset.tint = 0xFFFF00; // Yellow color for bullet
self.speed = 12;
self.targetX = 0;
self.targetY = 0;
// Update method for bullet movement towards target
self.update = function () {
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Remove bullet if it reaches target or goes off screen
if (distance < 30 || self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
if (self.parent) {
self.parent.removeChild(self);
}
var index = bullets.indexOf(self);
if (index !== -1) {
bullets.splice(index, 1);
}
}
};
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
// Attach car asset (red box, 200x120)
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Set size for car asset
carAsset.width = 200;
carAsset.height = 120;
// For touch feedback
self.flash = function () {
tween(self, {
alpha: 0.5
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
// DeadlyPole class for special scene
var DeadlyPole = Container.expand(function () {
var self = Container.call(this);
// Attach pole asset (red tinted)
var poleAsset = self.attachAsset('pole', {
anchorX: 0.5,
anchorY: 0.5
});
poleAsset.tint = 0xFF0000; // Red tint for deadly poles
self.speed = 24; // Falling speed (3x faster)
self.isFollowing = false;
self.followSpeed = 4; // Speed when following player (reduced for easier gameplay)
// Start following the player
self.startFollowing = function () {
self.isFollowing = true;
};
// Stop following and disappear with tween
self.stopFollowing = function () {
self.isFollowing = false;
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
// Update method for falling motion or following
self.update = function () {
if (self.isFollowing && car) {
// Move towards car position
var dx = car.x - self.x;
var dy = car.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.followSpeed;
self.y += dy / distance * self.followSpeed;
}
} else if (self.isFalling) {
// Falling pole movement
self.y += self.speed;
} else {
self.y += self.speed;
}
};
return self;
});
// GoldenTree class
var GoldenTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (golden, 140x140 - slightly bigger)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 105;
treeAsset.height = 105;
treeAsset.tint = 0xFFFFFF; // White color for golden trees
self.pointValue = 1000; // Golden tree gives 1000 points
// No glow effect for golden trees - they stay white
// Simple hit animation for golden trees - scale only, no color change
self.hitAnim = function () {
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
};
return self;
});
// Pole class
var Pole = Container.expand(function () {
var self = Container.call(this);
// Attach pole asset (thin metal pole, 30x300)
var poleAsset = self.attachAsset('pole', {
anchorX: 0.5,
anchorY: 0.5
});
self.pointValue = -10; // Pole reduces 10 points
self.isRepositioning = false; // Flag to track if pole is being repositioned
// Animate on hit (shake effect)
self.hitAnim = function () {
tween(self, {
rotation: 0.2
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotation: -0.2
}, {
duration: 80,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
rotation: 0
}, {
duration: 80,
easing: tween.easeIn
});
}
});
}
});
};
return self;
});
// PurpleTree class
var PurpleTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (purple, 140x140 - slightly bigger)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 140;
treeAsset.height = 140;
treeAsset.tint = 0x8A2BE2; // Fixed purple color for 5-point trees
self.pointValue = 5; // Purple tree gives 5 points
// Add purple glow effect
self.glowEffect = function () {
tween(self, {
alpha: 0.6
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.glowEffect(); // Loop the glow effect
}
});
}
});
};
// Start glow effect immediately
self.glowEffect();
// Special purple hit animation
self.hitAnim = function () {
tween(self, {
scaleX: 1.6,
scaleY: 1.6
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 180,
easing: tween.easeIn
});
}
});
};
return self;
});
// ThrowingMan class for special scene
var ThrowingMan = Container.expand(function () {
var self = Container.call(this);
// Create circular shape for the man
var manAsset = self.attachAsset('manCircle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Tree class
var Tree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (green ellipse, 120x120)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 120;
treeAsset.height = 120;
treeAsset.tint = 0x228B22; // Fixed green color for 5-point trees
self.pointValue = 5; // Normal tree gives 5 points
// Animate on hit
self.hitAnim = function () {
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeIn
});
}
});
};
return self;
});
// Weapon class for special scene
var Weapon = Container.expand(function () {
var self = Container.call(this);
// Create weapon visual using weapon image asset
var weaponAsset = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
});
weaponAsset.rotation = Math.PI / 4; // 45 degree rotation
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// --- Game constants ---
var CAR_START_X = 2048 / 2;
var CAR_START_Y = 2732 - 350;
var TREE_MIN_Y = 350;
var TREE_MAX_Y = 2732 - 350;
var TREE_MIN_X = 150;
var TREE_MAX_X = 2048 - 150;
var WIN_SCORE = 100000000;
// --- Scene Names ---
// Birinci Bölüm: Balloon collection phase (normal gameplay with trees and poles)
// İkinci Bölüm: Pole avoidance phase (special scene with deadly poles)
// --- Game state ---
var car = null;
var trees = [];
var poles = [];
var score = 0;
var scoreTxt = null;
var dragNode = null;
var lastIntersecting = [];
var lastIntersectingPoles = [];
var goldenTreeSpawned = false;
var treesHitThisRound = 0;
var purpleTreeCounter = 0;
var polesSpawnedThisRound = 0;
var countdownTimer = 10;
var countdownTxt = null;
var lastCarX = 0;
var lastCarY = 0;
var lastScore = 0;
var specialScene = false;
var specialSceneInitialized = false;
var deadlyPoles = [];
var lastIntersectingDeadly = [];
var throwTimer = 0;
var followingStartTime = null;
var followingDuration = 10000; // 10 seconds in milliseconds
var allFollowingTreesGone = false; // Flag to track if all following trees have disappeared
var bottomLeftCounter = 260;
var bottomLeftCounterTxt = null;
var fourthScene = false;
var fourthSceneInitialized = false;
var fourthSceneTrees = [];
var lastIntersectingFourthTrees = [];
// --- Create and add car ---
car = new Car();
car.x = CAR_START_X;
car.y = CAR_START_Y;
game.addChild(car);
// --- Create and add trees ---
for (var i = 0; i < 3; i++) {
var tree = new Tree();
tree.x = getRandomTreeX();
tree.y = getRandomTreeY();
trees.push(tree);
lastIntersecting.push(false);
game.addChild(tree);
}
// --- Create and add poles ---
for (var i = 0; i < 4; i++) {
var pole = new Pole();
var tries = 0;
var validPosition = false;
do {
pole.x = getRandomTreeX();
pole.y = getRandomTreeY();
tries++;
validPosition = true;
// Check distance from car
if (distance(car.x, car.y, pole.x, pole.y) < 300) {
validPosition = false;
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(pole.x, pole.y, trees[t].x, trees[t].y) < 200) {
validPosition = false;
break;
}
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(pole.x, pole.y, poles[p].x, poles[p].y) < 200) {
validPosition = false;
break;
}
}
} while (!validPosition && tries < 10);
poles.push(pole);
lastIntersectingPoles.push(false);
game.addChild(pole);
}
// --- Score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Countdown timer text ---
countdownTxt = new Text2('10', {
size: 100,
fill: 0xFF0000
});
countdownTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(countdownTxt);
// --- Bottom-left counter text ---
bottomLeftCounterTxt = new Text2('2600', {
size: 100,
fill: 0x8B4513
});
bottomLeftCounterTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(bottomLeftCounterTxt);
// --- Winner asset display ---
var winnerAsset = LK.getAsset('Winner', {
anchorX: 0.5,
anchorY: 0.5
});
winnerAsset.x = 150; // Position on left side
winnerAsset.y = 400; // Position in middle-left area
winnerAsset.lastIntersecting = false; // Flag to track intersection state
winnerAsset.lastLifeGainTime = 0; // Track last time life was gained
game.addChild(winnerAsset);
// --- Lives text ---
livesTxt = new Text2('🐱🐱🐱', {
size: 100,
fill: 0xFF0000
});
livesTxt.anchor.set(0, 0);
livesTxt.x = 150; // Positioned away from the top-left menu icon
livesTxt.y = 50;
LK.gui.addChild(livesTxt);
// --- Helper functions ---
function getRandomTreeX() {
return TREE_MIN_X + Math.floor(Math.random() * (TREE_MAX_X - TREE_MIN_X));
}
function getRandomTreeY() {
return TREE_MIN_Y + Math.floor(Math.random() * (TREE_MAX_Y - TREE_MIN_Y));
}
function updateScoreText() {
scoreTxt.setText(score);
}
function updateCountdownText() {
countdownTxt.setText(countdownTimer);
}
function updateBottomLeftCounterText() {
bottomLeftCounterTxt.setText(bottomLeftCounter);
}
function updateLivesText() {
var catText = '';
for (var i = 0; i < lives; i++) {
catText += '🐱';
}
livesTxt.setText(catText);
}
function spawnNewTree() {
var newTree;
// Spawn purple tree every 40 normal trees (1 in 40 chance)
purpleTreeCounter++;
if (purpleTreeCounter >= 40) {
newTree = new PurpleTree();
purpleTreeCounter = 0; // Reset counter
} else if (treesHitThisRound % 5 === 0 && !goldenTreeSpawned) {
// Spawn golden tree once every 5 hits if not already spawned this round
newTree = new GoldenTree();
goldenTreeSpawned = true;
} else {
newTree = new Tree();
// Reset golden tree availability after 10 hits
if (treesHitThisRound >= 10) {
goldenTreeSpawned = false;
treesHitThisRound = 0;
}
}
// Position new tree randomly (avoid placing too close to car and existing poles)
var tries = 0;
var validPosition = false;
do {
newTree.x = getRandomTreeX();
newTree.y = getRandomTreeY();
tries++;
validPosition = true;
// Check distance from car
if (distance(car.x, car.y, newTree.x, newTree.y) < 300) {
validPosition = false;
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(newTree.x, newTree.y, poles[p].x, poles[p].y) < 200) {
validPosition = false;
break;
}
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(newTree.x, newTree.y, trees[t].x, trees[t].y) < 200) {
validPosition = false;
break;
}
}
} while (!validPosition && tries < 10);
trees.push(newTree);
lastIntersecting.push(false);
game.addChild(newTree);
// Calculate current round based on score (every 50 points is a new round)
var currentRound = Math.floor(score / 50);
var polesPerRound = Math.max(1, 5 - currentRound); // Start with 5, decrease by 1 each round, minimum 1
// Spawn poles (decreasing each round)
if (polesSpawnedThisRound < polesPerRound) {
var newPole = new Pole();
var tries = 0;
var validPolePosition = false;
do {
newPole.x = getRandomTreeX();
newPole.y = getRandomTreeY();
tries++;
validPolePosition = true;
// Check distance from car
if (distance(car.x, car.y, newPole.x, newPole.y) < 300) {
validPolePosition = false;
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(newPole.x, newPole.y, trees[t].x, trees[t].y) < 200) {
validPolePosition = false;
break;
}
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(newPole.x, newPole.y, poles[p].x, poles[p].y) < 200) {
validPolePosition = false;
break;
}
}
} while (!validPolePosition && tries < 10);
poles.push(newPole);
lastIntersectingPoles.push(false);
game.addChild(newPole);
polesSpawnedThisRound++;
}
// Reset pole counter when round is complete
if (polesSpawnedThisRound >= polesPerRound && treesHitThisRound >= 10) {
polesSpawnedThisRound = 0;
}
}
// --- Move handler for dragging car ---
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = Math.max(200, Math.min(2732 - 100, y));
// Screen wrapping for X axis
if (newX > 2048) {
newX = 0; // Car exits right, appears on left
} else if (newX < 0) {
newX = 2048; // Car exits left, appears on right
}
dragNode.x = newX;
dragNode.y = newY;
}
// Check collision with Winner asset for 1 extra life per touch (with 5-second cooldown)
if (winnerAsset) {
var currentIntersectingWinner = car.intersects(winnerAsset);
if (!winnerAsset.lastIntersecting && currentIntersectingWinner) {
// Check if 5 seconds have passed since last life gain
var currentTime = Date.now();
if (currentTime - winnerAsset.lastLifeGainTime >= 5000) {
// Give 1 extra life per touch (maximum 9 lives)
if (lives < 9) {
lives += 1;
updateLivesText();
}
winnerAsset.lastLifeGainTime = currentTime; // Update last gain time
// Flash winner asset to show it was activated and add scaling animation
tween(winnerAsset, {
alpha: 0.3,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(winnerAsset, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
}
winnerAsset.lastIntersecting = currentIntersectingWinner;
}
// Collision detection for all trees
for (var i = 0; i < trees.length; i++) {
var tree = trees[i];
var currentIntersecting = car.intersects(tree);
if (!lastIntersecting[i] && currentIntersecting) {
// Car just hit tree
score += tree.pointValue;
storage.score = score; // Save score to storage
treesHitThisRound++;
updateScoreText();
// Update bottom-left counter - decrease by total score earned
bottomLeftCounter = Math.max(0, 2600 - score);
if (bottomLeftCounter <= 0) {
bottomLeftCounter = 0;
bottomLeftCounterTxt.visible = false;
}
updateBottomLeftCounterText();
// Animate car and tree
car.flash();
tree.hitAnim();
// Remove the hit tree
game.removeChild(tree);
trees.splice(i, 1);
lastIntersecting.splice(i, 1);
i--; // Adjust index since we removed an element
// Spawn new tree (normal or golden based on conditions)
spawnNewTree();
// Win condition at 2600 points with HAPPY BIRTHDAY message
if (score >= 2600) {
// Create background HAPPY BIRTHDAY text - much larger and screen-filling
var backgroundText = new Text2('HAPPY BIRTHDAY', {
size: 240,
fill: 0xFFD700,
// Golden color
alpha: 0.4 // Semi-transparent background
});
backgroundText.anchor.set(0.5, 0.5);
backgroundText.x = 2048 / 2;
backgroundText.y = 2732 / 2;
backgroundText.rotation = -0.3; // Slight rotation for background effect
game.addChild(backgroundText);
// Show winner screen after a brief delay
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
}
// Original high score win condition
if (score >= WIN_SCORE) {
LK.showYouWin();
}
}
if (i >= 0 && i < lastIntersecting.length) {
lastIntersecting[i] = currentIntersecting;
}
}
// Collision detection for all poles (reduce 1 life when touched)
for (var j = 0; j < poles.length; j++) {
var pole = poles[j];
var currentIntersectingPole = car.intersects(pole);
if (!lastIntersectingPoles[j] && currentIntersectingPole) {
// Check if 2 seconds have passed since last pole hit
var currentTime = Date.now();
if (currentTime - lastPoleHitTime >= 2000) {
// Reduce 1 life when touching pole
lives--;
updateLivesText();
lastPoleHitTime = currentTime; // Update last hit time
// Flash car to show damage taken
car.flash();
// Animate pole on hit
pole.hitAnim();
// Check if player is out of lives
if (lives <= 0) {
LK.showGameOver();
return;
}
}
}
if (j >= 0 && j < lastIntersectingPoles.length) {
lastIntersectingPoles[j] = currentIntersectingPole;
}
}
}
// --- Utility: distance between two points ---
function distance(x1, y1, x2, y2) {
var dx = x1 - x2;
var dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
// --- Touch/drag events ---
game.down = function (x, y, obj) {
// Only start drag if touch is on car (within 120px radius)
var dx = x - car.x;
var dy = y - car.y;
if (dx * dx + dy * dy < 120 * 120) {
dragNode = car;
handleMove(x, y, obj);
}
};
game.move = handleMove;
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Game update loop (not needed for movement, but for future extensibility) ---
game.update = function () {
// Check for special scene transition when reaching 2600 points
if (!specialScene && bottomLeftCounter <= 0) {
specialScene = true;
specialSceneInitialized = false;
}
// Initialize special scene (İkinci Bölüm: Pole avoidance phase)
if (specialScene && !specialSceneInitialized) {
// Clear existing trees and poles
for (var t = trees.length - 1; t >= 0; t--) {
game.removeChild(trees[t]);
}
trees = [];
lastIntersecting = [];
for (var p = poles.length - 1; p >= 0; p--) {
game.removeChild(poles[p]);
}
poles = [];
lastIntersectingPoles = [];
// Hide countdown timer and bottom-left counter in special scene
countdownTxt.visible = false;
bottomLeftCounterTxt.visible = false;
// Create deadly poles that fall from top
for (var dp = 0; dp < 5; dp++) {
var deadlyPole = new DeadlyPole();
deadlyPole.x = Math.random() * (2048 - 100) + 50;
deadlyPole.y = -200 - Math.random() * 400; // Start above screen
deadlyPoles.push(deadlyPole);
lastIntersectingDeadly.push(false);
game.addChild(deadlyPole);
}
// Start following behavior after 3 seconds
followingStartTime = Date.now() + 3000;
specialSceneInitialized = true;
}
// Special scene logic (İkinci Bölüm)
if (specialScene) {
// Handle deadly pole behavior
for (var dp = deadlyPoles.length - 1; dp >= 0; dp--) {
var deadlyPole = deadlyPoles[dp];
// Start following after delay
if (followingStartTime && Date.now() >= followingStartTime && !deadlyPole.isFollowing) {
deadlyPole.startFollowing();
}
// Check if following duration is over
if (deadlyPole.isFollowing && followingStartTime && Date.now() >= followingStartTime + followingDuration) {
deadlyPole.stopFollowing();
deadlyPoles.splice(dp, 1);
lastIntersectingDeadly.splice(dp, 1);
continue;
}
// Remove if off screen (bottom)
if (deadlyPole.y > 2832) {
game.removeChild(deadlyPole);
deadlyPoles.splice(dp, 1);
lastIntersectingDeadly.splice(dp, 1);
continue;
}
// Collision detection with car
var currentIntersectingDeadly = car.intersects(deadlyPole);
if (!lastIntersectingDeadly[dp] && currentIntersectingDeadly) {
// Check 2-second cooldown
var currentTime = Date.now();
if (currentTime - lastDeadlyPoleHitTime >= 2000) {
lives--;
updateLivesText();
lastDeadlyPoleHitTime = currentTime;
car.flash();
if (lives <= 0) {
LK.showGameOver();
return;
}
}
}
if (dp < lastIntersectingDeadly.length) {
lastIntersectingDeadly[dp] = currentIntersectingDeadly;
}
}
// Check if all deadly poles are gone
if (deadlyPoles.length === 0 && followingStartTime && Date.now() >= followingStartTime + followingDuration) {
// Special scene complete - show win
LK.showYouWin();
return;
}
// Skip normal game logic during special scene
return;
}
// Only run countdown timer logic if not in special scene
if (!specialScene) {
// Check if player hit something (score changed)
var scoreChanged = score !== lastScore;
if (scoreChanged) {
// Player hit something, reset countdown
countdownTimer = 10;
updateCountdownText();
} else {
// Player didn't hit anything, countdown every second (60 ticks = 1 second)
if (LK.ticks % 60 === 0) {
countdownTimer--;
updateCountdownText();
if (countdownTimer <= 0) {
if (score < 10) {
// Show F- grade for low score
var gradeText = new Text2('F-', {
size: 300,
fill: 0xFF0000
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 10 && score <= 30) {
// Show F+ grade for score between 10 and 30
var gradeText = new Text2('F+', {
size: 300,
fill: 0xFFA500
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 30 && score <= 50) {
// Show D- grade for score between 30 and 50
var gradeText = new Text2('D-', {
size: 300,
fill: 0x800080
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
LK.showGameOver();
}
}
}
}
}
if (fourthScene && !fourthSceneInitialized) {
// Initialize 6 monsters for fourth scene
for (var m = 0; m < 6; m++) {
var monster = new ThrowingMan();
monster.x = Math.random() * (2048 - 200) + 100;
monster.y = Math.random() * (2732 - 400) + 200;
monster.health = 8; // Each monster needs 8 shots (reduced from 10)
monster.maxHealth = 8;
fourthSceneTrees.push(monster);
lastIntersectingFourthTrees.push(false);
game.addChild(monster);
}
fourthSceneInitialized = true;
}
if (fourthScene && fourthSceneTrees.length > 0) {
// Check bullet collisions with monsters
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
for (var m = 0; m < fourthSceneTrees.length; m++) {
var monster = fourthSceneTrees[m];
if (bullet.intersects && bullet.intersects(monster)) {
// Remove bullet
game.removeChild(bullet);
bullets.splice(b, 1);
// Reduce monster health
monster.health--;
// Flash monster red when hit
tween(monster, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(monster, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
// Check if monster is defeated
if (monster.health <= 0) {
game.removeChild(monster);
fourthSceneTrees.splice(m, 1);
lastIntersectingFourthTrees.splice(m, 1);
// Check if all monsters are defeated
if (fourthSceneTrees.length === 0) {
// Clear all remaining bullets
for (var clearB = bullets.length - 1; clearB >= 0; clearB--) {
game.removeChild(bullets[clearB]);
}
bullets = [];
// Show HAPPY BIRTHDAY message
var birthdayText = new Text2('HAPPY BIRTHDAY', {
size: 200,
fill: 0xFFD700 // Golden color
});
birthdayText.anchor.set(0.5, 0.5);
birthdayText.x = 2048 / 2;
birthdayText.y = 2732 / 2;
game.addChild(birthdayText);
// Show message for 3 seconds before showing you win
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
return;
}
}
break; // Exit monster loop since bullet hit one
}
}
}
// Check collisions with car (reduce lives instead of instant death)
for (var ft = fourthSceneTrees.length - 1; ft >= 0; ft--) {
var monster = fourthSceneTrees[ft];
var currentIntersectingFourth = car.intersects(monster);
if (!lastIntersectingFourthTrees[ft] && currentIntersectingFourth) {
// Reduce car lives
lives--;
updateLivesText();
car.flash();
// Check if car is out of lives
if (lives <= 0) {
LK.showGameOver();
return;
}
}
if (ft < lastIntersectingFourthTrees.length) {
lastIntersectingFourthTrees[ft] = currentIntersectingFourth;
}
}
}
// Handle candle repositioning every 4 seconds (only in normal game, not special scenes)
if (!specialScene && !fourthScene) {
candleRepositionTimer++;
if (candleRepositionTimer >= repositionInterval) {
// Reposition all trees (candles) using tween animation
for (var t = 0; t < trees.length; t++) {
var tree = trees[t];
var newX = getRandomTreeX();
var newY = getRandomTreeY();
// Use tween to smoothly move trees to new positions
tween(tree, {
x: newX,
y: newY
}, {
duration: 1000,
easing: tween.easeInOut
});
}
candleRepositionTimer = 0; // Reset timer
}
// Handle pole repositioning every 5 seconds (only in normal game, not special scenes)
poleRepositionTimer++;
if (poleRepositionTimer >= poleRepositionInterval) {
// Reposition all poles using tween animation
for (var p = 0; p < poles.length; p++) {
var pole = poles[p];
var newX = getRandomTreeX();
var newY = getRandomTreeY();
// Set repositioning flag before starting movement
pole.isRepositioning = true;
// Use tween to smoothly move poles to new positions
tween(pole, {
x: newX,
y: newY
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Clear repositioning flag when movement completes
pole.isRepositioning = false;
}
});
}
poleRepositionTimer = 0; // Reset timer
}
}
// Handle winner asset repositioning every 7 seconds (only in normal game, not special scenes)
if (!specialScene && !fourthScene) {
winnerRepositionTimer++;
if (winnerRepositionTimer >= winnerRepositionInterval) {
// Reposition winner asset using tween animation
var newX = Math.random() * (2048 - 150) + 75; // Random X within game bounds
var newY = Math.random() * (2732 - 300) + 150; // Random Y within game bounds
// Use tween to smoothly move winner asset to new position
tween(winnerAsset, {
x: newX,
y: newY
}, {
duration: 1000,
easing: tween.easeInOut
});
winnerRepositionTimer = 0; // Reset timer
}
}
// Handle falling poles system (only in normal game, not special scenes)
if (!specialScene && !fourthScene) {
// Spawn new falling poles with dynamic intervals
if (LK.ticks - lastFallingPoleSpawn >= fallingPoleInterval) {
// Create two poles with a gap in the middle for character
var gapCenterX = Math.random() * (2048 - characterGapSize - 200) + 100 + characterGapSize / 2;
// Left pole
var leftPole = new Pole();
leftPole.x = Math.random() * (gapCenterX - characterGapSize / 2 - 50) + 25;
leftPole.y = -150;
leftPole.speed = 8; // Falling speed
leftPole.isFalling = true;
fallingPoles.push(leftPole);
game.addChild(leftPole);
// Right pole
var rightPole = new Pole();
rightPole.x = Math.random() * (2048 - (gapCenterX + characterGapSize / 2) - 50) + (gapCenterX + characterGapSize / 2) + 25;
rightPole.y = -150;
rightPole.speed = 8; // Falling speed
rightPole.isFalling = true;
fallingPoles.push(rightPole);
game.addChild(rightPole);
lastFallingPoleSpawn = LK.ticks;
// Progress to next interval if available
if (currentIntervalIndex < fallingPoleIntervals.length - 1) {
currentIntervalIndex++;
fallingPoleInterval = fallingPoleIntervals[currentIntervalIndex];
}
}
// Update falling poles
for (var fp = fallingPoles.length - 1; fp >= 0; fp--) {
var fallingPole = fallingPoles[fp];
fallingPole.y += fallingPole.speed;
// Remove poles that have fallen off screen
if (fallingPole.y > 2832 + 150) {
game.removeChild(fallingPole);
fallingPoles.splice(fp, 1);
continue;
}
// Check collision with car
if (car.intersects(fallingPole)) {
var currentTime = Date.now();
if (currentTime - lastPoleHitTime >= 2000) {
lives--;
updateLivesText();
lastPoleHitTime = currentTime;
car.flash();
fallingPole.hitAnim();
if (lives <= 0) {
LK.showGameOver();
return;
}
}
}
}
}
// Update tracking variables
lastCarX = car.x;
lastCarY = car.y;
lastScore = score;
};
// --- Initialize score ---
score = 0; // Always start from 0 points
updateScoreText();
// --- Initialize bottom-left counter ---
bottomLeftCounter = 2600;
updateBottomLeftCounterText();
// --- Initialize lives display ---
lives = 3;
updateLivesText();
// --- Initialize tracking variables ---
lastCarX = car.x;
lastCarY = car.y;
lastScore = score;
// --- Initialize to always start from first section (Birinci Bölüm) ---
specialScene = false; // Start in first section, transition at 2600 points
specialSceneInitialized = false;
followingStartTime = null;
allFollowingTreesGone = false;
// Initialize bullets array for special scene
var bullets = [];
// --- Falling poles system ---
var fallingPoles = [];
var lastFallingPoleSpawn = 0;
var fallingPoleIntervals = [300, 240, 180, 120, 60]; // 5, 4, 3, 2, 1 seconds at 60 FPS
var currentIntervalIndex = 0;
var fallingPoleInterval = fallingPoleIntervals[0]; // Start with 5 seconds
var characterGapSize = 250; // Gap size for character to pass through
// --- Candle repositioning timer ---
var candleRepositionTimer = 0;
var repositionInterval = 240; // 4 seconds at 60 FPS (4 * 60 = 240 ticks)
// --- Pole repositioning timer ---
var poleRepositionTimer = 0;
var poleRepositionInterval = 600; // 10 seconds at 60 FPS (10 * 60 = 600 ticks)
var poleCollisionCount = 0; // Track pole collisions for game over
var lives = 3; // Character has 3 lives
var lastPoleHitTime = 0; // Track last time player was hit by pole for 2-second cooldown
var lastDeadlyPoleHitTime = 0; // Track last time player was hit by deadly pole for 2-second cooldown
// --- Winner asset repositioning timer ---
var winnerRepositionTimer = 0;
var winnerRepositionInterval = 420; // 7 seconds at 60 FPS (7 * 60 = 420 ticks);;;