User prompt
even with value 200 `Math.abs(self.parent.x + self.x - player.x) < 200 && Math.abs(self.parent.y + self.y - player.y) < 200 ` doesn't work. find another way to detect proximity
Code edit (1 edits merged)
Please save this source code
User prompt
`isBonusActive && Math.abs(self.x - player.x) < 150 && Math.abs(self.y - player.y) < 150 ||` I think self.x is relative it should take into acount parents position
Code edit (4 edits merged)
Please save this source code
User prompt
`isBonusActive && self.x === player.x && self.y === player.y ` is too restrictive, the detristus have just to pass near the player to be taken
User prompt
consition in `if (!self.taken && (isBonusActive || player.body.torso.intersects(self)))` is not enough because when isBonusActive still the detritus must reach player position (angle/ node) on the road
User prompt
while bonus is active, make detritus reaching the player position automatically taken without need of intersect
Code edit (3 edits merged)
Please save this source code
User prompt
in startBonusAnimation, call stopBouns() after BONUS_DURATION_SEC seconds
Code edit (9 edits merged)
Please save this source code
User prompt
in startBonusAnimation, when setting isBonusActive active stop bgMusic and play bonusMusic
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
in Player.update(), add other flashy colors to animate player tint when bonus is active and make them change fast
Code edit (1 edits merged)
Please save this source code
User prompt
in Player.update(), animate player tint using tintPlayer() from white to green and vice versa
Code edit (14 edits merged)
Please save this source code
User prompt
in player class while isBonusActive, animate player tint using tintPlayer() from white to green and vice versa
Code edit (1 edits merged)
Please save this source code
User prompt
when bonus reaches 1st target then it moves toward the player while decreasing width and height to 10
Code edit (1 edits merged)
Please save this source code
User prompt
Animate bonus : move to road x,y while increasing width and height to 1024
Code edit (7 edits merged)
Please save this source code
User prompt
in startBonusAnimation add a green screen flash
User prompt
play startButon audio when start button pressed
/**** 
* Classes
****/ 
var BoilingLava = Container.expand(function () {
	var self = Container.call(this);
	var lavaBubbles = [];
	self.x = 400;
	self.y = 400;
	// Function to create a new lava bubble
	function createLavaBubble() {
		var bubble = self.attachAsset('lavaBubble', {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 1,
			scaleY: 1,
			x: Math.random() * 200 - 100,
			// Random x position within a range
			y: Math.random() * 200 - 100 // Random y position within a range
		});
		lavaBubbles.push(bubble);
	}
	// Update function to animate the lava bubbles
	self.update = function () {
		if (LK.ticks % 10 == 0) {
			// Emit a bubble every second
			createLavaBubble();
		}
		for (var i = lavaBubbles.length - 1; i >= 0; i--) {
			var bubble = lavaBubbles[i];
			bubble.y -= 2; // Move bubble upwards
			bubble.alpha -= 0.015; // Fade out bubble
			if (bubble.alpha <= 0) {
				bubble.destroy();
				lavaBubbles.splice(i, 1);
			}
		}
	};
	return self;
});
/** 
* config {
* 	x        : Number || 0,
* 	y        : Number || 0,
* 	rotation : Number || 0,
* }
**/ 
var ConfigContainer = Container.expand(function (config) {
	var self = Container.call(this);
	config = config || {};
	;
	self.x = config.x || 0;
	self.y = config.y || 0;
	self.rotation = config.rotation || 0;
	if (config.scale !== undefined || config.scaleX !== undefined || config.scaleY !== undefined) {
		var scaleX = config.scaleX !== undefined ? config.scaleX : config.scale !== undefined ? config.scale : 1;
		var scaleY = config.scaleY !== undefined ? config.scaleY : config.scale !== undefined ? config.scale : 1;
		self.scale.set(scaleX, scaleY);
	}
	;
	return self;
});
var Wall = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var wallIndex = Math.floor(Math.random() * ROAD_GEN_WALL_ASSETS);
	var scale = 1; //0.9 + 0.2 * Math.random();
	self.attachAsset('wall' + wallIndex, {
		anchorX: 0.5,
		anchorY: 1,
		scaleX: scale,
		//Math.random() < 0.5 ? -scale : scale,
		scaleY: scale
	});
	self.taken = false;
	self.update = function () {
		//log("player.sliding:", player.sliding);
		if (!player.sliding && player.body.torso.intersects(self)) {
			player.hitWall = true;
			player.sliding = true; // Keep down
			player.isDead = true;
			tintPlayer(0x666666);
			log("Player hit a wall!");
			// Flash screen red for 1 second (1000ms) to show we are dead.
			LK.effects.flashScreen(0xff0000, 1000);
			// Play lose sound
			LK.getSound('lose').play();
			callGameOver(true);
		}
	};
	return self;
});
var Sun = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	for (var i = 0; i < 5; i++) {
		self.attachAsset('circle', {
			width: 200 + 50 * i,
			height: 200 + 50 * i,
			color: 0xFFFF00,
			alpha: 0.1,
			anchorX: 0.5,
			anchorY: 0.5
		});
	}
	var sunAsset = self.attachAsset('sun', {
		anchorX: 0.5075,
		anchorY: 0.5,
		alpha: 0.75
	});
	;
	self.update = update;
	;
	function update() {
		if (LK.ticks % 30 == 0) {
			for (var i = 0; i < self.children.length - 1; i++) {
				var circle = self.children[i];
				if (i % 2 !== 0) {
					circle.alpha = 0.01 + 0.09 * Math.abs(Math.sin(LK.ticks / 90));
				} else {
					circle.alpha = 0.01 + 0.09 * Math.abs(Math.cos(LK.ticks / 90));
				}
			}
		}
	}
});
var RoadSegment = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var background = self.addChild(new Container());
	self.background = background;
	self.attachAsset('roadSegment', {
		anchorX: 1,
		anchorY: 1,
		tint: 0x62C344
	});
	self.segment = self.attachAsset('roadSegment', {
		anchorX: 1,
		anchorY: 1,
		scaleX: 0.95,
		scaleY: 0.95,
		tint: 0x62C344 // TEMP DEBUG0x555555
	});
	self.segmentRiver = self.attachAsset('lava', {
		anchorX: 1,
		anchorY: 1,
		scaleX: 0.95,
		scaleY: 0.95,
		visible: false
	});
	;
	self.update = update;
	self.regenerate = regenerate;
	self.fadeout = false;
	self.distance = config.distance;
	self.step = config.step;
	self.stepChance = config.stepChance;
	self.previous = config.previous;
	self.isRiver = false;
	self.isWall = false;
	;
	function attachAssetRadial(parent, asset, obj) {
		var offsetTotal = ROAD_SEGMENT_HEIGHT - (obj.offset || 10);
		var rotation = -(1 - obj.anchorR) * ROAD_SEGMENT_ANGLE;
		parent.attachAsset(asset, {
			x: Math.sin(rotation) * offsetTotal,
			y: Math.cos(rotation) * -offsetTotal,
			width: obj.width,
			height: obj.height,
			anchorX: obj.anchorX,
			anchorY: obj.anchorY,
			scaleX: obj.scaleX || 1,
			scaleY: obj.scaleY || 1,
			rotation: (obj.rotation || 0) + 0
		});
	}
	function attachObjectRadial(parent, object, obj) {
		var offsetTotal = ROAD_SEGMENT_HEIGHT - (obj.offset || 10);
		var rotation = -(1 - obj.anchorR) * ROAD_SEGMENT_ANGLE;
		object.x = Math.sin(rotation) * offsetTotal - (obj.levitOffset && !obj.isOver || 0); //125* (obj.levitOffset || 0) - 400 * (obj.flying || 0);
		object.y = Math.cos(rotation) * -offsetTotal - (obj.levitOffset || 0);
		//object.width = obj.width;
		//object.height = obj.height;
		object.anchorX = obj.anchorX;
		object.anchorY = obj.anchorY;
		object.scaleX = obj.scaleX || 1;
		object.scaleY = obj.scaleY || 1;
		object.rotation = (obj.rotation || 0) + 0;
		parent.addChild(object);
	}
	function generateBackground() {
		//log(self.distance);
		background.removeChildren();
		self.isRiver = false;
		self.isWall = false;
		self.segmentRiver.visible = false;
		var plantCount = Math.floor(Math.random() * (ROAD_GEN_PLANT_MAX + 1));
		if (self.distance === SCORE_TOTAL_DISTANCE) {
			attachAssetRadial(background, 'flag', {
				anchorR: 0.5,
				anchorX: 0.1,
				anchorY: 1
			});
		} else {
			if (Math.random() < ROAD_GEN_DETRITUS_CHANCE) {
				var detritus = new Detritus();
				var scale = 1; //0.9 + 0.2 * Math.random();
				attachObjectRadial(background, detritus, {
					anchorR: 0.1 + 0.8 * Math.random(),
					anchorX: 0.5,
					anchorY: 0.1,
					scaleX: Math.random() < 0.5 ? -scale : scale,
					scaleY: scale,
					rotation: -Math.PI * 0.25 + Math.random() * Math.PI * 0.5,
					levitOffset: 170
				});
			} else if (Math.random() < ROAD_GEN_DETRITUS_FLYING_CHANCE) {
				var detritusFlying = new DetritusFlying();
				var scale = 1; //0.9 + 0.2 * Math.random();
				attachObjectRadial(background, detritusFlying, {
					anchorR: 0.1 + 0.8 * Math.random(),
					anchorX: 0.5,
					anchorY: 0.1,
					scaleX: Math.random() < 0.5 ? -scale : scale,
					scaleY: scale,
					rotation: -Math.PI * 0.25 + Math.random() * Math.PI * 0.5,
					flying: 1,
					levitOffset: 700
				});
			} else if (road && road.riverSegmentCount < ROAD_MAX_RIVER_SEGMENTS && !self.isRiver && !self.isWall && !self.previous.isWall && (!self.previous.previous || !self.previous.previous.isWall) && (!self.previous.previous.previous || !self.previous.previous.previous.isWall) && (!self.previous.previous.previous.previous || !self.previous.previous.previous.previous.isWall) && Math.random() < ROAD_GEN_FRAGILE_NATURE_CHANCE) {
				self.isRiver = true;
				self.segmentRiver.visible = true;
				road.riverSegmentCount++;
				self.previous.isRiver = true;
				self.previous.segmentRiver.visible = true;
				road.riverSegmentCount++;
				attachObjectRadial(background, new BoilingLava(), {
					anchorR: 0.1 + 0.8 * Math.random()
				});
				attachObjectRadial(self.previous.background, new BoilingLava(), {
					anchorR: 0.1 + 0.8 * Math.random()
				});
			} else if (road && road.wallSegmentCount < ROAD_MAX_WALL_SEGMENTS && !self.isWall && !self.isRiver && !self.previous.isRiver && (!self.previous.previous || !self.previous.previous.isRiver) && (!self.previous.previous.previous || !self.previous.previous.previous.isRiver) && (!self.previous.previous.previous.previous || !self.previous.previous.previous.previous.isRiver) && Math.random() < ROAD_GEN_WALL_CHANCE) {
				self.isWall = true;
				road.wallSegmentCount++;
				var wallIndex = Math.floor(Math.random() * ROAD_GEN_WALL_ASSETS);
				var scale = 1; //0.9 + 0.2 * Math.random();
				var anchorR = 0.5; // 0.4 + 0.2 * Math.random();
				attachObjectRadial(background, new Wall(), {
					anchorR: anchorR,
					anchorX: 0.5,
					anchorY: 1,
					scaleX: scale,
					scaleY: scale
				});
				if (wallIndex == 0) {
					// for pipes : add 2 other above
					attachObjectRadial(background, new Wall(), {
						anchorR: anchorR,
						anchorX: 0.5,
						anchorY: 1,
						scaleX: scale,
						scaleY: scale,
						levitOffset: 250,
						isOver: true
					});
					attachObjectRadial(background, new Wall(), {
						anchorR: anchorR,
						anchorX: 0.5,
						anchorY: 1,
						scaleX: scale,
						scaleY: scale,
						levitOffset: 500,
						isOver: true
					});
				}
			} else {
				if (Math.random() < ROAD_GEN_TREE_CHANCE) {
					var treeIndex = Math.floor(Math.random() * ROAD_GEN_TREE_ASSETS);
					var scale = 0.9 + 0.2 * Math.random();
					attachAssetRadial(background, 'tree' + treeIndex, {
						anchorR: 0.4 + 0.2 * Math.random(),
						anchorX: 0.5,
						anchorY: 1,
						scaleX: scale,
						scaleY: scale
					});
				}
				for (var i = 0; i < plantCount; i++) {
					var plantIndex = Math.floor(Math.random() * ROAD_GEN_PLANT_ASSETS);
					var scale = 0.9 + 0.2 * Math.random();
					attachAssetRadial(background, 'plant' + plantIndex, {
						anchorR: 0.1 + 0.8 * Math.random(),
						anchorX: 0.5,
						anchorY: 1,
						scaleX: Math.random() < 0.5 ? -scale : scale,
						scaleY: scale
					});
				}
			}
		}
		if (Math.random() < ROAD_GEN_CLOUD_CHANCE) {
			var cloudIndex = Math.floor(Math.random() * ROAD_GEN_CLOUD_ASSETS);
			var scale = 1.0 + 1.5 * Math.random();
			attachAssetRadial(background, 'cloud' + cloudIndex, {
				offset: -1000,
				anchorR: -1.0 + 2.0 * Math.random(),
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: scale,
				scaleY: scale,
				rotation: -0.2
			});
		}
	}
	function regenerate(incrementDistance) {
		if (incrementDistance) {
			self.distance += SCORE_SEGMENT_DISTANCE * ROAD_SEGMENT_COUNT;
		}
		var stepping = self.distance <= SCORE_TOTAL_DISTANCE;
		var stepChance = self.previous.stepChance;
		var stepUp = stepping && (self.previous.step < ROAD_STEP_COUNT ? Math.random() < self.stepChance : false);
		var stepDown = stepping && (self.previous.step > 0 ? Math.random() < self.stepChance : false);
		if (stepUp || stepDown) {
			if (stepUp && stepDown) {
				if (Math.random() < 0.5) {
					stepDown = false;
				} else {
					stepUp = false;
				}
			}
			self.step = self.previous.step + (stepUp ? 1 : -1);
			self.stepChance = ROAD_STEP_CHANCE_BASE;
		} else {
			self.step = self.previous.step;
			self.stepChance = self.previous.stepChance + ROAD_STEP_CHANCE_INCREMENT;
		}
		generateBackground();
		self.fadeout = false;
		rescale();
	}
	function update() {
		if (!isActive()) {
			return;
		}
		if (self.fadeout && self.alpha > 0) {
			if ((self.alpha -= ROAD_SEGMENT_FADEOUT) < 0) {
				self.alpha = 0;
			}
		} else if (!self.fadeout && self.alpha < 1) {
			if ((self.alpha += ROAD_SEGMENT_FADEOUT) > 1) {
				self.alpha = 1;
			}
		}
	}
	function rescale() {
		var newScale = (ROAD_STEP_HEIGHT_BASE + self.step * ROAD_STEP_HEIGHT_INCREMENT) / ROAD_SEGMENT_HEIGHT; // TEMP
		self.scale.set(newScale);
	}
	;
	rescale();
});
var Road = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var segments = [];
	self.segments = segments;
	self.riverSegmentCount = 0;
	self.wallSegmentCount = 0;
	for (var i = 0; i < ROAD_SEGMENT_COUNT; i++) {
		var segment = segments[i] = self.addChild(new RoadSegment({
			index: i,
			previous: i > 0 ? segments[i - 1] : undefined,
			rotation: i * ROAD_SEGMENT_ANGLE,
			distance: (i - 1) * SCORE_SEGMENT_DISTANCE,
			stepChance: ROAD_STEP_CHANCE_BASE,
			step: ROAD_STEP_DEFAULT
		}));
		if (i === ROAD_SEGMENT_COUNT - 1) {
			segments[0].previous = segment;
		}
		if (i >= ROAD_FREE_RUN_COUNT) {
			segment.regenerate();
		}
		self.attachAsset('roadSegment', {
			anchorX: 1,
			anchorY: 1,
			scaleX: 0.30,
			scaleY: 0.27,
			rotation: i * ROAD_SEGMENT_ANGLE,
			tint: 0x8b4513
		});
	}
	self.earthCore = self.attachAsset('earthCore', {
		width: 470,
		height: 470,
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.75,
		//tint: 0x62C344
		//tint: 0x87CEEB,
		tint: 0x888888
	});
	self.earthCore2 = self.attachAsset('earthCore', {
		width: 470,
		height: 470,
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: -1,
		scaleY: -1,
		alpha: 0.75,
		tint: 0x888888
	});
	self.attachAsset('ring', {
		width: 560,
		height: 560,
		anchorX: 0.5,
		anchorY: 0.5,
		tint: 0xff7518,
		blendMode: 3
	});
	var destroyIndex = -1;
	var regenerateIndex = -1;
	;
	self.rotate = rotate;
	self.getIndex = getIndex;
	self.getNode = getNode;
	self.getNodeDist = getNodeDist;
	;
	function rotate(speed) {
		if (!isActive()) {
			return;
		}
		self.rotation -= speed;
		var newDestroyIndex = getIndex(ROAD_SEGMENT_DESTROY);
		var newRegenerateIndex = getIndex(20); // TEMP
		if (newDestroyIndex >= 0 && newDestroyIndex !== destroyIndex && !segments[newDestroyIndex].fadeout) {
			destroyIndex = newDestroyIndex;
			if (segments[destroyIndex].isRiver) {
				self.riverSegmentCount--;
				self.riverSegmentCount = Math.max(0, self.riverSegmentCount);
			}
			if (segments[destroyIndex].isWall) {
				self.wallSegmentCount--;
				self.wallSegmentCount = Math.max(0, self.wallSegmentCount);
			}
			segments[destroyIndex].fadeout = true;
		}
		if (newRegenerateIndex >= 0 && newRegenerateIndex !== regenerateIndex && segments[newRegenerateIndex].fadeout) {
			regenerateIndex = newRegenerateIndex;
			segments[regenerateIndex].regenerate(true);
		}
		self.earthCore.rotation -= 0.001;
		self.earthCore2.rotation += 0.005;
	}
	function getIndex(shift) {
		return Math.floor(-self.rotation / MATH_2_PI * ROAD_SEGMENT_COUNT + (shift || 0) + 1) % ROAD_SEGMENT_COUNT;
	}
	function getNode(shift) {
		return segments[getIndex(shift)];
	}
	function getNodeDist() {
		return ROAD_SEGMENT_ANGLE - -self.rotation % ROAD_SEGMENT_ANGLE;
	}
});
var PlayerBody = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	// Animation settings
	var animation = animationStand;
	var alpha = 0;
	var blendAnimation = undefined;
	var blendDecrement = 0;
	var blendAlpha = 0;
	var afterTint = 0xAAAAAA;
	// Body settings
	var bodyOffsetY = config.y || 0;
	var pelvisOffsetX = -35; // old -10;
	var armUpperAngle = -3 * MATH_EIGHTH_PI;
	var armUpperOffsetX = 30; // old 15;
	var armUpperOffsetY = 10; // old 30
	var armLowerAngle = -MATH_QUARTER_PI;
	var armLowerOffsetY = 90;
	var legUpperAngle = 3 * MATH_EIGHTH_PI;
	var legUpperOffsetX = -0;
	var legLowerOffsetY = 135;
	var footOffsetY = 100;
	// Create and attach body parts
	var armUpperRight = self.addChild(new ConfigContainer({
		y: armUpperOffsetY
	}));
	var legUpperRight = self.addChild(new ConfigContainer({
		x: pelvisOffsetX + legUpperOffsetX
	}));
	var torso = self.attachAsset('torso', {
		anchorX: 0.5
	});
	var head = self.attachAsset('head', {
		anchorX: 0.52,
		//0.15,
		anchorY: 0.8 //0.95
	});
	self.head = head;
	var pelvis = self.attachAsset('pelvis', {
		x: pelvisOffsetX,
		y: torso.height - 20,
		// old torso.height,
		anchorX: 0.5,
		anchorY: 0.2,
		tint: afterTint
	});
	self.pelvis = pelvis;
	self.torso = torso;
	var legUpperLeft = self.addChild(new ConfigContainer({
		x: pelvisOffsetX - legUpperOffsetX - 30
	}));
	var armUpperLeft = self.addChild(new ConfigContainer({
		y: armUpperOffsetY
	}));
	// Arm extensions
	armUpperRight.attachAsset('upperArm', {
		rotation: armUpperAngle,
		anchorX: 0.85,
		anchorY: 0.25,
		tint: afterTint
	});
	self.armUpperRight = armUpperRight;
	armUpperLeft.attachAsset('upperArm', {
		rotation: armUpperAngle,
		anchorX: 0.85,
		anchorY: 0.25
	});
	self.armUpperLeft = armUpperLeft;
	var armLowerRight = armUpperRight.attachAsset('lowerArm', {
		y: armLowerOffsetY,
		rotation: armLowerAngle,
		anchorX: 0.95,
		anchorY: 0.05,
		tint: afterTint
	});
	self.armLowerRight = armLowerRight;
	var armLowerLeft = armUpperLeft.attachAsset('lowerArm', {
		y: armLowerOffsetY,
		rotation: armLowerAngle,
		anchorX: 0.95,
		anchorY: 0.05
	});
	self.armLowerLeft = armLowerLeft;
	// Leg extensions
	legUpperRight.attachAsset('upperLeg', {
		rotation: legUpperAngle,
		anchorY: 0.1,
		tint: afterTint
	});
	self.legUpperRight = legUpperRight;
	legUpperLeft.attachAsset('upperLeg', {
		rotation: legUpperAngle,
		anchorY: 0.1
	});
	self.legUpperLeft = legUpperLeft;
	var legLowerRight = legUpperRight.attachAsset('lowerLeg', {
		y: legLowerOffsetY,
		anchorX: 0.5,
		// old 0.65,
		tint: afterTint
	});
	self.legLowerRight = legLowerRight;
	var legLowerLeft = legUpperLeft.attachAsset('lowerLeg', {
		y: legLowerOffsetY,
		anchorX: 0.5 // old 0.65
	});
	self.legLowerLeft = legLowerLeft;
	var footRight = legLowerRight.attachAsset('foot', {
		y: footOffsetY - 10,
		anchorX: -0.2,
		// old 0.2,
		anchorY: 0.2,
		// old 0.05,
		tint: afterTint
	});
	self.footRight = footRight;
	var footLeft = legLowerLeft.attachAsset('foot', {
		y: footOffsetY,
		anchorX: -0.2,
		// old 0.2,
		anchorY: 0.2 // old 0.05,
	});
	self.footLeft = footLeft;
	// Additional positioning
	armUpperLeft.x = -torso.width / 2 + armUpperOffsetX;
	armUpperRight.x = torso.width / 2 - armUpperOffsetX;
	legUpperLeft.y = torso.height + pelvis.height / 3 - 40;
	legUpperRight.y = torso.height + pelvis.height / 3 - 20;
	;
	self.animate = animate;
	self.pushAnimation = pushAnimation;
	;
	function animate(increment) {
		alpha = (alpha + increment) % 1;
		if (blendAlpha > 0) {
			if ((blendAlpha -= blendDecrement) <= 0) {
				blendAlpha = 0;
				blendAnimation = undefined;
			}
		}
		var pose = animation(alpha);
		var blendPose = blendAnimation && blendAnimation(alpha);
		self.y = animateProperty(pose, blendPose, blendAlpha, 'BODY_OFFSET', bodyOffsetY);
		head.rotation = animateProperty(pose, blendPose, blendAlpha, 'HEAD_ROTATION');
		armUpperRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_UPPER_RIGHT_ROTATION');
		armUpperLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_UPPER_LEFT_ROTATION');
		armLowerRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_LOWER_RIGHT_ROTATION', armLowerAngle);
		armLowerLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_LOWER_LEFT_ROTATION', armLowerAngle);
		legUpperRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_UPPER_RIGHT_ROTATION');
		legUpperLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_UPPER_LEFT_ROTATION');
		legLowerRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_LOWER_RIGHT_ROTATION');
		legLowerLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_LOWER_LEFT_ROTATION');
		footRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'FOOT_RIGHT_ROTATION');
		footLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'FOOT_LEFT_ROTATION');
	}
	function pushAnimation(newAnimation, newBlendDecrement) {
		blendDecrement = newBlendDecrement;
		if (newAnimation !== animation) {
			blendAnimation = animation;
			animation = newAnimation;
			blendAlpha = 1;
		}
	}
	;
	animate(0);
});
var Player = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var speedFactor = PLAYER_SPEED_FACTOR_BASE;
	var stopped = false;
	var falling = false;
	var jumping = false; // If the player is currently jumping (and immune to gravity)
	var jumpAction = false; // Tracks tap releases
	var jumpStep = 0; // Tracks min/max jump heights
	var aerialSpeed = 0; // Actual vertical/air speed
	var baseY = config.y;
	var step = ROAD_STEP_DEFAULT;
	var nodeDistance = 0;
	var nodeTicks = 0;
	var nodeMulti = 0;
	var slideTimer = 0;
	var SLIDE_DURATION = 100; // Duration of slide in frames
	var body = self.addChild(new PlayerBody({
		y: -370
	}));
	self.body = body;
	self.update = update;
	self.jumpStart = jumpStart;
	self.jumpEnd = jumpEnd;
	self.jumping = false;
	self.sliding = false;
	self.slideStart = slideStart;
	self.slideEnd = slideEnd;
	self.hitWall = false;
	self.isDead = false;
	self.intersects = function (obj) {
		var bounds1 = self.getBounds();
		var bounds2 = obj.getBounds();
		if (self.sliding) {
			// When sliding, reduce height for collision check
			return bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height / 4 > bounds2.y;
		} else {
			return bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height > bounds2.y;
		}
	};
	;
	self.update = update;
	self.jumpStart = jumpStart;
	self.jumpEnd = jumpEnd;
	self.slideStart = slideStart;
	self.slideEnd = slideEnd;
	;
	function update() {
		if (!isActive()) {
			return;
		}
		var nextNode = road.getNode(1);
		var currentNode = nextNode.previous;
		// Update horizontal movement
		var outputSpeed = PLAYER_SPEED_BASE * speedFactor;
		var distance = road.getNodeDist();
		// Check for collision with Detritus
		// Collision check removed from Player update method
		// Check for collision
		if (stopped && nextNode.step <= step) {
			stopped = false;
		} else if (nextNode.step > step && outputSpeed > distance) {
			road.rotate(distance - PLAYER_SPACING);
			speedFactor = PLAYER_SPEED_FACTOR_BASE;
			stopped = true;
			if (currentNode.step <= step) {
				body.pushAnimation(animationStand, PLAYER_POSE_TRANSITION);
			}
		}
		if (!stopped) {
			road.rotate(outputSpeed);
			if (!gameOver) {
				if (speedFactor < PLAYER_SPEED_FACTOR_MAX) {
					if ((speedFactor += PLAYER_SPEED_FACTOR_INCREMENT) >= PLAYER_SPEED_FACTOR_MAX) {
						speedFactor = PLAYER_SPEED_FACTOR_MAX;
					}
				}
			} else {
				if (speedFactor > 0) {
					if ((speedFactor -= PLAYER_SPEED_DECREMENT) <= 0) {
						speedFactor = 0;
					}
				}
			}
		}
		// TOOD: Get new node if applicable
		// Update vertical movement
		if (jumping) {
			//log("speedFactor", speedFactor, "outputSpeed", outputSpeed);
			jumpStep += aerialSpeed * speedFactor;
			//log("jumpAction:", jumpAction, "jumpStep:", jumpStep, "PLAYER_JUMP_STEP_MIN:", PLAYER_JUMP_STEP_MIN, "PLAYER_JUMP_STEP_MAX:", PLAYER_JUMP_STEP_MAX);
			if (!jumpAction && jumpStep <= PLAYER_JUMP_STEP_MIN || jumpStep >= PLAYER_JUMP_STEP_MAX) {
				falling = true;
				jumping = false;
				jumpAction = false;
				jumpStep = 0;
			}
		} else if (falling) {
			aerialSpeed -= PLAYER_GRAVITY * speedFactor;
		} else if (step > currentNode.step) {
			falling = true;
			body.pushAnimation(animationJump, PLAYER_POSE_TRANSITION);
		}
		step += aerialSpeed;
		//log("falling", falling, "step:", step, "currentNode.step:", currentNode.step);
		if (falling && step <= currentNode.step) {
			step = currentNode.step;
			falling = false;
			aerialSpeed = 0;
			body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION);
		}
		if (self.sliding) {
			slideTimer += 1 * speedFactor;
			if (slideTimer >= SLIDE_DURATION) {
				slideEnd();
			}
			// Adjust player's vertical position when sliding
			self.y = baseY - 500; //- stepHeight / 4; // Adjust to quarter height when sliding
		} else {
			self.y = baseY - stepHeight;
		}
		// Score calculations
		if (!gameOver && currentNode.distance > nodeDistance) {
			adjustScore(nodeMulti / nodeTicks);
			currentNode.distance = nodeDistance;
			nodeMulti = 0;
			nodeTicks = 0;
		}
		nodeMulti += stopped ? SCORE_STOPPED_FACTOR : speedFactor;
		nodeTicks++;
		// Adjustments
		var stepHeight = ROAD_STEP_HEIGHT_BASE + step * ROAD_STEP_HEIGHT_INCREMENT; // TODO: Better scaling
		if (!self.sliding) {
			self.y = baseY - stepHeight; // TODO: Better scaling
		}
		self.scale.set(PLAYER_SCALE_BASE * stepHeight / ROAD_SEGMENT_HEIGHT); // TODO: Better scaling
		body.animate(PLAYER_ANIMATION_SPEED_BASE * speedFactor);
		//multiplierText.setText((stopped ? SCORE_STOPPED_FACTOR : speedFactor).toFixed(1) + 'x');
		// Get the current road segment
		var currentSegment = road.getNode(0);
		// Check if the current segment is a river segment
		if (currentSegment.isRiver && !jumping && !falling && !jumpAction) {
			self.isDead = true;
			tintPlayer(0xff0000);
			// Player is above a river segment
			log("Player fell in the river!");
			// Flash screen red for 1 second (1000ms) to show we are dead.
			LK.effects.flashScreen(0xff0000, 1000);
			// Play lose sound
			LK.getSound('lose').play();
			callGameOver(true);
		}
		if (isBonusActive) {
			// Animate player tint using multiple flashy colors
			var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
			var colorIndex = Math.floor(LK.ticks / 5 % colors.length);
			var tintColor = colors[colorIndex];
			tintPlayer(tintColor);
		}
	}
	function jumpStart() {
		if (!gameOver && !jumping && !falling && !self.sliding) {
			LK.getSound('jump').play();
			jumping = true;
			jumpAction = true;
			aerialSpeed = PLAYER_JUMP_STEP_INCREMENT;
			body.pushAnimation(animationJump, PLAYER_POSE_TRANSITION);
		}
	}
	function jumpEnd() {
		jumpAction = false;
	}
	function slideStart() {
		if (!gameOver && !jumping && !falling && !self.sliding) {
			self.sliding = true;
			slideTimer = 0;
			body.pushAnimation(animationSlide, PLAYER_POSE_TRANSITION);
			body.rotation = -Math.PI / 2;
			//body.scale.x *= -1;
			LK.getSound('slide').play();
			/* Flies
			// Rotate the body, not the entire player container
			body.rotation = -Math.PI / 2;
			// Flip the body horizontally to ensure correct orientation
			body.scale.x *= -1;
			// Play slide sound (if available)
			// LK.getSound('slide').play();
			*/
		}
	}
	function slideEnd() {
		if (self.sliding && !self.hitWall) {
			self.sliding = false;
			slideTimer = 0;
			body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION);
			// Restore body's original rotation and scale
			body.rotation = 0;
			//body.scale.x *= -1;
		}
	}
	function adjustScore(averageMulti) {
		var points = Math.floor(averageMulti * SCORE_SEGMENT_POINTS);
		score = Math.max(0, score + points);
		scoreText.setText(kgCollected.toFixed(2) + 'kg');
		// Old score : scoreText.setText(String(score).padStart(6, '0'));
		distanceText.setText((distanceRun += SCORE_SEGMENT_DISTANCE) + 'm');
		incrementText.setText((points === SCORE_MAX_POINTS ? '[MAX!] +' : '+') + points);
	}
	;
	body.pushAnimation(animationRun, PLAYER_POSE_TRANSITION);
});
var FragileNature = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var fragileNatureIndex = Math.floor(Math.random() * ROAD_GEN_FRAGILE_NATURE_ASSETS);
	var scale = 0.9 + 0.2 * Math.random();
	self.attachAsset('fragileNature' + fragileNatureIndex, {
		anchorX: 0.5,
		anchorY: 1,
		scaleX: Math.random() < 0.5 ? -scale : scale,
		scaleY: scale
	});
	self.taken = false;
	self.update = function () {
		if (self.taken) {
			// TODO : Do domthing when Fragile Nature is hit
		} else if (!self.taken && player.body.torso.intersects(self)) {
			self.taken = true;
		}
	};
	return self;
});
var DetritusFlying = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var detritusFlyingIndex = Math.floor(Math.random() * ROAD_GEN_DETRITUS_FLYING_ASSETS);
	var scale = 0.9 + 0.2 * Math.random();
	self.attachAsset('detritusFlying' + detritusFlyingIndex, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: Math.random() < 0.5 ? -scale : scale,
		scaleY: scale
	});
	self.update = function () {
		if (!isActive()) {
			return;
		}
		if (self.taken) {
			var targetX = recycleBin.x;
			var targetY = recycleBin.y;
			var startX = player.x;
			var startY = player.y;
			var speed = 15;
			var accelerationY = 1.0; // Adjust as needed
			var dx = targetX - self.x;
			var dy = targetY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			var velocityX = dx / distance * speed;
			var velocityY = dy / distance * speed;
			if (distance > speed) {
				if (Math.abs(self.x - startX) < Math.abs(dx / 2)) {
					self.x += velocityX;
					self.y -= velocityY * 0.1 - accelerationY * 0.2; // Move up
				} else {
					self.x += velocityX;
					self.y += velocityY + accelerationY; // Move down with acceleration
				}
				self.scale.x *= 0.996;
				self.scale.y *= 0.996;
				self.rotation += 0.1; // Rotate detritusFlying on themselves when taken
			} else {
				self.x = targetX;
				self.y = targetY;
				self.destroy();
			}
		} else if (!self.taken && player.body.torso.intersects(self)) {
			self.taken = true;
			// Flash Detritus in green for 500ms
			LK.effects.flashObject(self, 0x00ff00, 500);
			kgCollected += 0.01; // Add 0.01 to kgCollected
			var globalPosition = self.parent.toGlobal(self.position);
			self.parent.removeChild(self);
			game.addChildAt(self, game.getChildIndex(recycleBin));
			self.position = game.toLocal(globalPosition);
			LK.getSound('collectDetritus').play();
			if (!isBonusActive) {
				nbCollectedBeforeBonus++;
			}
			if (nbCollectedBeforeBonus >= BONUS_COLLECT_THRESHOLD) {
				log("Gained Bonus !!!");
				startBonusAnimation();
			}
		} else {
			self.rotation += 0.05 + (Math.random() - 0.25) * 0.1;
		}
	};
	self.taken = false;
	return self;
});
var Detritus = ConfigContainer.expand(function (config) {
	var self = ConfigContainer.call(this, config);
	var detritusIndex = Math.floor(Math.random() * ROAD_GEN_DETRITUS_ASSETS);
	var scale = 0.9 + 0.2 * Math.random();
	self.attachAsset('detritus' + detritusIndex, {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: Math.random() < 0.5 ? -scale : scale,
		scaleY: scale
	});
	self.taken = false;
	self.update = function () {
		if (!isActive()) {
			return;
		}
		if (self.taken) {
			var targetX = recycleBin.x;
			var targetY = recycleBin.y;
			var startX = player.x;
			var startY = player.y;
			var speed = 15;
			var accelerationY = 1.0; // Adjust as needed
			var dx = targetX - self.x;
			var dy = targetY - self.y;
			var distance = Math.sqrt(dx * dx + dy * dy);
			var velocityX = dx / distance * speed;
			var velocityY = dy / distance * speed;
			if (distance > speed) {
				if (Math.abs(self.x - startX) < Math.abs(dx / 2)) {
					self.x += velocityX;
					self.y -= velocityY * 0.5 - accelerationY * 0.2; // Move up
				} else {
					self.x += velocityX;
					self.y += velocityY + accelerationY; // Move down with acceleration
				}
				self.scale.x *= 0.996;
				self.scale.y *= 0.996;
				self.rotation += 0.1; // Rotate detritus on themselves when taken
			} else {
				self.x = targetX;
				self.y = targetY;
				self.destroy();
			}
		} else if (!self.taken && player.body.torso.intersects(self)) {
			self.taken = true;
			// Flash DetritusFlying in green for 500ms
			LK.effects.flashObject(self, 0x00ff00, 500);
			kgCollected += 0.02; // Add 0.01 to kgCollected
			var globalPosition = self.parent.toGlobal(self.position);
			self.parent.removeChild(self);
			game.addChildAt(self, game.getChildIndex(recycleBin));
			self.position = game.toLocal(globalPosition);
			LK.getSound('collectDetritus').play();
			if (!isBonusActive) {
				nbCollectedBeforeBonus++;
			}
			if (nbCollectedBeforeBonus >= BONUS_COLLECT_THRESHOLD) {
				log("Gained Bonus !!!");
				startBonusAnimation();
			}
		}
	};
	return self;
});
/** 
* config {
* 	x        : Number || 0, // See: ConfigContainer
* 	y        : Number || 0, // See: ConfigContainer
* 	rotation : Number || 0, // See: ConfigContainer
* 	anchorX  : Number || 0,
* 	anchorY  : Number || 1,
* 	size     : Number || TEXT_DEFAULT_SIZE,
* 	weight   : Number || TEXT_DEFAULT_WEIGHT,
* 	font     : String || TEXT_DEFAULT_FONT,
* 	fill     : String || TEXT_DEFAULT_FILL,
* 	border   : String || TEXT_DEFAULT_BORDER,
* }
**/ 
var BorderedText = ConfigContainer.expand(function (text, config) {
	var self = ConfigContainer.call(this, config);
	config = config || {};
	;
	var anchorX = config.anchorX !== undefined ? config.anchorX : 0;
	var anchorY = config.anchorY !== undefined ? config.anchorY : 1;
	var size = config.size !== undefined ? config.size : TEXT_DEFAULT_SIZE;
	var weight = config.weight !== undefined ? config.weight : TEXT_DEFAULT_WEIGHT;
	var font = config.font !== undefined ? config.font : TEXT_DEFAULT_FONT;
	var textFill = config.fill !== undefined ? config.fill : TEXT_DEFAULT_FILL;
	var borderFill = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER;
	var textAssets = [];
	var mainAsset;
	;
	self.setText = setText;
	self.setFill = setFill;
	;
	function setText(newText) {
		for (var i = 0; i < textAssets.length; i++) {
			textAssets[i].setText(newText);
		}
	}
	function setFill(newFill) {
		textFill = newFill;
		mainAsset.fill = newFill;
	}
	function buildTextAssets(newText) {
		for (var i = 0; i < TEXT_OFFSETS.length; i++) {
			var main = i === TEXT_OFFSETS.length - 1;
			var fill = main ? textFill : borderFill;
			var textAsset = textAssets[i];
			if (textAsset) {
				textAsset.destroy();
			}
			textAsset = self.addChild(new Text2(newText, {
				fill: fill,
				font: font,
				size: size
			}));
			textAsset.anchor = {
				x: anchorX,
				y: anchorY
			}; // NOTE: Cannot be set in config
			textAsset.x = TEXT_OFFSETS[i][0] * weight; // NOTE: Cannot be set in config
			textAsset.y = TEXT_OFFSETS[i][1] * weight; // NOTE: Cannot be set in config
			textAssets[i] = textAsset;
		}
		mainAsset = textAssets[TEXT_OFFSETS.length - 1];
	}
	;
	buildTextAssets(text);
	return self;
});
var Starfield = Container.expand(function (config) {
	var self = Container.call(this);
	config = config || {};
	var starCount = config.starCount || 100;
	var starColor = config.starColor || 0xFFFFFF;
	var starSize = config.starSize || 2;
	for (var i = 0; i < starCount; i++) {
		var sizeVar = Math.random() * 3;
		var star = self.attachAsset('circle', {
			width: starSize + sizeVar,
			height: starSize + sizeVar,
			color: starColor,
			x: Math.random() * GAME_WIDTH,
			y: Math.random() * GAME_HEIGHT,
			anchorX: 0.5,
			anchorY: 0.5
		});
		star.direction = Math.random() < 0.5 ? 1 : -1; // Randomly set direction to 1 or -1
	}
	// Add alpha animation to random stars
	self.update = function () {
		for (var i = 0; i < starsToAnimate.length; i++) {
			var star = starsToAnimate[i];
			star.alpha += star.direction * 0.015; // Change alpha based on direction
			if (star.alpha <= 0.1 || star.alpha >= 1) {
				star.direction *= -1; // Reverse direction if alpha goes out of bounds
			}
		}
	};
	function selectRandomStars() {
		starsToAnimate = [];
		for (var i = 0; i < 20; i++) {
			var randomIndex = Math.floor(Math.random() * self.children.length);
			starsToAnimate.push(self.children[randomIndex]);
		}
	}
	starAnimationInterval = LK.setInterval(selectRandomStars, 3000); // Select 10 stars every 3 seconds
	return self;
});
var StartButton = Container.expand(function () {
	var self = Container.call(this);
	// Attach the start button asset
	var button = self.attachAsset('startButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Attach the start recycle asset
	var recycle = self.attachAsset('startRecycle', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add update function to animate recycle rotation
	self.update = function () {
		recycle.rotation += 0.005; // Adjust rotation speed as needed
		button.rotation -= 0.005; // Adjust rotation speed as needed
		//recycle.alpha = 0.5 + 0.5 * Math.sin(LK.ticks / 30); // Animate alpha from 1 to 0 and vice versa
	};
	// Event handler for button press
	self.down = function (x, y, obj) {
		isStarted = true;
		LK.getSound('startButton').play(); // Play startButton audio
		LK.setTimeout(function () {
			LK.playMusic('bgMusic', {
				loop: true
			});
		}, 100);
		recycleBin.visible = true;
		recycleBinBack.visible = true;
		recycleBin.alpha = 0;
		recycleBinBack.alpha = 0;
		var binFadeInInterval = LK.setInterval(function () {
			if (recycleBin.alpha < 1) {
				recycleBin.alpha += 0.05;
				recycleBinBack.alpha += 0.05;
			} else {
				LK.clearInterval(binFadeInInterval);
			}
		}, 100);
		self.destroy(); // Remove the start button after it's pressed
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x121B20 // Sky blue color
});
/**** 
* Game Code
****/ 
// Always call move and tick methods from the main tick method in the 'Game' class.
if (isBonusStartAnim && bonus.update) {
	bonus.update();
}
var isDebug = true;
var _console = console;
var isStarted = false;
var isBonusStartAnim = false;
var isBonusActive = false;
//==============================================================================
;
// Math Constants / Pre-calculations
var MATH_4_PI = Math.PI * 4;
var MATH_2_PI = Math.PI * 2;
var MATH_HALF_PI = Math.PI / 2;
var MATH_THIRD_PI = Math.PI / 3;
var MATH_QUARTER_PI = Math.PI / 4;
var MATH_FIFTH_PI = Math.PI / 5;
var MATH_SIXTH_PI = Math.PI / 5;
var MATH_EIGHTH_PI = Math.PI / 8;
var MATH_HALF_ROOT_3 = Math.sqrt(3) / 2; // Required by: TEXT_OFFSETS, BorderedText, BorderedSymbol, BorderedShape, SymbolText
;
// Text Settings
var TEXT_OFFSETS = [[0, 1], [MATH_HALF_ROOT_3, 0.5], [MATH_HALF_ROOT_3, -0.5], [0, -1], [-MATH_HALF_ROOT_3, -0.5], [-MATH_HALF_ROOT_3, 0.5], [0, 0]]; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText
var TEXT_DEFAULT_WEIGHT = 4; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText
var TEXT_DEFAULT_BORDER = '#000000'; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText
var TEXT_DEFAULT_FILL = '#FFFFFF'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_FONT = 'Courier'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_SIZE = 80; // Required by: BorderedText, SymbolText
;
// Game Constants
var GAME_TICKS = 60;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
;
// Road Constants
var ROAD_SEGMENT_DISTANCE = 1.0;
var ROAD_SEGMENT_POINTS = 100;
var ROAD_SEGMENT_COUNT = 30;
var ROAD_SEGMENT_ANGLE = MATH_2_PI / ROAD_SEGMENT_COUNT;
var ROAD_SEGMENT_HEIGHT = 1000;
var ROAD_SEGMENT_DESTROY = -2;
var ROAD_SEGMENT_FADEOUT = 0.015;
var ROAD_MAX_RIVER_SEGMENTS = 1;
var ROAD_MAX_WALL_SEGMENTS = 1;
var ROAD_FREE_RUN_COUNT = 5;
var ROAD_STEP_COUNT = 5;
var ROAD_STEP_DEFAULT = 10;
var ROAD_STEP_HEIGHT_BASE = 512;
var ROAD_STEP_HEIGHT_INCREMENT = 20;
var ROAD_STEP_CHANCE_BASE = 0; //0.1;
var ROAD_STEP_CHANCE_INCREMENT = 0; //0.05;
var ROAD_GEN_PLANT_ASSETS = 3;
var ROAD_GEN_PLANT_MAX = 3;
var ROAD_GEN_TREE_ASSETS = 4;
var ROAD_GEN_TREE_CHANCE = 0; //0.1;
var ROAD_GEN_CLOUD_ASSETS = 4;
var ROAD_GEN_CLOUD_CHANCE = 0.2;
var ROAD_GEN_DETRITUS_ASSETS = 2;
var ROAD_GEN_DETRITUS_CHANCE = 0.1;
var ROAD_GEN_DETRITUS_FLYING_ASSETS = 2;
var ROAD_GEN_DETRITUS_FLYING_CHANCE = 0.1;
var ROAD_GEN_FRAGILE_NATURE_ASSETS = 2;
var ROAD_GEN_FRAGILE_NATURE_CHANCE = 0.1;
var ROAD_GEN_WALL_ASSETS = 1;
var ROAD_GEN_WALL_CHANCE = 0.2;
;
// Player Constants
var PLAYER_SPACING = ROAD_SEGMENT_ANGLE / 8; // 1/8 of a segment
var PLAYER_POSE_TRANSITION = 0.1;
var PLAYER_GRAVITY = 0.02;
var PLAYER_JUMP_STEP_MIN = 1.5; // 720; // 1.0;
var PLAYER_JUMP_STEP_MAX = 2.0; //1500; //2.0;
var PLAYER_JUMP_STEP_INCREMENT = 0.75; //; 0.15;
var PLAYER_ANIMATION_SPEED_BASE = 0.02;
var PLAYER_SCALE_BASE = 1; //0.5;
var PLAYER_SPEED_BASE = MATH_2_PI / (GAME_TICKS * 10); // 10s to complete a revolution
var PLAYER_SPEED_FACTOR_BASE = 1.0;
var PLAYER_SPEED_FACTOR_MAX = 2.8; //1.5;
var PLAYER_SPEED_FACTOR_INCREMENT = 0.0015; //0.001;
var PLAYER_SPEED_DECREMENT = 0.005;
;
// Scoring Constants
var SCORE_TOTAL_DISTANCE = 500;
var SCORE_SEGMENT_DISTANCE = 1;
var SCORE_SEGMENT_POINTS = 100;
var SCORE_STOPPED_FACTOR = -0.2;
var SCORE_MAX_POINTS = SCORE_SEGMENT_POINTS * PLAYER_SPEED_FACTOR_MAX;
// Bonus Constants
var BONUS_COLLECT_THRESHOLD = 3;
//==============================================================================
// Game Instances & Variables
//==============================================================================
;
// Variables
var score = 0;
var winningTime = 0;
var gameOver = false;
var distanceRemaining = SCORE_TOTAL_DISTANCE;
var distanceRun = 0;
var kgCollected = 0;
var nbCollectedBeforeBonus = 0; // Number of detritus collected since last bonus
;
var startY = 0; // Variable to store the initial Y position for swipe detection
var swipeStarted = false; // Flag to start detecting swipe only after tap
// Instances
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: GAME_WIDTH / 2,
	y: GAME_HEIGHT / 2
}));
var starfield = game.addChild(new Starfield({
	starCount: 200,
	starColor: 0xFFFFFF,
	starSize: 2
}));
var sun = game.addChild(new Sun({
	x: GAME_WIDTH / 2,
	y: 50
}));
var playerContainer = game.addChild(new Container());
var roadContainer = game.addChild(new Container());
var road = roadContainer.addChild(new Road({
	x: GAME_WIDTH / 2,
	y: GAME_HEIGHT - GAME_WIDTH / 2
}));
var player = playerContainer.addChild(new Player({
	x: road.x,
	y: road.y,
	scale: 0.5
}));
var distanceText = game.addChild(new BorderedText(distanceRun + 'm', {
	x: road.x,
	y: road.y - TEXT_DEFAULT_SIZE,
	anchorX: 0.5,
	anchorY: 1
}));
var scoreText = game.addChild(new BorderedText('0.00kg', {
	x: road.x,
	y: road.y,
	size: TEXT_DEFAULT_SIZE * 1.2,
	anchorX: 0.5,
	anchorY: 0.5
}));
var multiplierText = game.addChild(new BorderedText('collected', {
	x: road.x,
	y: road.y + TEXT_DEFAULT_SIZE * 0.8,
	size: TEXT_DEFAULT_SIZE * 0.8,
	anchorX: 0.5,
	anchorY: 0
}));
multiplierText.visible = true;
var incrementText = game.addChild(new BorderedText('', {
	x: road.x - 300,
	y: road.y,
	anchorX: 1,
	anchorY: 0.5
}));
incrementText.visible = false;
var winningText = game.addChild(new BorderedText('', {
	x: GAME_WIDTH / 2,
	y: 500,
	anchorX: 0.5,
	anchorY: 0.5,
	size: 2 * TEXT_DEFAULT_SIZE
}));
;
var recycleBinBack = game.addChild(LK.getAsset('recycleBinBack', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 250,
	//450,
	y: 1750,
	visible: false
}));
;
var recycleBin = game.addChild(LK.getAsset('recycleBin', {
	anchorX: 0.5,
	anchorY: 0.5,
	x: 250,
	//450,
	y: 1750,
	visible: false
}));
;
var startButton = game.addChild(new StartButton());
startButton.x = GAME_WIDTH / 2;
startButton.y = road.y;
//==============================================================================
// Global events
//==============================================================================
;
var startY = 0;
game.down = function (x, y, obj) {
	startY = y;
	swipeStarted = true;
};
game.move = function (x, y, obj) {
	if (!swipeStarted) {
		return;
	}
	if (startY - y > 50) {
		// Detect swipe up
		player.jumpStart();
	}
	if (startY - y < -50) {
		// Detect swipe down
		player.slideStart();
	}
};
game.up = function (x, y, obj) {
	player.jumpEnd();
	startY = 0;
	swipeStarted = false;
};
;
//==============================================================================
// Global functions
//==============================================================================
;
// Helpers
function log() {
	if (isDebug) {
		_console.log.apply(_console, arguments);
	}
}
;
function isActive() {
	return isStarted && !isBonusStartAnim && !player.isDead;
}
;
function callGameOver(lost) {
	gameOver = true;
	winningTime = LK.ticks;
	LK.setScore(score + kgCollected * 100);
	LK.stopMusic();
	winningText.setText('The end');
	// Check if winningTime is set and show game over screen after 60 ticks
	game.update = function () {
		if (lost || winningTime && LK.ticks >= winningTime + 120) {
			LK.setTimeout(function () {
				LK.showGameOver();
			}, 1000);
			LK.clearInterval(starAnimationInterval);
		}
	};
}
;
function startBonusAnimation() {
	log('startBonusAnimation...');
	nbCollectedBeforeBonus = 0;
	isBonusStartAnim = true;
	// Set alpha of all taken detritus to 0
	for (var i = 0; i < game.children.length; i++) {
		var child = game.children[i];
		if (child.taken) {
			log("IS TAKEN : ", child);
			child.alpha = 0;
		}
	}
	// Play bonus start sound
	LK.getSound('bonusStart').play();
	// Flash screen green for 500ms to indicate bonus start
	LK.effects.flashScreen(0x00ff00, 500);
	// Create a bonus asset at the recycleBin position
	var bonus = game.addChild(LK.getAsset('bonus', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 10,
		height: 10,
		x: recycleBin.x,
		y: recycleBin.y
	}));
	// Animate bonus: move to road x,y while increasing width and height to 1024
	var targetX = road.x;
	var targetY = road.y;
	var targetWidth = 1024;
	var targetHeight = 1024;
	var duration = 40; // Duration of the animation in milliseconds
	var duration2 = 30; // Duration of the animation in milliseconds
	var startTime = LK.ticks;
	var startTime2 = LK.ticks;
	bonus.update = function () {
		var elapsed = LK.ticks - startTime;
		var progress = Math.min(elapsed / duration, 1);
		if (progress < 1) {
			bonus.x = recycleBin.x + (targetX - recycleBin.x) * progress;
			bonus.y = recycleBin.y + (targetY - recycleBin.y) * progress;
			bonus.width = 100 + (targetWidth - 100) * progress;
			bonus.height = 100 + (targetHeight - 100) * progress;
		}
		if (progress > 0.5) {
			LK.getSound('bonusMove').play();
		}
		if (progress === 1 && elapsed > duration * 2) {
			// Set new target to player position
			targetX = player.x;
			targetY = player.y - player.height / 2;
			targetWidth = 10;
			targetHeight = 10;
			startTime2 = LK.ticks; // Reset start time for the new animation phase
			bonus.update = function () {
				var elapsed2 = LK.ticks - startTime2;
				var progress2 = Math.min(elapsed2 / duration2, 1);
				if (progress2 > 0.5) {
					LK.getSound('bonusMove').play();
				}
				if (progress2 < 1) {
					bonus.x = road.x + (targetX - road.x) * progress2;
					bonus.y = road.y + (targetY - road.y) * progress2;
					bonus.width = 1024 + (targetWidth - 1024) * progress2;
					bonus.height = 1024 + (targetHeight - 1024) * progress2;
				}
				if (progress2 === 1) {
					bonus.destroy();
					isBonusActive = true;
					isBonusStartAnim = false;
				}
			};
		}
	};
}
;
// Animations & handlers
function animateProperty(animationPose, blendPose, blendAlpha, key, baseValue) {
	var animationPoseValue = (animationPose || {})[key] || 0;
	var blendPoseValue = (blendPose || {})[key] || 0;
	return (baseValue || 0) + animationPoseValue * (1 - blendAlpha) + blendPoseValue * blendAlpha;
}
function animationRun(alpha) {
	// Animation constants
	var armUpperAngle = MATH_FIFTH_PI;
	var armLowerAngle = -5 * MATH_EIGHTH_PI;
	var legLowerAngle = MATH_EIGHTH_PI;
	// Sinusoidals
	var rightSinusoidal = Math.cos(MATH_2_PI * (alpha + 0.5));
	var leftSinusoidal = Math.cos(MATH_2_PI * alpha);
	var doubleSinusoidal = Math.cos(MATH_4_PI * alpha);
	// Animation calculations
	var armUpperRightSwing = -rightSinusoidal * MATH_QUARTER_PI;
	var armUpperLeftSwing = -leftSinusoidal * MATH_QUARTER_PI;
	var legUpperRightSwing = rightSinusoidal * MATH_THIRD_PI;
	var legUpperLeftSwing = leftSinusoidal * MATH_THIRD_PI;
	return {
		BODY_OFFSET: doubleSinusoidal * 10,
		HEAD_ROTATION: (1 + doubleSinusoidal) * Math.PI / 32,
		ARM_UPPER_RIGHT_ROTATION: armUpperAngle + armUpperRightSwing,
		ARM_UPPER_LEFT_ROTATION: armUpperAngle + armUpperLeftSwing,
		ARM_LOWER_RIGHT_ROTATION: armLowerAngle + armUpperRightSwing / 2,
		ARM_LOWER_LEFT_ROTATION: armLowerAngle + armUpperLeftSwing / 2,
		LEG_UPPER_RIGHT_ROTATION: legUpperRightSwing,
		LEG_UPPER_LEFT_ROTATION: legUpperLeftSwing,
		LEG_LOWER_RIGHT_ROTATION: legLowerAngle + (alpha > 0.5 ? MATH_THIRD_PI + (1 - Math.abs(rightSinusoidal)) * MATH_SIXTH_PI : Math.abs(-legUpperRightSwing)),
		LEG_LOWER_LEFT_ROTATION: legLowerAngle + (alpha <= 0.5 ? MATH_THIRD_PI + (1 - Math.abs(leftSinusoidal)) * MATH_SIXTH_PI : Math.abs(-legUpperLeftSwing)),
		FOOT_RIGHT_ROTATION: Math.max(0, legUpperRightSwing * 2 / 3) - legLowerAngle,
		FOOT_LEFT_ROTATION: Math.max(0, legUpperLeftSwing * 2 / 3) - legLowerAngle
	};
}
function animationJump(alpha) {
	var sinusoidal = Math.cos(MATH_2_PI * alpha);
	return animationRun(0.35 + sinusoidal * 0.02);
}
function animationStand(alpha) {
	var sinusoidal = Math.cos(MATH_2_PI * alpha);
	var offsetAngle = Math.PI / 16;
	var sharedAngle = (2 + sinusoidal) * Math.PI / 64;
	return {
		BODY_OFFSET: sinusoidal * 5,
		ARM_UPPER_RIGHT_ROTATION: 0.5 * offsetAngle,
		ARM_UPPER_LEFT_ROTATION: 3 * offsetAngle,
		ARM_LOWER_RIGHT_ROTATION: -4 * offsetAngle,
		ARM_LOWER_LEFT_ROTATION: -4 * offsetAngle,
		LEG_UPPER_RIGHT_ROTATION: 1.5 * offsetAngle - sharedAngle,
		LEG_UPPER_LEFT_ROTATION: -1.5 * offsetAngle - sharedAngle,
		LEG_LOWER_RIGHT_ROTATION: 2 * sharedAngle,
		LEG_LOWER_LEFT_ROTATION: 2 * sharedAngle,
		FOOT_RIGHT_ROTATION: -sharedAngle - 1.5 * offsetAngle,
		FOOT_LEFT_ROTATION: -sharedAngle + 1.5 * offsetAngle
	};
}
// On belly head front 
function animationSlide(alpha) {
	return {
		BODY_ROTATION: Math.PI / 2,
		// Rotate body 90 degrees
		BODY_OFFSET: -30,
		// Adjust body position to align with ground
		HEAD_ROTATION: Math.PI / 4,
		// Arm Right
		ARM_UPPER_RIGHT_ROTATION: Math.PI / 4,
		ARM_LOWER_RIGHT_ROTATION: -Math.PI / 4,
		// Arm Left
		ARM_UPPER_LEFT_ROTATION: -Math.PI,
		ARM_LOWER_LEFT_ROTATION: -Math.PI / 2,
		// Upper Legs
		LEG_UPPER_RIGHT_ROTATION: Math.PI / 10,
		LEG_UPPER_LEFT_ROTATION: Math.PI / 10,
		// Lower legs
		LEG_LOWER_RIGHT_ROTATION: Math.PI / 6,
		LEG_LOWER_LEFT_ROTATION: Math.PI / 6,
		FOOT_RIGHT_ROTATION: 0,
		FOOT_LEFT_ROTATION: 0
	};
}
function tintPlayer(color) {
	player.body.torso.tint = color; // Tint the player's torso
	player.body.legUpperLeft.children[0].tint = color; // Tint the player's upper left leg
	player.body.legUpperRight.children[0].tint = color; // Tint the player's upper right leg
	player.body.armUpperLeft.children[0].tint = color; // Tint the player's upper left arm
	player.body.armUpperRight.children[0].tint = color; // Tint the player's upper right arm
	player.body.armLowerLeft.tint = color;
	player.body.armLowerRight.tint = color;
	player.body.legLowerLeft.tint = color;
	player.body.legLowerRight.tint = color;
	player.body.pelvis.tint = color; // Tint the player's pelvis
	player.body.head.tint = color; // Tint the player's head
	player.body.footRight.tint = color;
	player.body.footLeft.tint = color;
}
var starAnimationInterval;
var starsToAnimate = []; ===================================================================
--- original.js
+++ change.js
@@ -774,10 +774,12 @@
 			LK.getSound('lose').play();
 			callGameOver(true);
 		}
 		if (isBonusActive) {
-			// Animate player tint using tintPlayer() from white to green and vice versa
-			var tintColor = Math.sin(LK.ticks / 30) > 0 ? 0x00ff00 : 0xffffff;
+			// Animate player tint using multiple flashy colors
+			var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
+			var colorIndex = Math.floor(LK.ticks / 5 % colors.length);
+			var tintColor = colors[colorIndex];
 			tintPlayer(tintColor);
 		}
 	}
 	function jumpStart() {
 white
 circle sliced into many pieces, flat image. 2d, white background, shadowless.
 
 
 
 pixel art of a tall, tree. game asset, 2d, white background, shadowless.
 
 
 pixel art cloud. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
 
 
 dark space.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 flying lava bubble. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
 
 
 a bonus crystal ball with the recycle symbol. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
 
 
 
 top view A green round start button empty in the center like a ring.