User prompt
Add 10more smoke machines next to the first one with the same spacing as flamethrower and first base
User prompt
Add 18 another smoke machine next to the first one but let the same distance between them
User prompt
Move the left flamethrower left by 100 units
User prompt
Put a second flamethrower to the left side edge of the screen
User prompt
Mirror the flamethrower to the left side edge of the screen
User prompt
Move the flamethrower effect right by 7 units
User prompt
Move the flamethrower effect right by 77 units
User prompt
Move the flamethrower effect right by 50 units
User prompt
Move the fireworks effect right by 50 units
User prompt
Move all people closer to each other by 25 unitsto let place on the sides
User prompt
Move all people a little bit closer to each other to let place on the sides
User prompt
Move all people closer to each other to let place on the sides
User prompt
The proportion of people dancing should be 50% women and 50% men
User prompt
Change reflector-lines to transparent spotlights
User prompt
Change reflector-lines to reflectorlight asset
User prompt
Ensure that reflector lines are really white not grey
User prompt
Change reflector grey lines to snowwhite
User prompt
Add thick white reflector laserlines to the reflector
User prompt
Brighten up the equalizer lights by increasing the bar color value
User prompt
Brighten up the equalizer lights by increasing the bar color value (brightness) in hsvToRgb
User prompt
Change the equalizer lights Brighten up
User prompt
Move the digital clock up by 500 units
User prompt
Scale down the digital clock size to half
User prompt
Scale down the digital clock size to a quarter
User prompt
Scale down the digital clock size to the half
/**** * 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, width: 2048, height: 2020 * (2048 / 2040) // maintain aspect ratio }); game.addChild(djDeck); djDeck.x = 2048 / 2; djDeck.y = 2732 / 2 + 333 + 12 + 12; // Move the DJ deck down by 357 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 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; // --- DJ Deck Asset --- var djDeck = LK.getAsset('djDeck', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2020 * (2048 / 2040) // maintain aspect ratio }); game.addChild(djDeck); djDeck.x = 2048 / 2; djDeck.y = 2732 / 2 + 333 + 12 + 12; // Move the DJ deck down by 357 units // 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); // --- Digital Clock (center of map) --- var digitalClockTxt = new Text2('', { size: 60, fill: "#fff" }); digitalClockTxt.anchor.set(0.5, 0.5); digitalClockTxt.x = 2048 / 2; digitalClockTxt.y = 2732 / 2; game.addChild(digitalClockTxt); // Helper to format time as HH:MM:SS function formatTime(date) { var h = date.getHours(); var m = date.getMinutes(); var s = date.getSeconds(); function pad(n) { return n < 10 ? '0' + n : n; } return pad(h) + ':' + pad(m) + ':' + pad(s); } // --- 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(); } // --- Update Digital Clock --- if (typeof digitalClockTxt !== 'undefined') { var now = new Date(); digitalClockTxt.setText(formatTime(now)); } };
/****
* 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,
width: 2048,
height: 2020 * (2048 / 2040) // maintain aspect ratio
});
game.addChild(djDeck);
djDeck.x = 2048 / 2;
djDeck.y = 2732 / 2 + 333 + 12 + 12; // Move the DJ deck down by 357 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
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;
// --- DJ Deck Asset ---
var djDeck = LK.getAsset('djDeck', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2020 * (2048 / 2040) // maintain aspect ratio
});
game.addChild(djDeck);
djDeck.x = 2048 / 2;
djDeck.y = 2732 / 2 + 333 + 12 + 12; // Move the DJ deck down by 357 units
// 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);
// --- Digital Clock (center of map) ---
var digitalClockTxt = new Text2('', {
size: 60,
fill: "#fff"
});
digitalClockTxt.anchor.set(0.5, 0.5);
digitalClockTxt.x = 2048 / 2;
digitalClockTxt.y = 2732 / 2;
game.addChild(digitalClockTxt);
// Helper to format time as HH:MM:SS
function formatTime(date) {
var h = date.getHours();
var m = date.getMinutes();
var s = date.getSeconds();
function pad(n) {
return n < 10 ? '0' + n : n;
}
return pad(h) + ':' + pad(m) + ':' + pad(s);
}
// --- 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();
}
// --- Update Digital Clock ---
if (typeof digitalClockTxt !== 'undefined') {
var now = new Date();
digitalClockTxt.setText(formatTime(now));
}
};
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