User prompt
Ensure all dancing people are behind the dj desk asset in display
User prompt
Ensure all dancing people line behind the dj desk in display
User prompt
Ensure all dancing people line under the dj desk in display
User prompt
Remove the duplicated dj desk from the map
User prompt
Move the dj desk asset down by 18 units
User prompt
Move the dj desk asset down by 25 units
User prompt
Add to the left side of the people line an animated Smoke diffuser ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move the flamethrower down onto the base top
User prompt
Scale down the Flamethrower flame particles size a little bit
User prompt
Scale down the Flamethrower flame particles size a little bit
User prompt
Scale down the Flamethrower particles size by a little bit
User prompt
Scale down the Flamethrower particles size by a quarter
User prompt
Scale down the Flamethrower particles size to the half
User prompt
Scale down the Flamethrower size to the half
User prompt
Scale down the Flamethrower size to the half
User prompt
Change the Flamethrower looking to a flame machine
User prompt
Change the Flamethrower looking to a party styled one
User prompt
Add animated Flamethrower next to the right side of people line ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animated Sparkler-rocket next to the right side of people line ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animated Sparkler rocket next to the people line right side
User prompt
Add Sparkler class for animated sparkler effect next to the people line right side
User prompt
Add Sparkler class for animated sparkler effect next to the people line right side
User prompt
Loading Add Sparkler class for animated sparkler effect
User prompt
Add animated rgb colorchanging flash lines to the ferriswheel ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ENSURE ferriswheel, cabin and rim dots are under the dancing people in display order
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
// ConfettiEffect: Animated confetti falling from the top
var ConfettiEffect = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numParticles = 100;
	var particles = [];
	var particleMinSize = 2.5;
	var particleMaxSize = 7.5;
	var speedY = 5; // pixels per second
	var speedX = 2; // horizontal drift
	// Create confetti particles
	for (var i = 0; i < numParticles; i++) {
		var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
		var particle = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size,
			height: size
		});
		// Random color (rainbow)
		var hue = Math.random();
		var rgb = hsvToRgb(hue, 1, 1);
		particle.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
		// Random initial position in upper half, above screen for seamless loop
		var px = Math.random() * 2048;
		var py = -particleMaxSize - Math.random() * 500;
		particle.x = px;
		particle.y = py;
		self.addChild(particle);
		particles.push({
			obj: particle,
			speedScale: 0.8 + Math.random() * 0.4,
			// particles fall at slightly different speeds
			phase: Math.random() * Math.PI * 2
		});
	}
	// HSV to RGB helper
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v, g = t, b = p;
				break;
			case 1:
				r = q, g = v, b = p;
				break;
			case 2:
				r = p, g = v, b = t;
				break;
			case 3:
				r = p, g = q, b = v;
				break;
			case 4:
				r = t, g = p, b = v;
				break;
			case 5:
				r = v, g = p, b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Animation
	self.update = function () {
		var t = Date.now() * 0.001;
		for (var i = 0; i < particles.length; i++) {
			var p = particles[i];
			// Move particles down with horizontal drift
			p.obj.y += speedY * p.speedScale;
			p.obj.x += speedX * Math.sin(t + p.phase);
			// Wrap around vertically (from bottom to top)
			if (p.obj.y > 2732) {
				p.obj.y = -particleMaxSize;
				p.obj.x = Math.random() * 2048; // new random x position
			}
			// Animate rotation
			p.obj.rotation += 0.05 * p.speedScale;
			// Animate scale for flicker
			// Use the original particle size for flicker, not the possibly already scaled width
			var baseSize = p.obj.width / (0.98 + 0.04 * Math.sin(t * 3 + p.phase)); // undo previous flicker
			var s = baseSize * (0.98 + 0.04 * Math.sin(t * 3 + p.phase));
			p.obj.width = s;
			p.obj.height = s;
		}
	};
	return self;
});
// Crossfader: Controls mix between decks
var Crossfader = Container.expand(function () {
	var self = Container.call(this);
	// State
	self.value = 0.5; // 0 = left, 1 = right
	self.dragging = false;
	// Visuals
	var track = self.attachAsset('crossfaderTrack', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var knob = self.attachAsset('crossfaderKnob', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	knob.y = 0;
	// Methods
	self.setValue = function (val) {
		self.value = Math.max(0, Math.min(1, val));
		knob.x = (self.value - 0.5) * (track.width - knob.width);
	};
	// Touch events
	self.down = function (x, y, obj) {
		self.dragging = true;
		self.setValue(x / track.width);
	};
	self.up = function (x, y, obj) {
		self.dragging = false;
	};
	self.move = function (x, y, obj) {
		if (self.dragging) {
			self.setValue(x / track.width);
		}
	};
	return self;
});
// DancingPerson: Animated dancing people (women and men)
var DancingPerson = Container.expand(function () {
	var self = Container.call(this);
	// Config
	// Use the correct asset for dancing woman (image asset 'dancingWoman') and man (keep purple box for man)
	var assets = ['dancingWoman', 'dancingMan']; // Use image for woman, box for man
	var assetId = assets[Math.floor(Math.random() * assets.length)];
	// Visuals
	var personGraphics = self.attachAsset(assetId, {
		anchorX: 0.5,
		anchorY: 1.0 // Anchor at the bottom center (feet)
	});
	// Animation state
	self.baseY = 0;
	self.phaseOffset = Math.random() * Math.PI * 2; // Random start phase for animation
	// Animation
	self.update = function () {
		var t = Date.now() * 0.002; // Time for animation
		// Simple bouncing and swaying animation
		self.y = self.baseY + Math.sin(t * 3 + self.phaseOffset) * 15; // Bounce up/down
		self.rotation = Math.sin(t * 2 + self.phaseOffset) * 0.08; // Sway left/right
	};
	return self;
});
// DeckPlatter: Represents a turntable platter
var DeckPlatter = Container.expand(function () {
	var self = Container.call(this);
	// State
	self.isActive = false;
	self.isScratching = false;
	self.lastAngle = 0;
	self.rotationOffset = 0;
	self.currentRotation = 0;
	self.track = 'A'; // 'A' or 'B'
	self.playing = true;
	self.scratchSpeed = 0;
	self.lastTouchAngle = null;
	// Visuals
	var platter = self.attachAsset('deckPlatter', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var highlight = self.attachAsset('deckPlatterHighlight', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	highlight.alpha = 0;
	var label = self.attachAsset('deckLabel', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: 140
	});
	// Label text
	var labelTxt = new Text2(self.track, {
		size: 40,
		fill: "#222"
	});
	labelTxt.anchor.set(0.5, 0.5);
	label.addChild(labelTxt);
	// Beat light
	var beatLight = self.attachAsset('beatLight', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -140
	});
	beatLight.alpha = 0.2;
	// Methods
	self.setActive = function (active) {
		self.isActive = active;
		highlight.alpha = active ? 0.4 : 0;
	};
	self.setTrack = function (track) {
		self.track = track;
		labelTxt.setText(track);
	};
	self.flashBeat = function () {
		beatLight.alpha = 1;
		tween(beatLight, {
			alpha: 0.2
		}, {
			duration: 200,
			easing: tween.easeOut
		});
	};
	// Touch events
	self.down = function (x, y, obj) {
		self.isScratching = true;
		self.setActive(true);
		// Calculate angle from center
		var dx = x - self.width / 2;
		var dy = y - self.height / 2;
		self.lastTouchAngle = Math.atan2(dy, dx);
		self.scratchSpeed = 0;
		LK.getSound('scratch').play();
	};
	self.up = function (x, y, obj) {
		self.isScratching = false;
		self.setActive(false);
		self.scratchSpeed = 0;
		self.lastTouchAngle = null;
	};
	self.move = function (x, y, obj) {
		if (!self.isScratching) return;
		// Calculate angle from center
		var dx = x - self.width / 2;
		var dy = y - self.height / 2;
		var angle = Math.atan2(dy, dx);
		if (self.lastTouchAngle !== null) {
			var delta = angle - self.lastTouchAngle;
			// Normalize delta
			if (delta > Math.PI) delta -= 2 * Math.PI;
			if (delta < -Math.PI) delta += 2 * Math.PI;
			self.rotationOffset += delta;
			self.scratchSpeed = delta;
		}
		self.lastTouchAngle = angle;
	};
	// Called every tick
	self.update = function () {
		// If not scratching, platter rotates at normal speed
		if (!self.isScratching && self.playing) {
			self.rotationOffset += 0.02; // Normal play speed
			self.scratchSpeed = 0.02;
		}
		// Apply rotation
		self.currentRotation = self.rotationOffset;
		self.rotation = self.currentRotation;
	};
	return self;
});
// Discoball: True 3D disco ball illusion with spinning mirrored tiles and shining white light particles
var Discoball = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var ballRadius = 150;
	var ballDiameter = ballRadius * 2;
	var numLat = 13; // latitude tiles (vertical)
	var numLon = 24; // longitude tiles (horizontal)
	var tileSize = ballDiameter / numLat * 0.95;
	var tileGap = tileSize * 0.12;
	var tiles = [];
	var shineParticles = [];
	var numShine = 18;
	var shineRadius = ballRadius * 1.08;
	var shineMinAlpha = 0.25;
	var shineMaxAlpha = 0.95;
	var shineMinSize = tileSize * 0.7;
	var shineMaxSize = tileSize * 1.7;
	var spinY = 0; // radians, for y-axis rotation
	var spinSpeed = 0.012;
	var spinDir = 1;
	// --- Mirrored Tiles Grid ---
	// Render both front and back halves for a full 3D ball illusion
	for (var lat = 0; lat < numLat; lat++) {
		var theta = Math.PI * (lat + 0.5) / numLat; // 0 (top) to PI (bottom)
		var y = Math.cos(theta) * ballRadius;
		var r = Math.sin(theta) * ballRadius;
		for (var lon = 0; lon < numLon; lon++) {
			var phi = 2 * Math.PI * lon / numLon;
			// 3D to 2D projection with y-axis spin
			var x3d = Math.cos(phi) * r;
			var z3d = Math.sin(phi) * r;
			// Y-axis spin
			var x2d = x3d * Math.cos(spinY) - z3d * Math.sin(spinY);
			var z2d = x3d * Math.sin(spinY) + z3d * Math.cos(spinY);
			// Draw both front and back tiles, but make back tiles dimmer and behind
			for (var side = 0; side < 2; side++) {
				// side 0: front (z2d > 0), side 1: back (z2d < 0)
				var isFront = side === 0;
				// Always create both front and back tiles, but only show one at a time
				var tile = LK.getAsset('centerCircle', {
					anchorX: 0.5,
					anchorY: 0.5,
					width: tileSize,
					height: tileSize
				});
				// Mirror color: light gray, with some random sparkle
				var base = 0xbb + Math.floor(Math.random() * 0x22);
				var color = base << 16 | base << 8 | base;
				tile.tint = color;
				// Back tiles are dimmer and more transparent
				if (isFront) {
					tile.alpha = 0.92 - 0.18 * (lat / numLat) + Math.random() * 0.08;
				} else {
					tile.alpha = 0.32 - 0.10 * (lat / numLat) + Math.random() * 0.04;
				}
				// Position
				tile.x = x2d;
				tile.y = y;
				// Simulate 3D: scaleX based on z2d
				tile.scaleX = 1 + 0.18 * (Math.abs(z2d) / ballRadius);
				tile.scaleY = 1;
				// For back tiles, send to back (add first), for front tiles, add last
				if (isFront) {
					self.addChild(tile);
				} else {
					self.addChildAt(tile, 0);
				}
				tiles.push({
					tile: tile,
					lat: lat,
					lon: lon,
					theta: theta,
					phi: phi,
					isFront: isFront
				});
			}
		}
	}
	// --- Specular highlight (white spot) ---
	// Removed the white oval particle in front of the discoball
	// --- Shining white light particles ---
	for (var i = 0; i < numShine; i++) {
		var angle = 2 * Math.PI * i / numShine + Math.random() * 0.2;
		var dist = shineRadius * (0.92 + Math.random() * 0.12);
		var particle = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: shineMinSize + Math.random() * (shineMaxSize - shineMinSize),
			height: shineMinSize + Math.random() * (shineMaxSize - shineMinSize)
		});
		particle.tint = 0xffffff;
		particle.alpha = shineMinAlpha + Math.random() * (shineMaxAlpha - shineMinAlpha);
		particle.x = Math.cos(angle) * dist;
		particle.y = Math.sin(angle) * dist;
		self.addChild(particle);
		shineParticles.push({
			particle: particle,
			baseAngle: angle,
			baseDist: dist,
			phase: Math.random() * Math.PI * 2
		});
	}
	// --- Light beams (background) ---
	var numBeams = 32;
	var beams = [];
	var lightBeamLength = 1200; // Increased from 800 to 1200 for longer beams
	for (var i = 0; i < numBeams; i++) {
		var beam = LK.getAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 0,
			width: 8,
			height: lightBeamLength
		});
		beam.tint = 0xffffff;
		beam.alpha = 0.12 + Math.random() * 0.28;
		beam.rotation = Math.PI * 2 / numBeams * i + Math.random() * 0.1;
		beams.push(beam);
		self.addChild(beam);
	}
	// --- Animation ---
	self.update = function () {
		// Animate y-axis spin in a full 360-degree circle
		spinY += spinSpeed * spinDir;
		if (spinY > Math.PI * 2) spinY -= Math.PI * 2;
		if (spinY < 0) spinY += Math.PI * 2;
		self._spinY = spinY; // direct assignment for full rotation
		// Update mirrored tiles positions for y-axis spin
		for (var i = 0; i < tiles.length; i++) {
			var t = tiles[i];
			// 3D to 2D projection with y-axis spin
			var x3d = Math.cos(t.phi) * Math.sin(t.theta) * ballRadius;
			var y3d = Math.cos(t.theta) * ballRadius;
			var z3d = Math.sin(t.phi) * Math.sin(t.theta) * ballRadius;
			// Y-axis spin
			var x2d = x3d * Math.cos(self._spinY) - z3d * Math.sin(self._spinY);
			var z2d = x3d * Math.sin(self._spinY) + z3d * Math.cos(self._spinY);
			// For front tiles, show if z2d > 0; for back tiles, show if z2d < 0
			if (t.isFront && z2d > 0 || !t.isFront && z2d < 0) {
				t.tile.visible = true;
				t.tile.x = x2d;
				t.tile.y = y3d;
				t.tile.scaleX = 1 + 0.18 * (Math.abs(z2d) / ballRadius);
				t.tile.scaleY = 1;
				// Flicker for disco effect
				if (t.isFront) {
					t.tile.alpha = 0.88 - 0.18 * (t.lat / numLat) + Math.random() * 0.09;
				} else {
					t.tile.alpha = 0.28 - 0.10 * (t.lat / numLat) + Math.random() * 0.04;
				}
			} else {
				t.tile.visible = false;
			}
		}
		// Animate highlight to move in a small circle for extra 3D effect
		// (highlight removed, nothing to animate here)
		var t = Date.now() * 0.001;
		// Animate shine particles
		for (var i = 0; i < shineParticles.length; i++) {
			var s = shineParticles[i];
			var phase = t * 1.2 + s.phase;
			var r = s.baseDist + Math.sin(phase) * 12;
			s.particle.x = Math.cos(s.baseAngle + Math.sin(phase) * 0.12) * r;
			s.particle.y = Math.sin(s.baseAngle + Math.cos(phase) * 0.12) * r;
			s.particle.alpha = shineMinAlpha + (shineMaxAlpha - shineMinAlpha) * (0.5 + 0.5 * Math.sin(phase * 2 + i));
			var size = shineMinSize + (shineMaxSize - shineMinSize) * (0.5 + 0.5 * Math.cos(phase * 1.5 + i));
			s.particle.width = size;
			s.particle.height = size;
		}
		// Animate beams (spin and flicker)
		for (var i = 0; i < beams.length; i++) {
			var beam = beams[i];
			beam.rotation += 0.015;
			beam.alpha = 0.12 + Math.random() * 0.28;
		}
	};
	return self;
});
// EqualizerWaves: Animated equalizer bars/waves for visual feedback
var EqualizerWaves = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numBars = 32;
	var barWidth = 40;
	var barSpacing = 16;
	var barMaxHeight = 220;
	var barMinHeight = 40;
	var bars = [];
	// Create bars
	for (var i = 0; i < numBars; i++) {
		var bar = LK.getAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 1.0,
			width: barWidth,
			height: barMinHeight,
			x: i * (barWidth + barSpacing),
			y: 0
		});
		// Rainbow gradient: hue from 0 to 360 across bars
		var hue = Math.floor(i / numBars * 360);
		var rgb = hsvToRgb(hue / 360, 1, 1);
		bar.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
		// No dark overlay or background is added to the equalizer.
		self.addChild(bar);
		bars.push(bar);
	}
	// HSV to RGB helper
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v, g = t, b = p;
				break;
			case 1:
				r = q, g = v, b = p;
				break;
			case 2:
				r = p, g = v, b = t;
				break;
			case 3:
				r = p, g = q, b = v;
				break;
			case 4:
				r = t, g = p, b = v;
				break;
			case 5:
				r = v, g = p, b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Center and scale down the whole equalizer to half size
	self.width = numBars * (barWidth + barSpacing) * 0.5;
	self.height = barMaxHeight * 0.5;
	self.scaleX = 0.5;
	self.scaleY = 0.5;
	self.x = 2048 / 2 - self.width / 2;
	self.y = 600; // Upper half of screen, below GUI
	// Animation state
	self.phase = [];
	for (var j = 0; j < numBars; j++) {
		self.phase[j] = Math.random() * Math.PI * 2;
	}
	// Animate bars
	self.update = function () {
		var t = Date.now() * 0.002;
		for (var i = 0; i < numBars; i++) {
			// Simulate music: combine sine waves and random
			var wave = Math.sin(t + self.phase[i]) * 0.5 + 0.5;
			var beatPulse = (Math.sin(t * 2 + i * 0.2) * 0.5 + 0.5) * 0.5;
			var randomPulse = Math.random() * 0.15;
			var h = barMinHeight + (barMaxHeight - barMinHeight) * (wave * 0.6 + beatPulse * 0.3 + randomPulse);
			bars[i].height = h;
		}
	};
	return self;
});
// FXButton: Triggers a sound effect
var FXButton = Container.expand(function () {
	var self = Container.call(this);
	// Visuals
	var btn = self.attachAsset('fxButton', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	var txt = new Text2('FX', {
		size: 48,
		fill: "#fff"
	});
	txt.anchor.set(0.5, 0.5);
	btn.addChild(txt);
	// State
	self.cooldown = false;
	// Touch
	self.down = function (x, y, obj) {
		if (self.cooldown) return;
		self.cooldown = true;
		LK.getSound('fx').play();
		tween(btn, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 80,
			onFinish: function onFinish() {
				tween(btn, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 120
				});
			}
		});
		LK.setTimeout(function () {
			self.cooldown = false;
		}, 400);
	};
	return self;
});
// FerrisWheel: Animated slow spinning, color-changing Ferris wheel
var FerrisWheel = Container.expand(function () {
	var self = Container.call(this);
	// Config
	self.wheelRadius = 200;
	self.numCabins = 8;
	self.spinSpeed = 0.002; // Radians per update for wheelAssembly
	self.colorChangeCycleTime = 6000; // ms for a full color cycle for a cabin
	// HSV to RGB helper function (essential for color animations)
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t_color = v * (1 - (1 - f) * s); // Renamed 't' to avoid conflict
		switch (i % 6) {
			case 0:
				r = v;
				g = t_color;
				b = p;
				break;
			case 1:
				r = q;
				g = v;
				b = p;
				break;
			case 2:
				r = p;
				g = v;
				b = t_color;
				break;
			case 3:
				r = p;
				g = q;
				b = v;
				break;
			case 4:
				r = t_color;
				g = p;
				b = v;
				break;
			case 5:
				r = v;
				g = p;
				b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Wheel structure (hub and spokes) - this part will rotate
	self.wheelAssembly = self.addChild(new Container());
	// --- Full Circle (Rim) ---
	// Approximate a circle by placing many small ellipses around the rim
	self.rimSegments = 36;
	self.rimRadius = self.wheelRadius;
	for (var i = 0; i < self.rimSegments; i++) {
		var rimAngle = Math.PI * 2 / self.rimSegments * i;
		var rimX = Math.cos(rimAngle) * self.rimRadius;
		var rimY = Math.sin(rimAngle) * self.rimRadius;
		var rim = self.wheelAssembly.attachAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: 11,
			height: 11,
			x: rimX,
			y: rimY
		});
		rim.tint = 0xffffff;
		rim.alpha = 0.7;
	}
	// Central Hub
	var hub = self.wheelAssembly.attachAsset('ferrisHub', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Spokes
	for (var i = 0; i < self.numCabins; i++) {
		var angle = Math.PI * 2 / self.numCabins * i;
		var spoke = self.wheelAssembly.attachAsset('ferrisSpoke', {
			anchorX: 0.5,
			anchorY: 0,
			// Anchor at the hub-connection point
			height: self.wheelRadius,
			// Spoke length is the wheel radius
			rotation: angle
		});
	}
	// Cabins - these will be positioned along the rim and kept upright
	self.cabins = [];
	for (var i = 0; i < self.numCabins; i++) {
		var cabinAngle = Math.PI * 2 / self.numCabins * i;
		// Create cabin using LK.getAsset and add it as a child to the FerrisWheel container (self)
		var cabin = self.addChild(LK.getAsset('ferrisCabin', {
			anchorX: 0.5,
			anchorY: 0.5
		}));
		cabin.initialAngle = cabinAngle; // Store its angular position on the wheel
		// Initial position and color
		var xPos = Math.cos(cabinAngle) * self.wheelRadius;
		var yPos = Math.sin(cabinAngle) * self.wheelRadius;
		cabin.x = xPos;
		cabin.y = yPos;
		var initialHue = cabin.initialAngle / (Math.PI * 2) % 1.0;
		var initialRgb = hsvToRgb(initialHue, 0.85, 0.95);
		cabin.tint = initialRgb[0] << 16 | initialRgb[1] << 8 | initialRgb[2];
		self.cabins.push(cabin);
	}
	self.update = function () {
		// Spin the wheel assembly
		self.wheelAssembly.rotation += self.spinSpeed;
		// Update cabin positions and colors
		var globalTimeFactor = Date.now() % self.colorChangeCycleTime / self.colorChangeCycleTime;
		for (var i = 0; i < self.cabins.length; i++) {
			var cabin = self.cabins[i];
			// Calculate current angle of the cabin attachment point on the rotating wheel
			var currentAttachmentAngle = cabin.initialAngle + self.wheelAssembly.rotation;
			// Update cabin position relative to the FerrisWheel container's center
			cabin.x = Math.cos(currentAttachmentAngle) * self.wheelRadius;
			cabin.y = Math.sin(currentAttachmentAngle) * self.wheelRadius;
			// Cabins are direct children of 'self' (FerrisWheel container).
			// 'self' itself is not rotating, so cabins remain upright (rotation = 0) by default.
			// Animate color: Cycle hue for each cabin
			var cabinHue = (cabin.initialAngle / (Math.PI * 2) + globalTimeFactor) % 1.0;
			var rgb = hsvToRgb(cabinHue, 0.85, 0.95);
			var newTint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
			// Tween for smooth color change
			if (cabin.tint !== newTint) {
				tween.stop(cabin, {
					tint: true
				}); // Stop existing tint tweens on this cabin
				tween(cabin, {
					tint: newTint
				}, {
					duration: 300,
					easing: tween.linear
				});
			}
		}
	};
	return self;
});
// FireworksEffect: Animated fireworks exploding in the upper half of the screen
var FireworksEffect = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numRockets = 5; // Number of simultaneous rockets/explosions
	var rocketInterval = 2000; // ms between new rockets
	var rocketTimer = 0;
	var lastTickTime = Date.now();
	var rockets = [];
	var explosionParticles = [];
	// Predefined rainbow colors for explosions
	var beamColors = [0xff0000,
	// red
	0xff8000,
	// orange
	0xffff00,
	// yellow
	0x00ff00,
	// green
	0x00ffff,
	// cyan
	0x0000ff,
	// blue
	0x8000ff,
	// violet
	0xff00ff // magenta
	];
	// Explosion particle configuration
	var particleMinSize = 8;
	var particleMaxSize = 20;
	var particleSpeed = 15; // pixels per update
	var particleLifetime = 1000; // ms
	// Create an explosion
	function createExplosion(x, y, color) {
		var numParticles = 60 + Math.random() * 40;
		for (var i = 0; i < numParticles; i++) {
			var angle = Math.random() * Math.PI * 2;
			var speed = particleSpeed * (0.8 + Math.random() * 0.4);
			var particle = LK.getAsset('centerCircle', {
				anchorX: 0.5,
				anchorY: 0.5,
				width: (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.5,
				height: (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.5
			});
			particle.tint = color;
			particle.alpha = 1.0;
			particle.x = x;
			particle.y = y;
			self.addChild(particle);
			explosionParticles.push({
				obj: particle,
				speedX: Math.cos(angle) * speed,
				speedY: Math.sin(angle) * speed,
				startTime: Date.now(),
				color: color
			});
		}
	}
	// Animation
	self.update = function () {
		var now = Date.now();
		var delta = now - lastTickTime;
		lastTickTime = now;
		// --- Manage rockets ---
		rocketTimer += delta;
		if (rocketTimer >= rocketInterval && rockets.length < numRockets) {
			rocketTimer -= rocketInterval;
			// Launch a new rocket from the bottom
			var startX = 200 + Math.random() * (2048 - 400);
			var endY = 200 + Math.random() * (600 - 200); // Explode in upper half
			var rocket = LK.getAsset('centerCircle', {
				anchorX: 0.5,
				anchorY: 0.5,
				width: 10,
				height: 10
			});
			rocket.tint = 0xffffff; // White trail
			rocket.alpha = 0.8;
			rocket.x = startX;
			rocket.y = 2732; // Start from bottom
			self.addChild(rocket);
			rockets.push({
				obj: rocket,
				startX: startX,
				endY: endY,
				speedY: -(2732 - endY) / (rocketInterval * 0.6) * 16,
				// Adjust speed based on distance and interval
				color: beamColors[Math.floor(Math.random() * beamColors.length)],
				// Random explosion color from beamColors
				launchedTime: now
			});
		}
		// Update rockets
		for (var i = rockets.length - 1; i >= 0; i--) {
			var rocket = rockets[i];
			rocket.obj.y += rocket.speedY * (delta / 16); // Move rocket based on delta
			// Check if rocket reached explosion point
			if (rocket.obj.y <= rocket.endY || now - rocket.launchedTime > rocketInterval * 0.8) {
				// Explode also after a certain time
				createExplosion(rocket.obj.x, rocket.obj.y, rocket.color);
				rocket.obj.destroy();
				rockets.splice(i, 1);
			}
		}
		// Update explosion particles
		for (var i = explosionParticles.length - 1; i >= 0; i--) {
			var p = explosionParticles[i];
			var elapsed = now - p.startTime;
			if (elapsed > particleLifetime) {
				p.obj.destroy();
				explosionParticles.splice(i, 1);
			} else {
				// Move particle
				p.obj.x += p.speedX * (delta / 16);
				p.obj.y += p.speedY * (delta / 16);
				// Fade out
				p.obj.alpha = 1.0 - elapsed / particleLifetime;
			}
		}
	};
	return self;
});
// Flamethrower: Animated flames from the ground
var Flamethrower = Container.expand(function () {
	var self = Container.call(this);
	// Visuals - Flamethrower base
	var base = self.attachAsset('flamethrowerBase', {
		anchorX: 0.5,
		anchorY: 1.0,
		scaleX: 0.5,
		scaleY: 0.5
	});
	base.y = 0; // Position at the base of the container
	// Config
	var numParticles = 50;
	var particles = [];
	var particleMinSize = 40;
	var particleMaxSize = 120;
	var speedY = -15; // pixels per second (upwards)
	var speedX = 4; // horizontal flicker/sway
	var particleLifetime = 500; // ms
	// HSV to RGB helper
	function hsvToRgb(h, s, v) {
		var r, g, b;
		var i = Math.floor(h * 6);
		var f = h * 6 - i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t = v * (1 - (1 - f) * s);
		switch (i % 6) {
			case 0:
				r = v, g = t, b = p;
				break;
			case 1:
				r = q, g = v, b = p;
				break;
			case 2:
				r = p, g = v, b = t;
				break;
			case 3:
				r = p, g = q, b = v;
				break;
			case 4:
				r = t, g = p, b = v;
				break;
			case 5:
				r = v, g = p, b = q;
				break;
		}
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
	// Create flame particles
	self.createFlame = function () {
		var size = (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.48;
		var particle = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size,
			height: size
		});
		// Flame color (yellow to red)
		var hue = Math.random() * 0.1 + 0.05; // Hue from 0.05 (reddish-orange) to 0.15 (yellowish-orange)
		var rgb = hsvToRgb(hue, 1, 1);
		particle.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
		// Initial position at the base of the flamethrower
		// Position the particle relative to the top-center of the base graphic
		particle.x = 0; // Relative to the Flamethrower container's origin
		particle.y = -base.height; // Position above the base graphic
		particle.alpha = 0.8 + Math.random() * 0.2;
		self.addChild(particle);
		particles.push({
			obj: particle,
			speedScale: 0.8 + Math.random() * 0.4,
			startTime: Date.now(),
			baseSize: size,
			phase: Math.random() * Math.PI * 2
		});
	};
	// Animation
	self.update = function () {
		var now = Date.now();
		var delta = now - (self._lastTickTime || now);
		self._lastTickTime = now;
		// Create new particles periodically
		if (particles.length < numParticles) {
			self.createFlame();
		}
		// Update particles
		for (var i = particles.length - 1; i >= 0; i--) {
			var p = particles[i];
			var elapsed = now - p.startTime;
			if (elapsed > particleLifetime) {
				p.obj.destroy();
				particles.splice(i, 1);
			} else {
				// Move particle upwards with horizontal flicker
				p.obj.y += speedY * p.speedScale * (delta / 16);
				p.obj.x += speedX * Math.sin(now * 0.003 + p.phase) * p.speedScale * (delta / 16);
				// Fade out
				p.obj.alpha = (1.0 - elapsed / particleLifetime) * (0.8 + 0.2 * Math.sin(now * 0.004 + p.phase)); // Add flicker to fade
				// Scale down over time
				var size = p.baseSize * (1.0 - elapsed / particleLifetime * 0.8);
				p.obj.width = size;
				p.obj.height = size;
				// Animate color slightly towards red as it fades
				var fadeHue = Math.random() * 0.05; // Shift slightly towards red (hue 0)
				var fadeRgb = hsvToRgb(fadeHue, 1, 1);
				var originalTint = p.obj.tint;
				var r1 = originalTint >> 16 & 0xFF;
				var g1 = originalTint >> 8 & 0xFF;
				var b1 = originalTint & 0xFF;
				var r2 = fadeRgb[0];
				var g2 = fadeRgb[1];
				var b2 = fadeRgb[2];
				var blend = elapsed / particleLifetime;
				var r = Math.round(r1 * (1 - blend) + r2 * blend);
				var g = Math.round(g1 * (1 - blend) + g2 * blend);
				var b = Math.round(b1 * (1 - blend) + b2 * blend);
				p.obj.tint = r << 16 | g << 8 | b;
			}
		}
	};
	return self;
});
// FogEffect: Animated fog in upper half of the map
var FogEffect = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numParticles = 60;
	var particles = [];
	var particleMinSize = 200;
	var particleMaxSize = 800;
	var speedX = 3; // pixels per second, increased horizontal speed
	var speedY = 0.2; // pixels per second, decreased vertical speed
	var particleAlpha = 0.18; // Increased particle alpha for denser fog
	// Create fog particles
	for (var i = 0; i < numParticles; i++) {
		var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
		var particle = LK.getAsset('crossfaderTrack', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size * 2.0,
			// Make particles significantly wider
			height: size * 0.4 // Make particles much flatter
		});
		particle.tint = 0xffffff; // white fog
		particle.alpha = particleAlpha;
		// Random initial position in upper half, extending slightly beyond bounds for seamless loop
		var px = Math.random() * (2048 + particleMaxSize) - particleMaxSize / 2;
		var py = 100 + Math.random() * (1366 - 100);
		particle.x = px;
		particle.y = py;
		self.addChild(particle);
		particles.push({
			obj: particle,
			speedScale: 0.5 + Math.random() * 1.0,
			// particles move at different speeds
			baseAlpha: particleAlpha
		});
	}
	// Animation
	self.update = function () {
		var t = Date.now() * 0.001;
		for (var i = 0; i < particles.length; i++) {
			var p = particles[i];
			// Move particles from right to left
			p.obj.x -= speedX * p.speedScale;
			p.obj.y += speedY * p.speedScale;
			// Wrap around horizontally (from left to right)
			if (p.obj.x < -particleMaxSize / 2) {
				p.obj.x = 2048 + particleMaxSize / 2;
				p.obj.y = Math.random() * (2732 / 2 - 100) + 100; // new random y position within upper half
			}
			// Animate alpha for subtle flow and density changes
			p.obj.alpha = p.baseAlpha + 0.08 * Math.sin(t * 0.8 + i); // Increased alpha animation range
		}
	};
	return self;
});
// LaserShow: Realistic party laser show with moving heads and multi-color animated beams
var LaserShow = Container.expand(function () {
	var self = Container.call(this);
	// Config
	var numHeads = 6;
	var beamsPerHead = 3;
	var headRadius = 700; // Reduced from 1100 to 700 to move heads closer to the middle
	var centerX = 2048 / 2;
	var centerY = 400;
	var beamLength = 1200; // Reduced from 1600 to 1200 to keep beams inside the new head radius
	var beamWidth = 18;
	var beamAlpha = 0.65;
	var headAlpha = 0.7;
	var heads = [];
	var beams = [];
	// Predefined rainbow colors for beams
	var beamColors = [0xff0000,
	// red
	0xff8000,
	// orange
	0xffff00,
	// yellow
	0x00ff00,
	// green
	0x00ffff,
	// cyan
	0x0000ff,
	// blue
	0x8000ff,
	// violet
	0xff00ff // magenta
	];
	// Create moving heads and beams
	for (var h = 0; h < numHeads; h++) {
		// Head position in a semi-circle
		var angle = Math.PI * (h + 1) / (numHeads + 1);
		var hx = centerX + Math.cos(angle) * headRadius;
		var hy = centerY + Math.sin(angle) * 200;
		// Head (visual: ellipse, colored)
		// Use rainbowEllipse for gradient rainbow look
		var head = LK.getAsset('rainbowEllipse', {
			anchorX: 0.5,
			anchorY: 0.5,
			x: hx,
			y: hy,
			width: 20,
			height: 20
		});
		head.alpha = headAlpha;
		// Animate color: store initial hue offset for each head
		head._rainbowHueOffset = h * (360 / numHeads);
		self.addChild(head);
		heads.push(head);
		// Beams for this head
		for (var b = 0; b < beamsPerHead; b++) {
			var beam = LK.getAsset('crossfaderTrack', {
				anchorX: 0.5,
				anchorY: 0,
				x: hx,
				y: hy,
				width: beamWidth,
				height: beamLength
			});
			// Assign color in a rainbow pattern, offset by head and beam
			beam.tint = beamColors[(h * beamsPerHead + b) % beamColors.length];
			beam.alpha = beamAlpha;
			// Initial rotation
			beam.rotation = 0;
			self.addChild(beam);
			beams.push({
				obj: beam,
				headIdx: h,
				beamIdx: b,
				baseAngle: angle
			});
		}
	}
	// Animate heads and beams
	self.update = function () {
		var t = Date.now() * 0.001;
		// Animate heads in a subtle up/down and color pulse
		for (var h = 0; h < heads.length; h++) {
			var head = heads[h];
			// Sway up/down
			head.y = centerY + Math.sin(Math.PI * (h + 1) / (numHeads + 1)) * 200 + Math.sin(t * 1.2 + h) * 18;
			// Pulse alpha
			head.alpha = headAlpha + 0.15 * Math.sin(t * 2 + h);
			// Animate rainbow color: cycle hue every second
			// Full cycle every 1 second (t mod 1.0)
			var hue = (head._rainbowHueOffset + t * 360) % 360 / 360;
			// HSV to RGB conversion
			var i = Math.floor(hue * 6);
			var f = hue * 6 - i;
			var q = 1 - f;
			var tcol = 1 - (1 - f);
			var r, g, b;
			switch (i % 6) {
				case 0:
					r = 1, g = tcol, b = 0;
					break;
				case 1:
					r = q, g = 1, b = 0;
					break;
				case 2:
					r = 0, g = 1, b = tcol;
					break;
				case 3:
					r = 0, g = q, b = 1;
					break;
				case 4:
					r = tcol, g = 0, b = 1;
					break;
				case 5:
					r = 1, g = 0, b = q;
					break;
			}
			var tint = Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255);
			// Tween the head tint for smooth color change
			tween(head, {
				tint: tint
			}, {
				duration: 120,
				easing: tween.linear
			});
		}
		// Animate beams: sweep, flicker, and color pulse
		for (var i = 0; i < beams.length; i++) {
			var beamData = beams[i];
			var beam = beamData.obj;
			var h = beamData.headIdx;
			var b = beamData.beamIdx;
			var baseAngle = beamData.baseAngle;
			// Beam origin follows head
			var head = heads[h];
			beam.x = head.x;
			beam.y = head.y;
			// Animate beam angle: base + sweeping + per-beam offset
			var sweep = Math.sin(t * 1.1 + h + b * 0.7) * 0.25 + Math.sin(t * 0.7 + b) * 0.12;
			var beatPulse = Math.sin(t * 2.5 + h + b) * 0.08;
			beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse;
			// Animate beam length for flicker
			var flicker = 1 + 0.12 * Math.sin(t * 3 + h * 2 + b * 1.5 + Math.sin(t * 1.5 + b));
			beam.height = beamLength * flicker;
			// Animate color pulse (simulate RGB laser color mixing)
			var colorIdx = (h * beamsPerHead + b + Math.floor(t * 2 + h + b)) % beamColors.length;
			beam.tint = beamColors[colorIdx];
			// Animate alpha for strobe effect
			beam.alpha = beamAlpha + 0.15 * Math.abs(Math.sin(t * 4 + h + b));
		}
	};
	return self;
});
// SmokeDiffuser: Animated smoke diffuser with rising smoke puffs
var SmokeDiffuser = Container.expand(function () {
	var self = Container.call(this);
	// Visuals - Diffuser base (small ellipse)
	var base = self.attachAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 1.0,
		width: 60,
		height: 30,
		y: 0
	});
	base.tint = 0x888888;
	base.alpha = 0.85;
	// Config
	var numParticles = 12;
	var particles = [];
	var particleMinSize = 32;
	var particleMaxSize = 64;
	var speedY = -3.5; // pixels per frame (upwards)
	var speedX = 0.7; // horizontal drift
	var particleLifetime = 1200; // ms
	// Create a smoke puff
	self.createSmoke = function () {
		var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
		var puff = LK.getAsset('centerCircle', {
			anchorX: 0.5,
			anchorY: 0.5,
			width: size,
			height: size
		});
		// Smoke color: light gray, some randomization
		var gray = 180 + Math.floor(Math.random() * 40);
		puff.tint = gray << 16 | gray << 8 | gray;
		puff.alpha = 0.32 + Math.random() * 0.18;
		// Start at base, with slight random x offset
		puff.x = Math.random() * 16 - 8;
		puff.y = -base.height + Math.random() * 8;
		self.addChild(puff);
		particles.push({
			obj: puff,
			speedScale: 0.8 + Math.random() * 0.5,
			startTime: Date.now(),
			baseSize: size,
			phase: Math.random() * Math.PI * 2
		});
	};
	// Animation
	self.update = function () {
		var now = Date.now();
		// Create new puffs if needed
		if (particles.length < numParticles) {
			self.createSmoke();
		}
		// Update puffs
		for (var i = particles.length - 1; i >= 0; i--) {
			var p = particles[i];
			var elapsed = now - p.startTime;
			if (elapsed > particleLifetime) {
				p.obj.destroy();
				particles.splice(i, 1);
			} else {
				// Move upwards, drift sideways
				p.obj.y += speedY * p.speedScale;
				p.obj.x += speedX * Math.sin(now * 0.001 + p.phase) * p.speedScale;
				// Fade out and grow
				p.obj.alpha = (1.0 - elapsed / particleLifetime) * (0.32 + 0.18 * Math.sin(now * 0.002 + p.phase));
				var size = p.baseSize * (1.0 + elapsed / particleLifetime * 0.7);
				p.obj.width = size;
				p.obj.height = size;
			}
		}
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// Default height, class will use actual radius
// --- Reflectors (behind discoball) ---
var reflectors = [];
var numReflectors = 7;
var reflectorSpacing = 2048 / (numReflectors + 1);
var reflectorY = 80; // Top, but below menu area
// Create reflectors but do not add to game yet
for (var i = 0; i < numReflectors; i++) {
	var rx = reflectorSpacing * (i + 1);
	var reflector = LK.getAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 60,
		height: 60
	});
	reflector.tint = 0xffffff;
	reflector.alpha = 0.0;
	reflector.x = rx;
	reflector.y = reflectorY;
	reflectors.push(reflector);
}
// --- Discoball (top of screen) ---
// Asset for dancing woman
// Asset for dancing man
var discoball = new Discoball();
// Add reflectors behind discoball in display order
for (var i = 0; i < reflectors.length; i++) {
	game.addChild(reflectors[i]);
}
// Insert discoball after reflectors so it appears above them
game.addChild(discoball);
discoball.x = 2048 / 2;
discoball.y = 200 - 69; // Move discoball up by 69 units
discoball.scaleX = 0.8;
discoball.scaleY = 0.8;
// --- Extra white particles for upper half sparkle effect ---
var extraWhiteParticles = [];
var numExtraParticles = 32;
for (var i = 0; i < numExtraParticles; i++) {
	// Random position in upper half, avoid top 100px (menu)
	var px = 100 + Math.random() * (2048 - 200);
	var py = 120 + Math.random() * (1366 - 220);
	var size = 8 + Math.random() * 10;
	var particle = LK.getAsset('centerCircle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: size,
		height: size
	});
	particle.tint = 0xffffff;
	particle.alpha = 0.18 + Math.random() * 0.22;
	particle.x = px;
	particle.y = py;
	game.addChild(particle);
	extraWhiteParticles.push({
		obj: particle,
		baseX: px,
		baseY: py,
		phase: Math.random() * Math.PI * 2,
		baseAlpha: particle.alpha,
		baseSize: size
	});
}
// Animate extra particles in game.update
// --- Fog Effect (upper half) ---
var fogEffect = new FogEffect();
game.addChild(fogEffect);
fogEffect.y = 0; // Position at the top of the screen
// --- Confetti Effect (upper half) ---
var confettiEffect = new ConfettiEffect();
game.addChild(confettiEffect);
confettiEffect.y = 0; // Position at the top of the screen
// --- DJ Deck Asset ---
var djDeck = LK.getAsset('djDeck', {
	anchorX: 0.5,
	anchorY: 0.5
});
game.addChild(djDeck);
djDeck.x = 2048 / 2;
djDeck.y = 2732 / 2 + 333 + 12; // Move the DJ deck down by 345 units
// --- Dancing People ---
var dancingPeople = [];
var numPeople = 8;
var peopleAreaY = djDeck.y - djDeck.height / 2 - 200 + 200 + 69; // Position above the DJ deck, moved down by 200 units, then down by 69 more
var peopleAreaWidth = 1600; // Area width for dancing people
var startX = 2048 / 2 - peopleAreaWidth / 2 - 77;
var personSpacing = peopleAreaWidth / (numPeople - 1);
for (var i = 0; i < numPeople; i++) {
	var person = new DancingPerson();
	person.x = startX + i * personSpacing;
	person.baseY = peopleAreaY; // Set the base Y position for animation
	// Insert each person below the djDeck in display order
	var djDeckIndex = game.children.indexOf(djDeck);
	if (djDeckIndex !== -1) {
		game.addChildAt(person, djDeckIndex);
	} else {
		game.addChild(person);
	}
	dancingPeople.push(person);
}
// --- Laser Show (upper half) ---
var laserShow = new LaserShow();
game.addChild(laserShow);
laserShow.y = -456; // Move laser effect up by 456 units (333 + 123)
// --- Fireworks Effect (upper half) ---
var fireworksEffect = new FireworksEffect();
game.addChild(fireworksEffect);
fireworksEffect.y = 0; // Position at the top of the screen
// --- Ferris Wheel (upper half) ---
var ferrisWheel = new FerrisWheel();
game.addChild(ferrisWheel);
ferrisWheel.x = 2048 / 2 - 400;
ferrisWheel.y = 450; // Positioned in the upper-middle part of the screen
ferrisWheel.scaleX = 0.8;
ferrisWheel.scaleY = 0.8;
// No dark overlay is present, so nothing to remove.
// --- Dancing People ---
var dancingPeople = [];
var numPeople = 8;
var peopleAreaY = djDeck.y - djDeck.height / 2 - 200 + 200 + 69; // Position above the DJ deck, moved down by 200 units, then down by 69 more
var peopleAreaWidth = 1600; // Area width for dancing people
var startX = 2048 / 2 - peopleAreaWidth / 2;
var personSpacing = peopleAreaWidth / (numPeople - 1);
for (var i = 0; i < numPeople; i++) {
	var person = new DancingPerson();
	person.x = startX + i * personSpacing;
	person.baseY = peopleAreaY; // Set the base Y position for animation
	game.addChild(person);
	dancingPeople.push(person);
}
// --- Smoke Diffuser (left of people line) ---
var smokeDiffuser = new SmokeDiffuser();
// Place to the left of the first dancing person, with a little gap
smokeDiffuser.x = startX - 80;
smokeDiffuser.y = peopleAreaY + 18; // Align base with people feet
game.addChild(smokeDiffuser);
// --- Flamethrower ---
var flamethrower = new Flamethrower();
// Position the flamethrower to the right of the dancing people line
flamethrower.x = startX + peopleAreaWidth + 100; // Adjust position as needed
// Move the flamethrower down so the flame aligns with the top of the base
flamethrower.y = peopleAreaY + (flamethrower.height ? flamethrower.height * 0.5 : 75); // 75 is half of base height (150*0.5), fallback if height is not set
game.addChild(flamethrower);
// Ensure ferriswheel, cabins, and rim dots are always under the dancing people in display order
for (var i = 0; i < dancingPeople.length; i++) {
	// Find the minimum index among all dancing people
	var minIndex = game.children.length;
	for (var j = 0; j < dancingPeople.length; j++) {
		var idx = game.children.indexOf(dancingPeople[j]);
		if (idx !== -1 && idx < minIndex) minIndex = idx;
	}
	// Move ferrisWheel itself below all dancing people
	if (game.children.indexOf(ferrisWheel) > minIndex) {
		game.setChildIndex(ferrisWheel, minIndex);
	}
	// Move all cabins below all dancing people
	if (ferrisWheel.cabins) {
		for (var c = 0; c < ferrisWheel.cabins.length; c++) {
			var cabin = ferrisWheel.cabins[c];
			if (game.children.indexOf(cabin) > minIndex) {
				game.setChildIndex(cabin, minIndex);
			}
		}
	}
	// Move all rim dots (centerCircle assets attached to wheelAssembly) below all dancing people
	if (ferrisWheel.wheelAssembly && ferrisWheel.wheelAssembly.children) {
		for (var w = 0; w < ferrisWheel.wheelAssembly.children.length; w++) {
			var rimDot = ferrisWheel.wheelAssembly.children[w];
			// Only move rim dots (centerCircle assets, not hub or spokes)
			if (rimDot && rimDot.assetId === 'centerCircle') {
				// The rim dots are not direct children of game, but of wheelAssembly, so nothing to do here for game order
				// If in future, rim dots are added to game, ensure they are below dancing people
			}
		}
	}
	// Now, ensure this dancing person is above ferrisWheel and all its parts
	var maxFerrisIndex = game.children.indexOf(ferrisWheel);
	if (ferrisWheel.cabins) {
		for (var c = 0; c < ferrisWheel.cabins.length; c++) {
			var idx = game.children.indexOf(ferrisWheel.cabins[c]);
			if (idx > maxFerrisIndex) maxFerrisIndex = idx;
		}
	}
	// (Rim dots are not direct children of game, so not included)
	game.setChildIndex(dancingPeople[i], maxFerrisIndex + 1);
}
// --- Decks ---
// Sound effects (dummy, as actual music is handled by LK)
// Beat lights
// FX Button
// Crossfader
// Deck platters (turntables)
// --- Layout constants ---
var deckY = 1366; // Move decks to vertical center of 2732px screen
var deckSpacing = 1000;
var crossfaderY = 1800;
var fxButtonY = 2200;
// --- Equalizer Waves (upper half) ---
var equalizer = new EqualizerWaves();
equalizer.y += 200; // Move the equalizer down by 200 units
game.addChild(equalizer);
// --- Decks ---
var leftDeck = new DeckPlatter();
leftDeck.setTrack('A');
game.addChild(leftDeck);
leftDeck.x = 2048 / 2 - deckSpacing / 2;
leftDeck.y = deckY;
var rightDeck = new DeckPlatter();
rightDeck.setTrack('B');
game.addChild(rightDeck);
rightDeck.x = 2048 / 2 + deckSpacing / 2;
rightDeck.y = deckY;
// --- Crossfader ---
var crossfader = new Crossfader();
game.addChild(crossfader);
crossfader.x = 2048 / 2;
crossfader.y = crossfaderY;
crossfader.setValue(0.5);
// --- FX Button ---
var fxButton = new FXButton();
game.addChild(fxButton);
fxButton.x = 2048 / 2;
fxButton.y = fxButtonY;
// --- Score / Combo / Energy ---
var energy = 50; // 0-100
var combo = 0;
var score = 0;
// Energy bar
var energyBarBg = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 600,
	height: 40
});
energyBarBg.tint = 0x222222;
energyBarBg.y = 0;
var energyBar = LK.getAsset('crossfaderTrack', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 600,
	height: 40
});
energyBar.tint = 0x00ff00;
energyBar.y = 0;
var energyBarContainer = new Container();
energyBarContainer.addChild(energyBarBg);
energyBarContainer.addChild(energyBar);
energyBarContainer.x = 2048 / 2;
energyBarContainer.y = 200;
LK.gui.top.addChild(energyBarContainer);
// --- Fog Button under pause button (top left, but not in 0,0 area) ---
var fogButton = LK.getAsset('fogButton', {
	anchorX: 1.0,
	anchorY: 0.0
});
LK.gui.topRight.addChild(fogButton);
fogButton.x = 0;
fogButton.y = 0;
// Score text
var scoreTxt = new Text2('Score: 0', {
	size: 80,
	fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 300;
// Combo text
var comboTxt = new Text2('', {
	size: 60,
	fill: "#ff0"
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.x = 2048 / 2;
comboTxt.y = 400;
// --- State ---
var dragging = null; // Which control is being dragged
var lastTouchObj = null;
// --- Beat simulation ---
var beatInterval = 600; // ms per beat
var beatTimer = 0;
var lastTickTime = Date.now();
// --- Music ---
LK.playMusic('trackA', {
	loop: true
});
LK.playMusic('trackB', {
	loop: true
});
// HSV to RGB helper for background color
function hsvToRgb(h, s, v) {
	var r, g, b;
	var i = Math.floor(h * 6);
	var f = h * 6 - i;
	var p = v * (1 - s);
	var q = v * (1 - f * s);
	var t = v * (1 - (1 - f) * s);
	switch (i % 6) {
		case 0:
			r = v, g = t, b = p;
			break;
		case 1:
			r = q, g = v, b = p;
			break;
		case 2:
			r = p, g = v, b = t;
			break;
		case 3:
			r = p, g = q, b = v;
			break;
		case 4:
			r = t, g = p, b = v;
			break;
		case 5:
			r = v, g = p, b = q;
			break;
	}
	return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
var trackAVol = 1;
var trackBVol = 1;
// --- Touch handling ---
function getTouchedControl(x, y) {
	// Check decks
	var lx = leftDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).x;
	var ly = leftDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).y;
	if (Math.pow(lx - leftDeck.width / 2, 2) + Math.pow(ly - leftDeck.height / 2, 2) < 200 * 200) return leftDeck;
	var rx = rightDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).x;
	var ry = rightDeck.toLocal(game.toGlobal({
		x: x,
		y: y
	})).y;
	if (Math.pow(rx - rightDeck.width / 2, 2) + Math.pow(ry - rightDeck.height / 2, 2) < 200 * 200) return rightDeck;
	// Crossfader
	var cf = crossfader.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (cf.x > 0 && cf.x < crossfader.width && cf.y > 0 && cf.y < crossfader.height) return crossfader;
	// FX Button
	var fx = fxButton.toLocal(game.toGlobal({
		x: x,
		y: y
	}));
	if (Math.pow(fx.x - fxButton.width / 2, 2) + Math.pow(fx.y - fxButton.height / 2, 2) < 60 * 60) return fxButton;
	return null;
}
game.down = function (x, y, obj) {
	var control = getTouchedControl(x, y);
	dragging = control;
	lastTouchObj = obj;
	if (control && control.down) {
		// Convert to local
		var local = control.toLocal(game.toGlobal({
			x: x,
			y: y
		}));
		control.down(local.x, local.y, obj);
	}
};
game.up = function (x, y, obj) {
	if (dragging && dragging.up) {
		var local = dragging.toLocal(game.toGlobal({
			x: x,
			y: y
		}));
		dragging.up(local.x, local.y, obj);
	}
	dragging = null;
	lastTouchObj = null;
};
game.move = function (x, y, obj) {
	if (dragging && dragging.move) {
		var local = dragging.toLocal(game.toGlobal({
			x: x,
			y: y
		}));
		dragging.move(local.x, local.y, obj);
	}
};
// --- Game update ---
game.update = function () {
	// Update discoball animation
	discoball.update();
	// Update fog effect animation
	fogEffect.update();
	// Update confetti effect animation
	confettiEffect.update();
	// Update dancing people animations
	for (var i = 0; i < dancingPeople.length; i++) {
		dancingPeople[i].update();
	}
	// Animate extra white particles (subtle sparkle and movement)
	if (extraWhiteParticles) {
		var t = Date.now() * 0.001;
		for (var i = 0; i < extraWhiteParticles.length; i++) {
			var p = extraWhiteParticles[i];
			// Subtle float and twinkle
			p.obj.x = p.baseX + Math.sin(t * 1.2 + p.phase + i) * 8;
			p.obj.y = p.baseY + Math.cos(t * 1.1 + p.phase + i * 0.7) * 6;
			p.obj.alpha = p.baseAlpha + 0.10 * Math.sin(t * 2.2 + p.phase + i * 0.5);
			var s = p.baseSize * (0.95 + 0.12 * Math.sin(t * 1.7 + p.phase + i));
			p.obj.width = s;
			p.obj.height = s;
		}
	}
	// Animate flashing white reflectors
	if (reflectors) {
		var t = Date.now() * 0.001;
		for (var i = 0; i < reflectors.length; i++) {
			// Staggered flash, each reflector flashes in sequence
			var phase = t * 2.2 + i * 0.5;
			// Use a sharp pulse for flash, then fade out
			var flash = Math.max(0, Math.sin(phase));
			// Sharpen the flash curve for a strobe effect
			flash = Math.pow(flash, 6);
			reflectors[i].alpha = 0.18 + 0.82 * flash;
			// Animate as perfect circles for extra pop (half size base)
			var size = 60 + 20 * flash;
			// Prevent reflectors from covering the discoball
			// Compute distance from reflector to discoball center
			var dx = reflectors[i].x - discoball.x;
			var dy = reflectors[i].y - discoball.y;
			var dist = Math.sqrt(dx * dx + dy * dy);
			// Discoball's visible radius (scaled)
			var discoballRadius = 150 * discoball.scaleX; // matches Discoball class
			// If reflector would overlap discoball, shrink it so it cannot cover
			if (dist < discoballRadius + size / 2) {
				// Max allowed size so edge of reflector does not enter discoball
				var maxSize = Math.max(0, 2 * (dist - discoballRadius));
				if (size > maxSize) size = maxSize;
				if (size < 0) size = 0;
			}
			reflectors[i].width = size;
			reflectors[i].height = size;
		}
	}
	// Update equalizer animation
	equalizer.update();
	// Update laser show animation
	laserShow.update();
	// Update fireworks effect animation
	fireworksEffect.update();
	// Update Ferris wheel animation
	if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) {
		// Defensive check
		ferrisWheel.update();
	}
	// Update smoke diffuser animation
	if (typeof smokeDiffuser !== 'undefined' && smokeDiffuser.update) {
		smokeDiffuser.update();
	}
	// Update flamethrower animation
	if (typeof flamethrower !== 'undefined' && flamethrower.update) {
		flamethrower.update();
	}
	// Update decks
	leftDeck.update();
	rightDeck.update();
	// Simulate beat
	var now = Date.now();
	beatTimer += now - lastTickTime;
	lastTickTime = now;
	if (beatTimer >= beatInterval) {
		beatTimer -= beatInterval;
		leftDeck.flashBeat();
		rightDeck.flashBeat();
		LK.getSound('beat').play();
		// Energy drops if not scratching or mixing
		if (!leftDeck.isScratching && !rightDeck.isScratching && crossfader.value > 0.2 && crossfader.value < 0.8) {
			energy -= 2;
			combo = 0;
		} else {
			// Combo up if scratching or crossfading
			combo += 1;
			score += 10 * combo;
			energy += 2;
			if (energy > 100) energy = 100;
		}
		if (energy < 0) energy = 0;
		// Update visuals
		scoreTxt.setText('Score: ' + score);
		if (combo > 1) {
			comboTxt.setText('Combo x' + combo);
		} else {
			comboTxt.setText('');
		}
	}
	// Update energy bar
	energyBar.width = 600 * (energy / 100);
	// Crossfader logic: adjust music volumes
	trackAVol = 1 - crossfader.value;
	trackBVol = crossfader.value;
	// (In a real game, would set music volumes here, but LK handles music globally.)
	// End game if energy is 0
	if (energy <= 0) {
		// Game over event disabled
	}
	// Animate background color
	var time = Date.now() * 0.0005; // Time for animation
	var hue = time * 360 % 360; // Cycle through hues over time
	var rgb = hsvToRgb(hue / 360, 0.6, 0.5); // Convert HSV to RGB (adjust saturation and value for desired effect)
	game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]);
	// Win if score is high
	if (score >= 5000) {
		LK.effects.flashScreen(0x00ff00, 1000);
		LK.showYouWin();
	}
}; ===================================================================
--- original.js
+++ change.js
@@ -1328,21 +1328,21 @@
 var dancingPeople = [];
 var numPeople = 8;
 var peopleAreaY = djDeck.y - djDeck.height / 2 - 200 + 200 + 69; // Position above the DJ deck, moved down by 200 units, then down by 69 more
 var peopleAreaWidth = 1600; // Area width for dancing people
