/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Represents a single collectible in the corridor
var Collectible = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('collectible', {
		anchorX: 0.5,
		anchorY: 1
	});
	self.lane = 1;
	self.depth = 0;
	self.type = 'collectible';
	self.active = true;
	self.getHitbox = function () {
		return {
			x: self.x - sprite.width / 2,
			y: self.y - sprite.height,
			w: sprite.width,
			h: sprite.height
		};
	};
	return self;
});
// Represents a single obstacle in the corridor
var Obstacle = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('obstacle', {
		anchorX: 0.5,
		anchorY: 1
	});
	// Logical position in corridor space
	self.lane = 1; // 0=left, 1=center, 2=right
	self.depth = 0; // Distance from player (z)
	self.type = 'obstacle';
	self.active = true;
	// For collision detection
	self.getHitbox = function () {
		// Returns {x, y, w, h} in screen space
		return {
			x: self.x - sprite.width / 2,
			y: self.y - sprite.height,
			w: sprite.width,
			h: sprite.height
		};
	};
	return self;
});
// Represents a projectile shot by the player
var Projectile = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('btnShoot', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	sprite.tint = 0xff2222;
	self.type = 'projectile';
	self.active = true;
	self.lane = 1;
	self.row = 1;
	self.depth = 0;
	self.speed = 0.7; // how fast the projectile moves forward (z axis)
	self.lastDepth = self.depth;
	self.update = function () {
		self.lastDepth = self.depth;
		self.depth += self.speed;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// --- Scrolling background setup ---
var bg1 = LK.getAsset('ceilingSlice', {
	anchorX: 0.5,
	anchorY: 0
});
var bg2 = LK.getAsset('ceilingSlice', {
	anchorX: 0.5,
	anchorY: 0
});
bg1.width = 2048;
bg1.height = 2732;
bg2.width = 2048;
bg2.height = 2732;
bg1.x = 2048 / 2;
bg2.x = 2048 / 2;
bg1.y = 0;
bg2.y = -2732;
bg1.alpha = 0.18;
bg2.alpha = 0.18;
game.addChild(bg1);
game.addChild(bg2);
// Collectible (ellipse)
// Obstacle (box)
// Ceiling slice (horizontal rectangle, will be stretched/scaled for perspective)
// Floor slice (horizontal rectangle, will be stretched/scaled for perspective)
// Corridor wall slice (vertical rectangle, will be stretched/scaled for perspective)
// --- Raycasting & Corridor Parameters ---
var corridor = {
	width: 3,
	height: 2,
	// 2 rows: 0=top, 1=bottom
	// 6 lanes: (0,0)=top-left, (1,0)=top-center, (2,0)=top-right, (0,1)=bottom-left, (1,1)=bottom-center, (2,1)=bottom-right
	laneWidth: 700,
	rowHeight: 900,
	// vertical distance between rows (was 600, now lower lane is much lower)
	// vertical distance between rows
	depthSlices: 16,
	maxDepth: 16,
	fov: Math.PI / 3,
	wallColor: 0x4444aa,
	floorColor: 0x222222,
	ceilingColor: 0x111133
};
// Player state
var player = {
	lane: 1,
	// 0=left, 1=center, 2=right
	row: 1,
	// 0=top, 1=bottom
	moving: false,
	moveTarget: 1,
	moveRowTarget: 1,
	moveTween: null,
	moveRowTween: null,
	alive: true
};
// Input delay state
var inputDelayActive = false;
var inputDelayTimer = null;
var inputDelayMs = 180; // ms, matches tween duration
// Game state
var speed = 0.18; // units per frame (z axis)
var speedIncrease = 0.00004; // per frame
var maxSpeed = 0.38;
var objects = []; // all obstacles and collectibles
var spawnTimer = 0;
var spawnInterval = 38; // frames between spawns
var score = 0;
var distance = 0; // how far player has run (for difficulty)
var lastTouchX = null;
// --- GUI ---
var scoreTxt = new Text2('0', {
	size: 220,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var distTxt = new Text2('0m', {
	size: 120,
	fill: "#aaa"
});
distTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(distTxt);
distTxt.y = 220;
// --- Corridor rendering nodes ---
var wallNodes = [];
var floorNodes = [];
var ceilingNodes = [];
for (var i = 0; i < corridor.depthSlices; i++) {
	// Left wall
	var wl = LK.getAsset('wallSlice', {
		anchorX: 0.5,
		anchorY: 1
	});
	game.addChild(wl);
	wallNodes.push(wl);
	// Right wall
	var wr = LK.getAsset('wallSlice', {
		anchorX: 0.5,
		anchorY: 1
	});
	game.addChild(wr);
	wallNodes.push(wr);
	// Floor
	var fl = LK.getAsset('floorSlice', {
		anchorX: 0.5,
		anchorY: 0
	});
	game.addChild(fl);
	floorNodes.push(fl);
	// Ceiling
	var cl = LK.getAsset('ceilingSlice', {
		anchorX: 0.5,
		anchorY: 1
	});
	game.addChild(cl);
	ceilingNodes.push(cl);
}
// --- Helper: Perspective projection for fake-3D ---
function projectZ(z) {
	// Returns scale and y offset for a given depth (z)
	// Camera is at y = horizonY, looking down corridor
	var screenH = 2732;
	var horizonY = screenH * 0.42;
	var vanishY = horizonY;
	var baseScale = 1.0;
	// Perspective: scale = nearPlane / (z + nearPlane)
	var near = 1.2;
	var scale = near / (z + near);
	var y = vanishY + screenH * 0.32 / (z + 0.7);
	return {
		scale: scale,
		y: y
	};
}
// --- Helper: X position for lane at given depth ---
function laneX(lane, z) {
	var centerX = 2048 / 2;
	var laneOffset = (lane - 1) * corridor.laneWidth;
	var p = projectZ(z);
	return centerX + laneOffset * p.scale;
}
// --- Helper: Y position for row at given depth ---
function laneY(row, z) {
	// row: 0=top, 1=bottom
	var baseY = 0;
	var rowOffset = (row - 0.5) * corridor.rowHeight;
	var p = projectZ(z);
	return baseY + rowOffset * p.scale;
}
// --- Spawning obstacles and collectibles ---
function spawnObject() {
	// Randomly choose obstacle or collectible
	var isObstacle = Math.random() < 0.7;
	var lane = Math.floor(Math.random() * corridor.width);
	var row = Math.floor(Math.random() * corridor.height);
	var depth = corridor.maxDepth + 2; // spawn just beyond farthest slice
	var obj;
	if (isObstacle) {
		obj = new Obstacle();
	} else {
		obj = new Collectible();
	}
	obj.lane = lane;
	obj.row = row;
	obj.depth = depth;
	obj.active = true;
	objects.push(obj);
	game.addChild(obj);
}
// --- On-screen button controls for movement ---
var btnSize = 340;
var btnMargin = 0; // Make buttons much closer together (no margin at all)
var btnAlpha = 0.92;
// Create button assets
var btnUp = LK.getAsset('btnUp', {
	anchorX: 0.5,
	anchorY: 0.5
});
var btnDown = LK.getAsset('btnDown', {
	anchorX: 0.5,
	anchorY: 0.5
});
var btnLeft = LK.getAsset('btnLeft', {
	anchorX: 0.5,
	anchorY: 0.5
});
var btnRight = LK.getAsset('btnRight', {
	anchorX: 0.5,
	anchorY: 0.5
});
// Set button sizes and alpha
btnUp.width = btnUp.height = btnSize;
btnDown.width = btnDown.height = btnSize;
btnLeft.width = btnLeft.height = btnSize;
btnRight.width = btnRight.height = btnSize;
btnUp.alpha = btnDown.alpha = btnLeft.alpha = btnRight.alpha = btnAlpha;
// Position buttons in a diamond at the bottom left of the screen, avoiding the top left 100x100 area
// Move the buttons further up and to the right
var btnYBase = 2732 - btnSize - btnMargin - 120; // move up by 120px (was 40)
var btnXBase = btnMargin + btnSize + 140; // move right by 140px (was 180)
btnUp.x = btnXBase;
btnUp.y = btnYBase - btnSize - btnMargin;
btnDown.x = btnXBase;
btnDown.y = btnYBase + btnSize + btnMargin;
btnLeft.x = btnXBase - btnSize - btnMargin;
btnLeft.y = btnYBase;
btnRight.x = btnXBase + btnSize + btnMargin;
btnRight.y = btnYBase;
// Add buttons to game
game.addChild(btnUp);
game.addChild(btnDown);
game.addChild(btnLeft);
game.addChild(btnRight);
// --- Shoot button setup ---
var btnShoot = LK.getAsset('btnShoot', {
	anchorX: 0.5,
	anchorY: 0.5
});
btnShoot.width = btnShoot.height = btnSize;
btnShoot.alpha = btnAlpha;
// Place shoot button even further to the right of the right button, with a slightly larger margin
btnShoot.x = btnRight.x + btnSize + btnMargin + 240;
btnShoot.y = btnRight.y;
game.addChild(btnShoot);
// Button event handlers
btnUp.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.row > 0) {
		movePlayerTo(player.lane, player.row - 1);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
btnDown.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.row < 1) {
		movePlayerTo(player.lane, player.row + 1);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
btnLeft.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.lane > 0) {
		movePlayerTo(player.lane - 1, player.row);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
btnRight.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.lane < 2) {
		movePlayerTo(player.lane + 1, player.row);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
// --- Projectiles array ---
var projectiles = [];
// --- Shoot button event handler ---
btnShoot.down = function (x, y, obj) {
	if (!player.alive) return;
	// Create a new projectile at the player's current lane/row, just in front of the player
	var proj = new Projectile();
	proj.lane = player.lane;
	proj.row = player.row;
	proj.depth = 0.7; // just in front of player
	proj.lastDepth = proj.depth;
	projectiles.push(proj);
	game.addChild(proj);
};
// Disable tap-to-move and swipe
game.down = function (x, y, obj) {};
game.move = function (x, y, obj) {};
game.up = function (x, y, obj) {};
// --- Move player to lane with tween ---
function movePlayerTo(targetLane, targetRow) {
	if (player.moving || !player.alive) return;
	if (typeof targetRow === "undefined") targetRow = player.row;
	player.moveTarget = targetLane;
	player.moveRowTarget = targetRow;
	player.moving = true;
	// Activate input delay immediately on move
	inputDelayActive = true;
	if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
	inputDelayTimer = LK.setTimeout(function () {
		inputDelayActive = false;
		inputDelayTimer = null;
	}, inputDelayMs);
	var startLane = player.lane;
	var endLane = targetLane;
	var startRow = player.row;
	var endRow = targetRow;
	var t = {
		v: startLane
	};
	var tr = {
		v: startRow
	};
	player.moveTween = tween(t, {
		v: endLane
	}, {
		duration: 180,
		easing: tween.cubicOut,
		onFinish: function onFinish() {
			player.lane = endLane;
			player.moving = false;
			// If a move was queued during the tween, perform it now
			if (typeof player.queuedMove !== "undefined" && player.queuedMove !== null && (player.queuedMove !== player.lane || player.queuedRowMove !== player.row)) {
				var nextLane = player.queuedMove;
				var nextRow = player.queuedRowMove;
				player.queuedMove = null;
				player.queuedRowMove = null;
				movePlayerTo(nextLane, nextRow);
			}
		}
	});
	player.moveRowTween = tween(tr, {
		v: endRow
	}, {
		duration: 180,
		easing: tween.cubicOut,
		onFinish: function onFinish() {
			player.row = endRow;
		}
	});
	// We'll interpolate player.lane and player.row visually in render
	player.laneTweenObj = t;
	player.rowTweenObj = tr;
}
// --- Main update loop ---
game.update = function () {
	// --- Scroll background ---
	var bgScrollSpeed = speed * 2732 * 0.18; // scale to match corridor speed visually
	bg1.y += bgScrollSpeed;
	bg2.y += bgScrollSpeed;
	if (bg1.y >= 2732) bg1.y = bg2.y - 2732;
	if (bg2.y >= 2732) bg2.y = bg1.y - 2732;
	if (!player.alive) return;
	// Increase speed over time, and also based on collectibles collected (score)
	var speedFromCollectibles = 0.018 * score; // Each collectible increases speed by 0.018
	speed += speedIncrease;
	speed = 0.18 + speedFromCollectibles + (speed - 0.18); // base + collectibles + time
	if (speed > maxSpeed) speed = maxSpeed;
	distance += speed;
	distTxt.setText(Math.floor(distance) + "m");
	// Spawn new objects
	spawnTimer--;
	if (spawnTimer <= 0) {
		spawnObject();
		spawnTimer = spawnInterval - Math.floor(distance / 60);
		if (spawnTimer < 18) spawnTimer = 18;
	}
	// Move objects closer (decrease depth)
	for (var i = objects.length - 1; i >= 0; i--) {
		var obj = objects[i];
		obj.depth -= speed;
		if (obj.depth < 0.2) {
			// Passed player, remove
			obj.destroy();
			objects.splice(i, 1);
			continue;
		}
		// Project to screen
		var p = projectZ(obj.depth);
		obj.scaleX = obj.scaleY = p.scale * 1.2;
		if (typeof obj.row === "undefined") obj.row = 1;
		obj.x = laneX(obj.lane, obj.depth);
		obj.y = p.y + 320 * p.scale + laneY(obj.row, obj.depth);
		obj.visible = obj.depth > 0.2 && obj.depth < corridor.maxDepth + 2;
	}
	// Player lane interpolation (for smooth movement)
	var visLane = player.lane;
	var visRow = player.row;
	if (player.moving && player.laneTweenObj) {
		visLane = player.laneTweenObj.v;
	}
	if (player.moving && player.rowTweenObj) {
		visRow = player.rowTweenObj.v;
	}
	// --- Render corridor slices ---
	for (var i = 0; i < corridor.depthSlices; i++) {
		// Offset z by distance to make walls/floor/ceiling scroll opposite to objects
		var z = i + 1.2 - distance % 1; // reverse scroll direction
		var p = projectZ(z);
		// Walls
		var wallW = 80 * p.scale * 2.2;
		var wallH = 800 * p.scale * 2.2;
		// Left wall
		var wl = wallNodes[i * 2];
		wl.x = laneX(0, z) - corridor.laneWidth * p.scale / 2 - wallW / 2;
		wl.y = p.y + wallH;
		wl.width = wallW;
		wl.height = wallH;
		wl.visible = true;
		// Right wall
		var wr = wallNodes[i * 2 + 1];
		wr.x = laneX(2, z) + corridor.laneWidth * p.scale / 2 + wallW / 2;
		wr.y = p.y + wallH;
		wr.width = wallW;
		wr.height = wallH;
		wr.visible = true;
		// Floor
		var fl = floorNodes[i];
		fl.x = 2048 / 2;
		fl.y = p.y + wallH;
		fl.width = corridor.laneWidth * corridor.width * p.scale * 1.1;
		fl.height = 80 * p.scale * 2.2;
		fl.visible = true;
		// Ceiling
		var cl = ceilingNodes[i];
		cl.x = 2048 / 2;
		cl.y = p.y - 80 * p.scale * 1.2;
		cl.width = corridor.laneWidth * corridor.width * p.scale * 1.1;
		cl.height = 80 * p.scale * 2.2;
		cl.visible = true;
	}
	// --- Collision detection ---
	// Player is always at z=0, y=bottom of screen
	var playerZ = 0.7;
	var playerP = projectZ(playerZ);
	var playerX = laneX(visLane, playerZ);
	var playerY = playerP.y + 320 * playerP.scale + laneY(visRow, playerZ);
	var playerW = 340 * playerP.scale * 1.2;
	var playerH = 420 * playerP.scale * 1.2;
	// --- Render player visual representation ---
	if (typeof player.visual === "undefined") {
		// Create the player visual using a new asset, anchor center
		player.visual = LK.getAsset('player', {
			anchorX: 0.5,
			anchorY: 1
		});
		player.visual.width = 340;
		player.visual.height = 420;
		player.visual.alpha = 0.98;
		game.addChild(player.visual);
	}
	player.visual.visible = player.alive;
	player.visual.x = playerX;
	player.visual.y = playerY;
	player.visual.scaleX = playerW / player.visual.width;
	player.visual.scaleY = playerH / player.visual.height;
	// Move player visual behind the movement buttons in the display list
	if (player.visual.parent && player.visual.parent.children) {
		var parent = player.visual.parent;
		// Remove player.visual if it exists
		if (parent) {
			parent.removeChild(player.visual);
			// Find the lowest index of the button visuals
			var btns = [btnUp, btnDown, btnLeft, btnRight];
			var minBtnIdx = parent.children.length;
			for (var i = 0; i < btns.length; i++) {
				var idx = parent.children.indexOf(btns[i]);
				if (idx !== -1 && idx < minBtnIdx) minBtnIdx = idx;
			}
			// Insert player.visual just before the first button, or at the end if not found
			if (minBtnIdx < parent.children.length) {
				parent.addChildAt(player.visual, minBtnIdx);
			} else {
				parent.addChild(player.visual);
			}
		}
	}
	;
	// --- Projectiles update/render/collision ---
	for (var pi = projectiles.length - 1; pi >= 0; pi--) {
		var proj = projectiles[pi];
		proj.update();
		// Project to screen
		var pz = projectZ(proj.depth);
		proj.scaleX = proj.scaleY = pz.scale * 1.3;
		proj.x = laneX(proj.lane, proj.depth);
		proj.y = pz.y + 320 * pz.scale + laneY(proj.row, proj.depth);
		proj.visible = proj.depth > 0.2 && proj.depth < corridor.maxDepth + 2;
		// Remove projectile if out of range
		if (proj.depth > corridor.maxDepth + 2) {
			proj.destroy();
			projectiles.splice(pi, 1);
			continue;
		}
		// Check collision with obstacles
		for (var oi = objects.length - 1; oi >= 0; oi--) {
			var obj = objects[oi];
			if (!obj.active) continue;
			if (obj.type !== 'obstacle') continue;
			// Only check collision if close enough in depth
			if (Math.abs(obj.depth - proj.depth) < 0.7 && obj.lane === proj.lane && obj.row === proj.row) {
				// Remove both projectile and obstacle
				obj.active = false;
				tween(obj, {
					scaleX: 2.2,
					scaleY: 2.2,
					alpha: 0
				}, {
					duration: 180,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						obj.destroy();
					}
				});
				objects.splice(oi, 1);
				proj.destroy();
				projectiles.splice(pi, 1);
				break;
			}
		}
	}
	// For debugging, could render a player shadow here
	for (var i = objects.length - 1; i >= 0; i--) {
		var obj = objects[i];
		if (!obj.active) continue;
		// Only check collision if close enough
		if (obj.depth < 1.2 && obj.depth > 0.2) {
			var dx = Math.abs(obj.x - playerX);
			var dy = Math.abs(obj.y - playerY);
			var hitW = (playerW + obj.width) / 2;
			var hitH = (playerH + obj.height) / 2;
			if (dx < hitW * 0.7 && dy < hitH * 0.7) {
				if (obj.type === 'obstacle') {
					// Hit obstacle: game over
					player.alive = false;
					LK.effects.flashScreen(0xff0000, 900);
					LK.showGameOver();
					return;
				} else if (obj.type === 'collectible') {
					// Collect item
					obj.active = false;
					score += 1;
					LK.setScore(score);
					scoreTxt.setText(score);
					tween(obj, {
						scaleX: 2.2,
						scaleY: 2.2,
						alpha: 0
					}, {
						duration: 220,
						easing: tween.cubicOut,
						onFinish: function onFinish() {
							obj.destroy();
						}
					});
					objects.splice(i, 1);
				}
			}
		}
	}
};
// --- Reset game state on new game ---
function resetGame() {
	// Remove all objects
	for (var i = 0; i < objects.length; i++) {
		objects[i].destroy();
	}
	objects = [];
	player.lane = 1;
	player.row = 1;
	player.moving = false;
	player.moveTarget = 1;
	player.moveRowTarget = 1;
	player.moveTween = null;
	player.moveRowTween = null;
	player.queuedMove = null;
	player.queuedRowMove = null;
	player.alive = true;
	speed = 0.18;
	score = 0;
	distance = 0;
	spawnTimer = 18;
	scoreTxt.setText('0');
	distTxt.setText('0m');
	// Reset input delay state
	inputDelayActive = false;
	if (inputDelayTimer !== null) {
		LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = null;
	}
}
resetGame();
// Play background music at game start
LK.playMusic('Music');
// --- LK handles game over and reset automatically, but listen for new game start ---
LK.on('gameStart', function () {
	resetGame();
	LK.playMusic('Music');
}); /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Represents a single collectible in the corridor
var Collectible = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('collectible', {
		anchorX: 0.5,
		anchorY: 1
	});
	self.lane = 1;
	self.depth = 0;
	self.type = 'collectible';
	self.active = true;
	self.getHitbox = function () {
		return {
			x: self.x - sprite.width / 2,
			y: self.y - sprite.height,
			w: sprite.width,
			h: sprite.height
		};
	};
	return self;
});
// Represents a single obstacle in the corridor
var Obstacle = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('obstacle', {
		anchorX: 0.5,
		anchorY: 1
	});
	// Logical position in corridor space
	self.lane = 1; // 0=left, 1=center, 2=right
	self.depth = 0; // Distance from player (z)
	self.type = 'obstacle';
	self.active = true;
	// For collision detection
	self.getHitbox = function () {
		// Returns {x, y, w, h} in screen space
		return {
			x: self.x - sprite.width / 2,
			y: self.y - sprite.height,
			w: sprite.width,
			h: sprite.height
		};
	};
	return self;
});
// Represents a projectile shot by the player
var Projectile = Container.expand(function () {
	var self = Container.call(this);
	var sprite = self.attachAsset('btnShoot', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	sprite.tint = 0xff2222;
	self.type = 'projectile';
	self.active = true;
	self.lane = 1;
	self.row = 1;
	self.depth = 0;
	self.speed = 0.7; // how fast the projectile moves forward (z axis)
	self.lastDepth = self.depth;
	self.update = function () {
		self.lastDepth = self.depth;
		self.depth += self.speed;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// --- Scrolling background setup ---
var bg1 = LK.getAsset('ceilingSlice', {
	anchorX: 0.5,
	anchorY: 0
});
var bg2 = LK.getAsset('ceilingSlice', {
	anchorX: 0.5,
	anchorY: 0
});
bg1.width = 2048;
bg1.height = 2732;
bg2.width = 2048;
bg2.height = 2732;
bg1.x = 2048 / 2;
bg2.x = 2048 / 2;
bg1.y = 0;
bg2.y = -2732;
bg1.alpha = 0.18;
bg2.alpha = 0.18;
game.addChild(bg1);
game.addChild(bg2);
// Collectible (ellipse)
// Obstacle (box)
// Ceiling slice (horizontal rectangle, will be stretched/scaled for perspective)
// Floor slice (horizontal rectangle, will be stretched/scaled for perspective)
// Corridor wall slice (vertical rectangle, will be stretched/scaled for perspective)
// --- Raycasting & Corridor Parameters ---
var corridor = {
	width: 3,
	height: 2,
	// 2 rows: 0=top, 1=bottom
	// 6 lanes: (0,0)=top-left, (1,0)=top-center, (2,0)=top-right, (0,1)=bottom-left, (1,1)=bottom-center, (2,1)=bottom-right
	laneWidth: 700,
	rowHeight: 900,
	// vertical distance between rows (was 600, now lower lane is much lower)
	// vertical distance between rows
	depthSlices: 16,
	maxDepth: 16,
	fov: Math.PI / 3,
	wallColor: 0x4444aa,
	floorColor: 0x222222,
	ceilingColor: 0x111133
};
// Player state
var player = {
	lane: 1,
	// 0=left, 1=center, 2=right
	row: 1,
	// 0=top, 1=bottom
	moving: false,
	moveTarget: 1,
	moveRowTarget: 1,
	moveTween: null,
	moveRowTween: null,
	alive: true
};
// Input delay state
var inputDelayActive = false;
var inputDelayTimer = null;
var inputDelayMs = 180; // ms, matches tween duration
// Game state
var speed = 0.18; // units per frame (z axis)
var speedIncrease = 0.00004; // per frame
var maxSpeed = 0.38;
var objects = []; // all obstacles and collectibles
var spawnTimer = 0;
var spawnInterval = 38; // frames between spawns
var score = 0;
var distance = 0; // how far player has run (for difficulty)
var lastTouchX = null;
// --- GUI ---
var scoreTxt = new Text2('0', {
	size: 220,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var distTxt = new Text2('0m', {
	size: 120,
	fill: "#aaa"
});
distTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(distTxt);
distTxt.y = 220;
// --- Corridor rendering nodes ---
var wallNodes = [];
var floorNodes = [];
var ceilingNodes = [];
for (var i = 0; i < corridor.depthSlices; i++) {
	// Left wall
	var wl = LK.getAsset('wallSlice', {
		anchorX: 0.5,
		anchorY: 1
	});
	game.addChild(wl);
	wallNodes.push(wl);
	// Right wall
	var wr = LK.getAsset('wallSlice', {
		anchorX: 0.5,
		anchorY: 1
	});
	game.addChild(wr);
	wallNodes.push(wr);
	// Floor
	var fl = LK.getAsset('floorSlice', {
		anchorX: 0.5,
		anchorY: 0
	});
	game.addChild(fl);
	floorNodes.push(fl);
	// Ceiling
	var cl = LK.getAsset('ceilingSlice', {
		anchorX: 0.5,
		anchorY: 1
	});
	game.addChild(cl);
	ceilingNodes.push(cl);
}
// --- Helper: Perspective projection for fake-3D ---
function projectZ(z) {
	// Returns scale and y offset for a given depth (z)
	// Camera is at y = horizonY, looking down corridor
	var screenH = 2732;
	var horizonY = screenH * 0.42;
	var vanishY = horizonY;
	var baseScale = 1.0;
	// Perspective: scale = nearPlane / (z + nearPlane)
	var near = 1.2;
	var scale = near / (z + near);
	var y = vanishY + screenH * 0.32 / (z + 0.7);
	return {
		scale: scale,
		y: y
	};
}
// --- Helper: X position for lane at given depth ---
function laneX(lane, z) {
	var centerX = 2048 / 2;
	var laneOffset = (lane - 1) * corridor.laneWidth;
	var p = projectZ(z);
	return centerX + laneOffset * p.scale;
}
// --- Helper: Y position for row at given depth ---
function laneY(row, z) {
	// row: 0=top, 1=bottom
	var baseY = 0;
	var rowOffset = (row - 0.5) * corridor.rowHeight;
	var p = projectZ(z);
	return baseY + rowOffset * p.scale;
}
// --- Spawning obstacles and collectibles ---
function spawnObject() {
	// Randomly choose obstacle or collectible
	var isObstacle = Math.random() < 0.7;
	var lane = Math.floor(Math.random() * corridor.width);
	var row = Math.floor(Math.random() * corridor.height);
	var depth = corridor.maxDepth + 2; // spawn just beyond farthest slice
	var obj;
	if (isObstacle) {
		obj = new Obstacle();
	} else {
		obj = new Collectible();
	}
	obj.lane = lane;
	obj.row = row;
	obj.depth = depth;
	obj.active = true;
	objects.push(obj);
	game.addChild(obj);
}
// --- On-screen button controls for movement ---
var btnSize = 340;
var btnMargin = 0; // Make buttons much closer together (no margin at all)
var btnAlpha = 0.92;
// Create button assets
var btnUp = LK.getAsset('btnUp', {
	anchorX: 0.5,
	anchorY: 0.5
});
var btnDown = LK.getAsset('btnDown', {
	anchorX: 0.5,
	anchorY: 0.5
});
var btnLeft = LK.getAsset('btnLeft', {
	anchorX: 0.5,
	anchorY: 0.5
});
var btnRight = LK.getAsset('btnRight', {
	anchorX: 0.5,
	anchorY: 0.5
});
// Set button sizes and alpha
btnUp.width = btnUp.height = btnSize;
btnDown.width = btnDown.height = btnSize;
btnLeft.width = btnLeft.height = btnSize;
btnRight.width = btnRight.height = btnSize;
btnUp.alpha = btnDown.alpha = btnLeft.alpha = btnRight.alpha = btnAlpha;
// Position buttons in a diamond at the bottom left of the screen, avoiding the top left 100x100 area
// Move the buttons further up and to the right
var btnYBase = 2732 - btnSize - btnMargin - 120; // move up by 120px (was 40)
var btnXBase = btnMargin + btnSize + 140; // move right by 140px (was 180)
btnUp.x = btnXBase;
btnUp.y = btnYBase - btnSize - btnMargin;
btnDown.x = btnXBase;
btnDown.y = btnYBase + btnSize + btnMargin;
btnLeft.x = btnXBase - btnSize - btnMargin;
btnLeft.y = btnYBase;
btnRight.x = btnXBase + btnSize + btnMargin;
btnRight.y = btnYBase;
// Add buttons to game
game.addChild(btnUp);
game.addChild(btnDown);
game.addChild(btnLeft);
game.addChild(btnRight);
// --- Shoot button setup ---
var btnShoot = LK.getAsset('btnShoot', {
	anchorX: 0.5,
	anchorY: 0.5
});
btnShoot.width = btnShoot.height = btnSize;
btnShoot.alpha = btnAlpha;
// Place shoot button even further to the right of the right button, with a slightly larger margin
btnShoot.x = btnRight.x + btnSize + btnMargin + 240;
btnShoot.y = btnRight.y;
game.addChild(btnShoot);
// Button event handlers
btnUp.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.row > 0) {
		movePlayerTo(player.lane, player.row - 1);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
btnDown.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.row < 1) {
		movePlayerTo(player.lane, player.row + 1);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
btnLeft.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.lane > 0) {
		movePlayerTo(player.lane - 1, player.row);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
btnRight.down = function (x, y, obj) {
	if (!player.moving && player.alive && !inputDelayActive && player.lane < 2) {
		movePlayerTo(player.lane + 1, player.row);
		inputDelayActive = true;
		if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = LK.setTimeout(function () {
			inputDelayActive = false;
			inputDelayTimer = null;
		}, inputDelayMs);
	}
};
// --- Projectiles array ---
var projectiles = [];
// --- Shoot button event handler ---
btnShoot.down = function (x, y, obj) {
	if (!player.alive) return;
	// Create a new projectile at the player's current lane/row, just in front of the player
	var proj = new Projectile();
	proj.lane = player.lane;
	proj.row = player.row;
	proj.depth = 0.7; // just in front of player
	proj.lastDepth = proj.depth;
	projectiles.push(proj);
	game.addChild(proj);
};
// Disable tap-to-move and swipe
game.down = function (x, y, obj) {};
game.move = function (x, y, obj) {};
game.up = function (x, y, obj) {};
// --- Move player to lane with tween ---
function movePlayerTo(targetLane, targetRow) {
	if (player.moving || !player.alive) return;
	if (typeof targetRow === "undefined") targetRow = player.row;
	player.moveTarget = targetLane;
	player.moveRowTarget = targetRow;
	player.moving = true;
	// Activate input delay immediately on move
	inputDelayActive = true;
	if (inputDelayTimer !== null) LK.clearTimeout(inputDelayTimer);
	inputDelayTimer = LK.setTimeout(function () {
		inputDelayActive = false;
		inputDelayTimer = null;
	}, inputDelayMs);
	var startLane = player.lane;
	var endLane = targetLane;
	var startRow = player.row;
	var endRow = targetRow;
	var t = {
		v: startLane
	};
	var tr = {
		v: startRow
	};
	player.moveTween = tween(t, {
		v: endLane
	}, {
		duration: 180,
		easing: tween.cubicOut,
		onFinish: function onFinish() {
			player.lane = endLane;
			player.moving = false;
			// If a move was queued during the tween, perform it now
			if (typeof player.queuedMove !== "undefined" && player.queuedMove !== null && (player.queuedMove !== player.lane || player.queuedRowMove !== player.row)) {
				var nextLane = player.queuedMove;
				var nextRow = player.queuedRowMove;
				player.queuedMove = null;
				player.queuedRowMove = null;
				movePlayerTo(nextLane, nextRow);
			}
		}
	});
	player.moveRowTween = tween(tr, {
		v: endRow
	}, {
		duration: 180,
		easing: tween.cubicOut,
		onFinish: function onFinish() {
			player.row = endRow;
		}
	});
	// We'll interpolate player.lane and player.row visually in render
	player.laneTweenObj = t;
	player.rowTweenObj = tr;
}
// --- Main update loop ---
game.update = function () {
	// --- Scroll background ---
	var bgScrollSpeed = speed * 2732 * 0.18; // scale to match corridor speed visually
	bg1.y += bgScrollSpeed;
	bg2.y += bgScrollSpeed;
	if (bg1.y >= 2732) bg1.y = bg2.y - 2732;
	if (bg2.y >= 2732) bg2.y = bg1.y - 2732;
	if (!player.alive) return;
	// Increase speed over time, and also based on collectibles collected (score)
	var speedFromCollectibles = 0.018 * score; // Each collectible increases speed by 0.018
	speed += speedIncrease;
	speed = 0.18 + speedFromCollectibles + (speed - 0.18); // base + collectibles + time
	if (speed > maxSpeed) speed = maxSpeed;
	distance += speed;
	distTxt.setText(Math.floor(distance) + "m");
	// Spawn new objects
	spawnTimer--;
	if (spawnTimer <= 0) {
		spawnObject();
		spawnTimer = spawnInterval - Math.floor(distance / 60);
		if (spawnTimer < 18) spawnTimer = 18;
	}
	// Move objects closer (decrease depth)
	for (var i = objects.length - 1; i >= 0; i--) {
		var obj = objects[i];
		obj.depth -= speed;
		if (obj.depth < 0.2) {
			// Passed player, remove
			obj.destroy();
			objects.splice(i, 1);
			continue;
		}
		// Project to screen
		var p = projectZ(obj.depth);
		obj.scaleX = obj.scaleY = p.scale * 1.2;
		if (typeof obj.row === "undefined") obj.row = 1;
		obj.x = laneX(obj.lane, obj.depth);
		obj.y = p.y + 320 * p.scale + laneY(obj.row, obj.depth);
		obj.visible = obj.depth > 0.2 && obj.depth < corridor.maxDepth + 2;
	}
	// Player lane interpolation (for smooth movement)
	var visLane = player.lane;
	var visRow = player.row;
	if (player.moving && player.laneTweenObj) {
		visLane = player.laneTweenObj.v;
	}
	if (player.moving && player.rowTweenObj) {
		visRow = player.rowTweenObj.v;
	}
	// --- Render corridor slices ---
	for (var i = 0; i < corridor.depthSlices; i++) {
		// Offset z by distance to make walls/floor/ceiling scroll opposite to objects
		var z = i + 1.2 - distance % 1; // reverse scroll direction
		var p = projectZ(z);
		// Walls
		var wallW = 80 * p.scale * 2.2;
		var wallH = 800 * p.scale * 2.2;
		// Left wall
		var wl = wallNodes[i * 2];
		wl.x = laneX(0, z) - corridor.laneWidth * p.scale / 2 - wallW / 2;
		wl.y = p.y + wallH;
		wl.width = wallW;
		wl.height = wallH;
		wl.visible = true;
		// Right wall
		var wr = wallNodes[i * 2 + 1];
		wr.x = laneX(2, z) + corridor.laneWidth * p.scale / 2 + wallW / 2;
		wr.y = p.y + wallH;
		wr.width = wallW;
		wr.height = wallH;
		wr.visible = true;
		// Floor
		var fl = floorNodes[i];
		fl.x = 2048 / 2;
		fl.y = p.y + wallH;
		fl.width = corridor.laneWidth * corridor.width * p.scale * 1.1;
		fl.height = 80 * p.scale * 2.2;
		fl.visible = true;
		// Ceiling
		var cl = ceilingNodes[i];
		cl.x = 2048 / 2;
		cl.y = p.y - 80 * p.scale * 1.2;
		cl.width = corridor.laneWidth * corridor.width * p.scale * 1.1;
		cl.height = 80 * p.scale * 2.2;
		cl.visible = true;
	}
	// --- Collision detection ---
	// Player is always at z=0, y=bottom of screen
	var playerZ = 0.7;
	var playerP = projectZ(playerZ);
	var playerX = laneX(visLane, playerZ);
	var playerY = playerP.y + 320 * playerP.scale + laneY(visRow, playerZ);
	var playerW = 340 * playerP.scale * 1.2;
	var playerH = 420 * playerP.scale * 1.2;
	// --- Render player visual representation ---
	if (typeof player.visual === "undefined") {
		// Create the player visual using a new asset, anchor center
		player.visual = LK.getAsset('player', {
			anchorX: 0.5,
			anchorY: 1
		});
		player.visual.width = 340;
		player.visual.height = 420;
		player.visual.alpha = 0.98;
		game.addChild(player.visual);
	}
	player.visual.visible = player.alive;
	player.visual.x = playerX;
	player.visual.y = playerY;
	player.visual.scaleX = playerW / player.visual.width;
	player.visual.scaleY = playerH / player.visual.height;
	// Move player visual behind the movement buttons in the display list
	if (player.visual.parent && player.visual.parent.children) {
		var parent = player.visual.parent;
		// Remove player.visual if it exists
		if (parent) {
			parent.removeChild(player.visual);
			// Find the lowest index of the button visuals
			var btns = [btnUp, btnDown, btnLeft, btnRight];
			var minBtnIdx = parent.children.length;
			for (var i = 0; i < btns.length; i++) {
				var idx = parent.children.indexOf(btns[i]);
				if (idx !== -1 && idx < minBtnIdx) minBtnIdx = idx;
			}
			// Insert player.visual just before the first button, or at the end if not found
			if (minBtnIdx < parent.children.length) {
				parent.addChildAt(player.visual, minBtnIdx);
			} else {
				parent.addChild(player.visual);
			}
		}
	}
	;
	// --- Projectiles update/render/collision ---
	for (var pi = projectiles.length - 1; pi >= 0; pi--) {
		var proj = projectiles[pi];
		proj.update();
		// Project to screen
		var pz = projectZ(proj.depth);
		proj.scaleX = proj.scaleY = pz.scale * 1.3;
		proj.x = laneX(proj.lane, proj.depth);
		proj.y = pz.y + 320 * pz.scale + laneY(proj.row, proj.depth);
		proj.visible = proj.depth > 0.2 && proj.depth < corridor.maxDepth + 2;
		// Remove projectile if out of range
		if (proj.depth > corridor.maxDepth + 2) {
			proj.destroy();
			projectiles.splice(pi, 1);
			continue;
		}
		// Check collision with obstacles
		for (var oi = objects.length - 1; oi >= 0; oi--) {
			var obj = objects[oi];
			if (!obj.active) continue;
			if (obj.type !== 'obstacle') continue;
			// Only check collision if close enough in depth
			if (Math.abs(obj.depth - proj.depth) < 0.7 && obj.lane === proj.lane && obj.row === proj.row) {
				// Remove both projectile and obstacle
				obj.active = false;
				tween(obj, {
					scaleX: 2.2,
					scaleY: 2.2,
					alpha: 0
				}, {
					duration: 180,
					easing: tween.cubicOut,
					onFinish: function onFinish() {
						obj.destroy();
					}
				});
				objects.splice(oi, 1);
				proj.destroy();
				projectiles.splice(pi, 1);
				break;
			}
		}
	}
	// For debugging, could render a player shadow here
	for (var i = objects.length - 1; i >= 0; i--) {
		var obj = objects[i];
		if (!obj.active) continue;
		// Only check collision if close enough
		if (obj.depth < 1.2 && obj.depth > 0.2) {
			var dx = Math.abs(obj.x - playerX);
			var dy = Math.abs(obj.y - playerY);
			var hitW = (playerW + obj.width) / 2;
			var hitH = (playerH + obj.height) / 2;
			if (dx < hitW * 0.7 && dy < hitH * 0.7) {
				if (obj.type === 'obstacle') {
					// Hit obstacle: game over
					player.alive = false;
					LK.effects.flashScreen(0xff0000, 900);
					LK.showGameOver();
					return;
				} else if (obj.type === 'collectible') {
					// Collect item
					obj.active = false;
					score += 1;
					LK.setScore(score);
					scoreTxt.setText(score);
					tween(obj, {
						scaleX: 2.2,
						scaleY: 2.2,
						alpha: 0
					}, {
						duration: 220,
						easing: tween.cubicOut,
						onFinish: function onFinish() {
							obj.destroy();
						}
					});
					objects.splice(i, 1);
				}
			}
		}
	}
};
// --- Reset game state on new game ---
function resetGame() {
	// Remove all objects
	for (var i = 0; i < objects.length; i++) {
		objects[i].destroy();
	}
	objects = [];
	player.lane = 1;
	player.row = 1;
	player.moving = false;
	player.moveTarget = 1;
	player.moveRowTarget = 1;
	player.moveTween = null;
	player.moveRowTween = null;
	player.queuedMove = null;
	player.queuedRowMove = null;
	player.alive = true;
	speed = 0.18;
	score = 0;
	distance = 0;
	spawnTimer = 18;
	scoreTxt.setText('0');
	distTxt.setText('0m');
	// Reset input delay state
	inputDelayActive = false;
	if (inputDelayTimer !== null) {
		LK.clearTimeout(inputDelayTimer);
		inputDelayTimer = null;
	}
}
resetGame();
// Play background music at game start
LK.playMusic('Music');
// --- LK handles game over and reset automatically, but listen for new game start ---
LK.on('gameStart', function () {
	resetGame();
	LK.playMusic('Music');
});