/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Ball class for all balls (cue, solids, stripes, 8)
var Ball = Container.expand(function () {
	var self = Container.call(this);
	// Ball properties
	self.radius = 30; // 60px diameter
	self.vx = 0;
	self.vy = 0;
	self.inPlay = true; // false if pocketed
	self.ballType = 'cue'; // 'cue', 'solid', 'stripe', 'eight'
	self.number = 0; // 0 for cue, 8 for eight, 1-7 solid, 9-15 stripe
	// Attach correct asset
	var assetId = 'cueBall';
	if (self.ballType === 'eight') assetId = 'eightBall';else if (self.ballType === 'solid') assetId = 'solidBall';else if (self.ballType === 'stripe') assetId = 'stripeBall';
	var ballSprite = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add number overlay (except cue ball)
	if (self.ballType !== 'cue') {
		var numText = new Text2('' + self.number, {
			size: 32,
			fill: 0x000000
		});
		numText.anchor.set(0.5, 0.5);
		numText.x = 0;
		numText.y = 0;
		self.addChild(numText);
		self.numText = numText;
	}
	// Update asset if type/number changes
	self.setType = function (type, number) {
		self.ballType = type;
		self.number = number;
		// Remove old children
		while (self.children.length) self.removeChild(self.children[0]);
		// Attach new asset
		var assetId = 'cueBall';
		if (type === 'eight') assetId = 'eightBall';else if (type === 'solid') assetId = 'solidBall';else if (type === 'stripe') assetId = 'stripeBall';
		ballSprite = self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		if (type !== 'cue') {
			var numText = new Text2('' + number, {
				size: 32,
				fill: 0x000000
			});
			numText.anchor.set(0.5, 0.5);
			numText.x = 0;
			numText.y = 0;
			self.addChild(numText);
			self.numText = numText;
		}
	};
	// Ball physics update
	self.update = function () {
		if (!self.inPlay) return;
		// Move
		self.x += self.vx;
		self.y += self.vy;
		// Friction (easier: balls roll further)
		self.vx *= 0.992;
		self.vy *= 0.992;
		// Stop if very slow (easier: balls keep moving at lower speeds)
		if (Math.abs(self.vx) < 0.025) self.vx = 0;
		if (Math.abs(self.vy) < 0.025) self.vy = 0;
	};
	// Pocketed
	self.pocket = function () {
		self.inPlay = false;
		self.visible = false;
		self.vx = 0;
		self.vy = 0;
	};
	return self;
});
// Cue stick class
var CueStick = Container.expand(function () {
	var self = Container.call(this);
	var cueSprite = self.attachAsset('cueStick', {
		anchorX: 0.05,
		anchorY: 0.5
	});
	self.visible = false;
	self.length = cueSprite.width;
	self.angle = 0;
	self.power = 0;
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x003300
});
/**** 
* Game Code
****/ 
// rules button asset
// Ball numbers (text overlays)
// Cue stick
// Balls
// Pockets
// Table
// Table and layout constants
var tableW = 1800,
	tableH = 900,
	border = 50;
var tableX = (2048 - tableW) / 2,
	tableY = (2732 - tableH) / 2;
var pocketR = 52; // easier: larger pockets
// Table border
var tableBorder = LK.getAsset('tableBorder', {
	anchorX: 0,
	anchorY: 0,
	x: tableX - border,
	y: tableY - border
});
game.addChild(tableBorder);
// Table felt
var table = LK.getAsset('table', {
	anchorX: 0,
	anchorY: 0,
	x: tableX,
	y: tableY
});
game.addChild(table);
// Pockets (6)
var pockets = [];
var pocketPos = [[tableX, tableY],
// TL
[tableX + tableW / 2, tableY],
// TM
[tableX + tableW, tableY],
// TR
[tableX, tableY + tableH],
// BL
[tableX + tableW / 2, tableY + tableH],
// BM
[tableX + tableW, tableY + tableH] // BR
];
for (var i = 0; i < 6; i++) {
	var p = LK.getAsset('pocket', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: pocketPos[i][0],
		y: pocketPos[i][1]
	});
	game.addChild(p);
	pockets.push(p);
}
// Balls
var balls = [];
// Helper: create and place a ball
function createBall(type, number, x, y) {
	var b = new Ball();
	b.setType(type, number);
	b.x = x;
	b.y = y;
	balls.push(b);
	game.addChild(b);
	return b;
}
// Place cue ball
var cueBall = createBall('cue', 0, tableX + tableW * 0.25, tableY + tableH / 2);
// Place rack (triangle) - 8 at center, 1 at apex, randomize solids/stripes
var rackX = tableX + tableW * 0.75,
	rackY = tableY + tableH / 2;
var rackRows = [[0], [-1, 1], [-2, 0, 2], [-3, -1, 1, 3], [-4, -2, 0, 2, 4]];
var rackBalls = [{
	type: 'solid',
	number: 1
}, {
	type: 'stripe',
	number: 9
}, {
	type: 'solid',
	number: 2
}, {
	type: 'stripe',
	number: 10
}, {
	type: 'solid',
	number: 3
}, {
	type: 'stripe',
	number: 11
}, {
	type: 'solid',
	number: 4
}, {
	type: 'stripe',
	number: 12
}, {
	type: 'solid',
	number: 5
}, {
	type: 'stripe',
	number: 13
}, {
	type: 'solid',
	number: 6
}, {
	type: 'stripe',
	number: 14
}, {
	type: 'solid',
	number: 7
}, {
	type: 'eight',
	number: 8
}, {
	type: 'stripe',
	number: 15
}];
// Shuffle rackBalls except 8-ball (must be center)
function shuffleRack(arr) {
	for (var i = arr.length - 1; i > 0; i--) {
		if (arr[i].type === 'eight') continue;
		var j = Math.floor(Math.random() * (i + 1));
		if (arr[j].type === 'eight') continue;
		var tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}
}
shuffleRack(rackBalls);
// Place balls in triangle
var rackIdx = 0;
for (var row = 0; row < rackRows.length; row++) {
	for (var col = 0; col < rackRows[row].length; col++) {
		var dx = row * 60 * Math.cos(Math.PI / 6);
		var dy = rackRows[row][col] * 62;
		var bx = rackX + dx;
		var by = rackY + dy;
		// 8-ball must be center of 3rd row
		var ballData;
		if (row === 2 && col === 1) {
			for (var k = 0; k < rackBalls.length; k++) {
				if (rackBalls[k].type === 'eight') {
					ballData = rackBalls[k];
					break;
				}
			}
		} else {
			// Find next non-8-ball
			for (var k = 0; k < rackBalls.length; k++) {
				if (rackBalls[k].type !== 'eight' && !rackBalls[k].used) {
					ballData = rackBalls[k];
					rackBalls[k].used = true;
					break;
				}
			}
		}
		createBall(ballData.type, ballData.number, bx, by);
		rackIdx++;
	}
}
// Cue stick
var cueStick = new CueStick();
game.addChild(cueStick);
// Game state
var isAiming = false;
var aimStart = {
	x: 0,
	y: 0
};
var aimEnd = {
	x: 0,
	y: 0
};
var shotInProgress = false;
var currentPlayer = 1; // 1 or 2 (future: multiplayer)
var playerGroup = {
	1: null,
	2: null
}; // 'solid' or 'stripe'
var turnFoul = false;
var firstContact = null;
var ballsPocketedThisTurn = [];
var gameOver = false;
// GUI: Turn/Status
var statusTxt = new Text2('Player 1: Aim & Shoot', {
	size: 80,
	fill: 0xFFFFFF
});
statusTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(statusTxt);
// GUI: Player group info at bottom
var groupTxt = new Text2('', {
	size: 48,
	fill: 0xFFFFFF
});
groupTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(groupTxt);
// --- Rules Button and Popup ---
// Create a square button at the top right
var rulesBtn = new Text2('?', {
	size: 80,
	fill: "#fff",
	align: "center"
});
rulesBtn.anchor.set(1, 0); // top right
rulesBtn.x = 0; // will be positioned by gui
rulesBtn.y = 0;
rulesBtn.bg = 0x333333;
rulesBtn.width = 120;
rulesBtn.height = 120;
rulesBtn.interactive = true;
rulesBtn.buttonMode = true;
LK.gui.topRight.addChild(rulesBtn);
// --- To Assets Button ---
// Add a 'to assets' button to the top right of the screen
var toAssetsBtn = LK.getAsset('toAssetsBtn', {
	anchorX: 1,
	anchorY: 0,
	x: 130,
	// offset to the left of rulesBtn
	y: 0
});
toAssetsBtn.interactive = true;
toAssetsBtn.buttonMode = true;
LK.gui.topRight.addChild(toAssetsBtn);
// Rules popup container (hidden by default)
var rulesPopup = new Container();
rulesPopup.visible = false;
rulesPopup.zIndex = 1000; // ensure on top
// Semi-transparent background
var popupBg = LK.getAsset('table', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732,
	tint: 0x000000,
	alpha: 0.7
});
rulesPopup.addChild(popupBg);
// Rules text (Turkish and English, toggleable)
var rulesTextTurkish = "8 Top Bilardo Kuralları:\n\n" + "1. Oyuncular sırayla ıstaka topunu kullanarak kendi grubundaki (düz veya çizgili) topları cebe sokmaya çalışır.\n" + "2. Tüm toplarını sokan oyuncu, siyah 8 topunu doğru cebe sokarsa oyunu kazanır.\n" + "3. Rakibin topunu veya 8 topunu erken sokmak fauldür.\n" + "4. Faul durumunda rakip serbest atış hakkı kazanır.\n" + "5. Oyun sırası, faul veya top sokulamayan turda değişir.\n\n" + "İyi oyunlar!";
var rulesTextEnglish = "8-Ball Billiards Rules:\n\n" + "1. Players take turns using the cue ball to pocket their group of balls (solids or stripes).\n" + "2. The player who pockets all their balls and then legally pockets the black 8-ball wins the game.\n" + "3. Pocketing the opponent's ball or the 8-ball early is a foul.\n" + "4. After a foul, the opponent gets ball-in-hand.\n" + "5. Turn changes after a foul or if no ball is pocketed.\n\n" + "Good luck!";
var currentRulesLang = "tr"; // "tr" for Turkish, "en" for English
var rulesText = new Text2(rulesTextTurkish, {
	size: 60,
	fill: "#fff",
	align: "center",
	wordWrap: true,
	wordWrapWidth: 1600
});
rulesText.anchor.set(0.5, 0);
rulesText.x = 2048 / 2;
rulesText.y = 400;
rulesPopup.addChild(rulesText);
// English button
var englishBtn = new Text2('English', {
	size: 48,
	fill: "#fff",
	align: "center"
});
englishBtn.anchor.set(1, 0);
englishBtn.x = 2048 / 2 + 800;
englishBtn.y = 400 - 80;
englishBtn.bg = 0x333333;
englishBtn.width = 220;
englishBtn.height = 90;
englishBtn.interactive = true;
englishBtn.buttonMode = true;
rulesPopup.addChild(englishBtn);
// Close button
var closeBtn = new Text2('Kapat', {
	size: 64,
	fill: "#fff",
	align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = 2048 / 2;
closeBtn.y = 400 + rulesText.height + 100;
closeBtn.bg = 0x333333;
closeBtn.width = 400;
closeBtn.height = 120;
closeBtn.interactive = true;
closeBtn.buttonMode = true;
rulesPopup.addChild(closeBtn);
// Add popup to game (so it overlays everything)
game.addChild(rulesPopup);
// Button event: show popup
// Track if rules popup is open and game is paused
var rulesPopupOpen = false;
rulesBtn.down = function (x, y, obj) {
	rulesPopup.visible = true;
	rulesPopupOpen = true;
	// Always show Turkish by default
	currentRulesLang = "tr";
	rulesText.setText(rulesTextTurkish);
	closeBtn.setText("Kapat");
	englishBtn.setText("English");
};
// English button event: toggle language
englishBtn.down = function (x, y, obj) {
	if (currentRulesLang === "tr") {
		currentRulesLang = "en";
		rulesText.setText(rulesTextEnglish);
		closeBtn.setText("Close");
		englishBtn.setText("Türkçe");
	} else {
		currentRulesLang = "tr";
		rulesText.setText(rulesTextTurkish);
		closeBtn.setText("Kapat");
		englishBtn.setText("English");
	}
	// Adjust closeBtn position in case rulesText height changes
	closeBtn.y = 400 + rulesText.height + 100;
};
// Close event: hide popup
closeBtn.down = function (x, y, obj) {
	rulesPopup.visible = false;
	rulesPopupOpen = false;
	// LK.resumeGame(); // Removed, LK handles resume for overlays
};
// Helper: update group info text
function updateGroupTxt() {
	// Helper to find the lowest-numbered in-play ball of a group
	function nextBallColor(group) {
		if (!group) return 'Unassigned';
		var minNum = 100;
		for (var i = 0; i < balls.length; i++) {
			if (!balls[i].inPlay) continue;
			if (balls[i].ballType === group && balls[i].number < minNum) {
				minNum = balls[i].number;
			}
		}
		if (minNum === 100) {
			// No balls left, so next is 8-ball
			return 'Black 8';
		}
		return (group === 'solid' ? 'Red ' : 'Blue ') + minNum;
	}
	var p1 = playerGroup[1] ? nextBallColor(playerGroup[1]) : 'Unassigned';
	var p2 = playerGroup[2] ? nextBallColor(playerGroup[2]) : 'Unassigned';
	groupTxt.setText('Player 1: ' + p1 + '    |    Player 2: ' + p2);
}
updateGroupTxt();
// Helper: check if all balls stopped
function allBallsStopped() {
	for (var i = 0; i < balls.length; i++) {
		if (!balls[i].inPlay) continue;
		if (Math.abs(balls[i].vx) > 0.05 || Math.abs(balls[i].vy) > 0.05) return false;
	}
	return true;
}
// Helper: distance between two points
function dist(ax, ay, bx, by) {
	var dx = ax - bx,
		dy = ay - by;
	return Math.sqrt(dx * dx + dy * dy);
}
// Helper: ball-ball collision
function resolveBallCollision(b1, b2) {
	var dx = b2.x - b1.x,
		dy = b2.y - b1.y;
	var d = Math.sqrt(dx * dx + dy * dy);
	if (d === 0 || d > b1.radius * 2) return false;
	// Overlap
	var overlap = b1.radius * 2 - d;
	var nx = dx / d,
		ny = dy / d;
	// Separate balls
	b1.x -= nx * overlap / 2;
	b1.y -= ny * overlap / 2;
	b2.x += nx * overlap / 2;
	b2.y += ny * overlap / 2;
	// Relative velocity
	var dvx = b2.vx - b1.vx,
		dvy = b2.vy - b1.vy;
	var dot = dvx * nx + dvy * ny;
	if (dot > 0) return false;
	// Elastic collision
	var impulse = dot;
	b1.vx += nx * impulse;
	b1.vy += ny * impulse;
	b2.vx -= nx * impulse;
	b2.vy -= ny * impulse;
	// Play collision sound
	LK.getSound('ballHit').play();
	return true;
}
// Helper: ball-table collision
function resolveTableCollision(ball) {
	if (!ball.inPlay) return;
	var minX = tableX + ball.radius,
		maxX = tableX + tableW - ball.radius;
	var minY = tableY + ball.radius,
		maxY = tableY + tableH - ball.radius;
	if (ball.x < minX) {
		ball.x = minX;
		ball.vx = -ball.vx * 0.9;
	}
	if (ball.x > maxX) {
		ball.x = maxX;
		ball.vx = -ball.vx * 0.9;
	}
	if (ball.y < minY) {
		ball.y = minY;
		ball.vy = -ball.vy * 0.9;
	}
	if (ball.y > maxY) {
		ball.y = maxY;
		ball.vy = -ball.vy * 0.9;
	}
}
// Helper: ball-pocket collision
function checkPocket(ball) {
	if (!ball.inPlay) return false;
	for (var i = 0; i < pockets.length; i++) {
		var px = pockets[i].x,
			py = pockets[i].y;
		if (dist(ball.x, ball.y, px, py) < pocketR) {
			ball.pocket();
			return true;
		}
	}
	return false;
}
// Helper: assign group after first legal pocket
function assignGroup(player, type) {
	if (playerGroup[1] === null && playerGroup[2] === null && (type === 'solid' || type === 'stripe')) {
		playerGroup[player] = type;
		playerGroup[player === 1 ? 2 : 1] = type === 'solid' ? 'stripe' : 'solid';
	}
}
// Helper: check win/lose
function checkGameEnd() {
	// If 8-ball pocketed
	var eight = null;
	for (var i = 0; i < balls.length; i++) {
		if (balls[i].ballType === 'eight') eight = balls[i];
	}
	if (!eight.inPlay) {
		// Did player clear all their group?
		var groupType = playerGroup[currentPlayer];
		var groupCleared = true;
		for (var i = 0; i < balls.length; i++) {
			if (balls[i].ballType === groupType && balls[i].inPlay) groupCleared = false;
		}
		if (groupCleared && !turnFoul) {
			statusTxt.setText('Player ' + currentPlayer + ' wins!');
			LK.showYouWin();
		} else {
			statusTxt.setText('Player ' + currentPlayer + ' loses!');
			LK.showGameOver();
		}
		gameOver = true;
	}
}
// Helper: reset cue ball after scratch
function resetCueBall() {
	cueBall.x = tableX + tableW * 0.25;
	cueBall.y = tableY + tableH / 2;
	cueBall.vx = 0;
	cueBall.vy = 0;
	cueBall.inPlay = true;
	cueBall.visible = true;
}
// Touch controls
game.down = function (x, y, obj) {
	if (rulesPopupOpen) return;
	if (gameOver) return;
	if (!allBallsStopped()) return;
	// Only allow aiming if touch is on table and cue ball is in play
	if (!cueBall.inPlay) return;
	if (x < tableX || x > tableX + tableW || y < tableY || y > tableY + tableH) return;
	// Only allow aiming if touch is not on a ball (except cue ball)
	for (var i = 0; i < balls.length; i++) {
		if (balls[i] !== cueBall && balls[i].inPlay && dist(x, y, balls[i].x, balls[i].y) < balls[i].radius) return;
	}
	isAiming = true;
	aimStart.x = x;
	aimStart.y = y;
	aimEnd.x = x;
	aimEnd.y = y;
	cueStick.visible = true;
};
game.move = function (x, y, obj) {
	if (rulesPopupOpen) return;
	if (!isAiming) return;
	aimEnd.x = x;
	aimEnd.y = y;
	// Update cue stick position/angle
	var dx = cueBall.x - aimEnd.x;
	var dy = cueBall.y - aimEnd.y;
	var angle = Math.atan2(dy, dx);
	cueStick.x = cueBall.x;
	cueStick.y = cueBall.y;
	cueStick.rotation = angle;
	// Power: distance from cue ball to drag point, max 300
	var power = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 300);
	cueStick.scale.x = 1 + power / 600;
	cueStick.power = power;
};
game.up = function (x, y, obj) {
	if (rulesPopupOpen) return;
	if (!isAiming) return;
	isAiming = false;
	cueStick.visible = false;
	if (gameOver) return;
	if (!allBallsStopped()) return;
	// Calculate shot
	var dx = cueBall.x - aimEnd.x;
	var dy = cueBall.y - aimEnd.y;
	var power = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 300);
	if (power < 10) return; // too weak
	var angle = Math.atan2(dy, dx);
	// Apply velocity to cue ball (easier: more power)
	cueBall.vx = Math.cos(angle) * (power / 8.5);
	cueBall.vy = Math.sin(angle) * (power / 8.5);
	shotInProgress = true;
	firstContact = null;
	ballsPocketedThisTurn = [];
	turnFoul = false;
};
// Main update loop
game.update = function () {
	if (gameOver) return;
	// Physics: update balls
	for (var i = 0; i < balls.length; i++) {
		balls[i].update();
	}
	// Ball-ball collisions
	for (var i = 0; i < balls.length; i++) {
		for (var j = i + 1; j < balls.length; j++) {
			if (!balls[i].inPlay || !balls[j].inPlay) continue;
			var collided = resolveBallCollision(balls[i], balls[j]);
			// First contact detection (cue ball)
			if (shotInProgress && !firstContact) {
				if (balls[i] === cueBall && balls[j].inPlay && balls[j].ballType !== 'cue' || balls[j] === cueBall && balls[i].inPlay && balls[i].ballType !== 'cue') {
					firstContact = balls[i] === cueBall ? balls[j] : balls[i];
				}
			}
		}
	}
	// Ball-table collisions
	for (var i = 0; i < balls.length; i++) {
		resolveTableCollision(balls[i]);
	}
	// Ball-pocket collisions
	for (var i = 0; i < balls.length; i++) {
		if (!balls[i].inPlay) continue;
		if (checkPocket(balls[i])) {
			ballsPocketedThisTurn.push(balls[i]);
			// If cue ball pocketed
			if (balls[i] === cueBall) {
				turnFoul = true;
			}
		}
	}
	// If shot in progress, check if all balls stopped
	if (shotInProgress && allBallsStopped()) {
		shotInProgress = false;
		// Rules: assign group if not yet assigned
		for (var i = 0; i < ballsPocketedThisTurn.length; i++) {
			var b = ballsPocketedThisTurn[i];
			if (playerGroup[currentPlayer] === null && (b.ballType === 'solid' || b.ballType === 'stripe')) {
				assignGroup(currentPlayer, b.ballType);
				updateGroupTxt();
			}
		}
		// Foul: cue ball pocketed or no group ball hit first
		if (turnFoul || playerGroup[currentPlayer] && (!firstContact || firstContact.ballType !== playerGroup[currentPlayer])) {
			turnFoul = true;
		}
		// End turn if foul or no group ball pocketed
		var groupBallPocketed = false;
		for (var i = 0; i < ballsPocketedThisTurn.length; i++) {
			if (playerGroup[currentPlayer] && ballsPocketedThisTurn[i].ballType === playerGroup[currentPlayer]) {
				groupBallPocketed = true;
			}
		}
		// 8-ball pocketed: check win/lose
		checkGameEnd();
		if (gameOver) return;
		// Foul: give ball in hand (reset cue ball)
		if (turnFoul) {
			resetCueBall();
			statusTxt.setText('Foul! Player ' + (currentPlayer === 1 ? 2 : 1) + "'s turn");
			currentPlayer = currentPlayer === 1 ? 2 : 1;
			updateGroupTxt();
		} else if (!groupBallPocketed && playerGroup[currentPlayer]) {
			// No group ball pocketed: switch turn
			currentPlayer = currentPlayer === 1 ? 2 : 1;
			statusTxt.setText('Player ' + currentPlayer + "'s turn");
			updateGroupTxt();
		} else {
			// Continue turn
			statusTxt.setText('Player ' + currentPlayer + ': Aim & Shoot');
		}
		ballsPocketedThisTurn = [];
		firstContact = null;
		turnFoul = false;
	}
	// Update cue stick position if aiming
	if (isAiming) {
		var dx = cueBall.x - aimEnd.x;
		var dy = cueBall.y - aimEnd.y;
		var angle = Math.atan2(dy, dx);
		cueStick.x = cueBall.x;
		cueStick.y = cueBall.y;
		cueStick.rotation = angle;
		var power = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 300);
		cueStick.scale.x = 1 + power / 600;
		cueStick.power = power;
		// Draw aiming line (gray, slightly transparent)
		if (!game.aimLine) {
			game.aimLine = new Container();
			game.addChild(game.aimLine);
		}
		var aimLine = game.aimLine;
		while (aimLine.children.length) aimLine.removeChild(aimLine.children[0]);
		var lineLen = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 400);
		var steps = Math.floor(lineLen / 16);
		for (var i = 1; i <= steps; i++) {
			var px = cueBall.x - dx / lineLen * (i * 16);
			var py = cueBall.y - dy / lineLen * (i * 16);
			var dot = LK.getAsset('cueBall', {
				anchorX: 0.5,
				anchorY: 0.5,
				x: px,
				y: py,
				scaleX: 0.18,
				scaleY: 0.18,
				tint: 0x888888,
				// gray
				alpha: 0.38 // slightly transparent
			});
			aimLine.addChild(dot);
		}
	} else {
		if (game.aimLine) {
			while (game.aimLine.children.length) game.aimLine.removeChild(game.aimLine.children[0]);
		}
	}
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// Ball class for all balls (cue, solids, stripes, 8)
var Ball = Container.expand(function () {
	var self = Container.call(this);
	// Ball properties
	self.radius = 30; // 60px diameter
	self.vx = 0;
	self.vy = 0;
	self.inPlay = true; // false if pocketed
	self.ballType = 'cue'; // 'cue', 'solid', 'stripe', 'eight'
	self.number = 0; // 0 for cue, 8 for eight, 1-7 solid, 9-15 stripe
	// Attach correct asset
	var assetId = 'cueBall';
	if (self.ballType === 'eight') assetId = 'eightBall';else if (self.ballType === 'solid') assetId = 'solidBall';else if (self.ballType === 'stripe') assetId = 'stripeBall';
	var ballSprite = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add number overlay (except cue ball)
	if (self.ballType !== 'cue') {
		var numText = new Text2('' + self.number, {
			size: 32,
			fill: 0x000000
		});
		numText.anchor.set(0.5, 0.5);
		numText.x = 0;
		numText.y = 0;
		self.addChild(numText);
		self.numText = numText;
	}
	// Update asset if type/number changes
	self.setType = function (type, number) {
		self.ballType = type;
		self.number = number;
		// Remove old children
		while (self.children.length) self.removeChild(self.children[0]);
		// Attach new asset
		var assetId = 'cueBall';
		if (type === 'eight') assetId = 'eightBall';else if (type === 'solid') assetId = 'solidBall';else if (type === 'stripe') assetId = 'stripeBall';
		ballSprite = self.attachAsset(assetId, {
			anchorX: 0.5,
			anchorY: 0.5
		});
		if (type !== 'cue') {
			var numText = new Text2('' + number, {
				size: 32,
				fill: 0x000000
			});
			numText.anchor.set(0.5, 0.5);
			numText.x = 0;
			numText.y = 0;
			self.addChild(numText);
			self.numText = numText;
		}
	};
	// Ball physics update
	self.update = function () {
		if (!self.inPlay) return;
		// Move
		self.x += self.vx;
		self.y += self.vy;
		// Friction (easier: balls roll further)
		self.vx *= 0.992;
		self.vy *= 0.992;
		// Stop if very slow (easier: balls keep moving at lower speeds)
		if (Math.abs(self.vx) < 0.025) self.vx = 0;
		if (Math.abs(self.vy) < 0.025) self.vy = 0;
	};
	// Pocketed
	self.pocket = function () {
		self.inPlay = false;
		self.visible = false;
		self.vx = 0;
		self.vy = 0;
	};
	return self;
});
// Cue stick class
var CueStick = Container.expand(function () {
	var self = Container.call(this);
	var cueSprite = self.attachAsset('cueStick', {
		anchorX: 0.05,
		anchorY: 0.5
	});
	self.visible = false;
	self.length = cueSprite.width;
	self.angle = 0;
	self.power = 0;
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x003300
});
/**** 
* Game Code
****/ 
// rules button asset
// Ball numbers (text overlays)
// Cue stick
// Balls
// Pockets
// Table
// Table and layout constants
var tableW = 1800,
	tableH = 900,
	border = 50;
var tableX = (2048 - tableW) / 2,
	tableY = (2732 - tableH) / 2;
var pocketR = 52; // easier: larger pockets
// Table border
var tableBorder = LK.getAsset('tableBorder', {
	anchorX: 0,
	anchorY: 0,
	x: tableX - border,
	y: tableY - border
});
game.addChild(tableBorder);
// Table felt
var table = LK.getAsset('table', {
	anchorX: 0,
	anchorY: 0,
	x: tableX,
	y: tableY
});
game.addChild(table);
// Pockets (6)
var pockets = [];
var pocketPos = [[tableX, tableY],
// TL
[tableX + tableW / 2, tableY],
// TM
[tableX + tableW, tableY],
// TR
[tableX, tableY + tableH],
// BL
[tableX + tableW / 2, tableY + tableH],
// BM
[tableX + tableW, tableY + tableH] // BR
];
for (var i = 0; i < 6; i++) {
	var p = LK.getAsset('pocket', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: pocketPos[i][0],
		y: pocketPos[i][1]
	});
	game.addChild(p);
	pockets.push(p);
}
// Balls
var balls = [];
// Helper: create and place a ball
function createBall(type, number, x, y) {
	var b = new Ball();
	b.setType(type, number);
	b.x = x;
	b.y = y;
	balls.push(b);
	game.addChild(b);
	return b;
}
// Place cue ball
var cueBall = createBall('cue', 0, tableX + tableW * 0.25, tableY + tableH / 2);
// Place rack (triangle) - 8 at center, 1 at apex, randomize solids/stripes
var rackX = tableX + tableW * 0.75,
	rackY = tableY + tableH / 2;
