/**** 
* Classes
****/ 
// Initialize rotation speed
//<Assets used in the game will automatically appear here>
// Ball class
var Ball = Container.expand(function () {
	var self = Container.call(this);
	var ballGraphics = self.attachAsset('ball', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.rotationSpeed = 0;
	var half = self.width / 2;
	self.speedX = 3;
	self.speedY = 3;
	self.accelerationY = 0.5; // Gravity
	self.friction = 0.99; // Friction to slow down the ball over time
	self.lastCollisionTime = 0; // Initialize last collision time
	self.update = function (net, player1, player2) {
		if (!ballCanMove) {
			return;
		}
		// Apply gravity
		self.speedY += self.accelerationY;
		// Apply friction
		self.speedX *= self.friction;
		self.speedY *= self.friction;
		// Apply speed limit
		self.speedX = Math.sign(self.speedX) * Math.min(Math.abs(self.speedX), SPEED_LIMIT);
		self.speedY = Math.sign(self.speedY) * Math.min(Math.abs(self.speedY), SPEED_LIMIT);
		// Update ball position
		self.x += self.speedX;
		self.y += self.speedY;
		self.rotation += self.rotationSpeed * (self.speedX > 0 ? 1 : -1); // Update ball rotation based on direction
		self.rotationSpeed *= 0.98; // Gradually decrease rotation speed
		// Check for out of bounds
		if (self.y + half > 2000) {
			self.y = 2000 - half;
			self.speedY = -Math.abs(self.speedY) * 0.7; // Make the ball bounce up with more reduced speed
			self.speedX *= 0.9; // Apply slight energy loss on horizontal speed
			playSound('whistle', 1000); // Play whistle sound when ball touches the ground with a cooldown of 1 second
			if (self.x < 1024 && servingPlayer === 2) {
				updateScore(2);
				resetBall(); // Reset the ball after scoring
			} else if (self.x >= 1024 && servingPlayer === 1) {
				updateScore(1);
				resetBall(); // Reset the ball after scoring
			} else {
				servingPlayer = servingPlayer == 1 ? 2 : 1;
				resetBall(); // Reset the ball after scoring
			}
			player1Touches = 0;
			player2Touches = 0;
		} else if (self.y - half < 0) {
			self.y = half;
			self.speedY = Math.abs(self.speedY) * 0.8; // Make the ball bounce down with reduced speed
		}
		// Check for x screen limits
		if (self.x - half < 0) {
			self.x = half;
			self.speedX = Math.abs(self.speedX); // Make the ball bounce to the right
		} else if (self.x + half > 2048) {
			self.x = 2048 - half;
			self.speedX = -Math.abs(self.speedX); // Make the ball bounce to the left
		}
		// Collision detection logic moved to game update
	};
});
// Net class
var Net = Container.expand(function () {
	var self = Container.call(this);
	var netGraphics = self.attachAsset('net', {
		anchorX: 0.5,
		anchorY: 1,
		alpha: isDebug ? 1 : 0
	});
});
// Player class
var Player = Container.expand(function (index) {
	var self = Container.call(this);
	var playerGraphics = self.attachAsset('player', {
		anchorX: 0.5,
		anchorY: 1,
		alpha: 0.98,
		scaleX: index === 2 ? -1 : 1,
		tint: index === 1 ? 0xADD8E6 : 0xFF6347 // Light blue for player 1, Tomato red for player 2
	});
	self.index = index;
	self.scale.set(1, 1); // Initialize player scale to normal
	self.jumping = false;
	self.falling = false;
	self.speedX = 0; // Initialize horizontal speed
	var collidSize = 245;
	self.collisionBody = LK.getAsset('collisionBody', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: isDebug ? 0.6 : 0,
		width: collidSize,
		height: collidSize,
		x: 0,
		y: -200
	});
	self.addChild(self.collisionBody);
	self.update = function () {
		var prevX = self.x; // Store previous x position
		var prevY = self.y; // Store previous y position
		if (self.jumping && !self.falling) {
			self.y -= 20; // Increase jump speed
			self.scale.set(1, 1 + 0.25 * (PLAYER_INITIAL_Y - self.y) / 700); // Progressive scaling when jumping
			if (self.y <= 1300) {
				self.falling = true;
			}
		}
		if (self.falling && self.y < PLAYER_INITIAL_Y) {
			self.y += 20; // Increase fall speed for smoother jump
			if (self.y > PLAYER_INITIAL_Y - 256) {
				self.scale.set(1 + 0.15 * (PLAYER_INITIAL_Y - self.y) / 700, 1 - 0.15 * (PLAYER_INITIAL_Y - self.y) / 700); // Progressive scaling when falling
			}
		}
		if (self.y >= PLAYER_INITIAL_Y) {
			self.jumping = false;
			self.falling = false;
			self.y = PLAYER_INITIAL_Y; // Ensure player lands on the ground
			// Reset scale to normal when player stops moving
			self.scale.set(1, 1);
		}
		if (self.y < 0) {
			self.y = 0; // Prevent player1 from moving above the window
		}
		if (self.index === 2 && self.x > 1024 && self.x < 1024 + self.width / 2) {
			self.x = 1024 + self.width / 2; // Prevent player2 from moving past the net
		}
		self.speedX = self.x - prevX; // Calculate horizontal speed based on movement
		self.speedY = self.y - prevY; // Calculate vertical speed based on movement
	};
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87CEEB // Sky blue background
});
/**** 
* Game Code
****/ 
var PLAYER1_INITIAL_X = 256;
var PLAYER_INITIAL_Y = 2000;
var PLAYER2_INITIAL_X = 1792;
var BALL1_INITIAL_X = 640;
var BALL_INITIAL_Y = 1256;
var BALL2_INITIAL_X = 1408;
var gameStarted = false;
var startButton = null;
var joystick;
var joystickBasePosition;
var joystickDrag;
var touchTime; // Global variable to store the touch time
var prevMouseY;
var lastPlayedTime;
var ballCanMove;
var SPEED_LIMIT; // Define a global speed limit
var isDebug = false;
var player2Debug;
var player1Touches;
var player2Touches;
var resetTime; // Global variable to store the reset time
var nextRoundPauseDelay;
var score1;
var score2;
var finalScore = 15; // Define the final score for the game
var servingPlayer = 1; // Initialize serving player to player 1
var ballShadow;
var player1Shadow;
var player2Shadow;
var beachBall = null;
function playSound(soundId, cooldown) {
	var currentTime = Date.now();
	if (!lastPlayedTime[soundId] || currentTime - lastPlayedTime[soundId] > cooldown) {
		LK.getSound(soundId).play();
		lastPlayedTime[soundId] = currentTime;
	}
}
function updateScore(index) {
	if (index === 1) {
		score1 += 1;
		scoreTxt1.setText(score1.toString().padStart(2, '0'));
	} else if (index === 2) {
		score2 += 1;
		scoreTxt2.setText(score2.toString().padStart(2, '0'));
	}
	// Check for game over condition
	if ((score1 >= finalScore || score2 >= finalScore) && Math.abs(score1 - score2) >= 2) {
		var resultText = new Text2(score1 > score2 ? 'VICTORY!' : 'DEFEAT!', {
			size: 300,
			fill: "#FFFFFF",
			fontWeight: "bold",
			dropShadow: true
		});
		resultText.anchor.set(0.5, 2);
		//resultText.x = 0;
		//resultText.y = -512;
		LK.gui.center.addChild(resultText);
		LK.setTimeout(function () {
			LK.showGameOver();
		}, 2000);
	}
}
function handleJoystickLimits(x, y) {
	if (joystickDrag) {
		var dx = x - joystickBasePosition.x;
		var dy = y - joystickBasePosition.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		var maxDistance = joystick.width / 3; // Max distance joystick can move from center
		if (distance > maxDistance) {
			var angle = Math.atan2(dy, dx);
			dx = Math.cos(angle) * maxDistance;
			dy = Math.sin(angle) * maxDistance;
		}
		joystick.x = joystickBasePosition.x + dx;
		joystick.y = joystickBasePosition.y + dy;
	}
}
function aiUpdate() {
	if (ball.x > 1024) {
		if (ball.x > player2.x + player2.width / 2) {
			player2.x += 5; // Move right
		} else if (ball.x < player2.x - player2.width / 2) {
			player2.x -= 5; // Move left
		}
	}
	if (!player2.jumping && player2.y >= PLAYER_INITIAL_Y && ball.y < player2.y && Math.abs(ball.x - player2.x) < player2.width) {
		player2.jumping = true;
	}
}
function resetBall() {
	ball.y = BALL_INITIAL_Y; // Set the ball's initial vertical position using BALL_INITIAL_Y
	ball.speedX = 3;
	ball.speedY = 3; // Reset speed
	ball.accelerationY = 0.5; // Reset gravity
	ball.friction = 0.99; // Reset friction
	ballCanMove = false; // Reset ball movement flag
	ball.rotation = 0; // Reset rotation angle
	resetTime = Date.now(); // Update reset time
	player1Touches = 0;
	player2Touches = 0;
	ball.alpha = 0;
	joystick.alpha = 0.;
	ball.x = servingPlayer === 1 ? BALL1_INITIAL_X : BALL2_INITIAL_X;
	// Move players progressively to their initial X positions
	var movePlayerToInitialX = function movePlayerToInitialX(player, initialX) {
		var moveInterval = LK.setInterval(function () {
			updateShadows();
			if (Math.abs(player.x - initialX) < 5) {
				player.x = initialX;
				LK.clearInterval(moveInterval);
			} else {
				player.x += (initialX - player.x) * 0.1;
			}
		}, 16); // Move players every 16ms (approximately 60 FPS)
	};
	movePlayerToInitialX(player1, PLAYER1_INITIAL_X);
	movePlayerToInitialX(player2, PLAYER2_INITIAL_X);
}
function customBoxCircleIntersect(box, circle) {
	var circleX = circle.x;
	var circleY = circle.y;
	if (circle.parent) {
		circleX += circle.parent.x;
		circleY += circle.parent.y;
	}
	var boxX = box.x;
	var boxY = box.y;
	var halfBoxWidth = box.width / 2;
	var halfCircleWidth = circle.width / 2;
	var netBuffer = 2; // Adjust netBuffer to control how far the ball bounces from the net
	var left = boxX - halfBoxWidth;
	var right = boxX + halfBoxWidth;
	var top = boxY - box.height;
	var bottom = boxY;
	// Check if the circle intersects with the box (considering entire ball)
	return circleX + halfCircleWidth > left && circleX - halfCircleWidth < right && circleY + halfCircleWidth > top + netBuffer && circleY - halfCircleWidth < bottom;
}
function customIntersect(circle1, circle2) {
	//console.log("customIntersect  ", circle1, circle2.parent);
	var circle2X = circle2.x;
	var circle2Y = circle2.y;
	if (circle2.parent) {
		circle2X += circle2.parent.x;
		circle2Y += circle2.parent.y;
	}
	var dx = circle1.x - circle2X;
	var dy = circle1.y - circle2Y;
	var distance = Math.sqrt(dx * dx + dy * dy);
	var radiusSum = circle1.width / 2 + circle2.width / 2;
	//console.log("customIntersect  ", distance.toFixed(0), radiusSum);
	return distance < radiusSum;
}
var background;
gameInitialize();
game.update = function () {
	if (Date.now() - resetTime < nextRoundPauseDelay) {
		updateShadows();
		return;
	}
	ball.alpha = 1;
	joystick.alpha = 1;
	if (!ballCanMove && (customIntersect(ball, player1.collisionBody) || customIntersect(ball, player2.collisionBody))) {
		///|| player2.collisionBody && customIntersect(ball, player2.collisionBody) || customIntersect(ball, player1) || customIntersect(ball, player2))) {
		ballCanMove = true;
		//console.log("Player collision detected 1");
	}
	// Check for collision with the net
	if (customBoxCircleIntersect(net, ball)) {
		// Check if enough time has passed since the last collision
		if (Date.now() - ball.lastCollisionTime > 0) {
			playSound('bump', 500); // Play bump sound on collision with a cooldown of 0.5 seconds
			// Reverse ball's horizontal direction and apply some energy loss
			// Handle y reaction when intersecting from the top
			var newSpeedX = ball.speedX * 1.15 + (Math.random() - 0.5) * 0.05;
			var newSpeedY = ball.speedY * 1.15 + (Math.random() - 0.5) * 0.05;
			// Check if the ball is intersecting the net from the top
			//console.log("TOP chceck", ball.y + ball.height / 2, net.y - net.height);
			if (ball.y + ball.height / 2 <= net.y - net.height + 100) {
				//console.log("TOP collision detected ", ball.y + ball.height / 2, net.y - net.height);
				// Top collision: Reverse the horizontal direction and limit the speed
				newSpeedX = Math.sign(ball.speedX) + ball.speedX * 1.25;
				// Top collision: Reverse the vertical direction and limit the speed
				newSpeedY = -Math.min(Math.abs(newSpeedY), SPEED_LIMIT);
			} else {
				// Side collision: Reverse the horizontal direction and limit the speed
				newSpeedX = -Math.sign(newSpeedX) * Math.min(Math.abs(newSpeedX), SPEED_LIMIT);
				newSpeedY = ball.speedY;
			}
			ball.speedX = newSpeedX;
			ball.speedY = newSpeedY;
			// Update the last collision time
			ball.lastCollisionTime = Date.now();
		}
	}
	// Check for collisions with players
	var touchIndex = 0;
	if (ball.x + ball.width / 2 < 1024) {
		player2Touches = 0;
	} else if (ball.x - ball.width / 2 > 1024) {
		player1Touches = 0;
	}
	if (ball.x < 1024 && customIntersect(ball, player1.collisionBody)) {
		touchIndex = 1;
	} else if (ball.x > 1024 && customIntersect(ball, player2.collisionBody)) {
		touchIndex = 2;
	}
	if (touchIndex) {
		playSound('bump', 500); // Play bump sound on collision with a cooldown of 0.5 seconds
		// Increment touch counters
		if (touchIndex === 1) {
			if (Date.now() - touchTime >= 300) {
				player1Touches++;
			}
			player2Touches = 0; // Reset opponent's touch counter
		} else if (touchIndex === 2) {
			if (Date.now() - touchTime >= 300) {
				player2Touches++;
			}
			player1Touches = 0; // Reset opponent's touch counter
		}
		touchTime = Date.now(); // Update touchTime when a player touches the ball
		// Check for touch limit
		if (player1Touches >= 4) {
			if (servingPlayer == 1) {
				servingPlayer = 2;
			} else {
				updateScore(2);
			}
		} else if (player2Touches >= 4) {
			if (servingPlayer == 2) {
				servingPlayer = 1;
			} else {
				updateScore(1);
			}
		}
		if (player1Touches >= 4 || player2Touches >= 4) {
			playSound('whistle', 1000); // Play whistle sound
			resetBall();
		}
		//console.log("Player collision detected 2");
		var player = touchIndex === 1 ? player1 : player2;
		var collisionAngle = Math.atan2(ball.y - player.y, ball.x - player.x);
		var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY);
		var playerSpeed = Math.sqrt(player.speedX * player.speedX + player.speedY * player.speedY);
		var newSpeedX = Math.sign(speed * Math.cos(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.cos(collisionAngle)) * Math.min(Math.abs(speed * Math.cos(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.cos(collisionAngle)), SPEED_LIMIT);
		var newSpeedY = Math.sign(speed * Math.sin(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.sin(collisionAngle)) * Math.min(Math.abs(speed * Math.sin(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.sin(collisionAngle)), SPEED_LIMIT);
		// Add a bit of randomness to the ball's speed to avoid infinite stuck
		ball.speedX = newSpeedX * 1.20 + (Math.random() - 0.5) * 0.05;
		ball.speedY = newSpeedY * 1.20 + (Math.random() - 0.5) * 0.05;
	}
	ball.rotationSpeed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 0.005; // Update rotation speed based on collision
	aiUpdate();
	if (joystickDrag) {
		var dx = joystick.x - joystickBasePosition.x;
		var dy = joystick.y - joystickBasePosition.y;
		var movementRatio = 0.25; // Adjust this ratio to make the movement smoother
		player1.x = Math.max(player1.width / 2, Math.min(player1.x + dx * movementRatio, 1024 - player1.width / 2 - net.width / 2));
		player1.speedX = dx * movementRatio;
		if (!player1.jumping && dy < -80 && Date.now() - resetTime >= nextRoundPauseDelay) {
			player1.jumping = true;
		}
		//console.log("MOVE joystickDrag dx: ".concat(dx.toFixed(0), ", player1.speedX: ").concat(player1.speedX.toFixed(0), ", joystickDrag dy: ").concat(dy.toFixed(0)));
	}
	updateShadows();
};
game.down = function (x, y, obj) {
	if (!gameStarted) {
		gameStarted = true;
		startButton.destroy();
		joystick.visible = true;
	} else {
		joystickDrag = true;
	}
};
game.move = function (x, y, obj) {
	if (Date.now() - resetTime < nextRoundPauseDelay) {
		return;
	}
	handleJoystickLimits(x, y);
};
game.up = function (x, y, obj) {
	joystickDrag = false;
	//console.log("UP joystickDrag state:", joystickDrag);
	joystick.x = joystickBasePosition.x;
	joystick.y = joystickBasePosition.y;
};
function gameInitialize() {
	background = LK.getAsset('background', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2
	});
	game.addChild(background);
	initShadows();
	var parasol = LK.getAsset('parasol', {
		anchorX: 0.5,
		anchorY: 1,
		x: 1848,
		y: 1450
	});
	game.addChild(parasol);
	var chair = LK.getAsset('chair', {
		anchorX: 0.5,
		anchorY: 1,
		x: 1948,
		y: 1500
	});
	game.addChild(chair);
	var towel = LK.getAsset('towel', {
		anchorX: 0.5,
		anchorY: 1,
		x: 10,
		y: 1540
	});
	game.addChild(towel);
	beachBall = LK.getAsset('beachBall', {
		anchorX: 0.5,
		anchorY: 1,
		x: 120,
		y: 1465
	});
	game.addChild(beachBall);
	player1 = new Player(1);
	player2 = new Player(2);
	game.addChild(player1);
	game.addChild(player2);
	player1.x = PLAYER1_INITIAL_X;
	player1.y = PLAYER_INITIAL_Y;
	player2.y = PLAYER_INITIAL_Y;
	player2.x = PLAYER2_INITIAL_X;
	ball = new Ball();
	ball.x = servingPlayer === 1 ? BALL1_INITIAL_X : BALL2_INITIAL_X;
	ball.y = BALL_INITIAL_Y; // Set the ball's initial vertical position using BALL_INITIAL_Y
	ball.rotationSpeed = 0;
	game.addChild(ball);
	scoreTxt1 = new Text2('00', {
		size: 200,
		fill: "#0000d8",
		fontWeight: "bold",
		dropShadow: true
	});
	scoreTxt1.anchor.set(-1, 0);
	LK.gui.topLeft.addChild(scoreTxt1);
	scoreTxt2 = new Text2('00', {
		size: 200,
		fill: "#FF6347",
		fontWeight: "bold",
		dropShadow: true
	});
	scoreTxt2.anchor.set(2, 0);
	LK.gui.topRight.addChild(scoreTxt2);
	joystick = game.addChild(LK.getAsset('joystick', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 - 330
	}));
	joystick.visible = false;
	joystickBasePosition = {
		x: joystick.x,
		y: joystick.y
	};
	joystickBasePosition = {
		x: joystick.x,
		y: joystick.y
	};
	joystickDrag = false;
	net = new Net();
	net.x = 2048 / 2;
	net.y = 2000;
	game.addChild(net);
	touchTime = 0; // Global variable to store the touch time
	prevMouseY = null;
	lastPlayedTime = {};
	ballCanMove = false;
	SPEED_LIMIT = 50; // Define a global speed limit
	isDebug = false;
	player2Debug = false;
	player1Touches = 0;
	player2Touches = 0;
	resetTime = 0; // Global variable to store the reset time
	nextRoundPauseDelay = 1600;
	score1 = 0;
	score2 = 0;
	scoreTxt1.setText(score1.toString().padStart(2, '0'));
	scoreTxt2.setText(score2.toString().padStart(2, '0'));
	startButton = LK.getAsset('startButton', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2
	});
	game.addChild(startButton);
}
function initShadows() {
	ballShadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 2.2,
		scaleY: 0.6
	});
	player1Shadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 2.7,
		scaleY: 0.7
	});
	player2Shadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 2.7,
		scaleY: 0.7
	});
	var parasolShadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 3.5,
		scaleY: 0.65,
		x: 1900 + 10,
		y: 1445 + 10
	});
	var beachBallShadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 0.75,
		scaleY: 0.35,
		x: 120 + 20,
		y: 1465
	});
	ballShadow.x = BALL1_INITIAL_X;
	ballShadow.y = PLAYER_INITIAL_Y;
	player1Shadow.x = PLAYER1_INITIAL_X;
	player1Shadow.y = PLAYER_INITIAL_Y;
	player2Shadow.x = PLAYER2_INITIAL_X;
	player2Shadow.y = PLAYER_INITIAL_Y;
	game.addChild(parasolShadow);
	game.addChild(beachBallShadow);
	game.addChild(ballShadow);
	game.addChild(player1Shadow);
	game.addChild(player2Shadow);
}
function updateShadows() {
	// Update ball shadow position and size
	ballShadow.visible = ball.alpha != 0;
	if (ballShadow.visible) {
		ballShadow.x = ball.x;
		ballShadow.scale.x = 2.2 * (1 + (ball.y - PLAYER_INITIAL_Y) / 3000);
		ballShadow.scale.y = 0.6 * (1 + (ball.y - PLAYER_INITIAL_Y) / 3000);
	}
	// Update player1 shadow position and size
	player1Shadow.x = player1.x;
	player1Shadow.scale.x = 2.7 * (1 + (player1.y - PLAYER_INITIAL_Y) / 1500);
	player1Shadow.scale.y = 0.7 * (1 + (player1.y - PLAYER_INITIAL_Y) / 1500);
	// Update player2 shadow position and size
	player2Shadow.x = player2.x;
	player2Shadow.scale.x = 2.7 * (1 + (player2.y - PLAYER_INITIAL_Y) / 1500);
	player2Shadow.scale.y = 0.7 * (1 + (player2.y - PLAYER_INITIAL_Y) / 1500);
} /**** 
* Classes
****/ 
// Initialize rotation speed
//<Assets used in the game will automatically appear here>
// Ball class
var Ball = Container.expand(function () {
	var self = Container.call(this);
	var ballGraphics = self.attachAsset('ball', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.rotationSpeed = 0;
	var half = self.width / 2;
	self.speedX = 3;
	self.speedY = 3;
	self.accelerationY = 0.5; // Gravity
	self.friction = 0.99; // Friction to slow down the ball over time
	self.lastCollisionTime = 0; // Initialize last collision time
	self.update = function (net, player1, player2) {
		if (!ballCanMove) {
			return;
		}
		// Apply gravity
		self.speedY += self.accelerationY;
		// Apply friction
		self.speedX *= self.friction;
		self.speedY *= self.friction;
		// Apply speed limit
		self.speedX = Math.sign(self.speedX) * Math.min(Math.abs(self.speedX), SPEED_LIMIT);
		self.speedY = Math.sign(self.speedY) * Math.min(Math.abs(self.speedY), SPEED_LIMIT);
		// Update ball position
		self.x += self.speedX;
		self.y += self.speedY;
		self.rotation += self.rotationSpeed * (self.speedX > 0 ? 1 : -1); // Update ball rotation based on direction
		self.rotationSpeed *= 0.98; // Gradually decrease rotation speed
		// Check for out of bounds
		if (self.y + half > 2000) {
			self.y = 2000 - half;
			self.speedY = -Math.abs(self.speedY) * 0.7; // Make the ball bounce up with more reduced speed
			self.speedX *= 0.9; // Apply slight energy loss on horizontal speed
			playSound('whistle', 1000); // Play whistle sound when ball touches the ground with a cooldown of 1 second
			if (self.x < 1024 && servingPlayer === 2) {
				updateScore(2);
				resetBall(); // Reset the ball after scoring
			} else if (self.x >= 1024 && servingPlayer === 1) {
				updateScore(1);
				resetBall(); // Reset the ball after scoring
			} else {
				servingPlayer = servingPlayer == 1 ? 2 : 1;
				resetBall(); // Reset the ball after scoring
			}
			player1Touches = 0;
			player2Touches = 0;
		} else if (self.y - half < 0) {
			self.y = half;
			self.speedY = Math.abs(self.speedY) * 0.8; // Make the ball bounce down with reduced speed
		}
		// Check for x screen limits
		if (self.x - half < 0) {
			self.x = half;
			self.speedX = Math.abs(self.speedX); // Make the ball bounce to the right
		} else if (self.x + half > 2048) {
			self.x = 2048 - half;
			self.speedX = -Math.abs(self.speedX); // Make the ball bounce to the left
		}
		// Collision detection logic moved to game update
	};
});
// Net class
var Net = Container.expand(function () {
	var self = Container.call(this);
	var netGraphics = self.attachAsset('net', {
		anchorX: 0.5,
		anchorY: 1,
		alpha: isDebug ? 1 : 0
	});
});
// Player class
var Player = Container.expand(function (index) {
	var self = Container.call(this);
	var playerGraphics = self.attachAsset('player', {
		anchorX: 0.5,
		anchorY: 1,
		alpha: 0.98,
		scaleX: index === 2 ? -1 : 1,
		tint: index === 1 ? 0xADD8E6 : 0xFF6347 // Light blue for player 1, Tomato red for player 2
	});
	self.index = index;
	self.scale.set(1, 1); // Initialize player scale to normal
	self.jumping = false;
	self.falling = false;
	self.speedX = 0; // Initialize horizontal speed
	var collidSize = 245;
	self.collisionBody = LK.getAsset('collisionBody', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: isDebug ? 0.6 : 0,
		width: collidSize,
		height: collidSize,
		x: 0,
		y: -200
	});
	self.addChild(self.collisionBody);
	self.update = function () {
		var prevX = self.x; // Store previous x position
		var prevY = self.y; // Store previous y position
		if (self.jumping && !self.falling) {
			self.y -= 20; // Increase jump speed
			self.scale.set(1, 1 + 0.25 * (PLAYER_INITIAL_Y - self.y) / 700); // Progressive scaling when jumping
			if (self.y <= 1300) {
				self.falling = true;
			}
		}
		if (self.falling && self.y < PLAYER_INITIAL_Y) {
			self.y += 20; // Increase fall speed for smoother jump
			if (self.y > PLAYER_INITIAL_Y - 256) {
				self.scale.set(1 + 0.15 * (PLAYER_INITIAL_Y - self.y) / 700, 1 - 0.15 * (PLAYER_INITIAL_Y - self.y) / 700); // Progressive scaling when falling
			}
		}
		if (self.y >= PLAYER_INITIAL_Y) {
			self.jumping = false;
			self.falling = false;
			self.y = PLAYER_INITIAL_Y; // Ensure player lands on the ground
			// Reset scale to normal when player stops moving
			self.scale.set(1, 1);
		}
		if (self.y < 0) {
			self.y = 0; // Prevent player1 from moving above the window
		}
		if (self.index === 2 && self.x > 1024 && self.x < 1024 + self.width / 2) {
			self.x = 1024 + self.width / 2; // Prevent player2 from moving past the net
		}
		self.speedX = self.x - prevX; // Calculate horizontal speed based on movement
		self.speedY = self.y - prevY; // Calculate vertical speed based on movement
	};
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87CEEB // Sky blue background
});
/**** 
* Game Code
****/ 
var PLAYER1_INITIAL_X = 256;
var PLAYER_INITIAL_Y = 2000;
var PLAYER2_INITIAL_X = 1792;
var BALL1_INITIAL_X = 640;
var BALL_INITIAL_Y = 1256;
var BALL2_INITIAL_X = 1408;
var gameStarted = false;
var startButton = null;
var joystick;
var joystickBasePosition;
var joystickDrag;
var touchTime; // Global variable to store the touch time
var prevMouseY;
var lastPlayedTime;
var ballCanMove;
var SPEED_LIMIT; // Define a global speed limit
var isDebug = false;
var player2Debug;
var player1Touches;
var player2Touches;
var resetTime; // Global variable to store the reset time
var nextRoundPauseDelay;
var score1;
var score2;
var finalScore = 15; // Define the final score for the game
var servingPlayer = 1; // Initialize serving player to player 1
var ballShadow;
var player1Shadow;
var player2Shadow;
var beachBall = null;
function playSound(soundId, cooldown) {
	var currentTime = Date.now();
	if (!lastPlayedTime[soundId] || currentTime - lastPlayedTime[soundId] > cooldown) {
		LK.getSound(soundId).play();
		lastPlayedTime[soundId] = currentTime;
	}
}
function updateScore(index) {
	if (index === 1) {
		score1 += 1;
		scoreTxt1.setText(score1.toString().padStart(2, '0'));
	} else if (index === 2) {
		score2 += 1;
		scoreTxt2.setText(score2.toString().padStart(2, '0'));
	}
	// Check for game over condition
	if ((score1 >= finalScore || score2 >= finalScore) && Math.abs(score1 - score2) >= 2) {
		var resultText = new Text2(score1 > score2 ? 'VICTORY!' : 'DEFEAT!', {
			size: 300,
			fill: "#FFFFFF",
			fontWeight: "bold",
			dropShadow: true
		});
		resultText.anchor.set(0.5, 2);
		//resultText.x = 0;
		//resultText.y = -512;
		LK.gui.center.addChild(resultText);
		LK.setTimeout(function () {
			LK.showGameOver();
		}, 2000);
	}
}
function handleJoystickLimits(x, y) {
	if (joystickDrag) {
		var dx = x - joystickBasePosition.x;
		var dy = y - joystickBasePosition.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		var maxDistance = joystick.width / 3; // Max distance joystick can move from center
		if (distance > maxDistance) {
			var angle = Math.atan2(dy, dx);
			dx = Math.cos(angle) * maxDistance;
			dy = Math.sin(angle) * maxDistance;
		}
		joystick.x = joystickBasePosition.x + dx;
		joystick.y = joystickBasePosition.y + dy;
	}
}
function aiUpdate() {
	if (ball.x > 1024) {
		if (ball.x > player2.x + player2.width / 2) {
			player2.x += 5; // Move right
		} else if (ball.x < player2.x - player2.width / 2) {
			player2.x -= 5; // Move left
		}
	}
	if (!player2.jumping && player2.y >= PLAYER_INITIAL_Y && ball.y < player2.y && Math.abs(ball.x - player2.x) < player2.width) {
		player2.jumping = true;
	}
}
function resetBall() {
	ball.y = BALL_INITIAL_Y; // Set the ball's initial vertical position using BALL_INITIAL_Y
	ball.speedX = 3;
	ball.speedY = 3; // Reset speed
	ball.accelerationY = 0.5; // Reset gravity
	ball.friction = 0.99; // Reset friction
	ballCanMove = false; // Reset ball movement flag
	ball.rotation = 0; // Reset rotation angle
	resetTime = Date.now(); // Update reset time
	player1Touches = 0;
	player2Touches = 0;
	ball.alpha = 0;
	joystick.alpha = 0.;
	ball.x = servingPlayer === 1 ? BALL1_INITIAL_X : BALL2_INITIAL_X;
	// Move players progressively to their initial X positions
	var movePlayerToInitialX = function movePlayerToInitialX(player, initialX) {
		var moveInterval = LK.setInterval(function () {
			updateShadows();
			if (Math.abs(player.x - initialX) < 5) {
				player.x = initialX;
				LK.clearInterval(moveInterval);
			} else {
				player.x += (initialX - player.x) * 0.1;
			}
		}, 16); // Move players every 16ms (approximately 60 FPS)
	};
	movePlayerToInitialX(player1, PLAYER1_INITIAL_X);
	movePlayerToInitialX(player2, PLAYER2_INITIAL_X);
}
function customBoxCircleIntersect(box, circle) {
	var circleX = circle.x;
	var circleY = circle.y;
	if (circle.parent) {
		circleX += circle.parent.x;
		circleY += circle.parent.y;
	}
	var boxX = box.x;
	var boxY = box.y;
	var halfBoxWidth = box.width / 2;
	var halfCircleWidth = circle.width / 2;
	var netBuffer = 2; // Adjust netBuffer to control how far the ball bounces from the net
	var left = boxX - halfBoxWidth;
	var right = boxX + halfBoxWidth;
	var top = boxY - box.height;
	var bottom = boxY;
	// Check if the circle intersects with the box (considering entire ball)
	return circleX + halfCircleWidth > left && circleX - halfCircleWidth < right && circleY + halfCircleWidth > top + netBuffer && circleY - halfCircleWidth < bottom;
}
function customIntersect(circle1, circle2) {
	//console.log("customIntersect  ", circle1, circle2.parent);
	var circle2X = circle2.x;
	var circle2Y = circle2.y;
	if (circle2.parent) {
		circle2X += circle2.parent.x;
		circle2Y += circle2.parent.y;
	}
	var dx = circle1.x - circle2X;
	var dy = circle1.y - circle2Y;
	var distance = Math.sqrt(dx * dx + dy * dy);
	var radiusSum = circle1.width / 2 + circle2.width / 2;
	//console.log("customIntersect  ", distance.toFixed(0), radiusSum);
	return distance < radiusSum;
}
var background;
gameInitialize();
game.update = function () {
	if (Date.now() - resetTime < nextRoundPauseDelay) {
		updateShadows();
		return;
	}
	ball.alpha = 1;
	joystick.alpha = 1;
	if (!ballCanMove && (customIntersect(ball, player1.collisionBody) || customIntersect(ball, player2.collisionBody))) {
		///|| player2.collisionBody && customIntersect(ball, player2.collisionBody) || customIntersect(ball, player1) || customIntersect(ball, player2))) {
		ballCanMove = true;
		//console.log("Player collision detected 1");
	}
	// Check for collision with the net
	if (customBoxCircleIntersect(net, ball)) {
		// Check if enough time has passed since the last collision
		if (Date.now() - ball.lastCollisionTime > 0) {
			playSound('bump', 500); // Play bump sound on collision with a cooldown of 0.5 seconds
			// Reverse ball's horizontal direction and apply some energy loss
			// Handle y reaction when intersecting from the top
			var newSpeedX = ball.speedX * 1.15 + (Math.random() - 0.5) * 0.05;
			var newSpeedY = ball.speedY * 1.15 + (Math.random() - 0.5) * 0.05;
			// Check if the ball is intersecting the net from the top
			//console.log("TOP chceck", ball.y + ball.height / 2, net.y - net.height);
			if (ball.y + ball.height / 2 <= net.y - net.height + 100) {
				//console.log("TOP collision detected ", ball.y + ball.height / 2, net.y - net.height);
				// Top collision: Reverse the horizontal direction and limit the speed
				newSpeedX = Math.sign(ball.speedX) + ball.speedX * 1.25;
				// Top collision: Reverse the vertical direction and limit the speed
				newSpeedY = -Math.min(Math.abs(newSpeedY), SPEED_LIMIT);
			} else {
				// Side collision: Reverse the horizontal direction and limit the speed
				newSpeedX = -Math.sign(newSpeedX) * Math.min(Math.abs(newSpeedX), SPEED_LIMIT);
				newSpeedY = ball.speedY;
			}
			ball.speedX = newSpeedX;
			ball.speedY = newSpeedY;
			// Update the last collision time
			ball.lastCollisionTime = Date.now();
		}
	}
	// Check for collisions with players
	var touchIndex = 0;
	if (ball.x + ball.width / 2 < 1024) {
		player2Touches = 0;
	} else if (ball.x - ball.width / 2 > 1024) {
		player1Touches = 0;
	}
	if (ball.x < 1024 && customIntersect(ball, player1.collisionBody)) {
		touchIndex = 1;
	} else if (ball.x > 1024 && customIntersect(ball, player2.collisionBody)) {
		touchIndex = 2;
	}
	if (touchIndex) {
		playSound('bump', 500); // Play bump sound on collision with a cooldown of 0.5 seconds
		// Increment touch counters
		if (touchIndex === 1) {
			if (Date.now() - touchTime >= 300) {
				player1Touches++;
			}
			player2Touches = 0; // Reset opponent's touch counter
		} else if (touchIndex === 2) {
			if (Date.now() - touchTime >= 300) {
				player2Touches++;
			}
			player1Touches = 0; // Reset opponent's touch counter
		}
		touchTime = Date.now(); // Update touchTime when a player touches the ball
		// Check for touch limit
		if (player1Touches >= 4) {
			if (servingPlayer == 1) {
				servingPlayer = 2;
			} else {
				updateScore(2);
			}
		} else if (player2Touches >= 4) {
			if (servingPlayer == 2) {
				servingPlayer = 1;
			} else {
				updateScore(1);
			}
		}
		if (player1Touches >= 4 || player2Touches >= 4) {
			playSound('whistle', 1000); // Play whistle sound
			resetBall();
		}
		//console.log("Player collision detected 2");
		var player = touchIndex === 1 ? player1 : player2;
		var collisionAngle = Math.atan2(ball.y - player.y, ball.x - player.x);
		var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY);
		var playerSpeed = Math.sqrt(player.speedX * player.speedX + player.speedY * player.speedY);
		var newSpeedX = Math.sign(speed * Math.cos(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.cos(collisionAngle)) * Math.min(Math.abs(speed * Math.cos(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.cos(collisionAngle)), SPEED_LIMIT);
		var newSpeedY = Math.sign(speed * Math.sin(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.sin(collisionAngle)) * Math.min(Math.abs(speed * Math.sin(collisionAngle) + (playerSpeed + 0.1 * Math.sign(playerSpeed)) * Math.sin(collisionAngle)), SPEED_LIMIT);
		// Add a bit of randomness to the ball's speed to avoid infinite stuck
		ball.speedX = newSpeedX * 1.20 + (Math.random() - 0.5) * 0.05;
		ball.speedY = newSpeedY * 1.20 + (Math.random() - 0.5) * 0.05;
	}
	ball.rotationSpeed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 0.005; // Update rotation speed based on collision
	aiUpdate();
	if (joystickDrag) {
		var dx = joystick.x - joystickBasePosition.x;
		var dy = joystick.y - joystickBasePosition.y;
		var movementRatio = 0.25; // Adjust this ratio to make the movement smoother
		player1.x = Math.max(player1.width / 2, Math.min(player1.x + dx * movementRatio, 1024 - player1.width / 2 - net.width / 2));
		player1.speedX = dx * movementRatio;
		if (!player1.jumping && dy < -80 && Date.now() - resetTime >= nextRoundPauseDelay) {
			player1.jumping = true;
		}
		//console.log("MOVE joystickDrag dx: ".concat(dx.toFixed(0), ", player1.speedX: ").concat(player1.speedX.toFixed(0), ", joystickDrag dy: ").concat(dy.toFixed(0)));
	}
	updateShadows();
};
game.down = function (x, y, obj) {
	if (!gameStarted) {
		gameStarted = true;
		startButton.destroy();
		joystick.visible = true;
	} else {
		joystickDrag = true;
	}
};
game.move = function (x, y, obj) {
	if (Date.now() - resetTime < nextRoundPauseDelay) {
		return;
	}
	handleJoystickLimits(x, y);
};
game.up = function (x, y, obj) {
	joystickDrag = false;
	//console.log("UP joystickDrag state:", joystickDrag);
	joystick.x = joystickBasePosition.x;
	joystick.y = joystickBasePosition.y;
};
function gameInitialize() {
	background = LK.getAsset('background', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2
	});
	game.addChild(background);
	initShadows();
	var parasol = LK.getAsset('parasol', {
		anchorX: 0.5,
		anchorY: 1,
		x: 1848,
		y: 1450
	});
	game.addChild(parasol);
	var chair = LK.getAsset('chair', {
		anchorX: 0.5,
		anchorY: 1,
		x: 1948,
		y: 1500
	});
	game.addChild(chair);
	var towel = LK.getAsset('towel', {
		anchorX: 0.5,
		anchorY: 1,
		x: 10,
		y: 1540
	});
	game.addChild(towel);
	beachBall = LK.getAsset('beachBall', {
		anchorX: 0.5,
		anchorY: 1,
		x: 120,
		y: 1465
	});
	game.addChild(beachBall);
	player1 = new Player(1);
	player2 = new Player(2);
	game.addChild(player1);
	game.addChild(player2);
	player1.x = PLAYER1_INITIAL_X;
	player1.y = PLAYER_INITIAL_Y;
	player2.y = PLAYER_INITIAL_Y;
	player2.x = PLAYER2_INITIAL_X;
	ball = new Ball();
	ball.x = servingPlayer === 1 ? BALL1_INITIAL_X : BALL2_INITIAL_X;
	ball.y = BALL_INITIAL_Y; // Set the ball's initial vertical position using BALL_INITIAL_Y
	ball.rotationSpeed = 0;
	game.addChild(ball);
	scoreTxt1 = new Text2('00', {
		size: 200,
		fill: "#0000d8",
		fontWeight: "bold",
		dropShadow: true
	});
	scoreTxt1.anchor.set(-1, 0);
	LK.gui.topLeft.addChild(scoreTxt1);
	scoreTxt2 = new Text2('00', {
		size: 200,
		fill: "#FF6347",
		fontWeight: "bold",
		dropShadow: true
	});
	scoreTxt2.anchor.set(2, 0);
	LK.gui.topRight.addChild(scoreTxt2);
	joystick = game.addChild(LK.getAsset('joystick', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 - 330
	}));
	joystick.visible = false;
	joystickBasePosition = {
		x: joystick.x,
		y: joystick.y
	};
	joystickBasePosition = {
		x: joystick.x,
		y: joystick.y
	};
	joystickDrag = false;
	net = new Net();
	net.x = 2048 / 2;
	net.y = 2000;
	game.addChild(net);
	touchTime = 0; // Global variable to store the touch time
	prevMouseY = null;
	lastPlayedTime = {};
	ballCanMove = false;
	SPEED_LIMIT = 50; // Define a global speed limit
	isDebug = false;
	player2Debug = false;
	player1Touches = 0;
	player2Touches = 0;
	resetTime = 0; // Global variable to store the reset time
	nextRoundPauseDelay = 1600;
	score1 = 0;
	score2 = 0;
	scoreTxt1.setText(score1.toString().padStart(2, '0'));
	scoreTxt2.setText(score2.toString().padStart(2, '0'));
	startButton = LK.getAsset('startButton', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2
	});
	game.addChild(startButton);
}
function initShadows() {
	ballShadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 2.2,
		scaleY: 0.6
	});
	player1Shadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 2.7,
		scaleY: 0.7
	});
	player2Shadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 2.7,
		scaleY: 0.7
	});
	var parasolShadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 3.5,
		scaleY: 0.65,
		x: 1900 + 10,
		y: 1445 + 10
	});
	var beachBallShadow = LK.getAsset('shadow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.33,
		scaleX: 0.75,
		scaleY: 0.35,
		x: 120 + 20,
		y: 1465
	});
	ballShadow.x = BALL1_INITIAL_X;
	ballShadow.y = PLAYER_INITIAL_Y;
	player1Shadow.x = PLAYER1_INITIAL_X;
	player1Shadow.y = PLAYER_INITIAL_Y;
	player2Shadow.x = PLAYER2_INITIAL_X;
	player2Shadow.y = PLAYER_INITIAL_Y;
	game.addChild(parasolShadow);
	game.addChild(beachBallShadow);
	game.addChild(ballShadow);
	game.addChild(player1Shadow);
	game.addChild(player2Shadow);
}
function updateShadows() {
	// Update ball shadow position and size
	ballShadow.visible = ball.alpha != 0;
	if (ballShadow.visible) {
		ballShadow.x = ball.x;
		ballShadow.scale.x = 2.2 * (1 + (ball.y - PLAYER_INITIAL_Y) / 3000);
		ballShadow.scale.y = 0.6 * (1 + (ball.y - PLAYER_INITIAL_Y) / 3000);
	}
	// Update player1 shadow position and size
	player1Shadow.x = player1.x;
	player1Shadow.scale.x = 2.7 * (1 + (player1.y - PLAYER_INITIAL_Y) / 1500);
	player1Shadow.scale.y = 0.7 * (1 + (player1.y - PLAYER_INITIAL_Y) / 1500);
	// Update player2 shadow position and size
	player2Shadow.x = player2.x;
	player2Shadow.scale.x = 2.7 * (1 + (player2.y - PLAYER_INITIAL_Y) / 1500);
	player2Shadow.scale.y = 0.7 * (1 + (player2.y - PLAYER_INITIAL_Y) / 1500);
}
:quality(85)/https://cdn.frvr.ai/667b12f12fe7ff4a7658074d.png%3F3) 
 white volley ball.
:quality(85)/https://cdn.frvr.ai/667b52222fe7ff4a765808d8.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/667ee6c8dd6f02985af5bd2d.png%3F3) 
 top view of a concave blue (0xADD8E6) plastic button. 4 small black directionnal chevrons engraved : right, left, top , bottom.. Photorealistic
:quality(85)/https://cdn.frvr.ai/667f2e1add6f02985af5bf4f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/667f2fe3dd6f02985af5bf7b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/667ff0486b731bccb12bc88f.png%3F3) 
 Beach ball. photo
:quality(85)/https://cdn.frvr.ai/667ffe216b731bccb12bc8f7.png%3F3) 
 full view of a Beach white towel with colored infinte logo. placed on the sand. photo
:quality(85)/https://cdn.frvr.ai/66807f21e7b74e71994abcc0.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6682ec9a6b731bccb12bce97.png%3F3) 
 Start button in the shape of a white beach volleyball with « START » written on it in black. Photo