/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BasementGhost = Container.expand(function () { var self = Container.call(this); var ghostGraphics = self.attachAsset('ghost', { anchorX: 0.5, anchorY: 0.5 }); self.speed = currentGhostSpeed; self.alpha = 0.8; ghostGraphics.alpha = 0.8; self.lastAttackTime = 0; self.update = function () { // Only update if in basement scene if (currentScene !== 'basement') { return; } // Move towards player var dx = player.x - self.x; var dy = player.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; } // Attack player if close enough if (distance < 70) { var currentTime = LK.ticks; if (currentTime - self.lastAttackTime > 60) { // Attack once per second playerHealth -= 10; self.lastAttackTime = currentTime; updateHealthBar(); // Flash player red when damaged LK.effects.flashObject(player, 0xff0000, 500); // Create blood effect at player position createBloodEffect(player.x, player.y); // Check if player is dead if (playerHealth <= 0) { LK.showGameOver(); return; } } } }; return self; }); var BloodEffect = Container.expand(function () { var self = Container.call(this); var bloodGraphics = self.attachAsset('blood', { anchorX: 0.5, anchorY: 0.5 }); self.lifeTime = 300; // 5 seconds at 60fps self.currentLife = 0; self.update = function () { self.currentLife++; // Fade out over time self.alpha = 1 - self.currentLife / self.lifeTime; // Remove after lifetime if (self.currentLife >= self.lifeTime) { self.destroy(); for (var i = bloodEffects.length - 1; i >= 0; i--) { if (bloodEffects[i] === self) { bloodEffects.splice(i, 1); break; } } } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.direction = { x: 0, y: 0 }; self.update = function () { // Only update if in basement scene if (currentScene !== 'basement') { return; } // Move bullet in direction self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Remove bullet if it goes off screen if (self.x < 0 || self.x > 2048 || self.y < 1800 || self.y > 2480) { self.destroy(); for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i] === self) { bullets.splice(i, 1); break; } } } // Check collision with basement ghosts for (var i = basementGhosts.length - 1; i >= 0; i--) { var ghost = basementGhosts[i]; if (self.intersects(ghost)) { // Create blood effect at ghost position createBloodEffect(ghost.x, ghost.y); // Increment kill count ghostKillCount++; // Increase speed every 20 kills if (ghostKillCount % 20 === 0) { currentGhostSpeed += 1; } // Remove ghost ghost.destroy(); basementGhosts.splice(i, 1); // Remove bullet self.destroy(); for (var j = bullets.length - 1; j >= 0; j--) { if (bullets[j] === self) { bullets.splice(j, 1); break; } } break; } } // Check collision with regular house ghosts for (var i = ghosts.length - 1; i >= 0; i--) { var ghost = ghosts[i]; if (self.intersects(ghost)) { // Create blood effect at ghost position createBloodEffect(ghost.x, ghost.y); // Increment kill count ghostKillCount++; // Increase speed every 20 kills if (ghostKillCount % 20 === 0) { currentGhostSpeed += 1; } // Remove ghost ghost.destroy(); ghosts.splice(i, 1); currentGhost = null; ghostSpawnTimer = 0; // Remove bullet self.destroy(); for (var j = bullets.length - 1; j >= 0; j--) { if (bullets[j] === self) { bullets.splice(j, 1); break; } } break; } } // Check collision with final boss if (finalBoss && finalBossActive && self.intersects(finalBoss)) { // Create blood effect at boss position createBloodEffect(finalBoss.x, finalBoss.y); // Increment player shot count playerShotCount++; // Damage boss finalBoss.health--; // Check if boss is defeated if (playerShotCount >= 25) { // Boss defeated - drop exit key if (!exitKey) { exitKey = game.addChild(LK.getAsset('key', { anchorX: 0.5, anchorY: 0.5 })); exitKey.x = finalBoss.x; exitKey.y = finalBoss.y; exitKey.visible = true; } finalBoss.destroy(); finalBoss = null; finalBossActive = false; // Flash green to indicate boss defeated LK.effects.flashScreen(0x00ff00, 1000); } // Remove bullet self.destroy(); for (var j = bullets.length - 1; j >= 0; j--) { if (bullets[j] === self) { bullets.splice(j, 1); break; } } } }; return self; }); var FinalBoss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; // Very fast boss self.health = 40; self.lastAttackTime = 0; self.update = function () { // Only update if boss is active if (!finalBossActive || currentScene !== 'basement') { return; } // Move towards player var dx = player.x - self.x; var dy = player.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; } // Attack player if close enough - deal 50 damage if (distance < 80) { var currentTime = LK.ticks; if (currentTime - self.lastAttackTime > 60) { // Attack once per second - deal 50 damage playerHealth -= 50; self.lastAttackTime = currentTime; updateHealthBar(); // Flash player red when damaged LK.effects.flashObject(player, 0xff0000, 500); // Create blood effect at player position createBloodEffect(player.x, player.y); // Check if player is dead if (playerHealth <= 0) { LK.showGameOver(); return; } } } }; return self; }); var Ghost = Container.expand(function () { var self = Container.call(this); var ghostGraphics = self.attachAsset('ghost', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.alpha = 0.7; ghostGraphics.alpha = 0.7; self.searchTimer = 0; self.maxSearchTime = 300; // 5 seconds at 60fps self.isLeaving = false; self.update = function () { // Don't update ghost if player is in room scene if (currentScene === 'room') { return; } // Only increment search timer when player is hidden if (playerHidden) { self.searchTimer++; } // Move ghost towards player if not leaving if (!self.isLeaving) { var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (!playerHidden && distance > 0) { // Chase player if not hidden self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else if (playerHidden && distance > 0) { // Move randomly when player is hidden self.x += (Math.random() - 0.5) * 4; self.y += (Math.random() - 0.5) * 4; } // Attack player if close enough and player is not hidden if (distance < 80 && !playerHidden) { // Damage player if (self.lastAttackTime === undefined) self.lastAttackTime = 0; var currentTime = LK.ticks; if (currentTime - self.lastAttackTime > 60) { // Attack once per second playerHealth -= 10; self.lastAttackTime = currentTime; updateHealthBar(); // Flash player red when damaged LK.effects.flashObject(player, 0xff0000, 500); // Create blood effect at player position createBloodEffect(player.x, player.y); // Check if player is dead if (playerHealth <= 0) { LK.showGameOver(); return; } // After dealing damage, ghost starts leaving self.isLeaving = true; } } } // If ghost hasn't found player after 5 seconds of searching (while player is hidden), start leaving if (self.searchTimer >= self.maxSearchTime && playerHidden && !self.isLeaving) { self.isLeaving = true; self.speed = 3; // Move faster when leaving } // If leaving, move towards entrance door if (self.isLeaving) { var doorX = house.entranceDoor.x + 40; var doorY = house.entranceDoor.y + 60; var dx = doorX - self.x; var dy = doorY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Remove ghost when it reaches the door if (distance < 30) { self.destroy(); currentGhost = null; ghostSpawnTimer = 0; for (var i = ghosts.length - 1; i >= 0; i--) { if (ghosts[i] === self) { ghosts.splice(i, 1); break; } } } } // Remove ghost if it goes off screen if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { self.destroy(); currentGhost = null; ghostSpawnTimer = 0; for (var i = ghosts.length - 1; i >= 0; i--) { if (ghosts[i] === self) { ghosts.splice(i, 1); break; } } } }; return self; }); var House = Container.expand(function () { var self = Container.call(this); // Basement floor var basementWall = self.attachAsset('houseWall', { x: 0, y: 1800 }); var basementFloor = self.attachAsset('houseFloor', { x: 0, y: 2400 }); // Ground floor var groundFloorWall = self.attachAsset('houseWall', { x: 0, y: 1200 }); var groundFloor = self.attachAsset('houseFloor', { x: 0, y: 1800 }); // Second floor var secondFloorWall = self.attachAsset('houseWall', { x: 0, y: 600 }); var secondFloor = self.attachAsset('houseFloor', { x: 0, y: 1200 }); // Stairs connecting floors - make them walkable var stairway = self.attachAsset('stairs', { x: 974, y: 1200, width: 100, height: 600 }); // Second floor room dividers var roomWall1 = self.attachAsset('houseWall', { x: 1024, y: 600, width: 20, height: 600 }); var roomWall2 = self.attachAsset('houseWall', { x: 0, y: 900, width: 1024, height: 20 }); var roomWall3 = self.attachAsset('houseWall', { x: 1044, y: 900, width: 1004, height: 20 }); // Floor barriers to prevent jumping between floors (except stairs) var floorBarrierLeft = self.attachAsset('houseWall', { x: 0, y: 1200, width: 974, height: 20 }); var floorBarrierRight = self.attachAsset('houseWall', { x: 1074, y: 1200, width: 974, height: 20 }); // Store barrier references for collision detection self.roomWall1 = roomWall1; self.roomWall2 = roomWall2; self.roomWall3 = roomWall3; self.floorBarrierLeft = floorBarrierLeft; self.floorBarrierRight = floorBarrierRight; // Windows var window1 = self.attachAsset('window', { x: 300, y: 700 }); var window2 = self.attachAsset('window', { x: 800, y: 700 }); var window3 = self.attachAsset('window', { x: 1200, y: 700 }); var window4 = self.attachAsset('window', { x: 1600, y: 700 }); var window5 = self.attachAsset('window', { x: 300, y: 1300 }); var window6 = self.attachAsset('window', { x: 800, y: 1300 }); var window7 = self.attachAsset('window', { x: 1200, y: 1300 }); var window8 = self.attachAsset('window', { x: 1600, y: 1300 }); // Exit door (locked) var exitDoor = self.attachAsset('door', { x: 1900, y: 1620 }); var exitLock = self.attachAsset('lock', { x: 1970, y: 1660 }); // Basement door (locked) var basementDoor = self.attachAsset('door', { x: 100, y: 1620 }); var basementLock = self.attachAsset('lock', { x: 170, y: 1660 }); // Closet on second floor var closet = self.attachAsset('closet', { x: 1800, y: 680 }); // Upper floor door on the ground var upperDoor = self.attachAsset('door', { x: 500, y: 1120 }); // Entrance door var entranceDoor = self.attachAsset('door', { x: 50, y: 1620 }); // Store door and closet references for game logic self.exitDoor = exitDoor; self.basementDoor = basementDoor; self.entranceDoor = entranceDoor; self.upperDoor = upperDoor; self.closet = closet; self.exitLocked = true; self.basementLocked = true; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); var monsterGraphics = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.lastAttackTime = 0; self.update = function () { // Only update if in room scene if (currentScene !== 'room') { return; } // Move towards player var dx = player.x - self.x; var dy = player.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; } // Attack player if close enough if (distance < 60) { var currentTime = LK.ticks; if (currentTime - self.lastAttackTime > 120) { // Attack every 2 seconds playerHealth -= 50; self.lastAttackTime = currentTime; updateHealthBar(); // Flash player red when damaged LK.effects.flashObject(player, 0xff0000, 500); // Create blood effect at player position createBloodEffect(player.x, player.y); // Check if player is dead if (playerHealth <= 0) { LK.showGameOver(); return; } } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; return self; }); var Room = Container.expand(function () { var self = Container.call(this); // Create room floor var roomFloor = self.attachAsset('houseFloor', { x: 0, y: 1200 }); // Create room walls var roomWallLeft = self.attachAsset('houseWall', { x: 0, y: 600, width: 20, height: 600 }); var roomWallRight = self.attachAsset('houseWall', { x: 2028, y: 600, width: 20, height: 600 }); var roomWallTop = self.attachAsset('houseWall', { x: 0, y: 600, width: 2048, height: 20 }); var roomWallBottom = self.attachAsset('houseWall', { x: 0, y: 1200, width: 2048, height: 20 }); // Exit door back to house var exitDoor = self.attachAsset('door', { x: 1000, y: 1120 }); // Basement key in bottom corner var basementKey = self.attachAsset('key', { x: 50, y: 1150, anchorX: 0.5, anchorY: 0.5 }); // Store door and key references self.exitDoor = exitDoor; self.basementKey = basementKey; return self; }); /**** * Initialize Game ****/ // Set dark atmospheric background var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // House structure assets // Set dark atmospheric background game.setBackgroundColor(0x0a0a0a); // Create the two-story house var house = game.addChild(new House()); // Position house to fill entire screen house.x = 0; house.y = 0; // Create player var player = game.addChild(new Player()); player.x = 2048 / 2; player.y = 1750; // Start on ground floor // Variables for player movement var dragNode = null; // Game state variables var gameStarted = false; var playerHidden = false; var originalPlayerAlpha = 1; var ghosts = []; var ghostSpawnTimer = 0; var currentGhost = null; var ghostSearchTimer = 0; var playerHealth = 100; var maxHealth = 100; var currentScene = 'house'; // 'house' or 'room' var roomScene = null; var roomMonster = null; var hasBasementKey = false; var playerWeapon = null; var basementGhosts = []; var totalBasementGhosts = 120; var spawnedBasementGhosts = 0; var basementGhostSpawnTimer = 0; var ghostsPerWave = 5; var bullets = []; var bloodEffects = []; var lastShootTime = 0; var ghostKillCount = 0; var currentGhostSpeed = 2; var finalBoss = null; var finalBossHealth = 40; var finalBossActive = false; var playerShotCount = 0; var hasExitKey = false; var exitKey = null; // Create health bar var healthBarBg = new Text2('', { size: 25, fill: 0x333333 }); healthBarBg.anchor.set(0.5, 0); healthBarBg.x = 0; healthBarBg.y = 150; LK.gui.top.addChild(healthBarBg); var healthBarFill = new Text2('', { size: 25, fill: 0xff0000 }); healthBarFill.anchor.set(0, 0); healthBarFill.x = -125; healthBarFill.y = 150; LK.gui.top.addChild(healthBarFill); var healthText = new Text2('Health: 100/100', { size: 25, fill: 0xffffff }); healthText.anchor.set(0.5, 0); healthText.x = 0; healthText.y = 175; LK.gui.top.addChild(healthText); // Function to update health bar function updateHealthBar() { var healthPercent = Math.max(0, playerHealth / maxHealth); var barWidth = Math.floor(250 * healthPercent); healthBarBg.setText('█████████████████████████'); healthBarFill.setText('█'.repeat(Math.max(0, Math.floor(25 * healthPercent)))); healthText.setText('Health: ' + Math.max(0, playerHealth) + '/' + maxHealth); } // Initialize health bar updateHealthBar(); // Touch controls for player movement game.down = function (x, y, obj) { // Check if we're in the house scene and clicked on upper floor door if (currentScene === 'house') { var upperDoorBounds = { x: house.upperDoor.x, y: house.upperDoor.y, width: 80, height: 120 }; if (x >= upperDoorBounds.x && x <= upperDoorBounds.x + upperDoorBounds.width && y >= upperDoorBounds.y && y <= upperDoorBounds.y + upperDoorBounds.height) { // Check if player is standing near the door var playerDistance = Math.sqrt((player.x - (house.upperDoor.x + 40)) * (player.x - (house.upperDoor.x + 40)) + (player.y - (house.upperDoor.y + 60)) * (player.y - (house.upperDoor.y + 60))); if (playerDistance < 60) { // Switch to room scene switchToRoom(); } return; } } // Check if we're in the room scene and clicked on exit door if (currentScene === 'room' && roomScene) { var roomExitBounds = { x: roomScene.exitDoor.x, y: roomScene.exitDoor.y, width: 80, height: 120 }; if (x >= roomExitBounds.x && x <= roomExitBounds.x + roomExitBounds.width && y >= roomExitBounds.y && y <= roomExitBounds.y + roomExitBounds.height) { // Check if player is standing near the door var playerDistance = Math.sqrt((player.x - (roomScene.exitDoor.x + 40)) * (player.x - (roomScene.exitDoor.x + 40)) + (player.y - (roomScene.exitDoor.y + 60)) * (player.y - (roomScene.exitDoor.y + 60))); if (playerDistance < 60) { // Switch back to house scene switchToHouse(); } return; } // Check if clicked on basement key if (roomScene.basementKey && roomScene.basementKey.visible) { var keyBounds = { x: roomScene.basementKey.x - 20, y: roomScene.basementKey.y - 10, width: 40, height: 20 }; if (x >= keyBounds.x && x <= keyBounds.x + keyBounds.width && y >= keyBounds.y && y <= keyBounds.y + keyBounds.height) { // Check if player is standing on the key var playerDistance = Math.sqrt((player.x - roomScene.basementKey.x) * (player.x - roomScene.basementKey.x) + (player.y - roomScene.basementKey.y) * (player.y - roomScene.basementKey.y)); if (playerDistance < 50) { // Collect the key hasBasementKey = true; roomScene.basementKey.visible = false; // Flash key collected effect LK.effects.flashScreen(0x00ff00, 500); } return; } } } // Check if clicked on basement door var basementDoorBounds = { x: house.basementDoor.x, y: house.basementDoor.y, width: 80, height: 120 }; if (x >= basementDoorBounds.x && x <= basementDoorBounds.x + basementDoorBounds.width && y >= basementDoorBounds.y && y <= basementDoorBounds.y + basementDoorBounds.height) { // Check if player has basement key and is close to door if (hasBasementKey) { var playerDistance = Math.sqrt((player.x - (house.basementDoor.x + 40)) * (player.x - (house.basementDoor.x + 40)) + (player.y - (house.basementDoor.y + 60)) * (player.y - (house.basementDoor.y + 60))); if (playerDistance < 60) { // Open basement door - unlock it and make lock invisible house.basementLocked = false; // Find and hide the basement lock for (var i = 0; i < house.children.length; i++) { if (house.children[i] === house.basementDoor.parent.children.find(function (child) { return child.x === 170 && child.y === 1660; })) { house.children[i].visible = false; break; } } // Flash green to indicate door is now open LK.effects.flashScreen(0x00ff00, 1000); // Use the key (remove it from inventory) hasBasementKey = false; } } else if (!house.basementLocked) { // Door is already unlocked, allow entry to basement var playerDistance = Math.sqrt((player.x - (house.basementDoor.x + 40)) * (player.x - (house.basementDoor.x + 40)) + (player.y - (house.basementDoor.y + 60)) * (player.y - (house.basementDoor.y + 60))); if (playerDistance < 60) { // Enter basement - move player down to basement level switchToBasement(); } } return; // Don't allow dragging when interacting with door } // Check if clicked on exit key (if it exists) if (exitKey && exitKey.visible) { var keyBounds = { x: exitKey.x - 20, y: exitKey.y - 10, width: 40, height: 20 }; if (x >= keyBounds.x && x <= keyBounds.x + keyBounds.width && y >= keyBounds.y && y <= keyBounds.y + keyBounds.height) { // Check if player is standing on the key var playerDistance = Math.sqrt((player.x - exitKey.x) * (player.x - exitKey.x) + (player.y - exitKey.y) * (player.y - exitKey.y)); if (playerDistance < 50) { // Collect the exit key hasExitKey = true; exitKey.visible = false; // Flash key collected effect LK.effects.flashScreen(0x00ff00, 500); } return; } } // Check if clicked on exit door var exitDoorBounds = { x: house.exitDoor.x, y: house.exitDoor.y, width: 80, height: 120 }; if (x >= exitDoorBounds.x && x <= exitDoorBounds.x + exitDoorBounds.width && y >= exitDoorBounds.y && y <= exitDoorBounds.y + exitDoorBounds.height) { // Check if player has exit key and is close to door if (hasExitKey) { var playerDistance = Math.sqrt((player.x - (house.exitDoor.x + 40)) * (player.x - (house.exitDoor.x + 40)) + (player.y - (house.exitDoor.y + 60)) * (player.y - (house.exitDoor.y + 60))); if (playerDistance < 60) { // Open exit door and win the game LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); } } return; } // Check if clicked on closet var closetBounds = { x: house.closet.x, y: house.closet.y, width: 80, height: 120 }; if (x >= closetBounds.x && x <= closetBounds.x + closetBounds.width && y >= closetBounds.y && y <= closetBounds.y + closetBounds.height) { // Check if player is standing near the closet var playerDistance = Math.sqrt((player.x - (house.closet.x + 40)) * (player.x - (house.closet.x + 40)) + (player.y - (house.closet.y + 60)) * (player.y - (house.closet.y + 60))); if (playerDistance < 60) { // Toggle hiding in closet if (!playerHidden) { // Hide player in closet player.x = closetBounds.x + closetBounds.width / 2; player.y = closetBounds.y + closetBounds.height / 2; player.alpha = 0.3; // Make player semi-transparent playerHidden = true; return; // Don't set drag node when hiding } else { // Come out of closet player.alpha = originalPlayerAlpha; playerHidden = false; return; // Don't set drag node when coming out } } } // Check if player has weapon - shoot bullet if (playerWeapon && playerWeapon.visible) { var currentTime = LK.ticks; if (currentTime - lastShootTime > 15) { // Limit shooting rate lastShootTime = currentTime; shootBullet(x, y); } } // Only allow dragging if not hidden if (!playerHidden) { dragNode = player; } // Apply collision boundaries to keep player inside house walls var newX = x; var newY = y; // Horizontal boundaries (house walls at x=0 and x=2048) if (newX < 20) newX = 20; // Left wall boundary if (newX > 2028) newX = 2028; // Right wall boundary // Vertical boundaries if (newY < 620) newY = 620; // Top of second floor if (newY > 2480) newY = 2480; // Bottom of basement // Prevent basement access if door is still locked if (newY > 1800 && house.basementLocked) newY = 1800; // Bottom of ground floor - prevent basement access if locked // Restrict basement movement - only allow going up through door area if (currentScene === 'basement' && newY < 1800) { // Only allow going up if player is near basement door var doorX = house.basementDoor.x + 40; if (newX < doorX - 60 || newX > doorX + 60) { newY = 1800; // Keep in basement if not near door } } // Floor barrier collision - prevent jumping between floors except via stairs // Strict floor separation - only allow movement between floors in stairs area if (player.y <= 1200 && newY > 1200) { // Player trying to move from second floor to ground floor if (newX < 974 || newX > 1074) { // Not in stairs area - block movement newY = 1200; // Keep on second floor } } else if (player.y > 1200 && newY <= 1200) { // Player trying to move from ground floor to second floor if (newX < 974 || newX > 1074) { // Not in stairs area - block movement newY = 1201; // Keep on ground floor } } // Second floor room wall collisions if (newY >= 600 && newY <= 1200) { // Vertical room divider at x=1024 if (newX > 1024 && newX < 1044 && newY >= 600 && newY <= 1200) { if (player.x <= 1024) { newX = 1024; } else { newX = 1044; } } // Horizontal room dividers at y=900 if (newY > 900 && newY < 920) { if (newX < 1024 || newX > 1044) { if (player.y <= 900) { newY = 900; } else { newY = 920; } } } } player.x = newX; player.y = newY; }; game.move = function (x, y, obj) { if (dragNode && !playerHidden) { // Apply collision boundaries to keep player inside house walls var newX = x; var newY = y; // Horizontal boundaries (house walls at x=0 and x=2048) if (newX < 20) newX = 20; // Left wall boundary if (newX > 2028) newX = 2028; // Right wall boundary // Vertical boundaries if (newY < 620) newY = 620; // Top of second floor if (newY > 2480) newY = 2480; // Bottom of basement // Prevent basement access if door is still locked if (newY > 1800 && house.basementLocked) newY = 1800; // Bottom of ground floor - prevent basement access if locked // Restrict basement movement - only allow going up through door area if (currentScene === 'basement' && newY < 1800) { // Only allow going up if player is near basement door var doorX = house.basementDoor.x + 40; if (newX < doorX - 60 || newX > doorX + 60) { newY = 1800; // Keep in basement if not near door } } // Floor barrier collision - prevent jumping between floors except via stairs // Strict floor separation - only allow movement between floors in stairs area if (dragNode.y <= 1200 && newY > 1200) { // Player trying to move from second floor to ground floor if (newX < 974 || newX > 1074) { // Not in stairs area - block movement newY = 1200; // Keep on second floor } } else if (dragNode.y > 1200 && newY <= 1200) { // Player trying to move from ground floor to second floor if (newX < 974 || newX > 1074) { // Not in stairs area - block movement newY = 1201; // Keep on ground floor } } // Second floor room wall collisions if (newY >= 600 && newY <= 1200) { // Vertical room divider at x=1024 if (newX > 1024 && newX < 1044 && newY >= 600 && newY <= 1200) { if (dragNode.x <= 1024) { newX = 1024; } else { newX = 1044; } } // Horizontal room dividers at y=900 if (newY > 900 && newY < 920) { if (newX < 1024 || newX > 1044) { if (dragNode.y <= 900) { newY = 900; } else { newY = 920; } } } } dragNode.x = newX; dragNode.y = newY; } }; game.up = function (x, y, obj) { dragNode = null; }; // Player floor tracking var playerFloor = 1; // 1 = ground floor, 2 = second floor // Game update loop for stair interaction game.update = function () { // Check if player is on stairs area (walkable stairs) var stairLeft = 974; var stairRight = 974 + 100; var stairTop = 1200; var stairBottom = 1800; // Determine current floor based on Y position if (player.y <= 1200) { playerFloor = 2; // Second floor } else if (player.y >= 1800) { playerFloor = 0; // Basement } else if (player.y >= 1200) { playerFloor = 1; // Ground floor } // Allow player to walk freely on stairs - no special collision needed // as stairs are now part of the walkable area between floors // Update collision boundaries - allow movement between floors via stairs if (player.y > 2480) player.y = 2480; // Bottom boundary - basement floor // Prevent basement access if door is still locked if (player.y > 1800 && house.basementLocked) player.y = 1800; // Bottom of ground floor - prevent basement access if locked if (player.y < 620) player.y = 620; // Top boundary // Update weapon position to follow player if (playerWeapon && playerWeapon.visible) { playerWeapon.x = player.x + 40; playerWeapon.y = player.y; // Auto-shoot at nearby ghosts var currentTime = LK.ticks; if (currentTime - lastShootTime > 30) { // Limit shooting rate // Find closest ghost within shooting range var closestGhost = null; var closestDistance = 300; // Detection range // Check basement ghosts for (var i = 0; i < basementGhosts.length; i++) { var ghost = basementGhosts[i]; var dx = ghost.x - player.x; var dy = ghost.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestGhost = ghost; closestDistance = distance; } } // Check regular house ghosts for (var i = 0; i < ghosts.length; i++) { var ghost = ghosts[i]; var dx = ghost.x - player.x; var dy = ghost.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestGhost = ghost; closestDistance = distance; } } // Check final boss if (finalBoss && finalBossActive) { var dx = finalBoss.x - player.x; var dy = finalBoss.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestGhost = finalBoss; closestDistance = distance; } } // Shoot at closest ghost if found if (closestGhost) { lastShootTime = currentTime; shootBullet(closestGhost.x, closestGhost.y); } } } // Ghost spawning system - every 15 seconds (900 ticks at 60fps) - only if boss not active if (!finalBossActive) { ghostSpawnTimer++; if (ghostSpawnTimer >= 900) { ghostSpawnTimer = 0; spawnGhost(); } } // Basement ghost spawning - spawn 5 ghosts every 8 seconds if in basement - only if boss not active if (currentScene === 'basement' && spawnedBasementGhosts < totalBasementGhosts && !finalBossActive) { basementGhostSpawnTimer++; if (basementGhostSpawnTimer >= 480) { // 8 seconds at 60fps basementGhostSpawnTimer = 0; spawnBasementGhosts(); } } // Spawn final boss when 120 ghosts are killed if (ghostKillCount >= 120 && !finalBossActive && !finalBoss) { spawnFinalBoss(); } }; // Ghost spawning function function spawnGhost() { // Only spawn if no ghost currently exists if (currentGhost === null) { // Animate door opening tween(house.entranceDoor, { scaleX: 0.1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Create ghost at entrance door var ghost = new Ghost(); ghost.x = house.entranceDoor.x + 40; ghost.y = house.entranceDoor.y + 60; ghosts.push(ghost); game.addChild(ghost); currentGhost = ghost; // Close door after ghost spawns tween(house.entranceDoor, { scaleX: 1 }, { duration: 300, easing: tween.easeIn }); } }); } } // Scene switching functions function switchToRoom() { currentScene = 'room'; // Hide house house.visible = false; // Create and show room if it doesn't exist if (!roomScene) { roomScene = game.addChild(new Room()); } roomScene.visible = true; // Create monster if it doesn't exist if (!roomMonster) { roomMonster = game.addChild(new Monster()); roomMonster.x = 100; roomMonster.y = 1150; } roomMonster.visible = true; // Position player in the room player.x = 1024; player.y = 900; } function switchToHouse() { currentScene = 'house'; // Hide room if (roomScene) { roomScene.visible = false; } // Hide monster if (roomMonster) { roomMonster.visible = false; } // Hide weapon when leaving basement if (playerWeapon) { playerWeapon.visible = false; } // Show house house.visible = true; // Position player back at upper floor door player.x = 500; player.y = 1050; } function switchToBasement() { currentScene = 'basement'; // Move player down to basement level player.y = 2300; // Position player in basement area // Give player a weapon if (!playerWeapon) { playerWeapon = game.addChild(LK.getAsset('weapon', { anchorX: 0.5, anchorY: 0.5 })); } playerWeapon.visible = true; // Initialize basement ghost spawning spawnedBasementGhosts = 0; basementGhostSpawnTimer = 0; basementGhosts = []; // Flash screen to indicate transition LK.effects.flashScreen(0x444444, 500); } // Function to spawn basement ghosts in waves function spawnBasementGhosts() { var ghostsToSpawn = Math.min(ghostsPerWave, totalBasementGhosts - spawnedBasementGhosts); for (var i = 0; i < ghostsToSpawn; i++) { var ghost = new BasementGhost(); // Spawn ghosts only from the front (top side) - directly in front of player ghost.x = 100 + Math.random() * 1848; ghost.y = 1850; basementGhosts.push(ghost); game.addChild(ghost); spawnedBasementGhosts++; } } // Function to shoot bullet towards target position function shootBullet(targetX, targetY) { var bullet = new Bullet(); bullet.x = player.x; bullet.y = player.y; // Calculate direction to target var dx = targetX - player.x; var dy = targetY - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { bullet.direction.x = dx / distance; bullet.direction.y = dy / distance; } bullets.push(bullet); game.addChild(bullet); } // Function to create blood effect function createBloodEffect(x, y) { var blood = new BloodEffect(); blood.x = x; blood.y = y; bloodEffects.push(blood); game.addChild(blood); } // Function to spawn final boss function spawnFinalBoss() { // Kill all existing ghosts for (var i = basementGhosts.length - 1; i >= 0; i--) { basementGhosts[i].destroy(); basementGhosts.splice(i, 1); } for (var i = ghosts.length - 1; i >= 0; i--) { ghosts[i].destroy(); ghosts.splice(i, 1); } currentGhost = null; ghostSpawnTimer = 0; // Force switch to basement scene switchToBasement(); // Create final boss finalBoss = game.addChild(new FinalBoss()); finalBoss.x = 200; finalBoss.y = 2200; finalBossActive = true; playerShotCount = 0; // Flash screen red to indicate boss arrival LK.effects.flashScreen(0xff0000, 1000); // Make sure player has weapon if (!playerWeapon) { playerWeapon = game.addChild(LK.getAsset('weapon', { anchorX: 0.5, anchorY: 0.5 })); } playerWeapon.visible = true; } // Display instructions var instructionText = new Text2('Explore the haunted house\nUse stairs to move between floors\nExit and basement doors are locked', { size: 50, fill: 0xCCCCCC }); instructionText.anchor.set(0.5, 0); LK.gui.top.addChild(instructionText); ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BasementGhost = Container.expand(function () {
var self = Container.call(this);
var ghostGraphics = self.attachAsset('ghost', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = currentGhostSpeed;
self.alpha = 0.8;
ghostGraphics.alpha = 0.8;
self.lastAttackTime = 0;
self.update = function () {
// Only update if in basement scene
if (currentScene !== 'basement') {
return;
}
// Move towards player
var dx = player.x - self.x;
var dy = player.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;
}
// Attack player if close enough
if (distance < 70) {
var currentTime = LK.ticks;
if (currentTime - self.lastAttackTime > 60) {
// Attack once per second
playerHealth -= 10;
self.lastAttackTime = currentTime;
updateHealthBar();
// Flash player red when damaged
LK.effects.flashObject(player, 0xff0000, 500);
// Create blood effect at player position
createBloodEffect(player.x, player.y);
// Check if player is dead
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
}
};
return self;
});
var BloodEffect = Container.expand(function () {
var self = Container.call(this);
var bloodGraphics = self.attachAsset('blood', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTime = 300; // 5 seconds at 60fps
self.currentLife = 0;
self.update = function () {
self.currentLife++;
// Fade out over time
self.alpha = 1 - self.currentLife / self.lifeTime;
// Remove after lifetime
if (self.currentLife >= self.lifeTime) {
self.destroy();
for (var i = bloodEffects.length - 1; i >= 0; i--) {
if (bloodEffects[i] === self) {
bloodEffects.splice(i, 1);
break;
}
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.direction = {
x: 0,
y: 0
};
self.update = function () {
// Only update if in basement scene
if (currentScene !== 'basement') {
return;
}
// Move bullet in direction
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Remove bullet if it goes off screen
if (self.x < 0 || self.x > 2048 || self.y < 1800 || self.y > 2480) {
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
}
// Check collision with basement ghosts
for (var i = basementGhosts.length - 1; i >= 0; i--) {
var ghost = basementGhosts[i];
if (self.intersects(ghost)) {
// Create blood effect at ghost position
createBloodEffect(ghost.x, ghost.y);
// Increment kill count
ghostKillCount++;
// Increase speed every 20 kills
if (ghostKillCount % 20 === 0) {
currentGhostSpeed += 1;
}
// Remove ghost
ghost.destroy();
basementGhosts.splice(i, 1);
// Remove bullet
self.destroy();
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
break;
}
}
// Check collision with regular house ghosts
for (var i = ghosts.length - 1; i >= 0; i--) {
var ghost = ghosts[i];
if (self.intersects(ghost)) {
// Create blood effect at ghost position
createBloodEffect(ghost.x, ghost.y);
// Increment kill count
ghostKillCount++;
// Increase speed every 20 kills
if (ghostKillCount % 20 === 0) {
currentGhostSpeed += 1;
}
// Remove ghost
ghost.destroy();
ghosts.splice(i, 1);
currentGhost = null;
ghostSpawnTimer = 0;
// Remove bullet
self.destroy();
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
break;
}
}
// Check collision with final boss
if (finalBoss && finalBossActive && self.intersects(finalBoss)) {
// Create blood effect at boss position
createBloodEffect(finalBoss.x, finalBoss.y);
// Increment player shot count
playerShotCount++;
// Damage boss
finalBoss.health--;
// Check if boss is defeated
if (playerShotCount >= 25) {
// Boss defeated - drop exit key
if (!exitKey) {
exitKey = game.addChild(LK.getAsset('key', {
anchorX: 0.5,
anchorY: 0.5
}));
exitKey.x = finalBoss.x;
exitKey.y = finalBoss.y;
exitKey.visible = true;
}
finalBoss.destroy();
finalBoss = null;
finalBossActive = false;
// Flash green to indicate boss defeated
LK.effects.flashScreen(0x00ff00, 1000);
}
// Remove bullet
self.destroy();
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
}
};
return self;
});
var FinalBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8; // Very fast boss
self.health = 40;
self.lastAttackTime = 0;
self.update = function () {
// Only update if boss is active
if (!finalBossActive || currentScene !== 'basement') {
return;
}
// Move towards player
var dx = player.x - self.x;
var dy = player.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;
}
// Attack player if close enough - deal 50 damage
if (distance < 80) {
var currentTime = LK.ticks;
if (currentTime - self.lastAttackTime > 60) {
// Attack once per second - deal 50 damage
playerHealth -= 50;
self.lastAttackTime = currentTime;
updateHealthBar();
// Flash player red when damaged
LK.effects.flashObject(player, 0xff0000, 500);
// Create blood effect at player position
createBloodEffect(player.x, player.y);
// Check if player is dead
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
}
};
return self;
});
var Ghost = Container.expand(function () {
var self = Container.call(this);
var ghostGraphics = self.attachAsset('ghost', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.alpha = 0.7;
ghostGraphics.alpha = 0.7;
self.searchTimer = 0;
self.maxSearchTime = 300; // 5 seconds at 60fps
self.isLeaving = false;
self.update = function () {
// Don't update ghost if player is in room scene
if (currentScene === 'room') {
return;
}
// Only increment search timer when player is hidden
if (playerHidden) {
self.searchTimer++;
}
// Move ghost towards player if not leaving
if (!self.isLeaving) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (!playerHidden && distance > 0) {
// Chase player if not hidden
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else if (playerHidden && distance > 0) {
// Move randomly when player is hidden
self.x += (Math.random() - 0.5) * 4;
self.y += (Math.random() - 0.5) * 4;
}
// Attack player if close enough and player is not hidden
if (distance < 80 && !playerHidden) {
// Damage player
if (self.lastAttackTime === undefined) self.lastAttackTime = 0;
var currentTime = LK.ticks;
if (currentTime - self.lastAttackTime > 60) {
// Attack once per second
playerHealth -= 10;
self.lastAttackTime = currentTime;
updateHealthBar();
// Flash player red when damaged
LK.effects.flashObject(player, 0xff0000, 500);
// Create blood effect at player position
createBloodEffect(player.x, player.y);
// Check if player is dead
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
// After dealing damage, ghost starts leaving
self.isLeaving = true;
}
}
}
// If ghost hasn't found player after 5 seconds of searching (while player is hidden), start leaving
if (self.searchTimer >= self.maxSearchTime && playerHidden && !self.isLeaving) {
self.isLeaving = true;
self.speed = 3; // Move faster when leaving
}
// If leaving, move towards entrance door
if (self.isLeaving) {
var doorX = house.entranceDoor.x + 40;
var doorY = house.entranceDoor.y + 60;
var dx = doorX - self.x;
var dy = doorY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Remove ghost when it reaches the door
if (distance < 30) {
self.destroy();
currentGhost = null;
ghostSpawnTimer = 0;
for (var i = ghosts.length - 1; i >= 0; i--) {
if (ghosts[i] === self) {
ghosts.splice(i, 1);
break;
}
}
}
}
// Remove ghost if it goes off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
currentGhost = null;
ghostSpawnTimer = 0;
for (var i = ghosts.length - 1; i >= 0; i--) {
if (ghosts[i] === self) {
ghosts.splice(i, 1);
break;
}
}
}
};
return self;
});
var House = Container.expand(function () {
var self = Container.call(this);
// Basement floor
var basementWall = self.attachAsset('houseWall', {
x: 0,
y: 1800
});
var basementFloor = self.attachAsset('houseFloor', {
x: 0,
y: 2400
});
// Ground floor
var groundFloorWall = self.attachAsset('houseWall', {
x: 0,
y: 1200
});
var groundFloor = self.attachAsset('houseFloor', {
x: 0,
y: 1800
});
// Second floor
var secondFloorWall = self.attachAsset('houseWall', {
x: 0,
y: 600
});
var secondFloor = self.attachAsset('houseFloor', {
x: 0,
y: 1200
});
// Stairs connecting floors - make them walkable
var stairway = self.attachAsset('stairs', {
x: 974,
y: 1200,
width: 100,
height: 600
});
// Second floor room dividers
var roomWall1 = self.attachAsset('houseWall', {
x: 1024,
y: 600,
width: 20,
height: 600
});
var roomWall2 = self.attachAsset('houseWall', {
x: 0,
y: 900,
width: 1024,
height: 20
});
var roomWall3 = self.attachAsset('houseWall', {
x: 1044,
y: 900,
width: 1004,
height: 20
});
// Floor barriers to prevent jumping between floors (except stairs)
var floorBarrierLeft = self.attachAsset('houseWall', {
x: 0,
y: 1200,
width: 974,
height: 20
});
var floorBarrierRight = self.attachAsset('houseWall', {
x: 1074,
y: 1200,
width: 974,
height: 20
});
// Store barrier references for collision detection
self.roomWall1 = roomWall1;
self.roomWall2 = roomWall2;
self.roomWall3 = roomWall3;
self.floorBarrierLeft = floorBarrierLeft;
self.floorBarrierRight = floorBarrierRight;
// Windows
var window1 = self.attachAsset('window', {
x: 300,
y: 700
});
var window2 = self.attachAsset('window', {
x: 800,
y: 700
});
var window3 = self.attachAsset('window', {
x: 1200,
y: 700
});
var window4 = self.attachAsset('window', {
x: 1600,
y: 700
});
var window5 = self.attachAsset('window', {
x: 300,
y: 1300
});
var window6 = self.attachAsset('window', {
x: 800,
y: 1300
});
var window7 = self.attachAsset('window', {
x: 1200,
y: 1300
});
var window8 = self.attachAsset('window', {
x: 1600,
y: 1300
});
// Exit door (locked)
var exitDoor = self.attachAsset('door', {
x: 1900,
y: 1620
});
var exitLock = self.attachAsset('lock', {
x: 1970,
y: 1660
});
// Basement door (locked)
var basementDoor = self.attachAsset('door', {
x: 100,
y: 1620
});
var basementLock = self.attachAsset('lock', {
x: 170,
y: 1660
});
// Closet on second floor
var closet = self.attachAsset('closet', {
x: 1800,
y: 680
});
// Upper floor door on the ground
var upperDoor = self.attachAsset('door', {
x: 500,
y: 1120
});
// Entrance door
var entranceDoor = self.attachAsset('door', {
x: 50,
y: 1620
});
// Store door and closet references for game logic
self.exitDoor = exitDoor;
self.basementDoor = basementDoor;
self.entranceDoor = entranceDoor;
self.upperDoor = upperDoor;
self.closet = closet;
self.exitLocked = true;
self.basementLocked = true;
return self;
});
var Monster = Container.expand(function () {
var self = Container.call(this);
var monsterGraphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.lastAttackTime = 0;
self.update = function () {
// Only update if in room scene
if (currentScene !== 'room') {
return;
}
// Move towards player
var dx = player.x - self.x;
var dy = player.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;
}
// Attack player if close enough
if (distance < 60) {
var currentTime = LK.ticks;
if (currentTime - self.lastAttackTime > 120) {
// Attack every 2 seconds
playerHealth -= 50;
self.lastAttackTime = currentTime;
updateHealthBar();
// Flash player red when damaged
LK.effects.flashObject(player, 0xff0000, 500);
// Create blood effect at player position
createBloodEffect(player.x, player.y);
// Check if player is dead
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
return self;
});
var Room = Container.expand(function () {
var self = Container.call(this);
// Create room floor
var roomFloor = self.attachAsset('houseFloor', {
x: 0,
y: 1200
});
// Create room walls
var roomWallLeft = self.attachAsset('houseWall', {
x: 0,
y: 600,
width: 20,
height: 600
});
var roomWallRight = self.attachAsset('houseWall', {
x: 2028,
y: 600,
width: 20,
height: 600
});
var roomWallTop = self.attachAsset('houseWall', {
x: 0,
y: 600,
width: 2048,
height: 20
});
var roomWallBottom = self.attachAsset('houseWall', {
x: 0,
y: 1200,
width: 2048,
height: 20
});
// Exit door back to house
var exitDoor = self.attachAsset('door', {
x: 1000,
y: 1120
});
// Basement key in bottom corner
var basementKey = self.attachAsset('key', {
x: 50,
y: 1150,
anchorX: 0.5,
anchorY: 0.5
});
// Store door and key references
self.exitDoor = exitDoor;
self.basementKey = basementKey;
return self;
});
/****
* Initialize Game
****/
// Set dark atmospheric background
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// House structure assets
// Set dark atmospheric background
game.setBackgroundColor(0x0a0a0a);
// Create the two-story house
var house = game.addChild(new House());
// Position house to fill entire screen
house.x = 0;
house.y = 0;
// Create player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 1750; // Start on ground floor
// Variables for player movement
var dragNode = null;
// Game state variables
var gameStarted = false;
var playerHidden = false;
var originalPlayerAlpha = 1;
var ghosts = [];
var ghostSpawnTimer = 0;
var currentGhost = null;
var ghostSearchTimer = 0;
var playerHealth = 100;
var maxHealth = 100;
var currentScene = 'house'; // 'house' or 'room'
var roomScene = null;
var roomMonster = null;
var hasBasementKey = false;
var playerWeapon = null;
var basementGhosts = [];
var totalBasementGhosts = 120;
var spawnedBasementGhosts = 0;
var basementGhostSpawnTimer = 0;
var ghostsPerWave = 5;
var bullets = [];
var bloodEffects = [];
var lastShootTime = 0;
var ghostKillCount = 0;
var currentGhostSpeed = 2;
var finalBoss = null;
var finalBossHealth = 40;
var finalBossActive = false;
var playerShotCount = 0;
var hasExitKey = false;
var exitKey = null;
// Create health bar
var healthBarBg = new Text2('', {
size: 25,
fill: 0x333333
});
healthBarBg.anchor.set(0.5, 0);
healthBarBg.x = 0;
healthBarBg.y = 150;
LK.gui.top.addChild(healthBarBg);
var healthBarFill = new Text2('', {
size: 25,
fill: 0xff0000
});
healthBarFill.anchor.set(0, 0);
healthBarFill.x = -125;
healthBarFill.y = 150;
LK.gui.top.addChild(healthBarFill);
var healthText = new Text2('Health: 100/100', {
size: 25,
fill: 0xffffff
});
healthText.anchor.set(0.5, 0);
healthText.x = 0;
healthText.y = 175;
LK.gui.top.addChild(healthText);
// Function to update health bar
function updateHealthBar() {
var healthPercent = Math.max(0, playerHealth / maxHealth);
var barWidth = Math.floor(250 * healthPercent);
healthBarBg.setText('█████████████████████████');
healthBarFill.setText('█'.repeat(Math.max(0, Math.floor(25 * healthPercent))));
healthText.setText('Health: ' + Math.max(0, playerHealth) + '/' + maxHealth);
}
// Initialize health bar
updateHealthBar();
// Touch controls for player movement
game.down = function (x, y, obj) {
// Check if we're in the house scene and clicked on upper floor door
if (currentScene === 'house') {
var upperDoorBounds = {
x: house.upperDoor.x,
y: house.upperDoor.y,
width: 80,
height: 120
};
if (x >= upperDoorBounds.x && x <= upperDoorBounds.x + upperDoorBounds.width && y >= upperDoorBounds.y && y <= upperDoorBounds.y + upperDoorBounds.height) {
// Check if player is standing near the door
var playerDistance = Math.sqrt((player.x - (house.upperDoor.x + 40)) * (player.x - (house.upperDoor.x + 40)) + (player.y - (house.upperDoor.y + 60)) * (player.y - (house.upperDoor.y + 60)));
if (playerDistance < 60) {
// Switch to room scene
switchToRoom();
}
return;
}
}
// Check if we're in the room scene and clicked on exit door
if (currentScene === 'room' && roomScene) {
var roomExitBounds = {
x: roomScene.exitDoor.x,
y: roomScene.exitDoor.y,
width: 80,
height: 120
};
if (x >= roomExitBounds.x && x <= roomExitBounds.x + roomExitBounds.width && y >= roomExitBounds.y && y <= roomExitBounds.y + roomExitBounds.height) {
// Check if player is standing near the door
var playerDistance = Math.sqrt((player.x - (roomScene.exitDoor.x + 40)) * (player.x - (roomScene.exitDoor.x + 40)) + (player.y - (roomScene.exitDoor.y + 60)) * (player.y - (roomScene.exitDoor.y + 60)));
if (playerDistance < 60) {
// Switch back to house scene
switchToHouse();
}
return;
}
// Check if clicked on basement key
if (roomScene.basementKey && roomScene.basementKey.visible) {
var keyBounds = {
x: roomScene.basementKey.x - 20,
y: roomScene.basementKey.y - 10,
width: 40,
height: 20
};
if (x >= keyBounds.x && x <= keyBounds.x + keyBounds.width && y >= keyBounds.y && y <= keyBounds.y + keyBounds.height) {
// Check if player is standing on the key
var playerDistance = Math.sqrt((player.x - roomScene.basementKey.x) * (player.x - roomScene.basementKey.x) + (player.y - roomScene.basementKey.y) * (player.y - roomScene.basementKey.y));
if (playerDistance < 50) {
// Collect the key
hasBasementKey = true;
roomScene.basementKey.visible = false;
// Flash key collected effect
LK.effects.flashScreen(0x00ff00, 500);
}
return;
}
}
}
// Check if clicked on basement door
var basementDoorBounds = {
x: house.basementDoor.x,
y: house.basementDoor.y,
width: 80,
height: 120
};
if (x >= basementDoorBounds.x && x <= basementDoorBounds.x + basementDoorBounds.width && y >= basementDoorBounds.y && y <= basementDoorBounds.y + basementDoorBounds.height) {
// Check if player has basement key and is close to door
if (hasBasementKey) {
var playerDistance = Math.sqrt((player.x - (house.basementDoor.x + 40)) * (player.x - (house.basementDoor.x + 40)) + (player.y - (house.basementDoor.y + 60)) * (player.y - (house.basementDoor.y + 60)));
if (playerDistance < 60) {
// Open basement door - unlock it and make lock invisible
house.basementLocked = false;
// Find and hide the basement lock
for (var i = 0; i < house.children.length; i++) {
if (house.children[i] === house.basementDoor.parent.children.find(function (child) {
return child.x === 170 && child.y === 1660;
})) {
house.children[i].visible = false;
break;
}
}
// Flash green to indicate door is now open
LK.effects.flashScreen(0x00ff00, 1000);
// Use the key (remove it from inventory)
hasBasementKey = false;
}
} else if (!house.basementLocked) {
// Door is already unlocked, allow entry to basement
var playerDistance = Math.sqrt((player.x - (house.basementDoor.x + 40)) * (player.x - (house.basementDoor.x + 40)) + (player.y - (house.basementDoor.y + 60)) * (player.y - (house.basementDoor.y + 60)));
if (playerDistance < 60) {
// Enter basement - move player down to basement level
switchToBasement();
}
}
return; // Don't allow dragging when interacting with door
}
// Check if clicked on exit key (if it exists)
if (exitKey && exitKey.visible) {
var keyBounds = {
x: exitKey.x - 20,
y: exitKey.y - 10,
width: 40,
height: 20
};
if (x >= keyBounds.x && x <= keyBounds.x + keyBounds.width && y >= keyBounds.y && y <= keyBounds.y + keyBounds.height) {
// Check if player is standing on the key
var playerDistance = Math.sqrt((player.x - exitKey.x) * (player.x - exitKey.x) + (player.y - exitKey.y) * (player.y - exitKey.y));
if (playerDistance < 50) {
// Collect the exit key
hasExitKey = true;
exitKey.visible = false;
// Flash key collected effect
LK.effects.flashScreen(0x00ff00, 500);
}
return;
}
}
// Check if clicked on exit door
var exitDoorBounds = {
x: house.exitDoor.x,
y: house.exitDoor.y,
width: 80,
height: 120
};
if (x >= exitDoorBounds.x && x <= exitDoorBounds.x + exitDoorBounds.width && y >= exitDoorBounds.y && y <= exitDoorBounds.y + exitDoorBounds.height) {
// Check if player has exit key and is close to door
if (hasExitKey) {
var playerDistance = Math.sqrt((player.x - (house.exitDoor.x + 40)) * (player.x - (house.exitDoor.x + 40)) + (player.y - (house.exitDoor.y + 60)) * (player.y - (house.exitDoor.y + 60)));
if (playerDistance < 60) {
// Open exit door and win the game
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
}
}
return;
}
// Check if clicked on closet
var closetBounds = {
x: house.closet.x,
y: house.closet.y,
width: 80,
height: 120
};
if (x >= closetBounds.x && x <= closetBounds.x + closetBounds.width && y >= closetBounds.y && y <= closetBounds.y + closetBounds.height) {
// Check if player is standing near the closet
var playerDistance = Math.sqrt((player.x - (house.closet.x + 40)) * (player.x - (house.closet.x + 40)) + (player.y - (house.closet.y + 60)) * (player.y - (house.closet.y + 60)));
if (playerDistance < 60) {
// Toggle hiding in closet
if (!playerHidden) {
// Hide player in closet
player.x = closetBounds.x + closetBounds.width / 2;
player.y = closetBounds.y + closetBounds.height / 2;
player.alpha = 0.3; // Make player semi-transparent
playerHidden = true;
return; // Don't set drag node when hiding
} else {
// Come out of closet
player.alpha = originalPlayerAlpha;
playerHidden = false;
return; // Don't set drag node when coming out
}
}
}
// Check if player has weapon - shoot bullet
if (playerWeapon && playerWeapon.visible) {
var currentTime = LK.ticks;
if (currentTime - lastShootTime > 15) {
// Limit shooting rate
lastShootTime = currentTime;
shootBullet(x, y);
}
}
// Only allow dragging if not hidden
if (!playerHidden) {
dragNode = player;
}
// Apply collision boundaries to keep player inside house walls
var newX = x;
var newY = y;
// Horizontal boundaries (house walls at x=0 and x=2048)
if (newX < 20) newX = 20; // Left wall boundary
if (newX > 2028) newX = 2028; // Right wall boundary
// Vertical boundaries
if (newY < 620) newY = 620; // Top of second floor
if (newY > 2480) newY = 2480; // Bottom of basement
// Prevent basement access if door is still locked
if (newY > 1800 && house.basementLocked) newY = 1800; // Bottom of ground floor - prevent basement access if locked
// Restrict basement movement - only allow going up through door area
if (currentScene === 'basement' && newY < 1800) {
// Only allow going up if player is near basement door
var doorX = house.basementDoor.x + 40;
if (newX < doorX - 60 || newX > doorX + 60) {
newY = 1800; // Keep in basement if not near door
}
}
// Floor barrier collision - prevent jumping between floors except via stairs
// Strict floor separation - only allow movement between floors in stairs area
if (player.y <= 1200 && newY > 1200) {
// Player trying to move from second floor to ground floor
if (newX < 974 || newX > 1074) {
// Not in stairs area - block movement
newY = 1200; // Keep on second floor
}
} else if (player.y > 1200 && newY <= 1200) {
// Player trying to move from ground floor to second floor
if (newX < 974 || newX > 1074) {
// Not in stairs area - block movement
newY = 1201; // Keep on ground floor
}
}
// Second floor room wall collisions
if (newY >= 600 && newY <= 1200) {
// Vertical room divider at x=1024
if (newX > 1024 && newX < 1044 && newY >= 600 && newY <= 1200) {
if (player.x <= 1024) {
newX = 1024;
} else {
newX = 1044;
}
}
// Horizontal room dividers at y=900
if (newY > 900 && newY < 920) {
if (newX < 1024 || newX > 1044) {
if (player.y <= 900) {
newY = 900;
} else {
newY = 920;
}
}
}
}
player.x = newX;
player.y = newY;
};
game.move = function (x, y, obj) {
if (dragNode && !playerHidden) {
// Apply collision boundaries to keep player inside house walls
var newX = x;
var newY = y;
// Horizontal boundaries (house walls at x=0 and x=2048)
if (newX < 20) newX = 20; // Left wall boundary
if (newX > 2028) newX = 2028; // Right wall boundary
// Vertical boundaries
if (newY < 620) newY = 620; // Top of second floor
if (newY > 2480) newY = 2480; // Bottom of basement
// Prevent basement access if door is still locked
if (newY > 1800 && house.basementLocked) newY = 1800; // Bottom of ground floor - prevent basement access if locked
// Restrict basement movement - only allow going up through door area
if (currentScene === 'basement' && newY < 1800) {
// Only allow going up if player is near basement door
var doorX = house.basementDoor.x + 40;
if (newX < doorX - 60 || newX > doorX + 60) {
newY = 1800; // Keep in basement if not near door
}
}
// Floor barrier collision - prevent jumping between floors except via stairs
// Strict floor separation - only allow movement between floors in stairs area
if (dragNode.y <= 1200 && newY > 1200) {
// Player trying to move from second floor to ground floor
if (newX < 974 || newX > 1074) {
// Not in stairs area - block movement
newY = 1200; // Keep on second floor
}
} else if (dragNode.y > 1200 && newY <= 1200) {
// Player trying to move from ground floor to second floor
if (newX < 974 || newX > 1074) {
// Not in stairs area - block movement
newY = 1201; // Keep on ground floor
}
}
// Second floor room wall collisions
if (newY >= 600 && newY <= 1200) {
// Vertical room divider at x=1024
if (newX > 1024 && newX < 1044 && newY >= 600 && newY <= 1200) {
if (dragNode.x <= 1024) {
newX = 1024;
} else {
newX = 1044;
}
}
// Horizontal room dividers at y=900
if (newY > 900 && newY < 920) {
if (newX < 1024 || newX > 1044) {
if (dragNode.y <= 900) {
newY = 900;
} else {
newY = 920;
}
}
}
}
dragNode.x = newX;
dragNode.y = newY;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Player floor tracking
var playerFloor = 1; // 1 = ground floor, 2 = second floor
// Game update loop for stair interaction
game.update = function () {
// Check if player is on stairs area (walkable stairs)
var stairLeft = 974;
var stairRight = 974 + 100;
var stairTop = 1200;
var stairBottom = 1800;
// Determine current floor based on Y position
if (player.y <= 1200) {
playerFloor = 2; // Second floor
} else if (player.y >= 1800) {
playerFloor = 0; // Basement
} else if (player.y >= 1200) {
playerFloor = 1; // Ground floor
}
// Allow player to walk freely on stairs - no special collision needed
// as stairs are now part of the walkable area between floors
// Update collision boundaries - allow movement between floors via stairs
if (player.y > 2480) player.y = 2480; // Bottom boundary - basement floor
// Prevent basement access if door is still locked
if (player.y > 1800 && house.basementLocked) player.y = 1800; // Bottom of ground floor - prevent basement access if locked
if (player.y < 620) player.y = 620; // Top boundary
// Update weapon position to follow player
if (playerWeapon && playerWeapon.visible) {
playerWeapon.x = player.x + 40;
playerWeapon.y = player.y;
// Auto-shoot at nearby ghosts
var currentTime = LK.ticks;
if (currentTime - lastShootTime > 30) {
// Limit shooting rate
// Find closest ghost within shooting range
var closestGhost = null;
var closestDistance = 300; // Detection range
// Check basement ghosts
for (var i = 0; i < basementGhosts.length; i++) {
var ghost = basementGhosts[i];
var dx = ghost.x - player.x;
var dy = ghost.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestGhost = ghost;
closestDistance = distance;
}
}
// Check regular house ghosts
for (var i = 0; i < ghosts.length; i++) {
var ghost = ghosts[i];
var dx = ghost.x - player.x;
var dy = ghost.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestGhost = ghost;
closestDistance = distance;
}
}
// Check final boss
if (finalBoss && finalBossActive) {
var dx = finalBoss.x - player.x;
var dy = finalBoss.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestGhost = finalBoss;
closestDistance = distance;
}
}
// Shoot at closest ghost if found
if (closestGhost) {
lastShootTime = currentTime;
shootBullet(closestGhost.x, closestGhost.y);
}
}
}
// Ghost spawning system - every 15 seconds (900 ticks at 60fps) - only if boss not active
if (!finalBossActive) {
ghostSpawnTimer++;
if (ghostSpawnTimer >= 900) {
ghostSpawnTimer = 0;
spawnGhost();
}
}
// Basement ghost spawning - spawn 5 ghosts every 8 seconds if in basement - only if boss not active
if (currentScene === 'basement' && spawnedBasementGhosts < totalBasementGhosts && !finalBossActive) {
basementGhostSpawnTimer++;
if (basementGhostSpawnTimer >= 480) {
// 8 seconds at 60fps
basementGhostSpawnTimer = 0;
spawnBasementGhosts();
}
}
// Spawn final boss when 120 ghosts are killed
if (ghostKillCount >= 120 && !finalBossActive && !finalBoss) {
spawnFinalBoss();
}
};
// Ghost spawning function
function spawnGhost() {
// Only spawn if no ghost currently exists
if (currentGhost === null) {
// Animate door opening
tween(house.entranceDoor, {
scaleX: 0.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create ghost at entrance door
var ghost = new Ghost();
ghost.x = house.entranceDoor.x + 40;
ghost.y = house.entranceDoor.y + 60;
ghosts.push(ghost);
game.addChild(ghost);
currentGhost = ghost;
// Close door after ghost spawns
tween(house.entranceDoor, {
scaleX: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
}
// Scene switching functions
function switchToRoom() {
currentScene = 'room';
// Hide house
house.visible = false;
// Create and show room if it doesn't exist
if (!roomScene) {
roomScene = game.addChild(new Room());
}
roomScene.visible = true;
// Create monster if it doesn't exist
if (!roomMonster) {
roomMonster = game.addChild(new Monster());
roomMonster.x = 100;
roomMonster.y = 1150;
}
roomMonster.visible = true;
// Position player in the room
player.x = 1024;
player.y = 900;
}
function switchToHouse() {
currentScene = 'house';
// Hide room
if (roomScene) {
roomScene.visible = false;
}
// Hide monster
if (roomMonster) {
roomMonster.visible = false;
}
// Hide weapon when leaving basement
if (playerWeapon) {
playerWeapon.visible = false;
}
// Show house
house.visible = true;
// Position player back at upper floor door
player.x = 500;
player.y = 1050;
}
function switchToBasement() {
currentScene = 'basement';
// Move player down to basement level
player.y = 2300; // Position player in basement area
// Give player a weapon
if (!playerWeapon) {
playerWeapon = game.addChild(LK.getAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
}));
}
playerWeapon.visible = true;
// Initialize basement ghost spawning
spawnedBasementGhosts = 0;
basementGhostSpawnTimer = 0;
basementGhosts = [];
// Flash screen to indicate transition
LK.effects.flashScreen(0x444444, 500);
}
// Function to spawn basement ghosts in waves
function spawnBasementGhosts() {
var ghostsToSpawn = Math.min(ghostsPerWave, totalBasementGhosts - spawnedBasementGhosts);
for (var i = 0; i < ghostsToSpawn; i++) {
var ghost = new BasementGhost();
// Spawn ghosts only from the front (top side) - directly in front of player
ghost.x = 100 + Math.random() * 1848;
ghost.y = 1850;
basementGhosts.push(ghost);
game.addChild(ghost);
spawnedBasementGhosts++;
}
}
// Function to shoot bullet towards target position
function shootBullet(targetX, targetY) {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
// Calculate direction to target
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.direction.x = dx / distance;
bullet.direction.y = dy / distance;
}
bullets.push(bullet);
game.addChild(bullet);
}
// Function to create blood effect
function createBloodEffect(x, y) {
var blood = new BloodEffect();
blood.x = x;
blood.y = y;
bloodEffects.push(blood);
game.addChild(blood);
}
// Function to spawn final boss
function spawnFinalBoss() {
// Kill all existing ghosts
for (var i = basementGhosts.length - 1; i >= 0; i--) {
basementGhosts[i].destroy();
basementGhosts.splice(i, 1);
}
for (var i = ghosts.length - 1; i >= 0; i--) {
ghosts[i].destroy();
ghosts.splice(i, 1);
}
currentGhost = null;
ghostSpawnTimer = 0;
// Force switch to basement scene
switchToBasement();
// Create final boss
finalBoss = game.addChild(new FinalBoss());
finalBoss.x = 200;
finalBoss.y = 2200;
finalBossActive = true;
playerShotCount = 0;
// Flash screen red to indicate boss arrival
LK.effects.flashScreen(0xff0000, 1000);
// Make sure player has weapon
if (!playerWeapon) {
playerWeapon = game.addChild(LK.getAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
}));
}
playerWeapon.visible = true;
}
// Display instructions
var instructionText = new Text2('Explore the haunted house\nUse stairs to move between floors\nExit and basement doors are locked', {
size: 50,
fill: 0xCCCCCC
});
instructionText.anchor.set(0.5, 0);
LK.gui.top.addChild(instructionText);
;
merdiven. In-Game asset. 2d. High contrast. No shadows
pencere. In-Game asset. 2d. High contrast. No shadows
pikselli bir karakter ama kız. In-Game asset. 2d. High contrast. No shadows
siyah sade uzun duvar. In-Game asset. 2d. High contrast. No shadows
hayalet. In-Game asset. 2d. High contrast. No shadows
kapı ama eskimiş. In-Game asset. 2d. High contrast. No shadows
silah gerçek. In-Game asset. 2d. High contrast. No shadows
anahtar. In-Game asset. 2d. High contrast. No shadows