var rackRows = [[0], [-1, 1], [-2, 0, 2], [-3, -1, 1, 3], [-4, -2, 0, 2, 4]];
var rackBalls = [{
	type: 'solid',
	number: 1
}, {
	type: 'stripe',
	number: 9
}, {
	type: 'solid',
	number: 2
}, {
	type: 'stripe',
	number: 10
}, {
	type: 'solid',
	number: 3
}, {
	type: 'stripe',
	number: 11
}, {
	type: 'solid',
	number: 4
}, {
	type: 'stripe',
	number: 12
}, {
	type: 'solid',
	number: 5
}, {
	type: 'stripe',
	number: 13
}, {
	type: 'solid',
	number: 6
}, {
	type: 'stripe',
	number: 14
}, {
	type: 'solid',
	number: 7
}, {
	type: 'eight',
	number: 8
}, {
	type: 'stripe',
	number: 15
}];
// Shuffle rackBalls except 8-ball (must be center)
function shuffleRack(arr) {
	for (var i = arr.length - 1; i > 0; i--) {
		if (arr[i].type === 'eight') continue;
		var j = Math.floor(Math.random() * (i + 1));
		if (arr[j].type === 'eight') continue;
		var tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}
}
shuffleRack(rackBalls);
// Place balls in triangle
var rackIdx = 0;
for (var row = 0; row < rackRows.length; row++) {
	for (var col = 0; col < rackRows[row].length; col++) {
		var dx = row * 60 * Math.cos(Math.PI / 6);
		var dy = rackRows[row][col] * 62;
		var bx = rackX + dx;
		var by = rackY + dy;
		// 8-ball must be center of 3rd row
		var ballData;
		if (row === 2 && col === 1) {
			for (var k = 0; k < rackBalls.length; k++) {
				if (rackBalls[k].type === 'eight') {
					ballData = rackBalls[k];
					break;
				}
			}
		} else {
			// Find next non-8-ball
			for (var k = 0; k < rackBalls.length; k++) {
				if (rackBalls[k].type !== 'eight' && !rackBalls[k].used) {
					ballData = rackBalls[k];
					rackBalls[k].used = true;
					break;
				}
			}
		}
		createBall(ballData.type, ballData.number, bx, by);
		rackIdx++;
	}
}
// Cue stick
var cueStick = new CueStick();
game.addChild(cueStick);
// Game state
var isAiming = false;
var aimStart = {
	x: 0,
	y: 0
};
var aimEnd = {
	x: 0,
	y: 0
};
var shotInProgress = false;
var currentPlayer = 1; // 1 or 2 (future: multiplayer)
var playerGroup = {
	1: null,
	2: null
}; // 'solid' or 'stripe'
var turnFoul = false;
var firstContact = null;
var ballsPocketedThisTurn = [];
var gameOver = false;
// GUI: Turn/Status
var statusTxt = new Text2('Player 1: Aim & Shoot', {
	size: 80,
	fill: 0xFFFFFF
});
statusTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(statusTxt);
// GUI: Player group info at bottom
var groupTxt = new Text2('', {
	size: 48,
	fill: 0xFFFFFF
});
groupTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(groupTxt);
// --- Rules Button and Popup ---
// Create a square button at the top right
var rulesBtn = new Text2('?', {
	size: 80,
	fill: "#fff",
	align: "center"
});
rulesBtn.anchor.set(1, 0); // top right
rulesBtn.x = 0; // will be positioned by gui
rulesBtn.y = 0;
rulesBtn.bg = 0x333333;
rulesBtn.width = 120;
rulesBtn.height = 120;
rulesBtn.interactive = true;
rulesBtn.buttonMode = true;
LK.gui.topRight.addChild(rulesBtn);
// --- To Assets Button ---
// Add a 'to assets' button to the top right of the screen
var toAssetsBtn = LK.getAsset('toAssetsBtn', {
	anchorX: 1,
	anchorY: 0,
	x: 130,
	// offset to the left of rulesBtn
	y: 0
});
toAssetsBtn.interactive = true;
toAssetsBtn.buttonMode = true;
LK.gui.topRight.addChild(toAssetsBtn);
// Rules popup container (hidden by default)
var rulesPopup = new Container();
rulesPopup.visible = false;
rulesPopup.zIndex = 1000; // ensure on top
// Semi-transparent background
var popupBg = LK.getAsset('table', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0,
	width: 2048,
	height: 2732,
	tint: 0x000000,
	alpha: 0.7
});
rulesPopup.addChild(popupBg);
// Rules text (Turkish and English, toggleable)
var rulesTextTurkish = "8 Top Bilardo Kuralları:\n\n" + "1. Oyuncular sırayla ıstaka topunu kullanarak kendi grubundaki (düz veya çizgili) topları cebe sokmaya çalışır.\n" + "2. Tüm toplarını sokan oyuncu, siyah 8 topunu doğru cebe sokarsa oyunu kazanır.\n" + "3. Rakibin topunu veya 8 topunu erken sokmak fauldür.\n" + "4. Faul durumunda rakip serbest atış hakkı kazanır.\n" + "5. Oyun sırası, faul veya top sokulamayan turda değişir.\n\n" + "İyi oyunlar!";
var rulesTextEnglish = "8-Ball Billiards Rules:\n\n" + "1. Players take turns using the cue ball to pocket their group of balls (solids or stripes).\n" + "2. The player who pockets all their balls and then legally pockets the black 8-ball wins the game.\n" + "3. Pocketing the opponent's ball or the 8-ball early is a foul.\n" + "4. After a foul, the opponent gets ball-in-hand.\n" + "5. Turn changes after a foul or if no ball is pocketed.\n\n" + "Good luck!";
var currentRulesLang = "tr"; // "tr" for Turkish, "en" for English
var rulesText = new Text2(rulesTextTurkish, {
	size: 60,
	fill: "#fff",
	align: "center",
	wordWrap: true,
	wordWrapWidth: 1600
});
rulesText.anchor.set(0.5, 0);
rulesText.x = 2048 / 2;
rulesText.y = 400;
rulesPopup.addChild(rulesText);
// English button
var englishBtn = new Text2('English', {
	size: 48,
	fill: "#fff",
	align: "center"
});
englishBtn.anchor.set(1, 0);
englishBtn.x = 2048 / 2 + 800;
englishBtn.y = 400 - 80;
englishBtn.bg = 0x333333;
englishBtn.width = 220;
englishBtn.height = 90;
englishBtn.interactive = true;
englishBtn.buttonMode = true;
rulesPopup.addChild(englishBtn);
// Close button
var closeBtn = new Text2('Kapat', {
	size: 64,
	fill: "#fff",
	align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = 2048 / 2;
closeBtn.y = 400 + rulesText.height + 100;
closeBtn.bg = 0x333333;
closeBtn.width = 400;
closeBtn.height = 120;
closeBtn.interactive = true;
closeBtn.buttonMode = true;
rulesPopup.addChild(closeBtn);
// Add popup to game (so it overlays everything)
game.addChild(rulesPopup);
// Button event: show popup
// Track if rules popup is open and game is paused
var rulesPopupOpen = false;
rulesBtn.down = function (x, y, obj) {
	rulesPopup.visible = true;
	rulesPopupOpen = true;
	// Always show Turkish by default
	currentRulesLang = "tr";
	rulesText.setText(rulesTextTurkish);
	closeBtn.setText("Kapat");
	englishBtn.setText("English");
};
// English button event: toggle language
englishBtn.down = function (x, y, obj) {
	if (currentRulesLang === "tr") {
		currentRulesLang = "en";
		rulesText.setText(rulesTextEnglish);
		closeBtn.setText("Close");
		englishBtn.setText("Türkçe");
	} else {
		currentRulesLang = "tr";
		rulesText.setText(rulesTextTurkish);
		closeBtn.setText("Kapat");
		englishBtn.setText("English");
	}
	// Adjust closeBtn position in case rulesText height changes
	closeBtn.y = 400 + rulesText.height + 100;
};
// Close event: hide popup
closeBtn.down = function (x, y, obj) {
	rulesPopup.visible = false;
	rulesPopupOpen = false;
	// LK.resumeGame(); // Removed, LK handles resume for overlays
};
// Helper: update group info text
function updateGroupTxt() {
	// Helper to find the lowest-numbered in-play ball of a group
	function nextBallColor(group) {
		if (!group) return 'Unassigned';
		var minNum = 100;
		for (var i = 0; i < balls.length; i++) {
			if (!balls[i].inPlay) continue;
			if (balls[i].ballType === group && balls[i].number < minNum) {
				minNum = balls[i].number;
			}
		}
		if (minNum === 100) {
			// No balls left, so next is 8-ball
			return 'Black 8';
		}
		return (group === 'solid' ? 'Red ' : 'Blue ') + minNum;
	}
	var p1 = playerGroup[1] ? nextBallColor(playerGroup[1]) : 'Unassigned';
	var p2 = playerGroup[2] ? nextBallColor(playerGroup[2]) : 'Unassigned';
	groupTxt.setText('Player 1: ' + p1 + '    |    Player 2: ' + p2);
}
updateGroupTxt();
// Helper: check if all balls stopped
function allBallsStopped() {
	for (var i = 0; i < balls.length; i++) {
		if (!balls[i].inPlay) continue;
		if (Math.abs(balls[i].vx) > 0.05 || Math.abs(balls[i].vy) > 0.05) return false;
	}
	return true;
}
// Helper: distance between two points
function dist(ax, ay, bx, by) {
	var dx = ax - bx,
		dy = ay - by;
	return Math.sqrt(dx * dx + dy * dy);
}
// Helper: ball-ball collision
function resolveBallCollision(b1, b2) {
	var dx = b2.x - b1.x,
		dy = b2.y - b1.y;
	var d = Math.sqrt(dx * dx + dy * dy);
	if (d === 0 || d > b1.radius * 2) return false;
	// Overlap
	var overlap = b1.radius * 2 - d;
	var nx = dx / d,
		ny = dy / d;
	// Separate balls
	b1.x -= nx * overlap / 2;
	b1.y -= ny * overlap / 2;
	b2.x += nx * overlap / 2;
	b2.y += ny * overlap / 2;
	// Relative velocity
	var dvx = b2.vx - b1.vx,
		dvy = b2.vy - b1.vy;
	var dot = dvx * nx + dvy * ny;
	if (dot > 0) return false;
	// Elastic collision
	var impulse = dot;
	b1.vx += nx * impulse;
	b1.vy += ny * impulse;
	b2.vx -= nx * impulse;
	b2.vy -= ny * impulse;
	// Play collision sound
	LK.getSound('ballHit').play();
	return true;
}
// Helper: ball-table collision
function resolveTableCollision(ball) {
	if (!ball.inPlay) return;
	var minX = tableX + ball.radius,
		maxX = tableX + tableW - ball.radius;
	var minY = tableY + ball.radius,
		maxY = tableY + tableH - ball.radius;
	if (ball.x < minX) {
		ball.x = minX;
		ball.vx = -ball.vx * 0.9;
	}
	if (ball.x > maxX) {
		ball.x = maxX;
		ball.vx = -ball.vx * 0.9;
	}
	if (ball.y < minY) {
		ball.y = minY;
		ball.vy = -ball.vy * 0.9;
	}
	if (ball.y > maxY) {
		ball.y = maxY;
		ball.vy = -ball.vy * 0.9;
	}
}
// Helper: ball-pocket collision
function checkPocket(ball) {
	if (!ball.inPlay) return false;
	for (var i = 0; i < pockets.length; i++) {
		var px = pockets[i].x,
			py = pockets[i].y;
		if (dist(ball.x, ball.y, px, py) < pocketR) {
			ball.pocket();
			return true;
		}
	}
	return false;
}
// Helper: assign group after first legal pocket
function assignGroup(player, type) {
	if (playerGroup[1] === null && playerGroup[2] === null && (type === 'solid' || type === 'stripe')) {
		playerGroup[player] = type;
		playerGroup[player === 1 ? 2 : 1] = type === 'solid' ? 'stripe' : 'solid';
	}
}
// Helper: check win/lose
function checkGameEnd() {
	// If 8-ball pocketed
	var eight = null;
	for (var i = 0; i < balls.length; i++) {
		if (balls[i].ballType === 'eight') eight = balls[i];
	}
	if (!eight.inPlay) {
		// Did player clear all their group?
		var groupType = playerGroup[currentPlayer];
		var groupCleared = true;
		for (var i = 0; i < balls.length; i++) {
			if (balls[i].ballType === groupType && balls[i].inPlay) groupCleared = false;
		}
		if (groupCleared && !turnFoul) {
			statusTxt.setText('Player ' + currentPlayer + ' wins!');
			LK.showYouWin();
		} else {
			statusTxt.setText('Player ' + currentPlayer + ' loses!');
			LK.showGameOver();
		}
		gameOver = true;
	}
}
// Helper: reset cue ball after scratch
function resetCueBall() {
	cueBall.x = tableX + tableW * 0.25;
	cueBall.y = tableY + tableH / 2;
	cueBall.vx = 0;
	cueBall.vy = 0;
	cueBall.inPlay = true;
	cueBall.visible = true;
}
// Touch controls
game.down = function (x, y, obj) {
	if (rulesPopupOpen) return;
	if (gameOver) return;
	if (!allBallsStopped()) return;
	// Only allow aiming if touch is on table and cue ball is in play
	if (!cueBall.inPlay) return;
	if (x < tableX || x > tableX + tableW || y < tableY || y > tableY + tableH) return;
	// Only allow aiming if touch is not on a ball (except cue ball)
	for (var i = 0; i < balls.length; i++) {
		if (balls[i] !== cueBall && balls[i].inPlay && dist(x, y, balls[i].x, balls[i].y) < balls[i].radius) return;
	}
	isAiming = true;
	aimStart.x = x;
	aimStart.y = y;
	aimEnd.x = x;
	aimEnd.y = y;
	cueStick.visible = true;
};
game.move = function (x, y, obj) {
	if (rulesPopupOpen) return;
	if (!isAiming) return;
	aimEnd.x = x;
	aimEnd.y = y;
	// Update cue stick position/angle
	var dx = cueBall.x - aimEnd.x;
	var dy = cueBall.y - aimEnd.y;
	var angle = Math.atan2(dy, dx);
	cueStick.x = cueBall.x;
	cueStick.y = cueBall.y;
	cueStick.rotation = angle;
	// Power: distance from cue ball to drag point, max 300
	var power = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 300);
	cueStick.scale.x = 1 + power / 600;
	cueStick.power = power;
};
game.up = function (x, y, obj) {
	if (rulesPopupOpen) return;
	if (!isAiming) return;
	isAiming = false;
	cueStick.visible = false;
	if (gameOver) return;
	if (!allBallsStopped()) return;
	// Calculate shot
	var dx = cueBall.x - aimEnd.x;
	var dy = cueBall.y - aimEnd.y;
	var power = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 300);
	if (power < 10) return; // too weak
	var angle = Math.atan2(dy, dx);
	// Apply velocity to cue ball (easier: more power)
	cueBall.vx = Math.cos(angle) * (power / 8.5);
	cueBall.vy = Math.sin(angle) * (power / 8.5);
	shotInProgress = true;
	firstContact = null;
	ballsPocketedThisTurn = [];
	turnFoul = false;
};
// Main update loop
game.update = function () {
	if (gameOver) return;
	// Physics: update balls
	for (var i = 0; i < balls.length; i++) {
		balls[i].update();
	}
	// Ball-ball collisions
	for (var i = 0; i < balls.length; i++) {
		for (var j = i + 1; j < balls.length; j++) {
			if (!balls[i].inPlay || !balls[j].inPlay) continue;
			var collided = resolveBallCollision(balls[i], balls[j]);
			// First contact detection (cue ball)
			if (shotInProgress && !firstContact) {
				if (balls[i] === cueBall && balls[j].inPlay && balls[j].ballType !== 'cue' || balls[j] === cueBall && balls[i].inPlay && balls[i].ballType !== 'cue') {
					firstContact = balls[i] === cueBall ? balls[j] : balls[i];
				}
			}
		}
	}
	// Ball-table collisions
	for (var i = 0; i < balls.length; i++) {
		resolveTableCollision(balls[i]);
	}
	// Ball-pocket collisions
	for (var i = 0; i < balls.length; i++) {
		if (!balls[i].inPlay) continue;
		if (checkPocket(balls[i])) {
			ballsPocketedThisTurn.push(balls[i]);
			// If cue ball pocketed
			if (balls[i] === cueBall) {
				turnFoul = true;
			}
		}
	}
	// If shot in progress, check if all balls stopped
	if (shotInProgress && allBallsStopped()) {
		shotInProgress = false;
		// Rules: assign group if not yet assigned
		for (var i = 0; i < ballsPocketedThisTurn.length; i++) {
			var b = ballsPocketedThisTurn[i];
			if (playerGroup[currentPlayer] === null && (b.ballType === 'solid' || b.ballType === 'stripe')) {
				assignGroup(currentPlayer, b.ballType);
				updateGroupTxt();
			}
		}
		// Foul: cue ball pocketed or no group ball hit first
		if (turnFoul || playerGroup[currentPlayer] && (!firstContact || firstContact.ballType !== playerGroup[currentPlayer])) {
			turnFoul = true;
		}
		// End turn if foul or no group ball pocketed
		var groupBallPocketed = false;
		for (var i = 0; i < ballsPocketedThisTurn.length; i++) {
			if (playerGroup[currentPlayer] && ballsPocketedThisTurn[i].ballType === playerGroup[currentPlayer]) {
				groupBallPocketed = true;
			}
		}
		// 8-ball pocketed: check win/lose
		checkGameEnd();
		if (gameOver) return;
		// Foul: give ball in hand (reset cue ball)
		if (turnFoul) {
			resetCueBall();
			statusTxt.setText('Foul! Player ' + (currentPlayer === 1 ? 2 : 1) + "'s turn");
			currentPlayer = currentPlayer === 1 ? 2 : 1;
			updateGroupTxt();
		} else if (!groupBallPocketed && playerGroup[currentPlayer]) {
			// No group ball pocketed: switch turn
			currentPlayer = currentPlayer === 1 ? 2 : 1;
			statusTxt.setText('Player ' + currentPlayer + "'s turn");
			updateGroupTxt();
		} else {
			// Continue turn
			statusTxt.setText('Player ' + currentPlayer + ': Aim & Shoot');
		}
		ballsPocketedThisTurn = [];
		firstContact = null;
		turnFoul = false;
	}
	// Update cue stick position if aiming
	if (isAiming) {
		var dx = cueBall.x - aimEnd.x;
		var dy = cueBall.y - aimEnd.y;
		var angle = Math.atan2(dy, dx);
		cueStick.x = cueBall.x;
		cueStick.y = cueBall.y;
		cueStick.rotation = angle;
		var power = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 300);
		cueStick.scale.x = 1 + power / 600;
		cueStick.power = power;
		// Draw aiming line (gray, slightly transparent)
		if (!game.aimLine) {
			game.aimLine = new Container();
			game.addChild(game.aimLine);
		}
		var aimLine = game.aimLine;
		while (aimLine.children.length) aimLine.removeChild(aimLine.children[0]);
		var lineLen = Math.min(dist(cueBall.x, cueBall.y, aimEnd.x, aimEnd.y), 400);
		var steps = Math.floor(lineLen / 16);
		for (var i = 1; i <= steps; i++) {
			var px = cueBall.x - dx / lineLen * (i * 16);
			var py = cueBall.y - dy / lineLen * (i * 16);
			var dot = LK.getAsset('cueBall', {
				anchorX: 0.5,
				anchorY: 0.5,
				x: px,
				y: py,
				scaleX: 0.18,
				scaleY: 0.18,
				tint: 0x888888,
				// gray
				alpha: 0.38 // slightly transparent
			});
			aimLine.addChild(dot);
		}
	} else {
		if (game.aimLine) {
			while (game.aimLine.children.length) game.aimLine.removeChild(game.aimLine.children[0]);
		}
	}
};