/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Cow = Container.expand(function () { var self = Container.call(this); var cowGraphics = self.attachAsset('cow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; // Slower than sheep self.wanderSpeed = 0.4; self.flockingDistance = 180; self.dogAvoidDistance = 220; self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderInterval = 80 + Math.random() * 140; self.scared = false; self.goalReached = false; // Track if cow has reached the goal area self.update = function () { // Check if cow is in cow goal area if (activeCowGoal && !self.goalReached) { var goalDist = Math.sqrt(Math.pow(self.x - activeCowGoal.x, 2) + Math.pow(self.y - activeCowGoal.y, 2)); if (goalDist < 150) { self.goalReached = true; // Celebration effects when cow reaches goal LK.effects.flashObject(self, 0x8B4513, 500); LK.getSound('moo').play(); // Add a little jump animation var originalY = self.y; tween(self, { y: originalY - 30 }, { duration: 300, easing: tween.bounceOut, onFinish: function onFinish() { tween(self, { y: originalY }, { duration: 200, easing: tween.bounceOut }); } }); } } // If cow reached the goal, make it stay there with small wandering if (self.goalReached) { // Small random movement within goal self.wanderTimer++; if (self.wanderTimer > self.wanderInterval) { self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderInterval = 120 + Math.random() * 180; } // Apply very small movement var slowSpeed = 0.2; self.x += Math.cos(self.wanderDirection) * slowSpeed; self.y += Math.sin(self.wanderDirection) * slowSpeed; // Ensure cow stays in goal area if (activeCowGoal) { var goalDist = Math.sqrt(Math.pow(self.x - activeCowGoal.x, 2) + Math.pow(self.y - activeCowGoal.y, 2)); if (goalDist > 140) { // Pull back toward center of goal var pullX = activeCowGoal.x - self.x; var pullY = activeCowGoal.y - self.y; var pullDist = Math.sqrt(pullX * pullX + pullY * pullY); self.x += pullX / pullDist * 1.0; self.y += pullY / pullDist * 1.0; } } return; // Skip normal movement behaviors } var vx = 0; var vy = 0; // Dog avoidance behavior var dog = game.dog; var dogDx = self.x - dog.x; var dogDy = self.y - dog.y; var dogDistance = Math.sqrt(dogDx * dogDx + dogDy * dogDy); if (dogDistance < self.dogAvoidDistance) { var dogFactor = 1 - dogDistance / self.dogAvoidDistance; vx += dogDx / dogDistance * dogFactor * 2.5; // Less responsive than sheep vy += dogDy / dogDistance * dogFactor * 2.5; self.scared = dogDistance < self.dogAvoidDistance * 0.6; } else { self.scared = false; } // Flocking behavior with other cows var cohesionX = 0; var cohesionY = 0; var alignmentX = 0; var alignmentY = 0; var separationX = 0; var separationY = 0; var flockCount = 0; for (var i = 0; i < game.cows.length; i++) { var otherCow = game.cows[i]; if (otherCow !== self) { var otherDx = otherCow.x - self.x; var otherDy = otherCow.y - self.y; var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDistance < self.flockingDistance) { // Cohesion - move toward center of flock cohesionX += otherCow.x; cohesionY += otherCow.y; // Alignment - align with flock direction alignmentX += Math.cos(otherCow.wanderDirection); alignmentY += Math.sin(otherCow.wanderDirection); // Separation - avoid crowding if (otherDistance < 70) { // Cows need more space separationX -= otherDx / otherDistance; separationY -= otherDy / otherDistance; } flockCount++; } } } // Apply flocking behaviors if there are nearby cows if (flockCount > 0) { // Cohesion cohesionX = cohesionX / flockCount - self.x; cohesionY = cohesionY / flockCount - self.y; var cohesionDist = Math.sqrt(cohesionX * cohesionX + cohesionY * cohesionY); if (cohesionDist > 0) { vx += cohesionX / cohesionDist * 0.1; vy += cohesionY / cohesionDist * 0.1; } // Alignment alignmentX /= flockCount; alignmentY /= flockCount; var alignmentDist = Math.sqrt(alignmentX * alignmentX + alignmentY * alignmentY); if (alignmentDist > 0) { vx += alignmentX / alignmentDist * 0.15; vy += alignmentY / alignmentDist * 0.15; } // Separation vx += separationX * 0.4; // Cows separate more strongly vy += separationY * 0.4; } else { // Wandering behavior when alone self.wanderTimer++; if (self.wanderTimer > self.wanderInterval) { self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderInterval = 80 + Math.random() * 140; } vx += Math.cos(self.wanderDirection) * self.wanderSpeed; vy += Math.sin(self.wanderDirection) * self.wanderSpeed; } // Randomly make scared cows moo if (self.scared && Math.random() < 0.01) { LK.getSound('moo').play(); } // Obstacle avoidance for (var i = 0; i < game.obstacles.length; i++) { var obstacle = game.obstacles[i]; var obsDx = self.x - obstacle.x; var obsDy = self.y - obstacle.y; var obsDistance = Math.sqrt(obsDx * obsDx + obsDy * obsDy); if (obsDistance < 130) { // Larger avoidance radius than sheep var obsFactor = 1 - obsDistance / 130; vx += obsDx / obsDistance * obsFactor * 2.2; vy += obsDy / obsDistance * obsFactor * 2.2; } } // Boundary avoidance if (self.x < 120) { vx += (120 - self.x) * 0.05; } if (self.x > 1928) { vx += (1928 - self.x) * 0.05; } if (self.y < 120) { vy += (120 - self.y) * 0.05; } if (self.y > 2612) { vy += (2612 - self.y) * 0.05; } // Normalize and apply movement var velocity = Math.sqrt(vx * vx + vy * vy); if (velocity > 0) { var moveSpeed = self.scared ? self.speed * 1.3 : self.speed; self.x += vx / velocity * moveSpeed; self.y += vy / velocity * moveSpeed; self.wanderDirection = Math.atan2(vy, vx); } }; return self; }); var CowGoal = Container.expand(function () { var self = Container.call(this); var goalGraphics = self.attachAsset('goal', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6, tint: 0x8B4513 // Brown tint for cow goal }); return self; }); var Dog = Container.expand(function () { var self = Container.call(this); var dogGraphics = self.attachAsset('dog', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.targetX = 0; self.targetY = 0; self.moving = false; self.setTarget = function (x, y) { self.targetX = x; self.targetY = y; self.moving = true; }; self.bark = function () { LK.getSound('bark').play(); LK.effects.flashObject(self, 0xA52A2A, 300); }; self.update = function () { if (self.moving) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { // Store previous position to detect movement var prevX = self.x; var prevY = self.y; // Move the dog self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Create grass trampling effect with dog footsteps if (LK.ticks % 15 === 0 && game.grassField) { // Add small particle effect at dog's feet var stepX = self.x - dx / distance * 20; var stepY = self.y - dy / distance * 20; // Highlight grass under the dog's feet if (Math.random() > 0.5) { LK.effects.flashObject(self, 0xFFFFFF, 100); } } } else { self.x = self.targetX; self.y = self.targetY; self.moving = false; } } }; return self; }); var Goal = Container.expand(function () { var self = Container.call(this); var goalGraphics = self.attachAsset('goal', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6 }); return self; }); var GrassField = Container.expand(function () { var self = Container.call(this); var blades = []; var bladesCount = 100; // Number of grass blades // Create grass blades for (var i = 0; i < bladesCount; i++) { var blade = LK.getAsset('goal', { anchorX: 0.5, anchorY: 1.0, // Bottom anchor so it can wave from bottom scaleX: 0.05 + Math.random() * 0.1, // Thin blades scaleY: 0.3 + Math.random() * 0.4, // Different heights tint: 0x4CAF50 + Math.floor(Math.random() * 0x2F2F2F), // Different shades of green alpha: 0.7 + Math.random() * 0.3 }); // Randomize positions throughout the field blade.x = Math.random() * 2048; blade.y = Math.random() * 2732; // Create unique wave pattern for each blade blade.waveSpeed = 0.01 + Math.random() * 0.02; blade.waveAmount = 0.05 + Math.random() * 0.1; blade.waveOffset = Math.random() * Math.PI * 2; blade.baseRotation = -0.1 + Math.random() * 0.2; blades.push(blade); self.addChild(blade); } // Animate grass blades self.update = function () { for (var i = 0; i < blades.length; i++) { var blade = blades[i]; // Gentle waving motion based on sine wave blade.rotation = blade.baseRotation + Math.sin(LK.ticks * blade.waveSpeed + blade.waveOffset) * blade.waveAmount; } }; // Method to create visual effect when dog moves through grass self.createRipple = function (x, y) { // Find blades near the position and create ripple effect for (var i = 0; i < blades.length; i++) { var blade = blades[i]; var dx = blade.x - x; var dy = blade.y - y; var dist = Math.sqrt(dx * dx + dy * dy); // Only affect nearby blades if (dist < 100) { // Calculate delay based on distance var delay = dist * 3; // Stop any existing tween on this blade tween.stop(blade); // Initial bend away var targetRotation = Math.atan2(dy, dx) * 0.5; // Apply tween for bending effect tween(blade, { rotation: targetRotation }, { duration: 200, onFinish: function (blade) { return function () { // Return to gentle swaying tween(blade, { rotation: blade.baseRotation }, { duration: 500, easing: tween.elasticOut }); }; }(blade) }); } } }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); var obstacleGraphics = self.attachAsset('rock', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Sheep = Container.expand(function () { var self = Container.call(this); var sheepGraphics = self.attachAsset('sheep', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.wanderSpeed = 0.5; self.flockingDistance = 150; self.dogAvoidDistance = 200; self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderInterval = 60 + Math.random() * 120; self.scared = false; self.goalReached = false; // Track if sheep has reached the goal area self.update = function () { // Check if sheep is in goal area if (activeGoal && !self.goalReached) { var goalDist = Math.sqrt(Math.pow(self.x - activeGoal.x, 2) + Math.pow(self.y - activeGoal.y, 2)); if (goalDist < 150) { self.goalReached = true; // Celebration effects when sheep reaches goal LK.effects.flashObject(self, 0x228b22, 500); LK.getSound('baa').play(); // Add a little jump animation var originalY = self.y; tween(self, { y: originalY - 30 }, { duration: 300, easing: tween.bounceOut, onFinish: function onFinish() { tween(self, { y: originalY }, { duration: 200, easing: tween.bounceOut }); } }); } } // If sheep reached the goal, make it stay there with small wandering if (self.goalReached) { // Small random movement within goal self.wanderTimer++; if (self.wanderTimer > self.wanderInterval) { self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderInterval = 120 + Math.random() * 180; } // Apply very small movement var slowSpeed = 0.2; self.x += Math.cos(self.wanderDirection) * slowSpeed; self.y += Math.sin(self.wanderDirection) * slowSpeed; // Ensure sheep stays in goal area if (activeGoal) { var goalDist = Math.sqrt(Math.pow(self.x - activeGoal.x, 2) + Math.pow(self.y - activeGoal.y, 2)); if (goalDist > 140) { // Pull back toward center of goal var pullX = activeGoal.x - self.x; var pullY = activeGoal.y - self.y; var pullDist = Math.sqrt(pullX * pullX + pullY * pullY); self.x += pullX / pullDist * 1.0; self.y += pullY / pullDist * 1.0; } } return; // Skip normal movement behaviors } var vx = 0; var vy = 0; // Dog avoidance behavior var dog = game.dog; var dogDx = self.x - dog.x; var dogDy = self.y - dog.y; var dogDistance = Math.sqrt(dogDx * dogDx + dogDy * dogDy); if (dogDistance < self.dogAvoidDistance) { var dogFactor = 1 - dogDistance / self.dogAvoidDistance; vx += dogDx / dogDistance * dogFactor * 3; vy += dogDy / dogDistance * dogFactor * 3; self.scared = dogDistance < self.dogAvoidDistance * 0.5; if (self.scared && Math.random() < 0.01) { LK.getSound('baa').play(); } } else { self.scared = false; } // Flocking behavior var cohesionX = 0; var cohesionY = 0; var alignmentX = 0; var alignmentY = 0; var separationX = 0; var separationY = 0; var flockCount = 0; for (var i = 0; i < game.sheep.length; i++) { var otherSheep = game.sheep[i]; if (otherSheep !== self) { var otherDx = otherSheep.x - self.x; var otherDy = otherSheep.y - self.y; var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDistance < self.flockingDistance) { // Cohesion - move toward center of flock cohesionX += otherSheep.x; cohesionY += otherSheep.y; // Alignment - align with flock direction alignmentX += Math.cos(otherSheep.wanderDirection); alignmentY += Math.sin(otherSheep.wanderDirection); // Separation - avoid crowding if (otherDistance < 60) { separationX -= otherDx / otherDistance; separationY -= otherDy / otherDistance; } flockCount++; } } } // Apply flocking behaviors if there are nearby sheep if (flockCount > 0) { // Cohesion cohesionX = cohesionX / flockCount - self.x; cohesionY = cohesionY / flockCount - self.y; var cohesionDist = Math.sqrt(cohesionX * cohesionX + cohesionY * cohesionY); if (cohesionDist > 0) { vx += cohesionX / cohesionDist * 0.1; vy += cohesionY / cohesionDist * 0.1; } // Alignment alignmentX /= flockCount; alignmentY /= flockCount; var alignmentDist = Math.sqrt(alignmentX * alignmentX + alignmentY * alignmentY); if (alignmentDist > 0) { vx += alignmentX / alignmentDist * 0.2; vy += alignmentY / alignmentDist * 0.2; } // Separation vx += separationX * 0.3; vy += separationY * 0.3; } else { // Wandering behavior when alone self.wanderTimer++; if (self.wanderTimer > self.wanderInterval) { self.wanderDirection = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.wanderInterval = 60 + Math.random() * 120; } vx += Math.cos(self.wanderDirection) * self.wanderSpeed; vy += Math.sin(self.wanderDirection) * self.wanderSpeed; } // Obstacle avoidance for (var i = 0; i < game.obstacles.length; i++) { var obstacle = game.obstacles[i]; var obsDx = self.x - obstacle.x; var obsDy = self.y - obstacle.y; var obsDistance = Math.sqrt(obsDx * obsDx + obsDy * obsDy); if (obsDistance < 120) { var obsFactor = 1 - obsDistance / 120; vx += obsDx / obsDistance * obsFactor * 2; vy += obsDy / obsDistance * obsFactor * 2; } } // Boundary avoidance if (self.x < 100) { vx += (100 - self.x) * 0.05; } if (self.x > 1948) { vx += (1948 - self.x) * 0.05; } if (self.y < 100) { vy += (100 - self.y) * 0.05; } if (self.y > 2632) { vy += (2632 - self.y) * 0.05; } // Normalize and apply movement var velocity = Math.sqrt(vx * vx + vy * vy); if (velocity > 0) { var moveSpeed = self.scared ? self.speed * 1.5 : self.speed; self.x += vx / velocity * moveSpeed; self.y += vy / velocity * moveSpeed; self.wanderDirection = Math.atan2(vy, vx); } }; return self; }); var StartScreen = Container.expand(function () { var self = Container.call(this); // Add a semi-transparent overlay for the start screen var overlay = LK.getAsset('goal', { anchorX: 0.5, anchorY: 0.5, scaleX: 15, scaleY: 20, alpha: 0.6, tint: 0x78AB46 }); self.addChild(overlay); // Add game title var titleText = new Text2('Shepherd', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.y = -350; self.addChild(titleText); // Add subtitle var subtitleText = new Text2('Herd sheep and cows to their pastures', { size: 60, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.y = -250; self.addChild(subtitleText); // Add sheep animation var sheepAnim = self.attachAsset('sheep', { anchorX: 0.5, anchorY: 0.5, x: -220, y: 0 }); // Add cow animation var cowAnim = self.attachAsset('cow', { anchorX: 0.5, anchorY: 0.5, x: 220, y: 0 }); // Add animated dog var dogAnim = self.attachAsset('dog', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 120 }); // Add start button var startButton = LK.getAsset('goal', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2, alpha: 0.9, tint: 0x228B22 }); startButton.y = 300; self.addChild(startButton); // Start button text var startText = new Text2('START', { size: 80, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startText.y = 300; self.addChild(startText); // Add press handler for start button startButton.down = function (x, y, obj) { self.startGame(); }; self.addChild(startButton); // Function to dismiss the start screen and start the game self.startGame = function () { // Play bark sound LK.getSound('bark').play(); // Animate fade out tween(self, { alpha: 0 }, { duration: 800, onFinish: function onFinish() { // Remove start screen and start the game game.removeChild(self); startGame(); } }); }; // Animation update function self.update = function () { // Animate sheep sheepAnim.x = -220 + Math.sin(LK.ticks / 40) * 30; sheepAnim.y = Math.sin(LK.ticks / 70) * 15; // Animate cow cowAnim.x = 220 + Math.sin(LK.ticks / 50 + 2) * 20; cowAnim.y = Math.sin(LK.ticks / 60 + 1) * 10; // Animate dog dogAnim.rotation = Math.sin(LK.ticks / 30) * 0.15; dogAnim.y = 120 + Math.sin(LK.ticks / 45) * 10; // Pulsing start button var scale = 1.0 + Math.sin(LK.ticks / 30) * 0.05; startButton.scale.x = 2.5 * scale; startButton.scale.y = 1.2 * scale; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x78AB46 }); /**** * Game Code ****/ // Game state variables var Level = function Level(sheepCount, obstacleCount, goalX, goalY, cowCount, cowGoalX, cowGoalY) { this.sheepCount = sheepCount; this.obstacleCount = obstacleCount; this.goalX = goalX; this.goalY = goalY; this.cowCount = cowCount || 0; // Default to 0 if not specified this.cowGoalX = cowGoalX || 0; // Default to 0 if not specified this.cowGoalY = cowGoalY || 0; // Default to 0 if not specified this.timeLimit = 60 + sheepCount * 10 + (cowCount || 0) * 15; // More time for levels with cows }; var currentLevel = 1; var maxLevels = 8; // Increased max levels var levels = []; var timeLeft = 0; var timerInterval = null; var activeGoal = null; var activeCowGoal = null; var levelCompleted = false; var sheepInGoal = 0; var cowsInGoal = 0; // Game elements game.grassField = null; game.sheep = []; game.cows = []; // New array for cows game.obstacles = []; game.dog = null; // Set up levels function setupLevels() { levels = [new Level(3, 3, 1700, 500), new Level(5, 5, 1600, 1800), new Level(7, 8, 500, 2200), new Level(10, 10, 300, 1200), // Level 5+ has cows (sheep, obstacles, sheepGoalX, sheepGoalY, cows, cowGoalX, cowGoalY) new Level(8, 12, 1700, 1300, 3, 400, 400), new Level(9, 14, 300, 600, 5, 1600, 2200), new Level(10, 15, 1500, 600, 7, 500, 2300), new Level(12, 18, 1700, 1300, 10, 300, 1700)]; } // Initialize UI var levelText = new Text2('Level 1', { size: 80, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); LK.gui.top.addChild(levelText); var scoreText = new Text2('Sheep: 0/0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); scoreText.x = -50; LK.gui.topRight.addChild(scoreText); var cowScoreText = new Text2('', { size: 60, fill: 0xA52A2A }); cowScoreText.anchor.set(1, 0); cowScoreText.x = -50; cowScoreText.y = 70; LK.gui.topRight.addChild(cowScoreText); var timerText = new Text2('Time: 0', { size: 60, fill: 0xFFFFFF }); timerText.anchor.set(0, 0); timerText.x = 50; LK.gui.topLeft.addChild(timerText); var instructionText = new Text2('Tap to move your dog\nHerd the sheep to the green area', { size: 60, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0); instructionText.y = 100; LK.gui.top.addChild(instructionText); // Second instruction text for levels with cows var cowInstructionText = new Text2('', { size: 60, fill: 0xA52A2A }); cowInstructionText.anchor.set(0.5, 0); cowInstructionText.y = 230; cowInstructionText.alpha = 0; LK.gui.top.addChild(cowInstructionText); // Show the instruction text for a limited time LK.setTimeout(function () { tween(instructionText, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { LK.gui.top.removeChild(instructionText); } }); }, 5000); // Start the game function startGame() { setupLevels(); startLevel(currentLevel); LK.playMusic('countryside'); } // Setup and start a level function startLevel(levelNum) { clearLevel(); var level = levels[levelNum - 1]; levelText.setText('Level ' + levelNum); // Set timer timeLeft = level.timeLimit; timerText.setText('Time: ' + timeLeft); if (timerInterval) { LK.clearInterval(timerInterval); } timerInterval = LK.setInterval(updateTimer, 1000); // Create grass field first (so it appears behind everything) if (!game.grassField) { game.grassField = new GrassField(); game.addChild(game.grassField); } // Create goal area with animation activeGoal = new Goal(); activeGoal.x = level.goalX; activeGoal.y = level.goalY; activeGoal.alpha = 0; game.addChild(activeGoal); // Animate goal area appearance tween(activeGoal, { alpha: 0.6, scaleX: 1.2, scaleY: 1.2 }, { duration: 400, onFinish: function onFinish() { tween(activeGoal, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.bounceOut }); } }); // Create cow goal area if this level has cows activeCowGoal = null; if (level.cowCount > 0) { activeCowGoal = new CowGoal(); activeCowGoal.x = level.cowGoalX; activeCowGoal.y = level.cowGoalY; activeCowGoal.alpha = 0; game.addChild(activeCowGoal); // Animate cow goal appearance with a slight delay LK.setTimeout(function () { tween(activeCowGoal, { alpha: 0.6, scaleX: 1.2, scaleY: 1.2 }, { duration: 400, onFinish: function onFinish() { tween(activeCowGoal, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.bounceOut }); } }); }, 300); // Show cow instructions for a few seconds cowInstructionText.setText('Herd the brown cows\nto the brown area'); cowInstructionText.alpha = 1; LK.setTimeout(function () { tween(cowInstructionText, { alpha: 0 }, { duration: 1000 }); }, 5000); } // Create dog game.dog = new Dog(); game.dog.x = 1024; game.dog.y = 1366; game.addChild(game.dog); // Create obstacles for (var i = 0; i < level.obstacleCount; i++) { var obstacle = new Obstacle(); var validPosition = false; // Ensure obstacles don't overlap with the goals while (!validPosition) { obstacle.x = 200 + Math.random() * 1648; obstacle.y = 200 + Math.random() * 2332; var goalDist = Math.sqrt(Math.pow(obstacle.x - activeGoal.x, 2) + Math.pow(obstacle.y - activeGoal.y, 2)); validPosition = goalDist > 300; // Also check cow goal distance if there is a cow goal if (validPosition && activeCowGoal) { var cowGoalDist = Math.sqrt(Math.pow(obstacle.x - activeCowGoal.x, 2) + Math.pow(obstacle.y - activeCowGoal.y, 2)); if (cowGoalDist <= 300) { validPosition = false; } } } game.obstacles.push(obstacle); game.addChild(obstacle); } // Create sheep for (var i = 0; i < level.sheepCount; i++) { var sheep = new Sheep(); var validPosition = false; // Place sheep away from goals and obstacles while (!validPosition) { sheep.x = 200 + Math.random() * 1648; sheep.y = 200 + Math.random() * 2332; var goalDist = Math.sqrt(Math.pow(sheep.x - activeGoal.x, 2) + Math.pow(sheep.y - activeGoal.y, 2)); validPosition = goalDist > 500; // Also check cow goal distance if there is a cow goal if (validPosition && activeCowGoal) { var cowGoalDist = Math.sqrt(Math.pow(sheep.x - activeCowGoal.x, 2) + Math.pow(sheep.y - activeCowGoal.y, 2)); if (cowGoalDist <= 400) { validPosition = false; } } if (validPosition) { // Check distance from obstacles for (var j = 0; j < game.obstacles.length; j++) { var obstacleDist = Math.sqrt(Math.pow(sheep.x - game.obstacles[j].x, 2) + Math.pow(sheep.y - game.obstacles[j].y, 2)); if (obstacleDist < 120) { validPosition = false; break; } } } } game.sheep.push(sheep); game.addChild(sheep); } // Create cows if this level has them if (level.cowCount > 0) { for (var i = 0; i < level.cowCount; i++) { var cow = new Cow(); var validPosition = false; // Place cows away from goals, obstacles, and sheep while (!validPosition) { cow.x = 200 + Math.random() * 1648; cow.y = 200 + Math.random() * 2332; // Check distance from sheep goal var sheepGoalDist = Math.sqrt(Math.pow(cow.x - activeGoal.x, 2) + Math.pow(cow.y - activeGoal.y, 2)); validPosition = sheepGoalDist > 500; // Check distance from cow goal if (validPosition && activeCowGoal) { var cowGoalDist = Math.sqrt(Math.pow(cow.x - activeCowGoal.x, 2) + Math.pow(cow.y - activeCowGoal.y, 2)); if (cowGoalDist <= 500) { validPosition = false; } } // Check distance from obstacles if (validPosition) { for (var j = 0; j < game.obstacles.length; j++) { var obstacleDist = Math.sqrt(Math.pow(cow.x - game.obstacles[j].x, 2) + Math.pow(cow.y - game.obstacles[j].y, 2)); if (obstacleDist < 140) { validPosition = false; break; } } } // Check distance from sheep if (validPosition) { for (var j = 0; j < game.sheep.length; j++) { var sheepDist = Math.sqrt(Math.pow(cow.x - game.sheep[j].x, 2) + Math.pow(cow.y - game.sheep[j].y, 2)); if (sheepDist < 200) { validPosition = false; break; } } } } game.cows.push(cow); game.addChild(cow); } } // Update UI sheepInGoal = 0; cowsInGoal = 0; levelCompleted = false; updateScoreText(); } // Clear all game elements from the current level function clearLevel() { // Remove sheep for (var i = 0; i < game.sheep.length; i++) { game.removeChild(game.sheep[i]); } game.sheep = []; // Remove cows for (var i = 0; i < game.cows.length; i++) { game.removeChild(game.cows[i]); } game.cows = []; // Remove obstacles for (var i = 0; i < game.obstacles.length; i++) { game.removeChild(game.obstacles[i]); } game.obstacles = []; // Remove dog if (game.dog) { game.removeChild(game.dog); game.dog = null; } // Remove goal if (activeGoal) { game.removeChild(activeGoal); activeGoal = null; } // Remove cow goal if (activeCowGoal) { game.removeChild(activeCowGoal); activeCowGoal = null; } // Keep grass field between levels but refresh it if (game.grassField) { // Add subtle animation to grass when changing levels tween(game.grassField, { alpha: 0.7 }, { duration: 300, onFinish: function onFinish() { tween(game.grassField, { alpha: 1 }, { duration: 300 }); } }); } } // Update the timer function updateTimer() { timeLeft--; timerText.setText('Time: ' + timeLeft); if (timeLeft <= 0) { LK.clearInterval(timerInterval); endLevel(false); } } // Update the score display function updateScoreText() { var level = levels[currentLevel - 1]; // Check if level is defined before accessing its properties if (level) { scoreText.setText('Sheep: ' + sheepInGoal + '/' + level.sheepCount); // Show cow score if there are cows in this level if (level.cowCount > 0) { cowScoreText.setText('Cows: ' + cowsInGoal + '/' + level.cowCount); cowScoreText.alpha = 1; } else { cowScoreText.alpha = 0; } } else { // Provide a default text if level is undefined scoreText.setText('Sheep: ' + sheepInGoal + '/0'); cowScoreText.alpha = 0; } } // End the current level function endLevel(success) { LK.clearInterval(timerInterval); if (success) { LK.getSound('success').play(); // Add cow points to score var cowPoints = cowsInGoal * 150; // Cows are worth more points LK.setScore(LK.getScore() + sheepInGoal * 100 + cowPoints + timeLeft * 10); if (currentLevel < maxLevels) { currentLevel++; LK.setTimeout(function () { startLevel(currentLevel); }, 1500); } else { // Game completed LK.showYouWin(); } } else { LK.showGameOver(); } } // Control the dog with tap/click game.down = function (x, y) { if (game.dog && !levelCompleted) { game.dog.setTarget(x, y); game.dog.bark(); } }; // Main game update loop game.update = function () { if (levelCompleted) { return; } // Update grass field if (game.grassField) { game.grassField.update(); // Create ripple effects when dog moves if (game.dog && game.dog.moving) { // Only create ripple occasionally to avoid overwhelming effects if (LK.ticks % 10 === 0) { game.grassField.createRipple(game.dog.x, game.dog.y); } } } // Update all sheep for (var i = 0; i < game.sheep.length; i++) { game.sheep[i].update(); } // Update all cows for (var i = 0; i < game.cows.length; i++) { game.cows[i].update(); } // Update dog if (game.dog) { game.dog.update(); } // Check for sheep in goal area if (activeGoal) { sheepInGoal = 0; for (var i = 0; i < game.sheep.length; i++) { var sheep = game.sheep[i]; var goalDist = Math.sqrt(Math.pow(sheep.x - activeGoal.x, 2) + Math.pow(sheep.y - activeGoal.y, 2)); if (goalDist < 150) { sheepInGoal++; } } } // Check for cows in cow goal area if this level has cows if (activeCowGoal) { cowsInGoal = 0; for (var i = 0; i < game.cows.length; i++) { var cow = game.cows[i]; var cowGoalDist = Math.sqrt(Math.pow(cow.x - activeCowGoal.x, 2) + Math.pow(cow.y - activeCowGoal.y, 2)); if (cowGoalDist < 150) { cowsInGoal++; } } } updateScoreText(); // Check for level completion var level = levels[currentLevel - 1]; if (level) { var sheepGoalMet = sheepInGoal >= level.sheepCount; var cowGoalMet = level.cowCount === 0 || cowsInGoal >= level.cowCount; if (sheepGoalMet && cowGoalMet && !levelCompleted) { levelCompleted = true; endLevel(true); } } }; // Create and show start screen instead of auto-starting var showStartScreen = function showStartScreen() { var startScreen = new StartScreen(); startScreen.x = 1024; // Center of screen startScreen.y = 1366; // Center of screen game.addChild(startScreen); // Play background music when showing start screen LK.playMusic('countryside', { fade: { start: 0, end: 0.8, duration: 1500 } }); }; // Call showStartScreen instead of startGame showStartScreen(); ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Cow = Container.expand(function () {
var self = Container.call(this);
var cowGraphics = self.attachAsset('cow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5; // Slower than sheep
self.wanderSpeed = 0.4;
self.flockingDistance = 180;
self.dogAvoidDistance = 220;
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderInterval = 80 + Math.random() * 140;
self.scared = false;
self.goalReached = false; // Track if cow has reached the goal area
self.update = function () {
// Check if cow is in cow goal area
if (activeCowGoal && !self.goalReached) {
var goalDist = Math.sqrt(Math.pow(self.x - activeCowGoal.x, 2) + Math.pow(self.y - activeCowGoal.y, 2));
if (goalDist < 150) {
self.goalReached = true;
// Celebration effects when cow reaches goal
LK.effects.flashObject(self, 0x8B4513, 500);
LK.getSound('moo').play();
// Add a little jump animation
var originalY = self.y;
tween(self, {
y: originalY - 30
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(self, {
y: originalY
}, {
duration: 200,
easing: tween.bounceOut
});
}
});
}
}
// If cow reached the goal, make it stay there with small wandering
if (self.goalReached) {
// Small random movement within goal
self.wanderTimer++;
if (self.wanderTimer > self.wanderInterval) {
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderInterval = 120 + Math.random() * 180;
}
// Apply very small movement
var slowSpeed = 0.2;
self.x += Math.cos(self.wanderDirection) * slowSpeed;
self.y += Math.sin(self.wanderDirection) * slowSpeed;
// Ensure cow stays in goal area
if (activeCowGoal) {
var goalDist = Math.sqrt(Math.pow(self.x - activeCowGoal.x, 2) + Math.pow(self.y - activeCowGoal.y, 2));
if (goalDist > 140) {
// Pull back toward center of goal
var pullX = activeCowGoal.x - self.x;
var pullY = activeCowGoal.y - self.y;
var pullDist = Math.sqrt(pullX * pullX + pullY * pullY);
self.x += pullX / pullDist * 1.0;
self.y += pullY / pullDist * 1.0;
}
}
return; // Skip normal movement behaviors
}
var vx = 0;
var vy = 0;
// Dog avoidance behavior
var dog = game.dog;
var dogDx = self.x - dog.x;
var dogDy = self.y - dog.y;
var dogDistance = Math.sqrt(dogDx * dogDx + dogDy * dogDy);
if (dogDistance < self.dogAvoidDistance) {
var dogFactor = 1 - dogDistance / self.dogAvoidDistance;
vx += dogDx / dogDistance * dogFactor * 2.5; // Less responsive than sheep
vy += dogDy / dogDistance * dogFactor * 2.5;
self.scared = dogDistance < self.dogAvoidDistance * 0.6;
} else {
self.scared = false;
}
// Flocking behavior with other cows
var cohesionX = 0;
var cohesionY = 0;
var alignmentX = 0;
var alignmentY = 0;
var separationX = 0;
var separationY = 0;
var flockCount = 0;
for (var i = 0; i < game.cows.length; i++) {
var otherCow = game.cows[i];
if (otherCow !== self) {
var otherDx = otherCow.x - self.x;
var otherDy = otherCow.y - self.y;
var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy);
if (otherDistance < self.flockingDistance) {
// Cohesion - move toward center of flock
cohesionX += otherCow.x;
cohesionY += otherCow.y;
// Alignment - align with flock direction
alignmentX += Math.cos(otherCow.wanderDirection);
alignmentY += Math.sin(otherCow.wanderDirection);
// Separation - avoid crowding
if (otherDistance < 70) {
// Cows need more space
separationX -= otherDx / otherDistance;
separationY -= otherDy / otherDistance;
}
flockCount++;
}
}
}
// Apply flocking behaviors if there are nearby cows
if (flockCount > 0) {
// Cohesion
cohesionX = cohesionX / flockCount - self.x;
cohesionY = cohesionY / flockCount - self.y;
var cohesionDist = Math.sqrt(cohesionX * cohesionX + cohesionY * cohesionY);
if (cohesionDist > 0) {
vx += cohesionX / cohesionDist * 0.1;
vy += cohesionY / cohesionDist * 0.1;
}
// Alignment
alignmentX /= flockCount;
alignmentY /= flockCount;
var alignmentDist = Math.sqrt(alignmentX * alignmentX + alignmentY * alignmentY);
if (alignmentDist > 0) {
vx += alignmentX / alignmentDist * 0.15;
vy += alignmentY / alignmentDist * 0.15;
}
// Separation
vx += separationX * 0.4; // Cows separate more strongly
vy += separationY * 0.4;
} else {
// Wandering behavior when alone
self.wanderTimer++;
if (self.wanderTimer > self.wanderInterval) {
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderInterval = 80 + Math.random() * 140;
}
vx += Math.cos(self.wanderDirection) * self.wanderSpeed;
vy += Math.sin(self.wanderDirection) * self.wanderSpeed;
}
// Randomly make scared cows moo
if (self.scared && Math.random() < 0.01) {
LK.getSound('moo').play();
}
// Obstacle avoidance
for (var i = 0; i < game.obstacles.length; i++) {
var obstacle = game.obstacles[i];
var obsDx = self.x - obstacle.x;
var obsDy = self.y - obstacle.y;
var obsDistance = Math.sqrt(obsDx * obsDx + obsDy * obsDy);
if (obsDistance < 130) {
// Larger avoidance radius than sheep
var obsFactor = 1 - obsDistance / 130;
vx += obsDx / obsDistance * obsFactor * 2.2;
vy += obsDy / obsDistance * obsFactor * 2.2;
}
}
// Boundary avoidance
if (self.x < 120) {
vx += (120 - self.x) * 0.05;
}
if (self.x > 1928) {
vx += (1928 - self.x) * 0.05;
}
if (self.y < 120) {
vy += (120 - self.y) * 0.05;
}
if (self.y > 2612) {
vy += (2612 - self.y) * 0.05;
}
// Normalize and apply movement
var velocity = Math.sqrt(vx * vx + vy * vy);
if (velocity > 0) {
var moveSpeed = self.scared ? self.speed * 1.3 : self.speed;
self.x += vx / velocity * moveSpeed;
self.y += vy / velocity * moveSpeed;
self.wanderDirection = Math.atan2(vy, vx);
}
};
return self;
});
var CowGoal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
tint: 0x8B4513 // Brown tint for cow goal
});
return self;
});
var Dog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('dog', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.targetX = 0;
self.targetY = 0;
self.moving = false;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
self.moving = true;
};
self.bark = function () {
LK.getSound('bark').play();
LK.effects.flashObject(self, 0xA52A2A, 300);
};
self.update = function () {
if (self.moving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
// Store previous position to detect movement
var prevX = self.x;
var prevY = self.y;
// Move the dog
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Create grass trampling effect with dog footsteps
if (LK.ticks % 15 === 0 && game.grassField) {
// Add small particle effect at dog's feet
var stepX = self.x - dx / distance * 20;
var stepY = self.y - dy / distance * 20;
// Highlight grass under the dog's feet
if (Math.random() > 0.5) {
LK.effects.flashObject(self, 0xFFFFFF, 100);
}
}
} else {
self.x = self.targetX;
self.y = self.targetY;
self.moving = false;
}
}
};
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
return self;
});
var GrassField = Container.expand(function () {
var self = Container.call(this);
var blades = [];
var bladesCount = 100; // Number of grass blades
// Create grass blades
for (var i = 0; i < bladesCount; i++) {
var blade = LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 1.0,
// Bottom anchor so it can wave from bottom
scaleX: 0.05 + Math.random() * 0.1,
// Thin blades
scaleY: 0.3 + Math.random() * 0.4,
// Different heights
tint: 0x4CAF50 + Math.floor(Math.random() * 0x2F2F2F),
// Different shades of green
alpha: 0.7 + Math.random() * 0.3
});
// Randomize positions throughout the field
blade.x = Math.random() * 2048;
blade.y = Math.random() * 2732;
// Create unique wave pattern for each blade
blade.waveSpeed = 0.01 + Math.random() * 0.02;
blade.waveAmount = 0.05 + Math.random() * 0.1;
blade.waveOffset = Math.random() * Math.PI * 2;
blade.baseRotation = -0.1 + Math.random() * 0.2;
blades.push(blade);
self.addChild(blade);
}
// Animate grass blades
self.update = function () {
for (var i = 0; i < blades.length; i++) {
var blade = blades[i];
// Gentle waving motion based on sine wave
blade.rotation = blade.baseRotation + Math.sin(LK.ticks * blade.waveSpeed + blade.waveOffset) * blade.waveAmount;
}
};
// Method to create visual effect when dog moves through grass
self.createRipple = function (x, y) {
// Find blades near the position and create ripple effect
for (var i = 0; i < blades.length; i++) {
var blade = blades[i];
var dx = blade.x - x;
var dy = blade.y - y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Only affect nearby blades
if (dist < 100) {
// Calculate delay based on distance
var delay = dist * 3;
// Stop any existing tween on this blade
tween.stop(blade);
// Initial bend away
var targetRotation = Math.atan2(dy, dx) * 0.5;
// Apply tween for bending effect
tween(blade, {
rotation: targetRotation
}, {
duration: 200,
onFinish: function (blade) {
return function () {
// Return to gentle swaying
tween(blade, {
rotation: blade.baseRotation
}, {
duration: 500,
easing: tween.elasticOut
});
};
}(blade)
});
}
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Sheep = Container.expand(function () {
var self = Container.call(this);
var sheepGraphics = self.attachAsset('sheep', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.wanderSpeed = 0.5;
self.flockingDistance = 150;
self.dogAvoidDistance = 200;
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderInterval = 60 + Math.random() * 120;
self.scared = false;
self.goalReached = false; // Track if sheep has reached the goal area
self.update = function () {
// Check if sheep is in goal area
if (activeGoal && !self.goalReached) {
var goalDist = Math.sqrt(Math.pow(self.x - activeGoal.x, 2) + Math.pow(self.y - activeGoal.y, 2));
if (goalDist < 150) {
self.goalReached = true;
// Celebration effects when sheep reaches goal
LK.effects.flashObject(self, 0x228b22, 500);
LK.getSound('baa').play();
// Add a little jump animation
var originalY = self.y;
tween(self, {
y: originalY - 30
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(self, {
y: originalY
}, {
duration: 200,
easing: tween.bounceOut
});
}
});
}
}
// If sheep reached the goal, make it stay there with small wandering
if (self.goalReached) {
// Small random movement within goal
self.wanderTimer++;
if (self.wanderTimer > self.wanderInterval) {
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderInterval = 120 + Math.random() * 180;
}
// Apply very small movement
var slowSpeed = 0.2;
self.x += Math.cos(self.wanderDirection) * slowSpeed;
self.y += Math.sin(self.wanderDirection) * slowSpeed;
// Ensure sheep stays in goal area
if (activeGoal) {
var goalDist = Math.sqrt(Math.pow(self.x - activeGoal.x, 2) + Math.pow(self.y - activeGoal.y, 2));
if (goalDist > 140) {
// Pull back toward center of goal
var pullX = activeGoal.x - self.x;
var pullY = activeGoal.y - self.y;
var pullDist = Math.sqrt(pullX * pullX + pullY * pullY);
self.x += pullX / pullDist * 1.0;
self.y += pullY / pullDist * 1.0;
}
}
return; // Skip normal movement behaviors
}
var vx = 0;
var vy = 0;
// Dog avoidance behavior
var dog = game.dog;
var dogDx = self.x - dog.x;
var dogDy = self.y - dog.y;
var dogDistance = Math.sqrt(dogDx * dogDx + dogDy * dogDy);
if (dogDistance < self.dogAvoidDistance) {
var dogFactor = 1 - dogDistance / self.dogAvoidDistance;
vx += dogDx / dogDistance * dogFactor * 3;
vy += dogDy / dogDistance * dogFactor * 3;
self.scared = dogDistance < self.dogAvoidDistance * 0.5;
if (self.scared && Math.random() < 0.01) {
LK.getSound('baa').play();
}
} else {
self.scared = false;
}
// Flocking behavior
var cohesionX = 0;
var cohesionY = 0;
var alignmentX = 0;
var alignmentY = 0;
var separationX = 0;
var separationY = 0;
var flockCount = 0;
for (var i = 0; i < game.sheep.length; i++) {
var otherSheep = game.sheep[i];
if (otherSheep !== self) {
var otherDx = otherSheep.x - self.x;
var otherDy = otherSheep.y - self.y;
var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy);
if (otherDistance < self.flockingDistance) {
// Cohesion - move toward center of flock
cohesionX += otherSheep.x;
cohesionY += otherSheep.y;
// Alignment - align with flock direction
alignmentX += Math.cos(otherSheep.wanderDirection);
alignmentY += Math.sin(otherSheep.wanderDirection);
// Separation - avoid crowding
if (otherDistance < 60) {
separationX -= otherDx / otherDistance;
separationY -= otherDy / otherDistance;
}
flockCount++;
}
}
}
// Apply flocking behaviors if there are nearby sheep
if (flockCount > 0) {
// Cohesion
cohesionX = cohesionX / flockCount - self.x;
cohesionY = cohesionY / flockCount - self.y;
var cohesionDist = Math.sqrt(cohesionX * cohesionX + cohesionY * cohesionY);
if (cohesionDist > 0) {
vx += cohesionX / cohesionDist * 0.1;
vy += cohesionY / cohesionDist * 0.1;
}
// Alignment
alignmentX /= flockCount;
alignmentY /= flockCount;
var alignmentDist = Math.sqrt(alignmentX * alignmentX + alignmentY * alignmentY);
if (alignmentDist > 0) {
vx += alignmentX / alignmentDist * 0.2;
vy += alignmentY / alignmentDist * 0.2;
}
// Separation
vx += separationX * 0.3;
vy += separationY * 0.3;
} else {
// Wandering behavior when alone
self.wanderTimer++;
if (self.wanderTimer > self.wanderInterval) {
self.wanderDirection = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.wanderInterval = 60 + Math.random() * 120;
}
vx += Math.cos(self.wanderDirection) * self.wanderSpeed;
vy += Math.sin(self.wanderDirection) * self.wanderSpeed;
}
// Obstacle avoidance
for (var i = 0; i < game.obstacles.length; i++) {
var obstacle = game.obstacles[i];
var obsDx = self.x - obstacle.x;
var obsDy = self.y - obstacle.y;
var obsDistance = Math.sqrt(obsDx * obsDx + obsDy * obsDy);
if (obsDistance < 120) {
var obsFactor = 1 - obsDistance / 120;
vx += obsDx / obsDistance * obsFactor * 2;
vy += obsDy / obsDistance * obsFactor * 2;
}
}
// Boundary avoidance
if (self.x < 100) {
vx += (100 - self.x) * 0.05;
}
if (self.x > 1948) {
vx += (1948 - self.x) * 0.05;
}
if (self.y < 100) {
vy += (100 - self.y) * 0.05;
}
if (self.y > 2632) {
vy += (2632 - self.y) * 0.05;
}
// Normalize and apply movement
var velocity = Math.sqrt(vx * vx + vy * vy);
if (velocity > 0) {
var moveSpeed = self.scared ? self.speed * 1.5 : self.speed;
self.x += vx / velocity * moveSpeed;
self.y += vy / velocity * moveSpeed;
self.wanderDirection = Math.atan2(vy, vx);
}
};
return self;
});
var StartScreen = Container.expand(function () {
var self = Container.call(this);
// Add a semi-transparent overlay for the start screen
var overlay = LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 20,
alpha: 0.6,
tint: 0x78AB46
});
self.addChild(overlay);
// Add game title
var titleText = new Text2('Shepherd', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -350;
self.addChild(titleText);
// Add subtitle
var subtitleText = new Text2('Herd sheep and cows to their pastures', {
size: 60,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.y = -250;
self.addChild(subtitleText);
// Add sheep animation
var sheepAnim = self.attachAsset('sheep', {
anchorX: 0.5,
anchorY: 0.5,
x: -220,
y: 0
});
// Add cow animation
var cowAnim = self.attachAsset('cow', {
anchorX: 0.5,
anchorY: 0.5,
x: 220,
y: 0
});
// Add animated dog
var dogAnim = self.attachAsset('dog', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 120
});
// Add start button
var startButton = LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
alpha: 0.9,
tint: 0x228B22
});
startButton.y = 300;
self.addChild(startButton);
// Start button text
var startText = new Text2('START', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.y = 300;
self.addChild(startText);
// Add press handler for start button
startButton.down = function (x, y, obj) {
self.startGame();
};
self.addChild(startButton);
// Function to dismiss the start screen and start the game
self.startGame = function () {
// Play bark sound
LK.getSound('bark').play();
// Animate fade out
tween(self, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
// Remove start screen and start the game
game.removeChild(self);
startGame();
}
});
};
// Animation update function
self.update = function () {
// Animate sheep
sheepAnim.x = -220 + Math.sin(LK.ticks / 40) * 30;
sheepAnim.y = Math.sin(LK.ticks / 70) * 15;
// Animate cow
cowAnim.x = 220 + Math.sin(LK.ticks / 50 + 2) * 20;
cowAnim.y = Math.sin(LK.ticks / 60 + 1) * 10;
// Animate dog
dogAnim.rotation = Math.sin(LK.ticks / 30) * 0.15;
dogAnim.y = 120 + Math.sin(LK.ticks / 45) * 10;
// Pulsing start button
var scale = 1.0 + Math.sin(LK.ticks / 30) * 0.05;
startButton.scale.x = 2.5 * scale;
startButton.scale.y = 1.2 * scale;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x78AB46
});
/****
* Game Code
****/
// Game state variables
var Level = function Level(sheepCount, obstacleCount, goalX, goalY, cowCount, cowGoalX, cowGoalY) {
this.sheepCount = sheepCount;
this.obstacleCount = obstacleCount;
this.goalX = goalX;
this.goalY = goalY;
this.cowCount = cowCount || 0; // Default to 0 if not specified
this.cowGoalX = cowGoalX || 0; // Default to 0 if not specified
this.cowGoalY = cowGoalY || 0; // Default to 0 if not specified
this.timeLimit = 60 + sheepCount * 10 + (cowCount || 0) * 15; // More time for levels with cows
};
var currentLevel = 1;
var maxLevels = 8; // Increased max levels
var levels = [];
var timeLeft = 0;
var timerInterval = null;
var activeGoal = null;
var activeCowGoal = null;
var levelCompleted = false;
var sheepInGoal = 0;
var cowsInGoal = 0;
// Game elements
game.grassField = null;
game.sheep = [];
game.cows = []; // New array for cows
game.obstacles = [];
game.dog = null;
// Set up levels
function setupLevels() {
levels = [new Level(3, 3, 1700, 500), new Level(5, 5, 1600, 1800), new Level(7, 8, 500, 2200), new Level(10, 10, 300, 1200),
// Level 5+ has cows (sheep, obstacles, sheepGoalX, sheepGoalY, cows, cowGoalX, cowGoalY)
new Level(8, 12, 1700, 1300, 3, 400, 400), new Level(9, 14, 300, 600, 5, 1600, 2200), new Level(10, 15, 1500, 600, 7, 500, 2300), new Level(12, 18, 1700, 1300, 10, 300, 1700)];
}
// Initialize UI
var levelText = new Text2('Level 1', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
var scoreText = new Text2('Sheep: 0/0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -50;
LK.gui.topRight.addChild(scoreText);
var cowScoreText = new Text2('', {
size: 60,
fill: 0xA52A2A
});
cowScoreText.anchor.set(1, 0);
cowScoreText.x = -50;
cowScoreText.y = 70;
LK.gui.topRight.addChild(cowScoreText);
var timerText = new Text2('Time: 0', {
size: 60,
fill: 0xFFFFFF
});
timerText.anchor.set(0, 0);
timerText.x = 50;
LK.gui.topLeft.addChild(timerText);
var instructionText = new Text2('Tap to move your dog\nHerd the sheep to the green area', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
instructionText.y = 100;
LK.gui.top.addChild(instructionText);
// Second instruction text for levels with cows
var cowInstructionText = new Text2('', {
size: 60,
fill: 0xA52A2A
});
cowInstructionText.anchor.set(0.5, 0);
cowInstructionText.y = 230;
cowInstructionText.alpha = 0;
LK.gui.top.addChild(cowInstructionText);
// Show the instruction text for a limited time
LK.setTimeout(function () {
tween(instructionText, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
LK.gui.top.removeChild(instructionText);
}
});
}, 5000);
// Start the game
function startGame() {
setupLevels();
startLevel(currentLevel);
LK.playMusic('countryside');
}
// Setup and start a level
function startLevel(levelNum) {
clearLevel();
var level = levels[levelNum - 1];
levelText.setText('Level ' + levelNum);
// Set timer
timeLeft = level.timeLimit;
timerText.setText('Time: ' + timeLeft);
if (timerInterval) {
LK.clearInterval(timerInterval);
}
timerInterval = LK.setInterval(updateTimer, 1000);
// Create grass field first (so it appears behind everything)
if (!game.grassField) {
game.grassField = new GrassField();
game.addChild(game.grassField);
}
// Create goal area with animation
activeGoal = new Goal();
activeGoal.x = level.goalX;
activeGoal.y = level.goalY;
activeGoal.alpha = 0;
game.addChild(activeGoal);
// Animate goal area appearance
tween(activeGoal, {
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
onFinish: function onFinish() {
tween(activeGoal, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
// Create cow goal area if this level has cows
activeCowGoal = null;
if (level.cowCount > 0) {
activeCowGoal = new CowGoal();
activeCowGoal.x = level.cowGoalX;
activeCowGoal.y = level.cowGoalY;
activeCowGoal.alpha = 0;
game.addChild(activeCowGoal);
// Animate cow goal appearance with a slight delay
LK.setTimeout(function () {
tween(activeCowGoal, {
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
onFinish: function onFinish() {
tween(activeCowGoal, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
}, 300);
// Show cow instructions for a few seconds
cowInstructionText.setText('Herd the brown cows\nto the brown area');
cowInstructionText.alpha = 1;
LK.setTimeout(function () {
tween(cowInstructionText, {
alpha: 0
}, {
duration: 1000
});
}, 5000);
}
// Create dog
game.dog = new Dog();
game.dog.x = 1024;
game.dog.y = 1366;
game.addChild(game.dog);
// Create obstacles
for (var i = 0; i < level.obstacleCount; i++) {
var obstacle = new Obstacle();
var validPosition = false;
// Ensure obstacles don't overlap with the goals
while (!validPosition) {
obstacle.x = 200 + Math.random() * 1648;
obstacle.y = 200 + Math.random() * 2332;
var goalDist = Math.sqrt(Math.pow(obstacle.x - activeGoal.x, 2) + Math.pow(obstacle.y - activeGoal.y, 2));
validPosition = goalDist > 300;
// Also check cow goal distance if there is a cow goal
if (validPosition && activeCowGoal) {
var cowGoalDist = Math.sqrt(Math.pow(obstacle.x - activeCowGoal.x, 2) + Math.pow(obstacle.y - activeCowGoal.y, 2));
if (cowGoalDist <= 300) {
validPosition = false;
}
}
}
game.obstacles.push(obstacle);
game.addChild(obstacle);
}
// Create sheep
for (var i = 0; i < level.sheepCount; i++) {
var sheep = new Sheep();
var validPosition = false;
// Place sheep away from goals and obstacles
while (!validPosition) {
sheep.x = 200 + Math.random() * 1648;
sheep.y = 200 + Math.random() * 2332;
var goalDist = Math.sqrt(Math.pow(sheep.x - activeGoal.x, 2) + Math.pow(sheep.y - activeGoal.y, 2));
validPosition = goalDist > 500;
// Also check cow goal distance if there is a cow goal
if (validPosition && activeCowGoal) {
var cowGoalDist = Math.sqrt(Math.pow(sheep.x - activeCowGoal.x, 2) + Math.pow(sheep.y - activeCowGoal.y, 2));
if (cowGoalDist <= 400) {
validPosition = false;
}
}
if (validPosition) {
// Check distance from obstacles
for (var j = 0; j < game.obstacles.length; j++) {
var obstacleDist = Math.sqrt(Math.pow(sheep.x - game.obstacles[j].x, 2) + Math.pow(sheep.y - game.obstacles[j].y, 2));
if (obstacleDist < 120) {
validPosition = false;
break;
}
}
}
}
game.sheep.push(sheep);
game.addChild(sheep);
}
// Create cows if this level has them
if (level.cowCount > 0) {
for (var i = 0; i < level.cowCount; i++) {
var cow = new Cow();
var validPosition = false;
// Place cows away from goals, obstacles, and sheep
while (!validPosition) {
cow.x = 200 + Math.random() * 1648;
cow.y = 200 + Math.random() * 2332;
// Check distance from sheep goal
var sheepGoalDist = Math.sqrt(Math.pow(cow.x - activeGoal.x, 2) + Math.pow(cow.y - activeGoal.y, 2));
validPosition = sheepGoalDist > 500;
// Check distance from cow goal
if (validPosition && activeCowGoal) {
var cowGoalDist = Math.sqrt(Math.pow(cow.x - activeCowGoal.x, 2) + Math.pow(cow.y - activeCowGoal.y, 2));
if (cowGoalDist <= 500) {
validPosition = false;
}
}
// Check distance from obstacles
if (validPosition) {
for (var j = 0; j < game.obstacles.length; j++) {
var obstacleDist = Math.sqrt(Math.pow(cow.x - game.obstacles[j].x, 2) + Math.pow(cow.y - game.obstacles[j].y, 2));
if (obstacleDist < 140) {
validPosition = false;
break;
}
}
}
// Check distance from sheep
if (validPosition) {
for (var j = 0; j < game.sheep.length; j++) {
var sheepDist = Math.sqrt(Math.pow(cow.x - game.sheep[j].x, 2) + Math.pow(cow.y - game.sheep[j].y, 2));
if (sheepDist < 200) {
validPosition = false;
break;
}
}
}
}
game.cows.push(cow);
game.addChild(cow);
}
}
// Update UI
sheepInGoal = 0;
cowsInGoal = 0;
levelCompleted = false;
updateScoreText();
}
// Clear all game elements from the current level
function clearLevel() {
// Remove sheep
for (var i = 0; i < game.sheep.length; i++) {
game.removeChild(game.sheep[i]);
}
game.sheep = [];
// Remove cows
for (var i = 0; i < game.cows.length; i++) {
game.removeChild(game.cows[i]);
}
game.cows = [];
// Remove obstacles
for (var i = 0; i < game.obstacles.length; i++) {
game.removeChild(game.obstacles[i]);
}
game.obstacles = [];
// Remove dog
if (game.dog) {
game.removeChild(game.dog);
game.dog = null;
}
// Remove goal
if (activeGoal) {
game.removeChild(activeGoal);
activeGoal = null;
}
// Remove cow goal
if (activeCowGoal) {
game.removeChild(activeCowGoal);
activeCowGoal = null;
}
// Keep grass field between levels but refresh it
if (game.grassField) {
// Add subtle animation to grass when changing levels
tween(game.grassField, {
alpha: 0.7
}, {
duration: 300,
onFinish: function onFinish() {
tween(game.grassField, {
alpha: 1
}, {
duration: 300
});
}
});
}
}
// Update the timer
function updateTimer() {
timeLeft--;
timerText.setText('Time: ' + timeLeft);
if (timeLeft <= 0) {
LK.clearInterval(timerInterval);
endLevel(false);
}
}
// Update the score display
function updateScoreText() {
var level = levels[currentLevel - 1];
// Check if level is defined before accessing its properties
if (level) {
scoreText.setText('Sheep: ' + sheepInGoal + '/' + level.sheepCount);
// Show cow score if there are cows in this level
if (level.cowCount > 0) {
cowScoreText.setText('Cows: ' + cowsInGoal + '/' + level.cowCount);
cowScoreText.alpha = 1;
} else {
cowScoreText.alpha = 0;
}
} else {
// Provide a default text if level is undefined
scoreText.setText('Sheep: ' + sheepInGoal + '/0');
cowScoreText.alpha = 0;
}
}
// End the current level
function endLevel(success) {
LK.clearInterval(timerInterval);
if (success) {
LK.getSound('success').play();
// Add cow points to score
var cowPoints = cowsInGoal * 150; // Cows are worth more points
LK.setScore(LK.getScore() + sheepInGoal * 100 + cowPoints + timeLeft * 10);
if (currentLevel < maxLevels) {
currentLevel++;
LK.setTimeout(function () {
startLevel(currentLevel);
}, 1500);
} else {
// Game completed
LK.showYouWin();
}
} else {
LK.showGameOver();
}
}
// Control the dog with tap/click
game.down = function (x, y) {
if (game.dog && !levelCompleted) {
game.dog.setTarget(x, y);
game.dog.bark();
}
};
// Main game update loop
game.update = function () {
if (levelCompleted) {
return;
}
// Update grass field
if (game.grassField) {
game.grassField.update();
// Create ripple effects when dog moves
if (game.dog && game.dog.moving) {
// Only create ripple occasionally to avoid overwhelming effects
if (LK.ticks % 10 === 0) {
game.grassField.createRipple(game.dog.x, game.dog.y);
}
}
}
// Update all sheep
for (var i = 0; i < game.sheep.length; i++) {
game.sheep[i].update();
}
// Update all cows
for (var i = 0; i < game.cows.length; i++) {
game.cows[i].update();
}
// Update dog
if (game.dog) {
game.dog.update();
}
// Check for sheep in goal area
if (activeGoal) {
sheepInGoal = 0;
for (var i = 0; i < game.sheep.length; i++) {
var sheep = game.sheep[i];
var goalDist = Math.sqrt(Math.pow(sheep.x - activeGoal.x, 2) + Math.pow(sheep.y - activeGoal.y, 2));
if (goalDist < 150) {
sheepInGoal++;
}
}
}
// Check for cows in cow goal area if this level has cows
if (activeCowGoal) {
cowsInGoal = 0;
for (var i = 0; i < game.cows.length; i++) {
var cow = game.cows[i];
var cowGoalDist = Math.sqrt(Math.pow(cow.x - activeCowGoal.x, 2) + Math.pow(cow.y - activeCowGoal.y, 2));
if (cowGoalDist < 150) {
cowsInGoal++;
}
}
}
updateScoreText();
// Check for level completion
var level = levels[currentLevel - 1];
if (level) {
var sheepGoalMet = sheepInGoal >= level.sheepCount;
var cowGoalMet = level.cowCount === 0 || cowsInGoal >= level.cowCount;
if (sheepGoalMet && cowGoalMet && !levelCompleted) {
levelCompleted = true;
endLevel(true);
}
}
};
// Create and show start screen instead of auto-starting
var showStartScreen = function showStartScreen() {
var startScreen = new StartScreen();
startScreen.x = 1024; // Center of screen
startScreen.y = 1366; // Center of screen
game.addChild(startScreen);
// Play background music when showing start screen
LK.playMusic('countryside', {
fade: {
start: 0,
end: 0.8,
duration: 1500
}
});
};
// Call showStartScreen instead of startGame
showStartScreen();
;
top down view of a pixel art dog. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
top down view of a pixel art sheep. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
top down view of pixel art rock. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
top down view of a pixel art cow. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows