User prompt
no i mean there in the gui theres text where it says :"npc class" and it shows the glass where the npc is in (survivors:mouse and bunny stunners:dog,fennec and (new npc) hedgehog collectors:bunny and deer supports:bat,squirrel and (new) robot
User prompt
can you make it whenever you touch an npc you can see their abbility and their class
User prompt
no i mean like classes that determine what they do like collector who stay at the campfire at night or attackers that attack an enemy and let every npc into a class and also class al the enemys(this is good for rescripting/remaking)
User prompt
the bat has a 2x multiplier tho the mouse is less visible and has a smaller visibility hitbox the mouse doesnt move when targeted make them move please they have they get invisible/save already 100 pixels near the cabin when in the cabin they are the only one that keeps giving there multiplier but their multiplier goes down to 2x they have 1 live but 2 lives during night ONLY during night (if they get hit during night they still have 1 life technichly geting 0.5 damage during night but not during blood moon,day,full moon/twisted blood moon,peacefull night) they also get a slowness if they get hit when its night time UNTIL its blood moon
User prompt
can you give me the mouse stats (also make it so that the fennec is able to move during blood moon)
User prompt
the detection range of night and day stay the same also the fennec foxes aura is both active at day and night
User prompt
no the vampire cultist needs very high detection range
User prompt
can you add a dog combat mechanich that fills 333 pixels and if an enemy steps in that they get stunned for 5 seconds and die after 10 seconds (the timer starts when the enemy is down) and also the vampire cultist has just a bit lower detection range than the wendigo so make an aura for the dog that you can see in assets and one for the vampire cultist that you cant see ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
also delete that a "vampire cultist" spawns the first bloodmoon they only spawn if a bat dies
User prompt
hey you know when you added the aura to the monsters like "zombie" and "wolf" can you like delet that please and also make an aura for the fennec foxes combat mechanic and also a aura for the dogs combat mechanic
User prompt
can you also add an aura to the zombie and wolf thing of the fenec fox ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yes please ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
okay uhm can you force spawn a vampire cultist on the first blood moon (but only 1 time and after this the vampire cultist only spawns when the bat dies even after this run)
User prompt
also lets rework the bats 2nd abillity if she dies she spawns a "vampire cultist" that kills the enemy that killed her and then chases after the player being the fastes entity and also chases after the npcs but when it turns day the "vampire cultist" disepears and drops a hear(put it in assets) that goes above every npcs head being an extra live for every npc and also gives the player an extra live ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
also hide the player for 1 second in the start ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
also dont force spawn the bat
User prompt
the boost is only at night
User prompt
make the bunny collect berrys and make the range 50 pixels but doesnt need to touch the berrys and also gets a 10x speed boost for 5 seconds also the squirell doesnt stay at the campfire it actually moves around and has a aura aurond it that you can see that fills in 100 pixels around it in a cirkel if any npc comes in contact with this they get a 2x speed boost for the duration that there in the circle if the go out of the cirkle the speedboost goes to 5x but each 2 seconds it decrease by 1 (5x(2)>4x(4)>3x(6)>2x(8)>1(10))it ends at 1 also the squirrel has 0.5 speed when targeted and also running to the cabin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
can you add vampire cultist chase theme and asset sprice
User prompt
also remake the bats 2nd abillity where she turns into another npc class called "cultist vampire" where she acts just like a zombie but with a different chase theme and jumpscare (so she technichly hides then a "cultist vampire" spawns and when it turns day the cultist vampire hides and she(the bat) shows and all of her abbility) but this only happens in full moons and blood moons
User prompt
also it still doesnt multiplie but i have a solution if theres a cat or a bunny spawn 2 wood (if theres both a cat and a bunny spawn 3 wood) and a if theres a bat spawn 4 wood (if a bat+bunny/cat spawn 5 wood and if a cat+bunny+bat spawn 10 wood) only at night
User prompt
also if the targeted npc survives they cant move and are still tinted blue ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
also if the dog is hit he teleports like the deer he stays out in bloodmoon AND full moon and fennec and mouse do get targeted by vampires
User prompt
also the bat,mouse and fennec are still targeted when it bloodmoon and the bat doesnt turn into a vampire
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bunny = Container.expand(function () { var self = Container.call(this); var bunnyGraphics = self.attachAsset('bunny', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.0 }); self.berriesGiven = 0; self.update = function () { // Gentle bouncing animation self.y += Math.sin(LK.ticks * 0.15) * 0.3; // Apply movement speed based on targeting if (self.isTargeted) { // Bunny can move when targeted but is drastically slowed var moveSpeed = 0.3; // Move very slowly towards a random direction when targeted if (LK.ticks % 300 === 0) { // Every 5 seconds instead of 2 var newX = Math.random() * (1800 - 200) + 200; var newY = Math.random() * (2400 - 200) + 200; tween(self, { x: newX, y: newY }, { duration: 6000, // Much slower movement - 6 seconds instead of 2 easing: tween.easeInOut }); } } else { // Normal movement when not targeted if (LK.ticks % 120 === 0) { // Every 2 seconds, move to a new position var newX = Math.random() * (1800 - 200) + 200; var newY = Math.random() * (2400 - 200) + 200; tween(self, { x: newX, y: newY }, { duration: 2000, easing: tween.easeInOut }); } } }; return self; }); var Campfire = Container.expand(function () { var self = Container.call(this); var campfireGraphics = self.attachAsset('campfire', { anchorX: 0.5, anchorY: 0.5 }); self.wood = 5; // Start with 5 wood self.burnRate = 1; // Wood burns every 60 frames (1 second) self.lastBurnTime = 0; self.nextBurnIn = Math.floor(Math.random() * 120) + 120; // Random 2-4 seconds (120-240 frames) self.addWood = function (amount) { self.wood += amount; updateCampfireDisplay(); }; self.update = function () { if (isNight || isPeacefulNight) { // Burn wood during night at random intervals if (LK.ticks - self.lastBurnTime >= self.nextBurnIn) { if (self.wood > 0) { self.wood--; self.lastBurnTime = LK.ticks; self.nextBurnIn = Math.floor(Math.random() * 120) + 120; // Random 2-4 seconds updateCampfireDisplay(); } else { // No wood left, start twisted blood moon only if not already in blood moon AND no wood available if (!isBloodMoon && campfire.wood === 0) { isTwistedBloodMoon = true; isNight = false; isBloodMoon = true; // Set blood moon as true for proper night detection LK.stopMusic(); LK.playMusic('twistedBloodMoonTheme'); dayNightTxt.setText('Full Moon'); dayNightTxt.fill = 0xCCCCCC; game.setBackgroundColor(0x111111); tween(background, { tint: 0x444444, alpha: 0.9 }, { duration: 1000 }); // Move all NPCs to cabin during twisted blood moon for (var i = 0; i < npcs.length; i++) { npcs[i].enterCabin(); } // Spawn vampires from bottom during twisted blood moon with cutscene var vampire1 = new Vampire(); vampire1.x = Math.random() * (1800 - 200) + 200; vampire1.y = 2800; // Start from bottom vampires.push(vampire1); game.addChild(vampire1); var vampire2 = new Vampire(); vampire2.x = Math.random() * (1800 - 200) + 200; vampire2.y = 2800; // Start from bottom vampires.push(vampire2); game.addChild(vampire2); var vampire2_1 = new Vampire2(); vampire2_1.x = Math.random() * (1800 - 200) + 200; vampire2_1.y = 2800; // Start from bottom vampires2.push(vampire2_1); game.addChild(vampire2_1); var vampire2_2 = new Vampire2(); vampire2_2.x = Math.random() * (1800 - 200) + 200; vampire2_2.y = 2800; // Start from bottom vampires2.push(vampire2_2); game.addChild(vampire2_2); self.wood = 0; // Set wood to 0 when full moon starts // Spawn 4 wood when full moon starts for (var fw = 0; fw < 4; fw++) { spawnWood(); } updateCampfireDisplay(); } } } // Fire animation effect campfireGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.3) * 0.2; campfireGraphics.scaleY = 1 + Math.cos(LK.ticks * 0.25) * 0.15; } }; return self; }); var Fox = Container.expand(function () { var self = Container.call(this); var foxGraphics = self.attachAsset('fox', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var JumpscareDisplay = Container.expand(function (assetName) { var self = Container.call(this); var jumpscareGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); self.showJumpscare = function () { // Position at center of screen self.x = 1024; self.y = 1366; // Rapid scale up with flash effect tween(jumpscareGraphics, { scaleX: 3, scaleY: 3, alpha: 1 }, { duration: 100, onFinish: function onFinish() { // Hold for a moment LK.setTimeout(function () { // Quick fade out tween(jumpscareGraphics, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); }, 300); } }); }; return self; }); var NPC = Container.expand(function (type) { var self = Container.call(this); self.type = type; // Set multiplier based on type: mouse gives 4x, cat/bunny/squirrel give 2x, bat gives 4x, others give no multiplier if (type === 'mouse' || type === 'bat') { self.multiplier = 4; } else if (type === 'cat' || type === 'bunny' || type === 'squirrel') { self.multiplier = 2; } else { self.multiplier = 0; // deer, fennec, dog don't give multipliers } self.inCabin = false; self.frozen = false; // Set hit points: deer has 2 lives, dog has 3 lives, others have 1 if (type === 'deer') { self.hitPoints = 2; } else if (type === 'dog') { self.hitPoints = 3; } else { self.hitPoints = 1; } self.isTargeted = false; self.rarity = 'common'; // Default rarity, will be set when spawned // Deer-specific properties for berry detection if (type === 'deer') { self.berryTarget = null; self.hasSpeedBoost = false; self.baseSpeed = 2; self.boostedSpeed = 8; // 4x speed boost during day self.nightBoostedSpeed = 4; // 2x speed boost during night/full moon/peaceful night } // Cat-specific properties for wood detection if (type === 'cat') { self.woodTarget = null; self.hasSpeedBoost = false; self.baseSpeed = 2; self.boostedSpeed = 8; // 4x speed boost during day self.nightBoostedSpeed = 4; // 2x speed boost during night/full moon/peaceful night } var npcGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5, scaleX: type === 'mouse' ? 2.0 : 1.2, scaleY: type === 'mouse' ? 1.8 : 1.0 }); // Add rarity visual indicator after graphics are created self.setRarityVisual = function () { // All animals keep their natural colors - no tinting applied // Rarity can be indicated through other means like size or behavior }; self.update = function () { if (self.frozen || self.inCabin) return; // Apply movement speed based on targeting and type if (self.isTargeted) { if (self.type === 'bunny') { // Bunny can move when targeted but is drastically slowed self.targetedSpeed = 0.3; } else if (self.type === 'cat' || self.type === 'squirrel') { // Cat and squirrel are more drastically slowed than deer self.targetedSpeed = 0.1; } else if (self.type === 'deer') { // Deer is slowed but not as much as commons self.targetedSpeed = 0.5; } else if (self.type === 'mouse') { // Mouse gets speed boost when targeted self.targetedSpeed = 4; } else if (self.type === 'fennec') { // Fennec can't enter cabin when targeted self.canEnterCabin = false; self.targetedSpeed = 1; } else if (self.type === 'dog') { // Dog is slowed drastically when targeted self.targetedSpeed = 0.2; } } // Check for nearby enemies that would cause NPCs to run to cabin var enemyNear = false; var shouldRunToCabin = false; // Check for wolves near this NPC for (var wf = 0; wf < wolves.length; wf++) { var wolfDist = Math.sqrt(Math.pow(wolves[wf].x - self.x, 2) + Math.pow(wolves[wf].y - self.y, 2)); if (wolfDist < 400) { enemyNear = true; break; } } // Check for wendigo near this NPC if (wendigo && !enemyNear) { var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2)); if (wendigoDist < 400) { enemyNear = true; } } // Check for vampires near this NPC if (!enemyNear) { for (var v = 0; v < vampires.length; v++) { var vampireDist = Math.sqrt(Math.pow(vampires[v].x - self.x, 2) + Math.pow(vampires[v].y - self.y, 2)); if (vampireDist < 400) { enemyNear = true; break; } } } // Check for vampires2 near this NPC if (!enemyNear) { for (var v2 = 0; v2 < vampires2.length; v2++) { var vampire2Dist = Math.sqrt(Math.pow(vampires2[v2].x - self.x, 2) + Math.pow(vampires2[v2].y - self.y, 2)); if (vampire2Dist < 400) { enemyNear = true; break; } } } // Check for zombies near this NPC if (!enemyNear) { for (var z = 0; z < zombies.length; z++) { var zombieDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2)); if (zombieDist < 400) { enemyNear = true; break; } } } // Determine if NPC should run to cabin shouldRunToCabin = isTwistedBloodMoon || isBloodMoon || enemyNear; // Special behavior during day - NPCs go to campfire and move around freely if (!isNight && !isPeacefulNight && !isBloodMoon && !isTwistedBloodMoon && !enemyNear) { // Move to campfire area during day var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 200) { // Move towards campfire if too far var moveSpeed = self.isTargeted ? self.targetedSpeed || 1 : 1; self.x += campfireDx / campfireDistance * moveSpeed; self.y += campfireDy / campfireDistance * moveSpeed; } else { // Already near campfire, do gentle movement around it self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.3; if (LK.ticks % 200 === 0) { var newX = campfire.x + (Math.random() - 0.5) * 300; var newY = campfire.y + (Math.random() - 0.5) * 300; // Keep within reasonable bounds newX = Math.max(200, Math.min(1800, newX)); newY = Math.max(200, Math.min(2400, newY)); tween(self, { x: newX, y: newY }, { duration: 3000, easing: tween.easeInOut }); } } return; } // If enemies are near or it's bloodmoon, run to cabin if (shouldRunToCabin) { self.enterCabin(); return; } // Cat behavior - collect wood with detection range and speed boost during night if (self.type === 'cat' && !self.inCabin) { // Wood detection with dynamic range and speed boost var detectionRange; var speedBoost; // Determine detection range and speed boost based on time of day if (isNight || isTwistedBloodMoon || isPeacefulNight || isBloodMoon) { // Night/full moon/peaceful night: shorter range, 2x speed boost detectionRange = 300; speedBoost = self.nightBoostedSpeed; } else { // Day: longer range, 4x speed boost detectionRange = 600; speedBoost = self.boostedSpeed; } // Find wood within detection range var woodInRange = []; for (var w = 0; w < woodItems.length; w++) { var wood = woodItems[w]; var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2)); if (woodDist <= detectionRange) { woodInRange.push({ wood: wood, distance: woodDist }); } } // If wood is in range, activate speed boost and target nearest one if (woodInRange.length > 0) { self.hasSpeedBoost = true; // Find nearest wood in range woodInRange.sort(function (a, b) { return a.distance - b.distance; }); var targetWood = woodInRange[0].wood; var targetDistance = woodInRange[0].distance; // Move towards target wood with speed boost var dx = targetWood.x - self.x; var dy = targetWood.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || speedBoost : speedBoost; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } // Collect wood if close enough if (targetDistance < 50) { for (var w = woodItems.length - 1; w >= 0; w--) { if (woodItems[w] === targetWood) { campfire.addWood(1 * self.multiplier); // Cat uses 2x multiplier LK.getSound('catWoodCollect').play(); woodItems.splice(w, 1); targetWood.destroy(); break; } } } return; } else { // No wood in range, remove speed boost and move towards nearest wood self.hasSpeedBoost = false; // Find nearest wood anywhere on the map var nearestWood = null; var nearestDistance = Infinity; for (var w = 0; w < woodItems.length; w++) { var wood = woodItems[w]; var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2)); if (woodDist < nearestDistance) { nearestDistance = woodDist; nearestWood = wood; } } if (nearestWood) { var dx = nearestWood.x - self.x; var dy = nearestWood.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || self.baseSpeed : self.baseSpeed; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } return; } } } // Bunny behavior - collect wood with 2x multiplier if (self.type === 'bunny' && !self.inCabin) { // Check for nearby wood to collect for (var w = woodItems.length - 1; w >= 0; w--) { var wood = woodItems[w]; var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2)); if (woodDist <= 150) { // Move towards wood var dx = wood.x - self.x; var dy = wood.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || 2 : 2; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } // Collect wood if close enough (2x collection) if (woodDist < 50) { woodItems.splice(w, 1); wood.destroy(); campfire.addWood(1 * self.multiplier); // Bunny uses 2x multiplier LK.getSound('woodCollect').play(); break; } return; } } } // Fennec-specific properties for stunning and cabin access if (self.type === 'fennec') { self.canEnterCabin = false; // Fennec can never enter cabin self.stunCooldown = 0; // Cooldown timer for stunning ability self.isStunned = false; // Whether fennec is currently stunned self.stunnedUntil = 0; // When stun effect ends } // Fennec fox behavior - collect berries and kill wolves/zombies if (self.type === 'fennec') { // Apply speed modifications based on moon phase var fennecSpeed = 2; // Base speed if (isBloodMoon || isTwistedBloodMoon) { fennecSpeed = 3; // Speed boost during blood moon/full moon } else if (isPeacefulNight || isNight) { fennecSpeed = 1; // Slowness during other nights } // Handle stunning effects if (self.isStunned && LK.ticks < self.stunnedUntil) { return; // Can't move when stunned } else if (self.isStunned) { self.isStunned = false; // Remove stun effect } // Reduce cooldown if (self.stunCooldown > 0) { self.stunCooldown--; } // Check for wolves and zombies to kill (150 pixel range) var targetEnemy = null; var closestEnemyDistance = Infinity; // Check for wolves within 150 pixels for (var w = 0; w < wolves.length; w++) { var wolfDist = Math.sqrt(Math.pow(wolves[w].x - self.x, 2) + Math.pow(wolves[w].y - self.y, 2)); if (wolfDist <= 150 && wolfDist < closestEnemyDistance) { targetEnemy = wolves[w]; closestEnemyDistance = wolfDist; } } // Check for zombies within 150 pixels for (var z = 0; z < zombies.length; z++) { var zombieDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2)); if (zombieDist <= 150 && zombieDist < closestEnemyDistance) { targetEnemy = zombies[z]; closestEnemyDistance = zombieDist; } } // Kill enemy if close enough and cooldown is ready if (targetEnemy && closestEnemyDistance <= 150 && self.stunCooldown === 0) { // Remove enemy from appropriate array if (targetEnemy.constructor.name === 'Wolf') { targetEnemy.destroy(); wolves.splice(wolves.indexOf(targetEnemy), 1); } else if (targetEnemy.constructor.name === 'Zombie') { targetEnemy.destroy(); zombies.splice(zombies.indexOf(targetEnemy), 1); } // Stun fennec for 3 seconds (180 frames at 60fps) self.isStunned = true; self.stunnedUntil = LK.ticks + 180; // Set cooldown for 10 seconds (600 frames at 60fps) self.stunCooldown = 600; return; // Skip other behavior while stunned } // Check for nearby berries to collect (150 pixel range) for (var b = normalBerries.length - 1; b >= 0; b--) { var berry = normalBerries[b]; var berryDist = Math.sqrt(Math.pow(berry.x - self.x, 2) + Math.pow(berry.y - self.y, 2)); if (berryDist <= 150) { // Move towards berry var dx = berry.x - self.x; var dy = berry.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || fennecSpeed : fennecSpeed; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } // Collect berry if close enough if (berryDist < 50) { normalBerries.splice(b, 1); berry.destroy(); berriesCollectedInCycle += 2; // Fennec collects 2 berries instead of 1 break; } return; } } } // Deer behavior - collect berries with detection range and speed boost if (self.type === 'deer') { // First priority: if targeted and at 1 life during blood moon, run to cabin if (isBloodMoon && self.isTargeted && self.hitPoints === 1) { self.enterCabin(); return; } // Second priority: run from wendigo if present if (isBloodMoon && wendigo) { var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2)); if (wendigoDist < 400) { // Run away from wendigo - use targetedSpeed if targeted, otherwise normal speed var speed = self.isTargeted ? self.targetedSpeed : 3; var dx = self.x - wendigo.x; var dy = self.y - wendigo.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * speed; self.y += dy / distance * speed; } return; } } // Berry detection with dynamic range and speed boost var detectionRange; var speedBoost; // Determine detection range and speed boost based on time of day if (isNight || isTwistedBloodMoon || isPeacefulNight || isBloodMoon) { // Night/full moon/peaceful night: shorter range, 2x speed boost detectionRange = 200; speedBoost = self.nightBoostedSpeed; } else { // Day: longer range, 4x speed boost detectionRange = 400; speedBoost = self.boostedSpeed; } // Find berries within detection range var berriesInRange = []; for (var b = 0; b < normalBerries.length; b++) { var berry = normalBerries[b]; var berryDist = Math.sqrt(Math.pow(berry.x - self.x, 2) + Math.pow(berry.y - self.y, 2)); if (berryDist <= detectionRange) { berriesInRange.push({ berry: berry, distance: berryDist }); } } // If berries are in range, activate speed boost and target nearest one if (berriesInRange.length > 0) { self.hasSpeedBoost = true; // Find nearest berry in range berriesInRange.sort(function (a, b) { return a.distance - b.distance; }); var targetBerry = berriesInRange[0].berry; var targetDistance = berriesInRange[0].distance; // Move towards target berry with speed boost var dx = targetBerry.x - self.x; var dy = targetBerry.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || speedBoost : speedBoost; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } // Collect berry if close enough if (targetDistance < 50) { for (var b = normalBerries.length - 1; b >= 0; b--) { if (normalBerries[b] === targetBerry) { normalBerries.splice(b, 1); targetBerry.destroy(); berriesCollectedInCycle++; // Give score when deer collects berry var scoreGain = 1 * scoreMultiplier; LK.setScore(LK.getScore() + scoreGain); updateScoreDisplay(); LK.getSound('berryCollect').play(); break; } } } return; } else { // No berries in range, remove speed boost and move towards nearest berry self.hasSpeedBoost = false; // Find nearest berry anywhere on the map var nearestBerry = null; var nearestDistance = Infinity; for (var b = 0; b < normalBerries.length; b++) { var berry = normalBerries[b]; var berryDist = Math.sqrt(Math.pow(berry.x - self.x, 2) + Math.pow(berry.y - self.y, 2)); if (berryDist < nearestDistance) { nearestDistance = berryDist; nearestBerry = berry; } } if (nearestBerry) { var dx = nearestBerry.x - self.x; var dy = nearestBerry.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || self.baseSpeed : self.baseSpeed; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } return; } } } // Mouse behavior - goes to campfire unless night/bloodmoon when it goes to cabin if (self.type === 'mouse') { if ((isNight || isBloodMoon || isPeacefulNight) && !self.inCabin) { // Mouse goes to cabin during night/bloodmoon var targetSpeed = isNight || isBloodMoon ? 5 : 3; var dx = cabin.x - self.x; var dy = cabin.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 50) { self.x += dx / distance * targetSpeed; self.y += dy / distance * targetSpeed; } else { self.enterCabin(); } return; } } // Bat behavior - flying animation, vampire transformation during blood moon, wood collection if (self.type === 'bat') { // Transform into vampire during blood moon or twisted blood moon if ((isBloodMoon || isTwistedBloodMoon) && !self.isVampire) { self.isVampire = true; // Remove bat graphics and add vampire graphics if (npcGraphics && npcGraphics.parent) { npcGraphics.parent.removeChild(npcGraphics); } npcGraphics = self.attachAsset('vampire', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.0 }); } else if (!isBloodMoon && !isTwistedBloodMoon && self.isVampire) { // Transform back to bat during day self.isVampire = false; // Remove vampire graphics and add bat graphics if (npcGraphics && npcGraphics.parent) { npcGraphics.parent.removeChild(npcGraphics); } npcGraphics = self.attachAsset('bat', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.0 }); } // Wood collection behavior with 4x multiplier if (!self.inCabin) { // Check for nearby wood to collect for (var w = woodItems.length - 1; w >= 0; w--) { var wood = woodItems[w]; var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2)); if (woodDist <= 150) { // Move towards wood var dx = wood.x - self.x; var dy = wood.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveSpeed = self.isTargeted ? self.targetedSpeed || 2 : 2; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } // Collect wood if close enough (4x collection) if (woodDist < 50) { woodItems.splice(w, 1); wood.destroy(); campfire.addWood(1 * self.multiplier); // Bat uses 4x multiplier LK.getSound('woodCollect').play(); break; } return; } } } // Flying animation - gentle up and down movement self.y += Math.sin(LK.ticks * 0.2 + self.x * 0.02) * 1.5; // Go to campfire area when not targeted if (!self.isTargeted) { var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 300) { tween(self, { x: campfire.x + (Math.random() - 0.5) * 400, y: campfire.y + (Math.random() - 0.5) * 400 }, { duration: 2500, easing: tween.easeInOut }); } } } // Dog behavior during night/blood moon if (self.type === 'dog' && (isNight || isBloodMoon) && !self.isTargeted) { // Check for nearby wolves, wendigo, and vampires var nearestThreat = null; var minDistance = Infinity; // Check wolves for (var w = 0; w < wolves.length; w++) { if (!wolves[w].stunned) { var dist = Math.sqrt(Math.pow(wolves[w].x - self.x, 2) + Math.pow(wolves[w].y - self.y, 2)); if (dist < minDistance) { minDistance = dist; nearestThreat = wolves[w]; } } } // Check wendigo if (wendigo && !wendigo.stunned) { var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2)); if (wendigoDist < minDistance) { minDistance = wendigoDist; nearestThreat = wendigo; } } // Check vampires for (var v = 0; v < vampires.length; v++) { if (!vampires[v].stunned) { var vDist = Math.sqrt(Math.pow(vampires[v].x - self.x, 2) + Math.pow(vampires[v].y - self.y, 2)); if (vDist < minDistance) { minDistance = vDist; nearestThreat = vampires[v]; } } } // Check vampires2 for (var v2 = 0; v2 < vampires2.length; v2++) { if (!vampires2[v2].stunned) { var v2Dist = Math.sqrt(Math.pow(vampires2[v2].x - self.x, 2) + Math.pow(vampires2[v2].y - self.y, 2)); if (v2Dist < minDistance) { minDistance = v2Dist; nearestThreat = vampires2[v2]; } } } // Check zombies for (var z = 0; z < zombies.length; z++) { if (!zombies[z].stunned) { var zDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2)); if (zDist < minDistance) { minDistance = zDist; nearestThreat = zombies[z]; } } } // If threat is close enough (within 300 pixels), stun it with ranged ability if (nearestThreat && minDistance < 300) { self.isTargeted = true; // Stun the threat immediately without needing to move to it self.stunThreat(nearestThreat); return; } } // Default behavior: move to campfire if not already there var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 200) { // Move towards campfire if too far var moveSpeed = self.isTargeted ? self.targetedSpeed || 1 : 1; self.x += campfireDx / campfireDistance * moveSpeed; self.y += campfireDy / campfireDistance * moveSpeed; } else if (!self.isTargeted) { // Already near campfire, do gentle movement around it self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.3; if (LK.ticks % 200 === 0) { var newX = campfire.x + (Math.random() - 0.5) * 300; var newY = campfire.y + (Math.random() - 0.5) * 300; // Keep within reasonable bounds newX = Math.max(200, Math.min(1800, newX)); newY = Math.max(200, Math.min(2400, newY)); tween(self, { x: newX, y: newY }, { duration: 3000, easing: tween.easeInOut }); } } }; self.enterCabin = function () { var targetX, targetY; // Check for any enemies nearby (within 400 pixels) var enemyNear = false; // Check for wolves for (var w = 0; w < wolves.length; w++) { var wolfDist = Math.sqrt(Math.pow(wolves[w].x - self.x, 2) + Math.pow(wolves[w].y - self.y, 2)); if (wolfDist < 400) { enemyNear = true; break; } } // Check for wendigo if (!enemyNear && wendigo) { var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2)); if (wendigoDist < 400) { enemyNear = true; } } // Check for vampires if (!enemyNear) { for (var v = 0; v < vampires.length; v++) { var vampireDist = Math.sqrt(Math.pow(vampires[v].x - self.x, 2) + Math.pow(vampires[v].y - self.y, 2)); if (vampireDist < 400) { enemyNear = true; break; } } } // Check for vampires2 if (!enemyNear) { for (var v2 = 0; v2 < vampires2.length; v2++) { var vampire2Dist = Math.sqrt(Math.pow(vampires2[v2].x - self.x, 2) + Math.pow(vampires2[v2].y - self.y, 2)); if (vampire2Dist < 400) { enemyNear = true; break; } } } // Check for zombies if (!enemyNear) { for (var z = 0; z < zombies.length; z++) { var zombieDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2)); if (zombieDist < 400) { enemyNear = true; break; } } } // Fennec can never enter cabin if (self.type === 'fennec') { self.inCabin = false; targetX = campfire.x + (Math.random() - 0.5) * 200; targetY = campfire.y + (Math.random() - 0.5) * 200; } else { // Determine where to go based on conditions for other NPCs var shouldGoCabin = isTwistedBloodMoon || isBloodMoon || enemyNear; if (shouldGoCabin) { // Go to cabin when there's danger self.inCabin = true; targetX = cabin.x; targetY = cabin.y; } else { // Go to campfire during normal times self.inCabin = false; targetX = campfire.x + (Math.random() - 0.5) * 200; targetY = campfire.y + (Math.random() - 0.5) * 200; } } tween(self, { x: targetX, y: targetY, alpha: self.inCabin ? 0.3 : 1 }, { duration: 1000 }); }; self.exitCabin = function () { self.inCabin = false; var newX = Math.random() * (1800 - 200) + 200; var newY = Math.random() * (2400 - 200) + 200; tween(self, { x: newX, y: newY, alpha: 1 }, { duration: 1000 }); }; self.freeze = function () { self.frozen = true; npcGraphics.tint = 0x4444ff; }; self.takeDamage = function (attacker) { // Special behavior for deer: unfreeze if targeted and hit by wendigo if (self.type === 'deer' && self.isTargeted && self.frozen && attacker === wendigo) { self.frozen = false; self.isTargeted = false; npcGraphics.tint = 0xFFFFFF; // Remove freeze tint // Don't take damage, just unfreeze return false; } // Special behavior for deer: remove targeting when hit by any enemy if (self.type === 'deer' && self.isTargeted) { self.isTargeted = false; } // Special behavior for deer: teleport to cabin when hit (unless already in cabin) if (self.type === 'deer' && !self.inCabin) { self.inCabin = true; tween(self, { x: cabin.x, y: cabin.y, alpha: 0.3 }, { duration: 500 }); } self.hitPoints--; // Always play kill sound when NPC gets hit LK.getSound(self.type + 'Kill').play(); // Stun the attacker for 3 seconds if provided if (attacker) { // Special case for wendigo: stun for 7 seconds instead of the normal 3 var stunDuration = attacker === wendigo ? 420 : 180; // 7 seconds for wendigo, 3 for others attacker.stunned = true; attacker.stunnedUntil = LK.ticks + stunDuration; // Find the graphics object to tint var graphicsToTint = null; if (attacker.children && attacker.children[0]) { graphicsToTint = attacker.children[0]; } else if (attacker.wendigoGraphics) { graphicsToTint = attacker.wendigoGraphics; } if (graphicsToTint) { tween(graphicsToTint, { tint: 0x4444FF }, { duration: 500 }); } } if (self.hitPoints <= 0) { // If it's a deer being killed, spawn 20 berries (can be multiplied) if (self.type === 'deer') { for (var i = 0; i < 20; i++) { LK.setTimeout(function () { spawnNormalBerry(); }, i * 100); // Stagger berry spawning } } return true; // NPC is killed } else { // Flash red to show damage tween(npcGraphics, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(npcGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } }); return false; } }; self.stunThreat = function (threat) { // Determine stun duration based on threat type var stunDuration = 300; // Default 5 seconds for most enemies if (threat === wendigo) { stunDuration = 180; // 3 seconds for wendigo } else if (threat.children && threat.children[0] && threat.children[0].texture.baseTexture.imageUrl.indexOf('vampire') !== -1) { stunDuration = 360; // 6 seconds for vampires } threat.stunned = true; threat.stunnedUntil = LK.ticks + stunDuration; // Play stun sound LK.getSound('dogStun').play(); // Make threat flash blue to show it's stunned tween(threat.children[0], { tint: 0x4444FF }, { duration: 300 }); // Dog gets slowed after stunning and retreats self.isTargeted = true; self.slowedUntil = LK.ticks + 300; // Slowed for 5 seconds var retreatX = Math.random() * (1800 - 200) + 200; var retreatY = Math.random() * (2400 - 200) + 200; tween(self, { x: retreatX, y: retreatY }, { duration: 2000, // Slower retreat due to being slowed onFinish: function onFinish() { self.isTargeted = false; } }); }; return self; }); var NormalBerry = Container.expand(function () { var self = Container.call(this); var berryGraphics = self.attachAsset('normalBerry', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Gentle floating animation self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.5; }; return self; }); var Vampire = Container.expand(function () { var self = Container.call(this); var vampireGraphics = self.attachAsset('vampire', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; // Base speed self.update = function () { // Update speed based on current state self.speed = isTwistedBloodMoon ? 6 : 4; // Faster during full moon if (!isTwistedBloodMoon) return; // During twisted blood moon cutscene, vampires move from bottom to campfire if (isBloodMoonCutscene && isTwistedBloodMoon) { // Spawn vampire from bottom if not already positioned if (self.y > 2600) { // Vampire starts from bottom, move to campfire tween(self, { x: campfire.x + (Math.random() - 0.5) * 300, y: campfire.y + (Math.random() - 0.5) * 300 }, { duration: 3000, easing: tween.easeInOut }); } // Scare nearby NPCs into cabin for (var i = 0; i < npcs.length; i++) { if (!npcs[i].inCabin && !npcs[i].killed) { var npcDist = Math.sqrt(Math.pow(npcs[i].x - self.x, 2) + Math.pow(npcs[i].y - self.y, 2)); if (npcDist < 300) { npcs[i].enterCabin(); } } } return; } // Check if stunned if (self.stunned && LK.ticks < self.stunnedUntil) { return; // Can't move when stunned } else if (self.stunned) { // Stun expired, remove stun self.stunned = false; tween(vampireGraphics, { tint: 0xFFFFFF }, { duration: 300 }); } // Find fox to attack first, then NPCs var target = null; var closestDistance = Infinity; // Check distance to fox var foxDx = fox.x - self.x; var foxDy = fox.y - self.y; var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy); if (foxDistance < closestDistance) { closestDistance = foxDistance; target = fox; } // Find nearest NPC if fox is too far (excluding bat, mouse, and fennec) var closestNPC = null; var closestNPCDistance = Infinity; for (var i = 0; i < npcs.length; i++) { if (!npcs[i].inCabin && !npcs[i].killed && npcs[i].type !== 'bat' && npcs[i].type !== 'mouse' && npcs[i].type !== 'fennec') { var dx = npcs[i].x - self.x; var dy = npcs[i].y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestNPCDistance) { closestNPCDistance = distance; closestNPC = npcs[i]; } } } // Choose closest target between fox and NPCs if (closestNPCDistance < closestDistance) { target = closestNPC; closestDistance = closestNPCDistance; } // Add random offset to movement to prevent grouping var randomOffsetX = (Math.random() - 0.5) * 100; var randomOffsetY = (Math.random() - 0.5) * 100; // Move towards target with random offset if (target) { var dx = target.x + randomOffsetX - self.x; var dy = target.y + randomOffsetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Attack if close enough if (target === fox && distance < 50) { // Vampire caught fox LK.effects.flashScreen(0xFF0000, 500); // Create and show vampire jumpscare var vampireJumpscare = new JumpscareDisplay('vampireJumpscare'); game.addChild(vampireJumpscare); vampireJumpscare.showJumpscare(); // Play jumpscare stinger immediately LK.getSound('jumpscareStinger').play(); // Play vampire sound slightly delayed LK.setTimeout(function () { LK.getSound('vampireJumpscare').play(); }, 100); // Stun ALL enemies, but kill only this vampire // Stun all wolves for (var w = 0; w < wolves.length; w++) { wolves[w].stunned = true; wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wolves[w].children[0]) { tween(wolves[w].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all other vampires for (var v = 0; v < vampires.length; v++) { vampires[v].stunned = true; vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires[v].children[0]) { tween(vampires[v].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires2 for (var v2 = 0; v2 < vampires2.length; v2++) { vampires2[v2].stunned = true; vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires2[v2].children[0]) { tween(vampires2[v2].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all zombies for (var z = 0; z < zombies.length; z++) { zombies[z].stunned = true; zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (zombies[z].children[0]) { tween(zombies[z].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun wendigo if it exists if (wendigo) { wendigo.stunned = true; wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wendigo.wendigoGraphics) { tween(wendigo.wendigoGraphics, { tint: 0x4444FF }, { duration: 500 }); } } // Kill only this vampire that hit the player self.destroy(); vampires.splice(vampires.indexOf(self), 1); playerLives--; updateLivesDisplay(); LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; } }, 1000); } else if (target !== fox && distance < 50) { // Attack NPC var isKilled = target.takeDamage(self); if (isKilled) { target.killed = true; tween(target, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { target.destroy(); npcs.splice(npcs.indexOf(target), 1); } }); } } } // Vampire animation vampireGraphics.scaleX = 0.8 + Math.sin(LK.ticks * 0.4) * 0.1; vampireGraphics.scaleY = 0.8 + Math.cos(LK.ticks * 0.3) * 0.1; }; return self; }); var Vampire2 = Container.expand(function () { var self = Container.call(this); var vampire2Graphics = self.attachAsset('vampire2', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; // Base speed self.update = function () { // Update speed based on current state self.speed = isTwistedBloodMoon ? 6 : 4; // Faster during full moon if (!isTwistedBloodMoon) return; // During twisted blood moon cutscene, vampire2 move from bottom to campfire if (isBloodMoonCutscene && isTwistedBloodMoon) { // Spawn vampire2 from bottom if not already positioned if (self.y > 2600) { // Vampire2 starts from bottom, move to campfire tween(self, { x: campfire.x + (Math.random() - 0.5) * 350, y: campfire.y + (Math.random() - 0.5) * 350 }, { duration: 3500, easing: tween.easeInOut }); } // Scare nearby NPCs into cabin for (var i = 0; i < npcs.length; i++) { if (!npcs[i].inCabin && !npcs[i].killed) { var npcDist = Math.sqrt(Math.pow(npcs[i].x - self.x, 2) + Math.pow(npcs[i].y - self.y, 2)); if (npcDist < 300) { npcs[i].enterCabin(); } } } return; } // Check if stunned if (self.stunned && LK.ticks < self.stunnedUntil) { return; // Can't move when stunned } else if (self.stunned) { // Stun expired, remove stun self.stunned = false; tween(vampire2Graphics, { tint: 0xFFFFFF }, { duration: 300 }); } // Find fox to attack first, then NPCs var target = null; var closestDistance = Infinity; // Check distance to fox var foxDx = fox.x - self.x; var foxDy = fox.y - self.y; var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy); if (foxDistance < closestDistance) { closestDistance = foxDistance; target = fox; } // Find nearest NPC if fox is too far (excluding bat, mouse, and fennec) var closestNPC = null; var closestNPCDistance = Infinity; for (var i = 0; i < npcs.length; i++) { if (!npcs[i].inCabin && !npcs[i].killed && npcs[i].type !== 'bat' && npcs[i].type !== 'mouse' && npcs[i].type !== 'fennec') { var dx = npcs[i].x - self.x; var dy = npcs[i].y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestNPCDistance) { closestNPCDistance = distance; closestNPC = npcs[i]; } } } // Choose closest target between fox and NPCs if (closestNPCDistance < closestDistance) { target = closestNPC; closestDistance = closestNPCDistance; } // Add different random offset for vampire2 to prevent grouping var randomOffsetX = (Math.random() - 0.5) * 150; var randomOffsetY = (Math.random() - 0.5) * 150; // Move towards target with random offset if (target) { var dx = target.x + randomOffsetX - self.x; var dy = target.y + randomOffsetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Attack if close enough if (target === fox && distance < 50) { // Vampire caught fox LK.effects.flashScreen(0xFF0000, 500); // Create and show vampire jumpscare var vampireJumpscare = new JumpscareDisplay('vampireJumpscare'); game.addChild(vampireJumpscare); vampireJumpscare.showJumpscare(); // Play jumpscare stinger immediately LK.getSound('jumpscareStinger').play(); // Play vampire sound slightly delayed LK.setTimeout(function () { LK.getSound('vampireJumpscare').play(); }, 100); // Stun ALL enemies, but kill only this vampire2 // Stun all wolves for (var w = 0; w < wolves.length; w++) { wolves[w].stunned = true; wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wolves[w].children[0]) { tween(wolves[w].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires for (var v = 0; v < vampires.length; v++) { vampires[v].stunned = true; vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires[v].children[0]) { tween(vampires[v].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all other vampires2 for (var v2 = 0; v2 < vampires2.length; v2++) { vampires2[v2].stunned = true; vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires2[v2].children[0]) { tween(vampires2[v2].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all zombies for (var z = 0; z < zombies.length; z++) { zombies[z].stunned = true; zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (zombies[z].children[0]) { tween(zombies[z].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun wendigo if it exists if (wendigo) { wendigo.stunned = true; wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wendigo.wendigoGraphics) { tween(wendigo.wendigoGraphics, { tint: 0x4444FF }, { duration: 500 }); } } // Kill only this vampire2 that hit the player self.destroy(); vampires2.splice(vampires2.indexOf(self), 1); playerLives--; updateLivesDisplay(); LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; } }, 1000); } else if (target !== fox && distance < 50) { // Attack NPC var isKilled = target.takeDamage(self); if (isKilled) { target.killed = true; tween(target, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { target.destroy(); npcs.splice(npcs.indexOf(target), 1); } }); } } } // Vampire animation vampire2Graphics.scaleX = 0.8 + Math.sin(LK.ticks * 0.4) * 0.1; vampire2Graphics.scaleY = 0.8 + Math.cos(LK.ticks * 0.3) * 0.1; }; return self; }); var Wendigo = Container.expand(function () { var self = Container.call(this); // Create a scary looking wendigo using a dark red box shape var wendigoGraphics = self.attachAsset('wendigoBody', { anchorX: 0.5, anchorY: 0.5 }); self.wendigoGraphics = wendigoGraphics; // Store reference for stunning self.speed = 3; self.lastFoxX = 0; self.lastFoxY = 0; self.update = function () { // Wendigo appears during blood moon or twisted blood moon if (!isBloodMoon && !isTwistedBloodMoon) return; if (isTwistedBloodMoon) { // During full moon, wendigo runs through but doesn't attack // Simple movement animation without targeting wendigoGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.2) * 0.1; wendigoGraphics.scaleY = 1 + Math.cos(LK.ticks * 0.2) * 0.1; // Move wendigo across screen self.x += 2; if (self.x > 2200) { self.x = -200; } return; } if (self.stunned && LK.ticks < self.stunnedUntil) { // Wendigo is stunned, can't move return; } else if (self.stunned) { // Stun expired, remove stun self.stunned = false; tween(wendigoGraphics, { tint: 0xFFFFFF }, { duration: 300 }); } // Check for dog collision for stunning for (var d = 0; d < npcs.length; d++) { if (npcs[d].type === 'dog' && npcs[d].isTargeted && self.intersects(npcs[d])) { npcs[d].stunThreat(self); break; } } // Calculate survival time and increase speed accordingly var survivalTime = (LK.ticks - bloodMoonStartTime) / 60; // Convert to seconds var currentSpeed = self.speed + survivalTime * wendigoSpeedIncreaseRate; // Wendigo targeting with probability system var target = null; var availableTargets = []; // Check for frozen NPC first (always highest priority) for (var i = 0; i < npcs.length; i++) { if (npcs[i].frozen && !npcs[i].killed) { target = npcs[i]; break; } } // If no frozen NPC, use probability system (excluding mouse, bat, and fennec from targeting) if (!target) { // Create weighted target list based on probabilities for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; if (!npc.inCabin && !npc.killed && npc.type !== 'mouse' && npc.type !== 'bat' && npc.type !== 'fennec') { var weight = 0; if (npc.type === 'cat' || npc.type === 'bunny' || npc.type === 'squirrel') { weight = 25; // Common NPCs 25% } else if (npc.type === 'dog') { weight = 50; // Dog 50% } else if (npc.type === 'deer') { weight = 30; // Deer 30% } // Add multiple entries based on weight for probability for (var w = 0; w < weight; w++) { availableTargets.push(npc); } } } // Select random target from weighted list if (availableTargets.length > 0) { target = availableTargets[Math.floor(Math.random() * availableTargets.length)]; target.isTargeted = true; // Mark target as targeted } } if (!target) { target = fox; } if (!target) return; // Move towards target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Start wendigo chase music if targeting fox and close enough if (target === fox && distance < 400 && !isWendigoChaseThemePlaying) { isWendigoChaseThemePlaying = true; LK.stopMusic(); LK.playMusic('wendigoChaseTheme'); } else if ((target !== fox || distance >= 500) && isWendigoChaseThemePlaying) { // Stop chase music if not targeting fox or far away isWendigoChaseThemePlaying = false; LK.stopMusic(); LK.playMusic('bloodMoonMusic'); } if (distance > 0) { // Normalize direction and apply increasing speed self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; } // Check if wendigo caught frozen NPC for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; if (npc.frozen && !npc.killed && self.intersects(npc)) { // Attack the NPC (dogs take 2 hits, others die in 1) var isKilled = npc.takeDamage(self); if (isKilled) { npc.killed = true; // During blood moon cutscene, mark NPC for delayed removal if (isBloodMoonCutscene) { cutsceneNPCKilled = npc; cutsceneNPCKillTime = LK.ticks; // Make NPC fade but don't remove immediately tween(npc, { alpha: 0.3, tint: 0x888888 }, { duration: 500 }); } else { // Normal behavior - make NPC disappear with death effect tween(npc, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { npc.destroy(); npcs.splice(npcs.indexOf(npc), 1); } }); } } // Play kill sound LK.getSound('wendigoGrowl').play(); break; } } // Check if wendigo caught the fox if (self.intersects(fox)) { // Fox is caught - create jumpscare effect LK.effects.flashScreen(0x000000, 700); // Create and show wendigo jumpscare var wendigoJumpscare = new JumpscareDisplay('wendigoJumpscare'); game.addChild(wendigoJumpscare); wendigoJumpscare.showJumpscare(); // Play jumpscare stinger immediately LK.getSound('jumpscareStinger').play(); // Scale wendigo up for additional effect tween(wendigo, { scaleX: 2.5, scaleY: 2.5 }, { duration: 150 }); // Play wendigo growl slightly delayed LK.setTimeout(function () { LK.getSound('wendigoGrowl').play(); }, 150); // Stun ALL enemies, but kill only this wendigo // Stun all wolves for (var w = 0; w < wolves.length; w++) { wolves[w].stunned = true; wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wolves[w].children[0]) { tween(wolves[w].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires for (var v = 0; v < vampires.length; v++) { vampires[v].stunned = true; vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires[v].children[0]) { tween(vampires[v].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires2 for (var v2 = 0; v2 < vampires2.length; v2++) { vampires2[v2].stunned = true; vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires2[v2].children[0]) { tween(vampires2[v2].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all zombies for (var z = 0; z < zombies.length; z++) { zombies[z].stunned = true; zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (zombies[z].children[0]) { tween(zombies[z].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Kill only this wendigo that hit the player self.destroy(); wendigo = null; // Decrease player lives playerLives--; updateLivesDisplay(); // Show scary game over after jumpscare LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; // End blood moon early when player gets caught isBloodMoon = false; startDay(); } }, 1200); return; } // Add scary breathing/pulsing effect wendigoGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.2) * 0.1; wendigoGraphics.scaleY = 1 + Math.cos(LK.ticks * 0.2) * 0.1; }; return self; }); var Wolf = Container.expand(function () { var self = Container.call(this); var wolfGraphics = self.attachAsset('wolf', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.update = function () { if (!isNight) return; if (self.stunned && LK.ticks < self.stunnedUntil) { // Wolf is stunned, can't move - stop chase music if this wolf was chasing if (isChaseThemePlaying) { var anyOtherWolfChasing = false; for (var wc = 0; wc < wolves.length; wc++) { var wolf = wolves[wc]; if (wolf !== self && wolf && !wolf.stunned) { var foxDist = Math.sqrt(Math.pow(fox.x - wolf.x, 2) + Math.pow(fox.y - wolf.y, 2)); if (foxDist < 800) { anyOtherWolfChasing = true; break; } } } if (!anyOtherWolfChasing) { isChaseThemePlaying = false; LK.stopMusic(); if (isPeacefulNight) { LK.playMusic('peacefulNightAmbience'); } else { LK.playMusic('nightMusic'); } } } return; } else if (self.stunned) { // Stun expired, remove stun self.stunned = false; tween(wolfGraphics, { tint: 0xFFFFFF }, { duration: 300 }); } // Check for dog collision for stunning for (var d = 0; d < npcs.length; d++) { if (npcs[d].type === 'dog' && npcs[d].isTargeted && self.intersects(npcs[d])) { npcs[d].stunThreat(self); break; } } // Find closest target (fox or NPCs) var closestTarget = null; var closestDistance = Infinity; var detectionRange = 800; // Bigger detection range for wolves // Check distance to fox var foxDx = fox.x - self.x; var foxDy = fox.y - self.y; var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy); if (foxDistance < detectionRange && foxDistance < closestDistance) { closestTarget = fox; closestDistance = foxDistance; } // Check distance to NPCs near campfire (excluding bats) for (var n = 0; n < npcs.length; n++) { if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat') { var npcDx = npcs[n].x - self.x; var npcDy = npcs[n].y - self.y; var npcDistance = Math.sqrt(npcDx * npcDx + npcDy * npcDy); // Check if NPC is near campfire (within 300 pixels of campfire) var npcToCampfireDist = Math.sqrt(Math.pow(npcs[n].x - campfire.x, 2) + Math.pow(npcs[n].y - campfire.y, 2)); if (npcToCampfireDist < 300 && npcDistance < detectionRange && npcDistance < closestDistance) { closestTarget = npcs[n]; closestDistance = npcDistance; } } } // If no high priority target found, go to campfire if (!closestTarget) { var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 200) { // Stay around campfire area closestTarget = campfire; closestDistance = campfireDistance; } } // Only chase if there's a target within range if (closestTarget && (closestDistance < detectionRange || closestTarget === campfire)) { // Start chase music if not already playing and wolf is chasing fox if (!isChaseThemePlaying && closestTarget === fox && closestDistance < detectionRange) { isChaseThemePlaying = true; LK.stopMusic(); LK.playMusic('chaseTheme'); } var dx = closestTarget.x - self.x; var dy = closestTarget.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { // Use slower speed when going to campfire, normal speed for other targets var moveSpeed = closestTarget === campfire ? self.speed * 0.5 : self.speed; self.x += dx / distance * moveSpeed; self.y += dy / distance * moveSpeed; } } else { // No valid target - check if we should stop chase music if (isChaseThemePlaying) { // Check if ANY wolf is actively chasing fox within detection range var anyWolfChasing = false; for (var wc = 0; wc < wolves.length; wc++) { var wolf = wolves[wc]; if (wolf && !wolf.stunned) { var foxDist = Math.sqrt(Math.pow(fox.x - wolf.x, 2) + Math.pow(fox.y - wolf.y, 2)); if (foxDist < detectionRange) { anyWolfChasing = true; break; } } } if (!anyWolfChasing) { isChaseThemePlaying = false; LK.stopMusic(); if (isPeacefulNight) { LK.playMusic('peacefulNightAmbience'); } else { LK.playMusic('nightMusic'); } } } // Add prowling animation when not chasing wolfGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.1; // 50% chance to target a berry and wait there if (!self.berryTarget && !self.wanderTarget && Math.random() < 0.01) { if (Math.random() < 0.5 && normalBerries.length > 0) { // 50% chance to target a berry self.berryTarget = normalBerries[Math.floor(Math.random() * normalBerries.length)]; } else { // Otherwise wander normally self.wanderTarget = { x: Math.random() * (1800 - 200) + 200, y: Math.random() * (2400 - 200) + 200 }; } } // Move towards berry target and wait there if (self.berryTarget) { // Check if berry still exists var berryExists = false; for (var b = 0; b < normalBerries.length; b++) { if (normalBerries[b] === self.berryTarget) { berryExists = true; break; } } if (!berryExists) { // Berry was collected, pick a new target self.berryTarget = null; } else { var berryDx = self.berryTarget.x - self.x; var berryDy = self.berryTarget.y - self.y; var berryDistance = Math.sqrt(berryDx * berryDx + berryDy * berryDy); if (berryDistance > 100) { // Move towards berry var berrySpeed = self.speed * 0.4; self.x += berryDx / berryDistance * berrySpeed; self.y += berryDy / berryDistance * berrySpeed; } // If close enough to berry (within 100 pixels), just wait there } } // Wandering behavior when not targeting berry or chasing if (!self.berryTarget && self.wanderTarget) { var wanderDx = self.wanderTarget.x - self.x; var wanderDy = self.wanderTarget.y - self.y; var wanderDistance = Math.sqrt(wanderDx * wanderDx + wanderDy * wanderDy); if (wanderDistance > 50) { // Only move if not close to target var wanderSpeed = self.speed * 0.3; // Slower wandering speed self.x += wanderDx / wanderDistance * wanderSpeed; self.y += wanderDy / wanderDistance * wanderSpeed; } else { // Reached wander target, pick a new one self.wanderTarget = null; } } } // Check if wolf caught an NPC near campfire (excluding bats) for (var n = 0; n < npcs.length; n++) { if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat' && self.intersects(npcs[n])) { var npcToCampfireDist = Math.sqrt(Math.pow(npcs[n].x - campfire.x, 2) + Math.pow(npcs[n].y - campfire.y, 2)); if (npcToCampfireDist < 300) { // Wolf attacks NPC near campfire var isKilled = npcs[n].takeDamage(self); if (isKilled) { npcs[n].killed = true; tween(npcs[n], { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { npcs[n].destroy(); npcs.splice(npcs.indexOf(npcs[n]), 1); } }); } else { // NPC runs around briefly then returns to campfire npcs[n].runFromWolf = true; npcs[n].runStartTime = LK.ticks; var runX = Math.random() * (1800 - 200) + 200; var runY = Math.random() * (2400 - 200) + 200; tween(npcs[n], { x: runX, y: runY }, { duration: 2000, onFinish: function onFinish() { // Return to campfire area var campfireReturnX = campfire.x + (Math.random() - 0.5) * 200; var campfireReturnY = campfire.y + (Math.random() - 0.5) * 200; tween(npcs[n], { x: campfireReturnX, y: campfireReturnY }, { duration: 1500, onFinish: function onFinish() { npcs[n].runFromWolf = false; } }); } }); } break; } } } // Check if wolf caught the fox for jumpscare if (self.intersects(fox)) { // Wolf jumpscare effect LK.effects.flashScreen(0xFFFFFF, 400); // Create and show wolf jumpscare var wolfJumpscare = new JumpscareDisplay('wolfJumpscare'); game.addChild(wolfJumpscare); wolfJumpscare.showJumpscare(); // Play jumpscare stinger immediately LK.getSound('jumpscareStinger').play(); // Scale wolf up for additional effect tween(self, { scaleX: 2, scaleY: 2 }, { duration: 120 }); // Play wolf roar slightly delayed LK.setTimeout(function () { LK.getSound('wolfRoar').play(); }, 120); // Stun ALL enemies, but kill only this wolf // Stun all other wolves for (var w = 0; w < wolves.length; w++) { wolves[w].stunned = true; wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wolves[w].children[0]) { tween(wolves[w].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires for (var v = 0; v < vampires.length; v++) { vampires[v].stunned = true; vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires[v].children[0]) { tween(vampires[v].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires2 for (var v2 = 0; v2 < vampires2.length; v2++) { vampires2[v2].stunned = true; vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires2[v2].children[0]) { tween(vampires2[v2].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all zombies for (var z = 0; z < zombies.length; z++) { zombies[z].stunned = true; zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (zombies[z].children[0]) { tween(zombies[z].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun wendigo if it exists if (wendigo) { wendigo.stunned = true; wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wendigo.wendigoGraphics) { tween(wendigo.wendigoGraphics, { tint: 0x4444FF }, { duration: 500 }); } } // Kill only this wolf that hit the player self.destroy(); wolves.splice(wolves.indexOf(self), 1); // Decrease player lives playerLives--; updateLivesDisplay(); // Check if game over or respawn LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; } }, 1000); return; } }; return self; }); var Wood = Container.expand(function () { var self = Container.call(this); var woodGraphics = self.attachAsset('wood', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Gentle floating animation self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.5; }; return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1; // Slow zombie self.update = function () { if (!isNight && !isTwistedBloodMoon && !isBloodMoon) return; // During blood moon cutscene, wait at campfire but don't follow wendigo if (isBloodMoonCutscene) { // If wendigo is at campfire and no NPCs around, zombies wait at campfire if (cutsceneWendigoAtCampfire && cutsceneZombiesWaiting) { var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 150) { // Move to campfire area self.x += campfireDx / campfireDistance * (self.speed * 0.5); self.y += campfireDy / campfireDistance * (self.speed * 0.5); } return; } // If wendigo killed an NPC, stay still for 5 seconds if (cutsceneNPCKilled && LK.ticks - cutsceneNPCKillTime < 300) { return; // Stay still } // Allow normal zombie behavior during blood moon cutscene when not waiting } // Check if stunned if (self.stunned && LK.ticks < self.stunnedUntil) { return; // Can't move when stunned } else if (self.stunned) { // Stun expired, remove stun self.stunned = false; tween(zombieGraphics, { tint: 0xFFFFFF }, { duration: 300 }); } // Zombies prioritize NPCs not in cabin over fox during blood moon var closestTarget = null; var closestDistance = Infinity; var detectionRange = 600; // Increased detection range for better chasing // First priority: Check for NPCs that are not in cabin (excluding bat, mouse, and fennec) for (var n = 0; n < npcs.length; n++) { if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat' && npcs[n].type !== 'mouse' && npcs[n].type !== 'fennec') { var npcDx = npcs[n].x - self.x; var npcDy = npcs[n].y - self.y; var npcDistance = Math.sqrt(npcDx * npcDx + npcDy * npcDy); if (npcDistance < detectionRange && npcDistance < closestDistance) { closestTarget = npcs[n]; closestDistance = npcDistance; } } } // Second priority: Check distance to fox only if no NPCs available if (!closestTarget) { var foxDx = fox.x - self.x; var foxDy = fox.y - self.y; var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy); if (foxDistance < detectionRange) { closestTarget = fox; closestDistance = foxDistance; } } // Only chase if there's a target within range if (closestTarget && closestDistance < detectionRange) { // Start chase music if not already playing and zombie is chasing fox if (!isZombieChaseThemePlaying && closestTarget === fox) { isZombieChaseThemePlaying = true; LK.stopMusic(); LK.playMusic('zombieChaseTheme'); } var dx = closestTarget.x - self.x; var dy = closestTarget.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } else { // Check if we should stop chase music if (isZombieChaseThemePlaying) { var anyZombieChasing = false; for (var zc = 0; zc < zombies.length; zc++) { if (zombies[zc] !== self) { var foxDist = Math.sqrt(Math.pow(fox.x - zombies[zc].x, 2) + Math.pow(fox.y - zombies[zc].y, 2)); if (foxDist < detectionRange) { anyZombieChasing = true; break; } } } if (!anyZombieChasing) { isZombieChaseThemePlaying = false; LK.stopMusic(); if (isTwistedBloodMoon) { LK.playMusic('twistedBloodMoonTheme'); } else if (isBloodMoon) { LK.playMusic('bloodMoonMusic'); } else if (isPeacefulNight) { LK.playMusic('peacefulNightAmbience'); } else if (isNight) { LK.playMusic('nightMusic'); } } } // Add prowling animation when not chasing zombieGraphics.scaleX = 0.7 + Math.sin(LK.ticks * 0.1) * 0.05; // Simplified movement - no berry targeting, focus on chasing } // Check collision with NPCs first (higher priority than fox) for (var n = 0; n < npcs.length; n++) { if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat' && npcs[n].type !== 'mouse' && npcs[n].type !== 'fennec' && self.intersects(npcs[n])) { // Zombie attacks NPC var isKilled = npcs[n].takeDamage(self); if (isKilled) { npcs[n].killed = true; tween(npcs[n], { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { npcs[n].destroy(); npcs.splice(npcs.indexOf(npcs[n]), 1); } }); } break; } } // Check collision with fox if (self.intersects(fox)) { // Zombie caught fox LK.effects.flashScreen(0x00FF00, 300); // Create and show zombie jumpscare var zombieJumpscare = new JumpscareDisplay('zombieJumpscare'); game.addChild(zombieJumpscare); zombieJumpscare.showJumpscare(); // Play jumpscare stinger immediately LK.getSound('jumpscareStinger').play(); // Play zombie sound slightly delayed LK.setTimeout(function () { LK.getSound('glitchSound').play(); }, 100); // Stun ALL enemies, but kill only this zombie // Stun all wolves for (var w = 0; w < wolves.length; w++) { wolves[w].stunned = true; wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wolves[w].children[0]) { tween(wolves[w].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires for (var v = 0; v < vampires.length; v++) { vampires[v].stunned = true; vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires[v].children[0]) { tween(vampires[v].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all vampires2 for (var v2 = 0; v2 < vampires2.length; v2++) { vampires2[v2].stunned = true; vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (vampires2[v2].children[0]) { tween(vampires2[v2].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun all other zombies for (var z = 0; z < zombies.length; z++) { zombies[z].stunned = true; zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (zombies[z].children[0]) { tween(zombies[z].children[0], { tint: 0x4444FF }, { duration: 500 }); } } // Stun wendigo if it exists if (wendigo) { wendigo.stunned = true; wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps if (wendigo.wendigoGraphics) { tween(wendigo.wendigoGraphics, { tint: 0x4444FF }, { duration: 500 }); } } // Kill only this zombie that hit the player self.destroy(); zombies.splice(zombies.indexOf(self), 1); playerLives--; updateLivesDisplay(); LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; } }, 800); return; } // Zombie animation zombieGraphics.scaleY = 0.7 + Math.cos(LK.ticks * 0.15) * 0.05; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228b22 }); /**** * Game Code ****/ // Game state variables var fox; var normalBerries = []; var wolves = []; var npcs = []; var dragNode = null; var berrySpawnTimer = 0; var wendigo; var wendigoSpeedIncreaseRate = 0.02; var bunny; var cabin; // Cutscene variables var isIntroCutscene = true; var introCutsceneTimer = 0; var introCutsceneDelay = 120; // 2 seconds delay before cutscene starts var foxAppearedSoundPlayed = false; var doorSoundPlayed = false; var isBloodMoonCutscene = false; var bloodMoonCutsceneTimer = 0; var cutsceneNPCKilled = null; var cutsceneNPCKillTime = 0; var cutsceneWendigoAtCampfire = false; var cutsceneZombiesWaiting = false; var cutsceneZombiesWaitStartTime = 0; var cutsceneNPCSpawned = false; // Day/Night cycle variables var isNight = false; var isBloodMoon = false; var isPeacefulNight = false; var isTwistedBloodMoon = false; var vampires = []; var vampires2 = []; var zombies = []; var nightCount = 0; var dayCount = 1; var berriesCollectedInCycle = 0; var scoreMultiplier = 1; var bloodMoonStartTime = 0; var bloodMoonCount = 0; var dayLengthMultiplier = 1; var isChaseThemePlaying = false; var isZombieChaseThemePlaying = false; var isWendigoChaseThemePlaying = false; var playerLives = 3; var campfire; var woodItems = []; var twistedBloodMoonWoodCollected = 0; // Track wood collected during full moon var lastBloodMoonNight = -1; // Track when last blood moon occurred var woodSpawnTimer = 0; // Timer for wood spawning during different phases // NPC types organized by rarity var commonNPCs = ['cat', 'bunny', 'squirrel']; var uncommonNPCs = ['deer', 'fennec']; var rareNPCs = ['dog', 'mouse', 'bat']; // Add forest background var background = game.attachAsset('forestBackground', { x: 0, y: 0 }); // Create score display var scoreTxt = new Text2('Score: 0 (x1)', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Create day/night indicator var dayNightTxt = new Text2('Day', { size: 60, fill: 0xFFFF00 }); dayNightTxt.anchor.set(0.5, 0); dayNightTxt.y = 100; LK.gui.top.addChild(dayNightTxt); // Create day counter display var dayCounterTxt = new Text2('Day 1', { size: 60, fill: 0xFFFFFF }); dayCounterTxt.anchor.set(0.5, 0); dayCounterTxt.y = 170; LK.gui.top.addChild(dayCounterTxt); // Create night counter display var nightCounterTxt = new Text2('Night 1', { size: 60, fill: 0xFFFFFF }); nightCounterTxt.anchor.set(0.5, 0); nightCounterTxt.y = 170; nightCounterTxt.visible = false; // Hidden by default during day LK.gui.top.addChild(nightCounterTxt); // Create lives counter display var livesTxt = new Text2('Lives: 3', { size: 60, fill: 0xFF0000 }); livesTxt.anchor.set(0.5, 0); livesTxt.y = 240; LK.gui.top.addChild(livesTxt); // Create campfire wood counter display var campfireTxt = new Text2('Campfire: 0 wood', { size: 50, fill: 0xFFFFFF }); campfireTxt.anchor.set(0.5, 0); campfireTxt.y = 310; campfireTxt.visible = false; // Hidden by default LK.gui.top.addChild(campfireTxt); // Create wood multiplier display var woodMultiplierTxt = new Text2('Wood Multiplier: Cat 2x, Bunny 2x, Bat 4x', { size: 45, fill: 0x90EE90 }); woodMultiplierTxt.anchor.set(0.5, 0); woodMultiplierTxt.y = 370; woodMultiplierTxt.visible = false; // Hidden by default LK.gui.top.addChild(woodMultiplierTxt); // Add cabin for NPCs cabin = game.attachAsset('cabin', { anchorX: 0.5, anchorY: 0.5, x: 1650, y: 450 }); // Add campfire campfire = game.addChild(new Campfire()); campfire.x = 1024; campfire.y = 1000; // Bunny removed from initial spawn // Initialize fox fox = game.addChild(new Fox()); // Start fox at cabin for intro cutscene fox.x = cabin.x; fox.y = cabin.y; // Spawn normal berry function function spawnNormalBerry() { // Spawn single berry - no duplication var berry = new NormalBerry(); berry.x = Math.random() * (2048 - 120) + 60; berry.y = Math.random() * (2732 - 120) + 60; normalBerries.push(berry); game.addChild(berry); } // Spawn wolf function function spawnWolf() { var wolf = new Wolf(); wolf.x = Math.random() * (2048 - 120) + 60; wolf.y = Math.random() * (2732 - 120) + 60; wolves.push(wolf); game.addChild(wolf); } // Spawn wood function function spawnWood() { var wood = new Wood(); wood.x = Math.random() * (2048 - 120) + 60; wood.y = Math.random() * (2732 - 120) + 60; woodItems.push(wood); game.addChild(wood); } // Spawn zombie function function spawnZombie() { var zombie = new Zombie(); // During blood moon, scatter zombies in random locations across the map if (isBloodMoon) { // Scatter zombies randomly across the entire map during blood moon zombie.x = Math.random() * (1800 - 200) + 200; // Random X position zombie.y = Math.random() * (2200 - 400) + 400; // Random Y position } else { // Default spawn behavior for non-blood moon scenarios zombie.x = Math.random() * (2048 - 120) + 60; zombie.y = Math.random() * (2732 - 120) + 60; } zombies.push(zombie); game.addChild(zombie); } // Spawn vampire function function spawnVampire() { var vampire = new Vampire(); vampire.x = Math.random() * (2048 - 120) + 60; vampire.y = Math.random() * (2732 - 120) + 60; vampires.push(vampire); game.addChild(vampire); } // Spawn vampire2 function function spawnVampire2() { var vampire2 = new Vampire2(); vampire2.x = Math.random() * (2048 - 120) + 60; vampire2.y = Math.random() * (2732 - 120) + 60; vampires2.push(vampire2); game.addChild(vampire2); } // Update campfire display function updateCampfireDisplay() { campfireTxt.setText('Campfire: ' + campfire.wood + ' wood'); } // Spawn NPC function function spawnNPC() { // Determine rarity first with updated probabilities var rarity; var selectedType; var rarityRand = Math.random(); if (rarityRand <= 0.5) { // Common NPCs: 50% chance rarity = 'common'; selectedType = commonNPCs[Math.floor(Math.random() * commonNPCs.length)]; } else if (rarityRand <= 0.85) { // Uncommon NPCs: 35% chance rarity = 'uncommon'; selectedType = uncommonNPCs[Math.floor(Math.random() * uncommonNPCs.length)]; } else { // Rare NPCs: 15% chance rarity = 'rare'; selectedType = rareNPCs[Math.floor(Math.random() * rareNPCs.length)]; } var npc = new NPC(selectedType); npc.rarity = rarity; npc.setRarityVisual(); // Apply visual indication based on rarity npc.x = Math.random() * (1800 - 200) + 200; npc.y = Math.random() * (2400 - 200) + 200; npcs.push(npc); game.addChild(npc); // If bat spawns, give extra life immediately if (selectedType === 'bat') { playerLives++; updateLivesDisplay(); LK.getSound('batTouch').play(); } // Apply multiplier immediately when NPC spawns if (npc.multiplier > 0) { scoreMultiplier = npc.multiplier; updateScoreDisplay(); } } // Transition to night function startNight() { isNight = true; nightCount++; berriesCollectedInCycle = 0; // Check for blood moon - 3rd night is guaranteed, then 25% chance (but not consecutive) var shouldBeBloodMoon = false; if (nightCount === 3) { // 3rd night is always a blood moon shouldBeBloodMoon = true; } else if (nightCount > 3 && nightCount !== lastBloodMoonNight + 1) { // After 3rd night, 25% chance for blood moon, but not if last night was blood moon shouldBeBloodMoon = Math.random() < 0.25; } // Check for peaceful night - 25% base chance, 100% chance before blood moon, forced on night 2 var peacefulChance = 0.25; if (nightCount === 2) { // Night 2 is always peaceful since night 3 is guaranteed blood moon peacefulChance = 1.0; } else if (nightCount > 3) { // For nights after 3, check if NEXT night could be blood moon var nextNightCouldBeBloodMoon = nightCount + 1 !== lastBloodMoonNight + 1 && Math.random() < 0.25; if (nextNightCouldBeBloodMoon) { // If next night could be blood moon, make this night peaceful peacefulChance = 1.0; } } isPeacefulNight = Math.random() < peacefulChance && !shouldBeBloodMoon; // Only play howl if not peaceful night if (!isPeacefulNight) { LK.getSound('howl').play(); } // Stop any currently playing music and switch to appropriate night music LK.stopMusic(); if (isPeacefulNight) { LK.playMusic('peacefulNightAmbience'); } else { LK.playMusic('nightMusic'); } if (shouldBeBloodMoon) { bloodMoonCount++; lastBloodMoonNight = nightCount; // Track when blood moon occurred // Add 5 berries to requirement for each blood moon dayLengthMultiplier = 1 + bloodMoonCount * 0.5; // Each blood moon adds 5 berries (0.5 * 10 = 5) isBloodMoon = true; bloodMoonStartTime = LK.ticks; // Start blood moon cutscene isBloodMoonCutscene = true; bloodMoonCutsceneTimer = 0; cutsceneWendigoAtCampfire = false; cutsceneZombiesWaiting = false; cutsceneZombiesWaitStartTime = 0; cutsceneNPCSpawned = false; // Reset multiplier during blood moon scoreMultiplier = 1; updateScoreDisplay(); // Stop current music and play blood moon music LK.stopMusic(); LK.playMusic('bloodMoonMusic'); // Freeze one random NPC if (npcs.length > 0) { var randomNPC = npcs[Math.floor(Math.random() * npcs.length)]; randomNPC.freeze(); } // Move other NPCs to cabin for (var i = 0; i < npcs.length; i++) { if (!npcs[i].frozen) { npcs[i].enterCabin(); } } // Always spawn wendigo during blood moon (twisted or normal) wendigo = new Wendigo(); wendigo.x = -100; wendigo.y = Math.random() * 2000 + 300; game.addChild(wendigo); // Always spawn zombies during blood moon (3 zombies) spawnZombie(); spawnZombie(); spawnZombie(); // Vampires are spawned when twisted blood moon is triggered in campfire // Spawn extra berries during blood moon (2-4 additional berries) var extraBerries = Math.floor(Math.random() * 3) + 2; for (var b = 0; b < extraBerries; b++) { spawnNormalBerry(); } // Change background to blood red game.setBackgroundColor(0x330000); tween(background, { tint: 0x660000, alpha: 0.8 }, { duration: 1500 }); dayNightTxt.setText('Blood Moon'); dayNightTxt.fill = 0xFF0000; // Hide day counter and show night counter dayCounterTxt.visible = false; nightCounterTxt.visible = true; nightCounterTxt.setText('Night ' + nightCount); } else { // Regular night if (!isPeacefulNight && nightCount !== 2) { // Spawn wolves - 3 by default, wolf count increases after each completed blood moon // Don't spawn wolves on night 2 var baseWolves = 3 + bloodMoonCount; // Add 1 wolf for each completed blood moon var fennecPresent = false; for (var f = 0; f < npcs.length; f++) { if (npcs[f].type === 'fennec') { fennecPresent = true; break; } } var wolvesToSpawn = fennecPresent ? baseWolves - 1 : baseWolves; // Additional check: explicitly prevent wolf spawning on night 2 if (nightCount === 2) { wolvesToSpawn = 0; } for (var w = 0; w < wolvesToSpawn; w++) { var wolf = new Wolf(); // Spawn wolves from both sides alternating if (w % 2 === 0) { // Even wolves spawn from left side wolf.x = -100; } else { // Odd wolves spawn from right side wolf.x = 2148; } wolf.y = Math.random() * (2200 - 400) + 400; wolves.push(wolf); game.addChild(wolf); } } // Despawn any existing wolves during peaceful nights (especially night 2) if (isPeacefulNight || nightCount === 2) { // Immediately destroy all existing wolves for (var w = wolves.length - 1; w >= 0; w--) { var wolf = wolves[w]; wolf.destroy(); wolves.splice(w, 1); } } // Move NPCs to campfire (even during peaceful nights) for (var i = 0; i < npcs.length; i++) { npcs[i].enterCabin(); } // Change background to dark blue night game.setBackgroundColor(0x001122); tween(background, { tint: 0x223355, alpha: 0.7 }, { duration: 1500 }); if (isPeacefulNight) { dayNightTxt.setText('Peaceful Night'); dayNightTxt.fill = 0x99CCFF; // Hide day counter and show night counter dayCounterTxt.visible = false; nightCounterTxt.visible = true; nightCounterTxt.setText('Night ' + nightCount); } else { dayNightTxt.setText('Night'); dayNightTxt.fill = 0x6666FF; } // Hide day counter and show night counter dayCounterTxt.visible = false; nightCounterTxt.visible = true; nightCounterTxt.setText('Night ' + nightCount); // Show wood counter during night campfireTxt.visible = true; woodMultiplierTxt.visible = true; updateCampfireDisplay(); // Spawn wood during night (3-5 pieces, but only 10% chance during full moon) if (!isTwistedBloodMoon || Math.random() < 0.1) { var woodToSpawn = Math.floor(Math.random() * 3) + 3; for (var w = 0; w < woodToSpawn; w++) { spawnWood(); } } // Spawn zombies (25% chance for 2 zombies, but not during peaceful nights) if (!isPeacefulNight && Math.random() < 0.25) { spawnZombie(); spawnZombie(); } } } // Transition to day function startDay() { isNight = false; isBloodMoon = false; isPeacefulNight = false; isChaseThemePlaying = false; isZombieChaseThemePlaying = false; isWendigoChaseThemePlaying = false; berriesCollectedInCycle = 0; // Stop any currently playing music and switch to day music LK.stopMusic(); LK.playMusic('dayMusic'); // Immediately destroy all wolves when day starts for (var i = 0; i < wolves.length; i++) { var wolf = wolves[i]; wolf.destroy(); } wolves = []; // Remove wendigo if exists if (wendigo) { wendigo.destroy(); wendigo = null; } // Hide wood counter during day campfireTxt.visible = false; woodMultiplierTxt.visible = false; // Remove remaining wood items for (var i = 0; i < woodItems.length; i++) { woodItems[i].destroy(); } woodItems = []; // Remove zombies for (var i = 0; i < zombies.length; i++) { zombies[i].destroy(); } zombies = []; // Remove vampires for (var i = 0; i < vampires.length; i++) { vampires[i].destroy(); } vampires = []; // Remove vampires2 for (var i = 0; i < vampires2.length; i++) { vampires2[i].destroy(); } vampires2 = []; // Reset full moon state isTwistedBloodMoon = false; twistedBloodMoonWoodCollected = 0; // Reset wood counter // Bring NPCs out of cabin for (var i = 0; i < npcs.length; i++) { if (!npcs[i].killed) { npcs[i].exitCabin(); } } // Spawn new NPC with 25% chance, but force spawn after night 1 or after blood moon var shouldSpawnNPC = Math.random() < 0.25; if (nightCount === 1 || shouldSpawnNPC) { spawnNPC(); } // Always spawn NPC after blood moon (100% chance) if (lastBloodMoonNight === nightCount) { spawnNPC(); } // Change background back to day game.setBackgroundColor(0x228b22); tween(background, { tint: 0xFFFFFF, alpha: 1 }, { duration: 1500 }); dayNightTxt.setText('Day'); dayNightTxt.fill = 0xFFFF00; // Update day counter dayCount++; dayCounterTxt.setText('Day ' + dayCount); // Show day counter and hide night counter dayCounterTxt.visible = true; nightCounterTxt.visible = false; } // Update score display with multiplier function updateScoreDisplay() { scoreTxt.setText('Score: ' + LK.getScore() + ' (x' + scoreMultiplier + ')'); } // Update lives display function updateLivesDisplay() { livesTxt.setText('Lives: ' + playerLives); } // Keyboard state tracking var keys = { left: false, right: false, up: false, down: false }; // Handle mouse/touch move function handleMove(x, y, obj) { // Disable input during intro cutscene if (isIntroCutscene) return; if (dragNode) { dragNode.x = x; dragNode.y = y; // Keep fox within bounds if (dragNode.x < 60) dragNode.x = 60; if (dragNode.x > 1988) dragNode.x = 1988; if (dragNode.y < 60) dragNode.y = 60; if (dragNode.y > 2672) dragNode.y = 2672; } } // Mouse/touch events game.move = handleMove; game.down = function (x, y, obj) { // Disable input during intro cutscene if (isIntroCutscene) return; dragNode = fox; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; // Start with day music LK.playMusic('dayMusic'); // Initial berry spawning spawnNormalBerry(); spawnNormalBerry(); spawnNormalBerry(); // Force spawn a bat immediately for testing var forcedBat = new NPC('bat'); forcedBat.rarity = 'rare'; forcedBat.setRarityVisual(); forcedBat.x = Math.random() * (1800 - 200) + 200; forcedBat.y = Math.random() * (2400 - 200) + 200; npcs.push(forcedBat); game.addChild(forcedBat); // Give extra life since bat spawned playerLives++; updateLivesDisplay(); LK.getSound('batTouch').play(); // Create light effect for twisted blood moon var foxLight = null; // Main game update loop game.update = function () { // Handle fox light during twisted blood moon if (isTwistedBloodMoon && !foxLight) { // Create a glowing light around fox foxLight = LK.getAsset('campfire', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, alpha: 0.7 }); game.addChild(foxLight); // Animate light with gentle glow tween(foxLight, { alpha: 0.4 }, { duration: 1000, onFinish: function onFinish() { tween(foxLight, { alpha: 0.7 }, { duration: 1000 }); } }); } else if (!isTwistedBloodMoon && foxLight) { // Remove light when not in twisted blood moon foxLight.destroy(); foxLight = null; } // Update light position to follow fox if (foxLight) { foxLight.x = fox.x; foxLight.y = fox.y; } // Handle intro cutscene if (isIntroCutscene) { introCutsceneTimer++; // Wait for delay before starting cutscene if (introCutsceneTimer <= introCutsceneDelay) { return; // Wait before starting cutscene } if (introCutsceneTimer === introCutsceneDelay + 1 && !foxAppearedSoundPlayed) { // Play fox appear sound when fox appears LK.getSound('foxAppear').play(); foxAppearedSoundPlayed = true; } if (introCutsceneTimer === introCutsceneDelay + 30 && !doorSoundPlayed) { // Play door sound 0.5 seconds after fox appears LK.getSound('doorOpen').play(); doorSoundPlayed = true; } // Find nearest berry for target var targetBerry = null; var closestDistance = Infinity; for (var b = 0; b < normalBerries.length; b++) { var dx = normalBerries[b].x - cabin.x; var dy = normalBerries[b].y - cabin.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; targetBerry = normalBerries[b]; } } // Move fox from cabin to nearest berry over 3 seconds after delay if (introCutsceneTimer <= introCutsceneDelay + 180 && targetBerry) { // 3 seconds at 60fps after delay var progress = (introCutsceneTimer - introCutsceneDelay) / 180; var startX = cabin.x; var startY = cabin.y; var endX = targetBerry.x; var endY = targetBerry.y; fox.x = tween.easeInOut(progress) * (endX - startX) + startX; fox.y = tween.easeInOut(progress) * (endY - startY) + startY; } else { // Cutscene finished isIntroCutscene = false; if (targetBerry) { fox.x = targetBerry.x; fox.y = targetBerry.y; } else { fox.x = 1024; fox.y = 1366; } } return; // Don't process other game logic during intro } // Handle blood moon cutscene if (isBloodMoonCutscene) { bloodMoonCutsceneTimer++; // Check if NPC was killed and 5 seconds have passed if (cutsceneNPCKilled && LK.ticks - cutsceneNPCKillTime >= 300) { // 5 seconds at 60fps // Remove the killed NPC body tween(cutsceneNPCKilled, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { if (cutsceneNPCKilled) { cutsceneNPCKilled.destroy(); npcs.splice(npcs.indexOf(cutsceneNPCKilled), 1); cutsceneNPCKilled = null; } } }); // End cutscene isBloodMoonCutscene = false; bloodMoonCutsceneTimer = 0; // Reset cutscene variables to allow fox movement again cutsceneNPCKilled = null; cutsceneNPCKillTime = 0; } } // Spawn berries over time with random intervals - 1.5-3 seconds berrySpawnTimer++; var baseSpawnTime = 90; // 1.5 seconds at 60fps var maxSpawnTime = 180; // 3 seconds at 60fps var randomSpawnTime = Math.random() * (maxSpawnTime - baseSpawnTime) + baseSpawnTime; if (berrySpawnTimer >= randomSpawnTime) { spawnNormalBerry(); berrySpawnTimer = 0; } // Wood spawning during specific phases woodSpawnTimer++; if (woodSpawnTimer >= 180) { // Every 3 seconds (180 frames at 60fps) woodSpawnTimer = 0; if (isTwistedBloodMoon) { // 25% chance during full moon if (Math.random() < 0.25) { spawnWood(); } } else if (isPeacefulNight || isNight) { // 20% chance during peaceful night and regular night (not blood moon) if (!isBloodMoon && Math.random() < 0.2) { spawnWood(); } } } // Check collisions with normal berries for (var i = normalBerries.length - 1; i >= 0; i--) { var berry = normalBerries[i]; if (fox.intersects(berry)) { // Collect berry and increase score with multiplier var scoreGain = 1 * scoreMultiplier; LK.setScore(LK.getScore() + scoreGain); berriesCollectedInCycle++; updateScoreDisplay(); LK.getSound('berryCollect').play(); // Remove berry from array first, then destroy normalBerries.splice(i, 1); berry.destroy(); // Check for day/night transitions (but not during twisted blood moon) if (!isTwistedBloodMoon) { var dayRequirement = 10 * dayLengthMultiplier; var nightRequirement = isBloodMoon ? dayRequirement : 5; if (!isNight && berriesCollectedInCycle >= dayRequirement) { startNight(); } else if (isNight && berriesCollectedInCycle >= nightRequirement) { startDay(); } } } } // Check collisions with wood items for (var i = woodItems.length - 1; i >= 0; i--) { var wood = woodItems[i]; if (fox.intersects(wood)) { // Collect wood and add to campfire campfire.addWood(1); // Play different sound based on game state if (isTwistedBloodMoon) { LK.getSound('foxTwistedWoodCollect').play(); } else { LK.getSound('woodCollect').play(); } // Track wood collected during full moon if (isTwistedBloodMoon) { twistedBloodMoonWoodCollected++; // End full moon when 5 wood collected if (twistedBloodMoonWoodCollected >= 5) { isTwistedBloodMoon = false; twistedBloodMoonWoodCollected = 0; // Reset counter startDay(); // End full moon and start day } } // Remove wood from array first, then destroy woodItems.splice(i, 1); wood.destroy(); } } // Check collisions with NPCs for sound and visual feedback only for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; if (!npc.inCabin && !npc.frozen && fox.intersects(npc)) { // Play touch sound for the specific animal type LK.getSound(npc.type + 'Touch').play(); // Give visual feedback for all animals tween(npc, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, onFinish: function onFinish() { tween(npc, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); } } // Wolf collision is now handled in Wolf class update method };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bunny = Container.expand(function () {
var self = Container.call(this);
var bunnyGraphics = self.attachAsset('bunny', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.0
});
self.berriesGiven = 0;
self.update = function () {
// Gentle bouncing animation
self.y += Math.sin(LK.ticks * 0.15) * 0.3;
// Apply movement speed based on targeting
if (self.isTargeted) {
// Bunny can move when targeted but is drastically slowed
var moveSpeed = 0.3;
// Move very slowly towards a random direction when targeted
if (LK.ticks % 300 === 0) {
// Every 5 seconds instead of 2
var newX = Math.random() * (1800 - 200) + 200;
var newY = Math.random() * (2400 - 200) + 200;
tween(self, {
x: newX,
y: newY
}, {
duration: 6000,
// Much slower movement - 6 seconds instead of 2
easing: tween.easeInOut
});
}
} else {
// Normal movement when not targeted
if (LK.ticks % 120 === 0) {
// Every 2 seconds, move to a new position
var newX = Math.random() * (1800 - 200) + 200;
var newY = Math.random() * (2400 - 200) + 200;
tween(self, {
x: newX,
y: newY
}, {
duration: 2000,
easing: tween.easeInOut
});
}
}
};
return self;
});
var Campfire = Container.expand(function () {
var self = Container.call(this);
var campfireGraphics = self.attachAsset('campfire', {
anchorX: 0.5,
anchorY: 0.5
});
self.wood = 5; // Start with 5 wood
self.burnRate = 1; // Wood burns every 60 frames (1 second)
self.lastBurnTime = 0;
self.nextBurnIn = Math.floor(Math.random() * 120) + 120; // Random 2-4 seconds (120-240 frames)
self.addWood = function (amount) {
self.wood += amount;
updateCampfireDisplay();
};
self.update = function () {
if (isNight || isPeacefulNight) {
// Burn wood during night at random intervals
if (LK.ticks - self.lastBurnTime >= self.nextBurnIn) {
if (self.wood > 0) {
self.wood--;
self.lastBurnTime = LK.ticks;
self.nextBurnIn = Math.floor(Math.random() * 120) + 120; // Random 2-4 seconds
updateCampfireDisplay();
} else {
// No wood left, start twisted blood moon only if not already in blood moon AND no wood available
if (!isBloodMoon && campfire.wood === 0) {
isTwistedBloodMoon = true;
isNight = false;
isBloodMoon = true;
// Set blood moon as true for proper night detection
LK.stopMusic();
LK.playMusic('twistedBloodMoonTheme');
dayNightTxt.setText('Full Moon');
dayNightTxt.fill = 0xCCCCCC;
game.setBackgroundColor(0x111111);
tween(background, {
tint: 0x444444,
alpha: 0.9
}, {
duration: 1000
});
// Move all NPCs to cabin during twisted blood moon
for (var i = 0; i < npcs.length; i++) {
npcs[i].enterCabin();
}
// Spawn vampires from bottom during twisted blood moon with cutscene
var vampire1 = new Vampire();
vampire1.x = Math.random() * (1800 - 200) + 200;
vampire1.y = 2800; // Start from bottom
vampires.push(vampire1);
game.addChild(vampire1);
var vampire2 = new Vampire();
vampire2.x = Math.random() * (1800 - 200) + 200;
vampire2.y = 2800; // Start from bottom
vampires.push(vampire2);
game.addChild(vampire2);
var vampire2_1 = new Vampire2();
vampire2_1.x = Math.random() * (1800 - 200) + 200;
vampire2_1.y = 2800; // Start from bottom
vampires2.push(vampire2_1);
game.addChild(vampire2_1);
var vampire2_2 = new Vampire2();
vampire2_2.x = Math.random() * (1800 - 200) + 200;
vampire2_2.y = 2800; // Start from bottom
vampires2.push(vampire2_2);
game.addChild(vampire2_2);
self.wood = 0; // Set wood to 0 when full moon starts
// Spawn 4 wood when full moon starts
for (var fw = 0; fw < 4; fw++) {
spawnWood();
}
updateCampfireDisplay();
}
}
}
// Fire animation effect
campfireGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
campfireGraphics.scaleY = 1 + Math.cos(LK.ticks * 0.25) * 0.15;
}
};
return self;
});
var Fox = Container.expand(function () {
var self = Container.call(this);
var foxGraphics = self.attachAsset('fox', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var JumpscareDisplay = Container.expand(function (assetName) {
var self = Container.call(this);
var jumpscareGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
self.showJumpscare = function () {
// Position at center of screen
self.x = 1024;
self.y = 1366;
// Rapid scale up with flash effect
tween(jumpscareGraphics, {
scaleX: 3,
scaleY: 3,
alpha: 1
}, {
duration: 100,
onFinish: function onFinish() {
// Hold for a moment
LK.setTimeout(function () {
// Quick fade out
tween(jumpscareGraphics, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
}, 300);
}
});
};
return self;
});
var NPC = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
// Set multiplier based on type: mouse gives 4x, cat/bunny/squirrel give 2x, bat gives 4x, others give no multiplier
if (type === 'mouse' || type === 'bat') {
self.multiplier = 4;
} else if (type === 'cat' || type === 'bunny' || type === 'squirrel') {
self.multiplier = 2;
} else {
self.multiplier = 0; // deer, fennec, dog don't give multipliers
}
self.inCabin = false;
self.frozen = false;
// Set hit points: deer has 2 lives, dog has 3 lives, others have 1
if (type === 'deer') {
self.hitPoints = 2;
} else if (type === 'dog') {
self.hitPoints = 3;
} else {
self.hitPoints = 1;
}
self.isTargeted = false;
self.rarity = 'common'; // Default rarity, will be set when spawned
// Deer-specific properties for berry detection
if (type === 'deer') {
self.berryTarget = null;
self.hasSpeedBoost = false;
self.baseSpeed = 2;
self.boostedSpeed = 8; // 4x speed boost during day
self.nightBoostedSpeed = 4; // 2x speed boost during night/full moon/peaceful night
}
// Cat-specific properties for wood detection
if (type === 'cat') {
self.woodTarget = null;
self.hasSpeedBoost = false;
self.baseSpeed = 2;
self.boostedSpeed = 8; // 4x speed boost during day
self.nightBoostedSpeed = 4; // 2x speed boost during night/full moon/peaceful night
}
var npcGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: type === 'mouse' ? 2.0 : 1.2,
scaleY: type === 'mouse' ? 1.8 : 1.0
});
// Add rarity visual indicator after graphics are created
self.setRarityVisual = function () {
// All animals keep their natural colors - no tinting applied
// Rarity can be indicated through other means like size or behavior
};
self.update = function () {
if (self.frozen || self.inCabin) return;
// Apply movement speed based on targeting and type
if (self.isTargeted) {
if (self.type === 'bunny') {
// Bunny can move when targeted but is drastically slowed
self.targetedSpeed = 0.3;
} else if (self.type === 'cat' || self.type === 'squirrel') {
// Cat and squirrel are more drastically slowed than deer
self.targetedSpeed = 0.1;
} else if (self.type === 'deer') {
// Deer is slowed but not as much as commons
self.targetedSpeed = 0.5;
} else if (self.type === 'mouse') {
// Mouse gets speed boost when targeted
self.targetedSpeed = 4;
} else if (self.type === 'fennec') {
// Fennec can't enter cabin when targeted
self.canEnterCabin = false;
self.targetedSpeed = 1;
} else if (self.type === 'dog') {
// Dog is slowed drastically when targeted
self.targetedSpeed = 0.2;
}
}
// Check for nearby enemies that would cause NPCs to run to cabin
var enemyNear = false;
var shouldRunToCabin = false;
// Check for wolves near this NPC
for (var wf = 0; wf < wolves.length; wf++) {
var wolfDist = Math.sqrt(Math.pow(wolves[wf].x - self.x, 2) + Math.pow(wolves[wf].y - self.y, 2));
if (wolfDist < 400) {
enemyNear = true;
break;
}
}
// Check for wendigo near this NPC
if (wendigo && !enemyNear) {
var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2));
if (wendigoDist < 400) {
enemyNear = true;
}
}
// Check for vampires near this NPC
if (!enemyNear) {
for (var v = 0; v < vampires.length; v++) {
var vampireDist = Math.sqrt(Math.pow(vampires[v].x - self.x, 2) + Math.pow(vampires[v].y - self.y, 2));
if (vampireDist < 400) {
enemyNear = true;
break;
}
}
}
// Check for vampires2 near this NPC
if (!enemyNear) {
for (var v2 = 0; v2 < vampires2.length; v2++) {
var vampire2Dist = Math.sqrt(Math.pow(vampires2[v2].x - self.x, 2) + Math.pow(vampires2[v2].y - self.y, 2));
if (vampire2Dist < 400) {
enemyNear = true;
break;
}
}
}
// Check for zombies near this NPC
if (!enemyNear) {
for (var z = 0; z < zombies.length; z++) {
var zombieDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2));
if (zombieDist < 400) {
enemyNear = true;
break;
}
}
}
// Determine if NPC should run to cabin
shouldRunToCabin = isTwistedBloodMoon || isBloodMoon || enemyNear;
// Special behavior during day - NPCs go to campfire and move around freely
if (!isNight && !isPeacefulNight && !isBloodMoon && !isTwistedBloodMoon && !enemyNear) {
// Move to campfire area during day
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 200) {
// Move towards campfire if too far
var moveSpeed = self.isTargeted ? self.targetedSpeed || 1 : 1;
self.x += campfireDx / campfireDistance * moveSpeed;
self.y += campfireDy / campfireDistance * moveSpeed;
} else {
// Already near campfire, do gentle movement around it
self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.3;
if (LK.ticks % 200 === 0) {
var newX = campfire.x + (Math.random() - 0.5) * 300;
var newY = campfire.y + (Math.random() - 0.5) * 300;
// Keep within reasonable bounds
newX = Math.max(200, Math.min(1800, newX));
newY = Math.max(200, Math.min(2400, newY));
tween(self, {
x: newX,
y: newY
}, {
duration: 3000,
easing: tween.easeInOut
});
}
}
return;
}
// If enemies are near or it's bloodmoon, run to cabin
if (shouldRunToCabin) {
self.enterCabin();
return;
}
// Cat behavior - collect wood with detection range and speed boost during night
if (self.type === 'cat' && !self.inCabin) {
// Wood detection with dynamic range and speed boost
var detectionRange;
var speedBoost;
// Determine detection range and speed boost based on time of day
if (isNight || isTwistedBloodMoon || isPeacefulNight || isBloodMoon) {
// Night/full moon/peaceful night: shorter range, 2x speed boost
detectionRange = 300;
speedBoost = self.nightBoostedSpeed;
} else {
// Day: longer range, 4x speed boost
detectionRange = 600;
speedBoost = self.boostedSpeed;
}
// Find wood within detection range
var woodInRange = [];
for (var w = 0; w < woodItems.length; w++) {
var wood = woodItems[w];
var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2));
if (woodDist <= detectionRange) {
woodInRange.push({
wood: wood,
distance: woodDist
});
}
}
// If wood is in range, activate speed boost and target nearest one
if (woodInRange.length > 0) {
self.hasSpeedBoost = true;
// Find nearest wood in range
woodInRange.sort(function (a, b) {
return a.distance - b.distance;
});
var targetWood = woodInRange[0].wood;
var targetDistance = woodInRange[0].distance;
// Move towards target wood with speed boost
var dx = targetWood.x - self.x;
var dy = targetWood.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || speedBoost : speedBoost;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Collect wood if close enough
if (targetDistance < 50) {
for (var w = woodItems.length - 1; w >= 0; w--) {
if (woodItems[w] === targetWood) {
campfire.addWood(1 * self.multiplier); // Cat uses 2x multiplier
LK.getSound('catWoodCollect').play();
woodItems.splice(w, 1);
targetWood.destroy();
break;
}
}
}
return;
} else {
// No wood in range, remove speed boost and move towards nearest wood
self.hasSpeedBoost = false;
// Find nearest wood anywhere on the map
var nearestWood = null;
var nearestDistance = Infinity;
for (var w = 0; w < woodItems.length; w++) {
var wood = woodItems[w];
var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2));
if (woodDist < nearestDistance) {
nearestDistance = woodDist;
nearestWood = wood;
}
}
if (nearestWood) {
var dx = nearestWood.x - self.x;
var dy = nearestWood.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || self.baseSpeed : self.baseSpeed;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
return;
}
}
}
// Bunny behavior - collect wood with 2x multiplier
if (self.type === 'bunny' && !self.inCabin) {
// Check for nearby wood to collect
for (var w = woodItems.length - 1; w >= 0; w--) {
var wood = woodItems[w];
var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2));
if (woodDist <= 150) {
// Move towards wood
var dx = wood.x - self.x;
var dy = wood.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || 2 : 2;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Collect wood if close enough (2x collection)
if (woodDist < 50) {
woodItems.splice(w, 1);
wood.destroy();
campfire.addWood(1 * self.multiplier); // Bunny uses 2x multiplier
LK.getSound('woodCollect').play();
break;
}
return;
}
}
}
// Fennec-specific properties for stunning and cabin access
if (self.type === 'fennec') {
self.canEnterCabin = false; // Fennec can never enter cabin
self.stunCooldown = 0; // Cooldown timer for stunning ability
self.isStunned = false; // Whether fennec is currently stunned
self.stunnedUntil = 0; // When stun effect ends
}
// Fennec fox behavior - collect berries and kill wolves/zombies
if (self.type === 'fennec') {
// Apply speed modifications based on moon phase
var fennecSpeed = 2; // Base speed
if (isBloodMoon || isTwistedBloodMoon) {
fennecSpeed = 3; // Speed boost during blood moon/full moon
} else if (isPeacefulNight || isNight) {
fennecSpeed = 1; // Slowness during other nights
}
// Handle stunning effects
if (self.isStunned && LK.ticks < self.stunnedUntil) {
return; // Can't move when stunned
} else if (self.isStunned) {
self.isStunned = false; // Remove stun effect
}
// Reduce cooldown
if (self.stunCooldown > 0) {
self.stunCooldown--;
}
// Check for wolves and zombies to kill (150 pixel range)
var targetEnemy = null;
var closestEnemyDistance = Infinity;
// Check for wolves within 150 pixels
for (var w = 0; w < wolves.length; w++) {
var wolfDist = Math.sqrt(Math.pow(wolves[w].x - self.x, 2) + Math.pow(wolves[w].y - self.y, 2));
if (wolfDist <= 150 && wolfDist < closestEnemyDistance) {
targetEnemy = wolves[w];
closestEnemyDistance = wolfDist;
}
}
// Check for zombies within 150 pixels
for (var z = 0; z < zombies.length; z++) {
var zombieDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2));
if (zombieDist <= 150 && zombieDist < closestEnemyDistance) {
targetEnemy = zombies[z];
closestEnemyDistance = zombieDist;
}
}
// Kill enemy if close enough and cooldown is ready
if (targetEnemy && closestEnemyDistance <= 150 && self.stunCooldown === 0) {
// Remove enemy from appropriate array
if (targetEnemy.constructor.name === 'Wolf') {
targetEnemy.destroy();
wolves.splice(wolves.indexOf(targetEnemy), 1);
} else if (targetEnemy.constructor.name === 'Zombie') {
targetEnemy.destroy();
zombies.splice(zombies.indexOf(targetEnemy), 1);
}
// Stun fennec for 3 seconds (180 frames at 60fps)
self.isStunned = true;
self.stunnedUntil = LK.ticks + 180;
// Set cooldown for 10 seconds (600 frames at 60fps)
self.stunCooldown = 600;
return; // Skip other behavior while stunned
}
// Check for nearby berries to collect (150 pixel range)
for (var b = normalBerries.length - 1; b >= 0; b--) {
var berry = normalBerries[b];
var berryDist = Math.sqrt(Math.pow(berry.x - self.x, 2) + Math.pow(berry.y - self.y, 2));
if (berryDist <= 150) {
// Move towards berry
var dx = berry.x - self.x;
var dy = berry.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || fennecSpeed : fennecSpeed;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Collect berry if close enough
if (berryDist < 50) {
normalBerries.splice(b, 1);
berry.destroy();
berriesCollectedInCycle += 2; // Fennec collects 2 berries instead of 1
break;
}
return;
}
}
}
// Deer behavior - collect berries with detection range and speed boost
if (self.type === 'deer') {
// First priority: if targeted and at 1 life during blood moon, run to cabin
if (isBloodMoon && self.isTargeted && self.hitPoints === 1) {
self.enterCabin();
return;
}
// Second priority: run from wendigo if present
if (isBloodMoon && wendigo) {
var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2));
if (wendigoDist < 400) {
// Run away from wendigo - use targetedSpeed if targeted, otherwise normal speed
var speed = self.isTargeted ? self.targetedSpeed : 3;
var dx = self.x - wendigo.x;
var dy = self.y - wendigo.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * speed;
self.y += dy / distance * speed;
}
return;
}
}
// Berry detection with dynamic range and speed boost
var detectionRange;
var speedBoost;
// Determine detection range and speed boost based on time of day
if (isNight || isTwistedBloodMoon || isPeacefulNight || isBloodMoon) {
// Night/full moon/peaceful night: shorter range, 2x speed boost
detectionRange = 200;
speedBoost = self.nightBoostedSpeed;
} else {
// Day: longer range, 4x speed boost
detectionRange = 400;
speedBoost = self.boostedSpeed;
}
// Find berries within detection range
var berriesInRange = [];
for (var b = 0; b < normalBerries.length; b++) {
var berry = normalBerries[b];
var berryDist = Math.sqrt(Math.pow(berry.x - self.x, 2) + Math.pow(berry.y - self.y, 2));
if (berryDist <= detectionRange) {
berriesInRange.push({
berry: berry,
distance: berryDist
});
}
}
// If berries are in range, activate speed boost and target nearest one
if (berriesInRange.length > 0) {
self.hasSpeedBoost = true;
// Find nearest berry in range
berriesInRange.sort(function (a, b) {
return a.distance - b.distance;
});
var targetBerry = berriesInRange[0].berry;
var targetDistance = berriesInRange[0].distance;
// Move towards target berry with speed boost
var dx = targetBerry.x - self.x;
var dy = targetBerry.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || speedBoost : speedBoost;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Collect berry if close enough
if (targetDistance < 50) {
for (var b = normalBerries.length - 1; b >= 0; b--) {
if (normalBerries[b] === targetBerry) {
normalBerries.splice(b, 1);
targetBerry.destroy();
berriesCollectedInCycle++;
// Give score when deer collects berry
var scoreGain = 1 * scoreMultiplier;
LK.setScore(LK.getScore() + scoreGain);
updateScoreDisplay();
LK.getSound('berryCollect').play();
break;
}
}
}
return;
} else {
// No berries in range, remove speed boost and move towards nearest berry
self.hasSpeedBoost = false;
// Find nearest berry anywhere on the map
var nearestBerry = null;
var nearestDistance = Infinity;
for (var b = 0; b < normalBerries.length; b++) {
var berry = normalBerries[b];
var berryDist = Math.sqrt(Math.pow(berry.x - self.x, 2) + Math.pow(berry.y - self.y, 2));
if (berryDist < nearestDistance) {
nearestDistance = berryDist;
nearestBerry = berry;
}
}
if (nearestBerry) {
var dx = nearestBerry.x - self.x;
var dy = nearestBerry.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || self.baseSpeed : self.baseSpeed;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
return;
}
}
}
// Mouse behavior - goes to campfire unless night/bloodmoon when it goes to cabin
if (self.type === 'mouse') {
if ((isNight || isBloodMoon || isPeacefulNight) && !self.inCabin) {
// Mouse goes to cabin during night/bloodmoon
var targetSpeed = isNight || isBloodMoon ? 5 : 3;
var dx = cabin.x - self.x;
var dy = cabin.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 50) {
self.x += dx / distance * targetSpeed;
self.y += dy / distance * targetSpeed;
} else {
self.enterCabin();
}
return;
}
}
// Bat behavior - flying animation, vampire transformation during blood moon, wood collection
if (self.type === 'bat') {
// Transform into vampire during blood moon or twisted blood moon
if ((isBloodMoon || isTwistedBloodMoon) && !self.isVampire) {
self.isVampire = true;
// Remove bat graphics and add vampire graphics
if (npcGraphics && npcGraphics.parent) {
npcGraphics.parent.removeChild(npcGraphics);
}
npcGraphics = self.attachAsset('vampire', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.0
});
} else if (!isBloodMoon && !isTwistedBloodMoon && self.isVampire) {
// Transform back to bat during day
self.isVampire = false;
// Remove vampire graphics and add bat graphics
if (npcGraphics && npcGraphics.parent) {
npcGraphics.parent.removeChild(npcGraphics);
}
npcGraphics = self.attachAsset('bat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.0
});
}
// Wood collection behavior with 4x multiplier
if (!self.inCabin) {
// Check for nearby wood to collect
for (var w = woodItems.length - 1; w >= 0; w--) {
var wood = woodItems[w];
var woodDist = Math.sqrt(Math.pow(wood.x - self.x, 2) + Math.pow(wood.y - self.y, 2));
if (woodDist <= 150) {
// Move towards wood
var dx = wood.x - self.x;
var dy = wood.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveSpeed = self.isTargeted ? self.targetedSpeed || 2 : 2;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Collect wood if close enough (4x collection)
if (woodDist < 50) {
woodItems.splice(w, 1);
wood.destroy();
campfire.addWood(1 * self.multiplier); // Bat uses 4x multiplier
LK.getSound('woodCollect').play();
break;
}
return;
}
}
}
// Flying animation - gentle up and down movement
self.y += Math.sin(LK.ticks * 0.2 + self.x * 0.02) * 1.5;
// Go to campfire area when not targeted
if (!self.isTargeted) {
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 300) {
tween(self, {
x: campfire.x + (Math.random() - 0.5) * 400,
y: campfire.y + (Math.random() - 0.5) * 400
}, {
duration: 2500,
easing: tween.easeInOut
});
}
}
}
// Dog behavior during night/blood moon
if (self.type === 'dog' && (isNight || isBloodMoon) && !self.isTargeted) {
// Check for nearby wolves, wendigo, and vampires
var nearestThreat = null;
var minDistance = Infinity;
// Check wolves
for (var w = 0; w < wolves.length; w++) {
if (!wolves[w].stunned) {
var dist = Math.sqrt(Math.pow(wolves[w].x - self.x, 2) + Math.pow(wolves[w].y - self.y, 2));
if (dist < minDistance) {
minDistance = dist;
nearestThreat = wolves[w];
}
}
}
// Check wendigo
if (wendigo && !wendigo.stunned) {
var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2));
if (wendigoDist < minDistance) {
minDistance = wendigoDist;
nearestThreat = wendigo;
}
}
// Check vampires
for (var v = 0; v < vampires.length; v++) {
if (!vampires[v].stunned) {
var vDist = Math.sqrt(Math.pow(vampires[v].x - self.x, 2) + Math.pow(vampires[v].y - self.y, 2));
if (vDist < minDistance) {
minDistance = vDist;
nearestThreat = vampires[v];
}
}
}
// Check vampires2
for (var v2 = 0; v2 < vampires2.length; v2++) {
if (!vampires2[v2].stunned) {
var v2Dist = Math.sqrt(Math.pow(vampires2[v2].x - self.x, 2) + Math.pow(vampires2[v2].y - self.y, 2));
if (v2Dist < minDistance) {
minDistance = v2Dist;
nearestThreat = vampires2[v2];
}
}
}
// Check zombies
for (var z = 0; z < zombies.length; z++) {
if (!zombies[z].stunned) {
var zDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2));
if (zDist < minDistance) {
minDistance = zDist;
nearestThreat = zombies[z];
}
}
}
// If threat is close enough (within 300 pixels), stun it with ranged ability
if (nearestThreat && minDistance < 300) {
self.isTargeted = true;
// Stun the threat immediately without needing to move to it
self.stunThreat(nearestThreat);
return;
}
}
// Default behavior: move to campfire if not already there
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 200) {
// Move towards campfire if too far
var moveSpeed = self.isTargeted ? self.targetedSpeed || 1 : 1;
self.x += campfireDx / campfireDistance * moveSpeed;
self.y += campfireDy / campfireDistance * moveSpeed;
} else if (!self.isTargeted) {
// Already near campfire, do gentle movement around it
self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.3;
if (LK.ticks % 200 === 0) {
var newX = campfire.x + (Math.random() - 0.5) * 300;
var newY = campfire.y + (Math.random() - 0.5) * 300;
// Keep within reasonable bounds
newX = Math.max(200, Math.min(1800, newX));
newY = Math.max(200, Math.min(2400, newY));
tween(self, {
x: newX,
y: newY
}, {
duration: 3000,
easing: tween.easeInOut
});
}
}
};
self.enterCabin = function () {
var targetX, targetY;
// Check for any enemies nearby (within 400 pixels)
var enemyNear = false;
// Check for wolves
for (var w = 0; w < wolves.length; w++) {
var wolfDist = Math.sqrt(Math.pow(wolves[w].x - self.x, 2) + Math.pow(wolves[w].y - self.y, 2));
if (wolfDist < 400) {
enemyNear = true;
break;
}
}
// Check for wendigo
if (!enemyNear && wendigo) {
var wendigoDist = Math.sqrt(Math.pow(wendigo.x - self.x, 2) + Math.pow(wendigo.y - self.y, 2));
if (wendigoDist < 400) {
enemyNear = true;
}
}
// Check for vampires
if (!enemyNear) {
for (var v = 0; v < vampires.length; v++) {
var vampireDist = Math.sqrt(Math.pow(vampires[v].x - self.x, 2) + Math.pow(vampires[v].y - self.y, 2));
if (vampireDist < 400) {
enemyNear = true;
break;
}
}
}
// Check for vampires2
if (!enemyNear) {
for (var v2 = 0; v2 < vampires2.length; v2++) {
var vampire2Dist = Math.sqrt(Math.pow(vampires2[v2].x - self.x, 2) + Math.pow(vampires2[v2].y - self.y, 2));
if (vampire2Dist < 400) {
enemyNear = true;
break;
}
}
}
// Check for zombies
if (!enemyNear) {
for (var z = 0; z < zombies.length; z++) {
var zombieDist = Math.sqrt(Math.pow(zombies[z].x - self.x, 2) + Math.pow(zombies[z].y - self.y, 2));
if (zombieDist < 400) {
enemyNear = true;
break;
}
}
}
// Fennec can never enter cabin
if (self.type === 'fennec') {
self.inCabin = false;
targetX = campfire.x + (Math.random() - 0.5) * 200;
targetY = campfire.y + (Math.random() - 0.5) * 200;
} else {
// Determine where to go based on conditions for other NPCs
var shouldGoCabin = isTwistedBloodMoon || isBloodMoon || enemyNear;
if (shouldGoCabin) {
// Go to cabin when there's danger
self.inCabin = true;
targetX = cabin.x;
targetY = cabin.y;
} else {
// Go to campfire during normal times
self.inCabin = false;
targetX = campfire.x + (Math.random() - 0.5) * 200;
targetY = campfire.y + (Math.random() - 0.5) * 200;
}
}
tween(self, {
x: targetX,
y: targetY,
alpha: self.inCabin ? 0.3 : 1
}, {
duration: 1000
});
};
self.exitCabin = function () {
self.inCabin = false;
var newX = Math.random() * (1800 - 200) + 200;
var newY = Math.random() * (2400 - 200) + 200;
tween(self, {
x: newX,
y: newY,
alpha: 1
}, {
duration: 1000
});
};
self.freeze = function () {
self.frozen = true;
npcGraphics.tint = 0x4444ff;
};
self.takeDamage = function (attacker) {
// Special behavior for deer: unfreeze if targeted and hit by wendigo
if (self.type === 'deer' && self.isTargeted && self.frozen && attacker === wendigo) {
self.frozen = false;
self.isTargeted = false;
npcGraphics.tint = 0xFFFFFF; // Remove freeze tint
// Don't take damage, just unfreeze
return false;
}
// Special behavior for deer: remove targeting when hit by any enemy
if (self.type === 'deer' && self.isTargeted) {
self.isTargeted = false;
}
// Special behavior for deer: teleport to cabin when hit (unless already in cabin)
if (self.type === 'deer' && !self.inCabin) {
self.inCabin = true;
tween(self, {
x: cabin.x,
y: cabin.y,
alpha: 0.3
}, {
duration: 500
});
}
self.hitPoints--;
// Always play kill sound when NPC gets hit
LK.getSound(self.type + 'Kill').play();
// Stun the attacker for 3 seconds if provided
if (attacker) {
// Special case for wendigo: stun for 7 seconds instead of the normal 3
var stunDuration = attacker === wendigo ? 420 : 180; // 7 seconds for wendigo, 3 for others
attacker.stunned = true;
attacker.stunnedUntil = LK.ticks + stunDuration;
// Find the graphics object to tint
var graphicsToTint = null;
if (attacker.children && attacker.children[0]) {
graphicsToTint = attacker.children[0];
} else if (attacker.wendigoGraphics) {
graphicsToTint = attacker.wendigoGraphics;
}
if (graphicsToTint) {
tween(graphicsToTint, {
tint: 0x4444FF
}, {
duration: 500
});
}
}
if (self.hitPoints <= 0) {
// If it's a deer being killed, spawn 20 berries (can be multiplied)
if (self.type === 'deer') {
for (var i = 0; i < 20; i++) {
LK.setTimeout(function () {
spawnNormalBerry();
}, i * 100); // Stagger berry spawning
}
}
return true; // NPC is killed
} else {
// Flash red to show damage
tween(npcGraphics, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(npcGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return false;
}
};
self.stunThreat = function (threat) {
// Determine stun duration based on threat type
var stunDuration = 300; // Default 5 seconds for most enemies
if (threat === wendigo) {
stunDuration = 180; // 3 seconds for wendigo
} else if (threat.children && threat.children[0] && threat.children[0].texture.baseTexture.imageUrl.indexOf('vampire') !== -1) {
stunDuration = 360; // 6 seconds for vampires
}
threat.stunned = true;
threat.stunnedUntil = LK.ticks + stunDuration;
// Play stun sound
LK.getSound('dogStun').play();
// Make threat flash blue to show it's stunned
tween(threat.children[0], {
tint: 0x4444FF
}, {
duration: 300
});
// Dog gets slowed after stunning and retreats
self.isTargeted = true;
self.slowedUntil = LK.ticks + 300; // Slowed for 5 seconds
var retreatX = Math.random() * (1800 - 200) + 200;
var retreatY = Math.random() * (2400 - 200) + 200;
tween(self, {
x: retreatX,
y: retreatY
}, {
duration: 2000,
// Slower retreat due to being slowed
onFinish: function onFinish() {
self.isTargeted = false;
}
});
};
return self;
});
var NormalBerry = Container.expand(function () {
var self = Container.call(this);
var berryGraphics = self.attachAsset('normalBerry', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Gentle floating animation
self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.5;
};
return self;
});
var Vampire = Container.expand(function () {
var self = Container.call(this);
var vampireGraphics = self.attachAsset('vampire', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4; // Base speed
self.update = function () {
// Update speed based on current state
self.speed = isTwistedBloodMoon ? 6 : 4; // Faster during full moon
if (!isTwistedBloodMoon) return;
// During twisted blood moon cutscene, vampires move from bottom to campfire
if (isBloodMoonCutscene && isTwistedBloodMoon) {
// Spawn vampire from bottom if not already positioned
if (self.y > 2600) {
// Vampire starts from bottom, move to campfire
tween(self, {
x: campfire.x + (Math.random() - 0.5) * 300,
y: campfire.y + (Math.random() - 0.5) * 300
}, {
duration: 3000,
easing: tween.easeInOut
});
}
// Scare nearby NPCs into cabin
for (var i = 0; i < npcs.length; i++) {
if (!npcs[i].inCabin && !npcs[i].killed) {
var npcDist = Math.sqrt(Math.pow(npcs[i].x - self.x, 2) + Math.pow(npcs[i].y - self.y, 2));
if (npcDist < 300) {
npcs[i].enterCabin();
}
}
}
return;
}
// Check if stunned
if (self.stunned && LK.ticks < self.stunnedUntil) {
return; // Can't move when stunned
} else if (self.stunned) {
// Stun expired, remove stun
self.stunned = false;
tween(vampireGraphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
// Find fox to attack first, then NPCs
var target = null;
var closestDistance = Infinity;
// Check distance to fox
var foxDx = fox.x - self.x;
var foxDy = fox.y - self.y;
var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy);
if (foxDistance < closestDistance) {
closestDistance = foxDistance;
target = fox;
}
// Find nearest NPC if fox is too far (excluding bat, mouse, and fennec)
var closestNPC = null;
var closestNPCDistance = Infinity;
for (var i = 0; i < npcs.length; i++) {
if (!npcs[i].inCabin && !npcs[i].killed && npcs[i].type !== 'bat' && npcs[i].type !== 'mouse' && npcs[i].type !== 'fennec') {
var dx = npcs[i].x - self.x;
var dy = npcs[i].y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestNPCDistance) {
closestNPCDistance = distance;
closestNPC = npcs[i];
}
}
}
// Choose closest target between fox and NPCs
if (closestNPCDistance < closestDistance) {
target = closestNPC;
closestDistance = closestNPCDistance;
}
// Add random offset to movement to prevent grouping
var randomOffsetX = (Math.random() - 0.5) * 100;
var randomOffsetY = (Math.random() - 0.5) * 100;
// Move towards target with random offset
if (target) {
var dx = target.x + randomOffsetX - self.x;
var dy = target.y + randomOffsetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Attack if close enough
if (target === fox && distance < 50) {
// Vampire caught fox
LK.effects.flashScreen(0xFF0000, 500);
// Create and show vampire jumpscare
var vampireJumpscare = new JumpscareDisplay('vampireJumpscare');
game.addChild(vampireJumpscare);
vampireJumpscare.showJumpscare();
// Play jumpscare stinger immediately
LK.getSound('jumpscareStinger').play();
// Play vampire sound slightly delayed
LK.setTimeout(function () {
LK.getSound('vampireJumpscare').play();
}, 100);
// Stun ALL enemies, but kill only this vampire
// Stun all wolves
for (var w = 0; w < wolves.length; w++) {
wolves[w].stunned = true;
wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wolves[w].children[0]) {
tween(wolves[w].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all other vampires
for (var v = 0; v < vampires.length; v++) {
vampires[v].stunned = true;
vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires[v].children[0]) {
tween(vampires[v].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires2
for (var v2 = 0; v2 < vampires2.length; v2++) {
vampires2[v2].stunned = true;
vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires2[v2].children[0]) {
tween(vampires2[v2].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all zombies
for (var z = 0; z < zombies.length; z++) {
zombies[z].stunned = true;
zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (zombies[z].children[0]) {
tween(zombies[z].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun wendigo if it exists
if (wendigo) {
wendigo.stunned = true;
wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wendigo.wendigoGraphics) {
tween(wendigo.wendigoGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Kill only this vampire that hit the player
self.destroy();
vampires.splice(vampires.indexOf(self), 1);
playerLives--;
updateLivesDisplay();
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
}
}, 1000);
} else if (target !== fox && distance < 50) {
// Attack NPC
var isKilled = target.takeDamage(self);
if (isKilled) {
target.killed = true;
tween(target, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
target.destroy();
npcs.splice(npcs.indexOf(target), 1);
}
});
}
}
}
// Vampire animation
vampireGraphics.scaleX = 0.8 + Math.sin(LK.ticks * 0.4) * 0.1;
vampireGraphics.scaleY = 0.8 + Math.cos(LK.ticks * 0.3) * 0.1;
};
return self;
});
var Vampire2 = Container.expand(function () {
var self = Container.call(this);
var vampire2Graphics = self.attachAsset('vampire2', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4; // Base speed
self.update = function () {
// Update speed based on current state
self.speed = isTwistedBloodMoon ? 6 : 4; // Faster during full moon
if (!isTwistedBloodMoon) return;
// During twisted blood moon cutscene, vampire2 move from bottom to campfire
if (isBloodMoonCutscene && isTwistedBloodMoon) {
// Spawn vampire2 from bottom if not already positioned
if (self.y > 2600) {
// Vampire2 starts from bottom, move to campfire
tween(self, {
x: campfire.x + (Math.random() - 0.5) * 350,
y: campfire.y + (Math.random() - 0.5) * 350
}, {
duration: 3500,
easing: tween.easeInOut
});
}
// Scare nearby NPCs into cabin
for (var i = 0; i < npcs.length; i++) {
if (!npcs[i].inCabin && !npcs[i].killed) {
var npcDist = Math.sqrt(Math.pow(npcs[i].x - self.x, 2) + Math.pow(npcs[i].y - self.y, 2));
if (npcDist < 300) {
npcs[i].enterCabin();
}
}
}
return;
}
// Check if stunned
if (self.stunned && LK.ticks < self.stunnedUntil) {
return; // Can't move when stunned
} else if (self.stunned) {
// Stun expired, remove stun
self.stunned = false;
tween(vampire2Graphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
// Find fox to attack first, then NPCs
var target = null;
var closestDistance = Infinity;
// Check distance to fox
var foxDx = fox.x - self.x;
var foxDy = fox.y - self.y;
var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy);
if (foxDistance < closestDistance) {
closestDistance = foxDistance;
target = fox;
}
// Find nearest NPC if fox is too far (excluding bat, mouse, and fennec)
var closestNPC = null;
var closestNPCDistance = Infinity;
for (var i = 0; i < npcs.length; i++) {
if (!npcs[i].inCabin && !npcs[i].killed && npcs[i].type !== 'bat' && npcs[i].type !== 'mouse' && npcs[i].type !== 'fennec') {
var dx = npcs[i].x - self.x;
var dy = npcs[i].y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestNPCDistance) {
closestNPCDistance = distance;
closestNPC = npcs[i];
}
}
}
// Choose closest target between fox and NPCs
if (closestNPCDistance < closestDistance) {
target = closestNPC;
closestDistance = closestNPCDistance;
}
// Add different random offset for vampire2 to prevent grouping
var randomOffsetX = (Math.random() - 0.5) * 150;
var randomOffsetY = (Math.random() - 0.5) * 150;
// Move towards target with random offset
if (target) {
var dx = target.x + randomOffsetX - self.x;
var dy = target.y + randomOffsetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Attack if close enough
if (target === fox && distance < 50) {
// Vampire caught fox
LK.effects.flashScreen(0xFF0000, 500);
// Create and show vampire jumpscare
var vampireJumpscare = new JumpscareDisplay('vampireJumpscare');
game.addChild(vampireJumpscare);
vampireJumpscare.showJumpscare();
// Play jumpscare stinger immediately
LK.getSound('jumpscareStinger').play();
// Play vampire sound slightly delayed
LK.setTimeout(function () {
LK.getSound('vampireJumpscare').play();
}, 100);
// Stun ALL enemies, but kill only this vampire2
// Stun all wolves
for (var w = 0; w < wolves.length; w++) {
wolves[w].stunned = true;
wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wolves[w].children[0]) {
tween(wolves[w].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires
for (var v = 0; v < vampires.length; v++) {
vampires[v].stunned = true;
vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires[v].children[0]) {
tween(vampires[v].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all other vampires2
for (var v2 = 0; v2 < vampires2.length; v2++) {
vampires2[v2].stunned = true;
vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires2[v2].children[0]) {
tween(vampires2[v2].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all zombies
for (var z = 0; z < zombies.length; z++) {
zombies[z].stunned = true;
zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (zombies[z].children[0]) {
tween(zombies[z].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun wendigo if it exists
if (wendigo) {
wendigo.stunned = true;
wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wendigo.wendigoGraphics) {
tween(wendigo.wendigoGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Kill only this vampire2 that hit the player
self.destroy();
vampires2.splice(vampires2.indexOf(self), 1);
playerLives--;
updateLivesDisplay();
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
}
}, 1000);
} else if (target !== fox && distance < 50) {
// Attack NPC
var isKilled = target.takeDamage(self);
if (isKilled) {
target.killed = true;
tween(target, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
target.destroy();
npcs.splice(npcs.indexOf(target), 1);
}
});
}
}
}
// Vampire animation
vampire2Graphics.scaleX = 0.8 + Math.sin(LK.ticks * 0.4) * 0.1;
vampire2Graphics.scaleY = 0.8 + Math.cos(LK.ticks * 0.3) * 0.1;
};
return self;
});
var Wendigo = Container.expand(function () {
var self = Container.call(this);
// Create a scary looking wendigo using a dark red box shape
var wendigoGraphics = self.attachAsset('wendigoBody', {
anchorX: 0.5,
anchorY: 0.5
});
self.wendigoGraphics = wendigoGraphics; // Store reference for stunning
self.speed = 3;
self.lastFoxX = 0;
self.lastFoxY = 0;
self.update = function () {
// Wendigo appears during blood moon or twisted blood moon
if (!isBloodMoon && !isTwistedBloodMoon) return;
if (isTwistedBloodMoon) {
// During full moon, wendigo runs through but doesn't attack
// Simple movement animation without targeting
wendigoGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
wendigoGraphics.scaleY = 1 + Math.cos(LK.ticks * 0.2) * 0.1;
// Move wendigo across screen
self.x += 2;
if (self.x > 2200) {
self.x = -200;
}
return;
}
if (self.stunned && LK.ticks < self.stunnedUntil) {
// Wendigo is stunned, can't move
return;
} else if (self.stunned) {
// Stun expired, remove stun
self.stunned = false;
tween(wendigoGraphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
// Check for dog collision for stunning
for (var d = 0; d < npcs.length; d++) {
if (npcs[d].type === 'dog' && npcs[d].isTargeted && self.intersects(npcs[d])) {
npcs[d].stunThreat(self);
break;
}
}
// Calculate survival time and increase speed accordingly
var survivalTime = (LK.ticks - bloodMoonStartTime) / 60; // Convert to seconds
var currentSpeed = self.speed + survivalTime * wendigoSpeedIncreaseRate;
// Wendigo targeting with probability system
var target = null;
var availableTargets = [];
// Check for frozen NPC first (always highest priority)
for (var i = 0; i < npcs.length; i++) {
if (npcs[i].frozen && !npcs[i].killed) {
target = npcs[i];
break;
}
}
// If no frozen NPC, use probability system (excluding mouse, bat, and fennec from targeting)
if (!target) {
// Create weighted target list based on probabilities
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (!npc.inCabin && !npc.killed && npc.type !== 'mouse' && npc.type !== 'bat' && npc.type !== 'fennec') {
var weight = 0;
if (npc.type === 'cat' || npc.type === 'bunny' || npc.type === 'squirrel') {
weight = 25; // Common NPCs 25%
} else if (npc.type === 'dog') {
weight = 50; // Dog 50%
} else if (npc.type === 'deer') {
weight = 30; // Deer 30%
}
// Add multiple entries based on weight for probability
for (var w = 0; w < weight; w++) {
availableTargets.push(npc);
}
}
}
// Select random target from weighted list
if (availableTargets.length > 0) {
target = availableTargets[Math.floor(Math.random() * availableTargets.length)];
target.isTargeted = true; // Mark target as targeted
}
}
if (!target) {
target = fox;
}
if (!target) return;
// Move towards target
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Start wendigo chase music if targeting fox and close enough
if (target === fox && distance < 400 && !isWendigoChaseThemePlaying) {
isWendigoChaseThemePlaying = true;
LK.stopMusic();
LK.playMusic('wendigoChaseTheme');
} else if ((target !== fox || distance >= 500) && isWendigoChaseThemePlaying) {
// Stop chase music if not targeting fox or far away
isWendigoChaseThemePlaying = false;
LK.stopMusic();
LK.playMusic('bloodMoonMusic');
}
if (distance > 0) {
// Normalize direction and apply increasing speed
self.x += dx / distance * currentSpeed;
self.y += dy / distance * currentSpeed;
}
// Check if wendigo caught frozen NPC
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (npc.frozen && !npc.killed && self.intersects(npc)) {
// Attack the NPC (dogs take 2 hits, others die in 1)
var isKilled = npc.takeDamage(self);
if (isKilled) {
npc.killed = true;
// During blood moon cutscene, mark NPC for delayed removal
if (isBloodMoonCutscene) {
cutsceneNPCKilled = npc;
cutsceneNPCKillTime = LK.ticks;
// Make NPC fade but don't remove immediately
tween(npc, {
alpha: 0.3,
tint: 0x888888
}, {
duration: 500
});
} else {
// Normal behavior - make NPC disappear with death effect
tween(npc, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
npc.destroy();
npcs.splice(npcs.indexOf(npc), 1);
}
});
}
}
// Play kill sound
LK.getSound('wendigoGrowl').play();
break;
}
}
// Check if wendigo caught the fox
if (self.intersects(fox)) {
// Fox is caught - create jumpscare effect
LK.effects.flashScreen(0x000000, 700);
// Create and show wendigo jumpscare
var wendigoJumpscare = new JumpscareDisplay('wendigoJumpscare');
game.addChild(wendigoJumpscare);
wendigoJumpscare.showJumpscare();
// Play jumpscare stinger immediately
LK.getSound('jumpscareStinger').play();
// Scale wendigo up for additional effect
tween(wendigo, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 150
});
// Play wendigo growl slightly delayed
LK.setTimeout(function () {
LK.getSound('wendigoGrowl').play();
}, 150);
// Stun ALL enemies, but kill only this wendigo
// Stun all wolves
for (var w = 0; w < wolves.length; w++) {
wolves[w].stunned = true;
wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wolves[w].children[0]) {
tween(wolves[w].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires
for (var v = 0; v < vampires.length; v++) {
vampires[v].stunned = true;
vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires[v].children[0]) {
tween(vampires[v].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires2
for (var v2 = 0; v2 < vampires2.length; v2++) {
vampires2[v2].stunned = true;
vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires2[v2].children[0]) {
tween(vampires2[v2].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all zombies
for (var z = 0; z < zombies.length; z++) {
zombies[z].stunned = true;
zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (zombies[z].children[0]) {
tween(zombies[z].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Kill only this wendigo that hit the player
self.destroy();
wendigo = null;
// Decrease player lives
playerLives--;
updateLivesDisplay();
// Show scary game over after jumpscare
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
// End blood moon early when player gets caught
isBloodMoon = false;
startDay();
}
}, 1200);
return;
}
// Add scary breathing/pulsing effect
wendigoGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
wendigoGraphics.scaleY = 1 + Math.cos(LK.ticks * 0.2) * 0.1;
};
return self;
});
var Wolf = Container.expand(function () {
var self = Container.call(this);
var wolfGraphics = self.attachAsset('wolf', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.update = function () {
if (!isNight) return;
if (self.stunned && LK.ticks < self.stunnedUntil) {
// Wolf is stunned, can't move - stop chase music if this wolf was chasing
if (isChaseThemePlaying) {
var anyOtherWolfChasing = false;
for (var wc = 0; wc < wolves.length; wc++) {
var wolf = wolves[wc];
if (wolf !== self && wolf && !wolf.stunned) {
var foxDist = Math.sqrt(Math.pow(fox.x - wolf.x, 2) + Math.pow(fox.y - wolf.y, 2));
if (foxDist < 800) {
anyOtherWolfChasing = true;
break;
}
}
}
if (!anyOtherWolfChasing) {
isChaseThemePlaying = false;
LK.stopMusic();
if (isPeacefulNight) {
LK.playMusic('peacefulNightAmbience');
} else {
LK.playMusic('nightMusic');
}
}
}
return;
} else if (self.stunned) {
// Stun expired, remove stun
self.stunned = false;
tween(wolfGraphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
// Check for dog collision for stunning
for (var d = 0; d < npcs.length; d++) {
if (npcs[d].type === 'dog' && npcs[d].isTargeted && self.intersects(npcs[d])) {
npcs[d].stunThreat(self);
break;
}
}
// Find closest target (fox or NPCs)
var closestTarget = null;
var closestDistance = Infinity;
var detectionRange = 800; // Bigger detection range for wolves
// Check distance to fox
var foxDx = fox.x - self.x;
var foxDy = fox.y - self.y;
var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy);
if (foxDistance < detectionRange && foxDistance < closestDistance) {
closestTarget = fox;
closestDistance = foxDistance;
}
// Check distance to NPCs near campfire (excluding bats)
for (var n = 0; n < npcs.length; n++) {
if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat') {
var npcDx = npcs[n].x - self.x;
var npcDy = npcs[n].y - self.y;
var npcDistance = Math.sqrt(npcDx * npcDx + npcDy * npcDy);
// Check if NPC is near campfire (within 300 pixels of campfire)
var npcToCampfireDist = Math.sqrt(Math.pow(npcs[n].x - campfire.x, 2) + Math.pow(npcs[n].y - campfire.y, 2));
if (npcToCampfireDist < 300 && npcDistance < detectionRange && npcDistance < closestDistance) {
closestTarget = npcs[n];
closestDistance = npcDistance;
}
}
}
// If no high priority target found, go to campfire
if (!closestTarget) {
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 200) {
// Stay around campfire area
closestTarget = campfire;
closestDistance = campfireDistance;
}
}
// Only chase if there's a target within range
if (closestTarget && (closestDistance < detectionRange || closestTarget === campfire)) {
// Start chase music if not already playing and wolf is chasing fox
if (!isChaseThemePlaying && closestTarget === fox && closestDistance < detectionRange) {
isChaseThemePlaying = true;
LK.stopMusic();
LK.playMusic('chaseTheme');
}
var dx = closestTarget.x - self.x;
var dy = closestTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Use slower speed when going to campfire, normal speed for other targets
var moveSpeed = closestTarget === campfire ? self.speed * 0.5 : self.speed;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
} else {
// No valid target - check if we should stop chase music
if (isChaseThemePlaying) {
// Check if ANY wolf is actively chasing fox within detection range
var anyWolfChasing = false;
for (var wc = 0; wc < wolves.length; wc++) {
var wolf = wolves[wc];
if (wolf && !wolf.stunned) {
var foxDist = Math.sqrt(Math.pow(fox.x - wolf.x, 2) + Math.pow(fox.y - wolf.y, 2));
if (foxDist < detectionRange) {
anyWolfChasing = true;
break;
}
}
}
if (!anyWolfChasing) {
isChaseThemePlaying = false;
LK.stopMusic();
if (isPeacefulNight) {
LK.playMusic('peacefulNightAmbience');
} else {
LK.playMusic('nightMusic');
}
}
}
// Add prowling animation when not chasing
wolfGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.1;
// 50% chance to target a berry and wait there
if (!self.berryTarget && !self.wanderTarget && Math.random() < 0.01) {
if (Math.random() < 0.5 && normalBerries.length > 0) {
// 50% chance to target a berry
self.berryTarget = normalBerries[Math.floor(Math.random() * normalBerries.length)];
} else {
// Otherwise wander normally
self.wanderTarget = {
x: Math.random() * (1800 - 200) + 200,
y: Math.random() * (2400 - 200) + 200
};
}
}
// Move towards berry target and wait there
if (self.berryTarget) {
// Check if berry still exists
var berryExists = false;
for (var b = 0; b < normalBerries.length; b++) {
if (normalBerries[b] === self.berryTarget) {
berryExists = true;
break;
}
}
if (!berryExists) {
// Berry was collected, pick a new target
self.berryTarget = null;
} else {
var berryDx = self.berryTarget.x - self.x;
var berryDy = self.berryTarget.y - self.y;
var berryDistance = Math.sqrt(berryDx * berryDx + berryDy * berryDy);
if (berryDistance > 100) {
// Move towards berry
var berrySpeed = self.speed * 0.4;
self.x += berryDx / berryDistance * berrySpeed;
self.y += berryDy / berryDistance * berrySpeed;
}
// If close enough to berry (within 100 pixels), just wait there
}
}
// Wandering behavior when not targeting berry or chasing
if (!self.berryTarget && self.wanderTarget) {
var wanderDx = self.wanderTarget.x - self.x;
var wanderDy = self.wanderTarget.y - self.y;
var wanderDistance = Math.sqrt(wanderDx * wanderDx + wanderDy * wanderDy);
if (wanderDistance > 50) {
// Only move if not close to target
var wanderSpeed = self.speed * 0.3; // Slower wandering speed
self.x += wanderDx / wanderDistance * wanderSpeed;
self.y += wanderDy / wanderDistance * wanderSpeed;
} else {
// Reached wander target, pick a new one
self.wanderTarget = null;
}
}
}
// Check if wolf caught an NPC near campfire (excluding bats)
for (var n = 0; n < npcs.length; n++) {
if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat' && self.intersects(npcs[n])) {
var npcToCampfireDist = Math.sqrt(Math.pow(npcs[n].x - campfire.x, 2) + Math.pow(npcs[n].y - campfire.y, 2));
if (npcToCampfireDist < 300) {
// Wolf attacks NPC near campfire
var isKilled = npcs[n].takeDamage(self);
if (isKilled) {
npcs[n].killed = true;
tween(npcs[n], {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
npcs[n].destroy();
npcs.splice(npcs.indexOf(npcs[n]), 1);
}
});
} else {
// NPC runs around briefly then returns to campfire
npcs[n].runFromWolf = true;
npcs[n].runStartTime = LK.ticks;
var runX = Math.random() * (1800 - 200) + 200;
var runY = Math.random() * (2400 - 200) + 200;
tween(npcs[n], {
x: runX,
y: runY
}, {
duration: 2000,
onFinish: function onFinish() {
// Return to campfire area
var campfireReturnX = campfire.x + (Math.random() - 0.5) * 200;
var campfireReturnY = campfire.y + (Math.random() - 0.5) * 200;
tween(npcs[n], {
x: campfireReturnX,
y: campfireReturnY
}, {
duration: 1500,
onFinish: function onFinish() {
npcs[n].runFromWolf = false;
}
});
}
});
}
break;
}
}
}
// Check if wolf caught the fox for jumpscare
if (self.intersects(fox)) {
// Wolf jumpscare effect
LK.effects.flashScreen(0xFFFFFF, 400);
// Create and show wolf jumpscare
var wolfJumpscare = new JumpscareDisplay('wolfJumpscare');
game.addChild(wolfJumpscare);
wolfJumpscare.showJumpscare();
// Play jumpscare stinger immediately
LK.getSound('jumpscareStinger').play();
// Scale wolf up for additional effect
tween(self, {
scaleX: 2,
scaleY: 2
}, {
duration: 120
});
// Play wolf roar slightly delayed
LK.setTimeout(function () {
LK.getSound('wolfRoar').play();
}, 120);
// Stun ALL enemies, but kill only this wolf
// Stun all other wolves
for (var w = 0; w < wolves.length; w++) {
wolves[w].stunned = true;
wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wolves[w].children[0]) {
tween(wolves[w].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires
for (var v = 0; v < vampires.length; v++) {
vampires[v].stunned = true;
vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires[v].children[0]) {
tween(vampires[v].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires2
for (var v2 = 0; v2 < vampires2.length; v2++) {
vampires2[v2].stunned = true;
vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires2[v2].children[0]) {
tween(vampires2[v2].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all zombies
for (var z = 0; z < zombies.length; z++) {
zombies[z].stunned = true;
zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (zombies[z].children[0]) {
tween(zombies[z].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun wendigo if it exists
if (wendigo) {
wendigo.stunned = true;
wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wendigo.wendigoGraphics) {
tween(wendigo.wendigoGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Kill only this wolf that hit the player
self.destroy();
wolves.splice(wolves.indexOf(self), 1);
// Decrease player lives
playerLives--;
updateLivesDisplay();
// Check if game over or respawn
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
}
}, 1000);
return;
}
};
return self;
});
var Wood = Container.expand(function () {
var self = Container.call(this);
var woodGraphics = self.attachAsset('wood', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Gentle floating animation
self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.5;
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1; // Slow zombie
self.update = function () {
if (!isNight && !isTwistedBloodMoon && !isBloodMoon) return;
// During blood moon cutscene, wait at campfire but don't follow wendigo
if (isBloodMoonCutscene) {
// If wendigo is at campfire and no NPCs around, zombies wait at campfire
if (cutsceneWendigoAtCampfire && cutsceneZombiesWaiting) {
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 150) {
// Move to campfire area
self.x += campfireDx / campfireDistance * (self.speed * 0.5);
self.y += campfireDy / campfireDistance * (self.speed * 0.5);
}
return;
}
// If wendigo killed an NPC, stay still for 5 seconds
if (cutsceneNPCKilled && LK.ticks - cutsceneNPCKillTime < 300) {
return; // Stay still
}
// Allow normal zombie behavior during blood moon cutscene when not waiting
}
// Check if stunned
if (self.stunned && LK.ticks < self.stunnedUntil) {
return; // Can't move when stunned
} else if (self.stunned) {
// Stun expired, remove stun
self.stunned = false;
tween(zombieGraphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
// Zombies prioritize NPCs not in cabin over fox during blood moon
var closestTarget = null;
var closestDistance = Infinity;
var detectionRange = 600; // Increased detection range for better chasing
// First priority: Check for NPCs that are not in cabin (excluding bat, mouse, and fennec)
for (var n = 0; n < npcs.length; n++) {
if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat' && npcs[n].type !== 'mouse' && npcs[n].type !== 'fennec') {
var npcDx = npcs[n].x - self.x;
var npcDy = npcs[n].y - self.y;
var npcDistance = Math.sqrt(npcDx * npcDx + npcDy * npcDy);
if (npcDistance < detectionRange && npcDistance < closestDistance) {
closestTarget = npcs[n];
closestDistance = npcDistance;
}
}
}
// Second priority: Check distance to fox only if no NPCs available
if (!closestTarget) {
var foxDx = fox.x - self.x;
var foxDy = fox.y - self.y;
var foxDistance = Math.sqrt(foxDx * foxDx + foxDy * foxDy);
if (foxDistance < detectionRange) {
closestTarget = fox;
closestDistance = foxDistance;
}
}
// Only chase if there's a target within range
if (closestTarget && closestDistance < detectionRange) {
// Start chase music if not already playing and zombie is chasing fox
if (!isZombieChaseThemePlaying && closestTarget === fox) {
isZombieChaseThemePlaying = true;
LK.stopMusic();
LK.playMusic('zombieChaseTheme');
}
var dx = closestTarget.x - self.x;
var dy = closestTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else {
// Check if we should stop chase music
if (isZombieChaseThemePlaying) {
var anyZombieChasing = false;
for (var zc = 0; zc < zombies.length; zc++) {
if (zombies[zc] !== self) {
var foxDist = Math.sqrt(Math.pow(fox.x - zombies[zc].x, 2) + Math.pow(fox.y - zombies[zc].y, 2));
if (foxDist < detectionRange) {
anyZombieChasing = true;
break;
}
}
}
if (!anyZombieChasing) {
isZombieChaseThemePlaying = false;
LK.stopMusic();
if (isTwistedBloodMoon) {
LK.playMusic('twistedBloodMoonTheme');
} else if (isBloodMoon) {
LK.playMusic('bloodMoonMusic');
} else if (isPeacefulNight) {
LK.playMusic('peacefulNightAmbience');
} else if (isNight) {
LK.playMusic('nightMusic');
}
}
}
// Add prowling animation when not chasing
zombieGraphics.scaleX = 0.7 + Math.sin(LK.ticks * 0.1) * 0.05;
// Simplified movement - no berry targeting, focus on chasing
}
// Check collision with NPCs first (higher priority than fox)
for (var n = 0; n < npcs.length; n++) {
if (!npcs[n].inCabin && !npcs[n].killed && npcs[n].type !== 'bat' && npcs[n].type !== 'mouse' && npcs[n].type !== 'fennec' && self.intersects(npcs[n])) {
// Zombie attacks NPC
var isKilled = npcs[n].takeDamage(self);
if (isKilled) {
npcs[n].killed = true;
tween(npcs[n], {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
npcs[n].destroy();
npcs.splice(npcs.indexOf(npcs[n]), 1);
}
});
}
break;
}
}
// Check collision with fox
if (self.intersects(fox)) {
// Zombie caught fox
LK.effects.flashScreen(0x00FF00, 300);
// Create and show zombie jumpscare
var zombieJumpscare = new JumpscareDisplay('zombieJumpscare');
game.addChild(zombieJumpscare);
zombieJumpscare.showJumpscare();
// Play jumpscare stinger immediately
LK.getSound('jumpscareStinger').play();
// Play zombie sound slightly delayed
LK.setTimeout(function () {
LK.getSound('glitchSound').play();
}, 100);
// Stun ALL enemies, but kill only this zombie
// Stun all wolves
for (var w = 0; w < wolves.length; w++) {
wolves[w].stunned = true;
wolves[w].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wolves[w].children[0]) {
tween(wolves[w].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires
for (var v = 0; v < vampires.length; v++) {
vampires[v].stunned = true;
vampires[v].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires[v].children[0]) {
tween(vampires[v].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all vampires2
for (var v2 = 0; v2 < vampires2.length; v2++) {
vampires2[v2].stunned = true;
vampires2[v2].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (vampires2[v2].children[0]) {
tween(vampires2[v2].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun all other zombies
for (var z = 0; z < zombies.length; z++) {
zombies[z].stunned = true;
zombies[z].stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (zombies[z].children[0]) {
tween(zombies[z].children[0], {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Stun wendigo if it exists
if (wendigo) {
wendigo.stunned = true;
wendigo.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
if (wendigo.wendigoGraphics) {
tween(wendigo.wendigoGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
}
}
// Kill only this zombie that hit the player
self.destroy();
zombies.splice(zombies.indexOf(self), 1);
playerLives--;
updateLivesDisplay();
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
}
}, 800);
return;
}
// Zombie animation
zombieGraphics.scaleY = 0.7 + Math.cos(LK.ticks * 0.15) * 0.05;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game state variables
var fox;
var normalBerries = [];
var wolves = [];
var npcs = [];
var dragNode = null;
var berrySpawnTimer = 0;
var wendigo;
var wendigoSpeedIncreaseRate = 0.02;
var bunny;
var cabin;
// Cutscene variables
var isIntroCutscene = true;
var introCutsceneTimer = 0;
var introCutsceneDelay = 120; // 2 seconds delay before cutscene starts
var foxAppearedSoundPlayed = false;
var doorSoundPlayed = false;
var isBloodMoonCutscene = false;
var bloodMoonCutsceneTimer = 0;
var cutsceneNPCKilled = null;
var cutsceneNPCKillTime = 0;
var cutsceneWendigoAtCampfire = false;
var cutsceneZombiesWaiting = false;
var cutsceneZombiesWaitStartTime = 0;
var cutsceneNPCSpawned = false;
// Day/Night cycle variables
var isNight = false;
var isBloodMoon = false;
var isPeacefulNight = false;
var isTwistedBloodMoon = false;
var vampires = [];
var vampires2 = [];
var zombies = [];
var nightCount = 0;
var dayCount = 1;
var berriesCollectedInCycle = 0;
var scoreMultiplier = 1;
var bloodMoonStartTime = 0;
var bloodMoonCount = 0;
var dayLengthMultiplier = 1;
var isChaseThemePlaying = false;
var isZombieChaseThemePlaying = false;
var isWendigoChaseThemePlaying = false;
var playerLives = 3;
var campfire;
var woodItems = [];
var twistedBloodMoonWoodCollected = 0; // Track wood collected during full moon
var lastBloodMoonNight = -1; // Track when last blood moon occurred
var woodSpawnTimer = 0; // Timer for wood spawning during different phases
// NPC types organized by rarity
var commonNPCs = ['cat', 'bunny', 'squirrel'];
var uncommonNPCs = ['deer', 'fennec'];
var rareNPCs = ['dog', 'mouse', 'bat'];
// Add forest background
var background = game.attachAsset('forestBackground', {
x: 0,
y: 0
});
// Create score display
var scoreTxt = new Text2('Score: 0 (x1)', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create day/night indicator
var dayNightTxt = new Text2('Day', {
size: 60,
fill: 0xFFFF00
});
dayNightTxt.anchor.set(0.5, 0);
dayNightTxt.y = 100;
LK.gui.top.addChild(dayNightTxt);
// Create day counter display
var dayCounterTxt = new Text2('Day 1', {
size: 60,
fill: 0xFFFFFF
});
dayCounterTxt.anchor.set(0.5, 0);
dayCounterTxt.y = 170;
LK.gui.top.addChild(dayCounterTxt);
// Create night counter display
var nightCounterTxt = new Text2('Night 1', {
size: 60,
fill: 0xFFFFFF
});
nightCounterTxt.anchor.set(0.5, 0);
nightCounterTxt.y = 170;
nightCounterTxt.visible = false; // Hidden by default during day
LK.gui.top.addChild(nightCounterTxt);
// Create lives counter display
var livesTxt = new Text2('Lives: 3', {
size: 60,
fill: 0xFF0000
});
livesTxt.anchor.set(0.5, 0);
livesTxt.y = 240;
LK.gui.top.addChild(livesTxt);
// Create campfire wood counter display
var campfireTxt = new Text2('Campfire: 0 wood', {
size: 50,
fill: 0xFFFFFF
});
campfireTxt.anchor.set(0.5, 0);
campfireTxt.y = 310;
campfireTxt.visible = false; // Hidden by default
LK.gui.top.addChild(campfireTxt);
// Create wood multiplier display
var woodMultiplierTxt = new Text2('Wood Multiplier: Cat 2x, Bunny 2x, Bat 4x', {
size: 45,
fill: 0x90EE90
});
woodMultiplierTxt.anchor.set(0.5, 0);
woodMultiplierTxt.y = 370;
woodMultiplierTxt.visible = false; // Hidden by default
LK.gui.top.addChild(woodMultiplierTxt);
// Add cabin for NPCs
cabin = game.attachAsset('cabin', {
anchorX: 0.5,
anchorY: 0.5,
x: 1650,
y: 450
});
// Add campfire
campfire = game.addChild(new Campfire());
campfire.x = 1024;
campfire.y = 1000;
// Bunny removed from initial spawn
// Initialize fox
fox = game.addChild(new Fox());
// Start fox at cabin for intro cutscene
fox.x = cabin.x;
fox.y = cabin.y;
// Spawn normal berry function
function spawnNormalBerry() {
// Spawn single berry - no duplication
var berry = new NormalBerry();
berry.x = Math.random() * (2048 - 120) + 60;
berry.y = Math.random() * (2732 - 120) + 60;
normalBerries.push(berry);
game.addChild(berry);
}
// Spawn wolf function
function spawnWolf() {
var wolf = new Wolf();
wolf.x = Math.random() * (2048 - 120) + 60;
wolf.y = Math.random() * (2732 - 120) + 60;
wolves.push(wolf);
game.addChild(wolf);
}
// Spawn wood function
function spawnWood() {
var wood = new Wood();
wood.x = Math.random() * (2048 - 120) + 60;
wood.y = Math.random() * (2732 - 120) + 60;
woodItems.push(wood);
game.addChild(wood);
}
// Spawn zombie function
function spawnZombie() {
var zombie = new Zombie();
// During blood moon, scatter zombies in random locations across the map
if (isBloodMoon) {
// Scatter zombies randomly across the entire map during blood moon
zombie.x = Math.random() * (1800 - 200) + 200; // Random X position
zombie.y = Math.random() * (2200 - 400) + 400; // Random Y position
} else {
// Default spawn behavior for non-blood moon scenarios
zombie.x = Math.random() * (2048 - 120) + 60;
zombie.y = Math.random() * (2732 - 120) + 60;
}
zombies.push(zombie);
game.addChild(zombie);
}
// Spawn vampire function
function spawnVampire() {
var vampire = new Vampire();
vampire.x = Math.random() * (2048 - 120) + 60;
vampire.y = Math.random() * (2732 - 120) + 60;
vampires.push(vampire);
game.addChild(vampire);
}
// Spawn vampire2 function
function spawnVampire2() {
var vampire2 = new Vampire2();
vampire2.x = Math.random() * (2048 - 120) + 60;
vampire2.y = Math.random() * (2732 - 120) + 60;
vampires2.push(vampire2);
game.addChild(vampire2);
}
// Update campfire display
function updateCampfireDisplay() {
campfireTxt.setText('Campfire: ' + campfire.wood + ' wood');
}
// Spawn NPC function
function spawnNPC() {
// Determine rarity first with updated probabilities
var rarity;
var selectedType;
var rarityRand = Math.random();
if (rarityRand <= 0.5) {
// Common NPCs: 50% chance
rarity = 'common';
selectedType = commonNPCs[Math.floor(Math.random() * commonNPCs.length)];
} else if (rarityRand <= 0.85) {
// Uncommon NPCs: 35% chance
rarity = 'uncommon';
selectedType = uncommonNPCs[Math.floor(Math.random() * uncommonNPCs.length)];
} else {
// Rare NPCs: 15% chance
rarity = 'rare';
selectedType = rareNPCs[Math.floor(Math.random() * rareNPCs.length)];
}
var npc = new NPC(selectedType);
npc.rarity = rarity;
npc.setRarityVisual(); // Apply visual indication based on rarity
npc.x = Math.random() * (1800 - 200) + 200;
npc.y = Math.random() * (2400 - 200) + 200;
npcs.push(npc);
game.addChild(npc);
// If bat spawns, give extra life immediately
if (selectedType === 'bat') {
playerLives++;
updateLivesDisplay();
LK.getSound('batTouch').play();
}
// Apply multiplier immediately when NPC spawns
if (npc.multiplier > 0) {
scoreMultiplier = npc.multiplier;
updateScoreDisplay();
}
}
// Transition to night
function startNight() {
isNight = true;
nightCount++;
berriesCollectedInCycle = 0;
// Check for blood moon - 3rd night is guaranteed, then 25% chance (but not consecutive)
var shouldBeBloodMoon = false;
if (nightCount === 3) {
// 3rd night is always a blood moon
shouldBeBloodMoon = true;
} else if (nightCount > 3 && nightCount !== lastBloodMoonNight + 1) {
// After 3rd night, 25% chance for blood moon, but not if last night was blood moon
shouldBeBloodMoon = Math.random() < 0.25;
}
// Check for peaceful night - 25% base chance, 100% chance before blood moon, forced on night 2
var peacefulChance = 0.25;
if (nightCount === 2) {
// Night 2 is always peaceful since night 3 is guaranteed blood moon
peacefulChance = 1.0;
} else if (nightCount > 3) {
// For nights after 3, check if NEXT night could be blood moon
var nextNightCouldBeBloodMoon = nightCount + 1 !== lastBloodMoonNight + 1 && Math.random() < 0.25;
if (nextNightCouldBeBloodMoon) {
// If next night could be blood moon, make this night peaceful
peacefulChance = 1.0;
}
}
isPeacefulNight = Math.random() < peacefulChance && !shouldBeBloodMoon;
// Only play howl if not peaceful night
if (!isPeacefulNight) {
LK.getSound('howl').play();
}
// Stop any currently playing music and switch to appropriate night music
LK.stopMusic();
if (isPeacefulNight) {
LK.playMusic('peacefulNightAmbience');
} else {
LK.playMusic('nightMusic');
}
if (shouldBeBloodMoon) {
bloodMoonCount++;
lastBloodMoonNight = nightCount; // Track when blood moon occurred
// Add 5 berries to requirement for each blood moon
dayLengthMultiplier = 1 + bloodMoonCount * 0.5; // Each blood moon adds 5 berries (0.5 * 10 = 5)
isBloodMoon = true;
bloodMoonStartTime = LK.ticks;
// Start blood moon cutscene
isBloodMoonCutscene = true;
bloodMoonCutsceneTimer = 0;
cutsceneWendigoAtCampfire = false;
cutsceneZombiesWaiting = false;
cutsceneZombiesWaitStartTime = 0;
cutsceneNPCSpawned = false;
// Reset multiplier during blood moon
scoreMultiplier = 1;
updateScoreDisplay();
// Stop current music and play blood moon music
LK.stopMusic();
LK.playMusic('bloodMoonMusic');
// Freeze one random NPC
if (npcs.length > 0) {
var randomNPC = npcs[Math.floor(Math.random() * npcs.length)];
randomNPC.freeze();
}
// Move other NPCs to cabin
for (var i = 0; i < npcs.length; i++) {
if (!npcs[i].frozen) {
npcs[i].enterCabin();
}
}
// Always spawn wendigo during blood moon (twisted or normal)
wendigo = new Wendigo();
wendigo.x = -100;
wendigo.y = Math.random() * 2000 + 300;
game.addChild(wendigo);
// Always spawn zombies during blood moon (3 zombies)
spawnZombie();
spawnZombie();
spawnZombie();
// Vampires are spawned when twisted blood moon is triggered in campfire
// Spawn extra berries during blood moon (2-4 additional berries)
var extraBerries = Math.floor(Math.random() * 3) + 2;
for (var b = 0; b < extraBerries; b++) {
spawnNormalBerry();
}
// Change background to blood red
game.setBackgroundColor(0x330000);
tween(background, {
tint: 0x660000,
alpha: 0.8
}, {
duration: 1500
});
dayNightTxt.setText('Blood Moon');
dayNightTxt.fill = 0xFF0000;
// Hide day counter and show night counter
dayCounterTxt.visible = false;
nightCounterTxt.visible = true;
nightCounterTxt.setText('Night ' + nightCount);
} else {
// Regular night
if (!isPeacefulNight && nightCount !== 2) {
// Spawn wolves - 3 by default, wolf count increases after each completed blood moon
// Don't spawn wolves on night 2
var baseWolves = 3 + bloodMoonCount; // Add 1 wolf for each completed blood moon
var fennecPresent = false;
for (var f = 0; f < npcs.length; f++) {
if (npcs[f].type === 'fennec') {
fennecPresent = true;
break;
}
}
var wolvesToSpawn = fennecPresent ? baseWolves - 1 : baseWolves;
// Additional check: explicitly prevent wolf spawning on night 2
if (nightCount === 2) {
wolvesToSpawn = 0;
}
for (var w = 0; w < wolvesToSpawn; w++) {
var wolf = new Wolf();
// Spawn wolves from both sides alternating
if (w % 2 === 0) {
// Even wolves spawn from left side
wolf.x = -100;
} else {
// Odd wolves spawn from right side
wolf.x = 2148;
}
wolf.y = Math.random() * (2200 - 400) + 400;
wolves.push(wolf);
game.addChild(wolf);
}
}
// Despawn any existing wolves during peaceful nights (especially night 2)
if (isPeacefulNight || nightCount === 2) {
// Immediately destroy all existing wolves
for (var w = wolves.length - 1; w >= 0; w--) {
var wolf = wolves[w];
wolf.destroy();
wolves.splice(w, 1);
}
}
// Move NPCs to campfire (even during peaceful nights)
for (var i = 0; i < npcs.length; i++) {
npcs[i].enterCabin();
}
// Change background to dark blue night
game.setBackgroundColor(0x001122);
tween(background, {
tint: 0x223355,
alpha: 0.7
}, {
duration: 1500
});
if (isPeacefulNight) {
dayNightTxt.setText('Peaceful Night');
dayNightTxt.fill = 0x99CCFF;
// Hide day counter and show night counter
dayCounterTxt.visible = false;
nightCounterTxt.visible = true;
nightCounterTxt.setText('Night ' + nightCount);
} else {
dayNightTxt.setText('Night');
dayNightTxt.fill = 0x6666FF;
}
// Hide day counter and show night counter
dayCounterTxt.visible = false;
nightCounterTxt.visible = true;
nightCounterTxt.setText('Night ' + nightCount);
// Show wood counter during night
campfireTxt.visible = true;
woodMultiplierTxt.visible = true;
updateCampfireDisplay();
// Spawn wood during night (3-5 pieces, but only 10% chance during full moon)
if (!isTwistedBloodMoon || Math.random() < 0.1) {
var woodToSpawn = Math.floor(Math.random() * 3) + 3;
for (var w = 0; w < woodToSpawn; w++) {
spawnWood();
}
}
// Spawn zombies (25% chance for 2 zombies, but not during peaceful nights)
if (!isPeacefulNight && Math.random() < 0.25) {
spawnZombie();
spawnZombie();
}
}
}
// Transition to day
function startDay() {
isNight = false;
isBloodMoon = false;
isPeacefulNight = false;
isChaseThemePlaying = false;
isZombieChaseThemePlaying = false;
isWendigoChaseThemePlaying = false;
berriesCollectedInCycle = 0;
// Stop any currently playing music and switch to day music
LK.stopMusic();
LK.playMusic('dayMusic');
// Immediately destroy all wolves when day starts
for (var i = 0; i < wolves.length; i++) {
var wolf = wolves[i];
wolf.destroy();
}
wolves = [];
// Remove wendigo if exists
if (wendigo) {
wendigo.destroy();
wendigo = null;
}
// Hide wood counter during day
campfireTxt.visible = false;
woodMultiplierTxt.visible = false;
// Remove remaining wood items
for (var i = 0; i < woodItems.length; i++) {
woodItems[i].destroy();
}
woodItems = [];
// Remove zombies
for (var i = 0; i < zombies.length; i++) {
zombies[i].destroy();
}
zombies = [];
// Remove vampires
for (var i = 0; i < vampires.length; i++) {
vampires[i].destroy();
}
vampires = [];
// Remove vampires2
for (var i = 0; i < vampires2.length; i++) {
vampires2[i].destroy();
}
vampires2 = [];
// Reset full moon state
isTwistedBloodMoon = false;
twistedBloodMoonWoodCollected = 0; // Reset wood counter
// Bring NPCs out of cabin
for (var i = 0; i < npcs.length; i++) {
if (!npcs[i].killed) {
npcs[i].exitCabin();
}
}
// Spawn new NPC with 25% chance, but force spawn after night 1 or after blood moon
var shouldSpawnNPC = Math.random() < 0.25;
if (nightCount === 1 || shouldSpawnNPC) {
spawnNPC();
}
// Always spawn NPC after blood moon (100% chance)
if (lastBloodMoonNight === nightCount) {
spawnNPC();
}
// Change background back to day
game.setBackgroundColor(0x228b22);
tween(background, {
tint: 0xFFFFFF,
alpha: 1
}, {
duration: 1500
});
dayNightTxt.setText('Day');
dayNightTxt.fill = 0xFFFF00;
// Update day counter
dayCount++;
dayCounterTxt.setText('Day ' + dayCount);
// Show day counter and hide night counter
dayCounterTxt.visible = true;
nightCounterTxt.visible = false;
}
// Update score display with multiplier
function updateScoreDisplay() {
scoreTxt.setText('Score: ' + LK.getScore() + ' (x' + scoreMultiplier + ')');
}
// Update lives display
function updateLivesDisplay() {
livesTxt.setText('Lives: ' + playerLives);
}
// Keyboard state tracking
var keys = {
left: false,
right: false,
up: false,
down: false
};
// Handle mouse/touch move
function handleMove(x, y, obj) {
// Disable input during intro cutscene
if (isIntroCutscene) return;
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
// Keep fox within bounds
if (dragNode.x < 60) dragNode.x = 60;
if (dragNode.x > 1988) dragNode.x = 1988;
if (dragNode.y < 60) dragNode.y = 60;
if (dragNode.y > 2672) dragNode.y = 2672;
}
}
// Mouse/touch events
game.move = handleMove;
game.down = function (x, y, obj) {
// Disable input during intro cutscene
if (isIntroCutscene) return;
dragNode = fox;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Start with day music
LK.playMusic('dayMusic');
// Initial berry spawning
spawnNormalBerry();
spawnNormalBerry();
spawnNormalBerry();
// Force spawn a bat immediately for testing
var forcedBat = new NPC('bat');
forcedBat.rarity = 'rare';
forcedBat.setRarityVisual();
forcedBat.x = Math.random() * (1800 - 200) + 200;
forcedBat.y = Math.random() * (2400 - 200) + 200;
npcs.push(forcedBat);
game.addChild(forcedBat);
// Give extra life since bat spawned
playerLives++;
updateLivesDisplay();
LK.getSound('batTouch').play();
// Create light effect for twisted blood moon
var foxLight = null;
// Main game update loop
game.update = function () {
// Handle fox light during twisted blood moon
if (isTwistedBloodMoon && !foxLight) {
// Create a glowing light around fox
foxLight = LK.getAsset('campfire', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.7
});
game.addChild(foxLight);
// Animate light with gentle glow
tween(foxLight, {
alpha: 0.4
}, {
duration: 1000,
onFinish: function onFinish() {
tween(foxLight, {
alpha: 0.7
}, {
duration: 1000
});
}
});
} else if (!isTwistedBloodMoon && foxLight) {
// Remove light when not in twisted blood moon
foxLight.destroy();
foxLight = null;
}
// Update light position to follow fox
if (foxLight) {
foxLight.x = fox.x;
foxLight.y = fox.y;
}
// Handle intro cutscene
if (isIntroCutscene) {
introCutsceneTimer++;
// Wait for delay before starting cutscene
if (introCutsceneTimer <= introCutsceneDelay) {
return; // Wait before starting cutscene
}
if (introCutsceneTimer === introCutsceneDelay + 1 && !foxAppearedSoundPlayed) {
// Play fox appear sound when fox appears
LK.getSound('foxAppear').play();
foxAppearedSoundPlayed = true;
}
if (introCutsceneTimer === introCutsceneDelay + 30 && !doorSoundPlayed) {
// Play door sound 0.5 seconds after fox appears
LK.getSound('doorOpen').play();
doorSoundPlayed = true;
}
// Find nearest berry for target
var targetBerry = null;
var closestDistance = Infinity;
for (var b = 0; b < normalBerries.length; b++) {
var dx = normalBerries[b].x - cabin.x;
var dy = normalBerries[b].y - cabin.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
targetBerry = normalBerries[b];
}
}
// Move fox from cabin to nearest berry over 3 seconds after delay
if (introCutsceneTimer <= introCutsceneDelay + 180 && targetBerry) {
// 3 seconds at 60fps after delay
var progress = (introCutsceneTimer - introCutsceneDelay) / 180;
var startX = cabin.x;
var startY = cabin.y;
var endX = targetBerry.x;
var endY = targetBerry.y;
fox.x = tween.easeInOut(progress) * (endX - startX) + startX;
fox.y = tween.easeInOut(progress) * (endY - startY) + startY;
} else {
// Cutscene finished
isIntroCutscene = false;
if (targetBerry) {
fox.x = targetBerry.x;
fox.y = targetBerry.y;
} else {
fox.x = 1024;
fox.y = 1366;
}
}
return; // Don't process other game logic during intro
}
// Handle blood moon cutscene
if (isBloodMoonCutscene) {
bloodMoonCutsceneTimer++;
// Check if NPC was killed and 5 seconds have passed
if (cutsceneNPCKilled && LK.ticks - cutsceneNPCKillTime >= 300) {
// 5 seconds at 60fps
// Remove the killed NPC body
tween(cutsceneNPCKilled, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
if (cutsceneNPCKilled) {
cutsceneNPCKilled.destroy();
npcs.splice(npcs.indexOf(cutsceneNPCKilled), 1);
cutsceneNPCKilled = null;
}
}
});
// End cutscene
isBloodMoonCutscene = false;
bloodMoonCutsceneTimer = 0;
// Reset cutscene variables to allow fox movement again
cutsceneNPCKilled = null;
cutsceneNPCKillTime = 0;
}
}
// Spawn berries over time with random intervals - 1.5-3 seconds
berrySpawnTimer++;
var baseSpawnTime = 90; // 1.5 seconds at 60fps
var maxSpawnTime = 180; // 3 seconds at 60fps
var randomSpawnTime = Math.random() * (maxSpawnTime - baseSpawnTime) + baseSpawnTime;
if (berrySpawnTimer >= randomSpawnTime) {
spawnNormalBerry();
berrySpawnTimer = 0;
}
// Wood spawning during specific phases
woodSpawnTimer++;
if (woodSpawnTimer >= 180) {
// Every 3 seconds (180 frames at 60fps)
woodSpawnTimer = 0;
if (isTwistedBloodMoon) {
// 25% chance during full moon
if (Math.random() < 0.25) {
spawnWood();
}
} else if (isPeacefulNight || isNight) {
// 20% chance during peaceful night and regular night (not blood moon)
if (!isBloodMoon && Math.random() < 0.2) {
spawnWood();
}
}
}
// Check collisions with normal berries
for (var i = normalBerries.length - 1; i >= 0; i--) {
var berry = normalBerries[i];
if (fox.intersects(berry)) {
// Collect berry and increase score with multiplier
var scoreGain = 1 * scoreMultiplier;
LK.setScore(LK.getScore() + scoreGain);
berriesCollectedInCycle++;
updateScoreDisplay();
LK.getSound('berryCollect').play();
// Remove berry from array first, then destroy
normalBerries.splice(i, 1);
berry.destroy();
// Check for day/night transitions (but not during twisted blood moon)
if (!isTwistedBloodMoon) {
var dayRequirement = 10 * dayLengthMultiplier;
var nightRequirement = isBloodMoon ? dayRequirement : 5;
if (!isNight && berriesCollectedInCycle >= dayRequirement) {
startNight();
} else if (isNight && berriesCollectedInCycle >= nightRequirement) {
startDay();
}
}
}
}
// Check collisions with wood items
for (var i = woodItems.length - 1; i >= 0; i--) {
var wood = woodItems[i];
if (fox.intersects(wood)) {
// Collect wood and add to campfire
campfire.addWood(1);
// Play different sound based on game state
if (isTwistedBloodMoon) {
LK.getSound('foxTwistedWoodCollect').play();
} else {
LK.getSound('woodCollect').play();
}
// Track wood collected during full moon
if (isTwistedBloodMoon) {
twistedBloodMoonWoodCollected++;
// End full moon when 5 wood collected
if (twistedBloodMoonWoodCollected >= 5) {
isTwistedBloodMoon = false;
twistedBloodMoonWoodCollected = 0; // Reset counter
startDay(); // End full moon and start day
}
}
// Remove wood from array first, then destroy
woodItems.splice(i, 1);
wood.destroy();
}
}
// Check collisions with NPCs for sound and visual feedback only
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (!npc.inCabin && !npc.frozen && fox.intersects(npc)) {
// Play touch sound for the specific animal type
LK.getSound(npc.type + 'Touch').play();
// Give visual feedback for all animals
tween(npc, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(npc, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
}
// Wolf collision is now handled in Wolf class update method
};
a cute fox with a basket. In-Game asset. 2d. High contrast. No shadows
a berry bush with a phew yummy berries on it. In-Game asset. 2d. High contrast. No shadows
a top view of a grass field with a cabin and some trees. In-Game asset. 2d. High contrast. No shadows
a cute little bunny. In-Game asset. 2d. High contrast. No shadows
cabin. In-Game asset. 2d. High contrast. No shadows
a angry wolve thats in 2d. In-Game asset. 2d. High contrast. No shadows
a cute cat with a bow. In-Game asset. 2d. High contrast. No shadows
a cute dear with an axe. In-Game asset. 2d. High contrast. No shadows
a dog cute dog with a hat and a shotgun. In-Game asset. 2d. High contrast. No shadows
a cute mouse with a hoodie on. In-Game asset. 2d. High contrast. No shadows
a cute fennec fox with a backbag. In-Game asset. 2d. High contrast. No shadows
a cute squirrel with a gutair. In-Game asset. 2d. High contrast. No shadows
a campfire. In-Game asset. 2d. High contrast. No shadows
sticks. In-Game asset. 2d. High contrast. No shadows
zombie duck. In-Game asset. 2d. High contrast. No shadows
cultists animal. In-Game asset. 2d. High contrast. No shadows
cultist girrafe. In-Game asset. 2d. High contrast. No shadows
cute nice bat cultist. In-Game asset. 2d. High contrast. No shadows
glitchSound
Sound effect
berryCollect
Sound effect
wendigoGrowl
Sound effect
wolfRoar
Sound effect
howl
Sound effect
dayMusic
Music
nightMusic
Music
bloodMoonMusic
Music
chaseTheme
Music
bunnyTouch
Sound effect
bunnyKill
Sound effect
catTouch
Sound effect
catKill
Sound effect
deerTouch
Sound effect
deerKill
Sound effect
dogTouch
Sound effect
dogKill
Sound effect
fennecTouch
Sound effect
fennecKill
Sound effect
mouseTouch
Sound effect
mouseKill
Sound effect
squirrelTouch
Sound effect
squirrelKill
Sound effect
peacefulNightAmbience
Music
twistedBloodMoonTheme
Music
wendigoChaseTheme
Music
zombieChaseTheme
Music
woodCollect
Sound effect
vampireJumpscare
Sound effect
dogStun
Sound effect
catWoodCollect
Sound effect
foxTwistedWoodCollect
Sound effect
batTouch
Sound effect
batKill
Sound effect