/****
* 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