-// Calculate the width of a person (use the largest possible, as both man and woman are 222px wide)
-var personWidth = 222;
-var totalPeopleWidth = numPeople * personWidth;
-var availableWidth = peopleAreaWidth;
-// If totalPeopleWidth > availableWidth, reduce spacing so all fit
-var personSpacing = (availableWidth - personWidth) / (numPeople - 1);
-// Center the line so all people are visible
-var startX = 2048 / 2 - ((numPeople - 1) * personSpacing + personWidth) / 2 + personWidth / 2;
+var startX = 2048 / 2 - peopleAreaWidth / 2 - 77;
+var personSpacing = peopleAreaWidth / (numPeople - 1);
 for (var i = 0; i < numPeople; i++) {
 	var person = new DancingPerson();
 	person.x = startX + i * personSpacing;
 	person.baseY = peopleAreaY; // Set the base Y position for animation
-	game.addChild(person);
+	// Insert each person below the djDeck in display order
+	var djDeckIndex = game.children.indexOf(djDeck);
+	if (djDeckIndex !== -1) {
+		game.addChildAt(person, djDeckIndex);
+	} else {
+		game.addChild(person);
+	}
 	dancingPeople.push(person);
 }
 // --- Laser Show (upper half) ---
 var laserShow = new LaserShow();
