/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bullet class for weapon projectiles
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Create bullet visual (small circle)
var bulletAsset = self.attachAsset('manCircle', {
anchorX: 0.5,
anchorY: 0.5
});
bulletAsset.width = 20;
bulletAsset.height = 20;
bulletAsset.tint = 0x4B0082; // Evil dark purple for bullet
self.speed = 12;
self.targetX = 0;
self.targetY = 0;
// Update method for bullet movement towards target
self.update = function () {
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - 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 bullet if it reaches target or goes off screen
if (distance < 30 || self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
if (self.parent) {
self.parent.removeChild(self);
}
var index = bullets.indexOf(self);
if (index !== -1) {
bullets.splice(index, 1);
}
}
};
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
// Attach car asset (red box, 200x120)
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Set size for car asset
carAsset.width = 200;
carAsset.height = 120;
carAsset.tint = 0x8B0000; // Dark evil red
// Add flashlight beam
var flashlightBeam = self.attachAsset('flashlightBeam', {
anchorX: 0,
anchorY: 0.5
});
flashlightBeam.x = 100; // Position in front of car
flashlightBeam.y = 0; // Center vertically with car
flashlightBeam.alpha = 0.5; // 50% opacity
flashlightBeam.tint = 0xFFFFFF; // White color
// Set flashlight beam to render on top of everything
flashlightBeam.zIndex = 1000;
// Override intersects method to exclude flashlight beam from collision detection
var originalIntersects = self.intersects;
self.intersects = function (other) {
// Create a temporary container with only the car asset (excluding flashlight)
var tempContainer = new Container();
tempContainer.x = self.x;
tempContainer.y = self.y;
tempContainer.width = carAsset.width;
tempContainer.height = carAsset.height;
tempContainer.addChild(carAsset);
// Use the car asset directly for collision detection
var result = carAsset.getBounds().intersects ? carAsset.getBounds().intersects(other.getBounds()) : false;
if (!result) {
// Fallback to distance-based collision detection
var dx = self.x - other.x;
var dy = self.y - other.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var carRadius = Math.max(carAsset.width, carAsset.height) / 2;
var otherRadius = Math.max(other.width || 50, other.height || 50) / 2;
result = distance < carRadius + otherRadius;
}
// Re-add car asset to self
self.addChild(carAsset);
return result;
};
// Track last position for direction calculation
self.lastX = 0;
self.lastY = 0;
// Update method to handle flashlight direction
self.update = function () {
// Calculate movement direction
var deltaX = self.x - self.lastX;
var deltaY = self.y - self.lastY;
// Only update flashlight direction if car is moving
if (deltaX !== 0 || deltaY !== 0) {
// Calculate angle based on movement direction
var angle = Math.atan2(deltaY, deltaX);
flashlightBeam.rotation = angle;
// Position flashlight beam at front of car based on direction
var distance = 100;
flashlightBeam.x = Math.cos(angle) * distance;
flashlightBeam.y = Math.sin(angle) * distance;
}
// Update last position
self.lastX = self.x;
self.lastY = self.y;
};
// For touch feedback
self.flash = function () {
tween(self, {
alpha: 0.5
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
// DeadlyTree class for special scene
var DeadlyTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (red tinted, 100x100)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 100;
treeAsset.height = 100;
treeAsset.tint = 0x8B0000; // Evil blood red tint for deadly trees
self.speed = 24; // Falling speed (3x faster)
self.isFollowing = false;
self.followSpeed = 8; // Speed when following player
// Start following the player
self.startFollowing = function () {
self.isFollowing = true;
};
// Stop following and disappear with tween
self.stopFollowing = function () {
self.isFollowing = false;
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
// Update method for falling motion or following
self.update = function () {
if (self.isFollowing && car) {
// Move towards car position
var dx = car.x - self.x;
var dy = car.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.followSpeed;
self.y += dy / distance * self.followSpeed;
}
} else {
self.y += self.speed;
}
};
return self;
});
// GoldenTree class
var GoldenTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (golden, 140x140 - slightly bigger)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 140;
treeAsset.height = 140;
treeAsset.tint = 0xB8860B; // Evil dark gold tint color
self.pointValue = 10; // Golden tree gives 10 points
// Add golden glow effect
self.glowEffect = function () {
tween(self, {
alpha: 0.7
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.glowEffect(); // Loop the glow effect
}
});
}
});
};
// Start glow effect immediately
self.glowEffect();
// Special golden hit animation
self.hitAnim = function () {
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
};
return self;
});
// Pole class
var Pole = Container.expand(function () {
var self = Container.call(this);
// Attach pole asset (thin metal pole, 30x300)
var poleAsset = self.attachAsset('pole', {
anchorX: 0.5,
anchorY: 0.5
});
poleAsset.tint = 0x2F2F2F; // Evil dark gray/black
self.pointValue = 10000; // Pole gives 10000 points
// Animate on hit (shake effect)
self.hitAnim = function () {
tween(self, {
rotation: 0.2
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotation: -0.2
}, {
duration: 80,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
rotation: 0
}, {
duration: 80,
easing: tween.easeIn
});
}
});
}
});
};
return self;
});
// PurpleTree class
var PurpleTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (purple, 140x140 - slightly bigger)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 140;
treeAsset.height = 140;
treeAsset.tint = 0x301934; // Evil dark purple tint color
self.pointValue = 30; // Purple tree gives 30 points
// Add purple glow effect
self.glowEffect = function () {
tween(self, {
alpha: 0.6
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.glowEffect(); // Loop the glow effect
}
});
}
});
};
// Start glow effect immediately
self.glowEffect();
// Special purple hit animation
self.hitAnim = function () {
tween(self, {
scaleX: 1.6,
scaleY: 1.6
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 180,
easing: tween.easeIn
});
}
});
};
return self;
});
// SCP-911 class for special scene
var SCP911 = Container.expand(function () {
var self = Container.call(this);
// Create circular shape for the man
var manAsset = self.attachAsset('manCircle', {
anchorX: 0.5,
anchorY: 0.5
});
manAsset.tint = 0x1C1C1C; // Evil dark black
return self;
});
// Tree class
var Tree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (green ellipse, 120x120)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 120;
treeAsset.height = 120;
treeAsset.tint = 0x013220; // Evil dark green tint
self.pointValue = 1; // Normal tree gives 1 point
// Animate on hit
self.hitAnim = function () {
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeIn
});
}
});
};
return self;
});
// Weapon class for special scene
var Weapon = Container.expand(function () {
var self = Container.call(this);
// Create weapon visual using weapon image asset
var weaponAsset = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
});
weaponAsset.rotation = Math.PI / 4; // 45 degree rotation
weaponAsset.tint = 0x2F2F2F; // Evil dark metal color
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0D0D0D // Dark evil black
});
/****
* Game Code
****/
// --- Game constants ---
var CAR_START_X = 2048 / 2;
var CAR_START_Y = 2732 - 350;
var TREE_MIN_Y = 350;
var TREE_MAX_Y = 2732 - 350;
var TREE_MIN_X = 150;
var TREE_MAX_X = 2048 - 150;
var WIN_SCORE = 100000000;
// --- Game state ---
var car = null;
var trees = [];
var poles = [];
var score = 0;
var scoreTxt = null;
var dragNode = null;
var lastIntersecting = [];
var lastIntersectingPoles = [];
var goldenTreeSpawned = false;
var treesHitThisRound = 0;
var purpleTreeCounter = 0;
var polesSpawnedThisRound = 0;
var countdownTimer = 10;
var countdownTxt = null;
var lastCarX = 0;
var lastCarY = 0;
var lastScore = 0;
var upwardCounter = 1;
var upwardCounterTxt = null;
var specialScene = false;
var specialSceneInitialized = false;
var scp911 = null;
var deadlyTrees = [];
var lastIntersectingDeadly = [];
var throwTimer = 0;
var followingStartTime = null;
var followingDuration = 10000; // 10 seconds in milliseconds
var allFollowingTreesGone = false; // Flag to track if all following trees have disappeared
var bottomLeftCounter = 150;
var bottomLeftCounterTxt = null;
var weapon = null;
var weaponSpawned = false;
var bullets = [];
var lastIntersectingWeapon = false;
var skeletonHealth = 1000;
var skeletonHealthTxt = null;
// --- Create and add car ---
car = new Car();
car.x = CAR_START_X;
car.y = CAR_START_Y;
car.lastX = CAR_START_X;
car.lastY = CAR_START_Y;
game.addChild(car);
// Ensure flashlight beam renders on top by setting display list order
game.setChildIndex(car, game.children.length - 1);
// --- Create and add trees ---
for (var i = 0; i < 5; i++) {
var tree = new Tree();
tree.x = getRandomTreeX();
tree.y = getRandomTreeY();
trees.push(tree);
lastIntersecting.push(false);
game.addChild(tree);
}
// --- Create and add poles ---
for (var i = 0; i < 5; i++) {
var pole = new Pole();
var tries = 0;
var validPosition = false;
do {
pole.x = getRandomTreeX();
pole.y = getRandomTreeY();
tries++;
validPosition = true;
// Check distance from car
if (distance(car.x, car.y, pole.x, pole.y) < 300) {
validPosition = false;
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(pole.x, pole.y, trees[t].x, trees[t].y) < 200) {
validPosition = false;
break;
}
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(pole.x, pole.y, poles[p].x, poles[p].y) < 200) {
validPosition = false;
break;
}
}
} while (!validPosition && tries < 20);
poles.push(pole);
lastIntersectingPoles.push(false);
game.addChild(pole);
}
// --- Score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0x8B0000 // Evil dark red
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Countdown timer text ---
countdownTxt = new Text2('10', {
size: 100,
fill: 0x8B0000 // Evil dark red
});
countdownTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(countdownTxt);
// --- Upward counter text ---
upwardCounterTxt = new Text2('1', {
size: 100,
fill: 0x4B0082 // Evil dark purple
});
upwardCounterTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(upwardCounterTxt);
// --- Bottom-left counter text ---
bottomLeftCounterTxt = new Text2('150', {
size: 100,
fill: 0x8B0000 // Evil dark red
});
bottomLeftCounterTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(bottomLeftCounterTxt);
// --- Skeleton health bar text ---
skeletonHealthTxt = new Text2('1000', {
size: 80,
fill: 0x8B0000 // Evil dark red
});
skeletonHealthTxt.anchor.set(0, 0.5);
skeletonHealthTxt.visible = false; // Initially hidden until special scene
game.addChild(skeletonHealthTxt);
// --- Helper functions ---
function getRandomTreeX() {
return TREE_MIN_X + Math.floor(Math.random() * (TREE_MAX_X - TREE_MIN_X));
}
function getRandomTreeY() {
return TREE_MIN_Y + Math.floor(Math.random() * (TREE_MAX_Y - TREE_MIN_Y));
}
function updateScoreText() {
scoreTxt.setText(score);
}
function updateCountdownText() {
countdownTxt.setText(countdownTimer);
}
function updateUpwardCounterText() {
upwardCounterTxt.setText(upwardCounter);
}
function updateBottomLeftCounterText() {
bottomLeftCounterTxt.setText(bottomLeftCounter);
}
function updateSkeletonHealthText() {
skeletonHealthTxt.setText(skeletonHealth);
}
function spawnNewTree() {
var newTree;
// Spawn purple tree every 40 normal trees (1 in 40 chance)
purpleTreeCounter++;
if (purpleTreeCounter >= 40) {
newTree = new PurpleTree();
purpleTreeCounter = 0; // Reset counter
} else if (treesHitThisRound % 5 === 0 && !goldenTreeSpawned) {
// Spawn golden tree once every 5 hits if not already spawned this round
newTree = new GoldenTree();
goldenTreeSpawned = true;
} else {
newTree = new Tree();
// Reset golden tree availability after 10 hits
if (treesHitThisRound >= 10) {
goldenTreeSpawned = false;
treesHitThisRound = 0;
}
}
// Position new tree randomly (avoid placing too close to car and existing poles)
var tries = 0;
var validPosition = false;
do {
newTree.x = getRandomTreeX();
newTree.y = getRandomTreeY();
tries++;
validPosition = true;
// Check distance from car
if (distance(car.x, car.y, newTree.x, newTree.y) < 300) {
validPosition = false;
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(newTree.x, newTree.y, poles[p].x, poles[p].y) < 200) {
validPosition = false;
break;
}
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(newTree.x, newTree.y, trees[t].x, trees[t].y) < 200) {
validPosition = false;
break;
}
}
} while (!validPosition && tries < 20);
trees.push(newTree);
lastIntersecting.push(false);
game.addChild(newTree);
// Calculate current round based on score (every 50 points is a new round)
var currentRound = Math.floor(score / 50);
var polesPerRound = Math.max(1, 5 - currentRound); // Start with 5, decrease by 1 each round, minimum 1
// Spawn poles (decreasing each round)
if (polesSpawnedThisRound < polesPerRound) {
var newPole = new Pole();
var tries = 0;
var validPolePosition = false;
do {
newPole.x = getRandomTreeX();
newPole.y = getRandomTreeY();
tries++;
validPolePosition = true;
// Check distance from car
if (distance(car.x, car.y, newPole.x, newPole.y) < 300) {
validPolePosition = false;
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(newPole.x, newPole.y, trees[t].x, trees[t].y) < 200) {
validPolePosition = false;
break;
}
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(newPole.x, newPole.y, poles[p].x, poles[p].y) < 200) {
validPolePosition = false;
break;
}
}
} while (!validPolePosition && tries < 20);
poles.push(newPole);
lastIntersectingPoles.push(false);
game.addChild(newPole);
polesSpawnedThisRound++;
}
// Reset pole counter when round is complete
if (polesSpawnedThisRound >= polesPerRound && treesHitThisRound >= 10) {
polesSpawnedThisRound = 0;
}
}
// --- Move handler for dragging car ---
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = Math.max(200, Math.min(2732 - 100, y));
// Screen wrapping for X axis
if (newX > 2048) {
newX = 0; // Car exits right, appears on left
} else if (newX < 0) {
newX = 2048; // Car exits left, appears on right
}
dragNode.x = newX;
dragNode.y = newY;
}
// Collision detection for all trees
for (var i = 0; i < trees.length; i++) {
var tree = trees[i];
var currentIntersecting = car.intersects(tree);
if (!lastIntersecting[i] && currentIntersecting) {
// Car just hit tree - take damage
score += tree.pointValue;
treesHitThisRound++;
updateScoreText();
// Flash screen red to show damage
LK.effects.flashScreen(0xFF0000, 300);
// Update bottom-left counter
bottomLeftCounter -= tree.pointValue;
if (bottomLeftCounter <= 0) {
bottomLeftCounter = 0;
bottomLeftCounterTxt.visible = false;
}
updateBottomLeftCounterText();
// Animate car and tree
car.flash();
tree.hitAnim();
// Remove the hit tree
game.removeChild(tree);
trees.splice(i, 1);
lastIntersecting.splice(i, 1);
i--; // Adjust index since we removed an element
// Spawn new tree (normal or golden based on conditions)
spawnNewTree();
// Win condition
if (score >= WIN_SCORE) {
LK.showYouWin();
}
}
if (i >= 0 && i < lastIntersecting.length) {
lastIntersecting[i] = currentIntersecting;
}
}
// Collision detection for all poles
for (var j = 0; j < poles.length; j++) {
var pole = poles[j];
var currentIntersectingPole = car.intersects(pole);
if (!lastIntersectingPoles[j] && currentIntersectingPole) {
// Car just hit pole - critical damage (instant game over)
// Flash screen red to show critical damage
LK.effects.flashScreen(0xFF0000, 1000);
// Animate car and pole
car.flash();
pole.hitAnim();
// Show jumpscare
var jumpscare = LK.getAsset('scpJumpscare', {
anchorX: 0.5,
anchorY: 0.5
});
jumpscare.x = 2048 / 2;
jumpscare.y = 2732 / 2;
jumpscare.alpha = 0;
jumpscare.scaleX = 0.1;
jumpscare.scaleY = 0.1;
game.addChild(jumpscare);
// Animate jumpscare to full screen with tween
tween(jumpscare, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold jumpscare for a moment then fade out
LK.setTimeout(function () {
tween(jumpscare, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
LK.showGameOver();
}
});
}, 800);
}
});
return; // Exit immediately to prevent further processing
}
if (j >= 0 && j < lastIntersectingPoles.length) {
lastIntersectingPoles[j] = currentIntersectingPole;
}
}
}
// --- Utility: distance between two points ---
function distance(x1, y1, x2, y2) {
var dx = x1 - x2;
var dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
// --- Touch/drag events ---
game.down = function (x, y, obj) {
// Only start drag if touch is on car (within 120px radius)
var dx = x - car.x;
var dy = y - car.y;
if (dx * dx + dy * dy < 120 * 120) {
dragNode = car;
handleMove(x, y, obj);
}
};
game.move = handleMove;
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Game update loop (not needed for movement, but for future extensibility) ---
game.update = function () {
// Check for special scene transition (150+ score)
if (score >= 150 && !specialScene) {
specialScene = true;
// Clear existing game elements
for (var i = trees.length - 1; i >= 0; i--) {
game.removeChild(trees[i]);
}
trees = [];
lastIntersecting = [];
for (var j = poles.length - 1; j >= 0; j--) {
game.removeChild(poles[j]);
}
poles = [];
lastIntersectingPoles = [];
// Change background to evil darker for special scene
game.setBackgroundColor(0x1A1A1A); // Evil darker gray
}
// Handle special scene logic
if (specialScene && !specialSceneInitialized) {
// Initialize special scene
scp911 = new SCP911();
scp911.x = 2048 / 2;
scp911.y = 200;
game.addChild(scp911);
// Show and position skeleton health bar
skeletonHealthTxt.visible = true;
skeletonHealthTxt.x = scp911.x + 100; // Position to the right of skeleton
skeletonHealthTxt.y = scp911.y;
skeletonHealth = 1000; // Reset health
updateSkeletonHealthText();
// Spawn weapon immediately in special scene
if (!weaponSpawned) {
weapon = new Weapon();
weapon.x = Math.random() * (2048 - 200) + 100; // Random X within game bounds
weapon.y = Math.random() * (2732 - 400) + 200; // Random Y within game bounds
game.addChild(weapon);
weaponSpawned = true;
}
specialSceneInitialized = true;
}
if (specialScene) {
// Initialize following start time when first entering special scene
if (followingStartTime === null) {
followingStartTime = Date.now();
}
// Spawn deadly trees that follow player for 10 seconds, then disappear
if (LK.ticks % 120 === 0 && deadlyTrees.length < 10 && !allFollowingTreesGone) {
var newDeadlyTree = new DeadlyTree();
newDeadlyTree.x = Math.random() * 2048;
newDeadlyTree.y = -100; // Start above screen
newDeadlyTree.spawnTime = Date.now();
newDeadlyTree.startFollowing();
deadlyTrees.push(newDeadlyTree);
lastIntersectingDeadly.push(false);
game.addChild(newDeadlyTree);
// Set timer to make tree disappear after 10 seconds
LK.setTimeout(function () {
if (newDeadlyTree.parent) {
newDeadlyTree.stopFollowing();
var index = deadlyTrees.indexOf(newDeadlyTree);
if (index !== -1) {
deadlyTrees.splice(index, 1);
lastIntersectingDeadly.splice(index, 1);
}
// Check if all deadly trees are gone
if (deadlyTrees.length === 0 && !allFollowingTreesGone) {
allFollowingTreesGone = true;
// Show message near the SCP-911
var messageText = new Text2('O zaman bunu al', {
size: 80,
fill: 0x8B0000 // Evil dark red
});
messageText.anchor.set(0.5, 0.5);
messageText.x = scp911.x;
messageText.y = scp911.y - 100;
game.addChild(messageText);
// Spawn weapon at random location
if (!weaponSpawned) {
weapon = new Weapon();
weapon.x = Math.random() * (2048 - 200) + 100; // Random X within game bounds
weapon.y = Math.random() * (2732 - 400) + 200; // Random Y within game bounds
game.addChild(weapon);
weaponSpawned = true;
}
}
}
}, 10000);
}
// Check collision with deadly trees
for (var k = deadlyTrees.length - 1; k >= 0; k--) {
var deadlyTree = deadlyTrees[k];
var currentIntersectingDeadly = car.intersects(deadlyTree);
// Check if tree has been alive for 10 seconds
if (deadlyTree.spawnTime && Date.now() - deadlyTree.spawnTime >= 10000) {
deadlyTree.stopFollowing();
deadlyTrees.splice(k, 1);
lastIntersectingDeadly.splice(k, 1);
// Check if all deadly trees are gone
if (deadlyTrees.length === 0 && !allFollowingTreesGone) {
allFollowingTreesGone = true;
// Show message near the SCP-911
var messageText = new Text2('O zaman bunu al', {
size: 80,
fill: 0x8B0000 // Evil dark red
});
messageText.anchor.set(0.5, 0.5);
messageText.x = scp911.x;
messageText.y = scp911.y - 100; // Position above the SCP-911
game.addChild(messageText);
// Spawn weapon at random location
if (!weaponSpawned) {
weapon = new Weapon();
weapon.x = Math.random() * (2048 - 200) + 100; // Random X within game bounds
weapon.y = Math.random() * (2732 - 400) + 200; // Random Y within game bounds
game.addChild(weapon);
weaponSpawned = true;
}
}
continue;
}
// Check if tree went off screen (only for non-following trees)
if (!deadlyTree.isFollowing && deadlyTree.y > 2732 + 100) {
game.removeChild(deadlyTree);
deadlyTrees.splice(k, 1);
lastIntersectingDeadly.splice(k, 1);
continue;
}
// Check collision - instant death
if (!lastIntersectingDeadly[k] && currentIntersectingDeadly) {
// Show jumpscare
var jumpscare = LK.getAsset('scpJumpscare', {
anchorX: 0.5,
anchorY: 0.5
});
jumpscare.x = 2048 / 2;
jumpscare.y = 2732 / 2;
jumpscare.alpha = 0;
jumpscare.scaleX = 0.1;
jumpscare.scaleY = 0.1;
game.addChild(jumpscare);
// Animate jumpscare to full screen with tween
tween(jumpscare, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold jumpscare for a moment then show death message
LK.setTimeout(function () {
// Show death message
var deathText = new Text2('ÖLDÜN!', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
deathText.anchor.set(0.5, 0.5);
deathText.x = 2048 / 2;
deathText.y = 2732 / 2;
game.addChild(deathText);
// Fade out jumpscare and show death message
tween(jumpscare, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
// Show message for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
});
}, 600);
}
});
return;
}
if (k < lastIntersectingDeadly.length) {
lastIntersectingDeadly[k] = currentIntersectingDeadly;
}
}
// Check weapon collision and bullet firing
if (weapon && scp911) {
var currentIntersectingWeapon = car.intersects(weapon);
if (!lastIntersectingWeapon && currentIntersectingWeapon) {
// Car touched weapon, make weapon follow car
weapon.isFollowingCar = true;
// Fire bullet at SCP-911
var newBullet = new Bullet();
newBullet.x = car.x;
newBullet.y = car.y;
newBullet.targetX = scp911.x;
newBullet.targetY = scp911.y;
bullets.push(newBullet);
game.addChild(newBullet);
}
lastIntersectingWeapon = currentIntersectingWeapon;
// Make weapon follow car if attached
if (weapon.isFollowingCar) {
weapon.x = car.x + 60; // Offset to the right of car
weapon.y = car.y;
}
}
// Check bullet collisions with SCP-911
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (scp911 && bullet.intersects && bullet.intersects(scp911)) {
// Bullet hit SCP-911 - reduce health
skeletonHealth -= 5;
updateSkeletonHealthText();
// Remove bullet
game.removeChild(bullet);
bullets.splice(b, 1);
// Flash SCP-911 red when hit
tween(scp911, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(scp911, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
// Check if SCP-911 is defeated
if (skeletonHealth <= 0) {
// Hide health bar
skeletonHealthTxt.visible = false;
// Show victory message
var victoryText = new Text2('SCP-911 Yenildi!', {
size: 200,
fill: 0x4B0082 // Evil dark purple
});
victoryText.anchor.set(0.5, 0.5);
victoryText.x = 2048 / 2;
victoryText.y = 2732 / 2;
game.addChild(victoryText);
// Show victory for 3 seconds then end game
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
}
}
}
// Only run countdown timer logic if not in special scene
if (!specialScene) {
// Check if player hit something (score changed)
var scoreChanged = score !== lastScore;
if (scoreChanged) {
// Player hit something, reset countdown
countdownTimer = 10;
updateCountdownText();
} else {
// Player didn't hit anything, countdown every second (60 ticks = 1 second)
if (LK.ticks % 60 === 0) {
countdownTimer--;
updateCountdownText();
if (countdownTimer <= 0) {
if (score < 10) {
// Show F- grade for low score
var gradeText = new Text2('F-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 10 && score <= 30) {
// Show F+ grade for score between 10 and 30
var gradeText = new Text2('F+', {
size: 300,
fill: 0x4B0082 // Evil dark purple
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 30 && score <= 50) {
// Show D- grade for score between 30 and 50
var gradeText = new Text2('D-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
LK.showGameOver();
}
}
}
}
}
// Only run upward counter logic if not in special scene
if (!specialScene) {
// Upward counter increments every second (60 ticks = 1 second)
if (LK.ticks % 60 === 0) {
upwardCounter++;
updateUpwardCounterText();
if (upwardCounter >= 90) {
if (score < 10) {
// Show F- grade for low score
var gradeText = new Text2('F-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 10 && score <= 30) {
// Show F+ grade for score between 10 and 30
var gradeText = new Text2('F+', {
size: 300,
fill: 0x4B0082 // Evil dark purple
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 30 && score <= 50) {
// Show D- grade for score between 30 and 50
var gradeText = new Text2('D-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
LK.showGameOver();
}
}
}
}
// Update tracking variables
lastCarX = car.x;
lastCarY = car.y;
lastScore = score;
};
// --- Initialize score ---
score = 0;
updateScoreText();
// --- Initialize upward counter ---
upwardCounter = 1;
updateUpwardCounterText();
// --- Initialize bottom-left counter ---
bottomLeftCounter = 150;
updateBottomLeftCounterText();
// --- Play SCP-911 music ---
LK.playMusic('SCP-9111');
// --- Initialize tracking variables ---
lastCarX = car.x;
lastCarY = car.y;
lastScore = score; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bullet class for weapon projectiles
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Create bullet visual (small circle)
var bulletAsset = self.attachAsset('manCircle', {
anchorX: 0.5,
anchorY: 0.5
});
bulletAsset.width = 20;
bulletAsset.height = 20;
bulletAsset.tint = 0x4B0082; // Evil dark purple for bullet
self.speed = 12;
self.targetX = 0;
self.targetY = 0;
// Update method for bullet movement towards target
self.update = function () {
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - 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 bullet if it reaches target or goes off screen
if (distance < 30 || self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
if (self.parent) {
self.parent.removeChild(self);
}
var index = bullets.indexOf(self);
if (index !== -1) {
bullets.splice(index, 1);
}
}
};
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
// Attach car asset (red box, 200x120)
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Set size for car asset
carAsset.width = 200;
carAsset.height = 120;
carAsset.tint = 0x8B0000; // Dark evil red
// Add flashlight beam
var flashlightBeam = self.attachAsset('flashlightBeam', {
anchorX: 0,
anchorY: 0.5
});
flashlightBeam.x = 100; // Position in front of car
flashlightBeam.y = 0; // Center vertically with car
flashlightBeam.alpha = 0.5; // 50% opacity
flashlightBeam.tint = 0xFFFFFF; // White color
// Set flashlight beam to render on top of everything
flashlightBeam.zIndex = 1000;
// Override intersects method to exclude flashlight beam from collision detection
var originalIntersects = self.intersects;
self.intersects = function (other) {
// Create a temporary container with only the car asset (excluding flashlight)
var tempContainer = new Container();
tempContainer.x = self.x;
tempContainer.y = self.y;
tempContainer.width = carAsset.width;
tempContainer.height = carAsset.height;
tempContainer.addChild(carAsset);
// Use the car asset directly for collision detection
var result = carAsset.getBounds().intersects ? carAsset.getBounds().intersects(other.getBounds()) : false;
if (!result) {
// Fallback to distance-based collision detection
var dx = self.x - other.x;
var dy = self.y - other.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var carRadius = Math.max(carAsset.width, carAsset.height) / 2;
var otherRadius = Math.max(other.width || 50, other.height || 50) / 2;
result = distance < carRadius + otherRadius;
}
// Re-add car asset to self
self.addChild(carAsset);
return result;
};
// Track last position for direction calculation
self.lastX = 0;
self.lastY = 0;
// Update method to handle flashlight direction
self.update = function () {
// Calculate movement direction
var deltaX = self.x - self.lastX;
var deltaY = self.y - self.lastY;
// Only update flashlight direction if car is moving
if (deltaX !== 0 || deltaY !== 0) {
// Calculate angle based on movement direction
var angle = Math.atan2(deltaY, deltaX);
flashlightBeam.rotation = angle;
// Position flashlight beam at front of car based on direction
var distance = 100;
flashlightBeam.x = Math.cos(angle) * distance;
flashlightBeam.y = Math.sin(angle) * distance;
}
// Update last position
self.lastX = self.x;
self.lastY = self.y;
};
// For touch feedback
self.flash = function () {
tween(self, {
alpha: 0.5
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
// DeadlyTree class for special scene
var DeadlyTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (red tinted, 100x100)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 100;
treeAsset.height = 100;
treeAsset.tint = 0x8B0000; // Evil blood red tint for deadly trees
self.speed = 24; // Falling speed (3x faster)
self.isFollowing = false;
self.followSpeed = 8; // Speed when following player
// Start following the player
self.startFollowing = function () {
self.isFollowing = true;
};
// Stop following and disappear with tween
self.stopFollowing = function () {
self.isFollowing = false;
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
// Update method for falling motion or following
self.update = function () {
if (self.isFollowing && car) {
// Move towards car position
var dx = car.x - self.x;
var dy = car.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.followSpeed;
self.y += dy / distance * self.followSpeed;
}
} else {
self.y += self.speed;
}
};
return self;
});
// GoldenTree class
var GoldenTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (golden, 140x140 - slightly bigger)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 140;
treeAsset.height = 140;
treeAsset.tint = 0xB8860B; // Evil dark gold tint color
self.pointValue = 10; // Golden tree gives 10 points
// Add golden glow effect
self.glowEffect = function () {
tween(self, {
alpha: 0.7
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.glowEffect(); // Loop the glow effect
}
});
}
});
};
// Start glow effect immediately
self.glowEffect();
// Special golden hit animation
self.hitAnim = function () {
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
};
return self;
});
// Pole class
var Pole = Container.expand(function () {
var self = Container.call(this);
// Attach pole asset (thin metal pole, 30x300)
var poleAsset = self.attachAsset('pole', {
anchorX: 0.5,
anchorY: 0.5
});
poleAsset.tint = 0x2F2F2F; // Evil dark gray/black
self.pointValue = 10000; // Pole gives 10000 points
// Animate on hit (shake effect)
self.hitAnim = function () {
tween(self, {
rotation: 0.2
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotation: -0.2
}, {
duration: 80,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
rotation: 0
}, {
duration: 80,
easing: tween.easeIn
});
}
});
}
});
};
return self;
});
// PurpleTree class
var PurpleTree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (purple, 140x140 - slightly bigger)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 140;
treeAsset.height = 140;
treeAsset.tint = 0x301934; // Evil dark purple tint color
self.pointValue = 30; // Purple tree gives 30 points
// Add purple glow effect
self.glowEffect = function () {
tween(self, {
alpha: 0.6
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.glowEffect(); // Loop the glow effect
}
});
}
});
};
// Start glow effect immediately
self.glowEffect();
// Special purple hit animation
self.hitAnim = function () {
tween(self, {
scaleX: 1.6,
scaleY: 1.6
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 180,
easing: tween.easeIn
});
}
});
};
return self;
});
// SCP-911 class for special scene
var SCP911 = Container.expand(function () {
var self = Container.call(this);
// Create circular shape for the man
var manAsset = self.attachAsset('manCircle', {
anchorX: 0.5,
anchorY: 0.5
});
manAsset.tint = 0x1C1C1C; // Evil dark black
return self;
});
// Tree class
var Tree = Container.expand(function () {
var self = Container.call(this);
// Attach tree asset (green ellipse, 120x120)
var treeAsset = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
treeAsset.width = 120;
treeAsset.height = 120;
treeAsset.tint = 0x013220; // Evil dark green tint
self.pointValue = 1; // Normal tree gives 1 point
// Animate on hit
self.hitAnim = function () {
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeIn
});
}
});
};
return self;
});
// Weapon class for special scene
var Weapon = Container.expand(function () {
var self = Container.call(this);
// Create weapon visual using weapon image asset
var weaponAsset = self.attachAsset('weapon', {
anchorX: 0.5,
anchorY: 0.5
});
weaponAsset.rotation = Math.PI / 4; // 45 degree rotation
weaponAsset.tint = 0x2F2F2F; // Evil dark metal color
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0D0D0D // Dark evil black
});
/****
* Game Code
****/
// --- Game constants ---
var CAR_START_X = 2048 / 2;
var CAR_START_Y = 2732 - 350;
var TREE_MIN_Y = 350;
var TREE_MAX_Y = 2732 - 350;
var TREE_MIN_X = 150;
var TREE_MAX_X = 2048 - 150;
var WIN_SCORE = 100000000;
// --- Game state ---
var car = null;
var trees = [];
var poles = [];
var score = 0;
var scoreTxt = null;
var dragNode = null;
var lastIntersecting = [];
var lastIntersectingPoles = [];
var goldenTreeSpawned = false;
var treesHitThisRound = 0;
var purpleTreeCounter = 0;
var polesSpawnedThisRound = 0;
var countdownTimer = 10;
var countdownTxt = null;
var lastCarX = 0;
var lastCarY = 0;
var lastScore = 0;
var upwardCounter = 1;
var upwardCounterTxt = null;
var specialScene = false;
var specialSceneInitialized = false;
var scp911 = null;
var deadlyTrees = [];
var lastIntersectingDeadly = [];
var throwTimer = 0;
var followingStartTime = null;
var followingDuration = 10000; // 10 seconds in milliseconds
var allFollowingTreesGone = false; // Flag to track if all following trees have disappeared
var bottomLeftCounter = 150;
var bottomLeftCounterTxt = null;
var weapon = null;
var weaponSpawned = false;
var bullets = [];
var lastIntersectingWeapon = false;
var skeletonHealth = 1000;
var skeletonHealthTxt = null;
// --- Create and add car ---
car = new Car();
car.x = CAR_START_X;
car.y = CAR_START_Y;
car.lastX = CAR_START_X;
car.lastY = CAR_START_Y;
game.addChild(car);
// Ensure flashlight beam renders on top by setting display list order
game.setChildIndex(car, game.children.length - 1);
// --- Create and add trees ---
for (var i = 0; i < 5; i++) {
var tree = new Tree();
tree.x = getRandomTreeX();
tree.y = getRandomTreeY();
trees.push(tree);
lastIntersecting.push(false);
game.addChild(tree);
}
// --- Create and add poles ---
for (var i = 0; i < 5; i++) {
var pole = new Pole();
var tries = 0;
var validPosition = false;
do {
pole.x = getRandomTreeX();
pole.y = getRandomTreeY();
tries++;
validPosition = true;
// Check distance from car
if (distance(car.x, car.y, pole.x, pole.y) < 300) {
validPosition = false;
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(pole.x, pole.y, trees[t].x, trees[t].y) < 200) {
validPosition = false;
break;
}
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(pole.x, pole.y, poles[p].x, poles[p].y) < 200) {
validPosition = false;
break;
}
}
} while (!validPosition && tries < 20);
poles.push(pole);
lastIntersectingPoles.push(false);
game.addChild(pole);
}
// --- Score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0x8B0000 // Evil dark red
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Countdown timer text ---
countdownTxt = new Text2('10', {
size: 100,
fill: 0x8B0000 // Evil dark red
});
countdownTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(countdownTxt);
// --- Upward counter text ---
upwardCounterTxt = new Text2('1', {
size: 100,
fill: 0x4B0082 // Evil dark purple
});
upwardCounterTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(upwardCounterTxt);
// --- Bottom-left counter text ---
bottomLeftCounterTxt = new Text2('150', {
size: 100,
fill: 0x8B0000 // Evil dark red
});
bottomLeftCounterTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(bottomLeftCounterTxt);
// --- Skeleton health bar text ---
skeletonHealthTxt = new Text2('1000', {
size: 80,
fill: 0x8B0000 // Evil dark red
});
skeletonHealthTxt.anchor.set(0, 0.5);
skeletonHealthTxt.visible = false; // Initially hidden until special scene
game.addChild(skeletonHealthTxt);
// --- Helper functions ---
function getRandomTreeX() {
return TREE_MIN_X + Math.floor(Math.random() * (TREE_MAX_X - TREE_MIN_X));
}
function getRandomTreeY() {
return TREE_MIN_Y + Math.floor(Math.random() * (TREE_MAX_Y - TREE_MIN_Y));
}
function updateScoreText() {
scoreTxt.setText(score);
}
function updateCountdownText() {
countdownTxt.setText(countdownTimer);
}
function updateUpwardCounterText() {
upwardCounterTxt.setText(upwardCounter);
}
function updateBottomLeftCounterText() {
bottomLeftCounterTxt.setText(bottomLeftCounter);
}
function updateSkeletonHealthText() {
skeletonHealthTxt.setText(skeletonHealth);
}
function spawnNewTree() {
var newTree;
// Spawn purple tree every 40 normal trees (1 in 40 chance)
purpleTreeCounter++;
if (purpleTreeCounter >= 40) {
newTree = new PurpleTree();
purpleTreeCounter = 0; // Reset counter
} else if (treesHitThisRound % 5 === 0 && !goldenTreeSpawned) {
// Spawn golden tree once every 5 hits if not already spawned this round
newTree = new GoldenTree();
goldenTreeSpawned = true;
} else {
newTree = new Tree();
// Reset golden tree availability after 10 hits
if (treesHitThisRound >= 10) {
goldenTreeSpawned = false;
treesHitThisRound = 0;
}
}
// Position new tree randomly (avoid placing too close to car and existing poles)
var tries = 0;
var validPosition = false;
do {
newTree.x = getRandomTreeX();
newTree.y = getRandomTreeY();
tries++;
validPosition = true;
// Check distance from car
if (distance(car.x, car.y, newTree.x, newTree.y) < 300) {
validPosition = false;
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(newTree.x, newTree.y, poles[p].x, poles[p].y) < 200) {
validPosition = false;
break;
}
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(newTree.x, newTree.y, trees[t].x, trees[t].y) < 200) {
validPosition = false;
break;
}
}
} while (!validPosition && tries < 20);
trees.push(newTree);
lastIntersecting.push(false);
game.addChild(newTree);
// Calculate current round based on score (every 50 points is a new round)
var currentRound = Math.floor(score / 50);
var polesPerRound = Math.max(1, 5 - currentRound); // Start with 5, decrease by 1 each round, minimum 1
// Spawn poles (decreasing each round)
if (polesSpawnedThisRound < polesPerRound) {
var newPole = new Pole();
var tries = 0;
var validPolePosition = false;
do {
newPole.x = getRandomTreeX();
newPole.y = getRandomTreeY();
tries++;
validPolePosition = true;
// Check distance from car
if (distance(car.x, car.y, newPole.x, newPole.y) < 300) {
validPolePosition = false;
}
// Check distance from all existing trees
for (var t = 0; t < trees.length; t++) {
if (distance(newPole.x, newPole.y, trees[t].x, trees[t].y) < 200) {
validPolePosition = false;
break;
}
}
// Check distance from all existing poles
for (var p = 0; p < poles.length; p++) {
if (distance(newPole.x, newPole.y, poles[p].x, poles[p].y) < 200) {
validPolePosition = false;
break;
}
}
} while (!validPolePosition && tries < 20);
poles.push(newPole);
lastIntersectingPoles.push(false);
game.addChild(newPole);
polesSpawnedThisRound++;
}
// Reset pole counter when round is complete
if (polesSpawnedThisRound >= polesPerRound && treesHitThisRound >= 10) {
polesSpawnedThisRound = 0;
}
}
// --- Move handler for dragging car ---
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = Math.max(200, Math.min(2732 - 100, y));
// Screen wrapping for X axis
if (newX > 2048) {
newX = 0; // Car exits right, appears on left
} else if (newX < 0) {
newX = 2048; // Car exits left, appears on right
}
dragNode.x = newX;
dragNode.y = newY;
}
// Collision detection for all trees
for (var i = 0; i < trees.length; i++) {
var tree = trees[i];
var currentIntersecting = car.intersects(tree);
if (!lastIntersecting[i] && currentIntersecting) {
// Car just hit tree - take damage
score += tree.pointValue;
treesHitThisRound++;
updateScoreText();
// Flash screen red to show damage
LK.effects.flashScreen(0xFF0000, 300);
// Update bottom-left counter
bottomLeftCounter -= tree.pointValue;
if (bottomLeftCounter <= 0) {
bottomLeftCounter = 0;
bottomLeftCounterTxt.visible = false;
}
updateBottomLeftCounterText();
// Animate car and tree
car.flash();
tree.hitAnim();
// Remove the hit tree
game.removeChild(tree);
trees.splice(i, 1);
lastIntersecting.splice(i, 1);
i--; // Adjust index since we removed an element
// Spawn new tree (normal or golden based on conditions)
spawnNewTree();
// Win condition
if (score >= WIN_SCORE) {
LK.showYouWin();
}
}
if (i >= 0 && i < lastIntersecting.length) {
lastIntersecting[i] = currentIntersecting;
}
}
// Collision detection for all poles
for (var j = 0; j < poles.length; j++) {
var pole = poles[j];
var currentIntersectingPole = car.intersects(pole);
if (!lastIntersectingPoles[j] && currentIntersectingPole) {
// Car just hit pole - critical damage (instant game over)
// Flash screen red to show critical damage
LK.effects.flashScreen(0xFF0000, 1000);
// Animate car and pole
car.flash();
pole.hitAnim();
// Show jumpscare
var jumpscare = LK.getAsset('scpJumpscare', {
anchorX: 0.5,
anchorY: 0.5
});
jumpscare.x = 2048 / 2;
jumpscare.y = 2732 / 2;
jumpscare.alpha = 0;
jumpscare.scaleX = 0.1;
jumpscare.scaleY = 0.1;
game.addChild(jumpscare);
// Animate jumpscare to full screen with tween
tween(jumpscare, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold jumpscare for a moment then fade out
LK.setTimeout(function () {
tween(jumpscare, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
LK.showGameOver();
}
});
}, 800);
}
});
return; // Exit immediately to prevent further processing
}
if (j >= 0 && j < lastIntersectingPoles.length) {
lastIntersectingPoles[j] = currentIntersectingPole;
}
}
}
// --- Utility: distance between two points ---
function distance(x1, y1, x2, y2) {
var dx = x1 - x2;
var dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
// --- Touch/drag events ---
game.down = function (x, y, obj) {
// Only start drag if touch is on car (within 120px radius)
var dx = x - car.x;
var dy = y - car.y;
if (dx * dx + dy * dy < 120 * 120) {
dragNode = car;
handleMove(x, y, obj);
}
};
game.move = handleMove;
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Game update loop (not needed for movement, but for future extensibility) ---
game.update = function () {
// Check for special scene transition (150+ score)
if (score >= 150 && !specialScene) {
specialScene = true;
// Clear existing game elements
for (var i = trees.length - 1; i >= 0; i--) {
game.removeChild(trees[i]);
}
trees = [];
lastIntersecting = [];
for (var j = poles.length - 1; j >= 0; j--) {
game.removeChild(poles[j]);
}
poles = [];
lastIntersectingPoles = [];
// Change background to evil darker for special scene
game.setBackgroundColor(0x1A1A1A); // Evil darker gray
}
// Handle special scene logic
if (specialScene && !specialSceneInitialized) {
// Initialize special scene
scp911 = new SCP911();
scp911.x = 2048 / 2;
scp911.y = 200;
game.addChild(scp911);
// Show and position skeleton health bar
skeletonHealthTxt.visible = true;
skeletonHealthTxt.x = scp911.x + 100; // Position to the right of skeleton
skeletonHealthTxt.y = scp911.y;
skeletonHealth = 1000; // Reset health
updateSkeletonHealthText();
// Spawn weapon immediately in special scene
if (!weaponSpawned) {
weapon = new Weapon();
weapon.x = Math.random() * (2048 - 200) + 100; // Random X within game bounds
weapon.y = Math.random() * (2732 - 400) + 200; // Random Y within game bounds
game.addChild(weapon);
weaponSpawned = true;
}
specialSceneInitialized = true;
}
if (specialScene) {
// Initialize following start time when first entering special scene
if (followingStartTime === null) {
followingStartTime = Date.now();
}
// Spawn deadly trees that follow player for 10 seconds, then disappear
if (LK.ticks % 120 === 0 && deadlyTrees.length < 10 && !allFollowingTreesGone) {
var newDeadlyTree = new DeadlyTree();
newDeadlyTree.x = Math.random() * 2048;
newDeadlyTree.y = -100; // Start above screen
newDeadlyTree.spawnTime = Date.now();
newDeadlyTree.startFollowing();
deadlyTrees.push(newDeadlyTree);
lastIntersectingDeadly.push(false);
game.addChild(newDeadlyTree);
// Set timer to make tree disappear after 10 seconds
LK.setTimeout(function () {
if (newDeadlyTree.parent) {
newDeadlyTree.stopFollowing();
var index = deadlyTrees.indexOf(newDeadlyTree);
if (index !== -1) {
deadlyTrees.splice(index, 1);
lastIntersectingDeadly.splice(index, 1);
}
// Check if all deadly trees are gone
if (deadlyTrees.length === 0 && !allFollowingTreesGone) {
allFollowingTreesGone = true;
// Show message near the SCP-911
var messageText = new Text2('O zaman bunu al', {
size: 80,
fill: 0x8B0000 // Evil dark red
});
messageText.anchor.set(0.5, 0.5);
messageText.x = scp911.x;
messageText.y = scp911.y - 100;
game.addChild(messageText);
// Spawn weapon at random location
if (!weaponSpawned) {
weapon = new Weapon();
weapon.x = Math.random() * (2048 - 200) + 100; // Random X within game bounds
weapon.y = Math.random() * (2732 - 400) + 200; // Random Y within game bounds
game.addChild(weapon);
weaponSpawned = true;
}
}
}
}, 10000);
}
// Check collision with deadly trees
for (var k = deadlyTrees.length - 1; k >= 0; k--) {
var deadlyTree = deadlyTrees[k];
var currentIntersectingDeadly = car.intersects(deadlyTree);
// Check if tree has been alive for 10 seconds
if (deadlyTree.spawnTime && Date.now() - deadlyTree.spawnTime >= 10000) {
deadlyTree.stopFollowing();
deadlyTrees.splice(k, 1);
lastIntersectingDeadly.splice(k, 1);
// Check if all deadly trees are gone
if (deadlyTrees.length === 0 && !allFollowingTreesGone) {
allFollowingTreesGone = true;
// Show message near the SCP-911
var messageText = new Text2('O zaman bunu al', {
size: 80,
fill: 0x8B0000 // Evil dark red
});
messageText.anchor.set(0.5, 0.5);
messageText.x = scp911.x;
messageText.y = scp911.y - 100; // Position above the SCP-911
game.addChild(messageText);
// Spawn weapon at random location
if (!weaponSpawned) {
weapon = new Weapon();
weapon.x = Math.random() * (2048 - 200) + 100; // Random X within game bounds
weapon.y = Math.random() * (2732 - 400) + 200; // Random Y within game bounds
game.addChild(weapon);
weaponSpawned = true;
}
}
continue;
}
// Check if tree went off screen (only for non-following trees)
if (!deadlyTree.isFollowing && deadlyTree.y > 2732 + 100) {
game.removeChild(deadlyTree);
deadlyTrees.splice(k, 1);
lastIntersectingDeadly.splice(k, 1);
continue;
}
// Check collision - instant death
if (!lastIntersectingDeadly[k] && currentIntersectingDeadly) {
// Show jumpscare
var jumpscare = LK.getAsset('scpJumpscare', {
anchorX: 0.5,
anchorY: 0.5
});
jumpscare.x = 2048 / 2;
jumpscare.y = 2732 / 2;
jumpscare.alpha = 0;
jumpscare.scaleX = 0.1;
jumpscare.scaleY = 0.1;
game.addChild(jumpscare);
// Animate jumpscare to full screen with tween
tween(jumpscare, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold jumpscare for a moment then show death message
LK.setTimeout(function () {
// Show death message
var deathText = new Text2('ÖLDÜN!', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
deathText.anchor.set(0.5, 0.5);
deathText.x = 2048 / 2;
deathText.y = 2732 / 2;
game.addChild(deathText);
// Fade out jumpscare and show death message
tween(jumpscare, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
// Show message for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
});
}, 600);
}
});
return;
}
if (k < lastIntersectingDeadly.length) {
lastIntersectingDeadly[k] = currentIntersectingDeadly;
}
}
// Check weapon collision and bullet firing
if (weapon && scp911) {
var currentIntersectingWeapon = car.intersects(weapon);
if (!lastIntersectingWeapon && currentIntersectingWeapon) {
// Car touched weapon, make weapon follow car
weapon.isFollowingCar = true;
// Fire bullet at SCP-911
var newBullet = new Bullet();
newBullet.x = car.x;
newBullet.y = car.y;
newBullet.targetX = scp911.x;
newBullet.targetY = scp911.y;
bullets.push(newBullet);
game.addChild(newBullet);
}
lastIntersectingWeapon = currentIntersectingWeapon;
// Make weapon follow car if attached
if (weapon.isFollowingCar) {
weapon.x = car.x + 60; // Offset to the right of car
weapon.y = car.y;
}
}
// Check bullet collisions with SCP-911
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (scp911 && bullet.intersects && bullet.intersects(scp911)) {
// Bullet hit SCP-911 - reduce health
skeletonHealth -= 5;
updateSkeletonHealthText();
// Remove bullet
game.removeChild(bullet);
bullets.splice(b, 1);
// Flash SCP-911 red when hit
tween(scp911, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(scp911, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
// Check if SCP-911 is defeated
if (skeletonHealth <= 0) {
// Hide health bar
skeletonHealthTxt.visible = false;
// Show victory message
var victoryText = new Text2('SCP-911 Yenildi!', {
size: 200,
fill: 0x4B0082 // Evil dark purple
});
victoryText.anchor.set(0.5, 0.5);
victoryText.x = 2048 / 2;
victoryText.y = 2732 / 2;
game.addChild(victoryText);
// Show victory for 3 seconds then end game
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
}
}
}
// Only run countdown timer logic if not in special scene
if (!specialScene) {
// Check if player hit something (score changed)
var scoreChanged = score !== lastScore;
if (scoreChanged) {
// Player hit something, reset countdown
countdownTimer = 10;
updateCountdownText();
} else {
// Player didn't hit anything, countdown every second (60 ticks = 1 second)
if (LK.ticks % 60 === 0) {
countdownTimer--;
updateCountdownText();
if (countdownTimer <= 0) {
if (score < 10) {
// Show F- grade for low score
var gradeText = new Text2('F-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 10 && score <= 30) {
// Show F+ grade for score between 10 and 30
var gradeText = new Text2('F+', {
size: 300,
fill: 0x4B0082 // Evil dark purple
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 30 && score <= 50) {
// Show D- grade for score between 30 and 50
var gradeText = new Text2('D-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
LK.showGameOver();
}
}
}
}
}
// Only run upward counter logic if not in special scene
if (!specialScene) {
// Upward counter increments every second (60 ticks = 1 second)
if (LK.ticks % 60 === 0) {
upwardCounter++;
updateUpwardCounterText();
if (upwardCounter >= 90) {
if (score < 10) {
// Show F- grade for low score
var gradeText = new Text2('F-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 10 && score <= 30) {
// Show F+ grade for score between 10 and 30
var gradeText = new Text2('F+', {
size: 300,
fill: 0x4B0082 // Evil dark purple
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else if (score >= 30 && score <= 50) {
// Show D- grade for score between 30 and 50
var gradeText = new Text2('D-', {
size: 300,
fill: 0x8B0000 // Evil dark red
});
gradeText.anchor.set(0.5, 0.5);
gradeText.x = 2048 / 2;
gradeText.y = 2732 / 2;
game.addChild(gradeText);
// Show grade for 2 seconds before game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
} else {
LK.showGameOver();
}
}
}
}
// Update tracking variables
lastCarX = car.x;
lastCarY = car.y;
lastScore = score;
};
// --- Initialize score ---
score = 0;
updateScoreText();
// --- Initialize upward counter ---
upwardCounter = 1;
updateUpwardCounterText();
// --- Initialize bottom-left counter ---
bottomLeftCounter = 150;
updateBottomLeftCounterText();
// --- Play SCP-911 music ---
LK.playMusic('SCP-9111');
// --- Initialize tracking variables ---
lastCarX = car.x;
lastCarY = car.y;
lastScore = score;