/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Dog = Container.expand(function () { var self = Container.call(this); self.speed = 2.5; self.chaseSpeed = 5; self.detectionRadius = 300; self.isChasing = false; self.trailFollowDistance = 150; self.alertRadius = 400; self.hasAlertedGuards = false; self.isResting = false; self.restTimer = 0; self.restDuration = 300; // 5 seconds at 60fps self.chaseTimer = 0; self.chaseDuration = 180; // 3 seconds at 60fps self.stopTimer = 0; self.stopDuration = 120; // 2 seconds at 60fps self.isStopped = false; self.isInKittenMode = false; self.normalSpeed = self.speed; self.normalChaseSpeed = self.chaseSpeed; var dogGraphics = self.attachAsset('dog_normal', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); self.update = function () { // Handle kitten mode changes if (isKittenMode && !self.isInKittenMode) { // Entering kitten mode - multiply speed and change to run texture self.isInKittenMode = true; self.speed = self.normalSpeed * 2; self.chaseSpeed = self.normalChaseSpeed * 2; // Change to run texture self.removeChild(dogGraphics); dogGraphics = self.attachAsset('dog_run', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); self.addChild(dogGraphics); } else if (!isKittenMode && self.isInKittenMode) { // Exiting kitten mode - restore normal speed and texture self.isInKittenMode = false; self.speed = self.normalSpeed; self.chaseSpeed = self.normalChaseSpeed; // Change back to normal texture self.removeChild(dogGraphics); dogGraphics = self.attachAsset('dog_normal', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); self.addChild(dogGraphics); } // Handle rest mode if (self.isResting) { self.restTimer++; // Change to passive texture after 1 second (60 frames) if (self.restTimer === 60) { self.removeChild(dogGraphics); dogGraphics = self.attachAsset('dog_rest', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); self.addChild(dogGraphics); } if (self.restTimer >= self.restDuration) { // Exit rest mode self.isResting = false; self.restTimer = 0; self.hasAlertedGuards = false; // Reset so dog can alert again // Change back to normal texture self.removeChild(dogGraphics); dogGraphics = self.attachAsset('dog_normal', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); self.addChild(dogGraphics); } // Dog doesn't move or detect during rest mode return; } // Check if player is in detection range and not in kitten mode var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var canDetectPlayer = distance < self.detectionRadius && !player.isHiding && !isKittenMode; if (canDetectPlayer) { if (!self.isChasing && !self.isStopped) { // Start chasing self.isChasing = true; self.chaseTimer = 0; self.hasAlertedGuards = false; dogGraphics.tint = 0xFF4444; // Red when chasing // Play dog alert sound when detecting player LK.getSound('dog_alert').play(); } // Handle chase duration if (self.isChasing) { self.chaseTimer++; if (self.chaseTimer >= self.chaseDuration) { // Stop chasing after 3 seconds and enter stop mode self.isChasing = false; self.isStopped = true; self.stopTimer = 0; dogGraphics.tint = 0xFFFFFF; // Remove red tint when stopping } } // Handle stop duration if (self.isStopped) { self.stopTimer++; if (self.stopTimer >= self.stopDuration) { // End stop mode after 2 seconds self.isStopped = false; self.stopTimer = 0; } } // Alert all guards when close enough during chase if (self.isChasing && distance < self.alertRadius && !self.hasAlertedGuards) { self.hasAlertedGuards = true; // Alert all guards for (var i = 0; i < guards.length; i++) { var guard = guards[i]; if (!guard.isAlerted) { guard.isAlerted = true; guard.alertState = 'chase_player'; guard.chaseTimer = 0; guard.alertSpeed = 6; // Change guard to chase texture var currentGraphics = guard.children[0]; guard.removeChild(currentGraphics); var chaseGraphics = guard.attachAsset('guard_chase', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); guard.addChild(chaseGraphics); } } // Enter rest mode for 5 seconds after alerting guards self.isResting = true; self.restTimer = 0; // Change to rest texture self.removeChild(dogGraphics); dogGraphics = self.attachAsset('dog_rest', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); self.addChild(dogGraphics); } // Chase player directly only if chasing and not stopped if (self.isChasing && !self.isStopped) { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.chaseSpeed; self.y += Math.sin(angle) * self.chaseSpeed; } } else if (self.isChasing && distance > self.detectionRadius * 1.5) { // Stop chasing if player gets too far self.isChasing = false; self.isStopped = false; self.chaseTimer = 0; self.stopTimer = 0; self.hasAlertedGuards = false; dogGraphics.tint = 0xFFFFFF; // Remove tint when not chasing } else if (!self.isChasing && !self.isStopped) { // Follow player trail when not actively chasing and not stopped var trailDistance = Math.sqrt(dx * dx + dy * dy); if (trailDistance > self.trailFollowDistance) { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } } // Keep dog within bounds if (self.x < 100) self.x = 100; if (self.x > 1948) self.x = 1948; if (self.y < 100) self.y = 100; if (self.y > 2632) self.y = 2632; // Update dog flip based on movement direction if (dx > 0) { dogGraphics.scaleX = Math.abs(dogGraphics.scaleX); } else { dogGraphics.scaleX = -Math.abs(dogGraphics.scaleX); } }; return self; }); var GoldenMoonRock = Container.expand(function () { var self = Container.call(this); var rockGraphics = self.attachAsset('golden_moon_rock', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9 }); rockGraphics.tint = 0xFFD700; // Golden color self.moveTimer = 0; self.moveDirection = Math.random() * Math.PI * 2; self.speed = 1.5; self.lifeTimer = 0; self.maxLifetime = 1800; // 30 seconds at 60fps self.update = function () { // Golden glow animation - more intense than moonbeam rockGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.2) * 0.3; rockGraphics.scaleX = 1.2 + Math.sin(LK.ticks * 0.12) * 0.2; rockGraphics.scaleY = 1.2 + Math.sin(LK.ticks * 0.12) * 0.2; rockGraphics.rotation += 0.03; // Slow random movement self.moveTimer++; if (self.moveTimer % 90 == 0) { // Change direction every 1.5 seconds self.moveDirection = Math.random() * Math.PI * 2; } self.x += Math.cos(self.moveDirection) * self.speed; self.y += Math.sin(self.moveDirection) * self.speed; // Keep within bounds if (self.x < 80) { self.x = 80; self.moveDirection = Math.random() * Math.PI; } if (self.x > 1968) { self.x = 1968; self.moveDirection = Math.PI + Math.random() * Math.PI; } if (self.y < 80) { self.y = 80; self.moveDirection = Math.PI * 0.5 + Math.random() * Math.PI; } if (self.y > 2652) { self.y = 2652; self.moveDirection = Math.PI * 1.5 + Math.random() * Math.PI; } // Handle lifetime self.lifeTimer++; if (self.lifeTimer >= self.maxLifetime) { // Fade out effect before disappearing tween(rockGraphics, { alpha: 0 }, { duration: 1500, onFinish: function onFinish() { self.destroy(); // Remove from goldenMoonRocks array for (var i = goldenMoonRocks.length - 1; i >= 0; i--) { if (goldenMoonRocks[i] === self) { goldenMoonRocks.splice(i, 1); break; } } } }); } }; return self; }); var Guard = Container.expand(function () { var self = Container.call(this); self.patrolSpeed = 1.5; self.alertSpeed = 4; self.detectionRadius = 250; self.arrestRadius = 100; // Smaller hitbox for arrest - much smaller than detection range self.isAlerted = false; self.alertState = 'none'; // 'none', 'chase_player', 'move_to_moonstone' self.chaseTimer = 0; self.chaseDuration = 180; // 3 seconds at 60fps self.patrolDirection = Math.random() * Math.PI * 2; self.patrolTimer = 0; self.lastInDetectionRange = false; // Add detection range aura var auraGraphics = self.attachAsset('guard_aura', { anchorX: 0.5, anchorY: 0.5, alpha: 0.2, scaleX: 1.0, scaleY: 1.0 }); self.addChild(auraGraphics); var guardGraphics = self.attachAsset('guard', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); self.update = function () { self.patrolTimer++; // Update aura animation and appearance var aura = self.children[0]; // Aura is first child if (aura) { // Pulsing animation var pulseScale = 1.0 + Math.sin(LK.ticks * 0.1) * 0.1; aura.scaleX = pulseScale; aura.scaleY = pulseScale; // Change aura color based on guard state if (self.isAlerted) { if (self.alertState === 'chase_player') { aura.tint = 0xff0000; // Red when chasing player aura.alpha = 0.4; } else if (self.alertState === 'move_to_moonstone') { aura.tint = 0xffff00; // Yellow when moving to moonstone aura.alpha = 0.3; } } else { aura.tint = 0xff4444; // Default red aura.alpha = 0.2; } // Intensify aura when player is in detection range var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var inRange = distance < self.detectionRadius + player.detectionRadius; if (inRange && !player.isHiding && !isKittenMode) { tween(aura, { alpha: 0.6 }, { duration: 200 }); } else { var targetAlpha = self.isAlerted ? 0.4 : 0.2; tween(aura, { alpha: targetAlpha }, { duration: 500 }); } } // Check if player is in detection range and not hiding (and not in kitten mode) var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var currentInDetectionRange = distance < self.detectionRadius + player.detectionRadius && !player.isHiding && !isKittenMode; if (currentInDetectionRange) { // Check if player just entered detection range (transition from false to true) if (!self.lastInDetectionRange && currentInDetectionRange) { var guardSounds = ['stop', 'hey', 'dont_run']; var randomSound = guardSounds[Math.floor(Math.random() * guardSounds.length)]; LK.getSound(randomSound).play(); // Double player speed when alerting a guard player.speed *= 2; // If "don't run" sound is played, alert 3 random guards if (randomSound === 'dont_run') { var availableGuards = []; for (var i = 0; i < guards.length; i++) { if (guards[i] !== self && !guards[i].isAlerted) { availableGuards.push(guards[i]); } } // Alert up to 3 random guards var guardsToAlert = Math.min(3, availableGuards.length); for (var j = 0; j < guardsToAlert; j++) { var randomIndex = Math.floor(Math.random() * availableGuards.length); var guardToAlert = availableGuards[randomIndex]; guardToAlert.isAlerted = true; guardToAlert.alertState = 'move_to_moonstone'; guardToAlert.chaseTimer = 0; guardToAlert.alertSpeed = 6; // Set increased speed for alerted guards // Remove from available list to avoid double selection availableGuards.splice(randomIndex, 1); } } } if (!self.isAlerted) { // Start chasing - reset timer self.chaseTimer = 0; // Increase speed for chase mode self.alertSpeed = 6; // Increased from 4 // Change to chase texture self.removeChild(guardGraphics); guardGraphics = self.attachAsset('guard_chase', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); self.addChild(guardGraphics); } self.isAlerted = true; self.alertState = 'chase_player'; guardGraphics.tint = 0xFF4444; } else if (self.isAlerted) { // Continue chasing for the duration even if player is out of range self.chaseTimer++; if (self.chaseTimer >= self.chaseDuration) { self.isAlerted = false; self.alertState = 'none'; guardGraphics.tint = 0xFFFFFF; self.chaseTimer = 0; // Reset speed to normal patrol speed self.alertSpeed = 4; // Reset to original speed // Revert to normal texture self.removeChild(guardGraphics); guardGraphics = self.attachAsset('guard', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); self.addChild(guardGraphics); } } else { guardGraphics.tint = 0xFFFFFF; } if (self.isAlerted) { var targetX, targetY; if (self.alertState === 'chase_player') { // Target only player (bot disabled) targetX = player.x; targetY = player.y; guardGraphics.tint = 0xFF4444; // Red tint for chasing player } else if (self.alertState === 'move_to_moonstone') { // Move towards moonstone drop position targetX = lastMoonstoneDropX; targetY = lastMoonstoneDropY; guardGraphics.tint = 0x44FF44; // Green tint for moving to moonstone } else { // Default behavior (shouldn't happen when alerted) targetX = player.x; targetY = player.y; } var angle = Math.atan2(targetY - self.y, targetX - self.x); self.x += Math.cos(angle) * self.alertSpeed; self.y += Math.sin(angle) * self.alertSpeed; } else { // Patrol behavior if (self.patrolTimer % 120 == 0) { self.patrolDirection = Math.random() * Math.PI * 2; } self.x += Math.cos(self.patrolDirection) * self.patrolSpeed; self.y += Math.sin(self.patrolDirection) * self.patrolSpeed; // Keep guards within bounds if (self.x < 100) { self.x = 100; self.patrolDirection = Math.random() * Math.PI; } if (self.x > 1948) { self.x = 1948; self.patrolDirection = Math.PI + Math.random() * Math.PI; } if (self.y < 100) { self.y = 100; self.patrolDirection = Math.PI * 0.5 + Math.random() * Math.PI; } if (self.y > 2632) { self.y = 2632; self.patrolDirection = Math.PI * 1.5 + Math.random() * Math.PI; } } // Update guard flip based on movement direction if (self.isAlerted) { // In chase mode, flip based on direction to player if (dx > 0) { guardGraphics.scaleX = Math.abs(guardGraphics.scaleX); // Face right } else { guardGraphics.scaleX = -Math.abs(guardGraphics.scaleX); // Face left } } else { // In patrol mode, flip based on patrol direction var movementX = Math.cos(self.patrolDirection); if (movementX > 0) { guardGraphics.scaleX = Math.abs(guardGraphics.scaleX); // Face right } else { guardGraphics.scaleX = -Math.abs(guardGraphics.scaleX); // Face left } } // Update last detection state self.lastInDetectionRange = distance < self.detectionRadius + player.detectionRadius && !player.isHiding; }; return self; }); var MoonBeam = Container.expand(function () { var self = Container.call(this); var moonbeamGraphics = self.attachAsset('golden_moonstone', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); moonbeamGraphics.tint = 0xFFD700; // Golden color self.moveTimer = 0; self.moveDirection = Math.random() * Math.PI * 2; self.speed = 2; self.lifeTimer = 0; self.maxLifetime = 900; // 15 seconds at 60fps self.update = function () { // Golden glow animation moonbeamGraphics.alpha = 0.6 + Math.sin(LK.ticks * 0.15) * 0.2; moonbeamGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.15; moonbeamGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.15; moonbeamGraphics.rotation += 0.02; // Random movement self.moveTimer++; if (self.moveTimer % 60 == 0) { // Change direction every second self.moveDirection = Math.random() * Math.PI * 2; } self.x += Math.cos(self.moveDirection) * self.speed; self.y += Math.sin(self.moveDirection) * self.speed; // Keep within bounds if (self.x < 60) { self.x = 60; self.moveDirection = Math.random() * Math.PI; } if (self.x > 1988) { self.x = 1988; self.moveDirection = Math.PI + Math.random() * Math.PI; } if (self.y < 60) { self.y = 60; self.moveDirection = Math.PI * 0.5 + Math.random() * Math.PI; } if (self.y > 2672) { self.y = 2672; self.moveDirection = Math.PI * 1.5 + Math.random() * Math.PI; } // Handle lifetime self.lifeTimer++; if (self.lifeTimer >= self.maxLifetime) { // Fade out effect before disappearing tween(moonbeamGraphics, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { self.destroy(); // Remove from moonbeams array for (var i = moonbeams.length - 1; i >= 0; i--) { if (moonbeams[i] === self) { moonbeams.splice(i, 1); break; } } } }); } }; return self; }); var Moonstone = Container.expand(function () { var self = Container.call(this); var moonstoneGraphics = self.attachAsset('moonstone', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.gravity = 0.5; self.bounce = 0.7; self.friction = 0.98; self.isPhysicsActive = false; self.isZigzagBouncing = false; self.zigzagTimer = 0; self.zigzagSpeed = 8; self.zigzagDirection = 0; self.activatePhysics = function () { self.isPhysicsActive = true; // Give initial random velocity when physics activates self.velocityX = (Math.random() - 0.5) * 10; self.velocityY = (Math.random() - 0.5) * 10; }; self.update = function () { // Gentle glow animation moonstoneGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.1) * 0.3; moonstoneGraphics.rotation += 0.02; // Zigzag bouncing behavior if (self.isZigzagBouncing) { self.zigzagTimer++; // Change direction every 30 frames (0.5 seconds) for zigzag effect if (self.zigzagTimer % 30 === 0) { // Create zigzag pattern by alternating direction var zigzagAngle = self.zigzagDirection + Math.PI / 3 * (Math.random() - 0.5); self.velocityX = Math.cos(zigzagAngle) * self.zigzagSpeed; self.velocityY = Math.sin(zigzagAngle) * self.zigzagSpeed; self.zigzagDirection = zigzagAngle; } // Update position with zigzag movement self.x += self.velocityX; self.y += self.velocityY; // Check collision with obstacles during zigzag for (var o = 0; o < obstacles.length; o++) { var obstacle = obstacles[o]; var isColliding = false; if (obstacle.hasCircularHitbox) { // Use circular collision detection for rocks var dx = self.x - obstacle.x; var dy = self.y - obstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); isColliding = distance < obstacle.hitboxRadius + 45; // 45 is moonstone radius } else { // Use default rectangular collision for trees isColliding = self.intersects(obstacle); } if (isColliding) { // Bounce off obstacle by reversing direction if (obstacle.hasCircularHitbox) { // For rocks, calculate proper bounce direction var dx = self.x - obstacle.x; var dy = self.y - obstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var normalX = dx / distance; var normalY = dy / distance; // Reflect velocity based on collision normal var dotProduct = self.velocityX * normalX + self.velocityY * normalY; self.velocityX = self.velocityX - 2 * dotProduct * normalX; self.velocityY = self.velocityY - 2 * dotProduct * normalY; // Push moonstone away from rock wall var pushDistance = obstacle.hitboxRadius + 50; self.x = obstacle.x + normalX * pushDistance; self.y = obstacle.y + normalY * pushDistance; } } else { // For trees, use simple velocity reversal self.velocityX = -self.velocityX; self.velocityY = -self.velocityY; self.zigzagDirection += Math.PI; // Reverse direction // Move away from obstacle to prevent sticking self.x -= self.velocityX; self.y -= self.velocityY; } break; } } // Bounce off boundaries with direction change for zigzag if (self.x <= 45) { self.x = 45; self.velocityX = Math.abs(self.velocityX); self.zigzagDirection = Math.random() * Math.PI / 2; // Random right direction } if (self.x >= 2003) { self.x = 2003; self.velocityX = -Math.abs(self.velocityX); self.zigzagDirection = Math.PI / 2 + Math.random() * Math.PI / 2; // Random left direction } if (self.y <= 45) { self.y = 45; self.velocityY = Math.abs(self.velocityY); self.zigzagDirection = Math.random() * Math.PI; // Random down direction } if (self.y >= 2687) { self.y = 2687; self.velocityY = -Math.abs(self.velocityY); self.zigzagDirection = Math.PI + Math.random() * Math.PI; // Random up direction } // Stop zigzag after 5 seconds if (self.zigzagTimer >= 300) { self.isZigzagBouncing = false; self.zigzagTimer = 0; } } // Ball physics when in kitten mode and not zigzag bouncing else if (self.isPhysicsActive && isKittenMode) { // Apply gravity self.velocityY += self.gravity; // Apply friction self.velocityX *= self.friction; self.velocityY *= self.friction; // Update position self.x += self.velocityX; self.y += self.velocityY; // Skip obstacle collision detection in kitten mode to allow free physics movement // Only bounce off screen boundaries if (self.x <= 45) { self.x = 45; self.velocityX = -self.velocityX * self.bounce; } if (self.x >= 2003) { self.x = 2003; self.velocityX = -self.velocityX * self.bounce; } if (self.y <= 45) { self.y = 45; self.velocityY = -self.velocityY * self.bounce; } if (self.y >= 2687) { self.y = 2687; self.velocityY = -self.velocityY * self.bounce; } } }; return self; }); var Obstacle = Container.expand(function (assetType) { var self = Container.call(this); var obstacleGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); // Add circular hitbox for rocks if (assetType === 'rock') { self.hitboxRadius = 90; // Circular hitbox radius for rocks self.hasCircularHitbox = true; } else { self.hasCircularHitbox = false; } // Add transparency tracking for trees self.isPlayerTouching = false; self.lastPlayerTouching = false; return self; }); var Player = Container.expand(function () { var self = Container.call(this); self.transformationLevel = 0; self.speed = 3; self.isHiding = false; self.detectionRadius = 120; self.lastX = 0; self.lastY = 0; self.footstepTimer = 0; self.footstepInterval = 15; // Play footstep every 15 frames when moving self.isWalking = false; self.lastWalkingState = false; var playerGraphics = self.attachAsset('texture_phase0', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.05, scaleY: 0.05 }); self.transform = function () { self.transformationLevel++; // Remove all current graphics to prevent duplication while (self.children.length > 0) { self.removeChild(self.children[0]); } // Update appearance based on transformation level if (self.transformationLevel >= 7) { // Full werewolf playerGraphics = self.attachAsset('werewolf', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.detectionRadius = 80; } else { // Use phase-specific texture with walking state var textureId = 'texture_phase' + self.transformationLevel; if (self.isWalking) { textureId += '_walk'; } playerGraphics = self.attachAsset(textureId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.05, scaleY: 0.05 }); // Adjust speed and detection based on transformation self.speed = 3 + self.transformationLevel * 0.7; self.detectionRadius = 120 - self.transformationLevel * 5; } LK.getSound('transformation').play(); LK.effects.flashObject(self, 0xE6E6FA, 1000); }; self.updateTexture = function () { // Check if walking state changed if (self.lastWalkingState !== self.isWalking) { self.lastWalkingState = self.isWalking; // Remove current graphics var currentGraphics = self.children[0]; self.removeChild(currentGraphics); var newGraphics; if (isKittenMode || isGiantMode) { // Handle kitten mode textures (also used for giant mode) var kittenTextureId = 'kitten'; if (self.isWalking) { kittenTextureId += '_walk'; } newGraphics = self.attachAsset(kittenTextureId, { anchorX: 0.5, anchorY: 0.5 }); } else if (self.transformationLevel >= 7) { // Werewolf doesn't change texture when walking newGraphics = self.attachAsset('werewolf', { anchorX: 0.5, anchorY: 0.5 }); } else { // Choose texture based on walking state and transformation level var textureId = 'texture_phase' + self.transformationLevel; if (self.isWalking) { textureId += '_walk'; } newGraphics = self.attachAsset(textureId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.05, scaleY: 0.05 }); } self.addChild(newGraphics); // Apply the last facing direction to the new graphics if (newGraphics && typeof playerLastFacingRight !== 'undefined') { if (playerLastFacingRight) { newGraphics.scaleX = Math.abs(newGraphics.scaleX); } else { newGraphics.scaleX = -Math.abs(newGraphics.scaleX); } } } }; self.checkHiding = function () { self.isHiding = false; // Check if player is behind any obstacle for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; var dx = self.x - obstacle.x; var dy = self.y - obstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { self.isHiding = true; break; } } }; return self; }); var PlayerBot = Container.expand(function () { var self = Container.call(this); self.transformationLevel = 0; self.botTransformationLevel = 0; // Independent transformation level for bot self.speed = 2.5; // Slightly slower than player self.isHiding = false; self.detectionRadius = 120; self.followDistance = 150; // Distance to maintain from player self.targetX = 0; self.targetY = 0; self.moveTimer = 0; self.footstepTimer = 0; self.footstepInterval = 20; // Slightly different timing than player self.lastX = 0; self.lastY = 0; self.isInBotKittenMode = false; // Independent kitten mode for bot self.botKittenModeTimer = 0; self.botKittenModeDuration = 1200; // 20 seconds at 60fps - longer than player self.botEffectTimer = 0; self.botEffectInterval = 480; // 8 seconds - bot activates effects more frequently self.canAlertGuards = true; // Bot can alert guards self.hasAlertedGuards = false; // Track if bot has alerted guards self.alertCooldown = 0; // Cooldown before bot can alert again self.alertCooldownDuration = 600; // 10 seconds at 60fps self.maintainDistance = false; // Whether bot should maintain distance from player self.distanceFromPlayer = 300; // Distance to maintain when keeping away self.isCaught = false; // Whether bot has been caught by guards self.canMove = true; // Whether bot can move (disabled when caught) self.mode = 'normal'; // Bot mode: 'normal' or 'caught' var botGraphics = self.attachAsset('texture_phase0', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.04, // Slightly smaller than player scaleY: 0.04, alpha: 0.8 // Slightly transparent to distinguish from player }); // Apply a slight blue tint to distinguish from player botGraphics.tint = 0xADD8E6; self.transform = function () { self.transformationLevel++; // Remove current graphics self.removeChild(botGraphics); // Update appearance based on transformation level if (self.transformationLevel >= 7) { // Full werewolf botGraphics = self.attachAsset('werewolf', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.speed = 7; // Slightly slower than player werewolf self.detectionRadius = 80; } else { // Use phase-specific texture var textureId = 'texture_phase' + self.transformationLevel; botGraphics = self.attachAsset(textureId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.04, scaleY: 0.04, alpha: 0.8 }); // Adjust speed and detection based on transformation self.speed = 2.5 + self.transformationLevel * 0.6; self.detectionRadius = 120 - self.transformationLevel * 5; } // Apply blue tint to distinguish from player botGraphics.tint = 0xADD8E6; // Re-add the updated graphics self.addChild(botGraphics); LK.effects.flashObject(self, 0xADD8E6, 800); }; self.botTransform = function () { self.botTransformationLevel++; // Remove current graphics self.removeChild(botGraphics); // Update appearance based on bot's independent transformation level if (self.botTransformationLevel >= 7) { // Full werewolf botGraphics = self.attachAsset('werewolf', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.speed = 8; // Bot werewolf is faster than regular bot self.detectionRadius = 70; } else { // Use phase-specific texture var textureId = 'texture_phase' + self.botTransformationLevel; botGraphics = self.attachAsset(textureId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.04, scaleY: 0.04, alpha: 0.8 }); // Adjust speed and detection based on bot's transformation self.speed = 2.5 + self.botTransformationLevel * 0.8; self.detectionRadius = 120 - self.botTransformationLevel * 6; } // Apply purple tint to distinguish bot transformations botGraphics.tint = 0x9370DB; // Re-add the updated graphics self.addChild(botGraphics); LK.effects.flashObject(self, 0x9370DB, 1000); // Bot transformation effect tween(self, { scaleX: (self.scaleX || 1) + 0.15, scaleY: (self.scaleY || 1) + 0.15 }, { duration: 800, easing: tween.easeOut }); }; self.activateBotKittenMode = function () { if (!self.isInBotKittenMode) { self.isInBotKittenMode = true; self.botKittenModeTimer = 0; // Transform to kitten with unique bot styling var currentBotGraphics = self.children[0]; self.removeChild(currentBotGraphics); var botKittenGraphics = self.attachAsset('kitten', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9 }); // Apply unique purple-blue tint for bot kitten mode botKittenGraphics.tint = 0x6A5ACD; self.addChild(botKittenGraphics); // Unique scaling for bot kitten mode tween(self, { scaleX: (self.scaleX || 1) * 0.4, scaleY: (self.scaleY || 1) * 0.4 }, { duration: 600, easing: tween.easeOut }); // Play unique bot meow with different pitch var botMeow = LK.getSound('meow'); botMeow.volume = 0.6; botMeow.play(); LK.effects.flashObject(self, 0x6A5ACD, 1200); } }; self.checkHiding = function () { self.isHiding = false; // Check if bot is behind any obstacle for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; var dx = self.x - obstacle.x; var dy = self.y - obstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { self.isHiding = true; break; } } }; self.update = function () { // Handle bot's independent kitten mode timer if (self.isInBotKittenMode) { self.botKittenModeTimer++; if (self.botKittenModeTimer >= self.botKittenModeDuration) { // Revert from bot kitten mode self.isInBotKittenMode = false; self.botKittenModeTimer = 0; // Change bot back to current bot transformation level var currentBotGraphics = self.children[0]; self.removeChild(currentBotGraphics); var revertGraphics; if (self.botTransformationLevel >= 7) { revertGraphics = self.attachAsset('werewolf', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); } else { var textureId = 'texture_phase' + self.botTransformationLevel; revertGraphics = self.attachAsset(textureId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.04, scaleY: 0.04, alpha: 0.8 }); } revertGraphics.tint = 0x9370DB; // Purple tint for bot transformations self.addChild(revertGraphics); // Restore bot size when reverting from kitten mode tween(self, { scaleX: 1, scaleY: 1 }, { duration: 600, easing: tween.easeOut }); LK.effects.flashObject(self, 0x9370DB, 900); } } // Bot independent effect timer self.botEffectTimer++; if (self.botEffectTimer >= self.botEffectInterval) { self.botEffectTimer = 0; // Bot has 30% chance to transform independently if (Math.random() < 0.3 && self.botTransformationLevel < 7) { self.botTransform(); } // Bot has 25% chance to activate kitten mode independently else if (Math.random() < 0.25 && !self.isInBotKittenMode) { self.activateBotKittenMode(); } } // Handle alert cooldown if (self.alertCooldown > 0) { self.alertCooldown--; if (self.alertCooldown <= 0) { self.canAlertGuards = true; self.hasAlertedGuards = false; self.maintainDistance = false; // Stop maintaining distance after cooldown } } // Check if bot should alert guards (only when bot enters a guard's detection range) var shouldAlert = false; if (self.canAlertGuards && !self.hasAlertedGuards && !isKittenMode) { // Check if bot is within any guard's detection range for (var g = 0; g < guards.length; g++) { var guard = guards[g]; var guardDx = self.x - guard.x; var guardDy = self.y - guard.y; var guardDistance = Math.sqrt(guardDx * guardDx + guardDy * guardDy); // Check if bot is within this guard's detection radius if (guardDistance < guard.detectionRadius + self.detectionRadius) { shouldAlert = true; break; } } } if (shouldAlert) { // Bot enters guard detection range - alert only the specific guard self.hasAlertedGuards = true; self.canAlertGuards = false; self.alertCooldown = self.alertCooldownDuration; self.maintainDistance = true; // Start maintaining distance // Alert only the guard whose zone the bot entered for (var g = 0; g < guards.length; g++) { var guard = guards[g]; var guardDx = self.x - guard.x; var guardDy = self.y - guard.y; var guardDistance = Math.sqrt(guardDx * guardDx + guardDy * guardDy); // Only alert this specific guard if bot is in its detection range if (guardDistance < guard.detectionRadius + self.detectionRadius && !guard.isAlerted) { guard.isAlerted = true; guard.alertState = 'chase_player'; guard.chaseTimer = 0; guard.alertSpeed = 6; // Change guard to chase texture var currentGraphics = guard.children[0]; guard.removeChild(currentGraphics); var chaseGraphics = guard.attachAsset('guard_chase', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); guard.addChild(chaseGraphics); break; // Only alert one guard - the one whose zone was entered } } // Play alert sound LK.getSound('dog_alert').play(); // Flash bot to indicate it alerted guards LK.effects.flashObject(self, 0xFF4444, 800); } // Store last position for movement detection self.lastX = self.x; self.lastY = self.y; // Skip movement and other behaviors if bot is caught if (self.isCaught || !self.canMove) { return; } // Free movement behavior - check for moonstones first var nearestMoonstone = null; var shortestDistance = Infinity; // Find nearest moonstone for (var m = 0; m < moonstones.length; m++) { var moonstone = moonstones[m]; var mstoneDx = moonstone.x - self.x; var mstoneDy = moonstone.y - self.y; var mstoneDistance = Math.sqrt(mstoneDx * mstoneDx + mstoneDy * mstoneDy); if (mstoneDistance < shortestDistance) { shortestDistance = mstoneDistance; nearestMoonstone = moonstone; } } // Calculate distance to player for bot logic var playerDistance = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); // Determine target based on bot state if (self.maintainDistance) { // Maintain distance from player when bot has alerted guards if (playerDistance < self.distanceFromPlayer) { // Move away from player var awayAngle = Math.atan2(self.y - player.y, self.x - player.x); self.targetX = self.x + Math.cos(awayAngle) * self.speed * 2; // Move away faster self.targetY = self.y + Math.sin(awayAngle) * self.speed * 2; } else { // Maintain current position if far enough self.targetX = self.x; self.targetY = self.y; } } else if (nearestMoonstone && shortestDistance < 600) { // Move towards nearest moonstone var mstoneDx = nearestMoonstone.x - self.x; var mstoneDy = nearestMoonstone.y - self.y; var mstoneAngle = Math.atan2(mstoneDy, mstoneDx); self.targetX = self.x + Math.cos(mstoneAngle) * self.speed * 1.5; // Faster when seeking moonstone self.targetY = self.y + Math.sin(mstoneAngle) * self.speed * 1.5; } else { // Free roaming movement when no moonstone nearby self.moveTimer++; if (self.moveTimer % 120 === 0) { // Change direction every 2 seconds for more freedom var randomAngle = Math.random() * Math.PI * 2; var roamDistance = 80 + Math.random() * 120; // Vary roaming distance self.targetX = self.x + Math.cos(randomAngle) * roamDistance; self.targetY = self.y + Math.sin(randomAngle) * roamDistance; } } // Move towards target position var targetDx = self.targetX - self.x; var targetDy = self.targetY - self.y; var targetDistance = Math.sqrt(targetDx * targetDx + targetDy * targetDy); if (targetDistance > 1) { var moveX = targetDx / targetDistance * self.speed; var moveY = targetDy / targetDistance * self.speed; self.x += moveX; self.y += moveY; } // Keep bot within bounds if (self.x < 40) self.x = 40; if (self.x > 2008) self.x = 2008; if (self.y < 40) self.y = 40; if (self.y > 2692) self.y = 2692; // Check collision with rock obstacles (walls) for (var o = 0; o < obstacles.length; o++) { var obstacle = obstacles[o]; if (obstacle.hasCircularHitbox) { // Only check rocks as walls var obstacleDx = self.x - obstacle.x; var obstacleDy = self.y - obstacle.y; var obstacleDistance = Math.sqrt(obstacleDx * obstacleDx + obstacleDy * obstacleDy); var botRadius = 35; // Bot collision radius if (obstacleDistance < obstacle.hitboxRadius + botRadius) { // Bot is colliding with rock wall - push back if (obstacleDistance > 0) { var normalX = obstacleDx / obstacleDistance; var normalY = obstacleDy / obstacleDistance; var pushDistance = obstacle.hitboxRadius + botRadius; self.x = obstacle.x + normalX * pushDistance; self.y = obstacle.y + normalY * pushDistance; } } } } // Play footstep sounds when bot is moving var movementX = self.x - self.lastX; var movementY = self.y - self.lastY; var isMoving = Math.sqrt(movementX * movementX + movementY * movementY) > 0.5; if (isMoving) { self.footstepTimer++; if (self.footstepTimer >= self.footstepInterval) { // Play footstep at lower volume for bot var footstepSound = LK.getSound('footsteps'); footstepSound.volume = 0.3; footstepSound.play(); self.footstepTimer = 0; } } else { self.footstepTimer = 0; } // Flip bot based on movement direction if (self.children.length > 0 && self.children[0]) { if (movementX > 0) { // Moving right - face right self.children[0].scaleX = Math.abs(self.children[0].scaleX); } else if (movementX < 0) { // Moving left - face left self.children[0].scaleX = -Math.abs(self.children[0].scaleX); } } // Update hiding status self.checkHiding(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x191970 }); /**** * Game Code ****/ // Game variables var player; var playerBot; var moonstones = []; var guards = []; var obstacles = []; var moonbeams = []; var goldenMoonRocks = []; var dragNode = null; var moonstonesCollected = 0; var playerLevel = 0; // Independent level counter for player transformations var botLevel = 0; // Independent level counter for bot transformations var totalMoonstones = 7; var lastMoonstoneDropX = 0; var lastMoonstoneDropY = 0; var moonbeamEventTimer = 0; var moonbeamEventInterval = 900; // 15 seconds at 60fps var goldenRockTimer = 0; var goldenRockInterval = 1800; // 30 seconds at 60fps var isGiantMode = false; var giantModeTimer = 0; var giantModeDuration = 900; // 15 seconds at 60fps var hasTransformedToKitten = storage.hasTransformedToKitten || false; var isKittenMode = false; var kittenModeTimer = 0; var kittenModeDuration = 900; // 15 seconds at 60fps var dog = null; var dogSpawned = false; var playerLastFacingRight = true; // Track player's last facing direction // Create UI var scoreText = new Text2('Moonstones: 0/7', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var transformationText = new Text2('Human Child', { size: 50, fill: 0xE6E6FA }); transformationText.anchor.set(0.5, 0); transformationText.y = 80; LK.gui.top.addChild(transformationText); // Create loading screen that covers the entire game var loadingScreen = LK.gui.center.addChild(LK.getAsset('loadingScreen', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, width: 2048, height: 2732 })); // Keep loading screen visible for exactly 3 seconds, then fade out LK.setTimeout(function () { tween(loadingScreen, { alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Remove loading screen from GUI and destroy to free memory LK.gui.center.removeChild(loadingScreen); loadingScreen.destroy(); loadingScreen = null; // Clear reference for garbage collection } }); }, 3000); // Create grass ground var grass = game.addChild(LK.getAsset('grass', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 0.82, scaleY: 1.16 })); // Create player player = game.addChild(new Player()); player.x = 1024; player.y = 1366; // Bot spawning disabled playerBot = null; // Generate only one moonstone initially var moonstone = game.addChild(new Moonstone()); moonstone.x = 150 + Math.random() * 1748; moonstone.y = 150 + Math.random() * 2432; moonstones.push(moonstone); // Function to spawn moonbeam at random location function spawnMoonbeam() { var moonbeam = game.addChild(new MoonBeam()); moonbeam.x = 60 + Math.random() * 1928; moonbeam.y = 60 + Math.random() * 2612; moonbeams.push(moonbeam); // Add entrance effect moonbeam.children[0].alpha = 0; tween(moonbeam.children[0], { alpha: 0.8 }, { duration: 1000, easing: tween.easeOut }); } // Function to spawn golden moon rock at random location function spawnGoldenMoonRock() { var goldenRock = game.addChild(new GoldenMoonRock()); var finalX = 80 + Math.random() * 1888; var finalY = 80 + Math.random() * 2572; // Start golden rock above screen goldenRock.x = finalX; goldenRock.y = -150; goldenMoonRocks.push(goldenRock); // Create blue trail effect during fall var trailPoints = []; var trailContainer = game.addChild(new Container()); // Add dramatic entrance effect with falling motion goldenRock.children[0].alpha = 0; goldenRock.children[0].scaleX = 0; goldenRock.children[0].scaleY = 0; // Animate falling with trail tween(goldenRock, { y: finalY }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Clean up trail after landing tween(trailContainer, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { trailContainer.destroy(); } }); } }); // Animate appearance tween(goldenRock.children[0], { alpha: 0.9, scaleX: 1.2, scaleY: 1.2 }, { duration: 1200, easing: tween.easeOut }); // Create trail effect during fall var trailTimer = 0; var _createTrail = function createTrail() { if (goldenRock.y < finalY - 50) { // Create trail particle var trailParticle = trailContainer.addChild(LK.getAsset('golden_moonstone', { anchorX: 0.5, anchorY: 0.5, x: goldenRock.x + (Math.random() - 0.5) * 20, y: goldenRock.y + (Math.random() - 0.5) * 20, scaleX: 0.3, scaleY: 0.3, alpha: 0.8 })); trailParticle.tint = 0x4169E1; // Bright blue color // Fade out trail particle tween(trailParticle, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { trailContainer.removeChild(trailParticle); } }); // Schedule next trail particle trailTimer++; if (trailTimer < 40) { // Create trail for first 2 seconds LK.setTimeout(_createTrail, 50); } } }; _createTrail(); } // Function to spawn new moonstone from the sky function spawnMoonstoneFromSky() { var newMoonstone = game.addChild(new Moonstone()); newMoonstone.x = 150 + Math.random() * 1748; newMoonstone.y = -100; // Start above the screen var finalY = 150 + Math.random() * 2432; // Store the drop position for guard alerting lastMoonstoneDropX = newMoonstone.x; lastMoonstoneDropY = finalY; moonstones.push(newMoonstone); // Activate physics if player is in kitten mode if (isKittenMode) { newMoonstone.activatePhysics(); } // Animate falling from sky tween(newMoonstone, { y: finalY }, { duration: 2000, easing: tween.easeOut, onComplete: function onComplete() { // Alert all guards to move towards the dropped moonstone for (var i = 0; i < guards.length; i++) { var guard = guards[i]; guard.isAlerted = true; guard.alertState = 'move_to_moonstone'; guard.chaseTimer = 0; guard.alertSpeed = 6; // Change to chase texture var currentGraphics = guard.children[0]; guard.removeChild(currentGraphics); var chaseGraphics = guard.attachAsset('guard_chase', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); guard.addChild(chaseGraphics); } } }); } // Generate obstacles for hiding - rocks first, then guards, then trees (so trees appear above guards and guards above rocks) // Generate rocks first (lowest z-index) for (var i = 0; i < 7; i++) { var rock = game.addChild(new Obstacle('rock')); rock.x = 200 + Math.random() * 1648; rock.y = 200 + Math.random() * 2332; obstacles.push(rock); } // Generate guards second so they appear above rocks but below trees for (var i = 0; i < 1; i++) { var guard = game.addChild(new Guard()); // Spawn guards far from player spawn (1024, 1366) var angle = Math.random() * Math.PI * 2; var distance = 1200 + Math.random() * 800; // 1200-2000 pixels away from center guard.x = 1024 + Math.cos(angle) * distance; guard.y = 1366 + Math.sin(angle) * distance; // Keep guards within game bounds if (guard.x < 100) guard.x = 100; if (guard.x > 1948) guard.x = 1948; if (guard.y < 100) guard.y = 100; if (guard.y > 2632) guard.y = 2632; guards.push(guard); } // Generate trees second (higher z-index, appear above rocks) for (var i = 0; i < 8; i++) { // Randomly choose between classic and new tree texture var treeType = Math.random() < 0.5 ? 'tree' : 'tree_new'; var tree = game.addChild(new Obstacle(treeType)); tree.x = 200 + Math.random() * 1648; tree.y = 200 + Math.random() * 2332; obstacles.push(tree); } // Start ambient music LK.playMusic('ambient'); function updateTransformationUI() { var transformationNames = ['Human Child', 'Growing Tail', 'Wolf Ears', 'Sharp Claws', 'Wolf Paws', 'Wolf Snout', 'Growing Fur', 'Full Werewolf']; transformationText.setText(transformationNames[player.transformationLevel] || 'Full Werewolf'); } function handleMove(x, y, obj) { if (dragNode) { // Track previous position for flip detection var previousX = dragNode.x; var previousY = dragNode.y; // Calculate desired movement var deltaX = x - dragNode.x; var deltaY = y - dragNode.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Check if player is actually moving var isMoving = distance > 0.5; // Only consider significant movement // Update player walking state and texture if (dragNode === player) { player.isWalking = isMoving; player.updateTexture(); // If player stops moving, set a timeout to return to idle texture if (!isMoving && player.isWalking !== false) { LK.setTimeout(function () { // Check if player is still not moving after timeout if (!player.isWalking) { player.isWalking = false; player.updateTexture(); } }, 200); // 200ms delay before returning to idle texture } } // Apply speed limit to prevent teleportation var maxSpeed = 15; // Maximum pixels per frame if (distance > maxSpeed) { // Normalize the movement vector and apply speed limit var normalizedX = deltaX / distance; var normalizedY = deltaY / distance; dragNode.x += normalizedX * maxSpeed; dragNode.y += normalizedY * maxSpeed; } else { dragNode.x = x; dragNode.y = y; } // Keep player within bounds if (dragNode.x < 40) dragNode.x = 40; if (dragNode.x > 2008) dragNode.x = 2008; if (dragNode.y < 40) dragNode.y = 40; if (dragNode.y > 2692) dragNode.y = 2692; // Check collision with rock obstacles (walls) if (dragNode === player) { for (var o = 0; o < obstacles.length; o++) { var obstacle = obstacles[o]; if (obstacle.hasCircularHitbox) { // Only check rocks as walls var dx = dragNode.x - obstacle.x; var dy = dragNode.y - obstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); var playerRadius = 40; // Player collision radius if (distance < obstacle.hitboxRadius + playerRadius) { // Player is colliding with rock wall - push back if (distance > 0) { var normalX = dx / distance; var normalY = dy / distance; var pushDistance = obstacle.hitboxRadius + playerRadius; dragNode.x = obstacle.x + normalX * pushDistance; dragNode.y = obstacle.y + normalY * pushDistance; } } } } } // Play footstep sounds when player is moving if (dragNode === player && isMoving) { dragNode.footstepTimer++; if (dragNode.footstepTimer >= dragNode.footstepInterval) { LK.getSound('footsteps').play(); dragNode.footstepTimer = 0; } } else if (dragNode === player && !isMoving) { // Reset footstep timer when not moving dragNode.footstepTimer = 0; } // Flip player based on movement direction if (dragNode === player && player.children.length > 0 && player.children[0]) { var movementX = dragNode.x - previousX; if (movementX > 0) { // Moving right - face right player.children[0].scaleX = Math.abs(player.children[0].scaleX); playerLastFacingRight = true; } else if (movementX < 0) { // Moving left - face left player.children[0].scaleX = -Math.abs(player.children[0].scaleX); playerLastFacingRight = false; } } } } game.move = handleMove; game.down = function (x, y, obj) { dragNode = player; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; // When player stops being dragged, return to idle texture after short delay if (player) { LK.setTimeout(function () { player.isWalking = false; player.updateTexture(); }, 100); // 100ms delay to return to idle texture } }; game.update = function () { // Spawn moonbeam only once per game (only if not already spawned) if (!hasTransformedToKitten && moonbeams.length === 0) { moonbeamEventTimer++; if (moonbeamEventTimer >= moonbeamEventInterval) { spawnMoonbeam(); moonbeamEventTimer = 0; } } // Spawn golden moon rock every 20 seconds - DISABLED // goldenRockTimer++; // if (goldenRockTimer >= goldenRockInterval) { // spawnGoldenMoonRock(); // goldenRockTimer = 0; // } // Update kitten mode timer if (isKittenMode) { kittenModeTimer++; if (kittenModeTimer >= kittenModeDuration) { // Revert from kitten mode isKittenMode = false; kittenModeTimer = 0; // Update player texture back to normal transformation (respecting walking state) player.updateTexture(); // Restore player size when reverting from kitten mode tween(player, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeOut }); LK.effects.flashObject(player, 0xE6E6FA, 1000); // Make guard detection ranges visible again when exiting kitten mode for (var g = 0; g < guards.length; g++) { var guard = guards[g]; var aura = guard.children[0]; // Aura is first child if (aura) { aura.visible = true; } } // Bot kitten mode revert disabled } } // Update giant mode timer if (isGiantMode) { giantModeTimer++; if (giantModeTimer >= giantModeDuration) { // Revert from giant mode isGiantMode = false; giantModeTimer = 0; // Update player texture back to normal transformation player.updateTexture(); // Restore player size when reverting from giant mode tween(player, { scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.easeOut }); LK.effects.flashObject(player, 0xFFD700, 1000); } } // Check golden moon rock collision (only if not in kitten mode) - DISABLED // if (!isKittenMode) { // for (var gr = goldenMoonRocks.length - 1; gr >= 0; gr--) { // var goldenRock = goldenMoonRocks[gr]; // if (player.intersects(goldenRock)) { // // Transform player to giant mode // isGiantMode = true; // giantModeTimer = 0; // // Update player texture to kitten mode (used for giant mode) // player.updateTexture(); // // Multiply player size by 20 (increased limit) // tween(player, { // scaleX: (player.scaleX || 1) * 20, // scaleY: (player.scaleY || 1) * 20 // }, { // duration: 800, // easing: tween.easeOut // }); // // Play transformation sound and effects // LK.getSound('transformation').play(); // LK.effects.flashObject(player, 0xFFD700, 1500); // // Remove the golden moon rock that was touched // goldenRock.destroy(); // goldenMoonRocks.splice(gr, 1); // break; // } // } // } // Update player hiding status player.checkHiding(); // Check moonbeam collision (only if not already in kitten mode or giant mode) if (!isKittenMode && !isGiantMode) { for (var m = moonbeams.length - 1; m >= 0; m--) { var moonbeam = moonbeams[m]; if (player.intersects(moonbeam)) { // If giant mode is active, just remove the moonbeam without activating kitten mode if (isGiantMode) { // Remove the moonbeam without any transformation moonbeam.destroy(); moonbeams.splice(m, 1); break; } else { // Transform player to kitten temporarily isKittenMode = true; kittenModeTimer = 0; // Update player texture to kitten mode (respecting walking state) player.updateTexture(); // Reduce player size by half when entering kitten mode tween(player, { scaleX: (player.scaleX || 1) * 0.5, scaleY: (player.scaleY || 1) * 0.5 }, { duration: 500, easing: tween.easeOut }); // Play meow sound for kitten transformation LK.getSound('meow').play(); // Activate physics for all existing moonstones for (var p = 0; p < moonstones.length; p++) { moonstones[p].activatePhysics(); } // Make all guards passive for (var k = 0; k < guards.length; k++) { var guard = guards[k]; guard.isAlerted = false; guard.alertState = 'none'; guard.chaseTimer = 0; // Reset guard graphics to normal var currentGuardGraphics = guard.children[0]; guard.removeChild(currentGuardGraphics); var normalGraphics = guard.attachAsset('guard', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); guard.addChild(normalGraphics); normalGraphics.tint = 0xFFFFFF; } // Remove the moonbeam that was touched moonbeam.destroy(); moonbeams.splice(m, 1); LK.effects.flashObject(player, 0xffa500, 1000); break; } } } } // Check moonstone interactions if (!isKittenMode) { // Normal collection when not in kitten mode for (var i = moonstones.length - 1; i >= 0; i--) { var moonstone = moonstones[i]; var collected = false; // Check player collection (only if not in giant mode) if (player.intersects(moonstone) && !isGiantMode) { moonstone.destroy(); moonstones.splice(i, 1); moonstonesCollected++; playerLevel++; // Player collected - increment player level player.transformationLevel = Math.min(playerLevel - 1, 7); // Set player transformation level player.transform(); LK.getSound('collect').play(); LK.setScore(moonstonesCollected); scoreText.setText('Moonstones: ' + moonstonesCollected + '/' + totalMoonstones); updateTransformationUI(); } // Bot collection disabled // Spawn dog when collecting 4th moonstone if (moonstonesCollected === 4 && !dogSpawned) { dogSpawned = true; dog = game.addChild(new Dog()); // Spawn dog far from player var angle = Math.random() * Math.PI * 2; var distance = 800 + Math.random() * 400; dog.x = player.x + Math.cos(angle) * distance; dog.y = player.y + Math.sin(angle) * distance; // Keep dog within bounds if (dog.x < 100) dog.x = 100; if (dog.x > 1948) dog.x = 1948; if (dog.y < 100) dog.y = 100; if (dog.y > 2632) dog.y = 2632; } // Increase player size slightly with smooth animation when player collects if (playerLevel > 0 && playerLevel <= 7) { var newScale; if (isGiantMode) { // No size limit in giant mode newScale = (player.scaleX || 1) + 0.05; } else { // Limited growth when not in giant mode newScale = Math.min((player.scaleX || 1) + 0.05, 1.35); // Cap maximum scale at 1.35 } tween(player, { scaleX: newScale, scaleY: newScale }, { duration: 500, easing: tween.easeOut }); } // Spawn new moonstone from sky if there are none on screen and haven't won yet if (moonstones.length === 0 && moonstonesCollected < totalMoonstones) { spawnMoonstoneFromSky(); } // Check win condition if (moonstonesCollected >= totalMoonstones) { LK.showYouWin(); return; } // Add more guards as player transforms - insert at lower z-index to appear below trees if (moonstonesCollected % 2 == 0 && guards.length < 8) { var newGuard = new Guard(); // Find the index where obstacles start to insert guard before them var obstacleIndex = -1; for (var idx = 0; idx < game.children.length; idx++) { if (obstacles.indexOf(game.children[idx]) !== -1) { obstacleIndex = idx; break; } } // Insert guard before obstacles if found, otherwise add normally if (obstacleIndex !== -1) { game.addChildAt(newGuard, obstacleIndex); } else { game.addChild(newGuard); } // Spawn new guards far from current player position var angle = Math.random() * Math.PI * 2; var distance = 1000 + Math.random() * 600; // 1000-1600 pixels away from player newGuard.x = player.x + Math.cos(angle) * distance; newGuard.y = player.y + Math.sin(angle) * distance; // Keep guards within game bounds if (newGuard.x < 100) newGuard.x = 100; if (newGuard.x > 1948) newGuard.x = 1948; if (newGuard.y < 100) newGuard.y = 100; if (newGuard.y > 2632) newGuard.y = 2632; guards.push(newGuard); } } } else { // Collision detection in kitten mode - trigger zigzag bouncing for (var i = 0; i < moonstones.length; i++) { var moonstone = moonstones[i]; if (moonstone.lastWasColliding === undefined) moonstone.lastWasColliding = false; var currentColliding = player.intersects(moonstone); // Check if collision just started (transition from false to true) if (!moonstone.lastWasColliding && currentColliding) { // Start zigzag bouncing behavior moonstone.isZigzagBouncing = true; moonstone.zigzagTimer = 0; moonstone.zigzagSpeed = 8; moonstone.zigzagDirection = Math.random() * Math.PI * 2; // Override physics with zigzag movement moonstone.velocityX = Math.cos(moonstone.zigzagDirection) * moonstone.zigzagSpeed; moonstone.velocityY = Math.sin(moonstone.zigzagDirection) * moonstone.zigzagSpeed; } moonstone.lastWasColliding = currentColliding; } } // Check guard capture (only if not in kitten mode and not in giant mode) if (!isKittenMode && !isGiantMode) { for (var g = 0; g < guards.length; g++) { var guard = guards[g]; // Check player capture using smaller arrest hitbox var guardDx = player.x - guard.x; var guardDy = player.y - guard.y; var guardDistance = Math.sqrt(guardDx * guardDx + guardDy * guardDy); var playerRadius = 40; // Player collision radius if (guardDistance < guard.arrestRadius + playerRadius) { // Change player texture to caught state var currentPlayerGraphics = player.children[0]; player.removeChild(currentPlayerGraphics); var caughtGraphics = player.attachAsset('player_caught', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1 }); player.addChild(caughtGraphics); LK.getSound('caught').play(); LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); return; } // Bot capture and rescue mechanics disabled } // Check dog capture if (dog && player.intersects(dog)) { // Change player texture to caught state var currentPlayerGraphics = player.children[0]; player.removeChild(currentPlayerGraphics); var caughtGraphics = player.attachAsset('player_caught', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1 }); player.addChild(caughtGraphics); LK.getSound('caught').play(); LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); return; } } // Handle tree transparency when player touches them for (var t = 0; t < obstacles.length; t++) { var obstacle = obstacles[t]; // Only apply transparency to trees, not rocks if (!obstacle.hasCircularHitbox) { obstacle.lastPlayerTouching = obstacle.isPlayerTouching; obstacle.isPlayerTouching = player.intersects(obstacle); // Check if player just started touching the tree if (!obstacle.lastPlayerTouching && obstacle.isPlayerTouching) { // Make tree semi-transparent tween(obstacle.children[0], { alpha: 0.3 }, { duration: 300, easing: tween.easeOut }); } // Check if player stopped touching the tree else if (obstacle.lastPlayerTouching && !obstacle.isPlayerTouching) { // Make tree fully opaque again tween(obstacle.children[0], { alpha: 1.0 }, { duration: 500, easing: tween.easeOut }); } } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Dog = Container.expand(function () {
var self = Container.call(this);
self.speed = 2.5;
self.chaseSpeed = 5;
self.detectionRadius = 300;
self.isChasing = false;
self.trailFollowDistance = 150;
self.alertRadius = 400;
self.hasAlertedGuards = false;
self.isResting = false;
self.restTimer = 0;
self.restDuration = 300; // 5 seconds at 60fps
self.chaseTimer = 0;
self.chaseDuration = 180; // 3 seconds at 60fps
self.stopTimer = 0;
self.stopDuration = 120; // 2 seconds at 60fps
self.isStopped = false;
self.isInKittenMode = false;
self.normalSpeed = self.speed;
self.normalChaseSpeed = self.chaseSpeed;
var dogGraphics = self.attachAsset('dog_normal', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.update = function () {
// Handle kitten mode changes
if (isKittenMode && !self.isInKittenMode) {
// Entering kitten mode - multiply speed and change to run texture
self.isInKittenMode = true;
self.speed = self.normalSpeed * 2;
self.chaseSpeed = self.normalChaseSpeed * 2;
// Change to run texture
self.removeChild(dogGraphics);
dogGraphics = self.attachAsset('dog_run', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(dogGraphics);
} else if (!isKittenMode && self.isInKittenMode) {
// Exiting kitten mode - restore normal speed and texture
self.isInKittenMode = false;
self.speed = self.normalSpeed;
self.chaseSpeed = self.normalChaseSpeed;
// Change back to normal texture
self.removeChild(dogGraphics);
dogGraphics = self.attachAsset('dog_normal', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(dogGraphics);
}
// Handle rest mode
if (self.isResting) {
self.restTimer++;
// Change to passive texture after 1 second (60 frames)
if (self.restTimer === 60) {
self.removeChild(dogGraphics);
dogGraphics = self.attachAsset('dog_rest', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.addChild(dogGraphics);
}
if (self.restTimer >= self.restDuration) {
// Exit rest mode
self.isResting = false;
self.restTimer = 0;
self.hasAlertedGuards = false; // Reset so dog can alert again
// Change back to normal texture
self.removeChild(dogGraphics);
dogGraphics = self.attachAsset('dog_normal', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(dogGraphics);
}
// Dog doesn't move or detect during rest mode
return;
}
// Check if player is in detection range and not in kitten mode
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var canDetectPlayer = distance < self.detectionRadius && !player.isHiding && !isKittenMode;
if (canDetectPlayer) {
if (!self.isChasing && !self.isStopped) {
// Start chasing
self.isChasing = true;
self.chaseTimer = 0;
self.hasAlertedGuards = false;
dogGraphics.tint = 0xFF4444; // Red when chasing
// Play dog alert sound when detecting player
LK.getSound('dog_alert').play();
}
// Handle chase duration
if (self.isChasing) {
self.chaseTimer++;
if (self.chaseTimer >= self.chaseDuration) {
// Stop chasing after 3 seconds and enter stop mode
self.isChasing = false;
self.isStopped = true;
self.stopTimer = 0;
dogGraphics.tint = 0xFFFFFF; // Remove red tint when stopping
}
}
// Handle stop duration
if (self.isStopped) {
self.stopTimer++;
if (self.stopTimer >= self.stopDuration) {
// End stop mode after 2 seconds
self.isStopped = false;
self.stopTimer = 0;
}
}
// Alert all guards when close enough during chase
if (self.isChasing && distance < self.alertRadius && !self.hasAlertedGuards) {
self.hasAlertedGuards = true;
// Alert all guards
for (var i = 0; i < guards.length; i++) {
var guard = guards[i];
if (!guard.isAlerted) {
guard.isAlerted = true;
guard.alertState = 'chase_player';
guard.chaseTimer = 0;
guard.alertSpeed = 6;
// Change guard to chase texture
var currentGraphics = guard.children[0];
guard.removeChild(currentGraphics);
var chaseGraphics = guard.attachAsset('guard_chase', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
guard.addChild(chaseGraphics);
}
}
// Enter rest mode for 5 seconds after alerting guards
self.isResting = true;
self.restTimer = 0;
// Change to rest texture
self.removeChild(dogGraphics);
dogGraphics = self.attachAsset('dog_rest', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.addChild(dogGraphics);
}
// Chase player directly only if chasing and not stopped
if (self.isChasing && !self.isStopped) {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.chaseSpeed;
self.y += Math.sin(angle) * self.chaseSpeed;
}
} else if (self.isChasing && distance > self.detectionRadius * 1.5) {
// Stop chasing if player gets too far
self.isChasing = false;
self.isStopped = false;
self.chaseTimer = 0;
self.stopTimer = 0;
self.hasAlertedGuards = false;
dogGraphics.tint = 0xFFFFFF; // Remove tint when not chasing
} else if (!self.isChasing && !self.isStopped) {
// Follow player trail when not actively chasing and not stopped
var trailDistance = Math.sqrt(dx * dx + dy * dy);
if (trailDistance > self.trailFollowDistance) {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
// Keep dog within bounds
if (self.x < 100) self.x = 100;
if (self.x > 1948) self.x = 1948;
if (self.y < 100) self.y = 100;
if (self.y > 2632) self.y = 2632;
// Update dog flip based on movement direction
if (dx > 0) {
dogGraphics.scaleX = Math.abs(dogGraphics.scaleX);
} else {
dogGraphics.scaleX = -Math.abs(dogGraphics.scaleX);
}
};
return self;
});
var GoldenMoonRock = Container.expand(function () {
var self = Container.call(this);
var rockGraphics = self.attachAsset('golden_moon_rock', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
rockGraphics.tint = 0xFFD700; // Golden color
self.moveTimer = 0;
self.moveDirection = Math.random() * Math.PI * 2;
self.speed = 1.5;
self.lifeTimer = 0;
self.maxLifetime = 1800; // 30 seconds at 60fps
self.update = function () {
// Golden glow animation - more intense than moonbeam
rockGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.2) * 0.3;
rockGraphics.scaleX = 1.2 + Math.sin(LK.ticks * 0.12) * 0.2;
rockGraphics.scaleY = 1.2 + Math.sin(LK.ticks * 0.12) * 0.2;
rockGraphics.rotation += 0.03;
// Slow random movement
self.moveTimer++;
if (self.moveTimer % 90 == 0) {
// Change direction every 1.5 seconds
self.moveDirection = Math.random() * Math.PI * 2;
}
self.x += Math.cos(self.moveDirection) * self.speed;
self.y += Math.sin(self.moveDirection) * self.speed;
// Keep within bounds
if (self.x < 80) {
self.x = 80;
self.moveDirection = Math.random() * Math.PI;
}
if (self.x > 1968) {
self.x = 1968;
self.moveDirection = Math.PI + Math.random() * Math.PI;
}
if (self.y < 80) {
self.y = 80;
self.moveDirection = Math.PI * 0.5 + Math.random() * Math.PI;
}
if (self.y > 2652) {
self.y = 2652;
self.moveDirection = Math.PI * 1.5 + Math.random() * Math.PI;
}
// Handle lifetime
self.lifeTimer++;
if (self.lifeTimer >= self.maxLifetime) {
// Fade out effect before disappearing
tween(rockGraphics, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
self.destroy();
// Remove from goldenMoonRocks array
for (var i = goldenMoonRocks.length - 1; i >= 0; i--) {
if (goldenMoonRocks[i] === self) {
goldenMoonRocks.splice(i, 1);
break;
}
}
}
});
}
};
return self;
});
var Guard = Container.expand(function () {
var self = Container.call(this);
self.patrolSpeed = 1.5;
self.alertSpeed = 4;
self.detectionRadius = 250;
self.arrestRadius = 100; // Smaller hitbox for arrest - much smaller than detection range
self.isAlerted = false;
self.alertState = 'none'; // 'none', 'chase_player', 'move_to_moonstone'
self.chaseTimer = 0;
self.chaseDuration = 180; // 3 seconds at 60fps
self.patrolDirection = Math.random() * Math.PI * 2;
self.patrolTimer = 0;
self.lastInDetectionRange = false;
// Add detection range aura
var auraGraphics = self.attachAsset('guard_aura', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
scaleX: 1.0,
scaleY: 1.0
});
self.addChild(auraGraphics);
var guardGraphics = self.attachAsset('guard', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.update = function () {
self.patrolTimer++;
// Update aura animation and appearance
var aura = self.children[0]; // Aura is first child
if (aura) {
// Pulsing animation
var pulseScale = 1.0 + Math.sin(LK.ticks * 0.1) * 0.1;
aura.scaleX = pulseScale;
aura.scaleY = pulseScale;
// Change aura color based on guard state
if (self.isAlerted) {
if (self.alertState === 'chase_player') {
aura.tint = 0xff0000; // Red when chasing player
aura.alpha = 0.4;
} else if (self.alertState === 'move_to_moonstone') {
aura.tint = 0xffff00; // Yellow when moving to moonstone
aura.alpha = 0.3;
}
} else {
aura.tint = 0xff4444; // Default red
aura.alpha = 0.2;
}
// Intensify aura when player is in detection range
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var inRange = distance < self.detectionRadius + player.detectionRadius;
if (inRange && !player.isHiding && !isKittenMode) {
tween(aura, {
alpha: 0.6
}, {
duration: 200
});
} else {
var targetAlpha = self.isAlerted ? 0.4 : 0.2;
tween(aura, {
alpha: targetAlpha
}, {
duration: 500
});
}
}
// Check if player is in detection range and not hiding (and not in kitten mode)
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var currentInDetectionRange = distance < self.detectionRadius + player.detectionRadius && !player.isHiding && !isKittenMode;
if (currentInDetectionRange) {
// Check if player just entered detection range (transition from false to true)
if (!self.lastInDetectionRange && currentInDetectionRange) {
var guardSounds = ['stop', 'hey', 'dont_run'];
var randomSound = guardSounds[Math.floor(Math.random() * guardSounds.length)];
LK.getSound(randomSound).play();
// Double player speed when alerting a guard
player.speed *= 2;
// If "don't run" sound is played, alert 3 random guards
if (randomSound === 'dont_run') {
var availableGuards = [];
for (var i = 0; i < guards.length; i++) {
if (guards[i] !== self && !guards[i].isAlerted) {
availableGuards.push(guards[i]);
}
}
// Alert up to 3 random guards
var guardsToAlert = Math.min(3, availableGuards.length);
for (var j = 0; j < guardsToAlert; j++) {
var randomIndex = Math.floor(Math.random() * availableGuards.length);
var guardToAlert = availableGuards[randomIndex];
guardToAlert.isAlerted = true;
guardToAlert.alertState = 'move_to_moonstone';
guardToAlert.chaseTimer = 0;
guardToAlert.alertSpeed = 6; // Set increased speed for alerted guards
// Remove from available list to avoid double selection
availableGuards.splice(randomIndex, 1);
}
}
}
if (!self.isAlerted) {
// Start chasing - reset timer
self.chaseTimer = 0;
// Increase speed for chase mode
self.alertSpeed = 6; // Increased from 4
// Change to chase texture
self.removeChild(guardGraphics);
guardGraphics = self.attachAsset('guard_chase', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.addChild(guardGraphics);
}
self.isAlerted = true;
self.alertState = 'chase_player';
guardGraphics.tint = 0xFF4444;
} else if (self.isAlerted) {
// Continue chasing for the duration even if player is out of range
self.chaseTimer++;
if (self.chaseTimer >= self.chaseDuration) {
self.isAlerted = false;
self.alertState = 'none';
guardGraphics.tint = 0xFFFFFF;
self.chaseTimer = 0;
// Reset speed to normal patrol speed
self.alertSpeed = 4; // Reset to original speed
// Revert to normal texture
self.removeChild(guardGraphics);
guardGraphics = self.attachAsset('guard', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.addChild(guardGraphics);
}
} else {
guardGraphics.tint = 0xFFFFFF;
}
if (self.isAlerted) {
var targetX, targetY;
if (self.alertState === 'chase_player') {
// Target only player (bot disabled)
targetX = player.x;
targetY = player.y;
guardGraphics.tint = 0xFF4444; // Red tint for chasing player
} else if (self.alertState === 'move_to_moonstone') {
// Move towards moonstone drop position
targetX = lastMoonstoneDropX;
targetY = lastMoonstoneDropY;
guardGraphics.tint = 0x44FF44; // Green tint for moving to moonstone
} else {
// Default behavior (shouldn't happen when alerted)
targetX = player.x;
targetY = player.y;
}
var angle = Math.atan2(targetY - self.y, targetX - self.x);
self.x += Math.cos(angle) * self.alertSpeed;
self.y += Math.sin(angle) * self.alertSpeed;
} else {
// Patrol behavior
if (self.patrolTimer % 120 == 0) {
self.patrolDirection = Math.random() * Math.PI * 2;
}
self.x += Math.cos(self.patrolDirection) * self.patrolSpeed;
self.y += Math.sin(self.patrolDirection) * self.patrolSpeed;
// Keep guards within bounds
if (self.x < 100) {
self.x = 100;
self.patrolDirection = Math.random() * Math.PI;
}
if (self.x > 1948) {
self.x = 1948;
self.patrolDirection = Math.PI + Math.random() * Math.PI;
}
if (self.y < 100) {
self.y = 100;
self.patrolDirection = Math.PI * 0.5 + Math.random() * Math.PI;
}
if (self.y > 2632) {
self.y = 2632;
self.patrolDirection = Math.PI * 1.5 + Math.random() * Math.PI;
}
}
// Update guard flip based on movement direction
if (self.isAlerted) {
// In chase mode, flip based on direction to player
if (dx > 0) {
guardGraphics.scaleX = Math.abs(guardGraphics.scaleX); // Face right
} else {
guardGraphics.scaleX = -Math.abs(guardGraphics.scaleX); // Face left
}
} else {
// In patrol mode, flip based on patrol direction
var movementX = Math.cos(self.patrolDirection);
if (movementX > 0) {
guardGraphics.scaleX = Math.abs(guardGraphics.scaleX); // Face right
} else {
guardGraphics.scaleX = -Math.abs(guardGraphics.scaleX); // Face left
}
}
// Update last detection state
self.lastInDetectionRange = distance < self.detectionRadius + player.detectionRadius && !player.isHiding;
};
return self;
});
var MoonBeam = Container.expand(function () {
var self = Container.call(this);
var moonbeamGraphics = self.attachAsset('golden_moonstone', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
moonbeamGraphics.tint = 0xFFD700; // Golden color
self.moveTimer = 0;
self.moveDirection = Math.random() * Math.PI * 2;
self.speed = 2;
self.lifeTimer = 0;
self.maxLifetime = 900; // 15 seconds at 60fps
self.update = function () {
// Golden glow animation
moonbeamGraphics.alpha = 0.6 + Math.sin(LK.ticks * 0.15) * 0.2;
moonbeamGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.15;
moonbeamGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.15;
moonbeamGraphics.rotation += 0.02;
// Random movement
self.moveTimer++;
if (self.moveTimer % 60 == 0) {
// Change direction every second
self.moveDirection = Math.random() * Math.PI * 2;
}
self.x += Math.cos(self.moveDirection) * self.speed;
self.y += Math.sin(self.moveDirection) * self.speed;
// Keep within bounds
if (self.x < 60) {
self.x = 60;
self.moveDirection = Math.random() * Math.PI;
}
if (self.x > 1988) {
self.x = 1988;
self.moveDirection = Math.PI + Math.random() * Math.PI;
}
if (self.y < 60) {
self.y = 60;
self.moveDirection = Math.PI * 0.5 + Math.random() * Math.PI;
}
if (self.y > 2672) {
self.y = 2672;
self.moveDirection = Math.PI * 1.5 + Math.random() * Math.PI;
}
// Handle lifetime
self.lifeTimer++;
if (self.lifeTimer >= self.maxLifetime) {
// Fade out effect before disappearing
tween(moonbeamGraphics, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
// Remove from moonbeams array
for (var i = moonbeams.length - 1; i >= 0; i--) {
if (moonbeams[i] === self) {
moonbeams.splice(i, 1);
break;
}
}
}
});
}
};
return self;
});
var Moonstone = Container.expand(function () {
var self = Container.call(this);
var moonstoneGraphics = self.attachAsset('moonstone', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.5;
self.bounce = 0.7;
self.friction = 0.98;
self.isPhysicsActive = false;
self.isZigzagBouncing = false;
self.zigzagTimer = 0;
self.zigzagSpeed = 8;
self.zigzagDirection = 0;
self.activatePhysics = function () {
self.isPhysicsActive = true;
// Give initial random velocity when physics activates
self.velocityX = (Math.random() - 0.5) * 10;
self.velocityY = (Math.random() - 0.5) * 10;
};
self.update = function () {
// Gentle glow animation
moonstoneGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.1) * 0.3;
moonstoneGraphics.rotation += 0.02;
// Zigzag bouncing behavior
if (self.isZigzagBouncing) {
self.zigzagTimer++;
// Change direction every 30 frames (0.5 seconds) for zigzag effect
if (self.zigzagTimer % 30 === 0) {
// Create zigzag pattern by alternating direction
var zigzagAngle = self.zigzagDirection + Math.PI / 3 * (Math.random() - 0.5);
self.velocityX = Math.cos(zigzagAngle) * self.zigzagSpeed;
self.velocityY = Math.sin(zigzagAngle) * self.zigzagSpeed;
self.zigzagDirection = zigzagAngle;
}
// Update position with zigzag movement
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with obstacles during zigzag
for (var o = 0; o < obstacles.length; o++) {
var obstacle = obstacles[o];
var isColliding = false;
if (obstacle.hasCircularHitbox) {
// Use circular collision detection for rocks
var dx = self.x - obstacle.x;
var dy = self.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
isColliding = distance < obstacle.hitboxRadius + 45; // 45 is moonstone radius
} else {
// Use default rectangular collision for trees
isColliding = self.intersects(obstacle);
}
if (isColliding) {
// Bounce off obstacle by reversing direction
if (obstacle.hasCircularHitbox) {
// For rocks, calculate proper bounce direction
var dx = self.x - obstacle.x;
var dy = self.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var normalX = dx / distance;
var normalY = dy / distance;
// Reflect velocity based on collision normal
var dotProduct = self.velocityX * normalX + self.velocityY * normalY;
self.velocityX = self.velocityX - 2 * dotProduct * normalX;
self.velocityY = self.velocityY - 2 * dotProduct * normalY;
// Push moonstone away from rock wall
var pushDistance = obstacle.hitboxRadius + 50;
self.x = obstacle.x + normalX * pushDistance;
self.y = obstacle.y + normalY * pushDistance;
}
} else {
// For trees, use simple velocity reversal
self.velocityX = -self.velocityX;
self.velocityY = -self.velocityY;
self.zigzagDirection += Math.PI; // Reverse direction
// Move away from obstacle to prevent sticking
self.x -= self.velocityX;
self.y -= self.velocityY;
}
break;
}
}
// Bounce off boundaries with direction change for zigzag
if (self.x <= 45) {
self.x = 45;
self.velocityX = Math.abs(self.velocityX);
self.zigzagDirection = Math.random() * Math.PI / 2; // Random right direction
}
if (self.x >= 2003) {
self.x = 2003;
self.velocityX = -Math.abs(self.velocityX);
self.zigzagDirection = Math.PI / 2 + Math.random() * Math.PI / 2; // Random left direction
}
if (self.y <= 45) {
self.y = 45;
self.velocityY = Math.abs(self.velocityY);
self.zigzagDirection = Math.random() * Math.PI; // Random down direction
}
if (self.y >= 2687) {
self.y = 2687;
self.velocityY = -Math.abs(self.velocityY);
self.zigzagDirection = Math.PI + Math.random() * Math.PI; // Random up direction
}
// Stop zigzag after 5 seconds
if (self.zigzagTimer >= 300) {
self.isZigzagBouncing = false;
self.zigzagTimer = 0;
}
}
// Ball physics when in kitten mode and not zigzag bouncing
else if (self.isPhysicsActive && isKittenMode) {
// Apply gravity
self.velocityY += self.gravity;
// Apply friction
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Update position
self.x += self.velocityX;
self.y += self.velocityY;
// Skip obstacle collision detection in kitten mode to allow free physics movement
// Only bounce off screen boundaries
if (self.x <= 45) {
self.x = 45;
self.velocityX = -self.velocityX * self.bounce;
}
if (self.x >= 2003) {
self.x = 2003;
self.velocityX = -self.velocityX * self.bounce;
}
if (self.y <= 45) {
self.y = 45;
self.velocityY = -self.velocityY * self.bounce;
}
if (self.y >= 2687) {
self.y = 2687;
self.velocityY = -self.velocityY * self.bounce;
}
}
};
return self;
});
var Obstacle = Container.expand(function (assetType) {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
// Add circular hitbox for rocks
if (assetType === 'rock') {
self.hitboxRadius = 90; // Circular hitbox radius for rocks
self.hasCircularHitbox = true;
} else {
self.hasCircularHitbox = false;
}
// Add transparency tracking for trees
self.isPlayerTouching = false;
self.lastPlayerTouching = false;
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
self.transformationLevel = 0;
self.speed = 3;
self.isHiding = false;
self.detectionRadius = 120;
self.lastX = 0;
self.lastY = 0;
self.footstepTimer = 0;
self.footstepInterval = 15; // Play footstep every 15 frames when moving
self.isWalking = false;
self.lastWalkingState = false;
var playerGraphics = self.attachAsset('texture_phase0', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05
});
self.transform = function () {
self.transformationLevel++;
// Remove all current graphics to prevent duplication
while (self.children.length > 0) {
self.removeChild(self.children[0]);
}
// Update appearance based on transformation level
if (self.transformationLevel >= 7) {
// Full werewolf
playerGraphics = self.attachAsset('werewolf', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.detectionRadius = 80;
} else {
// Use phase-specific texture with walking state
var textureId = 'texture_phase' + self.transformationLevel;
if (self.isWalking) {
textureId += '_walk';
}
playerGraphics = self.attachAsset(textureId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05
});
// Adjust speed and detection based on transformation
self.speed = 3 + self.transformationLevel * 0.7;
self.detectionRadius = 120 - self.transformationLevel * 5;
}
LK.getSound('transformation').play();
LK.effects.flashObject(self, 0xE6E6FA, 1000);
};
self.updateTexture = function () {
// Check if walking state changed
if (self.lastWalkingState !== self.isWalking) {
self.lastWalkingState = self.isWalking;
// Remove current graphics
var currentGraphics = self.children[0];
self.removeChild(currentGraphics);
var newGraphics;
if (isKittenMode || isGiantMode) {
// Handle kitten mode textures (also used for giant mode)
var kittenTextureId = 'kitten';
if (self.isWalking) {
kittenTextureId += '_walk';
}
newGraphics = self.attachAsset(kittenTextureId, {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.transformationLevel >= 7) {
// Werewolf doesn't change texture when walking
newGraphics = self.attachAsset('werewolf', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
// Choose texture based on walking state and transformation level
var textureId = 'texture_phase' + self.transformationLevel;
if (self.isWalking) {
textureId += '_walk';
}
newGraphics = self.attachAsset(textureId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05
});
}
self.addChild(newGraphics);
// Apply the last facing direction to the new graphics
if (newGraphics && typeof playerLastFacingRight !== 'undefined') {
if (playerLastFacingRight) {
newGraphics.scaleX = Math.abs(newGraphics.scaleX);
} else {
newGraphics.scaleX = -Math.abs(newGraphics.scaleX);
}
}
}
};
self.checkHiding = function () {
self.isHiding = false;
// Check if player is behind any obstacle
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var dx = self.x - obstacle.x;
var dy = self.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
self.isHiding = true;
break;
}
}
};
return self;
});
var PlayerBot = Container.expand(function () {
var self = Container.call(this);
self.transformationLevel = 0;
self.botTransformationLevel = 0; // Independent transformation level for bot
self.speed = 2.5; // Slightly slower than player
self.isHiding = false;
self.detectionRadius = 120;
self.followDistance = 150; // Distance to maintain from player
self.targetX = 0;
self.targetY = 0;
self.moveTimer = 0;
self.footstepTimer = 0;
self.footstepInterval = 20; // Slightly different timing than player
self.lastX = 0;
self.lastY = 0;
self.isInBotKittenMode = false; // Independent kitten mode for bot
self.botKittenModeTimer = 0;
self.botKittenModeDuration = 1200; // 20 seconds at 60fps - longer than player
self.botEffectTimer = 0;
self.botEffectInterval = 480; // 8 seconds - bot activates effects more frequently
self.canAlertGuards = true; // Bot can alert guards
self.hasAlertedGuards = false; // Track if bot has alerted guards
self.alertCooldown = 0; // Cooldown before bot can alert again
self.alertCooldownDuration = 600; // 10 seconds at 60fps
self.maintainDistance = false; // Whether bot should maintain distance from player
self.distanceFromPlayer = 300; // Distance to maintain when keeping away
self.isCaught = false; // Whether bot has been caught by guards
self.canMove = true; // Whether bot can move (disabled when caught)
self.mode = 'normal'; // Bot mode: 'normal' or 'caught'
var botGraphics = self.attachAsset('texture_phase0', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.04,
// Slightly smaller than player
scaleY: 0.04,
alpha: 0.8 // Slightly transparent to distinguish from player
});
// Apply a slight blue tint to distinguish from player
botGraphics.tint = 0xADD8E6;
self.transform = function () {
self.transformationLevel++;
// Remove current graphics
self.removeChild(botGraphics);
// Update appearance based on transformation level
if (self.transformationLevel >= 7) {
// Full werewolf
botGraphics = self.attachAsset('werewolf', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.speed = 7; // Slightly slower than player werewolf
self.detectionRadius = 80;
} else {
// Use phase-specific texture
var textureId = 'texture_phase' + self.transformationLevel;
botGraphics = self.attachAsset(textureId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.04,
scaleY: 0.04,
alpha: 0.8
});
// Adjust speed and detection based on transformation
self.speed = 2.5 + self.transformationLevel * 0.6;
self.detectionRadius = 120 - self.transformationLevel * 5;
}
// Apply blue tint to distinguish from player
botGraphics.tint = 0xADD8E6;
// Re-add the updated graphics
self.addChild(botGraphics);
LK.effects.flashObject(self, 0xADD8E6, 800);
};
self.botTransform = function () {
self.botTransformationLevel++;
// Remove current graphics
self.removeChild(botGraphics);
// Update appearance based on bot's independent transformation level
if (self.botTransformationLevel >= 7) {
// Full werewolf
botGraphics = self.attachAsset('werewolf', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.speed = 8; // Bot werewolf is faster than regular bot
self.detectionRadius = 70;
} else {
// Use phase-specific texture
var textureId = 'texture_phase' + self.botTransformationLevel;
botGraphics = self.attachAsset(textureId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.04,
scaleY: 0.04,
alpha: 0.8
});
// Adjust speed and detection based on bot's transformation
self.speed = 2.5 + self.botTransformationLevel * 0.8;
self.detectionRadius = 120 - self.botTransformationLevel * 6;
}
// Apply purple tint to distinguish bot transformations
botGraphics.tint = 0x9370DB;
// Re-add the updated graphics
self.addChild(botGraphics);
LK.effects.flashObject(self, 0x9370DB, 1000);
// Bot transformation effect
tween(self, {
scaleX: (self.scaleX || 1) + 0.15,
scaleY: (self.scaleY || 1) + 0.15
}, {
duration: 800,
easing: tween.easeOut
});
};
self.activateBotKittenMode = function () {
if (!self.isInBotKittenMode) {
self.isInBotKittenMode = true;
self.botKittenModeTimer = 0;
// Transform to kitten with unique bot styling
var currentBotGraphics = self.children[0];
self.removeChild(currentBotGraphics);
var botKittenGraphics = self.attachAsset('kitten', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
// Apply unique purple-blue tint for bot kitten mode
botKittenGraphics.tint = 0x6A5ACD;
self.addChild(botKittenGraphics);
// Unique scaling for bot kitten mode
tween(self, {
scaleX: (self.scaleX || 1) * 0.4,
scaleY: (self.scaleY || 1) * 0.4
}, {
duration: 600,
easing: tween.easeOut
});
// Play unique bot meow with different pitch
var botMeow = LK.getSound('meow');
botMeow.volume = 0.6;
botMeow.play();
LK.effects.flashObject(self, 0x6A5ACD, 1200);
}
};
self.checkHiding = function () {
self.isHiding = false;
// Check if bot is behind any obstacle
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var dx = self.x - obstacle.x;
var dy = self.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
self.isHiding = true;
break;
}
}
};
self.update = function () {
// Handle bot's independent kitten mode timer
if (self.isInBotKittenMode) {
self.botKittenModeTimer++;
if (self.botKittenModeTimer >= self.botKittenModeDuration) {
// Revert from bot kitten mode
self.isInBotKittenMode = false;
self.botKittenModeTimer = 0;
// Change bot back to current bot transformation level
var currentBotGraphics = self.children[0];
self.removeChild(currentBotGraphics);
var revertGraphics;
if (self.botTransformationLevel >= 7) {
revertGraphics = self.attachAsset('werewolf', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
} else {
var textureId = 'texture_phase' + self.botTransformationLevel;
revertGraphics = self.attachAsset(textureId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.04,
scaleY: 0.04,
alpha: 0.8
});
}
revertGraphics.tint = 0x9370DB; // Purple tint for bot transformations
self.addChild(revertGraphics);
// Restore bot size when reverting from kitten mode
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.easeOut
});
LK.effects.flashObject(self, 0x9370DB, 900);
}
}
// Bot independent effect timer
self.botEffectTimer++;
if (self.botEffectTimer >= self.botEffectInterval) {
self.botEffectTimer = 0;
// Bot has 30% chance to transform independently
if (Math.random() < 0.3 && self.botTransformationLevel < 7) {
self.botTransform();
}
// Bot has 25% chance to activate kitten mode independently
else if (Math.random() < 0.25 && !self.isInBotKittenMode) {
self.activateBotKittenMode();
}
}
// Handle alert cooldown
if (self.alertCooldown > 0) {
self.alertCooldown--;
if (self.alertCooldown <= 0) {
self.canAlertGuards = true;
self.hasAlertedGuards = false;
self.maintainDistance = false; // Stop maintaining distance after cooldown
}
}
// Check if bot should alert guards (only when bot enters a guard's detection range)
var shouldAlert = false;
if (self.canAlertGuards && !self.hasAlertedGuards && !isKittenMode) {
// Check if bot is within any guard's detection range
for (var g = 0; g < guards.length; g++) {
var guard = guards[g];
var guardDx = self.x - guard.x;
var guardDy = self.y - guard.y;
var guardDistance = Math.sqrt(guardDx * guardDx + guardDy * guardDy);
// Check if bot is within this guard's detection radius
if (guardDistance < guard.detectionRadius + self.detectionRadius) {
shouldAlert = true;
break;
}
}
}
if (shouldAlert) {
// Bot enters guard detection range - alert only the specific guard
self.hasAlertedGuards = true;
self.canAlertGuards = false;
self.alertCooldown = self.alertCooldownDuration;
self.maintainDistance = true; // Start maintaining distance
// Alert only the guard whose zone the bot entered
for (var g = 0; g < guards.length; g++) {
var guard = guards[g];
var guardDx = self.x - guard.x;
var guardDy = self.y - guard.y;
var guardDistance = Math.sqrt(guardDx * guardDx + guardDy * guardDy);
// Only alert this specific guard if bot is in its detection range
if (guardDistance < guard.detectionRadius + self.detectionRadius && !guard.isAlerted) {
guard.isAlerted = true;
guard.alertState = 'chase_player';
guard.chaseTimer = 0;
guard.alertSpeed = 6;
// Change guard to chase texture
var currentGraphics = guard.children[0];
guard.removeChild(currentGraphics);
var chaseGraphics = guard.attachAsset('guard_chase', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
guard.addChild(chaseGraphics);
break; // Only alert one guard - the one whose zone was entered
}
}
// Play alert sound
LK.getSound('dog_alert').play();
// Flash bot to indicate it alerted guards
LK.effects.flashObject(self, 0xFF4444, 800);
}
// Store last position for movement detection
self.lastX = self.x;
self.lastY = self.y;
// Skip movement and other behaviors if bot is caught
if (self.isCaught || !self.canMove) {
return;
}
// Free movement behavior - check for moonstones first
var nearestMoonstone = null;
var shortestDistance = Infinity;
// Find nearest moonstone
for (var m = 0; m < moonstones.length; m++) {
var moonstone = moonstones[m];
var mstoneDx = moonstone.x - self.x;
var mstoneDy = moonstone.y - self.y;
var mstoneDistance = Math.sqrt(mstoneDx * mstoneDx + mstoneDy * mstoneDy);
if (mstoneDistance < shortestDistance) {
shortestDistance = mstoneDistance;
nearestMoonstone = moonstone;
}
}
// Calculate distance to player for bot logic
var playerDistance = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
// Determine target based on bot state
if (self.maintainDistance) {
// Maintain distance from player when bot has alerted guards
if (playerDistance < self.distanceFromPlayer) {
// Move away from player
var awayAngle = Math.atan2(self.y - player.y, self.x - player.x);
self.targetX = self.x + Math.cos(awayAngle) * self.speed * 2; // Move away faster
self.targetY = self.y + Math.sin(awayAngle) * self.speed * 2;
} else {
// Maintain current position if far enough
self.targetX = self.x;
self.targetY = self.y;
}
} else if (nearestMoonstone && shortestDistance < 600) {
// Move towards nearest moonstone
var mstoneDx = nearestMoonstone.x - self.x;
var mstoneDy = nearestMoonstone.y - self.y;
var mstoneAngle = Math.atan2(mstoneDy, mstoneDx);
self.targetX = self.x + Math.cos(mstoneAngle) * self.speed * 1.5; // Faster when seeking moonstone
self.targetY = self.y + Math.sin(mstoneAngle) * self.speed * 1.5;
} else {
// Free roaming movement when no moonstone nearby
self.moveTimer++;
if (self.moveTimer % 120 === 0) {
// Change direction every 2 seconds for more freedom
var randomAngle = Math.random() * Math.PI * 2;
var roamDistance = 80 + Math.random() * 120; // Vary roaming distance
self.targetX = self.x + Math.cos(randomAngle) * roamDistance;
self.targetY = self.y + Math.sin(randomAngle) * roamDistance;
}
}
// Move towards target position
var targetDx = self.targetX - self.x;
var targetDy = self.targetY - self.y;
var targetDistance = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDistance > 1) {
var moveX = targetDx / targetDistance * self.speed;
var moveY = targetDy / targetDistance * self.speed;
self.x += moveX;
self.y += moveY;
}
// Keep bot within bounds
if (self.x < 40) self.x = 40;
if (self.x > 2008) self.x = 2008;
if (self.y < 40) self.y = 40;
if (self.y > 2692) self.y = 2692;
// Check collision with rock obstacles (walls)
for (var o = 0; o < obstacles.length; o++) {
var obstacle = obstacles[o];
if (obstacle.hasCircularHitbox) {
// Only check rocks as walls
var obstacleDx = self.x - obstacle.x;
var obstacleDy = self.y - obstacle.y;
var obstacleDistance = Math.sqrt(obstacleDx * obstacleDx + obstacleDy * obstacleDy);
var botRadius = 35; // Bot collision radius
if (obstacleDistance < obstacle.hitboxRadius + botRadius) {
// Bot is colliding with rock wall - push back
if (obstacleDistance > 0) {
var normalX = obstacleDx / obstacleDistance;
var normalY = obstacleDy / obstacleDistance;
var pushDistance = obstacle.hitboxRadius + botRadius;
self.x = obstacle.x + normalX * pushDistance;
self.y = obstacle.y + normalY * pushDistance;
}
}
}
}
// Play footstep sounds when bot is moving
var movementX = self.x - self.lastX;
var movementY = self.y - self.lastY;
var isMoving = Math.sqrt(movementX * movementX + movementY * movementY) > 0.5;
if (isMoving) {
self.footstepTimer++;
if (self.footstepTimer >= self.footstepInterval) {
// Play footstep at lower volume for bot
var footstepSound = LK.getSound('footsteps');
footstepSound.volume = 0.3;
footstepSound.play();
self.footstepTimer = 0;
}
} else {
self.footstepTimer = 0;
}
// Flip bot based on movement direction
if (self.children.length > 0 && self.children[0]) {
if (movementX > 0) {
// Moving right - face right
self.children[0].scaleX = Math.abs(self.children[0].scaleX);
} else if (movementX < 0) {
// Moving left - face left
self.children[0].scaleX = -Math.abs(self.children[0].scaleX);
}
}
// Update hiding status
self.checkHiding();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x191970
});
/****
* Game Code
****/
// Game variables
var player;
var playerBot;
var moonstones = [];
var guards = [];
var obstacles = [];
var moonbeams = [];
var goldenMoonRocks = [];
var dragNode = null;
var moonstonesCollected = 0;
var playerLevel = 0; // Independent level counter for player transformations
var botLevel = 0; // Independent level counter for bot transformations
var totalMoonstones = 7;
var lastMoonstoneDropX = 0;
var lastMoonstoneDropY = 0;
var moonbeamEventTimer = 0;
var moonbeamEventInterval = 900; // 15 seconds at 60fps
var goldenRockTimer = 0;
var goldenRockInterval = 1800; // 30 seconds at 60fps
var isGiantMode = false;
var giantModeTimer = 0;
var giantModeDuration = 900; // 15 seconds at 60fps
var hasTransformedToKitten = storage.hasTransformedToKitten || false;
var isKittenMode = false;
var kittenModeTimer = 0;
var kittenModeDuration = 900; // 15 seconds at 60fps
var dog = null;
var dogSpawned = false;
var playerLastFacingRight = true; // Track player's last facing direction
// Create UI
var scoreText = new Text2('Moonstones: 0/7', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var transformationText = new Text2('Human Child', {
size: 50,
fill: 0xE6E6FA
});
transformationText.anchor.set(0.5, 0);
transformationText.y = 80;
LK.gui.top.addChild(transformationText);
// Create loading screen that covers the entire game
var loadingScreen = LK.gui.center.addChild(LK.getAsset('loadingScreen', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
width: 2048,
height: 2732
}));
// Keep loading screen visible for exactly 3 seconds, then fade out
LK.setTimeout(function () {
tween(loadingScreen, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove loading screen from GUI and destroy to free memory
LK.gui.center.removeChild(loadingScreen);
loadingScreen.destroy();
loadingScreen = null; // Clear reference for garbage collection
}
});
}, 3000);
// Create grass ground
var grass = game.addChild(LK.getAsset('grass', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 0.82,
scaleY: 1.16
}));
// Create player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Bot spawning disabled
playerBot = null;
// Generate only one moonstone initially
var moonstone = game.addChild(new Moonstone());
moonstone.x = 150 + Math.random() * 1748;
moonstone.y = 150 + Math.random() * 2432;
moonstones.push(moonstone);
// Function to spawn moonbeam at random location
function spawnMoonbeam() {
var moonbeam = game.addChild(new MoonBeam());
moonbeam.x = 60 + Math.random() * 1928;
moonbeam.y = 60 + Math.random() * 2612;
moonbeams.push(moonbeam);
// Add entrance effect
moonbeam.children[0].alpha = 0;
tween(moonbeam.children[0], {
alpha: 0.8
}, {
duration: 1000,
easing: tween.easeOut
});
}
// Function to spawn golden moon rock at random location
function spawnGoldenMoonRock() {
var goldenRock = game.addChild(new GoldenMoonRock());
var finalX = 80 + Math.random() * 1888;
var finalY = 80 + Math.random() * 2572;
// Start golden rock above screen
goldenRock.x = finalX;
goldenRock.y = -150;
goldenMoonRocks.push(goldenRock);
// Create blue trail effect during fall
var trailPoints = [];
var trailContainer = game.addChild(new Container());
// Add dramatic entrance effect with falling motion
goldenRock.children[0].alpha = 0;
goldenRock.children[0].scaleX = 0;
goldenRock.children[0].scaleY = 0;
// Animate falling with trail
tween(goldenRock, {
y: finalY
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Clean up trail after landing
tween(trailContainer, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
trailContainer.destroy();
}
});
}
});
// Animate appearance
tween(goldenRock.children[0], {
alpha: 0.9,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1200,
easing: tween.easeOut
});
// Create trail effect during fall
var trailTimer = 0;
var _createTrail = function createTrail() {
if (goldenRock.y < finalY - 50) {
// Create trail particle
var trailParticle = trailContainer.addChild(LK.getAsset('golden_moonstone', {
anchorX: 0.5,
anchorY: 0.5,
x: goldenRock.x + (Math.random() - 0.5) * 20,
y: goldenRock.y + (Math.random() - 0.5) * 20,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8
}));
trailParticle.tint = 0x4169E1; // Bright blue color
// Fade out trail particle
tween(trailParticle, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
trailContainer.removeChild(trailParticle);
}
});
// Schedule next trail particle
trailTimer++;
if (trailTimer < 40) {
// Create trail for first 2 seconds
LK.setTimeout(_createTrail, 50);
}
}
};
_createTrail();
}
// Function to spawn new moonstone from the sky
function spawnMoonstoneFromSky() {
var newMoonstone = game.addChild(new Moonstone());
newMoonstone.x = 150 + Math.random() * 1748;
newMoonstone.y = -100; // Start above the screen
var finalY = 150 + Math.random() * 2432;
// Store the drop position for guard alerting
lastMoonstoneDropX = newMoonstone.x;
lastMoonstoneDropY = finalY;
moonstones.push(newMoonstone);
// Activate physics if player is in kitten mode
if (isKittenMode) {
newMoonstone.activatePhysics();
}
// Animate falling from sky
tween(newMoonstone, {
y: finalY
}, {
duration: 2000,
easing: tween.easeOut,
onComplete: function onComplete() {
// Alert all guards to move towards the dropped moonstone
for (var i = 0; i < guards.length; i++) {
var guard = guards[i];
guard.isAlerted = true;
guard.alertState = 'move_to_moonstone';
guard.chaseTimer = 0;
guard.alertSpeed = 6;
// Change to chase texture
var currentGraphics = guard.children[0];
guard.removeChild(currentGraphics);
var chaseGraphics = guard.attachAsset('guard_chase', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
guard.addChild(chaseGraphics);
}
}
});
}
// Generate obstacles for hiding - rocks first, then guards, then trees (so trees appear above guards and guards above rocks)
// Generate rocks first (lowest z-index)
for (var i = 0; i < 7; i++) {
var rock = game.addChild(new Obstacle('rock'));
rock.x = 200 + Math.random() * 1648;
rock.y = 200 + Math.random() * 2332;
obstacles.push(rock);
}
// Generate guards second so they appear above rocks but below trees
for (var i = 0; i < 1; i++) {
var guard = game.addChild(new Guard());
// Spawn guards far from player spawn (1024, 1366)
var angle = Math.random() * Math.PI * 2;
var distance = 1200 + Math.random() * 800; // 1200-2000 pixels away from center
guard.x = 1024 + Math.cos(angle) * distance;
guard.y = 1366 + Math.sin(angle) * distance;
// Keep guards within game bounds
if (guard.x < 100) guard.x = 100;
if (guard.x > 1948) guard.x = 1948;
if (guard.y < 100) guard.y = 100;
if (guard.y > 2632) guard.y = 2632;
guards.push(guard);
}
// Generate trees second (higher z-index, appear above rocks)
for (var i = 0; i < 8; i++) {
// Randomly choose between classic and new tree texture
var treeType = Math.random() < 0.5 ? 'tree' : 'tree_new';
var tree = game.addChild(new Obstacle(treeType));
tree.x = 200 + Math.random() * 1648;
tree.y = 200 + Math.random() * 2332;
obstacles.push(tree);
}
// Start ambient music
LK.playMusic('ambient');
function updateTransformationUI() {
var transformationNames = ['Human Child', 'Growing Tail', 'Wolf Ears', 'Sharp Claws', 'Wolf Paws', 'Wolf Snout', 'Growing Fur', 'Full Werewolf'];
transformationText.setText(transformationNames[player.transformationLevel] || 'Full Werewolf');
}
function handleMove(x, y, obj) {
if (dragNode) {
// Track previous position for flip detection
var previousX = dragNode.x;
var previousY = dragNode.y;
// Calculate desired movement
var deltaX = x - dragNode.x;
var deltaY = y - dragNode.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Check if player is actually moving
var isMoving = distance > 0.5; // Only consider significant movement
// Update player walking state and texture
if (dragNode === player) {
player.isWalking = isMoving;
player.updateTexture();
// If player stops moving, set a timeout to return to idle texture
if (!isMoving && player.isWalking !== false) {
LK.setTimeout(function () {
// Check if player is still not moving after timeout
if (!player.isWalking) {
player.isWalking = false;
player.updateTexture();
}
}, 200); // 200ms delay before returning to idle texture
}
}
// Apply speed limit to prevent teleportation
var maxSpeed = 15; // Maximum pixels per frame
if (distance > maxSpeed) {
// Normalize the movement vector and apply speed limit
var normalizedX = deltaX / distance;
var normalizedY = deltaY / distance;
dragNode.x += normalizedX * maxSpeed;
dragNode.y += normalizedY * maxSpeed;
} else {
dragNode.x = x;
dragNode.y = y;
}
// Keep player within bounds
if (dragNode.x < 40) dragNode.x = 40;
if (dragNode.x > 2008) dragNode.x = 2008;
if (dragNode.y < 40) dragNode.y = 40;
if (dragNode.y > 2692) dragNode.y = 2692;
// Check collision with rock obstacles (walls)
if (dragNode === player) {
for (var o = 0; o < obstacles.length; o++) {
var obstacle = obstacles[o];
if (obstacle.hasCircularHitbox) {
// Only check rocks as walls
var dx = dragNode.x - obstacle.x;
var dy = dragNode.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var playerRadius = 40; // Player collision radius
if (distance < obstacle.hitboxRadius + playerRadius) {
// Player is colliding with rock wall - push back
if (distance > 0) {
var normalX = dx / distance;
var normalY = dy / distance;
var pushDistance = obstacle.hitboxRadius + playerRadius;
dragNode.x = obstacle.x + normalX * pushDistance;
dragNode.y = obstacle.y + normalY * pushDistance;
}
}
}
}
}
// Play footstep sounds when player is moving
if (dragNode === player && isMoving) {
dragNode.footstepTimer++;
if (dragNode.footstepTimer >= dragNode.footstepInterval) {
LK.getSound('footsteps').play();
dragNode.footstepTimer = 0;
}
} else if (dragNode === player && !isMoving) {
// Reset footstep timer when not moving
dragNode.footstepTimer = 0;
}
// Flip player based on movement direction
if (dragNode === player && player.children.length > 0 && player.children[0]) {
var movementX = dragNode.x - previousX;
if (movementX > 0) {
// Moving right - face right
player.children[0].scaleX = Math.abs(player.children[0].scaleX);
playerLastFacingRight = true;
} else if (movementX < 0) {
// Moving left - face left
player.children[0].scaleX = -Math.abs(player.children[0].scaleX);
playerLastFacingRight = false;
}
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
// When player stops being dragged, return to idle texture after short delay
if (player) {
LK.setTimeout(function () {
player.isWalking = false;
player.updateTexture();
}, 100); // 100ms delay to return to idle texture
}
};
game.update = function () {
// Spawn moonbeam only once per game (only if not already spawned)
if (!hasTransformedToKitten && moonbeams.length === 0) {
moonbeamEventTimer++;
if (moonbeamEventTimer >= moonbeamEventInterval) {
spawnMoonbeam();
moonbeamEventTimer = 0;
}
}
// Spawn golden moon rock every 20 seconds - DISABLED
// goldenRockTimer++;
// if (goldenRockTimer >= goldenRockInterval) {
// spawnGoldenMoonRock();
// goldenRockTimer = 0;
// }
// Update kitten mode timer
if (isKittenMode) {
kittenModeTimer++;
if (kittenModeTimer >= kittenModeDuration) {
// Revert from kitten mode
isKittenMode = false;
kittenModeTimer = 0;
// Update player texture back to normal transformation (respecting walking state)
player.updateTexture();
// Restore player size when reverting from kitten mode
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut
});
LK.effects.flashObject(player, 0xE6E6FA, 1000);
// Make guard detection ranges visible again when exiting kitten mode
for (var g = 0; g < guards.length; g++) {
var guard = guards[g];
var aura = guard.children[0]; // Aura is first child
if (aura) {
aura.visible = true;
}
}
// Bot kitten mode revert disabled
}
}
// Update giant mode timer
if (isGiantMode) {
giantModeTimer++;
if (giantModeTimer >= giantModeDuration) {
// Revert from giant mode
isGiantMode = false;
giantModeTimer = 0;
// Update player texture back to normal transformation
player.updateTexture();
// Restore player size when reverting from giant mode
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeOut
});
LK.effects.flashObject(player, 0xFFD700, 1000);
}
}
// Check golden moon rock collision (only if not in kitten mode) - DISABLED
// if (!isKittenMode) {
// for (var gr = goldenMoonRocks.length - 1; gr >= 0; gr--) {
// var goldenRock = goldenMoonRocks[gr];
// if (player.intersects(goldenRock)) {
// // Transform player to giant mode
// isGiantMode = true;
// giantModeTimer = 0;
// // Update player texture to kitten mode (used for giant mode)
// player.updateTexture();
// // Multiply player size by 20 (increased limit)
// tween(player, {
// scaleX: (player.scaleX || 1) * 20,
// scaleY: (player.scaleY || 1) * 20
// }, {
// duration: 800,
// easing: tween.easeOut
// });
// // Play transformation sound and effects
// LK.getSound('transformation').play();
// LK.effects.flashObject(player, 0xFFD700, 1500);
// // Remove the golden moon rock that was touched
// goldenRock.destroy();
// goldenMoonRocks.splice(gr, 1);
// break;
// }
// }
// }
// Update player hiding status
player.checkHiding();
// Check moonbeam collision (only if not already in kitten mode or giant mode)
if (!isKittenMode && !isGiantMode) {
for (var m = moonbeams.length - 1; m >= 0; m--) {
var moonbeam = moonbeams[m];
if (player.intersects(moonbeam)) {
// If giant mode is active, just remove the moonbeam without activating kitten mode
if (isGiantMode) {
// Remove the moonbeam without any transformation
moonbeam.destroy();
moonbeams.splice(m, 1);
break;
} else {
// Transform player to kitten temporarily
isKittenMode = true;
kittenModeTimer = 0;
// Update player texture to kitten mode (respecting walking state)
player.updateTexture();
// Reduce player size by half when entering kitten mode
tween(player, {
scaleX: (player.scaleX || 1) * 0.5,
scaleY: (player.scaleY || 1) * 0.5
}, {
duration: 500,
easing: tween.easeOut
});
// Play meow sound for kitten transformation
LK.getSound('meow').play();
// Activate physics for all existing moonstones
for (var p = 0; p < moonstones.length; p++) {
moonstones[p].activatePhysics();
}
// Make all guards passive
for (var k = 0; k < guards.length; k++) {
var guard = guards[k];
guard.isAlerted = false;
guard.alertState = 'none';
guard.chaseTimer = 0;
// Reset guard graphics to normal
var currentGuardGraphics = guard.children[0];
guard.removeChild(currentGuardGraphics);
var normalGraphics = guard.attachAsset('guard', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
guard.addChild(normalGraphics);
normalGraphics.tint = 0xFFFFFF;
}
// Remove the moonbeam that was touched
moonbeam.destroy();
moonbeams.splice(m, 1);
LK.effects.flashObject(player, 0xffa500, 1000);
break;
}
}
}
}
// Check moonstone interactions
if (!isKittenMode) {
// Normal collection when not in kitten mode
for (var i = moonstones.length - 1; i >= 0; i--) {
var moonstone = moonstones[i];
var collected = false;
// Check player collection (only if not in giant mode)
if (player.intersects(moonstone) && !isGiantMode) {
moonstone.destroy();
moonstones.splice(i, 1);
moonstonesCollected++;
playerLevel++; // Player collected - increment player level
player.transformationLevel = Math.min(playerLevel - 1, 7); // Set player transformation level
player.transform();
LK.getSound('collect').play();
LK.setScore(moonstonesCollected);
scoreText.setText('Moonstones: ' + moonstonesCollected + '/' + totalMoonstones);
updateTransformationUI();
}
// Bot collection disabled
// Spawn dog when collecting 4th moonstone
if (moonstonesCollected === 4 && !dogSpawned) {
dogSpawned = true;
dog = game.addChild(new Dog());
// Spawn dog far from player
var angle = Math.random() * Math.PI * 2;
var distance = 800 + Math.random() * 400;
dog.x = player.x + Math.cos(angle) * distance;
dog.y = player.y + Math.sin(angle) * distance;
// Keep dog within bounds
if (dog.x < 100) dog.x = 100;
if (dog.x > 1948) dog.x = 1948;
if (dog.y < 100) dog.y = 100;
if (dog.y > 2632) dog.y = 2632;
}
// Increase player size slightly with smooth animation when player collects
if (playerLevel > 0 && playerLevel <= 7) {
var newScale;
if (isGiantMode) {
// No size limit in giant mode
newScale = (player.scaleX || 1) + 0.05;
} else {
// Limited growth when not in giant mode
newScale = Math.min((player.scaleX || 1) + 0.05, 1.35); // Cap maximum scale at 1.35
}
tween(player, {
scaleX: newScale,
scaleY: newScale
}, {
duration: 500,
easing: tween.easeOut
});
}
// Spawn new moonstone from sky if there are none on screen and haven't won yet
if (moonstones.length === 0 && moonstonesCollected < totalMoonstones) {
spawnMoonstoneFromSky();
}
// Check win condition
if (moonstonesCollected >= totalMoonstones) {
LK.showYouWin();
return;
}
// Add more guards as player transforms - insert at lower z-index to appear below trees
if (moonstonesCollected % 2 == 0 && guards.length < 8) {
var newGuard = new Guard();
// Find the index where obstacles start to insert guard before them
var obstacleIndex = -1;
for (var idx = 0; idx < game.children.length; idx++) {
if (obstacles.indexOf(game.children[idx]) !== -1) {
obstacleIndex = idx;
break;
}
}
// Insert guard before obstacles if found, otherwise add normally
if (obstacleIndex !== -1) {
game.addChildAt(newGuard, obstacleIndex);
} else {
game.addChild(newGuard);
}
// Spawn new guards far from current player position
var angle = Math.random() * Math.PI * 2;
var distance = 1000 + Math.random() * 600; // 1000-1600 pixels away from player
newGuard.x = player.x + Math.cos(angle) * distance;
newGuard.y = player.y + Math.sin(angle) * distance;
// Keep guards within game bounds
if (newGuard.x < 100) newGuard.x = 100;
if (newGuard.x > 1948) newGuard.x = 1948;
if (newGuard.y < 100) newGuard.y = 100;
if (newGuard.y > 2632) newGuard.y = 2632;
guards.push(newGuard);
}
}
} else {
// Collision detection in kitten mode - trigger zigzag bouncing
for (var i = 0; i < moonstones.length; i++) {
var moonstone = moonstones[i];
if (moonstone.lastWasColliding === undefined) moonstone.lastWasColliding = false;
var currentColliding = player.intersects(moonstone);
// Check if collision just started (transition from false to true)
if (!moonstone.lastWasColliding && currentColliding) {
// Start zigzag bouncing behavior
moonstone.isZigzagBouncing = true;
moonstone.zigzagTimer = 0;
moonstone.zigzagSpeed = 8;
moonstone.zigzagDirection = Math.random() * Math.PI * 2;
// Override physics with zigzag movement
moonstone.velocityX = Math.cos(moonstone.zigzagDirection) * moonstone.zigzagSpeed;
moonstone.velocityY = Math.sin(moonstone.zigzagDirection) * moonstone.zigzagSpeed;
}
moonstone.lastWasColliding = currentColliding;
}
}
// Check guard capture (only if not in kitten mode and not in giant mode)
if (!isKittenMode && !isGiantMode) {
for (var g = 0; g < guards.length; g++) {
var guard = guards[g];
// Check player capture using smaller arrest hitbox
var guardDx = player.x - guard.x;
var guardDy = player.y - guard.y;
var guardDistance = Math.sqrt(guardDx * guardDx + guardDy * guardDy);
var playerRadius = 40; // Player collision radius
if (guardDistance < guard.arrestRadius + playerRadius) {
// Change player texture to caught state
var currentPlayerGraphics = player.children[0];
player.removeChild(currentPlayerGraphics);
var caughtGraphics = player.attachAsset('player_caught', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
player.addChild(caughtGraphics);
LK.getSound('caught').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
}
// Bot capture and rescue mechanics disabled
}
// Check dog capture
if (dog && player.intersects(dog)) {
// Change player texture to caught state
var currentPlayerGraphics = player.children[0];
player.removeChild(currentPlayerGraphics);
var caughtGraphics = player.attachAsset('player_caught', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
player.addChild(caughtGraphics);
LK.getSound('caught').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
return;
}
}
// Handle tree transparency when player touches them
for (var t = 0; t < obstacles.length; t++) {
var obstacle = obstacles[t];
// Only apply transparency to trees, not rocks
if (!obstacle.hasCircularHitbox) {
obstacle.lastPlayerTouching = obstacle.isPlayerTouching;
obstacle.isPlayerTouching = player.intersects(obstacle);
// Check if player just started touching the tree
if (!obstacle.lastPlayerTouching && obstacle.isPlayerTouching) {
// Make tree semi-transparent
tween(obstacle.children[0], {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut
});
}
// Check if player stopped touching the tree
else if (obstacle.lastPlayerTouching && !obstacle.isPlayerTouching) {
// Make tree fully opaque again
tween(obstacle.children[0], {
alpha: 1.0
}, {
duration: 500,
easing: tween.easeOut
});
}
}
}
};
Pixel art de árbol pero de noche. In-Game asset. 2d. High contrast. No shadows
Piedra lunar brillante pixelart. In-Game asset. 2d. High contrast. No shadows
Guardia completamente negro con linterna iluminando con un circulo de aura pixelart. In-Game asset. 2d. High contrast. No shadows
Cesped oscuro de noche pixel art. In-Game asset. 2d. High contrast. No shadows
Niño pero de noche pixelart. In-Game asset. 2d. High contrast. No shadows
Cola de lobo, pixelart
Garras de lobo, pixelart
Nariz de lobo, pixel art
Silueta corriendo
Jaula de metal oscura , pixel art. In-Game asset. 2d. High contrast. No shadows
Transformalo en un gatito
Elimina los cristales y agrega árboles , estilo pixelart
Ahora que el perro esté ladrando
Silueta de perro corriendo
Corriendo
Corriendo pixelart
Gatito corriendo pixelart
Corriendo , pixelart
Corriendo, pixelart
Corriendo, pixelart
Corriendo, pixelart
Quítale la camisa, aslo más grande y peludo, que este en cuatro patas , pixelart