@@ -1359,8 +1359,22 @@
 ferrisWheel.y = 450; // Positioned in the upper-middle part of the screen
 ferrisWheel.scaleX = 0.8;
 ferrisWheel.scaleY = 0.8;
 // No dark overlay is present, so nothing to remove.
+// --- Dancing People ---
+var dancingPeople = [];
+var numPeople = 8;
+var peopleAreaY = djDeck.y - djDeck.height / 2 - 200 + 200 + 69; // Position above the DJ deck, moved down by 200 units, then down by 69 more
+var peopleAreaWidth = 1600; // Area width for dancing people
+var startX = 2048 / 2 - peopleAreaWidth / 2;
+var personSpacing = peopleAreaWidth / (numPeople - 1);
+for (var i = 0; i < numPeople; i++) {
+	var person = new DancingPerson();
+	person.x = startX + i * personSpacing;
+	person.baseY = peopleAreaY; // Set the base Y position for animation
+	game.addChild(person);
+	dancingPeople.push(person);
+}
 // --- Smoke Diffuser (left of people line) ---
 var smokeDiffuser = new SmokeDiffuser();
 // Place to the left of the first dancing person, with a little gap
 smokeDiffuser.x = startX - 80;
 
 
 Photorealistic Ferris cabin, sideview
 
 
 
 
 
 Grey circle with transparent inner place, front view
 there are 4 piece of holes in the image, repair it
 
 
 
 photorealistic grey Tablet lying down on dj deck, front view. The screen should be dark monocrome screen in power of mode without text.
 
 
 Photorealistic transparent rectange dj deck sample button with white rounded corners, front view. No text needed on image
 Transparent ballon shape.
 
 Replace arrow to OK text
 Dancing woman in a white top filled with multi-colored UV ink palm prints. UV luminous bracelet on the arm
 
 Photorealistic dollar, front view
 Dancing man in a white shirt filled with multi-colored UV ink palm prints. UV luminous bracelet on the arm
 
 White thin pencil in 45 degrees instead of arrow.
 Replace arrow to white X
 Change the orange filling color from the middle to black
 
 simple grey colored DJZ logo
 REPLACE THE PENCIL WITH WHITE MENU TEXT
 Replace pencil with white REC text, but no other changes!
1
Music
trackA
Music
trackB
Music
sample1
Sound effect
sample2
Sound effect
sample3
Sound effect
sample4
Sound effect
Sample5
Sound effect
sample6
Sound effect
sample7
Sound effect
sample8
Sound effect
sample9
Sound effect
sample10
Sound effect
sample11
Sound effect
sample12
Sound effect
sample13
Sound effect
sample14
Sound effect
sample15
Sound effect
sample16
Sound effect