User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'x')' in or related to this line: 'leftFilterKnobMid.x = leftDeckAsset.x + 190 + 400 + 30; // Move right by 30 units' Line Number: 5435
User prompt
Divide all knobs by 30 units horizontal and 30 units vertical
User prompt
Scale up filter knobs size by 1.5x ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Scale up filter knobs size by 2x
User prompt
Scale the sides of the crossfader s with white lines from MIN to MAX
User prompt
Rename tovedit asset to editbutton
User prompt
CHANGE EDIT BUTTON TO EDITBUTTON ASSET AND NOT TO TOVEDIT
User prompt
CHANGE EDIT BUTTON TO EDITBUTTON ASSET
User prompt
Replace edit button asset with edit asset!
User prompt
Replace edit button looking tovedit asset
User prompt
WHY DID YOU PUT EDIT BUTTON ON OK BUTTON??? YOU SHOULD MOVE IT DOWN TO DIVIDE THEM!!!
User prompt
MOVE EDIT BUTTON DOWN BY 20 UNITS
User prompt
MOVE EDIT BUTTON DOWN BY 18 UNITS
User prompt
MOVE EDIT BUTTON DOWN BY 12 UNITS
User prompt
MOVE ALL ARROWBUTTONS AND OK BUTTON UP BY 25 UNITS
User prompt
MOVE ALL ARROWBUTTONS AND OK BUTTON BACK TO THEIR LAST PLACE
User prompt
MOVE ALL ARROWBUTTONS AND OK BUTTON UP BY 20 UNITS
User prompt
A music creation and editing application (DAW – Digital Audio Workstation) should include several essential features, depending on whether it's targeted at beginners, intermediate users, or professionals. However, here are the core features that it must include: 🎛️ 1. Track Management Support for multiple tracks (audio, MIDI) Per-track volume, pan, mute, and solo controls Track grouping (bus routing) 🎹 2. MIDI Editing Piano roll view Drawing, deleting, and moving MIDI notes Editing velocity and other parameters MIDI automation 🎧 3. Audio Recording and Editing Recording from microphone or other inputs Cutting, stretching, timing (time-stretching, quantization) Normalization, fade-in/fade-out Moving and looping audio clips 🎚️ 4. Automation Automating parameters (e.g., volume, effect values) Editable automation curves Point-based editing 🎼 5. VST / Plugin Support Loading external virtual instruments (VSTi) and effects (VST fx) Controlling and automating plugin parameters 🧰 6. Basic Effects and Synths EQ, compressor, reverb, delay, distortion, etc. At least 1–2 built-in synthesizers or sampler instruments Effect chaining 🕒 7. Timeline and Tempo Control Timeline view (grid + ruler) Tempo and time signature changes Metronome and beat counter 💾 8. Save / Export Saving project files Exporting audio (e.g., WAV, MP3, FLAC) Exporting stems (individual tracks) 🖱️ 9. User Interface (UI/UX) Simple, intuitive interface Drag & drop functionality
User prompt
A music creation and editing application (DAW – Digital Audio Workstation) should include several essential features, depending on whether it's targeted at beginners, intermediate users, or professionals. However, here are the core features that it must include: 1. Track Management 2. MIDI Editing 3. Audio Recording and Editing 4. Basic Effects and Synths EQ, compressor, reverb, delay, distortion, etc. At least 1–2 built-in synthesizers or sampler instruments 5. Timeline and Tempo Control Timeline view (grid + ruler) 6. Loop and sample library integration 7. User Interface (UI/UX) Simple, intuitive interface Drag & drop functionality 8. ST / Plugin Support Loading external virtual instruments (VSTi) and effects (VST fx) 9. Save / Export Saving project files in assets menu song or music list Exporting audio (e.g., WAV, MP3, FLAC).
User prompt
A music creation and editing application (DAW – Digital Audio Workstation) should include several essential features, depending on whether it's targeted at beginners, intermediate users, or professionals. However, here are the core features that it must include: 1. Track Management 2. MIDI Editing 3. Audio Recording and Editing 4. Basic Effects and Synths EQ, compressor, reverb, delay, distortion, etc. At least 1–2 built-in synthesizers or sampler instruments 5. Timeline and Tempo Control Timeline view (grid + ruler) 6. Loop and sample library integration 7. User Interface (UI/UX) Simple, intuitive interface Drag & drop functionality 8. ST / Plugin Support Loading external virtual instruments (VSTi) and effects (VST fx) 9. Save / Export Saving project files in assets menu song or music list Exporting audio (e.g., WAV, MP3, FLAC) ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'storage.currentProject = projectData;' Line Number: 3490 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
A music creation and editing application (DAW – Digital Audio Workstation) should include several essential features, depending on whether it's targeted at beginners, intermediate users, or professionals. However, here are the core features that it must include: 🎛️ 1. Track Management Support for multiple tracks (audio, MIDI) Per-track volume, pan, mute, and solo controls Track grouping (bus routing) 🎹 2. MIDI Editing Piano roll view Drawing, deleting, and moving MIDI notes Editing velocity and other parameters MIDI automation 🎧 3. Audio Recording and Editing Recording from microphone or other inputs Cutting, stretching, timing (time-stretching, quantization) Normalization, fade-in/fade-out Moving and looping audio clips 🎚️ 4. Automation Automating parameters (e.g., volume, effect values) Editable automation curves Point-based editing 🎼 5. VST / Plugin Support Loading external virtual instruments (VSTi) and effects (VST fx) Controlling and automating plugin parameters 🧰 6. Basic Effects and Synths EQ, compressor, reverb, delay, distortion, etc. At least 1–2 built-in synthesizers or sampler instruments Effect chaining 🕒 7. Timeline and Tempo Control Timeline view (grid + ruler) Tempo and time signature changes Metronome and beat counter 💾 8. Save / Export Saving project files Exporting audio (e.g., WAV, MP3, FLAC) Exporting stems (individual tracks) 🖱️ 9. User Interface (UI/UX) Simple, intuitive interface Drag & drop functionality Keyboard shortcut support 🌐 10. Additional Useful Features (not essential, but great to have): Loop and sample library integration Collaboration (cloud-based workflow) Mobile or web version Audio to MIDI conversion Built-in tuner, metronome, chord detector ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
Add to the game a fullscreen SONG MAKER app what show up if edit button pressed
User prompt
Move ok, left, right, up, down arrows buttons up by 12 units
User prompt
Move edit button down by 12 units
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Playback starts automatically // Balloon: Animated colorful balloons falling from the top var Balloon = Container.expand(function () { var self = Container.call(this); // Config var size = 60 + Math.random() * 80; // Random size var speedY = 0.8 + Math.random() * 1.2; // Vertical speed var speedX = (Math.random() - 0.5) * 2; // Horizontal drift var rotationSpeed = (Math.random() - 0.5) * 0.05; // Rotation speed // Visuals // Use a more realistic width/height ratio for balloons (e.g. 0.55) var balloonWidth = size * 0.55; var balloonHeight = size; var balloonGraphic = self.attachAsset('balloon', { anchorX: 0.5, anchorY: 0.5, width: balloonWidth, height: balloonHeight }); // Random color (rainbow) var hue = Math.random(); var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1); balloonGraphic.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2]; // HSV to RGB helper (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); 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)]; } // Animation self.update = function () { self.y += speedY; self.x += speedX; self.rotation += rotationSpeed; // Wrap around if off screen (below the dancing people) if (self.y > dancingPeople[0].y + 200) { // Wrap below the dancing people self.y = -size; self.x = Math.random() * 2048; // New random x position // Reset size and color for a new balloon size = 60 + Math.random() * 80; var balloonWidth = size * 0.55; var balloonHeight = size; balloonGraphic.width = balloonWidth; // Maintain realistic balloon width/height ratio balloonGraphic.height = balloonHeight; var hue = Math.random(); var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1); balloonGraphic.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2]; speedY = 0.8 + Math.random() * 1.2; speedX = (Math.random() - 0.5) * 2; rotationSpeed = (Math.random() - 0.5) * 0.05; } }; return self; }); // ConfettiEffect: Animated confetti falling from the top with multiple types var ConfettiEffect = Container.expand(function () { var self = Container.call(this); // Config var numParticles = 200; // Increased from 120 to 200 for much higher density var particles = []; var particleMinSize = 3.0; // Slightly larger minimum size var particleMaxSize = 12.0; // Increased maximum size var speedY = 5; // pixels per second var speedX = 2; // horizontal drift // Define confetti types with more rectangle emphasis var confettiTypes = [{ asset: 'centerCircle', name: 'circle' }, { asset: 'crossfaderTrack', name: 'rectangle' }, { asset: 'crossfaderTrack', name: 'rectangle' }, { asset: 'crossfaderTrack', name: 'rectangle' }, { asset: 'crossfaderTrack', name: 'rectangle' }, { asset: 'beatLight', name: 'star' }]; // Create confetti particles for (var i = 0; i < numParticles; i++) { var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize); // Choose random confetti type var confettiType = confettiTypes[Math.floor(Math.random() * confettiTypes.length)]; var particle; if (confettiType.name === 'rectangle') { // Create rectangular confetti with more variety var rectWidth = size * (1.2 + Math.random() * 0.8); // More width variation (1.2x to 2.0x) var rectHeight = size * (0.4 + Math.random() * 0.6); // More height variation (0.4x to 1.0x) particle = LK.getAsset(confettiType.asset, { anchorX: 0.5, anchorY: 0.5, width: rectWidth, height: rectHeight }); } else if (confettiType.name === 'star') { // Create star-shaped confetti particle = LK.getAsset(confettiType.asset, { anchorX: 0.5, anchorY: 0.5, width: size * 0.8, height: size * 0.8 }); } else { // Create circular confetti particle = LK.getAsset(confettiType.asset, { anchorX: 0.5, anchorY: 0.5, width: size, height: size }); } // Random color (rainbow) var hue = Math.random(); var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.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, type: confettiType.name, baseSize: size, rotationSpeed: (Math.random() - 0.5) * 0.3 // Random rotation speed }); } // 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 // Reset rotation and add tween effect for new particles p.obj.rotation = 0; tween(p.obj, { rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1) }, { duration: 2000 + Math.random() * 3000, easing: tween.linear }); } // Enhanced rotation based on type if (p.type === 'rectangle') { p.obj.rotation += p.rotationSpeed * 1.5; // Rectangles rotate faster } else if (p.type === 'star') { p.obj.rotation += p.rotationSpeed * 0.8; // Stars rotate slower } else { p.obj.rotation += p.rotationSpeed; // Circles normal rotation } // Type-specific animations if (p.type === 'rectangle') { // Rectangles flutter and tumble more dramatically var flutter = 0.8 + 0.4 * Math.sin(t * 5 + p.phase); var tumble = 0.9 + 0.2 * Math.cos(t * 3 + p.phase); p.obj.scaleX = flutter; p.obj.scaleY = tumble; // Add extra horizontal drift for rectangles p.obj.x += Math.sin(t * 2 + p.phase) * 0.8; } else if (p.type === 'star') { // Stars twinkle var twinkle = 0.9 + 0.2 * Math.sin(t * 6 + p.phase); p.obj.scaleX = twinkle; p.obj.scaleY = twinkle; // Add alpha twinkling p.obj.alpha = 0.8 + 0.2 * Math.sin(t * 5 + p.phase); } else { // Circles flicker size var baseSize = p.baseSize; var s = baseSize * (0.95 + 0.1 * 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; }; // Touch events self.down = function (x, y, obj) { self.dragging = true; self.setValue((x + track.width / 2) / 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 / 2) / track.width); } }; return self; }); // DancingPerson: Animated dancing people (women and men) var DancingPerson = Container.expand(function (forcedAssetId) { var self = Container.call(this); // Config var assetId; self.isUV = false; // Add a property to track if this is a UV person if (typeof forcedAssetId === 'string') { assetId = forcedAssetId; self.isUV = forcedAssetId === 'UVW' || forcedAssetId === 'UWM'; } else { // fallback to random if not provided var assets = ['dancingWoman', 'dancingMan']; 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; // Move highlight behind the platter by adding it first, then re-adding platter self.removeChild(platter); self.addChild(platter); var label = self.attachAsset('deckLabel', { anchorX: 0.5, anchorY: 0.5, y: 350, width: 90, height: 30 }); // 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: 490 }); beatLight.alpha = 0.2; // Methods self.setActive = function (active) { self.isActive = active; if (active) { highlight.alpha = 0.6; highlight.visible = true; // Ensure highlight is above platter in display order self.removeChild(highlight); self.addChild(highlight); // Flash effect for better visual feedback tween(highlight, { alpha: 0.3 }, { duration: 200, easing: tween.easeOut }); } else { highlight.alpha = 0; highlight.visible = false; } }; 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 using platter dimensions var dx = x - platter.width / 2; var dy = y - platter.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 using platter dimensions var dx = x - platter.width / 2; var dy = y - platter.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 () { // Determine if this deck should be spinning based on start button state var shouldSpin = false; if (self.track === 'A' && typeof leftStartButtonOn !== 'undefined' && leftStartButtonOn) { shouldSpin = true; } else if (self.track === 'B' && typeof rightStartButtonOn !== 'undefined' && rightStartButtonOn) { shouldSpin = true; } // If not scratching, platter rotates at normal speed only if should spin if (!self.isScratching && self.playing && shouldSpin) { 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; }); // EqualizerBars: Animated frequency visualization bars for screens var EqualizerBars = Container.expand(function () { var self = Container.call(this); // Config var numBars = 12; var barWidth = 25 * 1.2; var barSpacing = 8; var maxBarHeight = 100; var minBarHeight = 10; var bars = []; // Create equalizer bars for (var i = 0; i < numBars; i++) { var bar = LK.getAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 1.0, width: barWidth, height: minBarHeight }); // Color based on frequency range (bass=red, mid=yellow, treble=cyan) if (i < 4) { bar.tint = 0xff0000; // Bass - red } else if (i < 8) { bar.tint = 0xffff00; // Mid - yellow } else { bar.tint = 0x00ffff; // Treble - cyan } bar.x = (i - (numBars - 1) / 2) * (barWidth + barSpacing); bar.y = 0; self.addChild(bar); bars.push({ obj: bar, phase: Math.random() * Math.PI * 2, baseHeight: minBarHeight }); } // Animation self.update = function () { var t = Date.now() * 0.001; for (var i = 0; i < bars.length; i++) { var b = bars[i]; // Simulate frequency response with different speeds for different frequency ranges var speed = 1.0; if (i < 4) speed = 0.8; // Bass moves slower else if (i < 8) speed = 1.2; // Mid moves medium else speed = 1.8; // Treble moves faster // Create realistic frequency bar movement var height = minBarHeight + (maxBarHeight - minBarHeight) * (0.3 + 0.7 * Math.abs(Math.sin(t * speed * 2 + b.phase + i * 0.3))); b.obj.height = height; // Add beat sync flash effect if (beatTimer && beatTimer < 100) { b.obj.alpha = 0.7 + 0.3 * Math.sin(t * 10 + i); } else { b.obj.alpha = 0.9; } } }; 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 = 8; // Number of simultaneous rockets/explosions - increased from 4 to 8 var rocketInterval = 800; // ms between new rockets - reduced from 1500 to 800 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 = 20 + Math.random() * 10; 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 = 20; 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 baseSize = (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.48; var size = baseSize; // Modify size based on fire effect mode if (typeof fireEffectMode !== 'undefined' && fireEffectMode === 2) { // Big flames mode - make particles larger size = baseSize * 2.5; } 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 based on fire effect mode var targetParticleCount = numParticles; if (typeof fireEffectMode !== 'undefined') { if (fireEffectMode === 0) { // Off mode - no flames targetParticleCount = 0; } else if (fireEffectMode === 2) { // Big flames mode - more particles targetParticleCount = numParticles * 2; } } if (particles.length < targetParticleCount) { 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 = 20; 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 () { // Only animate if fog animation is active if (typeof fogAnimationActive !== 'undefined' && !fogAnimationActive) { // Hide all particles when fog animation is not active for (var i = 0; i < particles.length; i++) { var p = particles[i]; p.obj.alpha = 0; } return; } 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; }); // KeyboardApp: Simple keyboard app for the tablet var KeyboardApp = Container.expand(function () { var self = Container.call(this); // Config var keyboardWidth = 742; var keyboardHeight = 250; var keyWidth = 50; var keyHeight = 50; var keySpacing = 8; var keys = []; var keyLabels = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']]; var shiftLabels = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'], ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'], ['z', 'x', 'c', 'v', 'b', 'n', 'm']]; var symbolLabels = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'], [',', '-', '.', '/', '+', ':', ';', '<', '>'], ['_', '=', '|', '\\', '{', '}', '[', ']'], ['é', 'ú', 'ü', 'ű', 'í', 'ó', 'ö', 'ő', 'á']]; self.isSymbolsMode = false; // Track if symbols mode is active self.isShiftMode = false; // Track if shift mode is active // Create keyboard background var keyboardBg = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: keyboardWidth, height: keyboardHeight }); keyboardBg.tint = 0x333333; // Create keys var startX = (keyboardWidth - (keyLabels[0].length * (keyWidth + keySpacing) - keySpacing)) / 2; var startY = 20; for (var row = 0; row < keyLabels.length; row++) { var rowLabels = keyLabels[row]; var rowStartX = (keyboardWidth - (rowLabels.length * (keyWidth + keySpacing) - keySpacing)) / 2; for (var col = 0; col < rowLabels.length; col++) { var keyBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: keyWidth, height: keyHeight }); keyBg.tint = 0x555555; keyBg.x = rowStartX + col * (keyWidth + keySpacing) + keyWidth / 2; keyBg.y = startY + row * (keyHeight + keySpacing) + keyHeight / 2; var keyText = new Text2(rowLabels[col], { size: 40, fill: 0xFFFFFF }); keyText.anchor.set(0.5, 0.5); keyBg.addChild(keyText); // Add basic press feedback keyBg.down = function (x, y, obj) { self.handleKeyPress(keyText.text); // Pass the key label to a new handler tween(this, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the key var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the key flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50, onFinish: function () { tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 150 }); }.bind(this) }); LK.getSound('fx').play(); // Play a generic sound }; keyBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(keyBg); } } // Add space bar var spaceBarWidth = keyWidth * 6 + keySpacing * 5; var spaceBarBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: spaceBarWidth * 1.1, height: keyHeight }); var spaceGraphic = self.attachAsset('Space', { anchorX: 0.5, anchorY: 0.5 }); spaceGraphic.width = spaceBarWidth * 1.1; spaceGraphic.height = keyHeight; spaceBarBg.addChild(spaceGraphic); spaceBarBg.tint = 0x555555; spaceBarBg.x = keyboardWidth / 2; spaceBarBg.y = startY + keyLabels.length * (keyHeight + keySpacing) + keyHeight / 2; var spaceText = new Text2('SPACE', { size: 40, fill: 0xFFFFFF }); spaceText.anchor.set(0.5, 0.5); spaceBarBg.addChild(spaceText); // Add basic press feedback for space bar spaceBarBg.down = function (x, y, obj) { tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the spacebar var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the spacebar flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; spaceBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(spaceBarBg); // Add symbols button to the left of the space bar var symbolsBarWidth = keyWidth * 2 + keySpacing; var symbolsBarBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: symbolsBarWidth, height: keyHeight }); symbolsBarBg.tint = 0x555555; symbolsBarBg.x = spaceBarBg.x - spaceBarBg.width / 2 - symbolsBarBg.width / 2 - keySpacing - 22; symbolsBarBg.y = spaceBarBg.y; var symbolsText = new Text2('SYMBOLS', { size: 30, fill: 0xFFFFFF }); symbolsText.anchor.set(0.5, 0.5); symbolsBarBg.addChild(symbolsText); // Add basic press feedback for symbols button symbolsBarBg.down = function (x, y, obj) { // Toggle symbols mode self.isSymbolsMode = !self.isSymbolsMode; // If switching to symbols mode, turn off shift mode if (self.isSymbolsMode) { self.isShiftMode = false; // Find and update the shift button text for (var i = 0; i < keys.length; i++) { if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2 && keys[i].children[0].text === 'SHIFT') { keys[i].children[0].setText('shift'); break; } } } self.updateKeyLabels(); // Update the labels // Update symbols button text based on mode symbolsText.setText(self.isSymbolsMode ? 'ABC' : 'SYMBOLS'); // Update symbols button text based on mode tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the symbols button var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the symbols button flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; symbolsBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(symbolsBarBg); // Add Shift button above the symbols button var shiftBarWidth = keyWidth * 2 + keySpacing; var shiftBarBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: shiftBarWidth, height: keyHeight }); shiftBarBg.tint = 0x555555; shiftBarBg.x = symbolsBarBg.x; shiftBarBg.y = symbolsBarBg.y - keyHeight - keySpacing; var shiftText = new Text2('SHIFT', { size: 30, fill: 0xFFFFFF }); shiftText.anchor.set(0.5, 0.5); shiftBarBg.addChild(shiftText); // Add basic press feedback for shift button shiftBarBg.down = function (x, y, obj) { // Toggle shift mode self.isShiftMode = !self.isShiftMode; self.updateKeyLabels(); // Update the labels // Update shift button text based on mode shiftText.setText(self.isShiftMode ? 'SHIFT' : 'shift'); // Maybe change visual later tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the shift button var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the shift button flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; shiftBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(shiftBarBg); // Add X button above the enter button var xBarWidth = keyWidth * 1.5; var xBarBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: xBarWidth, height: keyHeight }); xBarBg.tint = 0x555555; xBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + xBarBg.width / 2 + keySpacing + 15 + 20; xBarBg.y = spaceBarBg.y - keyHeight - keySpacing; var xText = new Text2('DELETE', { size: 28, fill: 0xFFFFFF }); xText.anchor.set(0.5, 0.5); xBarBg.addChild(xText); // Add basic press feedback for DELETE button xBarBg.down = function (x, y, obj) { self.handleKeyPress('DELETE'); // Handle DELETE/BACKSPACE key press tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the X button var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the X button flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; xBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(xBarBg); // Add enter button to the right of the space bar var enterBarWidth = keyWidth * 2 + keySpacing; var enterBarBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: enterBarWidth, height: keyHeight }); enterBarBg.tint = 0x555555; enterBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + enterBarBg.width / 2 + keySpacing + 15; enterBarBg.y = spaceBarBg.y; var enterText = new Text2('ENTER', { size: 40, fill: 0xFFFFFF }); enterText.anchor.set(0.5, 0.5); enterBarBg.addChild(enterText); // Add basic press feedback for enter button enterBarBg.down = function (x, y, obj) { tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the enter button var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the enter button flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; enterBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(enterBarBg); // Method to update key labels based on mode self.updateKeyLabels = function () { var currentLabels; if (self.isSymbolsMode) { currentLabels = symbolLabels; } else if (self.isShiftMode) { currentLabels = shiftLabels; } else { currentLabels = keyLabels; } var labelIndex = 0; for (var row = 0; row < currentLabels.length; row++) { var rowLabels = currentLabels[row]; for (var col = 0; col < rowLabels.length; col++) { if (labelIndex < keys.length) { var keyBg = keys[labelIndex]; // Find the Text2 child object var keyText = null; for (var i = 0; i < keyBg.children.length; i++) { if (keyBg.children[i] instanceof Text2) { keyText = keyBg.children[i]; break; } } if (keyText && keyText.setText) { keyText.setText(rowLabels[col]); } } labelIndex++; } } }; // Method to handle key press self.handleKeyPress = function (key) { // Initialize searchText if it hasn't been already if (typeof searchText === 'undefined') { searchText = new Text2('', { size: 28, fill: 0x888888 }); searchText.anchor.set(0.5, 0.5); // Position placeholder, actual position will be set by RekordboxApp searchText.x = 0; searchText.y = 0; } console.log("Key pressed:", key); // Add logic here to handle different key presses, e.g., appending to a text input }; symbolsBarBg.down = function (x, y, obj) { // Toggle symbols mode self.isSymbolsMode = !self.isSymbolsMode; self.updateKeyLabels(); // Update the labels tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the symbols button var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the symbols button flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; symbolsBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(symbolsBarBg); // Add enter button to the right of the space bar var enterBarWidth = keyWidth * 2 + keySpacing; var enterBarBg = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: enterBarWidth, height: keyHeight }); enterBarBg.tint = 0x555555; enterBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + enterBarBg.width / 2 + keySpacing + 15; enterBarBg.y = spaceBarBg.y; var enterText = new Text2('ENTER', { size: 40, fill: 0xFFFFFF }); enterText.anchor.set(0.5, 0.5); enterBarBg.addChild(enterText); // Add basic press feedback for enter button enterBarBg.down = function (x, y, obj) { self.handleKeyPress('ENTER'); // Handle enter key press // Reset shift and symbols mode after enter self.isShiftMode = false; self.isSymbolsMode = false; self.updateKeyLabels(); // Find and update the symbols button text for (var i = 0; i < keys.length; i++) { if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2) { if (keys[i].children[0].text === 'ABC' || keys[i].children[0].text === 'SYMBOLS') { // Find the symbols button and update its text keys[i].children[0].setText('SYMBOLS'); break; } } } // Find and update the shift button text for (var i = 0; i < keys.length; i++) { if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2 && keys[i].children[0].text === 'SHIFT') { keys[i].children[0].setText('shift'); break; } } tween(this, { scaleX: 1.05, scaleY: 1.05 }, { duration: 80 }); // Add orange flashing effect using tint and alpha // Create a temporary flashing graphic above the enter button var flashGraphic = LK.getAsset('flashRectangle', { anchorX: 0.5, anchorY: 0.5, width: this.width, height: this.height }); flashGraphic.alpha = 1.0; // Position the flash graphic centered on the enter button flashGraphic.x = this.x; flashGraphic.y = this.y; self.addChild(flashGraphic); // Fade out and remove the flash graphic tween(flashGraphic, { alpha: 0.0 }, { duration: 200, onFinish: function onFinish() { flashGraphic.destroy(); } }); tween(this, { tint: 0xff8000, alpha: 1.0 }, { duration: 50 }); LK.getSound('fx').play(); // Play a generic sound }; enterBarBg.up = function (x, y, obj) { // Return to original tint and alpha on release tween(this, { tint: 0x555555, alpha: 0.8 // Slightly faded normal state }, { duration: 100 }); tween(this, { scaleX: 1.0, scaleY: 1.0 }, { duration: 80 }); }; keys.push(enterBarBg); 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('laserBeam', { 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; // Only update head colors every few frames to improve performance if (LK.ticks % 4 === 0) { // 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); // Only tween if color actually changed if (head.tint !== tint) { head.tint = tint; } } } // 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; // Check laser show mode (controlled by second effect button) if (typeof laserShowMode !== 'undefined' && laserShowMode === 1) { // Slow laser show mode - beams visible beam.visible = true; var sweep = Math.sin(t * 1.2 + h + b * 0.5) * 0.3 + Math.sin(t * 0.8 + b) * 0.15; var beatPulse = Math.sin(t * 2.8 + h + b) * 0.12; beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse; // Slow beam length flicker var flicker = 1 + 0.18 * Math.sin(t * 3.5 + h * 2 + b * 1.2 + Math.sin(t * 2.0 + b)); beam.height = beamLength * flicker; // Color logic: if UV mode is on, show different colors; if only laser mode, all beams same color if (typeof uvEffectActive !== 'undefined' && uvEffectActive) { // UV mode active - different colors per beam var colorIdx = (h * beamsPerHead + b + Math.floor(t * 2.5 + h + b)) % beamColors.length; beam.tint = beamColors[colorIdx]; } else { // Only laser mode - all beams same color but cycle through gradient var sameColorIdx = Math.floor(t * 2.5) % beamColors.length; beam.tint = beamColors[sameColorIdx]; } // Slow alpha strobe effect beam.alpha = beamAlpha + 0.2 * Math.abs(Math.sin(t * 5 + h + b)); } else if (typeof laserShowMode !== 'undefined' && laserShowMode === 2) { // Fast laser show mode - beams visible beam.visible = true; var sweep = Math.sin(t * 2.4 + h + b * 0.9) * 0.5 + Math.sin(t * 1.8 + b) * 0.25; var beatPulse = Math.sin(t * 6.0 + h + b) * 0.2; beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse; // Fast beam length flicker var flicker = 1 + 0.35 * Math.sin(t * 7 + h * 2 + b * 1.8 + Math.sin(t * 3.5 + b)); beam.height = beamLength * flicker; // Color logic: if UV mode is on, show different colors; if only laser mode, all beams same color if (typeof uvEffectActive !== 'undefined' && uvEffectActive) { // UV mode active - different colors per beam var colorIdx = (h * beamsPerHead + b + Math.floor(t * 6 + h + b)) % beamColors.length; beam.tint = beamColors[colorIdx]; } else { // Only laser mode - all beams same color but cycle through gradient var sameColorIdx = Math.floor(t * 6) % beamColors.length; beam.tint = beamColors[sameColorIdx]; } // Fast alpha strobe effect beam.alpha = beamAlpha + 0.4 * Math.abs(Math.sin(t * 12 + h + b)); } else { // Off mode - hide beams completely beam.visible = false; } } }; return self; }); // MoneyParticle: Animated money bills falling from the top var MoneyParticle = Container.expand(function () { var self = Container.call(this); // Config var size = (40 + Math.random() * 30) / 1.2; // Random size between 40-70 var speedY = 1.2 + Math.random() * 0.8; // Vertical speed var speedX = (Math.random() - 0.5) * 1.5; // Horizontal drift var rotationSpeed = (Math.random() - 0.5) * 0.03; // Rotation speed // Create money bill graphic using money asset var moneyGraphic = self.attachAsset('money', { anchorX: 0.5, anchorY: 0.5, width: size * 1.8, // Money bills are wider than tall height: size }); // Keep natural money asset color (remove tint) // Animation self.update = function () { self.y += speedY; self.x += speedX; self.rotation += rotationSpeed; // Wrap around if off screen (below the dancing people) if (self.y > dancingPeople[0].y + 200) { // Reset to top with new random properties self.y = -size; self.x = Math.random() * 2048; // Reset size and properties for variety size = 40 + Math.random() * 30; moneyGraphic.width = size * 1.8; moneyGraphic.height = size; speedY = 1.2 + Math.random() * 0.8; speedX = (Math.random() - 0.5) * 1.5; rotationSpeed = (Math.random() - 0.5) * 0.03; // Animate scale for sparkle effect instead of tint tween(moneyGraphic, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(moneyGraphic, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeIn }); } }); } }; return self; }); // RekordboxApp: Full-featured rekordbox application interface for the tablet var RekordboxApp = Container.expand(function () { var self = Container.call(this); // Config self.currentScreen = 'browser'; // 'browser', 'playlist', 'settings', 'performance' self.selectedTrackIndex = 0; self.selectedCategoryIndex = 0; self.currentCategory = 'all'; // Track database self.tracks = [{ name: 'Summer Vibes', artist: 'DJ Alex', bpm: 128, key: 'Am', genre: 'House' }, { name: 'Night Drive', artist: 'Luna Beat', bpm: 132, key: 'Fm', genre: 'Techno' }, { name: 'Electric Dreams', artist: 'Synth Master', bpm: 140, key: 'Gm', genre: 'Electronic' }, { name: 'Deep Ocean', artist: 'Wave Runner', bpm: 124, key: 'Dm', genre: 'Deep House' }, { name: 'City Lights', artist: 'Urban Sound', bpm: 126, key: 'Em', genre: 'Progressive' }, { name: 'Midnight Express', artist: 'Beat Factory', bpm: 130, key: 'Cm', genre: 'Techno' }, { name: 'Tropical Storm', artist: 'Island Beats', bpm: 118, key: 'Bm', genre: 'Tropical' }, { name: 'Neon Pulse', artist: 'Cyber DJ', bpm: 135, key: 'F#m', genre: 'Electro' }, { name: 'Sunset Boulevard', artist: 'Coast Drive', bpm: 122, key: 'Am', genre: 'Chill' }, { name: 'Bass Revolution', artist: 'Low End Theory', bpm: 128, key: 'Dm', genre: 'Bass' }, { name: 'Street Anthem', artist: 'MC Flow', bpm: 90, key: 'Em', genre: 'Hip-Hop' }, { name: 'Island Rhythm', artist: 'Rasta King', bpm: 75, key: 'Am', genre: 'Reggae' }, { name: 'Smooth Jazz Night', artist: 'Jazz Collective', bpm: 110, key: 'Bbm', genre: 'Jazz' }, { name: 'Symphony No. 9', artist: 'Classical Ensemble', bpm: 120, key: 'Dm', genre: 'Classical' }, { name: 'Rock The House', artist: 'Thunder Strike', bpm: 140, key: 'Em', genre: 'EDM' }, { name: 'Pop Star', artist: 'Chart Topper', bpm: 125, key: 'Am', genre: 'Pop' }, { name: 'Floating Dreams', artist: 'Ambient Explorer', bpm: 80, key: 'Fm', genre: 'Ambient' }, { name: 'Drop The Beat', artist: 'Dubstep Master', bpm: 140, key: 'Gm', genre: 'Dubstep' }, { name: 'Trap Nation', artist: 'Trap Lord', bpm: 75, key: 'Cm', genre: 'Trap' }, { name: 'Liquid Motion', artist: 'DNB Producer', bpm: 175, key: 'Am', genre: 'Drum & Bass' }]; self.categories = ['CATEGORY', 'All Tracks', 'House', 'Techno', 'EDM', 'Electronic', 'Disco', 'Latin', 'POP', 'Hip-Hop']; self.visibleTrackStart = 0; self.maxVisibleTracks = 8; // Create main container for app content var appContainer = self.addChild(new Container()); // Header bar var headerBar = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 742, height: 50 }); headerBar.tint = 0x1a1a1a; appContainer.addChild(headerBar); // TRACKLIST logo/title var titleText = new Text2('TRACKLIST', { size: 56, fill: 0xFF6600 }); titleText.anchor.set(0, 0.5); titleText.x = 10; titleText.y = 25; appContainer.addChild(titleText); // Screen indicator var screenIndicator = new Text2('BROWSER', { size: 40, fill: 0xFFFFFF }); screenIndicator.anchor.set(1, 0.5); screenIndicator.x = 722; screenIndicator.y = 25; appContainer.addChild(screenIndicator); // Sidebar for categories var sidebar = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 180, height: 400 }); sidebar.tint = 0x2a2a2a; sidebar.y = 60; appContainer.addChild(sidebar); // Main content area var contentArea = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 542, height: 400 }); contentArea.tint = 0x1a1a1a; contentArea.x = 190; contentArea.y = 60; appContainer.addChild(contentArea); // Category items in sidebar self.categoryItems = []; for (var i = 0; i < self.categories.length; i++) { var categoryBg = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 170, height: 35 }); categoryBg.tint = i === self.selectedCategoryIndex ? 0x444444 : 0x333333; categoryBg.x = 5; categoryBg.y = 70 + i * 40; appContainer.addChild(categoryBg); var categoryText = new Text2(self.categories[i], { size: 28, fill: i === self.selectedCategoryIndex ? "#ff6600" : "#cccccc" }); categoryText.anchor.set(0, 0.5); categoryText.x = 15; categoryText.y = 87 + i * 40 - 2 - 5; appContainer.addChild(categoryText); self.categoryItems.push({ bg: categoryBg, text: categoryText }); } // Track list headers var headerTrack = new Text2('TRACK', { size: 28, fill: 0x888888 }); headerTrack.anchor.set(0, 0); headerTrack.x = 200; headerTrack.y = 70; appContainer.addChild(headerTrack); var headerArtist = new Text2('ARTIST', { size: 28, fill: 0x888888 }); headerArtist.anchor.set(0, 0); headerArtist.x = 413; // Increased x-position headerArtist.y = 70; appContainer.addChild(headerArtist); var headerBPM = new Text2('BPM', { size: 28, fill: 0x888888 }); headerBPM.anchor.set(0, 0); headerBPM.x = 610; // Increased x-position and moved left by 10 units headerBPM.y = 70; appContainer.addChild(headerBPM); var headerKey = new Text2('KEY', { size: 28, fill: 0x888888 }); headerKey.anchor.set(0, 0); headerKey.x = 620 + 40 + 17; // Increased x-position by adding 17 units to the previous elements x + width headerKey.y = 70; appContainer.addChild(headerKey); // Track list items self.trackItems = []; for (var i = 0; i < self.maxVisibleTracks; i++) { var trackBg = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 532, height: 35 }); trackBg.tint = 0x252525; trackBg.x = 195; trackBg.y = 95 + i * 40; var trackText = new Text2('', { size: 28, fill: 0xFFFFFF }); trackText.anchor.set(0, 0.5); trackText.x = 200; trackText.y = 112 + i * 40 + 17 + 30; var artistText = new Text2('', { size: 28, //{ O} fill: 0xCCCCCC //{ P} }); //{ Q} artistText.anchor.set(0, 0.5); artistText.x = 380 + 25 + 10; // Increased x-position//{ O} artistText.y = 112 + i * 40 + 17 + 30; var bpmText = new Text2('', { size: 28, //{ R} fill: 0xAAAAAA //{ S} }); //{ T} bpmText.anchor.set(0, 0.5); bpmText.x = 620; // Increased x-position bpmText.y = 112 + i * 40 + 17 + 30; var keyText = new Text2('', { size: 28, //{ U} fill: 0xAAAAAA //{ V} }); //{ W} keyText.anchor.set(0, 0.5); keyText.x = 620 + 40 + 17; // Increased x-position by adding 17 units to the previous elements x + width keyText.y = 112 + i * 40 + 17 + 30; self.trackItems.push({ bg: trackBg, track: trackText, artist: artistText, bpm: bpmText, key: keyText }); //{ X} } // Add all backgrounds first to ensure they are at the back for (var i = 0; i < self.maxVisibleTracks; i++) { appContainer.addChild(self.trackItems[i].bg); } // Then add all text elements so they are rendered on top for (var i = 0; i < self.maxVisibleTracks; i++) { var item = self.trackItems[i]; appContainer.addChild(item.track); appContainer.addChild(item.artist); appContainer.addChild(item.bpm); appContainer.addChild(item.key); } // Bottom status bar var statusBar = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 742, height: 30 }); statusBar.tint = 0x333333; statusBar.y = 470; appContainer.addChild(statusBar); var statusText = new Text2('Use arrow keys to navigate', { size: 24, fill: 0xCCCCCC }); statusText.anchor.set(0, 0.5); statusText.x = 235; statusText.y = 485; appContainer.addChild(statusText); // Search bar background var searchBarBg = LK.getAsset('Searchbarbg', { anchorX: 0.5, anchorY: 0.5, width: 512, height: 40 }); searchBarBg.tint = 0xffffff; searchBarBg.x = 457 + 10; // Adjusted for center anchor and moved right by 12 units searchBarBg.y = 125; // Adjusted for center anchor self.addChild(searchBarBg); // Add directly to self instead of appContainer for independent positioning // Search bar text var searchText = new Text2('Search tracks...', { size: 28, fill: 0x888888 }); searchText.anchor.set(0.5, 0.5); searchText.x = 457; // Centered with search bar background and moved right by 12 units searchText.y = 125; // Centered with search bar background self.addChild(searchText); // Add directly to self instead of appContainer for independent positioning // Track count info self.trackCountText = new Text2('', { size: 24, fill: 0x888888 }); self.trackCountText.anchor.set(1, 0.5); self.trackCountText.x = 732; self.trackCountText.y = 485; appContainer.addChild(self.trackCountText); // Methods self.updateDisplay = function () { // Update categories for (var i = 0; i < self.categoryItems.length; i++) { var isSelected = i === self.selectedCategoryIndex; self.categoryItems[i].bg.tint = isSelected ? 0x444444 : 0x333333; self.categoryItems[i].text.fill = isSelected ? "#ff6600" : "#cccccc"; } // Filter tracks based on category var filteredTracks = self.tracks; if (self.selectedCategoryIndex > 1) { // Filter by specific genre, skipping 'CATEGORY' and 'All Tracks' var genre = self.categories[self.selectedCategoryIndex]; filteredTracks = self.tracks.filter(function (track) { return track.genre === genre; }); } else if (self.selectedCategoryIndex === 1) { // 'All Tracks' category - show all tracks filteredTracks = self.tracks; } else { // 'CATEGORY' category - show no tracks filteredTracks = []; } // Update track list for (var i = 0; i < self.maxVisibleTracks; i++) { var trackIndex = self.visibleTrackStart + i; var item = self.trackItems[i]; if (trackIndex < filteredTracks.length) { var track = filteredTracks[trackIndex]; var isSelected = trackIndex === self.selectedTrackIndex; item.bg.tint = isSelected ? 0x0066cc : i % 2 === 0 ? 0x252525 : 0x2a2a2a; item.track.setText(track.name.length > 20 ? track.name.substring(0, 17) + '...' : track.name); item.artist.setText(track.artist.length > 15 ? track.artist.substring(0, 12) + '...' : track.artist); item.bpm.setText(track.bpm.toString()); item.key.setText(track.key); item.track.fill = isSelected ? "#ffffff" : "#cccccc"; item.artist.fill = isSelected ? "#ffffff" : "#aaaaaa"; item.bpm.fill = isSelected ? "#ffffff" : "#999999"; item.key.fill = isSelected ? "#ffffff" : "#999999"; item.bg.visible = true; item.track.visible = true; item.artist.visible = true; item.bpm.visible = true; item.key.visible = true; } else { item.bg.visible = false; item.track.visible = false; item.artist.visible = false; item.bpm.visible = false; item.key.visible = false; } } // Update track count self.trackCountText.setText(filteredTracks.length + ' tracks'); // Update screen indicator screenIndicator.setText(self.currentScreen.toUpperCase()); }; self.navigateUp = function () { if (self.selectedTrackIndex > 0) { self.selectedTrackIndex--; if (self.selectedTrackIndex < self.visibleTrackStart) { self.visibleTrackStart = self.selectedTrackIndex; } self.updateDisplay(); } }; self.navigateDown = function () { var maxTracks = self.tracks.length - 1; if (self.selectedTrackIndex < maxTracks) { self.selectedTrackIndex++; if (self.selectedTrackIndex >= self.visibleTrackStart + self.maxVisibleTracks) { self.visibleTrackStart = self.selectedTrackIndex - self.maxVisibleTracks + 1; } self.updateDisplay(); } }; self.navigateLeft = function () { if (self.selectedCategoryIndex > 0) { self.selectedCategoryIndex--; self.selectedTrackIndex = 0; self.visibleTrackStart = 0; self.updateDisplay(); } }; self.navigateRight = function () { if (self.selectedCategoryIndex < self.categories.length - 1) { self.selectedCategoryIndex++; self.selectedTrackIndex = 0; self.visibleTrackStart = 0; self.updateDisplay(); } }; self.selectTrack = function () { var filteredTracks = self.tracks; if (self.selectedCategoryIndex > 1) { // Filter by specific genre, skipping 'CATEGORY' and 'All Tracks' var genre = self.categories[self.selectedCategoryIndex]; filteredTracks = self.tracks.filter(function (track) { return track.genre === genre; }); } else if (self.selectedCategoryIndex === 1) { // 'All Tracks' category - show all tracks filteredTracks = self.tracks; } else { // 'CATEGORY' category - show no tracks filteredTracks = []; } if (self.selectedTrackIndex < filteredTracks.length) { var selectedTrack = filteredTracks[self.selectedTrackIndex]; // Flash effect to show selection tween(self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg, { tint: 0x00ff00 }, { duration: 150, onFinish: function onFinish() { tween(self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg, { tint: 0x0066cc }, { duration: 150 }); } }); // Play a selection sound LK.getSound('fx').play(); // Add score for track selection if (typeof score !== 'undefined') { score += 25; combo += 1; } } }; // Make search bar draggable searchBarBg.down = function (x, y, obj) { self.searchBarDragging = true; self.searchBarOffset = { x: x - searchBarBg.x, y: y - searchBarBg.y }; }; searchBarBg.up = function (x, y, obj) { self.searchBarDragging = false; self.searchBarOffset = null; }; searchBarBg.move = function (x, y, obj) { if (self.searchBarDragging && self.searchBarOffset) { searchBarBg.x = x - self.searchBarOffset.x; searchBarBg.y = y - self.searchBarOffset.y; // Keep search text aligned with background searchText.x = searchBarBg.x; searchText.y = searchBarBg.y; } }; // Initialize display self.updateDisplay(); 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 = 60; // Increased number of particles for denser smoke var particles = []; var particleMinSize = 48; var particleMaxSize = 96; var speedY = -3.5; // pixels per frame (upwards) var speedX = 0.7; // horizontal drift var particleLifetime = 2500; // ms // Increased particle lifetime // 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_color = v * (1 - (1 - f) * s); 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)]; } 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 depends on UV effect state if (typeof uvEffectActive !== 'undefined' && uvEffectActive) { // Colorful UV smoke var hue = Math.random(); var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1); puff.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2]; } else { // Normal 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(); // Only emit smoke if smokeEffectActive is true if (typeof smokeEffectActive !== 'undefined' && smokeEffectActive) { // 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 ****/ // --- Reflectors (behind discoball) --- // Add a vertical crossfader track 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 // --- DJDeck Asset (center of map) --- var djDeck = LK.getAsset('desk', { 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 // --- Rim Assets (both sides of djdeck) --- var rimLeft = LK.getAsset('rim', { anchorX: 0.5, anchorY: 0.5 }); rimLeft.x = djDeck.x - djDeck.width / 2 + rimLeft.width / 2 + 49; rimLeft.y = djDeck.y; var rimRight = LK.getAsset('rim', { anchorX: 0.5, anchorY: 0.5 }); rimRight.x = djDeck.x + djDeck.width / 2 - rimRight.width / 2 - 150 + 108; rimRight.y = djDeck.y; // Add rim assets after djdeck to ensure they appear above in display order game.addChild(rimLeft); game.addChild(rimRight); // Move rim assets above djdeck in display order var djdeckIndex = game.children.indexOf(djDeck); if (djdeckIndex !== -1) { game.setChildIndex(rimLeft, djdeckIndex + 1); game.setChildIndex(rimRight, djdeckIndex + 2); } // Remove and re-add rim assets to ensure they're above djdeckAsset in display order if (game.children.indexOf(rimLeft) !== -1) { game.removeChild(rimLeft); } if (game.children.indexOf(rimRight) !== -1) { game.removeChild(rimRight); } game.addChild(rimLeft); game.addChild(rimRight); // --- 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 - 25 * (numPeople - 1); // Area width for dancing people, reduced to move people closer var startX = 2048 / 2 - peopleAreaWidth / 2 - 77; var personSpacing = peopleAreaWidth / (numPeople - 1); for (var i = 0; i < numPeople; i++) { // Alternate: even index = woman, odd index = man var genderAsset = i % 2 === 0 ? 'dancingWoman' : 'dancingMan'; var person = new DancingPerson(genderAsset); 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 // --- Additional Fireworks Effects for more density in upper half --- var fireworksEffect2 = new FireworksEffect(); game.addChild(fireworksEffect2); fireworksEffect2.y = 0; // Position at the top of the screen fireworksEffect2.x = 500; // Offset position for varied launch points var fireworksEffect3 = new FireworksEffect(); game.addChild(fireworksEffect3); fireworksEffect3.y = 0; // Position at the top of the screen fireworksEffect3.x = -300; // Different offset position var fireworksEffect4 = new FireworksEffect(); game.addChild(fireworksEffect4); fireworksEffect4.y = 0; // Position at the top of the screen fireworksEffect4.x = 800; // Another offset position // --- Balloons (falling from top) --- var balloons = []; var numBalloons = 50; // Increased number of balloons for (var i = 0; i < numBalloons; i++) { var balloon = new Balloon(); // Position balloons initially above the screen balloon.y = -300 - Math.random() * 1000; // Start higher up for more variety balloon.x = Math.random() * 2048; game.addChild(balloon); balloons.push(balloon); } // --- Money Particles (falling from top) --- var moneyParticles = []; var numMoneyParticles = 25; // Number of money bills falling for (var i = 0; i < numMoneyParticles; i++) { var money = new MoneyParticle(); // Position money initially above the screen with staggered timing money.y = -100 - Math.random() * 800; money.x = Math.random() * 2048; game.addChild(money); moneyParticles.push(money); // Add initial sparkle effect with tween tween(money, { alpha: 0.7 }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function () { tween(this, { alpha: 1.0 }, { duration: 1000 + Math.random() * 1000, easing: tween.easeInOut }); }.bind(money) }); } // --- 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('desk', { 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. // --- Second Line Dancing People (append to existing array) --- // Don't recreate the array, just append to existing dancingPeople array 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 - 25 * (numPeople - 1); // Area width for dancing people, reduced to move people closer var startX = 2048 / 2 - peopleAreaWidth / 2; var personSpacing = peopleAreaWidth / (numPeople - 1); for (var i = 0; i < numPeople; i++) { var person = new DancingPerson(); // Use default random asset initially 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) --- // Add global smokeEffectActive variable to control smoke emission var smokeEffectActive = false; var fogAnimationActive = false; var fogTimeout = null; // --- Add 6 smoke diffusers spread out to the left of the people line, spaced 300 units apart, all moved right by 32 units --- var smokeDiffusers = []; for (var i = 0; i <= 5; i++) { var sd = new SmokeDiffuser(); sd.x = startX - 80 + 32 + i * 300; sd.y = peopleAreaY + 18 - 50; // Align base with people feet, moved up by 50 units game.addChild(sd); smokeDiffusers.push(sd); } // --- Flamethrower --- var flamethrower = new Flamethrower(); // Position the flamethrower to the right of the dancing people line flamethrower.x = startX + peopleAreaWidth + 100 + 77 + 7; // Move flamethrower right by 84 units (77+7) // Move the flamethrower down so the flame aligns with the top of the base, then up by 100 units flamethrower.y = peopleAreaY + (flamethrower.height ? flamethrower.height * 0.5 : 75) - 100; // 75 is half of base height (150*0.5), fallback if height is not set game.addChild(flamethrower); // --- Second Flamethrower (left edge) --- var flamethrowerLeft = new Flamethrower(); // Place at the left edge, aligned with the people line flamethrowerLeft.x = startX - 200; // Move left flamethrower left by 100 more units flamethrowerLeft.y = peopleAreaY + (flamethrowerLeft.height ? flamethrowerLeft.height * 0.5 : 75) - 100; // Move base up by 100 units game.addChild(flamethrowerLeft); // 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); } // Ensure balloons are behind the dancing people but in front of the DJ deck var minDancingPeopleIndex = game.children.length; for (var i = 0; i < dancingPeople.length; i++) { var idx = game.children.indexOf(dancingPeople[i]); if (idx !== -1 && idx < minDancingPeopleIndex) minDancingPeopleIndex = idx; } var djDeckIndex = game.children.indexOf(djdeckAsset); // Use djdeckAsset which is the visible asset if (balloons) { for (var i = 0; i < balloons.length; i++) { var balloon = balloons[i]; var balloonIndex = game.children.indexOf(balloon); if (balloonIndex !== -1) { // Position balloons just behind the dancing people (minIndex - 1) // But also ensure they are in front of the djdeckAsset (djDeckIndex + 1) var targetIndex = Math.max(djDeckIndex + 1, minDancingPeopleIndex - 1); // Make sure the targetIndex is valid if (targetIndex < game.children.length) { game.setChildIndex(balloon, targetIndex); } else { // If target index is out of bounds, place it just before the dancing people game.setChildIndex(balloon, minDancingPeopleIndex > 0 ? minDancingPeopleIndex - 1 : 0); } } } } // --- 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 = 2300; var fxButtonY = 2200; // --- 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 + 333 - 124; 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); // --- DJDeck Asset (center of map) --- var djdeckAsset = LK.getAsset('djdeck', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(djdeckAsset); djdeckAsset.x = 2048 / 2; djdeckAsset.y = 2732 / 2 + 300 + 23 - 5 - 15 - 5; // Scale djdeck to fit the screen width properly djdeckAsset.width = 2048; djdeckAsset.height = djdeckAsset.height * (2048 / djdeckAsset.width); // Ensure rim assets are above djdeckAsset in display order var djdeckAssetIndex = game.children.indexOf(djdeckAsset); if (djdeckAssetIndex !== -1) { var rimLeftIndex = game.children.indexOf(rimLeft); var rimRightIndex = game.children.indexOf(rimRight); if (rimLeftIndex !== -1 && rimLeftIndex <= djdeckAssetIndex) { var targetIndexLeft = djdeckAssetIndex + 1; if (targetIndexLeft < game.children.length) { game.setChildIndex(rimLeft, targetIndexLeft); } else { // If target index is out of bounds, move to last position game.setChildIndex(rimLeft, game.children.length - 1); } // Update djdeckAssetIndex after moving rimLeft djdeckAssetIndex = game.children.indexOf(djdeckAsset); } if (rimRightIndex !== -1 && rimRightIndex <= djdeckAssetIndex) { // Get fresh index for rimLeft after potential move var currentRimLeftIndex = game.children.indexOf(rimLeft); var targetIndex = Math.max(djdeckAssetIndex + 1, currentRimLeftIndex + 1); if (targetIndex < game.children.length) { game.setChildIndex(rimRight, targetIndex); } else { // If target index is out of bounds, move to last position game.setChildIndex(rimRight, game.children.length - 1); } } } // --- Sampletable Asset (center of map) --- var sampletableAsset = LK.getAsset('sampletable', { anchorX: 0.5, anchorY: 1.0 }); game.addChild(sampletableAsset); sampletableAsset.x = 2048 / 2; sampletableAsset.y = 2732; // --- Crossfader --- // Remove and re-add crossfader to ensure it's above the sampletable if (game.children.indexOf(crossfader) !== -1) { game.removeChild(crossfader); } game.addChild(crossfader); // --- FX Button --- // Remove and re-add fxButton to ensure it's above the sampletable if (game.children.indexOf(fxButton) !== -1) { game.removeChild(fxButton); } game.addChild(fxButton); // --- Samplebutton Asset (left side corner of sampletable) --- var samplebuttonAsset = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset); samplebuttonAsset.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset.width / 2 + 50 - 4; samplebuttonAsset.y = sampletableAsset.y - samplebuttonAsset.height / 2 - 50; // Add functionality to first samplebutton samplebuttonAsset.down = function (x, y, obj) { // Play sample1 sound LK.getSound('sample1').play(); // Visual feedback - scale animation tween(samplebuttonAsset, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(samplebuttonAsset, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect LK.effects.flashObject(samplebuttonAsset, 0xff0000, 300); }; // --- Second Samplebutton Asset (reusing existing asset for memory efficiency) --- var samplebuttonAsset2 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset2); samplebuttonAsset2.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset2.width / 2 + 200 - 4; samplebuttonAsset2.y = sampletableAsset.y - samplebuttonAsset2.height / 2 - 50; // --- Third Samplebutton Asset --- var samplebuttonAsset3 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset3); samplebuttonAsset3.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset3.width / 2 + 350; samplebuttonAsset3.y = sampletableAsset.y - samplebuttonAsset3.height / 2 - 50; // Add functionality to third samplebutton samplebuttonAsset3.down = function (x, y, obj) { // Play sample3 sound LK.getSound('sample3').play(); // Visual feedback - color flash animation tween(samplebuttonAsset3, { tint: 0x0000ff }, { duration: 150, onFinish: function onFinish() { tween(samplebuttonAsset3, { tint: 0xffffff }, { duration: 150 }); } }); // Flash effect with blue color LK.effects.flashObject(samplebuttonAsset3, 0x0000ff, 300); score += 50; combo += 1; }; // --- Fourth Samplebutton Asset --- var samplebuttonAsset4 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset4); samplebuttonAsset4.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset4.width / 2 + 500 - 4; samplebuttonAsset4.y = sampletableAsset.y - samplebuttonAsset4.height / 2 - 50; // Add functionality to fourth samplebutton samplebuttonAsset4.down = function (x, y, obj) { // Play sample4 sound LK.getSound('sample4').play(); // Visual feedback - pulse animation tween(samplebuttonAsset4, { alpha: 0.3 }, { duration: 80, onFinish: function onFinish() { tween(samplebuttonAsset4, { alpha: 1.0 }, { duration: 80 }); } }); // Flash effect with purple color LK.effects.flashObject(samplebuttonAsset4, 0xff00ff, 300); score += 50; combo += 1; }; // --- Second Line Samplebuttons (Above First Line) --- // --- First Samplebutton Second Line --- var samplebuttonAsset5 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset5); samplebuttonAsset5.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset5.width / 2 + 50 - 4; samplebuttonAsset5.y = sampletableAsset.y - samplebuttonAsset5.height / 2 - 250; // Add functionality to fifth samplebutton samplebuttonAsset5.down = function (x, y, obj) { // Play sample1 sound with different effect LK.getSound('sample1').play(); // Visual feedback - scale animation tween(samplebuttonAsset5, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, onFinish: function onFinish() { tween(samplebuttonAsset5, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); // Flash effect with cyan color LK.effects.flashObject(samplebuttonAsset5, 0x00ffff, 300); score += 75; combo += 1; }; // --- Second Samplebutton Second Line --- var samplebuttonAsset6 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset6); samplebuttonAsset6.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset6.width / 2 + 200 - 4; samplebuttonAsset6.y = sampletableAsset.y - samplebuttonAsset6.height / 2 - 250; // Add functionality to sixth samplebutton samplebuttonAsset6.down = function (x, y, obj) { // Play sample2 sound with different effect LK.getSound('sample2').play(); // Visual feedback - scale animation tween(samplebuttonAsset6, { scaleX: 1.2, scaleY: 1.2 }, { duration: 150, onFinish: function onFinish() { tween(samplebuttonAsset6, { scaleX: 1, scaleY: 1 }, { duration: 150 }); } }); // Flash effect with magenta color LK.effects.flashObject(samplebuttonAsset6, 0xff00ff, 300); score += 75; combo += 1; }; // --- Third Samplebutton Second Line --- var samplebuttonAsset7 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset7); samplebuttonAsset7.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset7.width / 2 + 350 - 4; samplebuttonAsset7.y = sampletableAsset.y - samplebuttonAsset7.height / 2 - 250; // Add functionality to seventh samplebutton samplebuttonAsset7.down = function (x, y, obj) { // Play sample3 sound with different effect LK.getSound('sample3').play(); // Visual feedback - pulsing tint animation tween(samplebuttonAsset7, { tint: 0xffff00, scaleX: 0.8, scaleY: 0.8 }, { duration: 120, onFinish: function onFinish() { tween(samplebuttonAsset7, { tint: 0xffffff, scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); // Flash effect with yellow color LK.effects.flashObject(samplebuttonAsset7, 0xffff00, 300); score += 75; combo += 1; }; // --- Fourth Samplebutton Second Line --- var samplebuttonAsset8 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAsset8); samplebuttonAsset8.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset8.width / 2 + 500 - 4; samplebuttonAsset8.y = sampletableAsset.y - samplebuttonAsset8.height / 2 - 250; // Add functionality to eighth samplebutton samplebuttonAsset8.down = function (x, y, obj) { // Play sample4 sound with different effect LK.getSound('sample4').play(); // Visual feedback - alpha fade tween(samplebuttonAsset8, { alpha: 0.5 }, { duration: 300, onFinish: function onFinish() { tween(samplebuttonAsset8, { alpha: 1.0 }, { duration: 100 }); } }); // Flash effect with orange color LK.effects.flashObject(samplebuttonAsset8, 0xff8000, 300); score += 75; combo += 1; }; // Add different functionality to second samplebutton samplebuttonAsset2.down = function (x, y, obj) { // Play sample2 sound (different from first button) LK.getSound('sample2').play(); // Visual feedback - scale animation tween(samplebuttonAsset2, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(samplebuttonAsset2, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect with different color LK.effects.flashObject(samplebuttonAsset2, 0x00ff00, 300); // Add score bonus for using sample buttons score += 50; combo += 1; }; // --- Right Side Sample Buttons (First Line) --- // --- First Samplebutton Right Side First Line --- var samplebuttonAssetR1 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR1); samplebuttonAssetR1.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR1.width / 2 - 50 + 4; samplebuttonAssetR1.y = sampletableAsset.y - samplebuttonAssetR1.height / 2 - 50; // Add functionality to first right side samplebutton samplebuttonAssetR1.down = function (x, y, obj) { // Play sample1 sound LK.getSound('sample1').play(); // Visual feedback - scale animation tween(samplebuttonAssetR1, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(samplebuttonAssetR1, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect LK.effects.flashObject(samplebuttonAssetR1, 0xff0000, 300); }; // --- Second Samplebutton Right Side First Line --- var samplebuttonAssetR2 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR2); samplebuttonAssetR2.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR2.width / 2 - 200 + 4; samplebuttonAssetR2.y = sampletableAsset.y - samplebuttonAssetR2.height / 2 - 50; // Add functionality to second right side samplebutton samplebuttonAssetR2.down = function (x, y, obj) { // Play sample2 sound LK.getSound('sample2').play(); // Visual feedback - scale animation tween(samplebuttonAssetR2, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(samplebuttonAssetR2, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect with different color LK.effects.flashObject(samplebuttonAssetR2, 0x00ff00, 300); // Add score bonus for using sample buttons score += 50; combo += 1; }; // --- Third Samplebutton Right Side First Line --- var samplebuttonAssetR3 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR3); samplebuttonAssetR3.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR3.width / 2 - 350 + 4; samplebuttonAssetR3.y = sampletableAsset.y - samplebuttonAssetR3.height / 2 - 50; // Add functionality to third right side samplebutton samplebuttonAssetR3.down = function (x, y, obj) { // Play sample3 sound LK.getSound('sample3').play(); // Visual feedback - color flash animation tween(samplebuttonAssetR3, { tint: 0x0000ff }, { duration: 150, onFinish: function onFinish() { tween(samplebuttonAssetR3, { tint: 0xffffff }, { duration: 150 }); } }); // Flash effect with blue color LK.effects.flashObject(samplebuttonAssetR3, 0x0000ff, 300); score += 50; combo += 1; }; // --- Left Deck Asset --- var leftDeckAsset = LK.getAsset('Deck', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftDeckAsset); leftDeckAsset.x = rimLeft.x; leftDeckAsset.y = rimLeft.y; // --- Right Deck Asset --- var rightDeckAsset = LK.getAsset('Deck', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightDeckAsset); rightDeckAsset.x = rimRight.x; rightDeckAsset.y = rimRight.y; // --- Right Side Sample Buttons (Second Line) --- // --- First Samplebutton Right Side Second Line --- var samplebuttonAssetR5 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR5); samplebuttonAssetR5.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR5.width / 2 - 50 + 4; samplebuttonAssetR5.y = sampletableAsset.y - samplebuttonAssetR5.height / 2 - 250; // Add functionality to fifth right side samplebutton samplebuttonAssetR5.down = function (x, y, obj) { // Play sample1 sound with different effect LK.getSound('sample1').play(); // Visual feedback - scale animation tween(samplebuttonAssetR5, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, onFinish: function onFinish() { tween(samplebuttonAssetR5, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); // Flash effect with cyan color LK.effects.flashObject(samplebuttonAssetR5, 0x00ffff, 300); score += 75; combo += 1; }; // --- Second Samplebutton Right Side Second Line --- var samplebuttonAssetR6 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR6); samplebuttonAssetR6.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR6.width / 2 - 200 + 4; samplebuttonAssetR6.y = sampletableAsset.y - samplebuttonAssetR6.height / 2 - 250; // Add functionality to sixth right side samplebutton samplebuttonAssetR6.down = function (x, y, obj) { // Play sample2 sound with different effect LK.getSound('sample2').play(); // Visual feedback - scale animation tween(samplebuttonAssetR6, { scaleX: 1.2, scaleY: 1.2 }, { duration: 150, onFinish: function onFinish() { tween(samplebuttonAssetR6, { scaleX: 1, scaleY: 1 }, { duration: 150 }); } }); // Flash effect with magenta color LK.effects.flashObject(samplebuttonAssetR6, 0xff00ff, 300); score += 75; combo += 1; }; // --- Third Samplebutton Right Side Second Line --- var samplebuttonAssetR7 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR7); samplebuttonAssetR7.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR7.width / 2 - 350 + 4; samplebuttonAssetR7.y = sampletableAsset.y - samplebuttonAssetR7.height / 2 - 250; // Add functionality to seventh right side samplebutton samplebuttonAssetR7.down = function (x, y, obj) { // Play sample3 sound with different effect LK.getSound('sample3').play(); // Visual feedback - pulsing tint animation tween(samplebuttonAssetR7, { tint: 0xffff00, scaleX: 0.8, scaleY: 0.8 }, { duration: 120, onFinish: function onFinish() { tween(samplebuttonAssetR7, { tint: 0xffffff, scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); // Flash effect with yellow color LK.effects.flashObject(samplebuttonAssetR7, 0xffff00, 300); score += 75; combo += 1; }; // --- Fourth Samplebutton Right Side Second Line --- var samplebuttonAssetR8 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR8); samplebuttonAssetR8.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR8.width / 2 - 500 + 4; samplebuttonAssetR8.y = sampletableAsset.y - samplebuttonAssetR8.height / 2 - 250; // --- Fourth Samplebutton Right Side First Line --- var samplebuttonAssetR4 = LK.getAsset('samplebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(samplebuttonAssetR4); samplebuttonAssetR4.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR4.width / 2 - 500 + 4; samplebuttonAssetR4.y = sampletableAsset.y - samplebuttonAssetR4.height / 2 - 50; // Add functionality to fourth right side samplebutton samplebuttonAssetR4.down = function (x, y, obj) { // Play sample4 sound LK.getSound('sample4').play(); // Visual feedback - pulse animation tween(samplebuttonAssetR4, { alpha: 0.3 }, { duration: 80, onFinish: function onFinish() { tween(samplebuttonAssetR4, { alpha: 1.0 }, { duration: 80 }); } }); // Flash effect with purple color LK.effects.flashObject(samplebuttonAssetR4, 0xff00ff, 300); score += 50; combo += 1; }; // Add functionality to eighth right side samplebutton samplebuttonAssetR8.down = function (x, y, obj) { // Play sample4 sound with different effect LK.getSound('sample4').play(); // Visual feedback - alpha fade tween(samplebuttonAssetR8, { alpha: 0.5 }, { duration: 300, onFinish: function onFinish() { tween(samplebuttonAssetR8, { alpha: 1.0 }, { duration: 100 }); } }); // Flash effect with orange color LK.effects.flashObject(samplebuttonAssetR8, 0xff8000, 300); score += 75; combo += 1; }; // --- Digital Clock (center of map) --- var digitalClockTxt = new Text2('', { size: 30, fill: "#fff" }); digitalClockTxt.anchor.set(0.5, 0.5); digitalClockTxt.x = 2048 / 2; digitalClockTxt.y = 2732 / 2 - 500; 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); } // 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; // Check first samplebutton var sb1 = samplebuttonAsset.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb1.x - samplebuttonAsset.width / 2, 2) + Math.pow(sb1.y - samplebuttonAsset.height / 2, 2) < 75 * 75) return samplebuttonAsset; // Check second samplebutton var sb2 = samplebuttonAsset2.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb2.x - samplebuttonAsset2.width / 2, 2) + Math.pow(sb2.y - samplebuttonAsset2.height / 2, 2) < 75 * 75) return samplebuttonAsset2; // Check third samplebutton var sb3 = samplebuttonAsset3.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb3.x - samplebuttonAsset3.width / 2, 2) + Math.pow(sb3.y - samplebuttonAsset3.height / 2, 2) < 75 * 75) return samplebuttonAsset3; // Check fourth samplebutton var sb4 = samplebuttonAsset4.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb4.x - samplebuttonAsset4.width / 2, 2) + Math.pow(sb4.y - samplebuttonAsset4.height / 2, 2) < 75 * 75) return samplebuttonAsset4; // Check second line sample buttons // Check fifth samplebutton (second line, first button) var sb5 = samplebuttonAsset5.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb5.x - samplebuttonAsset5.width / 2, 2) + Math.pow(sb5.y - samplebuttonAsset5.height / 2, 2) < 75 * 75) return samplebuttonAsset5; // Check sixth samplebutton (second line, second button) var sb6 = samplebuttonAsset6.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb6.x - samplebuttonAsset6.width / 2, 2) + Math.pow(sb6.y - samplebuttonAsset6.height / 2, 2) < 75 * 75) return samplebuttonAsset6; // Check seventh samplebutton (second line, third button) var sb7 = samplebuttonAsset7.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb7.x - samplebuttonAsset7.width / 2, 2) + Math.pow(sb7.y - samplebuttonAsset7.height / 2, 2) < 75 * 75) return samplebuttonAsset7; // Check eighth samplebutton (second line, fourth button) var sb8 = samplebuttonAsset8.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sb8.x - samplebuttonAsset8.width / 2, 2) + Math.pow(sb8.y - samplebuttonAsset8.height / 2, 2) < 75 * 75) return samplebuttonAsset8; // Check right side sample buttons // Check first right side samplebutton (first line) var sbR1 = samplebuttonAssetR1.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR1.x - samplebuttonAssetR1.width / 2, 2) + Math.pow(sbR1.y - samplebuttonAssetR1.height / 2, 2) < 75 * 75) return samplebuttonAssetR1; // Check second right side samplebutton (first line) var sbR2 = samplebuttonAssetR2.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR2.x - samplebuttonAssetR2.width / 2, 2) + Math.pow(sbR2.y - samplebuttonAssetR2.height / 2, 2) < 75 * 75) return samplebuttonAssetR2; // Check third right side samplebutton (first line) var sbR3 = samplebuttonAssetR3.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR3.x - samplebuttonAssetR3.width / 2, 2) + Math.pow(sbR3.y - samplebuttonAssetR3.height / 2, 2) < 75 * 75) return samplebuttonAssetR3; // Check fourth right side samplebutton (first line) var sbR4 = samplebuttonAssetR4.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR4.x - samplebuttonAssetR4.width / 2, 2) + Math.pow(sbR4.y - samplebuttonAssetR4.height / 2, 2) < 75 * 75) return samplebuttonAssetR4; // Check fifth right side samplebutton (second line) var sbR5 = samplebuttonAssetR5.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR5.x - samplebuttonAssetR5.width / 2, 2) + Math.pow(sbR5.y - samplebuttonAssetR5.height / 2, 2) < 75 * 75) return samplebuttonAssetR5; // Check sixth right side samplebutton (second line) var sbR6 = samplebuttonAssetR6.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR6.x - samplebuttonAssetR6.width / 2, 2) + Math.pow(sbR6.y - samplebuttonAssetR6.height / 2, 2) < 75 * 75) return samplebuttonAssetR6; // Check seventh right side samplebutton (second line) var sbR7 = samplebuttonAssetR7.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR7.x - samplebuttonAssetR7.width / 2, 2) + Math.pow(sbR7.y - samplebuttonAssetR7.height / 2, 2) < 75 * 75) return samplebuttonAssetR7; // Check eighth right side samplebutton (second line) var sbR8 = samplebuttonAssetR8.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(sbR8.x - samplebuttonAssetR8.width / 2, 2) + Math.pow(sbR8.y - samplebuttonAssetR8.height / 2, 2) < 75 * 75) return samplebuttonAssetR8; // Check right loop exit button var rightLoopExitLocal = rightLoopExitButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rightLoopExitLocal.x - rightLoopExitButton.width / 2, 2) + Math.pow(rightLoopExitLocal.y - rightLoopExitButton.height / 2, 2) < 50 * 50) return rightLoopExitButton; // Check right in button var rightInLocal = rightInButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rightInLocal.x - rightInButton.width / 2, 2) + Math.pow(rightInLocal.y - rightInButton.height / 2, 2) < 50 * 50) return rightInButton; // Check right out button var rightOutLocal = rightOutButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rightOutLocal.x - rightOutButton.width / 2, 2) + Math.pow(rightOutLocal.y - rightOutButton.height / 2, 2) < 50 * 50) return rightOutButton; // Check left start button var leftStartLocal = leftStartButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(leftStartLocal.x - leftStartButton.width / 2, 2) + Math.pow(leftStartLocal.y - leftStartButton.height / 2, 2) < 100 * 100) return leftStartButton; // Check right start button var rightStartLocal = rightStartButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rightStartLocal.x - rightStartButton.width / 2, 2) + Math.pow(rightStartLocal.y - rightStartButton.height / 2, 2) < 100 * 100) return rightStartButton; // Check left cue button var leftCueLocal = leftCueButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(leftCueLocal.x - leftCueButton.width / 2, 2) + Math.pow(leftCueLocal.y - leftCueButton.height / 2, 2) < 50 * 50) return leftCueButton; // Check right cue button var rightCueLocal = rightCueButton.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rightCueLocal.x - rightCueButton.width / 2, 2) + Math.pow(rightCueLocal.y - rightCueButton.height / 2, 2) < 50 * 50) return rightCueButton; // Check vertical crossfader track var verticalCfLocal = verticalCrossfaderTrack.toLocal(game.toGlobal({ x: x, y: y })); if (verticalCfLocal.x > 0 && verticalCfLocal.x < verticalCrossfaderTrack.width && verticalCfLocal.y > 0 && verticalCfLocal.y < verticalCrossfaderTrack.height) return verticalCrossfaderTrack; // Check second vertical crossfader track var verticalCfLocal2 = verticalCrossfaderTrack2.toLocal(game.toGlobal({ x: x, y: y })); if (verticalCfLocal2.x > 0 && verticalCfLocal2.x < verticalCrossfaderTrack2.width && verticalCfLocal2.y > 0 && verticalCfLocal2.y < verticalCrossfaderTrack2.height) return verticalCrossfaderTrack2; // Check filter knobs var lfh = leftFilterKnobHigh.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(lfh.x - leftFilterKnobHigh.width / 2, 2) + Math.pow(lfh.y - leftFilterKnobHigh.height / 2, 2) < 50 * 50) return leftFilterKnobHigh; var lfm = leftFilterKnobMid.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(lfm.x - leftFilterKnobMid.width / 2, 2) + Math.pow(lfm.y - leftFilterKnobMid.height / 2, 2) < 50 * 50) return leftFilterKnobMid; var lfl = leftFilterKnobLow.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(lfl.x - leftFilterKnobLow.width / 2, 2) + Math.pow(lfl.y - leftFilterKnobLow.height / 2, 2) < 50 * 50) return leftFilterKnobLow; var rfh = rightFilterKnobHigh.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rfh.x - rightFilterKnobHigh.width / 2, 2) + Math.pow(rfh.y - rightFilterKnobHigh.height / 2, 2) < 50 * 50) return rightFilterKnobHigh; var rfm = rightFilterKnobMid.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rfm.x - rightFilterKnobMid.width / 2, 2) + Math.pow(rfm.y - rightFilterKnobMid.height / 2, 2) < 50 * 50) return rightFilterKnobMid; var rfl = rightFilterKnobLow.toLocal(game.toGlobal({ x: x, y: y })); if (Math.pow(rfl.x - rightFilterKnobLow.width / 2, 2) + Math.pow(rfl.y - rightFilterKnobLow.height / 2, 2) < 50 * 50) return rightFilterKnobLow; } 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) { var local = dragging.toLocal(game.toGlobal({ x: x, y: y })); if (dragging === verticalCrossfaderTrack) { // Use the object's own move method for consistent behavior dragging.move(local.x, local.y, obj); } else if (dragging === verticalCrossfaderTrack2) { // Use the object's own move method for consistent behavior dragging.move(local.x, local.y, obj); } else if (dragging === leftFilterKnobHigh || dragging === leftFilterKnobMid || dragging === leftFilterKnobLow || dragging === rightFilterKnobHigh || dragging === rightFilterKnobMid || dragging === rightFilterKnobLow) { // Handle move for filter knobs var angle = Math.atan2(local.y - dragging.height / 2, local.x - dragging.width / 2); // Normalize angle to be between -PI and PI if (angle > Math.PI) angle -= 2 * Math.PI; if (angle < -Math.PI) angle += 2 * Math.PI; // Filter knob rotation: -135 degrees to +135 degrees (approx -2.35 to 2.35 radians) var minAngle = -Math.PI * 0.75; var maxAngle = Math.PI * 0.75; dragging.rotation = Math.max(minAngle, Math.min(maxAngle, angle)); } else if (dragging.move) { // Handle move for other objects that have a move method dragging.move(local.x, local.y, obj); } } }; // --- Start Button State Variables --- var leftStartButtonOn = false; var rightStartButtonOn = false; // --- Deck Spinning State Variables --- var leftDeckSpinning = false; var rightDeckSpinning = false; // --- Left Start Button --- var leftStartButton = LK.getAsset('Startbutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftStartButton); leftStartButton.x = rimLeft.x - 200; leftStartButton.y = rimLeft.y + rimLeft.height / 2 + leftStartButton.height / 2 + 20; // --- Left Cue Button --- var leftCueButton = LK.getAsset('Cuebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftCueButton); leftCueButton.x = leftStartButton.x + leftStartButton.width / 2 + leftCueButton.width / 2 + 20 + 200 - 20; leftCueButton.y = leftStartButton.y; // Add functionality to left start button leftStartButton.down = function (x, y, obj) { // Visual feedback - scale animation tween(leftStartButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 150, onFinish: function onFinish() { tween(leftStartButton, { scaleX: 1, scaleY: 1 }, { duration: 150 }); } }); // Toggle button state leftStartButtonOn = !leftStartButtonOn; // Stop any existing tween first tween.stop(leftStartButton, { tint: true }); if (leftStartButtonOn) { // Turn on green light effect tween(leftStartButton, { tint: 0x00ff00 }, { duration: 200, onFinish: function onFinish() { // Create pulsing green light effect function createPulse() { if (leftStartButtonOn) { // Only continue pulsing if still on tween(leftStartButton, { tint: 0x88ff88 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (leftStartButtonOn) { // Only continue pulsing if still on tween(leftStartButton, { tint: 0x00ff00 }, { duration: 800, easing: tween.easeInOut, onFinish: createPulse }); } } }); } } createPulse(); } }); } else { // Turn off light effect - return to normal tint tween(leftStartButton, { tint: 0xffffff }, { duration: 200 }); } // Start/stop left deck leftDeck.playing = !leftDeck.playing; // Start/stop left deck spinning leftDeckSpinning = !leftDeckSpinning; if (leftDeckSpinning) { // Start continuous clockwise spinning synchronized with deckplatter var _spinLeftDeck = function spinLeftDeck() { if (leftDeckSpinning) { // Synchronize deck asset rotation with deckplatter rotation speed // Deckplatter rotates at 0.02 radians per frame (60fps), so full rotation takes ~314 frames (~5.23 seconds) // To match this, deck asset should complete rotation in same time tween(leftDeckAsset, { rotation: leftDeckAsset.rotation + Math.PI * 2 }, { duration: 5233, easing: tween.linear, onFinish: _spinLeftDeck }); } }; _spinLeftDeck(); } else { // Stop spinning by stopping any rotation tweens tween.stop(leftDeckAsset, { rotation: true }); } // Play beat sound LK.getSound('beat').play(); // Add score bonus score += 100; combo += 1; }; // --- Right Start Button --- var rightStartButton = LK.getAsset('Startbutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightStartButton); rightStartButton.x = rimRight.x - 200; rightStartButton.y = rimRight.y + rimRight.height / 2 + rightStartButton.height / 2 + 20; // --- Right Cue Button --- var rightCueButton = LK.getAsset('Cuebutton', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightCueButton); rightCueButton.x = rightStartButton.x + rightStartButton.width / 2 + rightCueButton.width / 2 + 20 + 200 - 20 - 5 - 5; rightCueButton.y = rightStartButton.y; // Add functionality to right start button rightStartButton.down = function (x, y, obj) { // Visual feedback - scale animation tween(rightStartButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 150, onFinish: function onFinish() { tween(rightStartButton, { scaleX: 1, scaleY: 1 }, { duration: 150 }); } }); // Toggle button state rightStartButtonOn = !rightStartButtonOn; // Stop any existing tween first tween.stop(rightStartButton, { tint: true }); if (rightStartButtonOn) { // Turn on green light effect tween(rightStartButton, { tint: 0x00ff00 }, { duration: 200, onFinish: function onFinish() { // Create pulsing green light effect function createPulse() { if (rightStartButtonOn) { // Only continue pulsing if still on tween(rightStartButton, { tint: 0x88ff88 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (rightStartButtonOn) { // Only continue pulsing if still on tween(rightStartButton, { tint: 0x00ff00 }, { duration: 800, easing: tween.easeInOut, onFinish: createPulse }); } } }); } } createPulse(); } }); } else { // Turn off light effect - return to normal tint tween(rightStartButton, { tint: 0xffffff }, { duration: 200 }); } // Start/stop right deck rightDeck.playing = !rightDeck.playing; // Start/stop right deck spinning rightDeckSpinning = !rightDeckSpinning; if (rightDeckSpinning) { // Start continuous clockwise spinning synchronized with deckplatter var _spinRightDeck = function spinRightDeck() { if (rightDeckSpinning) { // Synchronize deck asset rotation with deckplatter rotation speed // Deckplatter rotates at 0.02 radians per frame (60fps), so full rotation takes ~314 frames (~5.23 seconds) // To match this, deck asset should complete rotation in same time tween(rightDeckAsset, { rotation: rightDeckAsset.rotation + Math.PI * 2 }, { duration: 5233, easing: tween.linear, onFinish: _spinRightDeck }); } }; _spinRightDeck(); } else { // Stop spinning by stopping any rotation tweens tween.stop(rightDeckAsset, { rotation: true }); } // Play beat sound LK.getSound('beat').play(); // Add score bonus score += 100; combo += 1; }; // Add functionality to left cue button leftCueButton.down = function (x, y, obj) { // Visual feedback - scale animation tween(leftCueButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(leftCueButton, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect with cyan color LK.effects.flashObject(leftCueButton, 0x00ffff, 300); // Reset left deck to start position leftDeck.rotationOffset = 0; leftDeck.currentRotation = 0; leftDeck.rotation = 0; // Play scratch sound LK.getSound('scratch').play(); // Add score bonus score += 50; combo += 1; }; // Add functionality to right cue button rightCueButton.down = function (x, y, obj) { // Visual feedback - scale animation tween(rightCueButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(rightCueButton, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect with magenta color LK.effects.flashObject(rightCueButton, 0xff00ff, 300); // Reset right deck to start position rightDeck.rotationOffset = 0; rightDeck.currentRotation = 0; rightDeck.rotation = 0; // Play scratch sound LK.getSound('scratch').play(); // Add score bonus score += 50; combo += 1; }; // Center left deck platter to the left deck center leftDeck.x = leftDeckAsset.x; leftDeck.y = leftDeckAsset.y - 3; // Center right deck platter to the right deck center rightDeck.x = rightDeckAsset.x; rightDeck.y = rightDeckAsset.y - 3; // --- Screens above decks --- // Left deck screen var leftDeckScreen = LK.getAsset('screen', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftDeckScreen); leftDeckScreen.x = leftDeckAsset.x; leftDeckScreen.y = leftDeckAsset.y - leftDeckAsset.height / 2 - leftDeckScreen.height / 2 - 50 - 200; // Right deck screen var rightDeckScreen = LK.getAsset('screen', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightDeckScreen); rightDeckScreen.x = rightDeckAsset.x; rightDeckScreen.y = rightDeckAsset.y - rightDeckAsset.height / 2 - rightDeckScreen.height / 2 - 50 - 200; // --- Effect Buttons Above Screens --- var effectButtons = []; var effectButtonSpacing = 220; var effectButtonY = leftDeckScreen.y - leftDeckScreen.height / 2 - 80 - 55; // Create 6 effect buttons (3 above each screen) for (var i = 0; i < 6; i++) { var effectButton = LK.getAsset('Effect', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(effectButton); // Position 3 buttons above left screen, 3 above right screen if (i < 3) { // Left side buttons effectButton.x = leftDeckScreen.x - effectButtonSpacing + i * effectButtonSpacing; } else { // Right side buttons effectButton.x = rightDeckScreen.x - effectButtonSpacing + (i - 3) * effectButtonSpacing; } effectButton.y = effectButtonY; // Add 'FIRE' text above the first effect button (i === 0) if (i === 0) { var fireText = new Text2('FIRE', { size: 36, fill: 0xFFFFFF }); fireText.anchor.set(0.5, 0.5); fireText.x = effectButton.x; fireText.y = effectButton.y - effectButton.height / 2 - 80; game.addChild(fireText); } // Add 'LASER' text above the second effect button (i === 1) if (i === 1) { var laserText = new Text2('LASER', { size: 36, fill: 0xFFFFFF }); laserText.anchor.set(0.5, 0.5); laserText.x = effectButton.x; laserText.y = effectButton.y - effectButton.height / 2 - 80; game.addChild(laserText); } // Add 'LIGHT' text above the third effect button (i === 2) if (i === 2) { var lightText = new Text2('LIGHT', { size: 36, fill: 0xFFFFFF }); lightText.anchor.set(0.5, 0.5); lightText.x = effectButton.x; lightText.y = effectButton.y - effectButton.height / 2 - 80; game.addChild(lightText); // Store the third effect button for later access effectButtons[i] = effectButton; } // Add 'SMOKE' text above the fourth effect button (i === 3) if (i === 3) { var smokeText = new Text2('SMOKE', { size: 36, fill: 0xFFFFFF }); smokeText.anchor.set(0.5, 0.5); smokeText.x = effectButton.x; smokeText.y = effectButton.y - effectButton.height / 2 - 80; game.addChild(smokeText); } // Add 'UV' text above the fifth effect button (i === 4) if (i === 4) { var uvText = new Text2('UV', { size: 36, fill: 0xFFFFFF }); uvText.anchor.set(0.5, 0.5); uvText.x = effectButton.x; uvText.y = effectButton.y - effectButton.height / 2 - 80; game.addChild(uvText); } // Add 'GIFTS' text above the sixth effect button (i === 5) if (i === 5) { var giftsText = new Text2('GIFTS', { size: 36, fill: 0xFFFFFF }); giftsText.anchor.set(0.5, 0.5); giftsText.x = effectButton.x; giftsText.y = effectButton.y - effectButton.height / 2 - 80; game.addChild(giftsText); } // Scale up effect button by 1.5 tween(effectButton, { scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut }); // Add up handler for SMOKE button (index 3) if (i === 3) { effectButton.down = function (x, y, obj) { smokeEffectActive = true; fogAnimationActive = true; // Clear any existing fog timeout if (fogTimeout) { LK.clearTimeout(fogTimeout); fogTimeout = null; } tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); LK.getSound('fx').play(); LK.effects.flashObject(this, 0x888888, 200); }; effectButton.up = function (x, y, obj) { smokeEffectActive = false; // Start 4 second countdown to turn off fog animation fogTimeout = LK.setTimeout(function () { fogAnimationActive = false; fogTimeout = null; }, 4000); // Optionally, provide a quick scale feedback on release tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 100 }); }; } else { // Add functionality to other effect buttons effectButton.down = function (x, y, obj) { // Visual feedback - scale animation that returns to 1.5 instead of 1.0 tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); // Play FX sound LK.getSound('fx').play(); // Flash effect with random color var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var randomColor = colors[Math.floor(Math.random() * colors.length)]; LK.effects.flashObject(this, randomColor, 400); // Add score bonus score += 75; combo += 1; }; } // If this is the first effect button (index 0) - FIRE button if (i === 0) { effectButton.down = function (x, y, obj) { // Visual feedback - scale animation that returns to 1.5 instead of 1.0 tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); // Play FX sound LK.getSound('fx').play(); // Cycle through fire effect modes: 0 (off) -> 1 (normal) -> 2 (big) -> 0 (off) fireEffectMode = (fireEffectMode + 1) % 3; // Flash effect with different colors based on mode var fireColors; if (fireEffectMode === 0) { // Off mode - gray color fireColors = [0x888888]; } else if (fireEffectMode === 1) { // Normal mode - orange colors fireColors = [0xff8000, 0xff4400, 0xffaa00]; } else { // Big flames mode - red colors fireColors = [0xff0000, 0xff4444, 0xff8888]; } var randomColor = fireColors[Math.floor(Math.random() * fireColors.length)]; LK.effects.flashObject(this, randomColor, 400); // Add score bonus score += 75; combo += 1; }; } effectButtons.push(effectButton); // If this is the second effect button (index 1) - LASER button if (i === 1) { effectButton.down = function (x, y, obj) { // Visual feedback - scale animation that returns to 1.5 instead of 1.0 tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); // Play FX sound LK.getSound('fx').play(); // Cycle through laser show modes: 0 (off) -> 1 (slow) -> 2 (fast) -> 0 (off) laserShowMode = (laserShowMode + 1) % 3; // Flash effect with different colors based on mode var laserColors; if (laserShowMode === 0) { // Off mode - gray color laserColors = [0x888888]; } else if (laserShowMode === 1) { // Slow mode - blue colors laserColors = [0x0000ff, 0x4444ff, 0x8888ff]; } else { // Fast mode - rainbow colors laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; } var randomColor = laserColors[Math.floor(Math.random() * laserColors.length)]; LK.effects.flashObject(this, randomColor, 400); // Add score bonus score += 75; combo += 1; }; } // If this is the third effect button (index 2) if (i === 2) { effectButton.down = function (x, y, obj) { // Visual feedback - scale animation that returns to 1.5 instead of 1.0 tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); // Play FX sound LK.getSound('fx').play(); // Flash effect with random color var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var randomColor = colors[Math.floor(Math.random() * colors.length)]; LK.effects.flashObject(this, randomColor, 400); // Add score bonus score += 75; combo += 1; // Toggle RGB flashing state isRgbFlashingActive = !isRgbFlashingActive; }; } // Define variable to track the gifts button mode var giftsMode = 0; // 0: off, 1: balloons, 2: money, 3: fireworks // Reference the gifts effect button var giftsEffectButton; // If this is the fifth effect button (index 4) - UV button if (i === 4) { effectButton.down = function (x, y, obj) { // Visual feedback - scale animation that returns to 1.5 instead of 1.0 tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); // Play FX sound LK.getSound('fx').play(); // Toggle UV effect state uvEffectActive = !uvEffectActive; // Flash effect with different colors based on mode var flashColor = uvEffectActive ? 0x8000ff : 0x888888; // Purple for ON, gray for OFF LK.effects.flashObject(this, flashColor, 400); // Add score bonus score += 75; combo += 1; }; // Add a reference to the UV button for flashing in update effectButtons[4] = effectButton; } // If this is the sixth effect button (index 5) - GIFTS button if (i === 5) { giftsEffectButton = effectButton; // Store reference to gifts button effectButton.down = function (x, y, obj) { // Cycle through gift modes giftsMode = (giftsMode + 1) % 4; // Visual feedback - scale animation tween(this, { scaleX: 1.7, scaleY: 1.7 }, { duration: 150, onFinish: function () { tween(this, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150 }); }.bind(this) }); // Play FX sound LK.getSound('fx').play(); // Add score bonus score += 100; combo += 1; }; } } // Tablet between the two screens var tabletAsset = LK.getAsset('tablet', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(tabletAsset); // Ensure screens exist before positioning tablet if (typeof leftDeckScreen !== 'undefined' && typeof rightDeckScreen !== 'undefined') { tabletAsset.x = (leftDeckScreen.x + rightDeckScreen.x) / 2; tabletAsset.y = leftDeckScreen.y + 77 + 35 - 20; } else { // Fallback positioning if screens are not defined tabletAsset.x = 2048 / 2; tabletAsset.y = 1366; } // Create rekordbox app instance var rekordboxApp = new RekordboxApp(); game.addChild(rekordboxApp); // Position and scale rekordbox app to fit tablet screen rekordboxApp.x = tabletAsset.x - tabletAsset.width / 2 + 20 + 30; rekordboxApp.y = tabletAsset.y - tabletAsset.height / 2 + 20 - 200 - 25; // Create keyboard app instance var keyboardApp = new KeyboardApp(); game.addChild(keyboardApp); // Position and scale keyboard app under the tracklist keyboardApp.x = rekordboxApp.x - 777; // Align with rekordbox app and move left by 777 units keyboardApp.y = rekordboxApp.y + rekordboxApp.height + 10 + 777 - 300 + 200 - 100 - 100; // Position below tracklist with spacing and move down by 777 units, then up by 300, then down by 200, then up by 100, then up by 100 keyboardApp.scaleX = rekordboxApp.scaleX * 1.1; // Match scale and adjust slightly keyboardApp.scaleY = keyboardApp.scaleX; // Maintain aspect ratio keyboardApp.visible = false; // Hide the keyboard initially // Scale up keyboard app to 3x size using tween animation tween(keyboardApp, { scaleX: 3.0, scaleY: 3.0 }, { duration: 500, easing: tween.easeOut }); // Scale down to better fit the tablet asset by 1.1 tween(rekordboxApp, { scaleX: 0.95 / 1.1, scaleY: 0.95 / 1.1 }, { duration: 500, easing: tween.easeOut }); // Add tablet press functionality to pop up fullscreen tracklist tabletAsset.down = function (x, y, obj) { // Create fullscreen tracklist popup var fullscreenTracklist = new RekordboxApp(); // Add to game at top layer game.addChild(fullscreenTracklist); keyboardApp.visible = true; // Show the keyboard app // Ensure keyboard is above the overlay and tracklist game.addChild(keyboardApp); // Position fullscreen - center on screen and move to top fullscreenTracklist.x = 2048 / 2 - 371 - 600; // Half of app width (742/2) fullscreenTracklist.y = 0 + 269; // Move to top of tablet height // Scale to fullscreen size fullscreenTracklist.scaleX = 2.5 * 1.05; fullscreenTracklist.scaleY = 2.5 * 1.05; // Create semi-transparent background overlay var overlay = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 2048, height: 2732 }); overlay.tint = 0x000000; overlay.alpha = 0.7; overlay.x = 0; overlay.y = 0; // Add overlay behind the app game.addChild(overlay); game.setChildIndex(overlay, game.children.indexOf(fullscreenTracklist) - 1); // Add close functionality when clicking overlay overlay.down = function (x, y, obj) { // Remove fullscreen tracklist and overlay if (game.children.indexOf(fullscreenTracklist) !== -1) { game.removeChild(fullscreenTracklist); } if (game.children.indexOf(overlay) !== -1) { game.removeChild(overlay); } keyboardApp.visible = false; // Hide the keyboard app }; // Visual feedback for tablet press tween(tabletAsset, { scaleX: 1.1, scaleY: 1.1 }, { duration: 100, onFinish: function onFinish() { tween(tabletAsset, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100 }); } }); // Play sound effect LK.getSound('fx').play(); }; // --- IN Button under tablet screen --- var inButton = LK.getAsset('in', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(inButton); inButton.x = tabletAsset.x - 700 - 200 - 12; inButton.y = tabletAsset.y + tabletAsset.height / 2 + inButton.height / 2 + 20 - 300 + 77; // Add whitegray text: ADJUST above in buttons var adjustTextLeft = new Text2('ADJUST', { size: 24, fill: 0xCCCCCC // Whitegray color }); adjustTextLeft.anchor.set(0.5, 0.5); adjustTextLeft.x = inButton.x + 107; adjustTextLeft.y = inButton.y - inButton.height / 2 - 20; // Position above the button game.addChild(adjustTextLeft); // Add whitegray text: IN under in buttons var inTextLeft = new Text2('IN', { size: 24, fill: 0xCCCCCC // Whitegray color }); inTextLeft.anchor.set(0.5, 0.5); inTextLeft.x = inButton.x; inTextLeft.y = inButton.y + inButton.height / 2 + 20; // Position under the button game.addChild(inTextLeft); // Add functionality to IN button inButton.down = function (x, y, obj) { // Stop any existing flashing effect first tween.stop(inButton, { tint: true }); // Start flashing orange light effect function createOrangeFlash() { tween(inButton, { tint: 0xff8000 // Orange color }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(inButton, { tint: 0xffffff // Back to white/normal }, { duration: 300, easing: tween.easeInOut, onFinish: createOrangeFlash // Continue flashing }); } }); } createOrangeFlash(); // Add score bonus score += 25; combo += 1; }; // --- OUT Button under tablet screen --- var outButton = LK.getAsset('Out', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(outButton); outButton.x = tabletAsset.x + 700 + 200; outButton.y = tabletAsset.y + tabletAsset.height / 2 + outButton.height / 2 + 20 - 300 + 77; // --- Right IN Button to the left of right OUT button --- var rightInButton = LK.getAsset('in', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightInButton); rightInButton.x = outButton.x - 192 - 200 - 12; rightInButton.y = outButton.y; // Create right ADJUST text now that rightInButton is defined var adjustTextRight = new Text2('ADJUST', { size: 24, fill: 0xCCCCCC // Whitegray color }); adjustTextRight.anchor.set(0.5, 0.5); adjustTextRight.x = rightInButton.x + 107; adjustTextRight.y = rightInButton.y - rightInButton.height / 2 - 20; // Position above the button game.addChild(adjustTextRight); // Add whitegray text: IN under right in buttons var inTextRight = new Text2('IN', { size: 24, fill: 0xCCCCCC // Whitegray color }); inTextRight.anchor.set(0.5, 0.5); inTextRight.x = rightInButton.x; inTextRight.y = rightInButton.y + rightInButton.height / 2 + 20; // Position under the button game.addChild(inTextRight); // Add functionality to right IN button rightInButton.down = function (x, y, obj) { // Stop any existing flashing effect first tween.stop(rightInButton, { tint: true }); // Start flashing orange light effect function createOrangeFlash() { tween(rightInButton, { tint: 0xff8000 // Orange color }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(rightInButton, { tint: 0xffffff // Back to white/normal }, { duration: 300, easing: tween.easeInOut, onFinish: createOrangeFlash // Continue flashing }); } }); } createOrangeFlash(); // Add score bonus score += 25; combo += 1; }; // --- OUT Button above left deck --- var outButtonLeft = LK.getAsset('Out', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(outButtonLeft); outButtonLeft.x = leftDeckAsset.x + 192 - 200; outButtonLeft.y = inButton.y; // --- Loop Exit Button on the right side of left out button --- var loopExitButton = LK.getAsset('LoopExit', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(loopExitButton); loopExitButton.x = outButtonLeft.x + 150 + 50; loopExitButton.y = outButtonLeft.y; // Add whitegray text: OUT under left out button var outTextLeft = new Text2('OUT', { size: 24, fill: 0xCCCCCC // Whitegray color }); outTextLeft.anchor.set(0.5, 0.5); outTextLeft.x = outButtonLeft.x; outTextLeft.y = inTextLeft.y - 3; // Position at same height as IN text, moved up by 3 units game.addChild(outTextLeft); // Add whitegray text: LOOP between left out button and left loop exit button var loopTextLeft = new Text2('LOOP', { size: 24, fill: 0xCCCCCC // Whitegray color }); loopTextLeft.anchor.set(0.5, 0.5); loopTextLeft.x = (outButtonLeft.x + loopExitButton.x) / 2; loopTextLeft.y = adjustTextLeft.y; // Position at same height as ADJUST text game.addChild(loopTextLeft); // Add whitegray text: EXIT under left loop exit button var exitTextLeft = new Text2('EXIT', { size: 24, fill: 0xCCCCCC // Whitegray color }); exitTextLeft.anchor.set(0.5, 0.5); exitTextLeft.x = loopExitButton.x; exitTextLeft.y = loopExitButton.y + loopExitButton.height / 2 + 20; // Position under the button game.addChild(exitTextLeft); // Add functionality to loop exit button loopExitButton.down = function (x, y, obj) { // Visual feedback - scale animation tween(loopExitButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(loopExitButton, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect with purple color LK.effects.flashObject(loopExitButton, 0x8000ff, 300); // Add score bonus score += 30; combo += 1; }; // Add functionality to left OUT button outButtonLeft.down = function (x, y, obj) { // Stop any existing flashing effect first tween.stop(outButtonLeft, { tint: true }); // Stop flashing the corresponding 'in' button tween.stop(inButton, { tint: true }); inButton.tint = 0xffffff; // Reset tint to normal // Start flashing orange light effect function createOrangeFlash() { tween(outButtonLeft, { tint: 0xff8000 // Orange color }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(outButtonLeft, { tint: 0xffffff // Back to white/normal }, { duration: 300, easing: tween.easeInOut, onFinish: createOrangeFlash // Continue flashing }); } }); } createOrangeFlash(); // Add score bonus score += 25; combo += 1; }; // --- Right OUT Button --- var rightOutButton = LK.getAsset('Out', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightOutButton); rightOutButton.x = rightDeckAsset.x + 192 - 200; rightOutButton.y = inButton.y; // --- Right Loop Exit Button next to right out button --- var rightLoopExitButton = LK.getAsset('LoopExit', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightLoopExitButton); rightLoopExitButton.x = rightOutButton.x + 150 + 50 + 5; rightLoopExitButton.y = rightOutButton.y; // Add functionality to right loop exit button rightLoopExitButton.down = function (x, y, obj) { // Visual feedback - scale animation tween(rightLoopExitButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(rightLoopExitButton, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Flash effect with purple color LK.effects.flashObject(rightLoopExitButton, 0x8000ff, 300); // Add score bonus score += 30; combo += 1; }; // Add whitegray text: OUT under right out button var outTextRight = new Text2('OUT', { size: 24, fill: 0xCCCCCC // Whitegray color }); outTextRight.anchor.set(0.5, 0.5); outTextRight.x = rightOutButton.x; outTextRight.y = inTextRight.y - 3; // Position at same height as IN text, moved up by 3 units game.addChild(outTextRight); // Add whitegray text: LOOP between right out button and right loop exit button var loopTextRight = new Text2('LOOP', { size: 24, fill: 0xCCCCCC // Whitegray color }); loopTextRight.anchor.set(0.5, 0.5); loopTextRight.x = (rightOutButton.x + rightLoopExitButton.x) / 2; loopTextRight.y = adjustTextRight.y; // Position at same height as ADJUST text game.addChild(loopTextRight); // Add whitegray text: EXIT under right loop exit button var exitTextRight = new Text2('EXIT', { size: 24, fill: 0xCCCCCC // Whitegray color }); exitTextRight.anchor.set(0.5, 0.5); exitTextRight.x = rightLoopExitButton.x; exitTextRight.y = rightLoopExitButton.y + rightLoopExitButton.height / 2 + 20; // Position under the button game.addChild(exitTextRight); // Add functionality to right OUT button rightOutButton.down = function (x, y, obj) { // Stop any existing flashing effect first tween.stop(rightOutButton, { tint: true }); // Stop flashing the corresponding 'in' button tween.stop(rightInButton, { tint: true }); rightInButton.tint = 0xffffff; // Reset tint to normal // Start flashing orange light effect function createOrangeFlash() { tween(rightOutButton, { tint: 0xff8000 // Orange color }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(rightOutButton, { tint: 0xffffff // Back to white/normal }, { duration: 300, easing: tween.easeInOut, onFinish: createOrangeFlash // Continue flashing }); } }); } createOrangeFlash(); // Add score bonus score += 25; combo += 1; }; // --- Connecting Lines between In and Out Buttons --- // Line between inButton and outButtonLeft var lineInToOutLeft = LK.getAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: Math.abs(outButtonLeft.x - inButton.x) - 120, height: 3 }); game.addChild(lineInToOutLeft); lineInToOutLeft.x = (inButton.x + outButtonLeft.x) / 2; lineInToOutLeft.y = inButton.y; lineInToOutLeft.tint = 0x888888; // Gray color // Line between rightInButton and rightOutButton var lineRightInToOut = LK.getAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: Math.abs(rightOutButton.x - rightInButton.x) - 120, height: 3 }); game.addChild(lineRightInToOut); lineRightInToOut.x = (rightInButton.x + rightOutButton.x) / 2; lineRightInToOut.y = rightInButton.y; lineRightInToOut.tint = 0x888888; // Gray color // Add a vertical crossfader track var verticalCrossfaderTrack = LK.getAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: 12, height: 500 }); game.addChild(verticalCrossfaderTrack); verticalCrossfaderTrack.x = djdeckAsset.x - 250; verticalCrossfaderTrack.y = djdeckAsset.y - 100 + 400; // Add a knob onto the vertical crossfader track var verticalCrossfaderKnob = LK.getAsset('crossfaderKnob', { anchorX: 0.5, anchorY: 0.5, rotation: Math.PI / 2 }); game.addChild(verticalCrossfaderKnob); verticalCrossfaderKnob.x = verticalCrossfaderTrack.x; verticalCrossfaderKnob.y = verticalCrossfaderTrack.y; // Add a second vertical crossfader track var verticalCrossfaderTrack2 = LK.getAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: 12, height: 500 }); game.addChild(verticalCrossfaderTrack2); verticalCrossfaderTrack2.x = djdeckAsset.x + 250; // Position on the right side verticalCrossfaderTrack2.y = djdeckAsset.y - 100 + 400; // Add a knob onto the second vertical crossfader track var verticalCrossfaderKnob2 = LK.getAsset('crossfaderKnob', { anchorX: 0.5, anchorY: 0.5, rotation: Math.PI / 2 }); game.addChild(verticalCrossfaderKnob2); verticalCrossfaderKnob2.x = verticalCrossfaderTrack2.x; verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y; // Add filter knobs var leftFilterKnobHigh = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftFilterKnobHigh); leftFilterKnobHigh.x = leftDeckAsset.x + 190 + 400; leftFilterKnobHigh.y = leftDeckAsset.y - 100; var leftFilterKnobMid = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftFilterKnobMid); leftFilterKnobMid.x = leftDeckAsset.x + 190 + 400; leftFilterKnobMid.y = leftDeckAsset.y; var leftFilterKnobLow = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftFilterKnobLow); leftFilterKnobLow.x = leftDeckAsset.x + 190 + 400; leftFilterKnobLow.y = leftDeckAsset.y + 100; var rightFilterKnobHigh = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightFilterKnobHigh); rightFilterKnobHigh.x = rightDeckAsset.x - 190 - 400; rightFilterKnobHigh.y = rightDeckAsset.y - 100; var rightFilterKnobMid = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightFilterKnobMid); rightFilterKnobMid.x = rightDeckAsset.x - 190 - 400; rightFilterKnobMid.y = rightDeckAsset.y; var rightFilterKnobLow = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightFilterKnobLow); rightFilterKnobLow.x = rightDeckAsset.x - 190 - 400; rightFilterKnobLow.y = rightDeckAsset.y + 100; // --- Vertical crossfader state --- var verticalCrossfaderValue = 0.5; // 0 = top, 1 = bottom var verticalCrossfaderDragging = false; // Second vertical crossfader state var verticalCrossfaderValue2 = 0.5; // 0 = top, 1 = bottom var verticalCrossfaderDragging2 = false; var isRgbFlashingActive = false; // State variable for RGB flashing effect var laserShowMode = 0; // Laser show state: 0 = off, 1 = slow, 2 = fast var fireEffectMode = 0; // Fire effect state: 0 = off, 1 = normal flames, 2 = big flames var uvEffectActive = false; // UV effect state: false = off, true = on var uvBackgroundColorTween = null; // Variable to hold the background color tween // Add vertical crossfader functionality with improved touch detection verticalCrossfaderTrack.down = function (x, y, obj) { verticalCrossfaderDragging = true; // Use relative position within track bounds for precise control var relativeY = (y + verticalCrossfaderTrack.height / 2) / verticalCrossfaderTrack.height; verticalCrossfaderValue = Math.max(0, Math.min(1, relativeY)); // Update knob position smoothly verticalCrossfaderKnob.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + verticalCrossfaderValue * verticalCrossfaderTrack.height; }; verticalCrossfaderTrack.up = function (x, y, obj) { verticalCrossfaderDragging = false; }; verticalCrossfaderTrack.move = function (x, y, obj) { if (verticalCrossfaderDragging) { // Use relative position within track bounds for precise control var relativeY = (y + verticalCrossfaderTrack.height / 2) / verticalCrossfaderTrack.height; verticalCrossfaderValue = Math.max(0, Math.min(1, relativeY)); // Update knob position smoothly verticalCrossfaderKnob.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + verticalCrossfaderValue * verticalCrossfaderTrack.height; } }; // Add vertical crossfader functionality for the second crossfader with improved touch detection verticalCrossfaderTrack2.down = function (x, y, obj) { verticalCrossfaderDragging2 = true; // Use relative position within track bounds for precise control var relativeY = (y + verticalCrossfaderTrack2.height / 2) / verticalCrossfaderTrack2.height; verticalCrossfaderValue2 = Math.max(0, Math.min(1, relativeY)); // Update knob position smoothly verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + verticalCrossfaderValue2 * verticalCrossfaderTrack2.height; }; verticalCrossfaderTrack2.up = function (x, y, obj) { verticalCrossfaderDragging2 = false; }; verticalCrossfaderTrack2.move = function (x, y, obj) { if (verticalCrossfaderDragging2) { // Use relative position within track bounds for precise control var relativeY = (y + verticalCrossfaderTrack2.height / 2) / verticalCrossfaderTrack2.height; verticalCrossfaderValue2 = Math.max(0, Math.min(1, relativeY)); // Update knob position smoothly verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + verticalCrossfaderValue2 * verticalCrossfaderTrack2.height; } }; var leftEqualizer = new EqualizerBars(); game.addChild(leftEqualizer); leftEqualizer.x = leftDeckScreen.x; leftEqualizer.y = leftDeckScreen.y + 50; // Add equalizer to right deck screen var rightEqualizer = new EqualizerBars(); game.addChild(rightEqualizer); rightEqualizer.x = rightDeckScreen.x; rightEqualizer.y = rightDeckScreen.y + 50; // Ensure deck platters are always at the top of the display order if (game.children.indexOf(leftDeck) !== -1) { game.setChildIndex(leftDeck, game.children.length - 1); } if (game.children.indexOf(rightDeck) !== -1) { game.setChildIndex(rightDeck, game.children.length - 1); } // --- Game update --- game.update = function () { // Update discoball animation discoball.update(); // Update fog effect animation fogEffect.update(); // Update confetti effect animation confettiEffect.update(); // Update balloons animation if (balloons) { for (var i = 0; i < balloons.length; i++) { balloons[i].update(); } } // Update money particles animation if (moneyParticles) { for (var i = 0; i < moneyParticles.length; i++) { moneyParticles[i].update(); } } // Update first dancing people line animations (first set created) for (var i = 0; i < Math.min(dancingPeople.length, 8); i++) { var person = dancingPeople[i]; // Check if UV effect is active if (uvEffectActive) { // If not already a UV person, swap to UV asset if (!person.isUV) { var uvAssetId = i % 2 === 0 ? 'UVW' : 'UWM'; var currentScaleX = person.scale.x; var currentScaleY = person.scale.y; var currentRotation = person.rotation; var currentAlpha = person.alpha; var currentX = person.x; var currentY = person.y; var currentBaseY = person.baseY; var currentPhaseOffset = person.phaseOffset; // Remove the old graphic child if (person.children.length > 0) { person.removeChildAt(0); } // Add the new UV graphic child var uvGraphic = person.attachAsset(uvAssetId, { anchorX: 0.5, anchorY: 1.0 }); // Restore previous transform and animation properties person.scale.set(currentScaleX, currentScaleY); person.rotation = currentRotation; person.alpha = currentAlpha; person.x = currentX; person.y = currentY; person.baseY = currentBaseY; person.phaseOffset = currentPhaseOffset; person.isUV = true; } } else { // If UV effect is off and it's a UV person, swap back to normal asset if (person.isUV) { var normalAssetId = i % 2 === 0 ? 'dancingWoman' : 'dancingMan'; var currentScaleX = person.scale.x; var currentScaleY = person.scale.y; var currentRotation = person.rotation; var currentAlpha = person.alpha; var currentX = person.x; var currentY = person.y; var currentBaseY = person.baseY; var currentPhaseOffset = person.phaseOffset; // Remove the old graphic child if (person.children.length > 0) { person.removeChildAt(0); } // Add the new normal graphic child var normalGraphic = person.attachAsset(normalAssetId, { anchorX: 0.5, anchorY: 1.0 }); // Restore previous transform and animation properties person.scale.set(currentScaleX, currentScaleY); person.rotation = currentRotation; person.alpha = currentAlpha; person.x = currentX; person.y = currentY; person.baseY = currentBaseY; person.phaseOffset = currentPhaseOffset; person.isUV = false; } } person.update(); } // Update second dancing people line animations (second set created) - swap with UV assets when UV is active for (var i = 8; i < dancingPeople.length; i++) { var person = dancingPeople[i]; // Check if UV effect is active if (uvEffectActive) { // If not already a UV person, swap to UV asset if (!person.isUV) { var uvAssetId = (i - 8) % 2 === 0 ? 'UVW' : 'UWM'; var currentScaleX = person.scale.x; var currentScaleY = person.scale.y; var currentRotation = person.rotation; var currentAlpha = person.alpha; var currentX = person.x; var currentY = person.y; var currentBaseY = person.baseY; var currentPhaseOffset = person.phaseOffset; // Remove the old graphic child if (person.children.length > 0) { person.removeChildAt(0); } // Add the new UV graphic child var uvGraphic = person.attachAsset(uvAssetId, { anchorX: 0.5, anchorY: 1.0 }); // Restore previous transform and animation properties person.scale.set(currentScaleX, currentScaleY); person.rotation = currentRotation; person.alpha = currentAlpha; person.x = currentX; person.y = currentY; person.baseY = currentBaseY; person.phaseOffset = currentPhaseOffset; person.isUV = true; } } else { // If UV effect is off and it's a UV person, swap back to normal asset if (person.isUV) { var normalAssetId = (i - 8) % 2 === 0 ? 'dancingWoman' : 'dancingMan'; var currentScaleX = person.scale.x; var currentScaleY = person.scale.y; var currentRotation = person.rotation; var currentAlpha = person.alpha; var currentX = person.x; var currentY = person.y; var currentBaseY = person.baseY; var currentPhaseOffset = person.phaseOffset; // Remove the old graphic child if (person.children.length > 0) { person.removeChildAt(0); } // Add the new normal graphic child var normalGraphic = person.attachAsset(normalAssetId, { anchorX: 0.5, anchorY: 1.0 }); // Restore previous transform and animation properties person.scale.set(currentScaleX, currentScaleY); person.rotation = currentRotation; person.alpha = currentAlpha; person.x = currentX; person.y = currentY; person.baseY = currentBaseY; person.phaseOffset = currentPhaseOffset; person.isUV = false; } } person.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 laser show animation laserShow.update(); // Update fireworks effect animation fireworksEffect.update(); fireworksEffect2.update(); fireworksEffect3.update(); fireworksEffect4.update(); // Update Ferris wheel animation if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) { // Defensive check ferrisWheel.update(); } // Update smoke diffuser animation // --- Synchronize all smoke diffusers so they emit smoke puffs at the same time --- if (typeof smokeDiffusers !== 'undefined' && smokeDiffusers.length > 0) { // Use the first smoke diffuser as the "master" for timing var now = Date.now(); // All diffusers share the same "now" and will create puffs in sync for (var i = 0; i < smokeDiffusers.length; i++) { var sd = smokeDiffusers[i]; // Patch: override the update method to use the same "now" for all if (sd && typeof sd.update === 'function') { // Save original Date.now var origDateNow = Date.now; Date.now = function () { return now; }; sd.update(); Date.now = origDateNow; } } } // Update flamethrower animation if (typeof flamethrower !== 'undefined' && flamethrower.update) { flamethrower.update(); } // Update decks leftDeck.update(); rightDeck.update(); // Update equalizers leftEqualizer.update(); rightEqualizer.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) // Only animate the background color if RGB flashing is active on the third effect button if (isRgbFlashingActive) { game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]); } else if (uvEffectActive) { // If UV effect is active, flash between indigo blue and purple var uvTime = Date.now() * 0.002; // Faster flashing for UV var uvHue = uvTime * 360 % 360; // Cycle between indigo (around 240 hue) and purple (around 270 hue) var targetHue = 240 + Math.sin(uvTime * Math.PI * 2) * 30; targetHue = (targetHue + 360) % 360; var uvRgb = hsvToRgb(targetHue / 360, 0.8, 0.7); // Adjust saturation and value for UV colors game.setBackgroundColor(uvRgb[0] << 16 | uvRgb[1] << 8 | uvRgb[2]); } else { // If neither is active, set a default background color game.setBackgroundColor(0x000000); // Or any other default color } // Win condition disabled // --- Update Digital Clock --- if (typeof digitalClockTxt !== 'undefined') { var now = new Date(); digitalClockTxt.setText(formatTime(now)); } // Display laser flashing effect on the second effect button's frame based on mode if (effectButtons[1]) { if (laserShowMode === 0) { // Off mode - normal white tint effectButtons[1].tint = 0xFFFFFF; } else if (laserShowMode === 1) { // Slow mode - slow blue pulsing var time = Date.now() * 0.0008; // Slow pulsing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var blueValue = Math.floor(intensity * 255); effectButtons[1].tint = 0x4444ff | blueValue << 8 | blueValue; } else if (laserShowMode === 2) { // Fast mode - slower rainbow cycling var time = Date.now() * 0.0008; // Slower cycling (reduced from 0.002) var laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var colorIndex = Math.floor(time * 3) % laserColors.length; // Slower color cycling (reduced from 8 to 3) effectButtons[1].tint = laserColors[colorIndex]; } } // Display fire effect flashing on the first effect button's frame based on mode if (effectButtons[0]) { if (fireEffectMode === 0) { // Off mode - normal white tint effectButtons[0].tint = 0xFFFFFF; } else if (fireEffectMode === 1) { // Normal flames mode - orange flashing var time = Date.now() * 0.002; // Medium speed flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var redValue = Math.floor(255); var greenValue = Math.floor(intensity * 128 + 64); // Orange tint var blueValue = 0; effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue; } else if (fireEffectMode === 2) { // Big flames mode - red flashing var time = Date.now() * 0.003; // Faster flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var redValue = Math.floor(255); var greenValue = Math.floor(intensity * 64); // More red tint var blueValue = Math.floor(intensity * 64); effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue; } } // Display RGB flashing effect on the third effect button's frame if active if (isRgbFlashingActive && effectButtons[2]) { var time = Date.now() * 0.0005; // Time for animation, even slower var hue = time * 360 % 360; // Cycle through hues var rgb = hsvToRgb(hue / 360, 1.0, 1.0); // Full saturation and value for vibrant flash effectButtons[2].tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2]; } else if (effectButtons[2]) { // If not active, set the tint back to white effectButtons[2].tint = 0xFFFFFF; } // --- UV button continuous purple flashing effect --- if (effectButtons[4]) { if (uvEffectActive) { // Animate between indigo and purple var uvBtnTime = Date.now() * 0.0015; // Slightly faster for button, but slower flashing for button // Indigo: #4B0082 (0x4B0082), Purple: #8000FF (0x8000FF) var t = 0.5 + 0.5 * Math.sin(uvBtnTime * Math.PI * 2); // Interpolate RGB var r = Math.round(0x4B * (1 - t) + 0x80 * t); var g = Math.round(0x00 * (1 - t) + 0x00 * t); var b = Math.round(0x82 * (1 - t) + 0xFF * t); effectButtons[4].tint = r << 16 | g << 8 | b; } else { effectButtons[4].tint = 0xFFFFFF; } } // Manage gifts effects based on giftsMode if (balloons) { for (var i = 0; i < balloons.length; i++) { balloons[i].update(); // Only show balloons if giftsMode is 1 (balloons mode) balloons[i].visible = giftsMode === 1; } } if (moneyParticles) { for (var i = 0; i < moneyParticles.length; i++) { moneyParticles[i].update(); // Only show money particles if giftsMode is 2 (money mode) moneyParticles[i].visible = giftsMode === 2; } } // Manage fireworks visibility (fireworks are spread across multiple instances) if (fireworksEffect) fireworksEffect.visible = giftsMode === 3; if (fireworksEffect2) fireworksEffect2.visible = giftsMode === 3; if (fireworksEffect3) fireworksEffect3.visible = giftsMode === 3; if (fireworksEffect4) fireworksEffect4.visible = giftsMode === 3; // Update fireworks effect animation only if visible if (fireworksEffect && fireworksEffect.visible) fireworksEffect.update(); if (fireworksEffect2 && fireworksEffect2.visible) fireworksEffect2.update(); if (fireworksEffect3 && fireworksEffect3.visible) fireworksEffect3.update(); if (fireworksEffect4 && fireworksEffect4.visible) fireworksEffect4.update(); // Update gifts button visual feedback based on mode if (giftsEffectButton) { tween.stop(giftsEffectButton, { tint: true }); // Stop any previous tint tweens if (giftsMode === 0) { // Off mode - no light (white tint) giftsEffectButton.tint = 0xFFFFFF; } else if (giftsMode === 1) { // Balloons mode - flashing pink var time = Date.now() * 0.002; // Medium speed flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var pinkValue = Math.floor(intensity * 255); giftsEffectButton.tint = 0xFF00FF | pinkValue << 8 | pinkValue; // Magenta/Pink } else if (giftsMode === 2) { // Money mode - green color giftsEffectButton.tint = 0x00FF00; // Green } else if (giftsMode === 3) { // Fireworks mode - flashing gold var time = Date.now() * 0.002; // Medium speed flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var goldR = Math.floor(intensity * 255); var goldG = Math.floor(intensity * 215); var goldB = Math.floor(intensity * 0); giftsEffectButton.tint = goldR << 16 | goldG << 8 | goldB; // Gold/Yellowish } } }; tabletAsset.x = (leftDeckScreen.x + rightDeckScreen.x) / 2; tabletAsset.y = leftDeckScreen.y + 77 + 35 - 20 - 200 - 35; // --- Arrow Buttons under Tablet --- var arrowButtonSpacing = 150; var arrowButtonY = tabletAsset.y + tabletAsset.height / 2 + 80; var leftArrowButton = LK.getAsset('Leftarrow', { anchorX: 0.5, anchorY: 0.5 }); leftArrowButton.x = tabletAsset.x - arrowButtonSpacing * 1.5 - 50 - 5 - 5 - 1; leftArrowButton.y = arrowButtonY; game.addChild(leftArrowButton); // Scale up left arrow button by 1.3 tween(leftArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut }); var upArrowButton = LK.getAsset('UParrow', { anchorX: 0.5, anchorY: 0.5 }); upArrowButton.x = tabletAsset.x + arrowButtonSpacing * 1.5 - 50 - 5 - 12 - 5 - 1; upArrowButton.y = arrowButtonY; game.addChild(upArrowButton); // Scale up up arrow button by 1.3 tween(upArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut }); var downArrowButton = LK.getAsset('Downarrow', { anchorX: 0.5, anchorY: 0.5 }); downArrowButton.x = tabletAsset.x + arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1; downArrowButton.y = arrowButtonY; game.addChild(downArrowButton); // Scale up down arrow button by 1.3 tween(downArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut }); var rightArrowButton = LK.getAsset('Rightarrow', { anchorX: 0.5, anchorY: 0.5 }); rightArrowButton.x = tabletAsset.x - arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1; rightArrowButton.y = arrowButtonY; game.addChild(rightArrowButton); // Scale up right arrow button by 1.3 tween(rightArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut }); var okButton = LK.getAsset('OKbutton', { anchorX: 0.5, anchorY: 0.5 }); // Place OK button to the right of the right arrow button with the same spacing as between arrow buttons okButton.x = upArrowButton.x + arrowButtonSpacing - 12 - 5 - 1 + 2 + 2; okButton.y = arrowButtonY; game.addChild(okButton); // Scale up OK button by 1.3 to match arrow buttons tween(okButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut }); // Add edit button under the OK button var editButton = LK.getAsset('OKbutton', { anchorX: 0.5, anchorY: 0.5 }); editButton.x = okButton.x; editButton.y = okButton.y + okButton.height + 20; // Position 20 units under the OK button game.addChild(editButton); // Scale up edit button by 1.3 to match arrow buttons tween(editButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, easing: tween.easeOut }); // --- Pulse Button Effect State --- var pulseButtonEffect = 1.0; // 1.0 = normal, <1.0 = decreased // Helper to decrease pulse effect function decreasePulseButtonEffect() { pulseButtonEffect = 0.5; // Optionally, restore after a short time for visual feedback LK.setTimeout(function () { pulseButtonEffect = 1.0; }, 200); } // --- Add decrease pulse effect to arrow and OK buttons --- leftArrowButton.down = function (x, y, obj) { decreasePulseButtonEffect(); // Navigate left in rekordbox app if (typeof rekordboxApp !== 'undefined') { rekordboxApp.navigateLeft(); } // Visual feedback tween(leftArrowButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80, onFinish: function onFinish() { tween(leftArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120 }); } }); }; rightArrowButton.down = function (x, y, obj) { decreasePulseButtonEffect(); // Navigate right in rekordbox app if (typeof rekordboxApp !== 'undefined') { rekordboxApp.navigateRight(); } tween(rightArrowButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80, onFinish: function onFinish() { tween(rightArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120 }); } }); }; upArrowButton.down = function (x, y, obj) { decreasePulseButtonEffect(); // Navigate up in rekordbox app if (typeof rekordboxApp !== 'undefined') { rekordboxApp.navigateUp(); } tween(upArrowButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80, onFinish: function onFinish() { tween(upArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120 }); } }); }; downArrowButton.down = function (x, y, obj) { decreasePulseButtonEffect(); // Navigate down in rekordbox app if (typeof rekordboxApp !== 'undefined') { rekordboxApp.navigateDown(); } tween(downArrowButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80, onFinish: function onFinish() { tween(downArrowButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120 }); } }); }; okButton.down = function (x, y, obj) { decreasePulseButtonEffect(); // Select track in rekordbox app if (typeof rekordboxApp !== 'undefined') { rekordboxApp.selectTrack(); } tween(okButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80, onFinish: function onFinish() { tween(okButton, { scaleX: 1.3, scaleY: 1.3 }, { duration: 120 }); } }); }; // Update second dancing people line animations (second set created) - swap with UV assets when UV is active for (var i = 8; i < dancingPeople.length; i++) { var person = dancingPeople[i]; // Check if UV effect is active if (uvEffectActive) { // If not already a UV person, swap to UV asset if (!person.isUV) { var uvAssetId = (i - 8) % 2 === 0 ? 'UVW' : 'UWM'; var currentScaleX = person.scale.x; var currentScaleY = person.scale.y; var currentRotation = person.rotation; var currentAlpha = person.alpha; var currentX = person.x; var currentY = person.y; var currentBaseY = person.baseY; var currentPhaseOffset = person.phaseOffset; // Remove the old graphic child if (person.children.length > 0) { person.removeChildAt(0); } // Add the new UV graphic child var uvGraphic = person.attachAsset(uvAssetId, { anchorX: 0.5, anchorY: 1.0 }); // Restore previous transform and animation properties person.scale.set(currentScaleX, currentScaleY); person.rotation = currentRotation; person.alpha = currentAlpha; person.x = currentX; person.y = currentY; person.baseY = currentBaseY; person.phaseOffset = currentPhaseOffset; person.isUV = true; } } else { // If UV effect is off and it's a UV person, swap back to normal asset if (person.isUV) { var normalAssetId = (i - 8) % 2 === 0 ? 'dancingWoman' : 'dancingMan'; var currentScaleX = person.scale.x; var currentScaleY = person.scale.y; var currentRotation = person.rotation; var currentAlpha = person.alpha; var currentX = person.x; var currentY = person.y; var currentBaseY = person.baseY; var currentPhaseOffset = person.phaseOffset; // Remove the old graphic child if (person.children.length > 0) { person.removeChildAt(0); } // Add the new normal graphic child var normalGraphic = person.attachAsset(normalAssetId, { anchorX: 0.5, anchorY: 1.0 }); // Restore previous transform and animation properties person.scale.set(currentScaleX, currentScaleY); person.rotation = currentRotation; person.alpha = currentAlpha; person.x = currentX; person.y = currentY; person.baseY = currentBaseY; person.phaseOffset = currentPhaseOffset; person.isUV = false; } } person.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 laser show animation laserShow.update(); // Update fireworks effect animation fireworksEffect.update(); fireworksEffect2.update(); fireworksEffect3.update(); fireworksEffect4.update(); // Update Ferris wheel animation if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) { // Defensive check ferrisWheel.update(); } // Update smoke diffuser animation // --- Synchronize all smoke diffusers so they emit smoke puffs at the same time --- if (typeof smokeDiffusers !== 'undefined' && smokeDiffusers.length > 0) { // Use the first smoke diffuser as the "master" for timing var now = Date.now(); // All diffusers share the same "now" and will create puffs in sync for (var i = 0; i < smokeDiffusers.length; i++) { var sd = smokeDiffusers[i]; // Patch: override the update method to use the same "now" for all if (sd && typeof sd.update === 'function') { // Save original Date.now var origDateNow = Date.now; Date.now = function () { return now; }; sd.update(); Date.now = origDateNow; } } } // Update flamethrower animation if (typeof flamethrower !== 'undefined' && flamethrower.update) { flamethrower.update(); } // Update decks leftDeck.update(); rightDeck.update(); // Update equalizers leftEqualizer.update(); rightEqualizer.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) // Only animate the background color if RGB flashing is active on the third effect button if (isRgbFlashingActive) { game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]); } else if (uvEffectActive) { // If UV effect is active, flash between indigo blue and purple var uvTime = Date.now() * 0.002; // Faster flashing for UV var uvHue = uvTime * 360 % 360; // Cycle between indigo (around 240 hue) and purple (around 270 hue) var targetHue = 240 + Math.sin(uvTime * Math.PI * 2) * 30; targetHue = (targetHue + 360) % 360; var uvRgb = hsvToRgb(targetHue / 360, 0.8, 0.7); // Adjust saturation and value for UV colors game.setBackgroundColor(uvRgb[0] << 16 | uvRgb[1] << 8 | uvRgb[2]); } else { // If neither is active, set a default background color game.setBackgroundColor(0x000000); // Or any other default color } // Win condition disabled // --- Update Digital Clock --- if (typeof digitalClockTxt !== 'undefined') { var now = new Date(); digitalClockTxt.setText(formatTime(now)); } // Display laser flashing effect on the second effect button's frame based on mode if (effectButtons[1]) { if (laserShowMode === 0) { // Off mode - normal white tint effectButtons[1].tint = 0xFFFFFF; } else if (laserShowMode === 1) { // Slow mode - slow blue pulsing var time = Date.now() * 0.0008; // Slow pulsing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var blueValue = Math.floor(intensity * 255); effectButtons[1].tint = 0x4444ff | blueValue << 8 | blueValue; } else if (laserShowMode === 2) { // Fast mode - slower rainbow cycling var time = Date.now() * 0.0008; // Slower cycling (reduced from 0.002) var laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var colorIndex = Math.floor(time * 3) % laserColors.length; // Slower color cycling (reduced from 8 to 3) effectButtons[1].tint = laserColors[colorIndex]; } } // Display fire effect flashing on the first effect button's frame based on mode if (effectButtons[0]) { if (fireEffectMode === 0) { // Off mode - normal white tint effectButtons[0].tint = 0xFFFFFF; } else if (fireEffectMode === 1) { // Normal flames mode - orange flashing var time = Date.now() * 0.002; // Medium speed flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var redValue = Math.floor(255); var greenValue = Math.floor(intensity * 128 + 64); // Orange tint var blueValue = 0; effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue; } else if (fireEffectMode === 2) { // Big flames mode - red flashing var time = Date.now() * 0.003; // Faster flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var redValue = Math.floor(255); var greenValue = Math.floor(intensity * 64); // More red tint var blueValue = Math.floor(intensity * 64); effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue; } } // Display RGB flashing effect on the third effect button's frame if active if (isRgbFlashingActive && effectButtons[2]) { var time = Date.now() * 0.0005; // Time for animation, even slower var hue = time * 360 % 360; // Cycle through hues var rgb = hsvToRgb(hue / 360, 1.0, 1.0); // Full saturation and value for vibrant flash effectButtons[2].tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2]; } else if (effectButtons[2]) { // If not active, set the tint back to white effectButtons[2].tint = 0xFFFFFF; } // --- UV button continuous purple flashing effect --- if (effectButtons[4]) { if (uvEffectActive) { // Animate between indigo and purple var uvBtnTime = Date.now() * 0.0015; // Slightly faster for button, but slower flashing for button // Indigo: #4B0082 (0x4B0082), Purple: #8000FF (0x8000FF) var t = 0.5 + 0.5 * Math.sin(uvBtnTime * Math.PI * 2); // Interpolate RGB var r = Math.round(0x4B * (1 - t) + 0x80 * t); var g = Math.round(0x00 * (1 - t) + 0x00 * t); var b = Math.round(0x82 * (1 - t) + 0xFF * t); effectButtons[4].tint = r << 16 | g << 8 | b; } else { effectButtons[4].tint = 0xFFFFFF; } } // Manage gifts effects based on giftsMode if (balloons) { for (var i = 0; i < balloons.length; i++) { balloons[i].update(); // Only show balloons if giftsMode is 1 (balloons mode) balloons[i].visible = giftsMode === 1; } } if (moneyParticles) { for (var i = 0; i < moneyParticles.length; i++) { moneyParticles[i].update(); // Only show money particles if giftsMode is 2 (money mode) moneyParticles[i].visible = giftsMode === 2; } } // Manage fireworks visibility (fireworks are spread across multiple instances) if (fireworksEffect) fireworksEffect.visible = giftsMode === 3; if (fireworksEffect2) fireworksEffect2.visible = giftsMode === 3; if (fireworksEffect3) fireworksEffect3.visible = giftsMode === 3; if (fireworksEffect4) fireworksEffect4.visible = giftsMode === 3; // Update fireworks effect animation only if visible if (fireworksEffect && fireworksEffect.visible) fireworksEffect.update(); if (fireworksEffect2 && fireworksEffect2.visible) fireworksEffect2.update(); if (fireworksEffect3 && fireworksEffect3.visible) fireworksEffect3.update(); if (fireworksEffect4 && fireworksEffect4.visible) fireworksEffect4.update(); // Update gifts button visual feedback based on mode if (giftsEffectButton) { tween.stop(giftsEffectButton, { tint: true }); // Stop any previous tint tweens if (giftsMode === 0) { // Off mode - no light (white tint) giftsEffectButton.tint = 0xFFFFFF; } else if (giftsMode === 1) { // Balloons mode - flashing pink var time = Date.now() * 0.002; // Medium speed flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var pinkValue = Math.floor(intensity * 255); giftsEffectButton.tint = 0xFF00FF | pinkValue << 8 | pinkValue; // Magenta/Pink } else if (giftsMode === 2) { // Money mode - green color giftsEffectButton.tint = 0x00FF00; // Green } else if (giftsMode === 3) { // Fireworks mode - flashing gold var time = Date.now() * 0.002; // Medium speed flashing var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2); var goldR = Math.floor(intensity * 255); var goldG = Math.floor(intensity * 215); var goldB = Math.floor(intensity * 0); giftsEffectButton.tint = goldR << 16 | goldG << 8 | goldB; // Gold/Yellowish } } okButton.y = arrowButtonY + 12 + 30; // Move OK button down by 30 units game.addChild(okButton); leftArrowButton.x = tabletAsset.x - arrowButtonSpacing * 1.5 - 50 - 5 - 5 - 1; leftArrowButton.y = arrowButtonY + 12 + 30; // Move left arrow down by 30 units game.addChild(leftArrowButton); upArrowButton.x = tabletAsset.x + arrowButtonSpacing * 1.5 - 50 - 5 - 12 - 5 - 1; upArrowButton.y = arrowButtonY + 12 + 30; // Move up arrow down by 30 units game.addChild(upArrowButton); downArrowButton.x = tabletAsset.x + arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1; downArrowButton.y = arrowButtonY + 12 + 30; // Move down arrow down by 30 units game.addChild(downArrowButton); rightArrowButton.x = tabletAsset.x - arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1; rightArrowButton.y = arrowButtonY + 12 + 30; // Move right arrow down by 30 units game.addChild(rightArrowButton); // Update edit button position in update loop if (typeof editButton !== 'undefined' && typeof okButton !== 'undefined') { editButton.x = okButton.x; editButton.y = okButton.y + okButton.height + 20 + 12; // Move down by 12 units } ; ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Playback starts automatically
// Balloon: Animated colorful balloons falling from the top
var Balloon = Container.expand(function () {
var self = Container.call(this);
// Config
var size = 60 + Math.random() * 80; // Random size
var speedY = 0.8 + Math.random() * 1.2; // Vertical speed
var speedX = (Math.random() - 0.5) * 2; // Horizontal drift
var rotationSpeed = (Math.random() - 0.5) * 0.05; // Rotation speed
// Visuals
// Use a more realistic width/height ratio for balloons (e.g. 0.55)
var balloonWidth = size * 0.55;
var balloonHeight = size;
var balloonGraphic = self.attachAsset('balloon', {
anchorX: 0.5,
anchorY: 0.5,
width: balloonWidth,
height: balloonHeight
});
// Random color (rainbow)
var hue = Math.random();
var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
balloonGraphic.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
// HSV to RGB helper (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);
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)];
}
// Animation
self.update = function () {
self.y += speedY;
self.x += speedX;
self.rotation += rotationSpeed;
// Wrap around if off screen (below the dancing people)
if (self.y > dancingPeople[0].y + 200) {
// Wrap below the dancing people
self.y = -size;
self.x = Math.random() * 2048; // New random x position
// Reset size and color for a new balloon
size = 60 + Math.random() * 80;
var balloonWidth = size * 0.55;
var balloonHeight = size;
balloonGraphic.width = balloonWidth; // Maintain realistic balloon width/height ratio
balloonGraphic.height = balloonHeight;
var hue = Math.random();
var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
balloonGraphic.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
speedY = 0.8 + Math.random() * 1.2;
speedX = (Math.random() - 0.5) * 2;
rotationSpeed = (Math.random() - 0.5) * 0.05;
}
};
return self;
});
// ConfettiEffect: Animated confetti falling from the top with multiple types
var ConfettiEffect = Container.expand(function () {
var self = Container.call(this);
// Config
var numParticles = 200; // Increased from 120 to 200 for much higher density
var particles = [];
var particleMinSize = 3.0; // Slightly larger minimum size
var particleMaxSize = 12.0; // Increased maximum size
var speedY = 5; // pixels per second
var speedX = 2; // horizontal drift
// Define confetti types with more rectangle emphasis
var confettiTypes = [{
asset: 'centerCircle',
name: 'circle'
}, {
asset: 'crossfaderTrack',
name: 'rectangle'
}, {
asset: 'crossfaderTrack',
name: 'rectangle'
}, {
asset: 'crossfaderTrack',
name: 'rectangle'
}, {
asset: 'crossfaderTrack',
name: 'rectangle'
}, {
asset: 'beatLight',
name: 'star'
}];
// Create confetti particles
for (var i = 0; i < numParticles; i++) {
var size = particleMinSize + Math.random() * (particleMaxSize - particleMinSize);
// Choose random confetti type
var confettiType = confettiTypes[Math.floor(Math.random() * confettiTypes.length)];
var particle;
if (confettiType.name === 'rectangle') {
// Create rectangular confetti with more variety
var rectWidth = size * (1.2 + Math.random() * 0.8); // More width variation (1.2x to 2.0x)
var rectHeight = size * (0.4 + Math.random() * 0.6); // More height variation (0.4x to 1.0x)
particle = LK.getAsset(confettiType.asset, {
anchorX: 0.5,
anchorY: 0.5,
width: rectWidth,
height: rectHeight
});
} else if (confettiType.name === 'star') {
// Create star-shaped confetti
particle = LK.getAsset(confettiType.asset, {
anchorX: 0.5,
anchorY: 0.5,
width: size * 0.8,
height: size * 0.8
});
} else {
// Create circular confetti
particle = LK.getAsset(confettiType.asset, {
anchorX: 0.5,
anchorY: 0.5,
width: size,
height: size
});
}
// Random color (rainbow)
var hue = Math.random();
var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.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,
type: confettiType.name,
baseSize: size,
rotationSpeed: (Math.random() - 0.5) * 0.3 // Random rotation speed
});
}
// 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
// Reset rotation and add tween effect for new particles
p.obj.rotation = 0;
tween(p.obj, {
rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1)
}, {
duration: 2000 + Math.random() * 3000,
easing: tween.linear
});
}
// Enhanced rotation based on type
if (p.type === 'rectangle') {
p.obj.rotation += p.rotationSpeed * 1.5; // Rectangles rotate faster
} else if (p.type === 'star') {
p.obj.rotation += p.rotationSpeed * 0.8; // Stars rotate slower
} else {
p.obj.rotation += p.rotationSpeed; // Circles normal rotation
}
// Type-specific animations
if (p.type === 'rectangle') {
// Rectangles flutter and tumble more dramatically
var flutter = 0.8 + 0.4 * Math.sin(t * 5 + p.phase);
var tumble = 0.9 + 0.2 * Math.cos(t * 3 + p.phase);
p.obj.scaleX = flutter;
p.obj.scaleY = tumble;
// Add extra horizontal drift for rectangles
p.obj.x += Math.sin(t * 2 + p.phase) * 0.8;
} else if (p.type === 'star') {
// Stars twinkle
var twinkle = 0.9 + 0.2 * Math.sin(t * 6 + p.phase);
p.obj.scaleX = twinkle;
p.obj.scaleY = twinkle;
// Add alpha twinkling
p.obj.alpha = 0.8 + 0.2 * Math.sin(t * 5 + p.phase);
} else {
// Circles flicker size
var baseSize = p.baseSize;
var s = baseSize * (0.95 + 0.1 * 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;
};
// Touch events
self.down = function (x, y, obj) {
self.dragging = true;
self.setValue((x + track.width / 2) / 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 / 2) / track.width);
}
};
return self;
});
// DancingPerson: Animated dancing people (women and men)
var DancingPerson = Container.expand(function (forcedAssetId) {
var self = Container.call(this);
// Config
var assetId;
self.isUV = false; // Add a property to track if this is a UV person
if (typeof forcedAssetId === 'string') {
assetId = forcedAssetId;
self.isUV = forcedAssetId === 'UVW' || forcedAssetId === 'UWM';
} else {
// fallback to random if not provided
var assets = ['dancingWoman', 'dancingMan'];
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;
// Move highlight behind the platter by adding it first, then re-adding platter
self.removeChild(platter);
self.addChild(platter);
var label = self.attachAsset('deckLabel', {
anchorX: 0.5,
anchorY: 0.5,
y: 350,
width: 90,
height: 30
});
// 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: 490
});
beatLight.alpha = 0.2;
// Methods
self.setActive = function (active) {
self.isActive = active;
if (active) {
highlight.alpha = 0.6;
highlight.visible = true;
// Ensure highlight is above platter in display order
self.removeChild(highlight);
self.addChild(highlight);
// Flash effect for better visual feedback
tween(highlight, {
alpha: 0.3
}, {
duration: 200,
easing: tween.easeOut
});
} else {
highlight.alpha = 0;
highlight.visible = false;
}
};
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 using platter dimensions
var dx = x - platter.width / 2;
var dy = y - platter.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 using platter dimensions
var dx = x - platter.width / 2;
var dy = y - platter.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 () {
// Determine if this deck should be spinning based on start button state
var shouldSpin = false;
if (self.track === 'A' && typeof leftStartButtonOn !== 'undefined' && leftStartButtonOn) {
shouldSpin = true;
} else if (self.track === 'B' && typeof rightStartButtonOn !== 'undefined' && rightStartButtonOn) {
shouldSpin = true;
}
// If not scratching, platter rotates at normal speed only if should spin
if (!self.isScratching && self.playing && shouldSpin) {
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;
});
// EqualizerBars: Animated frequency visualization bars for screens
var EqualizerBars = Container.expand(function () {
var self = Container.call(this);
// Config
var numBars = 12;
var barWidth = 25 * 1.2;
var barSpacing = 8;
var maxBarHeight = 100;
var minBarHeight = 10;
var bars = [];
// Create equalizer bars
for (var i = 0; i < numBars; i++) {
var bar = LK.getAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 1.0,
width: barWidth,
height: minBarHeight
});
// Color based on frequency range (bass=red, mid=yellow, treble=cyan)
if (i < 4) {
bar.tint = 0xff0000; // Bass - red
} else if (i < 8) {
bar.tint = 0xffff00; // Mid - yellow
} else {
bar.tint = 0x00ffff; // Treble - cyan
}
bar.x = (i - (numBars - 1) / 2) * (barWidth + barSpacing);
bar.y = 0;
self.addChild(bar);
bars.push({
obj: bar,
phase: Math.random() * Math.PI * 2,
baseHeight: minBarHeight
});
}
// Animation
self.update = function () {
var t = Date.now() * 0.001;
for (var i = 0; i < bars.length; i++) {
var b = bars[i];
// Simulate frequency response with different speeds for different frequency ranges
var speed = 1.0;
if (i < 4) speed = 0.8; // Bass moves slower
else if (i < 8) speed = 1.2; // Mid moves medium
else speed = 1.8; // Treble moves faster
// Create realistic frequency bar movement
var height = minBarHeight + (maxBarHeight - minBarHeight) * (0.3 + 0.7 * Math.abs(Math.sin(t * speed * 2 + b.phase + i * 0.3)));
b.obj.height = height;
// Add beat sync flash effect
if (beatTimer && beatTimer < 100) {
b.obj.alpha = 0.7 + 0.3 * Math.sin(t * 10 + i);
} else {
b.obj.alpha = 0.9;
}
}
};
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 = 8; // Number of simultaneous rockets/explosions - increased from 4 to 8
var rocketInterval = 800; // ms between new rockets - reduced from 1500 to 800
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 = 20 + Math.random() * 10;
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 = 20;
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 baseSize = (particleMinSize + Math.random() * (particleMaxSize - particleMinSize)) * 0.48;
var size = baseSize;
// Modify size based on fire effect mode
if (typeof fireEffectMode !== 'undefined' && fireEffectMode === 2) {
// Big flames mode - make particles larger
size = baseSize * 2.5;
}
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 based on fire effect mode
var targetParticleCount = numParticles;
if (typeof fireEffectMode !== 'undefined') {
if (fireEffectMode === 0) {
// Off mode - no flames
targetParticleCount = 0;
} else if (fireEffectMode === 2) {
// Big flames mode - more particles
targetParticleCount = numParticles * 2;
}
}
if (particles.length < targetParticleCount) {
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 = 20;
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 () {
// Only animate if fog animation is active
if (typeof fogAnimationActive !== 'undefined' && !fogAnimationActive) {
// Hide all particles when fog animation is not active
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.obj.alpha = 0;
}
return;
}
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;
});
// KeyboardApp: Simple keyboard app for the tablet
var KeyboardApp = Container.expand(function () {
var self = Container.call(this);
// Config
var keyboardWidth = 742;
var keyboardHeight = 250;
var keyWidth = 50;
var keyHeight = 50;
var keySpacing = 8;
var keys = [];
var keyLabels = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']];
var shiftLabels = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'], ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'], ['z', 'x', 'c', 'v', 'b', 'n', 'm']];
var symbolLabels = [['!', '@', '#', '$', '%', '^', '&', '*', '(', ')'], [',', '-', '.', '/', '+', ':', ';', '<', '>'], ['_', '=', '|', '\\', '{', '}', '[', ']'], ['é', 'ú', 'ü', 'ű', 'í', 'ó', 'ö', 'ő', 'á']];
self.isSymbolsMode = false; // Track if symbols mode is active
self.isShiftMode = false; // Track if shift mode is active
// Create keyboard background
var keyboardBg = self.attachAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: keyboardWidth,
height: keyboardHeight
});
keyboardBg.tint = 0x333333;
// Create keys
var startX = (keyboardWidth - (keyLabels[0].length * (keyWidth + keySpacing) - keySpacing)) / 2;
var startY = 20;
for (var row = 0; row < keyLabels.length; row++) {
var rowLabels = keyLabels[row];
var rowStartX = (keyboardWidth - (rowLabels.length * (keyWidth + keySpacing) - keySpacing)) / 2;
for (var col = 0; col < rowLabels.length; col++) {
var keyBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: keyWidth,
height: keyHeight
});
keyBg.tint = 0x555555;
keyBg.x = rowStartX + col * (keyWidth + keySpacing) + keyWidth / 2;
keyBg.y = startY + row * (keyHeight + keySpacing) + keyHeight / 2;
var keyText = new Text2(rowLabels[col], {
size: 40,
fill: 0xFFFFFF
});
keyText.anchor.set(0.5, 0.5);
keyBg.addChild(keyText);
// Add basic press feedback
keyBg.down = function (x, y, obj) {
self.handleKeyPress(keyText.text); // Pass the key label to a new handler
tween(this, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the key
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the key
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50,
onFinish: function () {
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 150
});
}.bind(this)
});
LK.getSound('fx').play(); // Play a generic sound
};
keyBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(keyBg);
}
}
// Add space bar
var spaceBarWidth = keyWidth * 6 + keySpacing * 5;
var spaceBarBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: spaceBarWidth * 1.1,
height: keyHeight
});
var spaceGraphic = self.attachAsset('Space', {
anchorX: 0.5,
anchorY: 0.5
});
spaceGraphic.width = spaceBarWidth * 1.1;
spaceGraphic.height = keyHeight;
spaceBarBg.addChild(spaceGraphic);
spaceBarBg.tint = 0x555555;
spaceBarBg.x = keyboardWidth / 2;
spaceBarBg.y = startY + keyLabels.length * (keyHeight + keySpacing) + keyHeight / 2;
var spaceText = new Text2('SPACE', {
size: 40,
fill: 0xFFFFFF
});
spaceText.anchor.set(0.5, 0.5);
spaceBarBg.addChild(spaceText);
// Add basic press feedback for space bar
spaceBarBg.down = function (x, y, obj) {
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the spacebar
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the spacebar
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
spaceBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(spaceBarBg);
// Add symbols button to the left of the space bar
var symbolsBarWidth = keyWidth * 2 + keySpacing;
var symbolsBarBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: symbolsBarWidth,
height: keyHeight
});
symbolsBarBg.tint = 0x555555;
symbolsBarBg.x = spaceBarBg.x - spaceBarBg.width / 2 - symbolsBarBg.width / 2 - keySpacing - 22;
symbolsBarBg.y = spaceBarBg.y;
var symbolsText = new Text2('SYMBOLS', {
size: 30,
fill: 0xFFFFFF
});
symbolsText.anchor.set(0.5, 0.5);
symbolsBarBg.addChild(symbolsText);
// Add basic press feedback for symbols button
symbolsBarBg.down = function (x, y, obj) {
// Toggle symbols mode
self.isSymbolsMode = !self.isSymbolsMode;
// If switching to symbols mode, turn off shift mode
if (self.isSymbolsMode) {
self.isShiftMode = false;
// Find and update the shift button text
for (var i = 0; i < keys.length; i++) {
if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2 && keys[i].children[0].text === 'SHIFT') {
keys[i].children[0].setText('shift');
break;
}
}
}
self.updateKeyLabels(); // Update the labels
// Update symbols button text based on mode
symbolsText.setText(self.isSymbolsMode ? 'ABC' : 'SYMBOLS'); // Update symbols button text based on mode
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the symbols button
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the symbols button
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
symbolsBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(symbolsBarBg);
// Add Shift button above the symbols button
var shiftBarWidth = keyWidth * 2 + keySpacing;
var shiftBarBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: shiftBarWidth,
height: keyHeight
});
shiftBarBg.tint = 0x555555;
shiftBarBg.x = symbolsBarBg.x;
shiftBarBg.y = symbolsBarBg.y - keyHeight - keySpacing;
var shiftText = new Text2('SHIFT', {
size: 30,
fill: 0xFFFFFF
});
shiftText.anchor.set(0.5, 0.5);
shiftBarBg.addChild(shiftText);
// Add basic press feedback for shift button
shiftBarBg.down = function (x, y, obj) {
// Toggle shift mode
self.isShiftMode = !self.isShiftMode;
self.updateKeyLabels(); // Update the labels
// Update shift button text based on mode
shiftText.setText(self.isShiftMode ? 'SHIFT' : 'shift'); // Maybe change visual later
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the shift button
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the shift button
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
shiftBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(shiftBarBg);
// Add X button above the enter button
var xBarWidth = keyWidth * 1.5;
var xBarBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: xBarWidth,
height: keyHeight
});
xBarBg.tint = 0x555555;
xBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + xBarBg.width / 2 + keySpacing + 15 + 20;
xBarBg.y = spaceBarBg.y - keyHeight - keySpacing;
var xText = new Text2('DELETE', {
size: 28,
fill: 0xFFFFFF
});
xText.anchor.set(0.5, 0.5);
xBarBg.addChild(xText);
// Add basic press feedback for DELETE button
xBarBg.down = function (x, y, obj) {
self.handleKeyPress('DELETE'); // Handle DELETE/BACKSPACE key press
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the X button
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the X button
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
xBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(xBarBg);
// Add enter button to the right of the space bar
var enterBarWidth = keyWidth * 2 + keySpacing;
var enterBarBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: enterBarWidth,
height: keyHeight
});
enterBarBg.tint = 0x555555;
enterBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + enterBarBg.width / 2 + keySpacing + 15;
enterBarBg.y = spaceBarBg.y;
var enterText = new Text2('ENTER', {
size: 40,
fill: 0xFFFFFF
});
enterText.anchor.set(0.5, 0.5);
enterBarBg.addChild(enterText);
// Add basic press feedback for enter button
enterBarBg.down = function (x, y, obj) {
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the enter button
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the enter button
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
enterBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(enterBarBg);
// Method to update key labels based on mode
self.updateKeyLabels = function () {
var currentLabels;
if (self.isSymbolsMode) {
currentLabels = symbolLabels;
} else if (self.isShiftMode) {
currentLabels = shiftLabels;
} else {
currentLabels = keyLabels;
}
var labelIndex = 0;
for (var row = 0; row < currentLabels.length; row++) {
var rowLabels = currentLabels[row];
for (var col = 0; col < rowLabels.length; col++) {
if (labelIndex < keys.length) {
var keyBg = keys[labelIndex];
// Find the Text2 child object
var keyText = null;
for (var i = 0; i < keyBg.children.length; i++) {
if (keyBg.children[i] instanceof Text2) {
keyText = keyBg.children[i];
break;
}
}
if (keyText && keyText.setText) {
keyText.setText(rowLabels[col]);
}
}
labelIndex++;
}
}
};
// Method to handle key press
self.handleKeyPress = function (key) {
// Initialize searchText if it hasn't been already
if (typeof searchText === 'undefined') {
searchText = new Text2('', {
size: 28,
fill: 0x888888
});
searchText.anchor.set(0.5, 0.5);
// Position placeholder, actual position will be set by RekordboxApp
searchText.x = 0;
searchText.y = 0;
}
console.log("Key pressed:", key);
// Add logic here to handle different key presses, e.g., appending to a text input
};
symbolsBarBg.down = function (x, y, obj) {
// Toggle symbols mode
self.isSymbolsMode = !self.isSymbolsMode;
self.updateKeyLabels(); // Update the labels
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the symbols button
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the symbols button
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
symbolsBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(symbolsBarBg);
// Add enter button to the right of the space bar
var enterBarWidth = keyWidth * 2 + keySpacing;
var enterBarBg = self.attachAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: enterBarWidth,
height: keyHeight
});
enterBarBg.tint = 0x555555;
enterBarBg.x = spaceBarBg.x + spaceBarBg.width / 2 + enterBarBg.width / 2 + keySpacing + 15;
enterBarBg.y = spaceBarBg.y;
var enterText = new Text2('ENTER', {
size: 40,
fill: 0xFFFFFF
});
enterText.anchor.set(0.5, 0.5);
enterBarBg.addChild(enterText);
// Add basic press feedback for enter button
enterBarBg.down = function (x, y, obj) {
self.handleKeyPress('ENTER'); // Handle enter key press
// Reset shift and symbols mode after enter
self.isShiftMode = false;
self.isSymbolsMode = false;
self.updateKeyLabels();
// Find and update the symbols button text
for (var i = 0; i < keys.length; i++) {
if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2) {
if (keys[i].children[0].text === 'ABC' || keys[i].children[0].text === 'SYMBOLS') {
// Find the symbols button and update its text
keys[i].children[0].setText('SYMBOLS');
break;
}
}
}
// Find and update the shift button text
for (var i = 0; i < keys.length; i++) {
if (keys[i].children.length > 0 && keys[i].children[0] instanceof Text2 && keys[i].children[0].text === 'SHIFT') {
keys[i].children[0].setText('shift');
break;
}
}
tween(this, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 80
});
// Add orange flashing effect using tint and alpha
// Create a temporary flashing graphic above the enter button
var flashGraphic = LK.getAsset('flashRectangle', {
anchorX: 0.5,
anchorY: 0.5,
width: this.width,
height: this.height
});
flashGraphic.alpha = 1.0;
// Position the flash graphic centered on the enter button
flashGraphic.x = this.x;
flashGraphic.y = this.y;
self.addChild(flashGraphic);
// Fade out and remove the flash graphic
tween(flashGraphic, {
alpha: 0.0
}, {
duration: 200,
onFinish: function onFinish() {
flashGraphic.destroy();
}
});
tween(this, {
tint: 0xff8000,
alpha: 1.0
}, {
duration: 50
});
LK.getSound('fx').play(); // Play a generic sound
};
enterBarBg.up = function (x, y, obj) {
// Return to original tint and alpha on release
tween(this, {
tint: 0x555555,
alpha: 0.8 // Slightly faded normal state
}, {
duration: 100
});
tween(this, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 80
});
};
keys.push(enterBarBg);
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('laserBeam', {
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;
// Only update head colors every few frames to improve performance
if (LK.ticks % 4 === 0) {
// 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);
// Only tween if color actually changed
if (head.tint !== tint) {
head.tint = tint;
}
}
}
// 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;
// Check laser show mode (controlled by second effect button)
if (typeof laserShowMode !== 'undefined' && laserShowMode === 1) {
// Slow laser show mode - beams visible
beam.visible = true;
var sweep = Math.sin(t * 1.2 + h + b * 0.5) * 0.3 + Math.sin(t * 0.8 + b) * 0.15;
var beatPulse = Math.sin(t * 2.8 + h + b) * 0.12;
beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse;
// Slow beam length flicker
var flicker = 1 + 0.18 * Math.sin(t * 3.5 + h * 2 + b * 1.2 + Math.sin(t * 2.0 + b));
beam.height = beamLength * flicker;
// Color logic: if UV mode is on, show different colors; if only laser mode, all beams same color
if (typeof uvEffectActive !== 'undefined' && uvEffectActive) {
// UV mode active - different colors per beam
var colorIdx = (h * beamsPerHead + b + Math.floor(t * 2.5 + h + b)) % beamColors.length;
beam.tint = beamColors[colorIdx];
} else {
// Only laser mode - all beams same color but cycle through gradient
var sameColorIdx = Math.floor(t * 2.5) % beamColors.length;
beam.tint = beamColors[sameColorIdx];
}
// Slow alpha strobe effect
beam.alpha = beamAlpha + 0.2 * Math.abs(Math.sin(t * 5 + h + b));
} else if (typeof laserShowMode !== 'undefined' && laserShowMode === 2) {
// Fast laser show mode - beams visible
beam.visible = true;
var sweep = Math.sin(t * 2.4 + h + b * 0.9) * 0.5 + Math.sin(t * 1.8 + b) * 0.25;
var beatPulse = Math.sin(t * 6.0 + h + b) * 0.2;
beam.rotation = baseAngle - Math.PI / 2 + sweep + beatPulse;
// Fast beam length flicker
var flicker = 1 + 0.35 * Math.sin(t * 7 + h * 2 + b * 1.8 + Math.sin(t * 3.5 + b));
beam.height = beamLength * flicker;
// Color logic: if UV mode is on, show different colors; if only laser mode, all beams same color
if (typeof uvEffectActive !== 'undefined' && uvEffectActive) {
// UV mode active - different colors per beam
var colorIdx = (h * beamsPerHead + b + Math.floor(t * 6 + h + b)) % beamColors.length;
beam.tint = beamColors[colorIdx];
} else {
// Only laser mode - all beams same color but cycle through gradient
var sameColorIdx = Math.floor(t * 6) % beamColors.length;
beam.tint = beamColors[sameColorIdx];
}
// Fast alpha strobe effect
beam.alpha = beamAlpha + 0.4 * Math.abs(Math.sin(t * 12 + h + b));
} else {
// Off mode - hide beams completely
beam.visible = false;
}
}
};
return self;
});
// MoneyParticle: Animated money bills falling from the top
var MoneyParticle = Container.expand(function () {
var self = Container.call(this);
// Config
var size = (40 + Math.random() * 30) / 1.2; // Random size between 40-70
var speedY = 1.2 + Math.random() * 0.8; // Vertical speed
var speedX = (Math.random() - 0.5) * 1.5; // Horizontal drift
var rotationSpeed = (Math.random() - 0.5) * 0.03; // Rotation speed
// Create money bill graphic using money asset
var moneyGraphic = self.attachAsset('money', {
anchorX: 0.5,
anchorY: 0.5,
width: size * 1.8,
// Money bills are wider than tall
height: size
});
// Keep natural money asset color (remove tint)
// Animation
self.update = function () {
self.y += speedY;
self.x += speedX;
self.rotation += rotationSpeed;
// Wrap around if off screen (below the dancing people)
if (self.y > dancingPeople[0].y + 200) {
// Reset to top with new random properties
self.y = -size;
self.x = Math.random() * 2048;
// Reset size and properties for variety
size = 40 + Math.random() * 30;
moneyGraphic.width = size * 1.8;
moneyGraphic.height = size;
speedY = 1.2 + Math.random() * 0.8;
speedX = (Math.random() - 0.5) * 1.5;
rotationSpeed = (Math.random() - 0.5) * 0.03;
// Animate scale for sparkle effect instead of tint
tween(moneyGraphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(moneyGraphic, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
};
return self;
});
// RekordboxApp: Full-featured rekordbox application interface for the tablet
var RekordboxApp = Container.expand(function () {
var self = Container.call(this);
// Config
self.currentScreen = 'browser'; // 'browser', 'playlist', 'settings', 'performance'
self.selectedTrackIndex = 0;
self.selectedCategoryIndex = 0;
self.currentCategory = 'all';
// Track database
self.tracks = [{
name: 'Summer Vibes',
artist: 'DJ Alex',
bpm: 128,
key: 'Am',
genre: 'House'
}, {
name: 'Night Drive',
artist: 'Luna Beat',
bpm: 132,
key: 'Fm',
genre: 'Techno'
}, {
name: 'Electric Dreams',
artist: 'Synth Master',
bpm: 140,
key: 'Gm',
genre: 'Electronic'
}, {
name: 'Deep Ocean',
artist: 'Wave Runner',
bpm: 124,
key: 'Dm',
genre: 'Deep House'
}, {
name: 'City Lights',
artist: 'Urban Sound',
bpm: 126,
key: 'Em',
genre: 'Progressive'
}, {
name: 'Midnight Express',
artist: 'Beat Factory',
bpm: 130,
key: 'Cm',
genre: 'Techno'
}, {
name: 'Tropical Storm',
artist: 'Island Beats',
bpm: 118,
key: 'Bm',
genre: 'Tropical'
}, {
name: 'Neon Pulse',
artist: 'Cyber DJ',
bpm: 135,
key: 'F#m',
genre: 'Electro'
}, {
name: 'Sunset Boulevard',
artist: 'Coast Drive',
bpm: 122,
key: 'Am',
genre: 'Chill'
}, {
name: 'Bass Revolution',
artist: 'Low End Theory',
bpm: 128,
key: 'Dm',
genre: 'Bass'
}, {
name: 'Street Anthem',
artist: 'MC Flow',
bpm: 90,
key: 'Em',
genre: 'Hip-Hop'
}, {
name: 'Island Rhythm',
artist: 'Rasta King',
bpm: 75,
key: 'Am',
genre: 'Reggae'
}, {
name: 'Smooth Jazz Night',
artist: 'Jazz Collective',
bpm: 110,
key: 'Bbm',
genre: 'Jazz'
}, {
name: 'Symphony No. 9',
artist: 'Classical Ensemble',
bpm: 120,
key: 'Dm',
genre: 'Classical'
}, {
name: 'Rock The House',
artist: 'Thunder Strike',
bpm: 140,
key: 'Em',
genre: 'EDM'
}, {
name: 'Pop Star',
artist: 'Chart Topper',
bpm: 125,
key: 'Am',
genre: 'Pop'
}, {
name: 'Floating Dreams',
artist: 'Ambient Explorer',
bpm: 80,
key: 'Fm',
genre: 'Ambient'
}, {
name: 'Drop The Beat',
artist: 'Dubstep Master',
bpm: 140,
key: 'Gm',
genre: 'Dubstep'
}, {
name: 'Trap Nation',
artist: 'Trap Lord',
bpm: 75,
key: 'Cm',
genre: 'Trap'
}, {
name: 'Liquid Motion',
artist: 'DNB Producer',
bpm: 175,
key: 'Am',
genre: 'Drum & Bass'
}];
self.categories = ['CATEGORY', 'All Tracks', 'House', 'Techno', 'EDM', 'Electronic', 'Disco', 'Latin', 'POP', 'Hip-Hop'];
self.visibleTrackStart = 0;
self.maxVisibleTracks = 8;
// Create main container for app content
var appContainer = self.addChild(new Container());
// Header bar
var headerBar = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 742,
height: 50
});
headerBar.tint = 0x1a1a1a;
appContainer.addChild(headerBar);
// TRACKLIST logo/title
var titleText = new Text2('TRACKLIST', {
size: 56,
fill: 0xFF6600
});
titleText.anchor.set(0, 0.5);
titleText.x = 10;
titleText.y = 25;
appContainer.addChild(titleText);
// Screen indicator
var screenIndicator = new Text2('BROWSER', {
size: 40,
fill: 0xFFFFFF
});
screenIndicator.anchor.set(1, 0.5);
screenIndicator.x = 722;
screenIndicator.y = 25;
appContainer.addChild(screenIndicator);
// Sidebar for categories
var sidebar = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 180,
height: 400
});
sidebar.tint = 0x2a2a2a;
sidebar.y = 60;
appContainer.addChild(sidebar);
// Main content area
var contentArea = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 542,
height: 400
});
contentArea.tint = 0x1a1a1a;
contentArea.x = 190;
contentArea.y = 60;
appContainer.addChild(contentArea);
// Category items in sidebar
self.categoryItems = [];
for (var i = 0; i < self.categories.length; i++) {
var categoryBg = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 170,
height: 35
});
categoryBg.tint = i === self.selectedCategoryIndex ? 0x444444 : 0x333333;
categoryBg.x = 5;
categoryBg.y = 70 + i * 40;
appContainer.addChild(categoryBg);
var categoryText = new Text2(self.categories[i], {
size: 28,
fill: i === self.selectedCategoryIndex ? "#ff6600" : "#cccccc"
});
categoryText.anchor.set(0, 0.5);
categoryText.x = 15;
categoryText.y = 87 + i * 40 - 2 - 5;
appContainer.addChild(categoryText);
self.categoryItems.push({
bg: categoryBg,
text: categoryText
});
}
// Track list headers
var headerTrack = new Text2('TRACK', {
size: 28,
fill: 0x888888
});
headerTrack.anchor.set(0, 0);
headerTrack.x = 200;
headerTrack.y = 70;
appContainer.addChild(headerTrack);
var headerArtist = new Text2('ARTIST', {
size: 28,
fill: 0x888888
});
headerArtist.anchor.set(0, 0);
headerArtist.x = 413; // Increased x-position
headerArtist.y = 70;
appContainer.addChild(headerArtist);
var headerBPM = new Text2('BPM', {
size: 28,
fill: 0x888888
});
headerBPM.anchor.set(0, 0);
headerBPM.x = 610; // Increased x-position and moved left by 10 units
headerBPM.y = 70;
appContainer.addChild(headerBPM);
var headerKey = new Text2('KEY', {
size: 28,
fill: 0x888888
});
headerKey.anchor.set(0, 0);
headerKey.x = 620 + 40 + 17; // Increased x-position by adding 17 units to the previous elements x + width
headerKey.y = 70;
appContainer.addChild(headerKey);
// Track list items
self.trackItems = [];
for (var i = 0; i < self.maxVisibleTracks; i++) {
var trackBg = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 532,
height: 35
});
trackBg.tint = 0x252525;
trackBg.x = 195;
trackBg.y = 95 + i * 40;
var trackText = new Text2('', {
size: 28,
fill: 0xFFFFFF
});
trackText.anchor.set(0, 0.5);
trackText.x = 200;
trackText.y = 112 + i * 40 + 17 + 30;
var artistText = new Text2('', {
size: 28,
//{ O}
fill: 0xCCCCCC //{ P}
}); //{ Q}
artistText.anchor.set(0, 0.5);
artistText.x = 380 + 25 + 10; // Increased x-position//{ O}
artistText.y = 112 + i * 40 + 17 + 30;
var bpmText = new Text2('', {
size: 28,
//{ R}
fill: 0xAAAAAA //{ S}
}); //{ T}
bpmText.anchor.set(0, 0.5);
bpmText.x = 620; // Increased x-position
bpmText.y = 112 + i * 40 + 17 + 30;
var keyText = new Text2('', {
size: 28,
//{ U}
fill: 0xAAAAAA //{ V}
}); //{ W}
keyText.anchor.set(0, 0.5);
keyText.x = 620 + 40 + 17; // Increased x-position by adding 17 units to the previous elements x + width
keyText.y = 112 + i * 40 + 17 + 30;
self.trackItems.push({
bg: trackBg,
track: trackText,
artist: artistText,
bpm: bpmText,
key: keyText
}); //{ X}
}
// Add all backgrounds first to ensure they are at the back
for (var i = 0; i < self.maxVisibleTracks; i++) {
appContainer.addChild(self.trackItems[i].bg);
}
// Then add all text elements so they are rendered on top
for (var i = 0; i < self.maxVisibleTracks; i++) {
var item = self.trackItems[i];
appContainer.addChild(item.track);
appContainer.addChild(item.artist);
appContainer.addChild(item.bpm);
appContainer.addChild(item.key);
}
// Bottom status bar
var statusBar = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 742,
height: 30
});
statusBar.tint = 0x333333;
statusBar.y = 470;
appContainer.addChild(statusBar);
var statusText = new Text2('Use arrow keys to navigate', {
size: 24,
fill: 0xCCCCCC
});
statusText.anchor.set(0, 0.5);
statusText.x = 235;
statusText.y = 485;
appContainer.addChild(statusText);
// Search bar background
var searchBarBg = LK.getAsset('Searchbarbg', {
anchorX: 0.5,
anchorY: 0.5,
width: 512,
height: 40
});
searchBarBg.tint = 0xffffff;
searchBarBg.x = 457 + 10; // Adjusted for center anchor and moved right by 12 units
searchBarBg.y = 125; // Adjusted for center anchor
self.addChild(searchBarBg); // Add directly to self instead of appContainer for independent positioning
// Search bar text
var searchText = new Text2('Search tracks...', {
size: 28,
fill: 0x888888
});
searchText.anchor.set(0.5, 0.5);
searchText.x = 457; // Centered with search bar background and moved right by 12 units
searchText.y = 125; // Centered with search bar background
self.addChild(searchText); // Add directly to self instead of appContainer for independent positioning
// Track count info
self.trackCountText = new Text2('', {
size: 24,
fill: 0x888888
});
self.trackCountText.anchor.set(1, 0.5);
self.trackCountText.x = 732;
self.trackCountText.y = 485;
appContainer.addChild(self.trackCountText);
// Methods
self.updateDisplay = function () {
// Update categories
for (var i = 0; i < self.categoryItems.length; i++) {
var isSelected = i === self.selectedCategoryIndex;
self.categoryItems[i].bg.tint = isSelected ? 0x444444 : 0x333333;
self.categoryItems[i].text.fill = isSelected ? "#ff6600" : "#cccccc";
}
// Filter tracks based on category
var filteredTracks = self.tracks;
if (self.selectedCategoryIndex > 1) {
// Filter by specific genre, skipping 'CATEGORY' and 'All Tracks'
var genre = self.categories[self.selectedCategoryIndex];
filteredTracks = self.tracks.filter(function (track) {
return track.genre === genre;
});
} else if (self.selectedCategoryIndex === 1) {
// 'All Tracks' category - show all tracks
filteredTracks = self.tracks;
} else {
// 'CATEGORY' category - show no tracks
filteredTracks = [];
}
// Update track list
for (var i = 0; i < self.maxVisibleTracks; i++) {
var trackIndex = self.visibleTrackStart + i;
var item = self.trackItems[i];
if (trackIndex < filteredTracks.length) {
var track = filteredTracks[trackIndex];
var isSelected = trackIndex === self.selectedTrackIndex;
item.bg.tint = isSelected ? 0x0066cc : i % 2 === 0 ? 0x252525 : 0x2a2a2a;
item.track.setText(track.name.length > 20 ? track.name.substring(0, 17) + '...' : track.name);
item.artist.setText(track.artist.length > 15 ? track.artist.substring(0, 12) + '...' : track.artist);
item.bpm.setText(track.bpm.toString());
item.key.setText(track.key);
item.track.fill = isSelected ? "#ffffff" : "#cccccc";
item.artist.fill = isSelected ? "#ffffff" : "#aaaaaa";
item.bpm.fill = isSelected ? "#ffffff" : "#999999";
item.key.fill = isSelected ? "#ffffff" : "#999999";
item.bg.visible = true;
item.track.visible = true;
item.artist.visible = true;
item.bpm.visible = true;
item.key.visible = true;
} else {
item.bg.visible = false;
item.track.visible = false;
item.artist.visible = false;
item.bpm.visible = false;
item.key.visible = false;
}
}
// Update track count
self.trackCountText.setText(filteredTracks.length + ' tracks');
// Update screen indicator
screenIndicator.setText(self.currentScreen.toUpperCase());
};
self.navigateUp = function () {
if (self.selectedTrackIndex > 0) {
self.selectedTrackIndex--;
if (self.selectedTrackIndex < self.visibleTrackStart) {
self.visibleTrackStart = self.selectedTrackIndex;
}
self.updateDisplay();
}
};
self.navigateDown = function () {
var maxTracks = self.tracks.length - 1;
if (self.selectedTrackIndex < maxTracks) {
self.selectedTrackIndex++;
if (self.selectedTrackIndex >= self.visibleTrackStart + self.maxVisibleTracks) {
self.visibleTrackStart = self.selectedTrackIndex - self.maxVisibleTracks + 1;
}
self.updateDisplay();
}
};
self.navigateLeft = function () {
if (self.selectedCategoryIndex > 0) {
self.selectedCategoryIndex--;
self.selectedTrackIndex = 0;
self.visibleTrackStart = 0;
self.updateDisplay();
}
};
self.navigateRight = function () {
if (self.selectedCategoryIndex < self.categories.length - 1) {
self.selectedCategoryIndex++;
self.selectedTrackIndex = 0;
self.visibleTrackStart = 0;
self.updateDisplay();
}
};
self.selectTrack = function () {
var filteredTracks = self.tracks;
if (self.selectedCategoryIndex > 1) {
// Filter by specific genre, skipping 'CATEGORY' and 'All Tracks'
var genre = self.categories[self.selectedCategoryIndex];
filteredTracks = self.tracks.filter(function (track) {
return track.genre === genre;
});
} else if (self.selectedCategoryIndex === 1) {
// 'All Tracks' category - show all tracks
filteredTracks = self.tracks;
} else {
// 'CATEGORY' category - show no tracks
filteredTracks = [];
}
if (self.selectedTrackIndex < filteredTracks.length) {
var selectedTrack = filteredTracks[self.selectedTrackIndex];
// Flash effect to show selection
tween(self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg, {
tint: 0x00ff00
}, {
duration: 150,
onFinish: function onFinish() {
tween(self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg, {
tint: 0x0066cc
}, {
duration: 150
});
}
});
// Play a selection sound
LK.getSound('fx').play();
// Add score for track selection
if (typeof score !== 'undefined') {
score += 25;
combo += 1;
}
}
};
// Make search bar draggable
searchBarBg.down = function (x, y, obj) {
self.searchBarDragging = true;
self.searchBarOffset = {
x: x - searchBarBg.x,
y: y - searchBarBg.y
};
};
searchBarBg.up = function (x, y, obj) {
self.searchBarDragging = false;
self.searchBarOffset = null;
};
searchBarBg.move = function (x, y, obj) {
if (self.searchBarDragging && self.searchBarOffset) {
searchBarBg.x = x - self.searchBarOffset.x;
searchBarBg.y = y - self.searchBarOffset.y;
// Keep search text aligned with background
searchText.x = searchBarBg.x;
searchText.y = searchBarBg.y;
}
};
// Initialize display
self.updateDisplay();
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 = 60; // Increased number of particles for denser smoke
var particles = [];
var particleMinSize = 48;
var particleMaxSize = 96;
var speedY = -3.5; // pixels per frame (upwards)
var speedX = 0.7; // horizontal drift
var particleLifetime = 2500; // ms // Increased particle lifetime
// 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_color = v * (1 - (1 - f) * s);
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)];
}
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 depends on UV effect state
if (typeof uvEffectActive !== 'undefined' && uvEffectActive) {
// Colorful UV smoke
var hue = Math.random();
var rgb = hsvToRgb(hue, 0.9 + Math.random() * 0.1, 0.9 + Math.random() * 0.1);
puff.tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
} else {
// Normal 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();
// Only emit smoke if smokeEffectActive is true
if (typeof smokeEffectActive !== 'undefined' && smokeEffectActive) {
// 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
****/
// --- Reflectors (behind discoball) ---
// Add a vertical crossfader track
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
// --- DJDeck Asset (center of map) ---
var djDeck = LK.getAsset('desk', {
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
// --- Rim Assets (both sides of djdeck) ---
var rimLeft = LK.getAsset('rim', {
anchorX: 0.5,
anchorY: 0.5
});
rimLeft.x = djDeck.x - djDeck.width / 2 + rimLeft.width / 2 + 49;
rimLeft.y = djDeck.y;
var rimRight = LK.getAsset('rim', {
anchorX: 0.5,
anchorY: 0.5
});
rimRight.x = djDeck.x + djDeck.width / 2 - rimRight.width / 2 - 150 + 108;
rimRight.y = djDeck.y;
// Add rim assets after djdeck to ensure they appear above in display order
game.addChild(rimLeft);
game.addChild(rimRight);
// Move rim assets above djdeck in display order
var djdeckIndex = game.children.indexOf(djDeck);
if (djdeckIndex !== -1) {
game.setChildIndex(rimLeft, djdeckIndex + 1);
game.setChildIndex(rimRight, djdeckIndex + 2);
}
// Remove and re-add rim assets to ensure they're above djdeckAsset in display order
if (game.children.indexOf(rimLeft) !== -1) {
game.removeChild(rimLeft);
}
if (game.children.indexOf(rimRight) !== -1) {
game.removeChild(rimRight);
}
game.addChild(rimLeft);
game.addChild(rimRight);
// --- 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 - 25 * (numPeople - 1); // Area width for dancing people, reduced to move people closer
var startX = 2048 / 2 - peopleAreaWidth / 2 - 77;
var personSpacing = peopleAreaWidth / (numPeople - 1);
for (var i = 0; i < numPeople; i++) {
// Alternate: even index = woman, odd index = man
var genderAsset = i % 2 === 0 ? 'dancingWoman' : 'dancingMan';
var person = new DancingPerson(genderAsset);
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
// --- Additional Fireworks Effects for more density in upper half ---
var fireworksEffect2 = new FireworksEffect();
game.addChild(fireworksEffect2);
fireworksEffect2.y = 0; // Position at the top of the screen
fireworksEffect2.x = 500; // Offset position for varied launch points
var fireworksEffect3 = new FireworksEffect();
game.addChild(fireworksEffect3);
fireworksEffect3.y = 0; // Position at the top of the screen
fireworksEffect3.x = -300; // Different offset position
var fireworksEffect4 = new FireworksEffect();
game.addChild(fireworksEffect4);
fireworksEffect4.y = 0; // Position at the top of the screen
fireworksEffect4.x = 800; // Another offset position
// --- Balloons (falling from top) ---
var balloons = [];
var numBalloons = 50; // Increased number of balloons
for (var i = 0; i < numBalloons; i++) {
var balloon = new Balloon();
// Position balloons initially above the screen
balloon.y = -300 - Math.random() * 1000; // Start higher up for more variety
balloon.x = Math.random() * 2048;
game.addChild(balloon);
balloons.push(balloon);
}
// --- Money Particles (falling from top) ---
var moneyParticles = [];
var numMoneyParticles = 25; // Number of money bills falling
for (var i = 0; i < numMoneyParticles; i++) {
var money = new MoneyParticle();
// Position money initially above the screen with staggered timing
money.y = -100 - Math.random() * 800;
money.x = Math.random() * 2048;
game.addChild(money);
moneyParticles.push(money);
// Add initial sparkle effect with tween
tween(money, {
alpha: 0.7
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function () {
tween(this, {
alpha: 1.0
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeInOut
});
}.bind(money)
});
}
// --- 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('desk', {
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.
// --- Second Line Dancing People (append to existing array) ---
// Don't recreate the array, just append to existing dancingPeople array
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 - 25 * (numPeople - 1); // Area width for dancing people, reduced to move people closer
var startX = 2048 / 2 - peopleAreaWidth / 2;
var personSpacing = peopleAreaWidth / (numPeople - 1);
for (var i = 0; i < numPeople; i++) {
var person = new DancingPerson(); // Use default random asset initially
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) ---
// Add global smokeEffectActive variable to control smoke emission
var smokeEffectActive = false;
var fogAnimationActive = false;
var fogTimeout = null;
// --- Add 6 smoke diffusers spread out to the left of the people line, spaced 300 units apart, all moved right by 32 units ---
var smokeDiffusers = [];
for (var i = 0; i <= 5; i++) {
var sd = new SmokeDiffuser();
sd.x = startX - 80 + 32 + i * 300;
sd.y = peopleAreaY + 18 - 50; // Align base with people feet, moved up by 50 units
game.addChild(sd);
smokeDiffusers.push(sd);
}
// --- Flamethrower ---
var flamethrower = new Flamethrower();
// Position the flamethrower to the right of the dancing people line
flamethrower.x = startX + peopleAreaWidth + 100 + 77 + 7; // Move flamethrower right by 84 units (77+7)
// Move the flamethrower down so the flame aligns with the top of the base, then up by 100 units
flamethrower.y = peopleAreaY + (flamethrower.height ? flamethrower.height * 0.5 : 75) - 100; // 75 is half of base height (150*0.5), fallback if height is not set
game.addChild(flamethrower);
// --- Second Flamethrower (left edge) ---
var flamethrowerLeft = new Flamethrower();
// Place at the left edge, aligned with the people line
flamethrowerLeft.x = startX - 200; // Move left flamethrower left by 100 more units
flamethrowerLeft.y = peopleAreaY + (flamethrowerLeft.height ? flamethrowerLeft.height * 0.5 : 75) - 100; // Move base up by 100 units
game.addChild(flamethrowerLeft);
// 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);
}
// Ensure balloons are behind the dancing people but in front of the DJ deck
var minDancingPeopleIndex = game.children.length;
for (var i = 0; i < dancingPeople.length; i++) {
var idx = game.children.indexOf(dancingPeople[i]);
if (idx !== -1 && idx < minDancingPeopleIndex) minDancingPeopleIndex = idx;
}
var djDeckIndex = game.children.indexOf(djdeckAsset); // Use djdeckAsset which is the visible asset
if (balloons) {
for (var i = 0; i < balloons.length; i++) {
var balloon = balloons[i];
var balloonIndex = game.children.indexOf(balloon);
if (balloonIndex !== -1) {
// Position balloons just behind the dancing people (minIndex - 1)
// But also ensure they are in front of the djdeckAsset (djDeckIndex + 1)
var targetIndex = Math.max(djDeckIndex + 1, minDancingPeopleIndex - 1);
// Make sure the targetIndex is valid
if (targetIndex < game.children.length) {
game.setChildIndex(balloon, targetIndex);
} else {
// If target index is out of bounds, place it just before the dancing people
game.setChildIndex(balloon, minDancingPeopleIndex > 0 ? minDancingPeopleIndex - 1 : 0);
}
}
}
}
// --- 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 = 2300;
var fxButtonY = 2200;
// --- 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 + 333 - 124;
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);
// --- DJDeck Asset (center of map) ---
var djdeckAsset = LK.getAsset('djdeck', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(djdeckAsset);
djdeckAsset.x = 2048 / 2;
djdeckAsset.y = 2732 / 2 + 300 + 23 - 5 - 15 - 5;
// Scale djdeck to fit the screen width properly
djdeckAsset.width = 2048;
djdeckAsset.height = djdeckAsset.height * (2048 / djdeckAsset.width);
// Ensure rim assets are above djdeckAsset in display order
var djdeckAssetIndex = game.children.indexOf(djdeckAsset);
if (djdeckAssetIndex !== -1) {
var rimLeftIndex = game.children.indexOf(rimLeft);
var rimRightIndex = game.children.indexOf(rimRight);
if (rimLeftIndex !== -1 && rimLeftIndex <= djdeckAssetIndex) {
var targetIndexLeft = djdeckAssetIndex + 1;
if (targetIndexLeft < game.children.length) {
game.setChildIndex(rimLeft, targetIndexLeft);
} else {
// If target index is out of bounds, move to last position
game.setChildIndex(rimLeft, game.children.length - 1);
}
// Update djdeckAssetIndex after moving rimLeft
djdeckAssetIndex = game.children.indexOf(djdeckAsset);
}
if (rimRightIndex !== -1 && rimRightIndex <= djdeckAssetIndex) {
// Get fresh index for rimLeft after potential move
var currentRimLeftIndex = game.children.indexOf(rimLeft);
var targetIndex = Math.max(djdeckAssetIndex + 1, currentRimLeftIndex + 1);
if (targetIndex < game.children.length) {
game.setChildIndex(rimRight, targetIndex);
} else {
// If target index is out of bounds, move to last position
game.setChildIndex(rimRight, game.children.length - 1);
}
}
}
// --- Sampletable Asset (center of map) ---
var sampletableAsset = LK.getAsset('sampletable', {
anchorX: 0.5,
anchorY: 1.0
});
game.addChild(sampletableAsset);
sampletableAsset.x = 2048 / 2;
sampletableAsset.y = 2732;
// --- Crossfader ---
// Remove and re-add crossfader to ensure it's above the sampletable
if (game.children.indexOf(crossfader) !== -1) {
game.removeChild(crossfader);
}
game.addChild(crossfader);
// --- FX Button ---
// Remove and re-add fxButton to ensure it's above the sampletable
if (game.children.indexOf(fxButton) !== -1) {
game.removeChild(fxButton);
}
game.addChild(fxButton);
// --- Samplebutton Asset (left side corner of sampletable) ---
var samplebuttonAsset = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset);
samplebuttonAsset.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset.width / 2 + 50 - 4;
samplebuttonAsset.y = sampletableAsset.y - samplebuttonAsset.height / 2 - 50;
// Add functionality to first samplebutton
samplebuttonAsset.down = function (x, y, obj) {
// Play sample1 sound
LK.getSound('sample1').play();
// Visual feedback - scale animation
tween(samplebuttonAsset, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(samplebuttonAsset, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect
LK.effects.flashObject(samplebuttonAsset, 0xff0000, 300);
};
// --- Second Samplebutton Asset (reusing existing asset for memory efficiency) ---
var samplebuttonAsset2 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset2);
samplebuttonAsset2.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset2.width / 2 + 200 - 4;
samplebuttonAsset2.y = sampletableAsset.y - samplebuttonAsset2.height / 2 - 50;
// --- Third Samplebutton Asset ---
var samplebuttonAsset3 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset3);
samplebuttonAsset3.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset3.width / 2 + 350;
samplebuttonAsset3.y = sampletableAsset.y - samplebuttonAsset3.height / 2 - 50;
// Add functionality to third samplebutton
samplebuttonAsset3.down = function (x, y, obj) {
// Play sample3 sound
LK.getSound('sample3').play();
// Visual feedback - color flash animation
tween(samplebuttonAsset3, {
tint: 0x0000ff
}, {
duration: 150,
onFinish: function onFinish() {
tween(samplebuttonAsset3, {
tint: 0xffffff
}, {
duration: 150
});
}
});
// Flash effect with blue color
LK.effects.flashObject(samplebuttonAsset3, 0x0000ff, 300);
score += 50;
combo += 1;
};
// --- Fourth Samplebutton Asset ---
var samplebuttonAsset4 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset4);
samplebuttonAsset4.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset4.width / 2 + 500 - 4;
samplebuttonAsset4.y = sampletableAsset.y - samplebuttonAsset4.height / 2 - 50;
// Add functionality to fourth samplebutton
samplebuttonAsset4.down = function (x, y, obj) {
// Play sample4 sound
LK.getSound('sample4').play();
// Visual feedback - pulse animation
tween(samplebuttonAsset4, {
alpha: 0.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(samplebuttonAsset4, {
alpha: 1.0
}, {
duration: 80
});
}
});
// Flash effect with purple color
LK.effects.flashObject(samplebuttonAsset4, 0xff00ff, 300);
score += 50;
combo += 1;
};
// --- Second Line Samplebuttons (Above First Line) ---
// --- First Samplebutton Second Line ---
var samplebuttonAsset5 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset5);
samplebuttonAsset5.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset5.width / 2 + 50 - 4;
samplebuttonAsset5.y = sampletableAsset.y - samplebuttonAsset5.height / 2 - 250;
// Add functionality to fifth samplebutton
samplebuttonAsset5.down = function (x, y, obj) {
// Play sample1 sound with different effect
LK.getSound('sample1').play();
// Visual feedback - scale animation
tween(samplebuttonAsset5, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(samplebuttonAsset5, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
// Flash effect with cyan color
LK.effects.flashObject(samplebuttonAsset5, 0x00ffff, 300);
score += 75;
combo += 1;
};
// --- Second Samplebutton Second Line ---
var samplebuttonAsset6 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset6);
samplebuttonAsset6.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset6.width / 2 + 200 - 4;
samplebuttonAsset6.y = sampletableAsset.y - samplebuttonAsset6.height / 2 - 250;
// Add functionality to sixth samplebutton
samplebuttonAsset6.down = function (x, y, obj) {
// Play sample2 sound with different effect
LK.getSound('sample2').play();
// Visual feedback - scale animation
tween(samplebuttonAsset6, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
onFinish: function onFinish() {
tween(samplebuttonAsset6, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
}
});
// Flash effect with magenta color
LK.effects.flashObject(samplebuttonAsset6, 0xff00ff, 300);
score += 75;
combo += 1;
};
// --- Third Samplebutton Second Line ---
var samplebuttonAsset7 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset7);
samplebuttonAsset7.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset7.width / 2 + 350 - 4;
samplebuttonAsset7.y = sampletableAsset.y - samplebuttonAsset7.height / 2 - 250;
// Add functionality to seventh samplebutton
samplebuttonAsset7.down = function (x, y, obj) {
// Play sample3 sound with different effect
LK.getSound('sample3').play();
// Visual feedback - pulsing tint animation
tween(samplebuttonAsset7, {
tint: 0xffff00,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 120,
onFinish: function onFinish() {
tween(samplebuttonAsset7, {
tint: 0xffffff,
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
// Flash effect with yellow color
LK.effects.flashObject(samplebuttonAsset7, 0xffff00, 300);
score += 75;
combo += 1;
};
// --- Fourth Samplebutton Second Line ---
var samplebuttonAsset8 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAsset8);
samplebuttonAsset8.x = sampletableAsset.x - sampletableAsset.width / 2 + samplebuttonAsset8.width / 2 + 500 - 4;
samplebuttonAsset8.y = sampletableAsset.y - samplebuttonAsset8.height / 2 - 250;
// Add functionality to eighth samplebutton
samplebuttonAsset8.down = function (x, y, obj) {
// Play sample4 sound with different effect
LK.getSound('sample4').play();
// Visual feedback - alpha fade
tween(samplebuttonAsset8, {
alpha: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
tween(samplebuttonAsset8, {
alpha: 1.0
}, {
duration: 100
});
}
});
// Flash effect with orange color
LK.effects.flashObject(samplebuttonAsset8, 0xff8000, 300);
score += 75;
combo += 1;
};
// Add different functionality to second samplebutton
samplebuttonAsset2.down = function (x, y, obj) {
// Play sample2 sound (different from first button)
LK.getSound('sample2').play();
// Visual feedback - scale animation
tween(samplebuttonAsset2, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(samplebuttonAsset2, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect with different color
LK.effects.flashObject(samplebuttonAsset2, 0x00ff00, 300);
// Add score bonus for using sample buttons
score += 50;
combo += 1;
};
// --- Right Side Sample Buttons (First Line) ---
// --- First Samplebutton Right Side First Line ---
var samplebuttonAssetR1 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR1);
samplebuttonAssetR1.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR1.width / 2 - 50 + 4;
samplebuttonAssetR1.y = sampletableAsset.y - samplebuttonAssetR1.height / 2 - 50;
// Add functionality to first right side samplebutton
samplebuttonAssetR1.down = function (x, y, obj) {
// Play sample1 sound
LK.getSound('sample1').play();
// Visual feedback - scale animation
tween(samplebuttonAssetR1, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(samplebuttonAssetR1, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect
LK.effects.flashObject(samplebuttonAssetR1, 0xff0000, 300);
};
// --- Second Samplebutton Right Side First Line ---
var samplebuttonAssetR2 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR2);
samplebuttonAssetR2.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR2.width / 2 - 200 + 4;
samplebuttonAssetR2.y = sampletableAsset.y - samplebuttonAssetR2.height / 2 - 50;
// Add functionality to second right side samplebutton
samplebuttonAssetR2.down = function (x, y, obj) {
// Play sample2 sound
LK.getSound('sample2').play();
// Visual feedback - scale animation
tween(samplebuttonAssetR2, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(samplebuttonAssetR2, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect with different color
LK.effects.flashObject(samplebuttonAssetR2, 0x00ff00, 300);
// Add score bonus for using sample buttons
score += 50;
combo += 1;
};
// --- Third Samplebutton Right Side First Line ---
var samplebuttonAssetR3 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR3);
samplebuttonAssetR3.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR3.width / 2 - 350 + 4;
samplebuttonAssetR3.y = sampletableAsset.y - samplebuttonAssetR3.height / 2 - 50;
// Add functionality to third right side samplebutton
samplebuttonAssetR3.down = function (x, y, obj) {
// Play sample3 sound
LK.getSound('sample3').play();
// Visual feedback - color flash animation
tween(samplebuttonAssetR3, {
tint: 0x0000ff
}, {
duration: 150,
onFinish: function onFinish() {
tween(samplebuttonAssetR3, {
tint: 0xffffff
}, {
duration: 150
});
}
});
// Flash effect with blue color
LK.effects.flashObject(samplebuttonAssetR3, 0x0000ff, 300);
score += 50;
combo += 1;
};
// --- Left Deck Asset ---
var leftDeckAsset = LK.getAsset('Deck', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftDeckAsset);
leftDeckAsset.x = rimLeft.x;
leftDeckAsset.y = rimLeft.y;
// --- Right Deck Asset ---
var rightDeckAsset = LK.getAsset('Deck', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightDeckAsset);
rightDeckAsset.x = rimRight.x;
rightDeckAsset.y = rimRight.y;
// --- Right Side Sample Buttons (Second Line) ---
// --- First Samplebutton Right Side Second Line ---
var samplebuttonAssetR5 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR5);
samplebuttonAssetR5.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR5.width / 2 - 50 + 4;
samplebuttonAssetR5.y = sampletableAsset.y - samplebuttonAssetR5.height / 2 - 250;
// Add functionality to fifth right side samplebutton
samplebuttonAssetR5.down = function (x, y, obj) {
// Play sample1 sound with different effect
LK.getSound('sample1').play();
// Visual feedback - scale animation
tween(samplebuttonAssetR5, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(samplebuttonAssetR5, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
// Flash effect with cyan color
LK.effects.flashObject(samplebuttonAssetR5, 0x00ffff, 300);
score += 75;
combo += 1;
};
// --- Second Samplebutton Right Side Second Line ---
var samplebuttonAssetR6 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR6);
samplebuttonAssetR6.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR6.width / 2 - 200 + 4;
samplebuttonAssetR6.y = sampletableAsset.y - samplebuttonAssetR6.height / 2 - 250;
// Add functionality to sixth right side samplebutton
samplebuttonAssetR6.down = function (x, y, obj) {
// Play sample2 sound with different effect
LK.getSound('sample2').play();
// Visual feedback - scale animation
tween(samplebuttonAssetR6, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
onFinish: function onFinish() {
tween(samplebuttonAssetR6, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
}
});
// Flash effect with magenta color
LK.effects.flashObject(samplebuttonAssetR6, 0xff00ff, 300);
score += 75;
combo += 1;
};
// --- Third Samplebutton Right Side Second Line ---
var samplebuttonAssetR7 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR7);
samplebuttonAssetR7.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR7.width / 2 - 350 + 4;
samplebuttonAssetR7.y = sampletableAsset.y - samplebuttonAssetR7.height / 2 - 250;
// Add functionality to seventh right side samplebutton
samplebuttonAssetR7.down = function (x, y, obj) {
// Play sample3 sound with different effect
LK.getSound('sample3').play();
// Visual feedback - pulsing tint animation
tween(samplebuttonAssetR7, {
tint: 0xffff00,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 120,
onFinish: function onFinish() {
tween(samplebuttonAssetR7, {
tint: 0xffffff,
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
// Flash effect with yellow color
LK.effects.flashObject(samplebuttonAssetR7, 0xffff00, 300);
score += 75;
combo += 1;
};
// --- Fourth Samplebutton Right Side Second Line ---
var samplebuttonAssetR8 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR8);
samplebuttonAssetR8.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR8.width / 2 - 500 + 4;
samplebuttonAssetR8.y = sampletableAsset.y - samplebuttonAssetR8.height / 2 - 250;
// --- Fourth Samplebutton Right Side First Line ---
var samplebuttonAssetR4 = LK.getAsset('samplebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(samplebuttonAssetR4);
samplebuttonAssetR4.x = sampletableAsset.x + sampletableAsset.width / 2 - samplebuttonAssetR4.width / 2 - 500 + 4;
samplebuttonAssetR4.y = sampletableAsset.y - samplebuttonAssetR4.height / 2 - 50;
// Add functionality to fourth right side samplebutton
samplebuttonAssetR4.down = function (x, y, obj) {
// Play sample4 sound
LK.getSound('sample4').play();
// Visual feedback - pulse animation
tween(samplebuttonAssetR4, {
alpha: 0.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(samplebuttonAssetR4, {
alpha: 1.0
}, {
duration: 80
});
}
});
// Flash effect with purple color
LK.effects.flashObject(samplebuttonAssetR4, 0xff00ff, 300);
score += 50;
combo += 1;
};
// Add functionality to eighth right side samplebutton
samplebuttonAssetR8.down = function (x, y, obj) {
// Play sample4 sound with different effect
LK.getSound('sample4').play();
// Visual feedback - alpha fade
tween(samplebuttonAssetR8, {
alpha: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
tween(samplebuttonAssetR8, {
alpha: 1.0
}, {
duration: 100
});
}
});
// Flash effect with orange color
LK.effects.flashObject(samplebuttonAssetR8, 0xff8000, 300);
score += 75;
combo += 1;
};
// --- Digital Clock (center of map) ---
var digitalClockTxt = new Text2('', {
size: 30,
fill: "#fff"
});
digitalClockTxt.anchor.set(0.5, 0.5);
digitalClockTxt.x = 2048 / 2;
digitalClockTxt.y = 2732 / 2 - 500;
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);
}
// 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;
// Check first samplebutton
var sb1 = samplebuttonAsset.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb1.x - samplebuttonAsset.width / 2, 2) + Math.pow(sb1.y - samplebuttonAsset.height / 2, 2) < 75 * 75) return samplebuttonAsset;
// Check second samplebutton
var sb2 = samplebuttonAsset2.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb2.x - samplebuttonAsset2.width / 2, 2) + Math.pow(sb2.y - samplebuttonAsset2.height / 2, 2) < 75 * 75) return samplebuttonAsset2;
// Check third samplebutton
var sb3 = samplebuttonAsset3.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb3.x - samplebuttonAsset3.width / 2, 2) + Math.pow(sb3.y - samplebuttonAsset3.height / 2, 2) < 75 * 75) return samplebuttonAsset3;
// Check fourth samplebutton
var sb4 = samplebuttonAsset4.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb4.x - samplebuttonAsset4.width / 2, 2) + Math.pow(sb4.y - samplebuttonAsset4.height / 2, 2) < 75 * 75) return samplebuttonAsset4;
// Check second line sample buttons
// Check fifth samplebutton (second line, first button)
var sb5 = samplebuttonAsset5.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb5.x - samplebuttonAsset5.width / 2, 2) + Math.pow(sb5.y - samplebuttonAsset5.height / 2, 2) < 75 * 75) return samplebuttonAsset5;
// Check sixth samplebutton (second line, second button)
var sb6 = samplebuttonAsset6.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb6.x - samplebuttonAsset6.width / 2, 2) + Math.pow(sb6.y - samplebuttonAsset6.height / 2, 2) < 75 * 75) return samplebuttonAsset6;
// Check seventh samplebutton (second line, third button)
var sb7 = samplebuttonAsset7.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb7.x - samplebuttonAsset7.width / 2, 2) + Math.pow(sb7.y - samplebuttonAsset7.height / 2, 2) < 75 * 75) return samplebuttonAsset7;
// Check eighth samplebutton (second line, fourth button)
var sb8 = samplebuttonAsset8.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sb8.x - samplebuttonAsset8.width / 2, 2) + Math.pow(sb8.y - samplebuttonAsset8.height / 2, 2) < 75 * 75) return samplebuttonAsset8;
// Check right side sample buttons
// Check first right side samplebutton (first line)
var sbR1 = samplebuttonAssetR1.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR1.x - samplebuttonAssetR1.width / 2, 2) + Math.pow(sbR1.y - samplebuttonAssetR1.height / 2, 2) < 75 * 75) return samplebuttonAssetR1;
// Check second right side samplebutton (first line)
var sbR2 = samplebuttonAssetR2.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR2.x - samplebuttonAssetR2.width / 2, 2) + Math.pow(sbR2.y - samplebuttonAssetR2.height / 2, 2) < 75 * 75) return samplebuttonAssetR2;
// Check third right side samplebutton (first line)
var sbR3 = samplebuttonAssetR3.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR3.x - samplebuttonAssetR3.width / 2, 2) + Math.pow(sbR3.y - samplebuttonAssetR3.height / 2, 2) < 75 * 75) return samplebuttonAssetR3;
// Check fourth right side samplebutton (first line)
var sbR4 = samplebuttonAssetR4.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR4.x - samplebuttonAssetR4.width / 2, 2) + Math.pow(sbR4.y - samplebuttonAssetR4.height / 2, 2) < 75 * 75) return samplebuttonAssetR4;
// Check fifth right side samplebutton (second line)
var sbR5 = samplebuttonAssetR5.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR5.x - samplebuttonAssetR5.width / 2, 2) + Math.pow(sbR5.y - samplebuttonAssetR5.height / 2, 2) < 75 * 75) return samplebuttonAssetR5;
// Check sixth right side samplebutton (second line)
var sbR6 = samplebuttonAssetR6.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR6.x - samplebuttonAssetR6.width / 2, 2) + Math.pow(sbR6.y - samplebuttonAssetR6.height / 2, 2) < 75 * 75) return samplebuttonAssetR6;
// Check seventh right side samplebutton (second line)
var sbR7 = samplebuttonAssetR7.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR7.x - samplebuttonAssetR7.width / 2, 2) + Math.pow(sbR7.y - samplebuttonAssetR7.height / 2, 2) < 75 * 75) return samplebuttonAssetR7;
// Check eighth right side samplebutton (second line)
var sbR8 = samplebuttonAssetR8.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(sbR8.x - samplebuttonAssetR8.width / 2, 2) + Math.pow(sbR8.y - samplebuttonAssetR8.height / 2, 2) < 75 * 75) return samplebuttonAssetR8;
// Check right loop exit button
var rightLoopExitLocal = rightLoopExitButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rightLoopExitLocal.x - rightLoopExitButton.width / 2, 2) + Math.pow(rightLoopExitLocal.y - rightLoopExitButton.height / 2, 2) < 50 * 50) return rightLoopExitButton;
// Check right in button
var rightInLocal = rightInButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rightInLocal.x - rightInButton.width / 2, 2) + Math.pow(rightInLocal.y - rightInButton.height / 2, 2) < 50 * 50) return rightInButton;
// Check right out button
var rightOutLocal = rightOutButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rightOutLocal.x - rightOutButton.width / 2, 2) + Math.pow(rightOutLocal.y - rightOutButton.height / 2, 2) < 50 * 50) return rightOutButton;
// Check left start button
var leftStartLocal = leftStartButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(leftStartLocal.x - leftStartButton.width / 2, 2) + Math.pow(leftStartLocal.y - leftStartButton.height / 2, 2) < 100 * 100) return leftStartButton;
// Check right start button
var rightStartLocal = rightStartButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rightStartLocal.x - rightStartButton.width / 2, 2) + Math.pow(rightStartLocal.y - rightStartButton.height / 2, 2) < 100 * 100) return rightStartButton;
// Check left cue button
var leftCueLocal = leftCueButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(leftCueLocal.x - leftCueButton.width / 2, 2) + Math.pow(leftCueLocal.y - leftCueButton.height / 2, 2) < 50 * 50) return leftCueButton;
// Check right cue button
var rightCueLocal = rightCueButton.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rightCueLocal.x - rightCueButton.width / 2, 2) + Math.pow(rightCueLocal.y - rightCueButton.height / 2, 2) < 50 * 50) return rightCueButton;
// Check vertical crossfader track
var verticalCfLocal = verticalCrossfaderTrack.toLocal(game.toGlobal({
x: x,
y: y
}));
if (verticalCfLocal.x > 0 && verticalCfLocal.x < verticalCrossfaderTrack.width && verticalCfLocal.y > 0 && verticalCfLocal.y < verticalCrossfaderTrack.height) return verticalCrossfaderTrack;
// Check second vertical crossfader track
var verticalCfLocal2 = verticalCrossfaderTrack2.toLocal(game.toGlobal({
x: x,
y: y
}));
if (verticalCfLocal2.x > 0 && verticalCfLocal2.x < verticalCrossfaderTrack2.width && verticalCfLocal2.y > 0 && verticalCfLocal2.y < verticalCrossfaderTrack2.height) return verticalCrossfaderTrack2;
// Check filter knobs
var lfh = leftFilterKnobHigh.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(lfh.x - leftFilterKnobHigh.width / 2, 2) + Math.pow(lfh.y - leftFilterKnobHigh.height / 2, 2) < 50 * 50) return leftFilterKnobHigh;
var lfm = leftFilterKnobMid.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(lfm.x - leftFilterKnobMid.width / 2, 2) + Math.pow(lfm.y - leftFilterKnobMid.height / 2, 2) < 50 * 50) return leftFilterKnobMid;
var lfl = leftFilterKnobLow.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(lfl.x - leftFilterKnobLow.width / 2, 2) + Math.pow(lfl.y - leftFilterKnobLow.height / 2, 2) < 50 * 50) return leftFilterKnobLow;
var rfh = rightFilterKnobHigh.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rfh.x - rightFilterKnobHigh.width / 2, 2) + Math.pow(rfh.y - rightFilterKnobHigh.height / 2, 2) < 50 * 50) return rightFilterKnobHigh;
var rfm = rightFilterKnobMid.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rfm.x - rightFilterKnobMid.width / 2, 2) + Math.pow(rfm.y - rightFilterKnobMid.height / 2, 2) < 50 * 50) return rightFilterKnobMid;
var rfl = rightFilterKnobLow.toLocal(game.toGlobal({
x: x,
y: y
}));
if (Math.pow(rfl.x - rightFilterKnobLow.width / 2, 2) + Math.pow(rfl.y - rightFilterKnobLow.height / 2, 2) < 50 * 50) return rightFilterKnobLow;
}
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) {
var local = dragging.toLocal(game.toGlobal({
x: x,
y: y
}));
if (dragging === verticalCrossfaderTrack) {
// Use the object's own move method for consistent behavior
dragging.move(local.x, local.y, obj);
} else if (dragging === verticalCrossfaderTrack2) {
// Use the object's own move method for consistent behavior
dragging.move(local.x, local.y, obj);
} else if (dragging === leftFilterKnobHigh || dragging === leftFilterKnobMid || dragging === leftFilterKnobLow || dragging === rightFilterKnobHigh || dragging === rightFilterKnobMid || dragging === rightFilterKnobLow) {
// Handle move for filter knobs
var angle = Math.atan2(local.y - dragging.height / 2, local.x - dragging.width / 2);
// Normalize angle to be between -PI and PI
if (angle > Math.PI) angle -= 2 * Math.PI;
if (angle < -Math.PI) angle += 2 * Math.PI;
// Filter knob rotation: -135 degrees to +135 degrees (approx -2.35 to 2.35 radians)
var minAngle = -Math.PI * 0.75;
var maxAngle = Math.PI * 0.75;
dragging.rotation = Math.max(minAngle, Math.min(maxAngle, angle));
} else if (dragging.move) {
// Handle move for other objects that have a move method
dragging.move(local.x, local.y, obj);
}
}
};
// --- Start Button State Variables ---
var leftStartButtonOn = false;
var rightStartButtonOn = false;
// --- Deck Spinning State Variables ---
var leftDeckSpinning = false;
var rightDeckSpinning = false;
// --- Left Start Button ---
var leftStartButton = LK.getAsset('Startbutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftStartButton);
leftStartButton.x = rimLeft.x - 200;
leftStartButton.y = rimLeft.y + rimLeft.height / 2 + leftStartButton.height / 2 + 20;
// --- Left Cue Button ---
var leftCueButton = LK.getAsset('Cuebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftCueButton);
leftCueButton.x = leftStartButton.x + leftStartButton.width / 2 + leftCueButton.width / 2 + 20 + 200 - 20;
leftCueButton.y = leftStartButton.y;
// Add functionality to left start button
leftStartButton.down = function (x, y, obj) {
// Visual feedback - scale animation
tween(leftStartButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
onFinish: function onFinish() {
tween(leftStartButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
}
});
// Toggle button state
leftStartButtonOn = !leftStartButtonOn;
// Stop any existing tween first
tween.stop(leftStartButton, {
tint: true
});
if (leftStartButtonOn) {
// Turn on green light effect
tween(leftStartButton, {
tint: 0x00ff00
}, {
duration: 200,
onFinish: function onFinish() {
// Create pulsing green light effect
function createPulse() {
if (leftStartButtonOn) {
// Only continue pulsing if still on
tween(leftStartButton, {
tint: 0x88ff88
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (leftStartButtonOn) {
// Only continue pulsing if still on
tween(leftStartButton, {
tint: 0x00ff00
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: createPulse
});
}
}
});
}
}
createPulse();
}
});
} else {
// Turn off light effect - return to normal tint
tween(leftStartButton, {
tint: 0xffffff
}, {
duration: 200
});
}
// Start/stop left deck
leftDeck.playing = !leftDeck.playing;
// Start/stop left deck spinning
leftDeckSpinning = !leftDeckSpinning;
if (leftDeckSpinning) {
// Start continuous clockwise spinning synchronized with deckplatter
var _spinLeftDeck = function spinLeftDeck() {
if (leftDeckSpinning) {
// Synchronize deck asset rotation with deckplatter rotation speed
// Deckplatter rotates at 0.02 radians per frame (60fps), so full rotation takes ~314 frames (~5.23 seconds)
// To match this, deck asset should complete rotation in same time
tween(leftDeckAsset, {
rotation: leftDeckAsset.rotation + Math.PI * 2
}, {
duration: 5233,
easing: tween.linear,
onFinish: _spinLeftDeck
});
}
};
_spinLeftDeck();
} else {
// Stop spinning by stopping any rotation tweens
tween.stop(leftDeckAsset, {
rotation: true
});
}
// Play beat sound
LK.getSound('beat').play();
// Add score bonus
score += 100;
combo += 1;
};
// --- Right Start Button ---
var rightStartButton = LK.getAsset('Startbutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightStartButton);
rightStartButton.x = rimRight.x - 200;
rightStartButton.y = rimRight.y + rimRight.height / 2 + rightStartButton.height / 2 + 20;
// --- Right Cue Button ---
var rightCueButton = LK.getAsset('Cuebutton', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightCueButton);
rightCueButton.x = rightStartButton.x + rightStartButton.width / 2 + rightCueButton.width / 2 + 20 + 200 - 20 - 5 - 5;
rightCueButton.y = rightStartButton.y;
// Add functionality to right start button
rightStartButton.down = function (x, y, obj) {
// Visual feedback - scale animation
tween(rightStartButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
onFinish: function onFinish() {
tween(rightStartButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
}
});
// Toggle button state
rightStartButtonOn = !rightStartButtonOn;
// Stop any existing tween first
tween.stop(rightStartButton, {
tint: true
});
if (rightStartButtonOn) {
// Turn on green light effect
tween(rightStartButton, {
tint: 0x00ff00
}, {
duration: 200,
onFinish: function onFinish() {
// Create pulsing green light effect
function createPulse() {
if (rightStartButtonOn) {
// Only continue pulsing if still on
tween(rightStartButton, {
tint: 0x88ff88
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (rightStartButtonOn) {
// Only continue pulsing if still on
tween(rightStartButton, {
tint: 0x00ff00
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: createPulse
});
}
}
});
}
}
createPulse();
}
});
} else {
// Turn off light effect - return to normal tint
tween(rightStartButton, {
tint: 0xffffff
}, {
duration: 200
});
}
// Start/stop right deck
rightDeck.playing = !rightDeck.playing;
// Start/stop right deck spinning
rightDeckSpinning = !rightDeckSpinning;
if (rightDeckSpinning) {
// Start continuous clockwise spinning synchronized with deckplatter
var _spinRightDeck = function spinRightDeck() {
if (rightDeckSpinning) {
// Synchronize deck asset rotation with deckplatter rotation speed
// Deckplatter rotates at 0.02 radians per frame (60fps), so full rotation takes ~314 frames (~5.23 seconds)
// To match this, deck asset should complete rotation in same time
tween(rightDeckAsset, {
rotation: rightDeckAsset.rotation + Math.PI * 2
}, {
duration: 5233,
easing: tween.linear,
onFinish: _spinRightDeck
});
}
};
_spinRightDeck();
} else {
// Stop spinning by stopping any rotation tweens
tween.stop(rightDeckAsset, {
rotation: true
});
}
// Play beat sound
LK.getSound('beat').play();
// Add score bonus
score += 100;
combo += 1;
};
// Add functionality to left cue button
leftCueButton.down = function (x, y, obj) {
// Visual feedback - scale animation
tween(leftCueButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(leftCueButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect with cyan color
LK.effects.flashObject(leftCueButton, 0x00ffff, 300);
// Reset left deck to start position
leftDeck.rotationOffset = 0;
leftDeck.currentRotation = 0;
leftDeck.rotation = 0;
// Play scratch sound
LK.getSound('scratch').play();
// Add score bonus
score += 50;
combo += 1;
};
// Add functionality to right cue button
rightCueButton.down = function (x, y, obj) {
// Visual feedback - scale animation
tween(rightCueButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(rightCueButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect with magenta color
LK.effects.flashObject(rightCueButton, 0xff00ff, 300);
// Reset right deck to start position
rightDeck.rotationOffset = 0;
rightDeck.currentRotation = 0;
rightDeck.rotation = 0;
// Play scratch sound
LK.getSound('scratch').play();
// Add score bonus
score += 50;
combo += 1;
};
// Center left deck platter to the left deck center
leftDeck.x = leftDeckAsset.x;
leftDeck.y = leftDeckAsset.y - 3;
// Center right deck platter to the right deck center
rightDeck.x = rightDeckAsset.x;
rightDeck.y = rightDeckAsset.y - 3;
// --- Screens above decks ---
// Left deck screen
var leftDeckScreen = LK.getAsset('screen', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftDeckScreen);
leftDeckScreen.x = leftDeckAsset.x;
leftDeckScreen.y = leftDeckAsset.y - leftDeckAsset.height / 2 - leftDeckScreen.height / 2 - 50 - 200;
// Right deck screen
var rightDeckScreen = LK.getAsset('screen', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightDeckScreen);
rightDeckScreen.x = rightDeckAsset.x;
rightDeckScreen.y = rightDeckAsset.y - rightDeckAsset.height / 2 - rightDeckScreen.height / 2 - 50 - 200;
// --- Effect Buttons Above Screens ---
var effectButtons = [];
var effectButtonSpacing = 220;
var effectButtonY = leftDeckScreen.y - leftDeckScreen.height / 2 - 80 - 55;
// Create 6 effect buttons (3 above each screen)
for (var i = 0; i < 6; i++) {
var effectButton = LK.getAsset('Effect', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(effectButton);
// Position 3 buttons above left screen, 3 above right screen
if (i < 3) {
// Left side buttons
effectButton.x = leftDeckScreen.x - effectButtonSpacing + i * effectButtonSpacing;
} else {
// Right side buttons
effectButton.x = rightDeckScreen.x - effectButtonSpacing + (i - 3) * effectButtonSpacing;
}
effectButton.y = effectButtonY;
// Add 'FIRE' text above the first effect button (i === 0)
if (i === 0) {
var fireText = new Text2('FIRE', {
size: 36,
fill: 0xFFFFFF
});
fireText.anchor.set(0.5, 0.5);
fireText.x = effectButton.x;
fireText.y = effectButton.y - effectButton.height / 2 - 80;
game.addChild(fireText);
}
// Add 'LASER' text above the second effect button (i === 1)
if (i === 1) {
var laserText = new Text2('LASER', {
size: 36,
fill: 0xFFFFFF
});
laserText.anchor.set(0.5, 0.5);
laserText.x = effectButton.x;
laserText.y = effectButton.y - effectButton.height / 2 - 80;
game.addChild(laserText);
}
// Add 'LIGHT' text above the third effect button (i === 2)
if (i === 2) {
var lightText = new Text2('LIGHT', {
size: 36,
fill: 0xFFFFFF
});
lightText.anchor.set(0.5, 0.5);
lightText.x = effectButton.x;
lightText.y = effectButton.y - effectButton.height / 2 - 80;
game.addChild(lightText);
// Store the third effect button for later access
effectButtons[i] = effectButton;
}
// Add 'SMOKE' text above the fourth effect button (i === 3)
if (i === 3) {
var smokeText = new Text2('SMOKE', {
size: 36,
fill: 0xFFFFFF
});
smokeText.anchor.set(0.5, 0.5);
smokeText.x = effectButton.x;
smokeText.y = effectButton.y - effectButton.height / 2 - 80;
game.addChild(smokeText);
}
// Add 'UV' text above the fifth effect button (i === 4)
if (i === 4) {
var uvText = new Text2('UV', {
size: 36,
fill: 0xFFFFFF
});
uvText.anchor.set(0.5, 0.5);
uvText.x = effectButton.x;
uvText.y = effectButton.y - effectButton.height / 2 - 80;
game.addChild(uvText);
}
// Add 'GIFTS' text above the sixth effect button (i === 5)
if (i === 5) {
var giftsText = new Text2('GIFTS', {
size: 36,
fill: 0xFFFFFF
});
giftsText.anchor.set(0.5, 0.5);
giftsText.x = effectButton.x;
giftsText.y = effectButton.y - effectButton.height / 2 - 80;
game.addChild(giftsText);
}
// Scale up effect button by 1.5
tween(effectButton, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut
});
// Add up handler for SMOKE button (index 3)
if (i === 3) {
effectButton.down = function (x, y, obj) {
smokeEffectActive = true;
fogAnimationActive = true;
// Clear any existing fog timeout
if (fogTimeout) {
LK.clearTimeout(fogTimeout);
fogTimeout = null;
}
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
LK.getSound('fx').play();
LK.effects.flashObject(this, 0x888888, 200);
};
effectButton.up = function (x, y, obj) {
smokeEffectActive = false;
// Start 4 second countdown to turn off fog animation
fogTimeout = LK.setTimeout(function () {
fogAnimationActive = false;
fogTimeout = null;
}, 4000);
// Optionally, provide a quick scale feedback on release
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 100
});
};
} else {
// Add functionality to other effect buttons
effectButton.down = function (x, y, obj) {
// Visual feedback - scale animation that returns to 1.5 instead of 1.0
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
// Play FX sound
LK.getSound('fx').play();
// Flash effect with random color
var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
LK.effects.flashObject(this, randomColor, 400);
// Add score bonus
score += 75;
combo += 1;
};
}
// If this is the first effect button (index 0) - FIRE button
if (i === 0) {
effectButton.down = function (x, y, obj) {
// Visual feedback - scale animation that returns to 1.5 instead of 1.0
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
// Play FX sound
LK.getSound('fx').play();
// Cycle through fire effect modes: 0 (off) -> 1 (normal) -> 2 (big) -> 0 (off)
fireEffectMode = (fireEffectMode + 1) % 3;
// Flash effect with different colors based on mode
var fireColors;
if (fireEffectMode === 0) {
// Off mode - gray color
fireColors = [0x888888];
} else if (fireEffectMode === 1) {
// Normal mode - orange colors
fireColors = [0xff8000, 0xff4400, 0xffaa00];
} else {
// Big flames mode - red colors
fireColors = [0xff0000, 0xff4444, 0xff8888];
}
var randomColor = fireColors[Math.floor(Math.random() * fireColors.length)];
LK.effects.flashObject(this, randomColor, 400);
// Add score bonus
score += 75;
combo += 1;
};
}
effectButtons.push(effectButton);
// If this is the second effect button (index 1) - LASER button
if (i === 1) {
effectButton.down = function (x, y, obj) {
// Visual feedback - scale animation that returns to 1.5 instead of 1.0
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
// Play FX sound
LK.getSound('fx').play();
// Cycle through laser show modes: 0 (off) -> 1 (slow) -> 2 (fast) -> 0 (off)
laserShowMode = (laserShowMode + 1) % 3;
// Flash effect with different colors based on mode
var laserColors;
if (laserShowMode === 0) {
// Off mode - gray color
laserColors = [0x888888];
} else if (laserShowMode === 1) {
// Slow mode - blue colors
laserColors = [0x0000ff, 0x4444ff, 0x8888ff];
} else {
// Fast mode - rainbow colors
laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
}
var randomColor = laserColors[Math.floor(Math.random() * laserColors.length)];
LK.effects.flashObject(this, randomColor, 400);
// Add score bonus
score += 75;
combo += 1;
};
}
// If this is the third effect button (index 2)
if (i === 2) {
effectButton.down = function (x, y, obj) {
// Visual feedback - scale animation that returns to 1.5 instead of 1.0
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
// Play FX sound
LK.getSound('fx').play();
// Flash effect with random color
var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
LK.effects.flashObject(this, randomColor, 400);
// Add score bonus
score += 75;
combo += 1;
// Toggle RGB flashing state
isRgbFlashingActive = !isRgbFlashingActive;
};
}
// Define variable to track the gifts button mode
var giftsMode = 0; // 0: off, 1: balloons, 2: money, 3: fireworks
// Reference the gifts effect button
var giftsEffectButton;
// If this is the fifth effect button (index 4) - UV button
if (i === 4) {
effectButton.down = function (x, y, obj) {
// Visual feedback - scale animation that returns to 1.5 instead of 1.0
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
// Play FX sound
LK.getSound('fx').play();
// Toggle UV effect state
uvEffectActive = !uvEffectActive;
// Flash effect with different colors based on mode
var flashColor = uvEffectActive ? 0x8000ff : 0x888888; // Purple for ON, gray for OFF
LK.effects.flashObject(this, flashColor, 400);
// Add score bonus
score += 75;
combo += 1;
};
// Add a reference to the UV button for flashing in update
effectButtons[4] = effectButton;
}
// If this is the sixth effect button (index 5) - GIFTS button
if (i === 5) {
giftsEffectButton = effectButton; // Store reference to gifts button
effectButton.down = function (x, y, obj) {
// Cycle through gift modes
giftsMode = (giftsMode + 1) % 4;
// Visual feedback - scale animation
tween(this, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 150,
onFinish: function () {
tween(this, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150
});
}.bind(this)
});
// Play FX sound
LK.getSound('fx').play();
// Add score bonus
score += 100;
combo += 1;
};
}
}
// Tablet between the two screens
var tabletAsset = LK.getAsset('tablet', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(tabletAsset);
// Ensure screens exist before positioning tablet
if (typeof leftDeckScreen !== 'undefined' && typeof rightDeckScreen !== 'undefined') {
tabletAsset.x = (leftDeckScreen.x + rightDeckScreen.x) / 2;
tabletAsset.y = leftDeckScreen.y + 77 + 35 - 20;
} else {
// Fallback positioning if screens are not defined
tabletAsset.x = 2048 / 2;
tabletAsset.y = 1366;
}
// Create rekordbox app instance
var rekordboxApp = new RekordboxApp();
game.addChild(rekordboxApp);
// Position and scale rekordbox app to fit tablet screen
rekordboxApp.x = tabletAsset.x - tabletAsset.width / 2 + 20 + 30;
rekordboxApp.y = tabletAsset.y - tabletAsset.height / 2 + 20 - 200 - 25;
// Create keyboard app instance
var keyboardApp = new KeyboardApp();
game.addChild(keyboardApp);
// Position and scale keyboard app under the tracklist
keyboardApp.x = rekordboxApp.x - 777; // Align with rekordbox app and move left by 777 units
keyboardApp.y = rekordboxApp.y + rekordboxApp.height + 10 + 777 - 300 + 200 - 100 - 100; // Position below tracklist with spacing and move down by 777 units, then up by 300, then down by 200, then up by 100, then up by 100
keyboardApp.scaleX = rekordboxApp.scaleX * 1.1; // Match scale and adjust slightly
keyboardApp.scaleY = keyboardApp.scaleX; // Maintain aspect ratio
keyboardApp.visible = false; // Hide the keyboard initially
// Scale up keyboard app to 3x size using tween animation
tween(keyboardApp, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 500,
easing: tween.easeOut
});
// Scale down to better fit the tablet asset by 1.1
tween(rekordboxApp, {
scaleX: 0.95 / 1.1,
scaleY: 0.95 / 1.1
}, {
duration: 500,
easing: tween.easeOut
});
// Add tablet press functionality to pop up fullscreen tracklist
tabletAsset.down = function (x, y, obj) {
// Create fullscreen tracklist popup
var fullscreenTracklist = new RekordboxApp();
// Add to game at top layer
game.addChild(fullscreenTracklist);
keyboardApp.visible = true; // Show the keyboard app
// Ensure keyboard is above the overlay and tracklist
game.addChild(keyboardApp);
// Position fullscreen - center on screen and move to top
fullscreenTracklist.x = 2048 / 2 - 371 - 600; // Half of app width (742/2)
fullscreenTracklist.y = 0 + 269; // Move to top of tablet height
// Scale to fullscreen size
fullscreenTracklist.scaleX = 2.5 * 1.05;
fullscreenTracklist.scaleY = 2.5 * 1.05;
// Create semi-transparent background overlay
var overlay = LK.getAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732
});
overlay.tint = 0x000000;
overlay.alpha = 0.7;
overlay.x = 0;
overlay.y = 0;
// Add overlay behind the app
game.addChild(overlay);
game.setChildIndex(overlay, game.children.indexOf(fullscreenTracklist) - 1);
// Add close functionality when clicking overlay
overlay.down = function (x, y, obj) {
// Remove fullscreen tracklist and overlay
if (game.children.indexOf(fullscreenTracklist) !== -1) {
game.removeChild(fullscreenTracklist);
}
if (game.children.indexOf(overlay) !== -1) {
game.removeChild(overlay);
}
keyboardApp.visible = false; // Hide the keyboard app
};
// Visual feedback for tablet press
tween(tabletAsset, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(tabletAsset, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
}
});
// Play sound effect
LK.getSound('fx').play();
};
// --- IN Button under tablet screen ---
var inButton = LK.getAsset('in', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(inButton);
inButton.x = tabletAsset.x - 700 - 200 - 12;
inButton.y = tabletAsset.y + tabletAsset.height / 2 + inButton.height / 2 + 20 - 300 + 77;
// Add whitegray text: ADJUST above in buttons
var adjustTextLeft = new Text2('ADJUST', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
adjustTextLeft.anchor.set(0.5, 0.5);
adjustTextLeft.x = inButton.x + 107;
adjustTextLeft.y = inButton.y - inButton.height / 2 - 20; // Position above the button
game.addChild(adjustTextLeft);
// Add whitegray text: IN under in buttons
var inTextLeft = new Text2('IN', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
inTextLeft.anchor.set(0.5, 0.5);
inTextLeft.x = inButton.x;
inTextLeft.y = inButton.y + inButton.height / 2 + 20; // Position under the button
game.addChild(inTextLeft);
// Add functionality to IN button
inButton.down = function (x, y, obj) {
// Stop any existing flashing effect first
tween.stop(inButton, {
tint: true
});
// Start flashing orange light effect
function createOrangeFlash() {
tween(inButton, {
tint: 0xff8000 // Orange color
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(inButton, {
tint: 0xffffff // Back to white/normal
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: createOrangeFlash // Continue flashing
});
}
});
}
createOrangeFlash();
// Add score bonus
score += 25;
combo += 1;
};
// --- OUT Button under tablet screen ---
var outButton = LK.getAsset('Out', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(outButton);
outButton.x = tabletAsset.x + 700 + 200;
outButton.y = tabletAsset.y + tabletAsset.height / 2 + outButton.height / 2 + 20 - 300 + 77;
// --- Right IN Button to the left of right OUT button ---
var rightInButton = LK.getAsset('in', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightInButton);
rightInButton.x = outButton.x - 192 - 200 - 12;
rightInButton.y = outButton.y;
// Create right ADJUST text now that rightInButton is defined
var adjustTextRight = new Text2('ADJUST', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
adjustTextRight.anchor.set(0.5, 0.5);
adjustTextRight.x = rightInButton.x + 107;
adjustTextRight.y = rightInButton.y - rightInButton.height / 2 - 20; // Position above the button
game.addChild(adjustTextRight);
// Add whitegray text: IN under right in buttons
var inTextRight = new Text2('IN', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
inTextRight.anchor.set(0.5, 0.5);
inTextRight.x = rightInButton.x;
inTextRight.y = rightInButton.y + rightInButton.height / 2 + 20; // Position under the button
game.addChild(inTextRight);
// Add functionality to right IN button
rightInButton.down = function (x, y, obj) {
// Stop any existing flashing effect first
tween.stop(rightInButton, {
tint: true
});
// Start flashing orange light effect
function createOrangeFlash() {
tween(rightInButton, {
tint: 0xff8000 // Orange color
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(rightInButton, {
tint: 0xffffff // Back to white/normal
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: createOrangeFlash // Continue flashing
});
}
});
}
createOrangeFlash();
// Add score bonus
score += 25;
combo += 1;
};
// --- OUT Button above left deck ---
var outButtonLeft = LK.getAsset('Out', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(outButtonLeft);
outButtonLeft.x = leftDeckAsset.x + 192 - 200;
outButtonLeft.y = inButton.y;
// --- Loop Exit Button on the right side of left out button ---
var loopExitButton = LK.getAsset('LoopExit', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(loopExitButton);
loopExitButton.x = outButtonLeft.x + 150 + 50;
loopExitButton.y = outButtonLeft.y;
// Add whitegray text: OUT under left out button
var outTextLeft = new Text2('OUT', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
outTextLeft.anchor.set(0.5, 0.5);
outTextLeft.x = outButtonLeft.x;
outTextLeft.y = inTextLeft.y - 3; // Position at same height as IN text, moved up by 3 units
game.addChild(outTextLeft);
// Add whitegray text: LOOP between left out button and left loop exit button
var loopTextLeft = new Text2('LOOP', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
loopTextLeft.anchor.set(0.5, 0.5);
loopTextLeft.x = (outButtonLeft.x + loopExitButton.x) / 2;
loopTextLeft.y = adjustTextLeft.y; // Position at same height as ADJUST text
game.addChild(loopTextLeft);
// Add whitegray text: EXIT under left loop exit button
var exitTextLeft = new Text2('EXIT', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
exitTextLeft.anchor.set(0.5, 0.5);
exitTextLeft.x = loopExitButton.x;
exitTextLeft.y = loopExitButton.y + loopExitButton.height / 2 + 20; // Position under the button
game.addChild(exitTextLeft);
// Add functionality to loop exit button
loopExitButton.down = function (x, y, obj) {
// Visual feedback - scale animation
tween(loopExitButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(loopExitButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect with purple color
LK.effects.flashObject(loopExitButton, 0x8000ff, 300);
// Add score bonus
score += 30;
combo += 1;
};
// Add functionality to left OUT button
outButtonLeft.down = function (x, y, obj) {
// Stop any existing flashing effect first
tween.stop(outButtonLeft, {
tint: true
});
// Stop flashing the corresponding 'in' button
tween.stop(inButton, {
tint: true
});
inButton.tint = 0xffffff; // Reset tint to normal
// Start flashing orange light effect
function createOrangeFlash() {
tween(outButtonLeft, {
tint: 0xff8000 // Orange color
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(outButtonLeft, {
tint: 0xffffff // Back to white/normal
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: createOrangeFlash // Continue flashing
});
}
});
}
createOrangeFlash();
// Add score bonus
score += 25;
combo += 1;
};
// --- Right OUT Button ---
var rightOutButton = LK.getAsset('Out', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightOutButton);
rightOutButton.x = rightDeckAsset.x + 192 - 200;
rightOutButton.y = inButton.y;
// --- Right Loop Exit Button next to right out button ---
var rightLoopExitButton = LK.getAsset('LoopExit', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightLoopExitButton);
rightLoopExitButton.x = rightOutButton.x + 150 + 50 + 5;
rightLoopExitButton.y = rightOutButton.y;
// Add functionality to right loop exit button
rightLoopExitButton.down = function (x, y, obj) {
// Visual feedback - scale animation
tween(rightLoopExitButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(rightLoopExitButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Flash effect with purple color
LK.effects.flashObject(rightLoopExitButton, 0x8000ff, 300);
// Add score bonus
score += 30;
combo += 1;
};
// Add whitegray text: OUT under right out button
var outTextRight = new Text2('OUT', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
outTextRight.anchor.set(0.5, 0.5);
outTextRight.x = rightOutButton.x;
outTextRight.y = inTextRight.y - 3; // Position at same height as IN text, moved up by 3 units
game.addChild(outTextRight);
// Add whitegray text: LOOP between right out button and right loop exit button
var loopTextRight = new Text2('LOOP', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
loopTextRight.anchor.set(0.5, 0.5);
loopTextRight.x = (rightOutButton.x + rightLoopExitButton.x) / 2;
loopTextRight.y = adjustTextRight.y; // Position at same height as ADJUST text
game.addChild(loopTextRight);
// Add whitegray text: EXIT under right loop exit button
var exitTextRight = new Text2('EXIT', {
size: 24,
fill: 0xCCCCCC // Whitegray color
});
exitTextRight.anchor.set(0.5, 0.5);
exitTextRight.x = rightLoopExitButton.x;
exitTextRight.y = rightLoopExitButton.y + rightLoopExitButton.height / 2 + 20; // Position under the button
game.addChild(exitTextRight);
// Add functionality to right OUT button
rightOutButton.down = function (x, y, obj) {
// Stop any existing flashing effect first
tween.stop(rightOutButton, {
tint: true
});
// Stop flashing the corresponding 'in' button
tween.stop(rightInButton, {
tint: true
});
rightInButton.tint = 0xffffff; // Reset tint to normal
// Start flashing orange light effect
function createOrangeFlash() {
tween(rightOutButton, {
tint: 0xff8000 // Orange color
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(rightOutButton, {
tint: 0xffffff // Back to white/normal
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: createOrangeFlash // Continue flashing
});
}
});
}
createOrangeFlash();
// Add score bonus
score += 25;
combo += 1;
};
// --- Connecting Lines between In and Out Buttons ---
// Line between inButton and outButtonLeft
var lineInToOutLeft = LK.getAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: Math.abs(outButtonLeft.x - inButton.x) - 120,
height: 3
});
game.addChild(lineInToOutLeft);
lineInToOutLeft.x = (inButton.x + outButtonLeft.x) / 2;
lineInToOutLeft.y = inButton.y;
lineInToOutLeft.tint = 0x888888; // Gray color
// Line between rightInButton and rightOutButton
var lineRightInToOut = LK.getAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: Math.abs(rightOutButton.x - rightInButton.x) - 120,
height: 3
});
game.addChild(lineRightInToOut);
lineRightInToOut.x = (rightInButton.x + rightOutButton.x) / 2;
lineRightInToOut.y = rightInButton.y;
lineRightInToOut.tint = 0x888888; // Gray color
// Add a vertical crossfader track
var verticalCrossfaderTrack = LK.getAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: 12,
height: 500
});
game.addChild(verticalCrossfaderTrack);
verticalCrossfaderTrack.x = djdeckAsset.x - 250;
verticalCrossfaderTrack.y = djdeckAsset.y - 100 + 400;
// Add a knob onto the vertical crossfader track
var verticalCrossfaderKnob = LK.getAsset('crossfaderKnob', {
anchorX: 0.5,
anchorY: 0.5,
rotation: Math.PI / 2
});
game.addChild(verticalCrossfaderKnob);
verticalCrossfaderKnob.x = verticalCrossfaderTrack.x;
verticalCrossfaderKnob.y = verticalCrossfaderTrack.y;
// Add a second vertical crossfader track
var verticalCrossfaderTrack2 = LK.getAsset('crossfaderTrack', {
anchorX: 0.5,
anchorY: 0.5,
width: 12,
height: 500
});
game.addChild(verticalCrossfaderTrack2);
verticalCrossfaderTrack2.x = djdeckAsset.x + 250; // Position on the right side
verticalCrossfaderTrack2.y = djdeckAsset.y - 100 + 400;
// Add a knob onto the second vertical crossfader track
var verticalCrossfaderKnob2 = LK.getAsset('crossfaderKnob', {
anchorX: 0.5,
anchorY: 0.5,
rotation: Math.PI / 2
});
game.addChild(verticalCrossfaderKnob2);
verticalCrossfaderKnob2.x = verticalCrossfaderTrack2.x;
verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y;
// Add filter knobs
var leftFilterKnobHigh = LK.getAsset('filterKnob', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftFilterKnobHigh);
leftFilterKnobHigh.x = leftDeckAsset.x + 190 + 400;
leftFilterKnobHigh.y = leftDeckAsset.y - 100;
var leftFilterKnobMid = LK.getAsset('filterKnob', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftFilterKnobMid);
leftFilterKnobMid.x = leftDeckAsset.x + 190 + 400;
leftFilterKnobMid.y = leftDeckAsset.y;
var leftFilterKnobLow = LK.getAsset('filterKnob', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(leftFilterKnobLow);
leftFilterKnobLow.x = leftDeckAsset.x + 190 + 400;
leftFilterKnobLow.y = leftDeckAsset.y + 100;
var rightFilterKnobHigh = LK.getAsset('filterKnob', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightFilterKnobHigh);
rightFilterKnobHigh.x = rightDeckAsset.x - 190 - 400;
rightFilterKnobHigh.y = rightDeckAsset.y - 100;
var rightFilterKnobMid = LK.getAsset('filterKnob', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightFilterKnobMid);
rightFilterKnobMid.x = rightDeckAsset.x - 190 - 400;
rightFilterKnobMid.y = rightDeckAsset.y;
var rightFilterKnobLow = LK.getAsset('filterKnob', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(rightFilterKnobLow);
rightFilterKnobLow.x = rightDeckAsset.x - 190 - 400;
rightFilterKnobLow.y = rightDeckAsset.y + 100;
// --- Vertical crossfader state ---
var verticalCrossfaderValue = 0.5; // 0 = top, 1 = bottom
var verticalCrossfaderDragging = false;
// Second vertical crossfader state
var verticalCrossfaderValue2 = 0.5; // 0 = top, 1 = bottom
var verticalCrossfaderDragging2 = false;
var isRgbFlashingActive = false; // State variable for RGB flashing effect
var laserShowMode = 0; // Laser show state: 0 = off, 1 = slow, 2 = fast
var fireEffectMode = 0; // Fire effect state: 0 = off, 1 = normal flames, 2 = big flames
var uvEffectActive = false; // UV effect state: false = off, true = on
var uvBackgroundColorTween = null; // Variable to hold the background color tween
// Add vertical crossfader functionality with improved touch detection
verticalCrossfaderTrack.down = function (x, y, obj) {
verticalCrossfaderDragging = true;
// Use relative position within track bounds for precise control
var relativeY = (y + verticalCrossfaderTrack.height / 2) / verticalCrossfaderTrack.height;
verticalCrossfaderValue = Math.max(0, Math.min(1, relativeY));
// Update knob position smoothly
verticalCrossfaderKnob.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + verticalCrossfaderValue * verticalCrossfaderTrack.height;
};
verticalCrossfaderTrack.up = function (x, y, obj) {
verticalCrossfaderDragging = false;
};
verticalCrossfaderTrack.move = function (x, y, obj) {
if (verticalCrossfaderDragging) {
// Use relative position within track bounds for precise control
var relativeY = (y + verticalCrossfaderTrack.height / 2) / verticalCrossfaderTrack.height;
verticalCrossfaderValue = Math.max(0, Math.min(1, relativeY));
// Update knob position smoothly
verticalCrossfaderKnob.y = verticalCrossfaderTrack.y - verticalCrossfaderTrack.height / 2 + verticalCrossfaderValue * verticalCrossfaderTrack.height;
}
};
// Add vertical crossfader functionality for the second crossfader with improved touch detection
verticalCrossfaderTrack2.down = function (x, y, obj) {
verticalCrossfaderDragging2 = true;
// Use relative position within track bounds for precise control
var relativeY = (y + verticalCrossfaderTrack2.height / 2) / verticalCrossfaderTrack2.height;
verticalCrossfaderValue2 = Math.max(0, Math.min(1, relativeY));
// Update knob position smoothly
verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + verticalCrossfaderValue2 * verticalCrossfaderTrack2.height;
};
verticalCrossfaderTrack2.up = function (x, y, obj) {
verticalCrossfaderDragging2 = false;
};
verticalCrossfaderTrack2.move = function (x, y, obj) {
if (verticalCrossfaderDragging2) {
// Use relative position within track bounds for precise control
var relativeY = (y + verticalCrossfaderTrack2.height / 2) / verticalCrossfaderTrack2.height;
verticalCrossfaderValue2 = Math.max(0, Math.min(1, relativeY));
// Update knob position smoothly
verticalCrossfaderKnob2.y = verticalCrossfaderTrack2.y - verticalCrossfaderTrack2.height / 2 + verticalCrossfaderValue2 * verticalCrossfaderTrack2.height;
}
};
var leftEqualizer = new EqualizerBars();
game.addChild(leftEqualizer);
leftEqualizer.x = leftDeckScreen.x;
leftEqualizer.y = leftDeckScreen.y + 50;
// Add equalizer to right deck screen
var rightEqualizer = new EqualizerBars();
game.addChild(rightEqualizer);
rightEqualizer.x = rightDeckScreen.x;
rightEqualizer.y = rightDeckScreen.y + 50;
// Ensure deck platters are always at the top of the display order
if (game.children.indexOf(leftDeck) !== -1) {
game.setChildIndex(leftDeck, game.children.length - 1);
}
if (game.children.indexOf(rightDeck) !== -1) {
game.setChildIndex(rightDeck, game.children.length - 1);
}
// --- Game update ---
game.update = function () {
// Update discoball animation
discoball.update();
// Update fog effect animation
fogEffect.update();
// Update confetti effect animation
confettiEffect.update();
// Update balloons animation
if (balloons) {
for (var i = 0; i < balloons.length; i++) {
balloons[i].update();
}
}
// Update money particles animation
if (moneyParticles) {
for (var i = 0; i < moneyParticles.length; i++) {
moneyParticles[i].update();
}
}
// Update first dancing people line animations (first set created)
for (var i = 0; i < Math.min(dancingPeople.length, 8); i++) {
var person = dancingPeople[i];
// Check if UV effect is active
if (uvEffectActive) {
// If not already a UV person, swap to UV asset
if (!person.isUV) {
var uvAssetId = i % 2 === 0 ? 'UVW' : 'UWM';
var currentScaleX = person.scale.x;
var currentScaleY = person.scale.y;
var currentRotation = person.rotation;
var currentAlpha = person.alpha;
var currentX = person.x;
var currentY = person.y;
var currentBaseY = person.baseY;
var currentPhaseOffset = person.phaseOffset;
// Remove the old graphic child
if (person.children.length > 0) {
person.removeChildAt(0);
}
// Add the new UV graphic child
var uvGraphic = person.attachAsset(uvAssetId, {
anchorX: 0.5,
anchorY: 1.0
});
// Restore previous transform and animation properties
person.scale.set(currentScaleX, currentScaleY);
person.rotation = currentRotation;
person.alpha = currentAlpha;
person.x = currentX;
person.y = currentY;
person.baseY = currentBaseY;
person.phaseOffset = currentPhaseOffset;
person.isUV = true;
}
} else {
// If UV effect is off and it's a UV person, swap back to normal asset
if (person.isUV) {
var normalAssetId = i % 2 === 0 ? 'dancingWoman' : 'dancingMan';
var currentScaleX = person.scale.x;
var currentScaleY = person.scale.y;
var currentRotation = person.rotation;
var currentAlpha = person.alpha;
var currentX = person.x;
var currentY = person.y;
var currentBaseY = person.baseY;
var currentPhaseOffset = person.phaseOffset;
// Remove the old graphic child
if (person.children.length > 0) {
person.removeChildAt(0);
}
// Add the new normal graphic child
var normalGraphic = person.attachAsset(normalAssetId, {
anchorX: 0.5,
anchorY: 1.0
});
// Restore previous transform and animation properties
person.scale.set(currentScaleX, currentScaleY);
person.rotation = currentRotation;
person.alpha = currentAlpha;
person.x = currentX;
person.y = currentY;
person.baseY = currentBaseY;
person.phaseOffset = currentPhaseOffset;
person.isUV = false;
}
}
person.update();
}
// Update second dancing people line animations (second set created) - swap with UV assets when UV is active
for (var i = 8; i < dancingPeople.length; i++) {
var person = dancingPeople[i];
// Check if UV effect is active
if (uvEffectActive) {
// If not already a UV person, swap to UV asset
if (!person.isUV) {
var uvAssetId = (i - 8) % 2 === 0 ? 'UVW' : 'UWM';
var currentScaleX = person.scale.x;
var currentScaleY = person.scale.y;
var currentRotation = person.rotation;
var currentAlpha = person.alpha;
var currentX = person.x;
var currentY = person.y;
var currentBaseY = person.baseY;
var currentPhaseOffset = person.phaseOffset;
// Remove the old graphic child
if (person.children.length > 0) {
person.removeChildAt(0);
}
// Add the new UV graphic child
var uvGraphic = person.attachAsset(uvAssetId, {
anchorX: 0.5,
anchorY: 1.0
});
// Restore previous transform and animation properties
person.scale.set(currentScaleX, currentScaleY);
person.rotation = currentRotation;
person.alpha = currentAlpha;
person.x = currentX;
person.y = currentY;
person.baseY = currentBaseY;
person.phaseOffset = currentPhaseOffset;
person.isUV = true;
}
} else {
// If UV effect is off and it's a UV person, swap back to normal asset
if (person.isUV) {
var normalAssetId = (i - 8) % 2 === 0 ? 'dancingWoman' : 'dancingMan';
var currentScaleX = person.scale.x;
var currentScaleY = person.scale.y;
var currentRotation = person.rotation;
var currentAlpha = person.alpha;
var currentX = person.x;
var currentY = person.y;
var currentBaseY = person.baseY;
var currentPhaseOffset = person.phaseOffset;
// Remove the old graphic child
if (person.children.length > 0) {
person.removeChildAt(0);
}
// Add the new normal graphic child
var normalGraphic = person.attachAsset(normalAssetId, {
anchorX: 0.5,
anchorY: 1.0
});
// Restore previous transform and animation properties
person.scale.set(currentScaleX, currentScaleY);
person.rotation = currentRotation;
person.alpha = currentAlpha;
person.x = currentX;
person.y = currentY;
person.baseY = currentBaseY;
person.phaseOffset = currentPhaseOffset;
person.isUV = false;
}
}
person.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 laser show animation
laserShow.update();
// Update fireworks effect animation
fireworksEffect.update();
fireworksEffect2.update();
fireworksEffect3.update();
fireworksEffect4.update();
// Update Ferris wheel animation
if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) {
// Defensive check
ferrisWheel.update();
}
// Update smoke diffuser animation
// --- Synchronize all smoke diffusers so they emit smoke puffs at the same time ---
if (typeof smokeDiffusers !== 'undefined' && smokeDiffusers.length > 0) {
// Use the first smoke diffuser as the "master" for timing
var now = Date.now();
// All diffusers share the same "now" and will create puffs in sync
for (var i = 0; i < smokeDiffusers.length; i++) {
var sd = smokeDiffusers[i];
// Patch: override the update method to use the same "now" for all
if (sd && typeof sd.update === 'function') {
// Save original Date.now
var origDateNow = Date.now;
Date.now = function () {
return now;
};
sd.update();
Date.now = origDateNow;
}
}
}
// Update flamethrower animation
if (typeof flamethrower !== 'undefined' && flamethrower.update) {
flamethrower.update();
}
// Update decks
leftDeck.update();
rightDeck.update();
// Update equalizers
leftEqualizer.update();
rightEqualizer.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)
// Only animate the background color if RGB flashing is active on the third effect button
if (isRgbFlashingActive) {
game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]);
} else if (uvEffectActive) {
// If UV effect is active, flash between indigo blue and purple
var uvTime = Date.now() * 0.002; // Faster flashing for UV
var uvHue = uvTime * 360 % 360;
// Cycle between indigo (around 240 hue) and purple (around 270 hue)
var targetHue = 240 + Math.sin(uvTime * Math.PI * 2) * 30;
targetHue = (targetHue + 360) % 360;
var uvRgb = hsvToRgb(targetHue / 360, 0.8, 0.7); // Adjust saturation and value for UV colors
game.setBackgroundColor(uvRgb[0] << 16 | uvRgb[1] << 8 | uvRgb[2]);
} else {
// If neither is active, set a default background color
game.setBackgroundColor(0x000000); // Or any other default color
}
// Win condition disabled
// --- Update Digital Clock ---
if (typeof digitalClockTxt !== 'undefined') {
var now = new Date();
digitalClockTxt.setText(formatTime(now));
}
// Display laser flashing effect on the second effect button's frame based on mode
if (effectButtons[1]) {
if (laserShowMode === 0) {
// Off mode - normal white tint
effectButtons[1].tint = 0xFFFFFF;
} else if (laserShowMode === 1) {
// Slow mode - slow blue pulsing
var time = Date.now() * 0.0008; // Slow pulsing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var blueValue = Math.floor(intensity * 255);
effectButtons[1].tint = 0x4444ff | blueValue << 8 | blueValue;
} else if (laserShowMode === 2) {
// Fast mode - slower rainbow cycling
var time = Date.now() * 0.0008; // Slower cycling (reduced from 0.002)
var laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var colorIndex = Math.floor(time * 3) % laserColors.length; // Slower color cycling (reduced from 8 to 3)
effectButtons[1].tint = laserColors[colorIndex];
}
}
// Display fire effect flashing on the first effect button's frame based on mode
if (effectButtons[0]) {
if (fireEffectMode === 0) {
// Off mode - normal white tint
effectButtons[0].tint = 0xFFFFFF;
} else if (fireEffectMode === 1) {
// Normal flames mode - orange flashing
var time = Date.now() * 0.002; // Medium speed flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var redValue = Math.floor(255);
var greenValue = Math.floor(intensity * 128 + 64); // Orange tint
var blueValue = 0;
effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
} else if (fireEffectMode === 2) {
// Big flames mode - red flashing
var time = Date.now() * 0.003; // Faster flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var redValue = Math.floor(255);
var greenValue = Math.floor(intensity * 64); // More red tint
var blueValue = Math.floor(intensity * 64);
effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
}
}
// Display RGB flashing effect on the third effect button's frame if active
if (isRgbFlashingActive && effectButtons[2]) {
var time = Date.now() * 0.0005; // Time for animation, even slower
var hue = time * 360 % 360; // Cycle through hues
var rgb = hsvToRgb(hue / 360, 1.0, 1.0); // Full saturation and value for vibrant flash
effectButtons[2].tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
} else if (effectButtons[2]) {
// If not active, set the tint back to white
effectButtons[2].tint = 0xFFFFFF;
}
// --- UV button continuous purple flashing effect ---
if (effectButtons[4]) {
if (uvEffectActive) {
// Animate between indigo and purple
var uvBtnTime = Date.now() * 0.0015; // Slightly faster for button, but slower flashing for button
// Indigo: #4B0082 (0x4B0082), Purple: #8000FF (0x8000FF)
var t = 0.5 + 0.5 * Math.sin(uvBtnTime * Math.PI * 2);
// Interpolate RGB
var r = Math.round(0x4B * (1 - t) + 0x80 * t);
var g = Math.round(0x00 * (1 - t) + 0x00 * t);
var b = Math.round(0x82 * (1 - t) + 0xFF * t);
effectButtons[4].tint = r << 16 | g << 8 | b;
} else {
effectButtons[4].tint = 0xFFFFFF;
}
}
// Manage gifts effects based on giftsMode
if (balloons) {
for (var i = 0; i < balloons.length; i++) {
balloons[i].update();
// Only show balloons if giftsMode is 1 (balloons mode)
balloons[i].visible = giftsMode === 1;
}
}
if (moneyParticles) {
for (var i = 0; i < moneyParticles.length; i++) {
moneyParticles[i].update();
// Only show money particles if giftsMode is 2 (money mode)
moneyParticles[i].visible = giftsMode === 2;
}
}
// Manage fireworks visibility (fireworks are spread across multiple instances)
if (fireworksEffect) fireworksEffect.visible = giftsMode === 3;
if (fireworksEffect2) fireworksEffect2.visible = giftsMode === 3;
if (fireworksEffect3) fireworksEffect3.visible = giftsMode === 3;
if (fireworksEffect4) fireworksEffect4.visible = giftsMode === 3;
// Update fireworks effect animation only if visible
if (fireworksEffect && fireworksEffect.visible) fireworksEffect.update();
if (fireworksEffect2 && fireworksEffect2.visible) fireworksEffect2.update();
if (fireworksEffect3 && fireworksEffect3.visible) fireworksEffect3.update();
if (fireworksEffect4 && fireworksEffect4.visible) fireworksEffect4.update();
// Update gifts button visual feedback based on mode
if (giftsEffectButton) {
tween.stop(giftsEffectButton, {
tint: true
}); // Stop any previous tint tweens
if (giftsMode === 0) {
// Off mode - no light (white tint)
giftsEffectButton.tint = 0xFFFFFF;
} else if (giftsMode === 1) {
// Balloons mode - flashing pink
var time = Date.now() * 0.002; // Medium speed flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var pinkValue = Math.floor(intensity * 255);
giftsEffectButton.tint = 0xFF00FF | pinkValue << 8 | pinkValue; // Magenta/Pink
} else if (giftsMode === 2) {
// Money mode - green color
giftsEffectButton.tint = 0x00FF00; // Green
} else if (giftsMode === 3) {
// Fireworks mode - flashing gold
var time = Date.now() * 0.002; // Medium speed flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var goldR = Math.floor(intensity * 255);
var goldG = Math.floor(intensity * 215);
var goldB = Math.floor(intensity * 0);
giftsEffectButton.tint = goldR << 16 | goldG << 8 | goldB; // Gold/Yellowish
}
}
};
tabletAsset.x = (leftDeckScreen.x + rightDeckScreen.x) / 2;
tabletAsset.y = leftDeckScreen.y + 77 + 35 - 20 - 200 - 35;
// --- Arrow Buttons under Tablet ---
var arrowButtonSpacing = 150;
var arrowButtonY = tabletAsset.y + tabletAsset.height / 2 + 80;
var leftArrowButton = LK.getAsset('Leftarrow', {
anchorX: 0.5,
anchorY: 0.5
});
leftArrowButton.x = tabletAsset.x - arrowButtonSpacing * 1.5 - 50 - 5 - 5 - 1;
leftArrowButton.y = arrowButtonY;
game.addChild(leftArrowButton);
// Scale up left arrow button by 1.3
tween(leftArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
var upArrowButton = LK.getAsset('UParrow', {
anchorX: 0.5,
anchorY: 0.5
});
upArrowButton.x = tabletAsset.x + arrowButtonSpacing * 1.5 - 50 - 5 - 12 - 5 - 1;
upArrowButton.y = arrowButtonY;
game.addChild(upArrowButton);
// Scale up up arrow button by 1.3
tween(upArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
var downArrowButton = LK.getAsset('Downarrow', {
anchorX: 0.5,
anchorY: 0.5
});
downArrowButton.x = tabletAsset.x + arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
downArrowButton.y = arrowButtonY;
game.addChild(downArrowButton);
// Scale up down arrow button by 1.3
tween(downArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
var rightArrowButton = LK.getAsset('Rightarrow', {
anchorX: 0.5,
anchorY: 0.5
});
rightArrowButton.x = tabletAsset.x - arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
rightArrowButton.y = arrowButtonY;
game.addChild(rightArrowButton);
// Scale up right arrow button by 1.3
tween(rightArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
var okButton = LK.getAsset('OKbutton', {
anchorX: 0.5,
anchorY: 0.5
});
// Place OK button to the right of the right arrow button with the same spacing as between arrow buttons
okButton.x = upArrowButton.x + arrowButtonSpacing - 12 - 5 - 1 + 2 + 2;
okButton.y = arrowButtonY;
game.addChild(okButton);
// Scale up OK button by 1.3 to match arrow buttons
tween(okButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
// Add edit button under the OK button
var editButton = LK.getAsset('OKbutton', {
anchorX: 0.5,
anchorY: 0.5
});
editButton.x = okButton.x;
editButton.y = okButton.y + okButton.height + 20; // Position 20 units under the OK button
game.addChild(editButton);
// Scale up edit button by 1.3 to match arrow buttons
tween(editButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
// --- Pulse Button Effect State ---
var pulseButtonEffect = 1.0; // 1.0 = normal, <1.0 = decreased
// Helper to decrease pulse effect
function decreasePulseButtonEffect() {
pulseButtonEffect = 0.5;
// Optionally, restore after a short time for visual feedback
LK.setTimeout(function () {
pulseButtonEffect = 1.0;
}, 200);
}
// --- Add decrease pulse effect to arrow and OK buttons ---
leftArrowButton.down = function (x, y, obj) {
decreasePulseButtonEffect();
// Navigate left in rekordbox app
if (typeof rekordboxApp !== 'undefined') {
rekordboxApp.navigateLeft();
}
// Visual feedback
tween(leftArrowButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80,
onFinish: function onFinish() {
tween(leftArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120
});
}
});
};
rightArrowButton.down = function (x, y, obj) {
decreasePulseButtonEffect();
// Navigate right in rekordbox app
if (typeof rekordboxApp !== 'undefined') {
rekordboxApp.navigateRight();
}
tween(rightArrowButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80,
onFinish: function onFinish() {
tween(rightArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120
});
}
});
};
upArrowButton.down = function (x, y, obj) {
decreasePulseButtonEffect();
// Navigate up in rekordbox app
if (typeof rekordboxApp !== 'undefined') {
rekordboxApp.navigateUp();
}
tween(upArrowButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80,
onFinish: function onFinish() {
tween(upArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120
});
}
});
};
downArrowButton.down = function (x, y, obj) {
decreasePulseButtonEffect();
// Navigate down in rekordbox app
if (typeof rekordboxApp !== 'undefined') {
rekordboxApp.navigateDown();
}
tween(downArrowButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80,
onFinish: function onFinish() {
tween(downArrowButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120
});
}
});
};
okButton.down = function (x, y, obj) {
decreasePulseButtonEffect();
// Select track in rekordbox app
if (typeof rekordboxApp !== 'undefined') {
rekordboxApp.selectTrack();
}
tween(okButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80,
onFinish: function onFinish() {
tween(okButton, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 120
});
}
});
};
// Update second dancing people line animations (second set created) - swap with UV assets when UV is active
for (var i = 8; i < dancingPeople.length; i++) {
var person = dancingPeople[i];
// Check if UV effect is active
if (uvEffectActive) {
// If not already a UV person, swap to UV asset
if (!person.isUV) {
var uvAssetId = (i - 8) % 2 === 0 ? 'UVW' : 'UWM';
var currentScaleX = person.scale.x;
var currentScaleY = person.scale.y;
var currentRotation = person.rotation;
var currentAlpha = person.alpha;
var currentX = person.x;
var currentY = person.y;
var currentBaseY = person.baseY;
var currentPhaseOffset = person.phaseOffset;
// Remove the old graphic child
if (person.children.length > 0) {
person.removeChildAt(0);
}
// Add the new UV graphic child
var uvGraphic = person.attachAsset(uvAssetId, {
anchorX: 0.5,
anchorY: 1.0
});
// Restore previous transform and animation properties
person.scale.set(currentScaleX, currentScaleY);
person.rotation = currentRotation;
person.alpha = currentAlpha;
person.x = currentX;
person.y = currentY;
person.baseY = currentBaseY;
person.phaseOffset = currentPhaseOffset;
person.isUV = true;
}
} else {
// If UV effect is off and it's a UV person, swap back to normal asset
if (person.isUV) {
var normalAssetId = (i - 8) % 2 === 0 ? 'dancingWoman' : 'dancingMan';
var currentScaleX = person.scale.x;
var currentScaleY = person.scale.y;
var currentRotation = person.rotation;
var currentAlpha = person.alpha;
var currentX = person.x;
var currentY = person.y;
var currentBaseY = person.baseY;
var currentPhaseOffset = person.phaseOffset;
// Remove the old graphic child
if (person.children.length > 0) {
person.removeChildAt(0);
}
// Add the new normal graphic child
var normalGraphic = person.attachAsset(normalAssetId, {
anchorX: 0.5,
anchorY: 1.0
});
// Restore previous transform and animation properties
person.scale.set(currentScaleX, currentScaleY);
person.rotation = currentRotation;
person.alpha = currentAlpha;
person.x = currentX;
person.y = currentY;
person.baseY = currentBaseY;
person.phaseOffset = currentPhaseOffset;
person.isUV = false;
}
}
person.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 laser show animation
laserShow.update();
// Update fireworks effect animation
fireworksEffect.update();
fireworksEffect2.update();
fireworksEffect3.update();
fireworksEffect4.update();
// Update Ferris wheel animation
if (typeof ferrisWheel !== 'undefined' && ferrisWheel.update) {
// Defensive check
ferrisWheel.update();
}
// Update smoke diffuser animation
// --- Synchronize all smoke diffusers so they emit smoke puffs at the same time ---
if (typeof smokeDiffusers !== 'undefined' && smokeDiffusers.length > 0) {
// Use the first smoke diffuser as the "master" for timing
var now = Date.now();
// All diffusers share the same "now" and will create puffs in sync
for (var i = 0; i < smokeDiffusers.length; i++) {
var sd = smokeDiffusers[i];
// Patch: override the update method to use the same "now" for all
if (sd && typeof sd.update === 'function') {
// Save original Date.now
var origDateNow = Date.now;
Date.now = function () {
return now;
};
sd.update();
Date.now = origDateNow;
}
}
}
// Update flamethrower animation
if (typeof flamethrower !== 'undefined' && flamethrower.update) {
flamethrower.update();
}
// Update decks
leftDeck.update();
rightDeck.update();
// Update equalizers
leftEqualizer.update();
rightEqualizer.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)
// Only animate the background color if RGB flashing is active on the third effect button
if (isRgbFlashingActive) {
game.setBackgroundColor(rgb[0] << 16 | rgb[1] << 8 | rgb[2]);
} else if (uvEffectActive) {
// If UV effect is active, flash between indigo blue and purple
var uvTime = Date.now() * 0.002; // Faster flashing for UV
var uvHue = uvTime * 360 % 360;
// Cycle between indigo (around 240 hue) and purple (around 270 hue)
var targetHue = 240 + Math.sin(uvTime * Math.PI * 2) * 30;
targetHue = (targetHue + 360) % 360;
var uvRgb = hsvToRgb(targetHue / 360, 0.8, 0.7); // Adjust saturation and value for UV colors
game.setBackgroundColor(uvRgb[0] << 16 | uvRgb[1] << 8 | uvRgb[2]);
} else {
// If neither is active, set a default background color
game.setBackgroundColor(0x000000); // Or any other default color
}
// Win condition disabled
// --- Update Digital Clock ---
if (typeof digitalClockTxt !== 'undefined') {
var now = new Date();
digitalClockTxt.setText(formatTime(now));
}
// Display laser flashing effect on the second effect button's frame based on mode
if (effectButtons[1]) {
if (laserShowMode === 0) {
// Off mode - normal white tint
effectButtons[1].tint = 0xFFFFFF;
} else if (laserShowMode === 1) {
// Slow mode - slow blue pulsing
var time = Date.now() * 0.0008; // Slow pulsing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var blueValue = Math.floor(intensity * 255);
effectButtons[1].tint = 0x4444ff | blueValue << 8 | blueValue;
} else if (laserShowMode === 2) {
// Fast mode - slower rainbow cycling
var time = Date.now() * 0.0008; // Slower cycling (reduced from 0.002)
var laserColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var colorIndex = Math.floor(time * 3) % laserColors.length; // Slower color cycling (reduced from 8 to 3)
effectButtons[1].tint = laserColors[colorIndex];
}
}
// Display fire effect flashing on the first effect button's frame based on mode
if (effectButtons[0]) {
if (fireEffectMode === 0) {
// Off mode - normal white tint
effectButtons[0].tint = 0xFFFFFF;
} else if (fireEffectMode === 1) {
// Normal flames mode - orange flashing
var time = Date.now() * 0.002; // Medium speed flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var redValue = Math.floor(255);
var greenValue = Math.floor(intensity * 128 + 64); // Orange tint
var blueValue = 0;
effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
} else if (fireEffectMode === 2) {
// Big flames mode - red flashing
var time = Date.now() * 0.003; // Faster flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var redValue = Math.floor(255);
var greenValue = Math.floor(intensity * 64); // More red tint
var blueValue = Math.floor(intensity * 64);
effectButtons[0].tint = redValue << 16 | greenValue << 8 | blueValue;
}
}
// Display RGB flashing effect on the third effect button's frame if active
if (isRgbFlashingActive && effectButtons[2]) {
var time = Date.now() * 0.0005; // Time for animation, even slower
var hue = time * 360 % 360; // Cycle through hues
var rgb = hsvToRgb(hue / 360, 1.0, 1.0); // Full saturation and value for vibrant flash
effectButtons[2].tint = rgb[0] << 16 | rgb[1] << 8 | rgb[2];
} else if (effectButtons[2]) {
// If not active, set the tint back to white
effectButtons[2].tint = 0xFFFFFF;
}
// --- UV button continuous purple flashing effect ---
if (effectButtons[4]) {
if (uvEffectActive) {
// Animate between indigo and purple
var uvBtnTime = Date.now() * 0.0015; // Slightly faster for button, but slower flashing for button
// Indigo: #4B0082 (0x4B0082), Purple: #8000FF (0x8000FF)
var t = 0.5 + 0.5 * Math.sin(uvBtnTime * Math.PI * 2);
// Interpolate RGB
var r = Math.round(0x4B * (1 - t) + 0x80 * t);
var g = Math.round(0x00 * (1 - t) + 0x00 * t);
var b = Math.round(0x82 * (1 - t) + 0xFF * t);
effectButtons[4].tint = r << 16 | g << 8 | b;
} else {
effectButtons[4].tint = 0xFFFFFF;
}
}
// Manage gifts effects based on giftsMode
if (balloons) {
for (var i = 0; i < balloons.length; i++) {
balloons[i].update();
// Only show balloons if giftsMode is 1 (balloons mode)
balloons[i].visible = giftsMode === 1;
}
}
if (moneyParticles) {
for (var i = 0; i < moneyParticles.length; i++) {
moneyParticles[i].update();
// Only show money particles if giftsMode is 2 (money mode)
moneyParticles[i].visible = giftsMode === 2;
}
}
// Manage fireworks visibility (fireworks are spread across multiple instances)
if (fireworksEffect) fireworksEffect.visible = giftsMode === 3;
if (fireworksEffect2) fireworksEffect2.visible = giftsMode === 3;
if (fireworksEffect3) fireworksEffect3.visible = giftsMode === 3;
if (fireworksEffect4) fireworksEffect4.visible = giftsMode === 3;
// Update fireworks effect animation only if visible
if (fireworksEffect && fireworksEffect.visible) fireworksEffect.update();
if (fireworksEffect2 && fireworksEffect2.visible) fireworksEffect2.update();
if (fireworksEffect3 && fireworksEffect3.visible) fireworksEffect3.update();
if (fireworksEffect4 && fireworksEffect4.visible) fireworksEffect4.update();
// Update gifts button visual feedback based on mode
if (giftsEffectButton) {
tween.stop(giftsEffectButton, {
tint: true
}); // Stop any previous tint tweens
if (giftsMode === 0) {
// Off mode - no light (white tint)
giftsEffectButton.tint = 0xFFFFFF;
} else if (giftsMode === 1) {
// Balloons mode - flashing pink
var time = Date.now() * 0.002; // Medium speed flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var pinkValue = Math.floor(intensity * 255);
giftsEffectButton.tint = 0xFF00FF | pinkValue << 8 | pinkValue; // Magenta/Pink
} else if (giftsMode === 2) {
// Money mode - green color
giftsEffectButton.tint = 0x00FF00; // Green
} else if (giftsMode === 3) {
// Fireworks mode - flashing gold
var time = Date.now() * 0.002; // Medium speed flashing
var intensity = 0.5 + 0.5 * Math.sin(time * Math.PI * 2);
var goldR = Math.floor(intensity * 255);
var goldG = Math.floor(intensity * 215);
var goldB = Math.floor(intensity * 0);
giftsEffectButton.tint = goldR << 16 | goldG << 8 | goldB; // Gold/Yellowish
}
}
okButton.y = arrowButtonY + 12 + 30; // Move OK button down by 30 units
game.addChild(okButton);
leftArrowButton.x = tabletAsset.x - arrowButtonSpacing * 1.5 - 50 - 5 - 5 - 1;
leftArrowButton.y = arrowButtonY + 12 + 30; // Move left arrow down by 30 units
game.addChild(leftArrowButton);
upArrowButton.x = tabletAsset.x + arrowButtonSpacing * 1.5 - 50 - 5 - 12 - 5 - 1;
upArrowButton.y = arrowButtonY + 12 + 30; // Move up arrow down by 30 units
game.addChild(upArrowButton);
downArrowButton.x = tabletAsset.x + arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
downArrowButton.y = arrowButtonY + 12 + 30; // Move down arrow down by 30 units
game.addChild(downArrowButton);
rightArrowButton.x = tabletAsset.x - arrowButtonSpacing * 0.5 - 50 - 5 - 12 - 5 - 1;
rightArrowButton.y = arrowButtonY + 12 + 30; // Move right arrow down by 30 units
game.addChild(rightArrowButton);
// Update edit button position in update loop
if (typeof editButton !== 'undefined' && typeof okButton !== 'undefined') {
editButton.x = okButton.x;
editButton.y = okButton.y + okButton.height + 20 + 12; // Move down by 12 units
}
;
;
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