User prompt
wolves cant spawn on night 2/ the forced peacefull night but they spawn
User prompt
let the zombies come in as the same side as the wendigo but chasing the npcs(if there not in the cabing)
User prompt
no the zombies HAVE to move
User prompt
the zombies dont move in bloodmoon
User prompt
hey the 2nd night is peacefull night bc the 3rd night is a forced bloodmoon night also the wolves cannot be with 5 when night 2 happens they start at 3 and whenever its blood moon it add another wolf (so 3+1) and repeats that
User prompt
let the deer go to the berries not wander around ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
hey set 75% chance to get a peacefull night to 100%(right before bloodmoon peacefull night apears peacefull night can still apear without the next night being bloodmoon)
User prompt
also the wolf count only increases AFTER bloodmoon
User prompt
hey uhm recap on the abbilities bunny,squirrel and cat all give a 2x multiplier only bunny can still move when targeted and cat can collect wood (so they dont go to the campfire) fennec:spawns -1 of every enemy EXCLUDING the wendigo (they freeze when targeted) deer: collects berrys and drops 20 berrys when killed has 2 lives instead of one (and unfreezes if targeted and then hit by wendigo) mouse:gives a 4x multiplier gets a small speed boost during night (they cant be targeted by the wendigo) dog:stuns 1 of the enemys and has 3 lives when he stuns anyone he gets slowed he stuns vampires for 6 seconds any other enemy for 5 seconds and the wendigo for 3 seconds (while playing the sound dog stun) bat: gives 1 extra life when its blood moon they turn into a vampire but turns back when its day again (cant be targeted/hit by the wendigo OR vampires (both variants)) rarities: cat,bunny and squirrel(50%) fennec&deer(35%) and mouse,dog and bat(15%) they also have the same size as the player/fox ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
can you make all the npcs the same size as the fox
User prompt
also the mouse doesnt give the 4x multiplier
User prompt
hey the npcs still go to the cabin can you please let them go to the campfire
User prompt
also if the night starts let the wolves come in from both sides and let them leave from both sides if it turns day
User prompt
also if its night all the npcs go to the campfire and if a wolf gets close it hits/kills 1 of the npcs
User prompt
if a berry spawns can you make it so theres a 25% it duplicates also make every npc the same size
User prompt
can you PLEASE make the wendigo show up if its blood moon idk whats happening but its so annoying
User prompt
can you make wood spawn in twisted blood moon 25% each 3 seconds
User prompt
the wendigo still doesnt apear the zombies dont have to follow him(wendigo)
User prompt
the wendigo still doesnt spawn and the player cant move during the bloodmoon cutscene let the player move while the bloodmoon cutscene is playing and let the wendigo apear
User prompt
uhm the wendigo and the zombies dont show during blood moon and can you make when its twisted bloodmoon a cutscene where the vampires go from the bottem to the campfire ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
can you make the wolves slower when they go to the campfire and make there detection range bigger ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
the player cant move during bloodmoon can you fix that
User prompt
can you also make the npcs all be at the campfire and staying there if its night UNLESS its twisted blood moon or if its a cat ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
while the npc is being killed the player can still move
User prompt
if an npc shows up make them apear at the top and going to the campfire ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * 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.5, scaleY: 1.5 }); 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 = 3; // Start with 3 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('Twisted Blood Moon'); dayNightTxt.fill = 0x8B0000; game.setBackgroundColor(0x110000); tween(background, { tint: 0x220000, alpha: 0.8 }, { duration: 1000 }); // Move all NPCs to cabin during twisted blood moon for (var i = 0; i < npcs.length; i++) { npcs[i].enterCabin(); } // Spawn vampires during twisted blood moon spawnVampire(); spawnVampire(); spawnVampire2(); spawnVampire2(); self.wood = 0; // Set wood to 0 when twisted blood moon starts 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 NPC = Container.expand(function (type) { var self = Container.call(this); self.type = type; // Set multiplier based on type: only mouse gives 4x, cat/bunny/squirrel give 2x, deer/fennec/dog give no multiplier if (type === 'mouse') { 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 var npcGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); // 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; } } // Cat behavior - collect wood during night but run to cabin during twisted blood moon or when wolves are near if (self.type === 'cat' && (isNight || isPeacefulNight) && !self.inCabin) { // Check if twisted blood moon or wolves are present - run to cabin if (isTwistedBloodMoon || wolves.length > 0) { // Check if any wolf is close (within 400 pixels) var wolfNear = false; 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) { wolfNear = true; break; } } if (isTwistedBloodMoon || wolfNear) { self.enterCabin(); return; } } // 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 < 100) { // 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) { self.x += dx / distance * 2; self.y += dy / distance * 2; } // Collect wood if close enough if (woodDist < 50) { campfire.addWood(1); LK.getSound('catWoodCollect').play(); woodItems.splice(w, 1); wood.destroy(); break; } return; } } } // Fennec fox behavior - collect berries and deplete wolves if (self.type === 'fennec' && (!self.isTargeted || !self.canEnterCabin)) { // Check for nearby berries to collect 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 < 100) { // 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) { self.x += dx / distance * 2; self.y += dy / distance * 2; } // Collect berry if close enough if (berryDist < 50) { normalBerries.splice(b, 1); berry.destroy(); berriesCollectedInCycle++; break; } return; } } // Deplete wolves during night if (isNight && wolves.length > 0) { var targetWolf = wolves[0]; var wolfDist = Math.sqrt(Math.pow(targetWolf.x - self.x, 2) + Math.pow(targetWolf.y - self.y, 2)); if (wolfDist < 100) { // Remove one wolf targetWolf.destroy(); wolves.splice(0, 1); } } } // Deer behavior - run from wendigo, give berries when killed if (self.type === 'deer') { 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; } } } // Mouse behavior - run to cabin but slow down when targeted if (self.type === 'mouse') { if ((isNight || isBloodMoon) && !self.inCabin) { var targetSpeed = self.isTargeted ? 1 : 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 and avoidance if (self.type === 'bat') { // Flying animation - gentle up and down movement self.y += Math.sin(LK.ticks * 0.2 + self.x * 0.02) * 1.5; // Random flying pattern when not targeted if (!self.isTargeted && LK.ticks % 150 === 0) { var newX = Math.random() * (1800 - 200) + 200; var newY = Math.random() * (2200 - 300) + 300; // Stay higher up tween(self, { x: newX, y: newY }, { duration: 2500, easing: tween.easeInOut }); } } // Dog behavior during night/blood moon if (self.type === 'dog' && (isNight || isBloodMoon) && !self.isTargeted) { // Check for nearby wolves or wendigo 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; } } // If threat is close enough (within 300 pixels), attack it if (nearestThreat && minDistance < 300) { self.isTargeted = true; // Move towards threat to stun it var dx = nearestThreat.x - self.x; var dy = nearestThreat.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * 4; // Dog moves fast when attacking self.y += dy / distance * 4; } return; } } // Normal movement when not attacking if (!self.isTargeted) { // Gentle movement animation self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.3; if (LK.ticks % 200 === 0) { var newX = Math.random() * (1800 - 200) + 200; var newY = Math.random() * (2400 - 200) + 200; tween(self, { x: newX, y: newY }, { duration: 3000, easing: tween.easeInOut }); } } }; self.enterCabin = function () { var targetX, targetY; if ((isNight || isPeacefulNight) && !isTwistedBloodMoon) { // Go to campfire during night - don't set inCabin to true so wolves can attack self.inCabin = false; targetX = campfire.x + (Math.random() - 0.5) * 200; targetY = campfire.y + (Math.random() - 0.5) * 200; } else { // Go to cabin during day or twisted blood moon self.inCabin = true; targetX = cabin.x; targetY = cabin.y; } 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) { 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 && attacker.children && attacker.children[0]) { attacker.stunned = true; attacker.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps tween(attacker.children[0], { 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) { if (threat === wendigo) { threat.stunned = true; threat.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps } else { threat.stunned = true; threat.stunnedUntil = LK.ticks + 300; // 5 seconds at 60fps } // 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 retreats after stunning self.isTargeted = true; var retreatX = Math.random() * (1800 - 200) + 200; var retreatY = Math.random() * (2400 - 200) + 200; tween(self, { x: retreatX, y: retreatY }, { duration: 1000, 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; // Fast vampire self.update = function () { if (!isTwistedBloodMoon) return; // During twisted blood moon cutscene, vampires go to campfire and scare NPCs if (isBloodMoonCutscene) { // Move vampire to campfire var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 200) { self.x += campfireDx / campfireDistance * self.speed; self.y += campfireDy / campfireDistance * self.speed; } else { // Vampire at campfire, 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(); } } } // End cutscene after scaring NPCs LK.setTimeout(function () { isBloodMoonCutscene = false; bloodMoonCutsceneTimer = 0; }, 1000); } 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 bats) 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') { 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, 300); // Play vampire jumpscare sound LK.getSound('vampireJumpscare').play(); // Stun vampire for 3 seconds self.stunned = true; self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps tween(vampireGraphics, { tint: 0x4444FF }, { duration: 500 }); playerLives--; updateLivesDisplay(); LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; } }, 800); } 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; // Fast vampire self.update = function () { if (!isTwistedBloodMoon) return; // During twisted blood moon cutscene, vampires go to campfire and scare NPCs if (isBloodMoonCutscene) { // Move vampire2 to campfire var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 250) { self.x += campfireDx / campfireDistance * self.speed; self.y += campfireDy / campfireDistance * self.speed; } else { // Vampire2 at campfire, 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 bats) 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') { 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, 300); // Play vampire jumpscare sound LK.getSound('vampireJumpscare').play(); // Stun vampire2 for 3 seconds self.stunned = true; self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps tween(vampire2Graphics, { tint: 0x4444FF }, { duration: 500 }); playerLives--; updateLivesDisplay(); LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; } }, 800); } 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.speed = 3; self.lastFoxX = 0; self.lastFoxY = 0; self.update = function () { if (!isBloodMoon) return; if (isTwistedBloodMoon) { // During twisted blood 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; } // During blood moon cutscene, check for no NPCs scenario if (isBloodMoonCutscene) { var availableNPCs = 0; for (var n = 0; n < npcs.length; n++) { if (!npcs[n].inCabin && !npcs[n].killed && !npcs[n].frozen) { availableNPCs++; } } // If no NPCs available and wendigo not at campfire yet if (availableNPCs === 0 && !cutsceneWendigoAtCampfire) { // Move wendigo to campfire var campfireDx = campfire.x - self.x; var campfireDy = campfire.y - self.y; var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy); if (campfireDistance > 100) { self.x += campfireDx / campfireDistance * currentSpeed; self.y += campfireDy / campfireDistance * currentSpeed; } else { // Wendigo reached campfire cutsceneWendigoAtCampfire = true; cutsceneZombiesWaiting = true; cutsceneZombiesWaitStartTime = LK.ticks; } return; } // If zombies are waiting at campfire for 5 seconds, spawn NPC from top if (cutsceneZombiesWaiting && LK.ticks - cutsceneZombiesWaitStartTime >= 300 && !cutsceneNPCSpawned) { // Spawn NPC from top of screen var newNPC = new NPC('bunny'); // Spawn a bunny for the cutscene newNPC.x = Math.random() * (1800 - 200) + 200; // Random X position within safe bounds newNPC.y = -100; // Start from top side npcs.push(newNPC); game.addChild(newNPC); cutsceneNPCSpawned = true; // Move NPC to campfire tween(newNPC, { x: campfire.x + (Math.random() - 0.5) * 200, y: campfire.y + (Math.random() - 0.5) * 200 }, { duration: 2000, easing: tween.easeInOut }); } } 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 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) { var weight = 0; if (npc.type === 'cat' || npc.type === 'bunny' || npc.type === 'squirrel') { weight = 25; // Common NPCs 25% } else if (npc.type === 'mouse') { weight = 10; // Mouse 10% } else if (npc.type === 'fennec') { weight = 20; // Fennec 20% } else if (npc.type === 'dog') { weight = 50; // Dog 50% } else if (npc.type === 'deer') { weight = 30; // Deer 30% } else if (npc.type === 'bat') { weight = 50; // Bat 50% (like dog) } // 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(0xffffff, 500); // Scale wendigo up for jumpscare tween(wendigo, { scaleX: 3, scaleY: 3 }, { duration: 200 }); // Play jumpscare sound LK.getSound('wendigoGrowl').play(); // 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(); } }, 1000); 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 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 = 600; // Increased range so player can distract wolves from campfire // 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) { 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) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } else { // Check if we should stop chase music if (isChaseThemePlaying) { var anyWolfChasing = false; for (var wc = 0; wc < wolves.length; wc++) { if (wolves[wc] !== self) { var foxDist = Math.sqrt(Math.pow(fox.x - wolves[wc].x, 2) + Math.pow(fox.y - wolves[wc].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, 300); // Scale wolf up for jumpscare tween(self, { scaleX: 2.5, scaleY: 2.5 }, { duration: 150 }); // Play wolf roar sound LK.getSound('wolfRoar').play(); // Stun wolf for 3 seconds self.stunned = true; self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps tween(wolfGraphics, { tint: 0x4444FF }, { duration: 500 }); // 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; // Remove this wolf self.destroy(); wolves.splice(wolves.indexOf(self), 1); } }, 800); 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 && !isBloodMoon && !isTwistedBloodMoon) return; // During blood moon cutscene, follow wendigo or wait at campfire if (isBloodMoonCutscene && wendigo) { // 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; } var dx = wendigo.x - self.x; var dy = wendigo.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 100) { // Stay close but not too close to wendigo self.x += dx / distance * (self.speed * 0.8); self.y += dy / distance * (self.speed * 0.8); } // If wendigo killed an NPC, stay still for 5 seconds if (cutsceneNPCKilled && LK.ticks - cutsceneNPCKillTime < 300) { return; // Stay still } 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(zombieGraphics, { tint: 0xFFFFFF }, { duration: 300 }); } // Find closest target (fox or NPCs) var closestTarget = null; var closestDistance = Infinity; var detectionRange = 400; // Zombies only chase when within 400 pixels // 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 that are not in cabin (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); if (npcDistance < detectionRange && npcDistance < closestDistance) { closestTarget = npcs[n]; closestDistance = npcDistance; } } } // 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; // 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 collision with fox if (self.intersects(fox)) { // Zombie caught fox LK.effects.flashScreen(0x00FF00, 300); // Stun zombie for 3 seconds self.stunned = true; self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps tween(zombieGraphics, { tint: 0x4444FF }, { duration: 500 }); playerLives--; updateLivesDisplay(); LK.setTimeout(function () { if (playerLives <= 0) { LK.showGameOver(); } else { // Respawn fox at safe location fox.x = 1024; fox.y = 1366; // Remove this zombie self.destroy(); zombies.splice(zombies.indexOf(self), 1); } }, 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 twisted blood 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 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); // 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() { var berriesSpawned = 1; var maxBerries = 4; // Spawn first berry var berry = new NormalBerry(); berry.x = Math.random() * (2048 - 120) + 60; berry.y = Math.random() * (2732 - 120) + 60; normalBerries.push(berry); game.addChild(berry); // Check for additional berries (25% chance each, max 4 total) // Works during day, blood moon, and twisted blood moon for increased spawn rate var shouldAllowDuplication = !isNight || isBloodMoon || isTwistedBloodMoon; if (shouldAllowDuplication) { // Check for additional berries (25% chance each, max 4 total) while (berriesSpawned < maxBerries && Math.random() < 0.25) { var extraBerry = new NormalBerry(); extraBerry.x = Math.random() * (2048 - 120) + 60; extraBerry.y = Math.random() * (2732 - 120) + 60; normalBerries.push(extraBerry); game.addChild(extraBerry); berriesSpawned++; } } } // 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(); 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(); } } // 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, 75% chance after blood moon var peacefulChance = 0.25; if (nightCount > 3 && shouldBeBloodMoon) { // If next night will be blood moon, increase peaceful chance to 75% peacefulChance = 0.75; } isPeacefulNight = Math.random() < peacefulChance; // 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); // Spawn zombies (50% chance for 3 zombies during blood moon) if (Math.random() < 0.5) { 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; } else { // Regular night if (!isPeacefulNight) { // Spawn wolves - 3 by default, reduced by 1 if fennec fox present var baseWolves = 3 + bloodMoonCount; // Add 1 wolf for each survived 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; for (var w = 0; w < wolvesToSpawn; w++) { spawnWolf(); } } // Move NPCs to cabin (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; } else { dayNightTxt.setText('Night'); dayNightTxt.fill = 0x6666FF; } // Show wood counter during night campfireTxt.visible = true; updateCampfireDisplay(); // Spawn wood during night (3-5 pieces, but only 10% chance during twisted blood 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; berriesCollectedInCycle = 0; // Stop any currently playing music and switch to day music LK.stopMusic(); LK.playMusic('dayMusic'); // Remove wolves for (var i = 0; i < wolves.length; i++) { wolves[i].destroy(); } wolves = []; // Remove wendigo if exists if (wendigo) { wendigo.destroy(); wendigo = null; } // Hide wood counter during day campfireTxt.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 twisted blood 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 if (Math.random() < 0.25) { 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); } // 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 cutscenes if (isIntroCutscene || isBloodMoonCutscene) 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 cutscenes if (isIntroCutscene || isBloodMoonCutscene) 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(); // Main game update loop game.update = function () { // 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() { cutsceneNPCKilled.destroy(); npcs.splice(npcs.indexOf(cutsceneNPCKilled), 1); cutsceneNPCKilled = null; } }); // End cutscene isBloodMoonCutscene = false; bloodMoonCutsceneTimer = 0; } } // Spawn berries over time with random intervals - slower at night berrySpawnTimer++; var baseSpawnTime = isNight ? 180 : 90; // Night: 3-8 seconds, Day: 1.5-5 seconds var maxSpawnTime = isNight ? 480 : 300; 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) { // 10% chance during twisted blood moon if (Math.random() < 0.1) { 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 twisted blood moon if (isTwistedBloodMoon) { twistedBloodMoonWoodCollected++; // End twisted blood moon when 3 wood collected if (twistedBloodMoonWoodCollected >= 3) { isTwistedBloodMoon = false; twistedBloodMoonWoodCollected = 0; // Reset counter startDay(); // End twisted blood moon and start day } } // Remove wood from array first, then destroy woodItems.splice(i, 1); wood.destroy(); } } // Check collisions with NPCs for score multiplier 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(); // Bat doesn't give extra life on touch since it gives on spawn if (npc.type === 'mouse') { // Mouse gives 4x multiplier scoreMultiplier = 4; updateScoreDisplay(); } else if (npc.type === 'cat' || npc.type === 'bunny' || npc.type === 'squirrel') { // Common NPCs (cat, bunny, squirrel) give 2x multiplier scoreMultiplier = 2; updateScoreDisplay(); } // deer, fennec, dog, bat don't give any multiplier // 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.5,
scaleY: 1.5
});
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 = 3; // Start with 3 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('Twisted Blood Moon');
dayNightTxt.fill = 0x8B0000;
game.setBackgroundColor(0x110000);
tween(background, {
tint: 0x220000,
alpha: 0.8
}, {
duration: 1000
});
// Move all NPCs to cabin during twisted blood moon
for (var i = 0; i < npcs.length; i++) {
npcs[i].enterCabin();
}
// Spawn vampires during twisted blood moon
spawnVampire();
spawnVampire();
spawnVampire2();
spawnVampire2();
self.wood = 0; // Set wood to 0 when twisted blood moon starts
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 NPC = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
// Set multiplier based on type: only mouse gives 4x, cat/bunny/squirrel give 2x, deer/fennec/dog give no multiplier
if (type === 'mouse') {
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
var npcGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
// 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;
}
}
// Cat behavior - collect wood during night but run to cabin during twisted blood moon or when wolves are near
if (self.type === 'cat' && (isNight || isPeacefulNight) && !self.inCabin) {
// Check if twisted blood moon or wolves are present - run to cabin
if (isTwistedBloodMoon || wolves.length > 0) {
// Check if any wolf is close (within 400 pixels)
var wolfNear = false;
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) {
wolfNear = true;
break;
}
}
if (isTwistedBloodMoon || wolfNear) {
self.enterCabin();
return;
}
}
// 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 < 100) {
// 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) {
self.x += dx / distance * 2;
self.y += dy / distance * 2;
}
// Collect wood if close enough
if (woodDist < 50) {
campfire.addWood(1);
LK.getSound('catWoodCollect').play();
woodItems.splice(w, 1);
wood.destroy();
break;
}
return;
}
}
}
// Fennec fox behavior - collect berries and deplete wolves
if (self.type === 'fennec' && (!self.isTargeted || !self.canEnterCabin)) {
// Check for nearby berries to collect
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 < 100) {
// 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) {
self.x += dx / distance * 2;
self.y += dy / distance * 2;
}
// Collect berry if close enough
if (berryDist < 50) {
normalBerries.splice(b, 1);
berry.destroy();
berriesCollectedInCycle++;
break;
}
return;
}
}
// Deplete wolves during night
if (isNight && wolves.length > 0) {
var targetWolf = wolves[0];
var wolfDist = Math.sqrt(Math.pow(targetWolf.x - self.x, 2) + Math.pow(targetWolf.y - self.y, 2));
if (wolfDist < 100) {
// Remove one wolf
targetWolf.destroy();
wolves.splice(0, 1);
}
}
}
// Deer behavior - run from wendigo, give berries when killed
if (self.type === 'deer') {
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;
}
}
}
// Mouse behavior - run to cabin but slow down when targeted
if (self.type === 'mouse') {
if ((isNight || isBloodMoon) && !self.inCabin) {
var targetSpeed = self.isTargeted ? 1 : 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 and avoidance
if (self.type === 'bat') {
// Flying animation - gentle up and down movement
self.y += Math.sin(LK.ticks * 0.2 + self.x * 0.02) * 1.5;
// Random flying pattern when not targeted
if (!self.isTargeted && LK.ticks % 150 === 0) {
var newX = Math.random() * (1800 - 200) + 200;
var newY = Math.random() * (2200 - 300) + 300; // Stay higher up
tween(self, {
x: newX,
y: newY
}, {
duration: 2500,
easing: tween.easeInOut
});
}
}
// Dog behavior during night/blood moon
if (self.type === 'dog' && (isNight || isBloodMoon) && !self.isTargeted) {
// Check for nearby wolves or wendigo
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;
}
}
// If threat is close enough (within 300 pixels), attack it
if (nearestThreat && minDistance < 300) {
self.isTargeted = true;
// Move towards threat to stun it
var dx = nearestThreat.x - self.x;
var dy = nearestThreat.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * 4; // Dog moves fast when attacking
self.y += dy / distance * 4;
}
return;
}
}
// Normal movement when not attacking
if (!self.isTargeted) {
// Gentle movement animation
self.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.3;
if (LK.ticks % 200 === 0) {
var newX = Math.random() * (1800 - 200) + 200;
var newY = Math.random() * (2400 - 200) + 200;
tween(self, {
x: newX,
y: newY
}, {
duration: 3000,
easing: tween.easeInOut
});
}
}
};
self.enterCabin = function () {
var targetX, targetY;
if ((isNight || isPeacefulNight) && !isTwistedBloodMoon) {
// Go to campfire during night - don't set inCabin to true so wolves can attack
self.inCabin = false;
targetX = campfire.x + (Math.random() - 0.5) * 200;
targetY = campfire.y + (Math.random() - 0.5) * 200;
} else {
// Go to cabin during day or twisted blood moon
self.inCabin = true;
targetX = cabin.x;
targetY = cabin.y;
}
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) {
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 && attacker.children && attacker.children[0]) {
attacker.stunned = true;
attacker.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
tween(attacker.children[0], {
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) {
if (threat === wendigo) {
threat.stunned = true;
threat.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
} else {
threat.stunned = true;
threat.stunnedUntil = LK.ticks + 300; // 5 seconds at 60fps
}
// 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 retreats after stunning
self.isTargeted = true;
var retreatX = Math.random() * (1800 - 200) + 200;
var retreatY = Math.random() * (2400 - 200) + 200;
tween(self, {
x: retreatX,
y: retreatY
}, {
duration: 1000,
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; // Fast vampire
self.update = function () {
if (!isTwistedBloodMoon) return;
// During twisted blood moon cutscene, vampires go to campfire and scare NPCs
if (isBloodMoonCutscene) {
// Move vampire to campfire
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 200) {
self.x += campfireDx / campfireDistance * self.speed;
self.y += campfireDy / campfireDistance * self.speed;
} else {
// Vampire at campfire, 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();
}
}
}
// End cutscene after scaring NPCs
LK.setTimeout(function () {
isBloodMoonCutscene = false;
bloodMoonCutsceneTimer = 0;
}, 1000);
}
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 bats)
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') {
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, 300);
// Play vampire jumpscare sound
LK.getSound('vampireJumpscare').play();
// Stun vampire for 3 seconds
self.stunned = true;
self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
tween(vampireGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
playerLives--;
updateLivesDisplay();
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
}
}, 800);
} 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; // Fast vampire
self.update = function () {
if (!isTwistedBloodMoon) return;
// During twisted blood moon cutscene, vampires go to campfire and scare NPCs
if (isBloodMoonCutscene) {
// Move vampire2 to campfire
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 250) {
self.x += campfireDx / campfireDistance * self.speed;
self.y += campfireDy / campfireDistance * self.speed;
} else {
// Vampire2 at campfire, 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 bats)
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') {
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, 300);
// Play vampire jumpscare sound
LK.getSound('vampireJumpscare').play();
// Stun vampire2 for 3 seconds
self.stunned = true;
self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
tween(vampire2Graphics, {
tint: 0x4444FF
}, {
duration: 500
});
playerLives--;
updateLivesDisplay();
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
}
}, 800);
} 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.speed = 3;
self.lastFoxX = 0;
self.lastFoxY = 0;
self.update = function () {
if (!isBloodMoon) return;
if (isTwistedBloodMoon) {
// During twisted blood 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;
}
// During blood moon cutscene, check for no NPCs scenario
if (isBloodMoonCutscene) {
var availableNPCs = 0;
for (var n = 0; n < npcs.length; n++) {
if (!npcs[n].inCabin && !npcs[n].killed && !npcs[n].frozen) {
availableNPCs++;
}
}
// If no NPCs available and wendigo not at campfire yet
if (availableNPCs === 0 && !cutsceneWendigoAtCampfire) {
// Move wendigo to campfire
var campfireDx = campfire.x - self.x;
var campfireDy = campfire.y - self.y;
var campfireDistance = Math.sqrt(campfireDx * campfireDx + campfireDy * campfireDy);
if (campfireDistance > 100) {
self.x += campfireDx / campfireDistance * currentSpeed;
self.y += campfireDy / campfireDistance * currentSpeed;
} else {
// Wendigo reached campfire
cutsceneWendigoAtCampfire = true;
cutsceneZombiesWaiting = true;
cutsceneZombiesWaitStartTime = LK.ticks;
}
return;
}
// If zombies are waiting at campfire for 5 seconds, spawn NPC from top
if (cutsceneZombiesWaiting && LK.ticks - cutsceneZombiesWaitStartTime >= 300 && !cutsceneNPCSpawned) {
// Spawn NPC from top of screen
var newNPC = new NPC('bunny'); // Spawn a bunny for the cutscene
newNPC.x = Math.random() * (1800 - 200) + 200; // Random X position within safe bounds
newNPC.y = -100; // Start from top side
npcs.push(newNPC);
game.addChild(newNPC);
cutsceneNPCSpawned = true;
// Move NPC to campfire
tween(newNPC, {
x: campfire.x + (Math.random() - 0.5) * 200,
y: campfire.y + (Math.random() - 0.5) * 200
}, {
duration: 2000,
easing: tween.easeInOut
});
}
}
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
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) {
var weight = 0;
if (npc.type === 'cat' || npc.type === 'bunny' || npc.type === 'squirrel') {
weight = 25; // Common NPCs 25%
} else if (npc.type === 'mouse') {
weight = 10; // Mouse 10%
} else if (npc.type === 'fennec') {
weight = 20; // Fennec 20%
} else if (npc.type === 'dog') {
weight = 50; // Dog 50%
} else if (npc.type === 'deer') {
weight = 30; // Deer 30%
} else if (npc.type === 'bat') {
weight = 50; // Bat 50% (like dog)
}
// 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(0xffffff, 500);
// Scale wendigo up for jumpscare
tween(wendigo, {
scaleX: 3,
scaleY: 3
}, {
duration: 200
});
// Play jumpscare sound
LK.getSound('wendigoGrowl').play();
// 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();
}
}, 1000);
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
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 = 600; // Increased range so player can distract wolves from campfire
// 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) {
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) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else {
// Check if we should stop chase music
if (isChaseThemePlaying) {
var anyWolfChasing = false;
for (var wc = 0; wc < wolves.length; wc++) {
if (wolves[wc] !== self) {
var foxDist = Math.sqrt(Math.pow(fox.x - wolves[wc].x, 2) + Math.pow(fox.y - wolves[wc].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, 300);
// Scale wolf up for jumpscare
tween(self, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 150
});
// Play wolf roar sound
LK.getSound('wolfRoar').play();
// Stun wolf for 3 seconds
self.stunned = true;
self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
tween(wolfGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
// 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;
// Remove this wolf
self.destroy();
wolves.splice(wolves.indexOf(self), 1);
}
}, 800);
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 && !isBloodMoon && !isTwistedBloodMoon) return;
// During blood moon cutscene, follow wendigo or wait at campfire
if (isBloodMoonCutscene && wendigo) {
// 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;
}
var dx = wendigo.x - self.x;
var dy = wendigo.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 100) {
// Stay close but not too close to wendigo
self.x += dx / distance * (self.speed * 0.8);
self.y += dy / distance * (self.speed * 0.8);
}
// If wendigo killed an NPC, stay still for 5 seconds
if (cutsceneNPCKilled && LK.ticks - cutsceneNPCKillTime < 300) {
return; // Stay still
}
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(zombieGraphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
// Find closest target (fox or NPCs)
var closestTarget = null;
var closestDistance = Infinity;
var detectionRange = 400; // Zombies only chase when within 400 pixels
// 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 that are not in cabin (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);
if (npcDistance < detectionRange && npcDistance < closestDistance) {
closestTarget = npcs[n];
closestDistance = npcDistance;
}
}
}
// 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;
// 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 collision with fox
if (self.intersects(fox)) {
// Zombie caught fox
LK.effects.flashScreen(0x00FF00, 300);
// Stun zombie for 3 seconds
self.stunned = true;
self.stunnedUntil = LK.ticks + 180; // 3 seconds at 60fps
tween(zombieGraphics, {
tint: 0x4444FF
}, {
duration: 500
});
playerLives--;
updateLivesDisplay();
LK.setTimeout(function () {
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn fox at safe location
fox.x = 1024;
fox.y = 1366;
// Remove this zombie
self.destroy();
zombies.splice(zombies.indexOf(self), 1);
}
}, 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 twisted blood 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 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);
// 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() {
var berriesSpawned = 1;
var maxBerries = 4;
// Spawn first berry
var berry = new NormalBerry();
berry.x = Math.random() * (2048 - 120) + 60;
berry.y = Math.random() * (2732 - 120) + 60;
normalBerries.push(berry);
game.addChild(berry);
// Check for additional berries (25% chance each, max 4 total)
// Works during day, blood moon, and twisted blood moon for increased spawn rate
var shouldAllowDuplication = !isNight || isBloodMoon || isTwistedBloodMoon;
if (shouldAllowDuplication) {
// Check for additional berries (25% chance each, max 4 total)
while (berriesSpawned < maxBerries && Math.random() < 0.25) {
var extraBerry = new NormalBerry();
extraBerry.x = Math.random() * (2048 - 120) + 60;
extraBerry.y = Math.random() * (2732 - 120) + 60;
normalBerries.push(extraBerry);
game.addChild(extraBerry);
berriesSpawned++;
}
}
}
// 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();
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();
}
}
// 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, 75% chance after blood moon
var peacefulChance = 0.25;
if (nightCount > 3 && shouldBeBloodMoon) {
// If next night will be blood moon, increase peaceful chance to 75%
peacefulChance = 0.75;
}
isPeacefulNight = Math.random() < peacefulChance;
// 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);
// Spawn zombies (50% chance for 3 zombies during blood moon)
if (Math.random() < 0.5) {
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;
} else {
// Regular night
if (!isPeacefulNight) {
// Spawn wolves - 3 by default, reduced by 1 if fennec fox present
var baseWolves = 3 + bloodMoonCount; // Add 1 wolf for each survived 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;
for (var w = 0; w < wolvesToSpawn; w++) {
spawnWolf();
}
}
// Move NPCs to cabin (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;
} else {
dayNightTxt.setText('Night');
dayNightTxt.fill = 0x6666FF;
}
// Show wood counter during night
campfireTxt.visible = true;
updateCampfireDisplay();
// Spawn wood during night (3-5 pieces, but only 10% chance during twisted blood 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;
berriesCollectedInCycle = 0;
// Stop any currently playing music and switch to day music
LK.stopMusic();
LK.playMusic('dayMusic');
// Remove wolves
for (var i = 0; i < wolves.length; i++) {
wolves[i].destroy();
}
wolves = [];
// Remove wendigo if exists
if (wendigo) {
wendigo.destroy();
wendigo = null;
}
// Hide wood counter during day
campfireTxt.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 twisted blood 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
if (Math.random() < 0.25) {
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);
}
// 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 cutscenes
if (isIntroCutscene || isBloodMoonCutscene) 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 cutscenes
if (isIntroCutscene || isBloodMoonCutscene) 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();
// Main game update loop
game.update = function () {
// 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() {
cutsceneNPCKilled.destroy();
npcs.splice(npcs.indexOf(cutsceneNPCKilled), 1);
cutsceneNPCKilled = null;
}
});
// End cutscene
isBloodMoonCutscene = false;
bloodMoonCutsceneTimer = 0;
}
}
// Spawn berries over time with random intervals - slower at night
berrySpawnTimer++;
var baseSpawnTime = isNight ? 180 : 90; // Night: 3-8 seconds, Day: 1.5-5 seconds
var maxSpawnTime = isNight ? 480 : 300;
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) {
// 10% chance during twisted blood moon
if (Math.random() < 0.1) {
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 twisted blood moon
if (isTwistedBloodMoon) {
twistedBloodMoonWoodCollected++;
// End twisted blood moon when 3 wood collected
if (twistedBloodMoonWoodCollected >= 3) {
isTwistedBloodMoon = false;
twistedBloodMoonWoodCollected = 0; // Reset counter
startDay(); // End twisted blood moon and start day
}
}
// Remove wood from array first, then destroy
woodItems.splice(i, 1);
wood.destroy();
}
}
// Check collisions with NPCs for score multiplier
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();
// Bat doesn't give extra life on touch since it gives on spawn
if (npc.type === 'mouse') {
// Mouse gives 4x multiplier
scoreMultiplier = 4;
updateScoreDisplay();
} else if (npc.type === 'cat' || npc.type === 'bunny' || npc.type === 'squirrel') {
// Common NPCs (cat, bunny, squirrel) give 2x multiplier
scoreMultiplier = 2;
updateScoreDisplay();
}
// deer, fennec, dog, bat don't give any multiplier
// 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