User prompt
Add white "-" under the vertical crossfaders.
User prompt
Add white "-" above the vertical crossfaders.
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (key === 'DELETE') {' Line Number: 1822
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (key === 'DELETE') {' Line Number: 1822
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (key === 'DELETE') {' Line Number: 1832
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (key === 'DELETE') {' Line Number: 1832
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (key === 'DELETE') {' Line Number: 1832
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (key === 'DELETE') {' Line Number: 1832
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'addChild')' in or related to this line: 'rekordboxApp.addChild(searchText); // Add to rekordboxApp so it's accessible for search functionality' Line Number: 2860
User prompt
Add a real song or music search engine to the game, which you can play when the player clicks on it
User prompt
Could you change the filterknob rotation method from drag and rotate by click to this: The player clicks on it and pushes it up to turn to right and if it drags down, it turns to the left
User prompt
HOW COULD YOU FIX THIS BUG? STILL NOT WORKING THE FILTERKNOB RATATION AROUND HER ANCOR POINT ENSURE IF PLAYER CLICK ON FILTER KNOB AND MOVING THE MOUSE AROUND THE KNOB, THEN YOU SHOULD ROTATE THE KNOB AFTER THE MOUSE UNTIL NEXT CLICK. SYNCRONISE THE MOUSE AND THE FILTER KNOB MOVING WELL IN THE SAME TIME.
User prompt
STILL NOT WORKING. ENSURE IF PLAYER CLICK ON FILTER KNOB AND MOVING THE MOUSE AROUND THE KNOB, THEN YOU SHOULD ROTATE THE KNOB AFTER THE MOUSE UNTIL NEXT CLICK. SYNCRONISE THE MOUSE AND THE FILTER KNOB MOVING WELL IN THE SAME TIME.
User prompt
Add ancor point for for each one filter knob's center and ensure drag and rotate by click and rotate around this ancor point
User prompt
Add ancor point for for each one filter knob to ensure drag and rotate by click and rotate around this ancor point
User prompt
Add ancor point for for each one filter knob to ensure drag and rotate by click and swipe βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
// Music Editor Module v1.0 - For DJ Mixing Tool // Base structure optimized for FRVR.Ava.Combo[POGAAS].v1.0 and LK engine export default function MusicEditor({ assets, onSave }) { const timelineRef = useRef(null); const [tracks, setTracks] = useState([]); const [currentTrack, setCurrentTrack] = useState(null); const [isRecording, setIsRecording] = useState(false); const [midiData, setMidiData] = useState([]); const [audioClips, setAudioClips] = useState([]); const [selectedSample, setSelectedSample] = useState(null); const [searchQuery, setSearchQuery] = useState(""); useEffect(() => { // Load from assets (song/music menu) if (assets?.songData) { setTracks(assets.songData); } }, [assets]); const handleRecord = () => { setIsRecording(!isRecording); if (!isRecording) { // Start recording // Use external API / MediaRecorder } else { // Stop recording and save onSave(tracks); } }; const handleDropSample = (sample, trackIndex, time) => { const newTracks = [...tracks]; newTracks[trackIndex].clips.push({ sample, time }); setTracks(newTracks); }; const handleSearch = (q) => { setSearchQuery(q); // Music browser: actual search from built-in library or external API }; return (
User prompt
Add SAMPLE CREATOR / EDITOR / Selector app to the game. If a player click on a non selected samplebutton the pop up these app in fullscreen
User prompt
Divide the instruments better to avoid overlaping
User prompt
Divide all instruments text from each other
User prompt
Scale up instruments text size by 2.5
User prompt
Move pianoroll down under the instruments
User prompt
Move the SongMakerApp instruments tools container down by 500 units.
User prompt
Move instruments down by 700 units
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'var localPoint = self.toLocal(globalPoint);' Line Number: 2390
/**** * 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; }); // PianoRoll: Interactive piano roll for composing music var PianoRoll = Container.expand(function (config) { var self = Container.call(this); // Config self.config = config; self.notes = []; // Array to store notes {beatIndex, keyIndex, visual, pitch} self.instruments = ['Drums', 'Guitar', 'Piano', 'Synthesizer', 'Bass', 'Strings']; // Added more instruments var numKeys = Math.floor(config.height / config.noteHeight); var keyLabels = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']; // Piano Roll Background var pianoRollBg = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: config.width, height: config.height }); pianoRollBg.tint = 0x2a2a2a; pianoRollBg.x = 0; // Relative to container pianoRollBg.y = 0; // Relative to container // Piano Keyboard (left side) var keyboardBg = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: config.keyboardWidth, height: config.height }); keyboardBg.tint = 0x000000; keyboardBg.x = 0; // Relative to container keyboardBg.y = 0; // Relative to container // Create piano keys var keys = []; for (var i = 0; i < numKeys; i++) { var keyY = i * config.noteHeight; var keyNote = keyLabels[i % 12]; var isBlackKey = keyNote.indexOf('#') !== -1; var key = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: config.keyboardWidth - 2, height: config.noteHeight - 1 }); key.tint = isBlackKey ? 0x333333 : 0xFFFFFF; key.x = 1; key.y = keyY; self.addChild(key); // Key label var keyText = new Text2(keyNote + Math.floor(i / 12 + 3), { size: 16, fill: isBlackKey ? 0xFFFFFF : 0x000000 }); keyText.anchor.set(0, 0.5); keyText.x = key.x + 5; keyText.y = key.y + config.noteHeight / 2; self.addChild(keyText); // Key interaction key.keyIndex = i; key.note = keyNote + Math.floor(i / 12 + 3); key.down = function (x, y, obj) { // Play note sound - placeholder for now console.log("Key pressed:", this.note); // Visual feedback var originalTint = this.tint; this.tint = 0xFF6600; var keyRef = this; LK.setTimeout(function () { keyRef.tint = originalTint; }, 200); }; keys.push(key); } // Grid lines var gridContainer = new Container(); self.addChild(gridContainer); var totalBars = 16; // Example: 16 bars var beatsPerBar = 4; // Example: 4 beats per bar var timelineWidth = config.width - config.keyboardWidth; var beatWidth = timelineWidth / (totalBars * beatsPerBar); // Horizontal grid lines (for each note) for (var i = 0; i <= numKeys; i++) { var gridY = i * config.noteHeight; var hLine = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: timelineWidth, height: 1 }); hLine.tint = config.gridColor; hLine.x = config.keyboardWidth; hLine.y = gridY; gridContainer.addChild(hLine); } // Vertical grid lines (for each beat) for (var i = 0; i <= totalBars * beatsPerBar; i++) { var gridX = config.keyboardWidth + i * beatWidth; var vLine = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: 1, height: config.height }); vLine.tint = i % beatsPerBar === 0 ? 0x555555 : config.gridColor; vLine.x = gridX; vLine.y = 0; // Relative to container gridContainer.addChild(vLine); } // Note placement area for interaction var noteArea = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: timelineWidth, height: config.height }); noteArea.tint = 0x1a1a1a; noteArea.alpha = 0.001; // Make it almost transparent but interactive noteArea.x = config.keyboardWidth; noteArea.y = 0; // Relative to container self.addChild(noteArea); // Variables for dragging instrument voices var draggedInstrument = null; var dragOffset = { x: 0, y: 0 }; // Instrument voice selection (using instruments from SongMakerApp) var instrumentVoices = self.config.instruments || ['Drums', 'Guitar', 'Piano', 'Synthesizer', 'Bass', 'Strings']; var instrumentButtons = []; var instrumentButtonY = 50; // Position buttons relative to instrumentsToolsContainer var instrumentsPerRow = 4; // Number of instrument buttons per row var buttonSpacing = 20; for (var i = 0; i < instrumentVoices.length; i++) { var button = self.attachAsset('crossfaderTrack', { anchorX: 0.5, anchorY: 0.5, width: 150, height: 60 }); var row = Math.floor(i / instrumentsPerRow); var col = i % instrumentsPerRow; button.tint = config.noteColors[i % config.noteColors.length]; // Position buttons relative to the instrumentsToolsContainer button.x = config.keyboardWidth + 100 + col * (button.width + buttonSpacing); button.y = instrumentButtonY + row * (button.height + buttonSpacing); // Arrange in rows self.addChild(button); var buttonText = new Text2(instrumentVoices[i], { size: 32, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); button.addChild(buttonText); button.instrumentVoice = instrumentVoices[i]; button.down = function (x, y, obj) { draggedInstrument = { voice: this.instrumentVoice, color: this.tint }; dragOffset.x = x; dragOffset.y = y; }; instrumentButtons.push(button); } self.move = function (x, y, obj) { if (draggedInstrument) { // Convert global touch coordinates to local coordinates relative to PianoRoll container var globalPoint = obj && obj.position ? obj.position : { x: x, y: y }; // Get global position from event object with fallback var localPoint = self.toLocal(globalPoint); // Create a temporary visual representation of the dragged note/instrument voice if (!self.draggedNoteVisual) { self.draggedNoteVisual = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: beatWidth - 2, height: config.noteHeight - 2 }); self.draggedNoteVisual.alpha = 0.7; self.addChild(self.draggedNoteVisual); } self.draggedNoteVisual.tint = draggedInstrument.color; self.draggedNoteVisual.x = localPoint.x - dragOffset.x + config.keyboardWidth; // Adjust for drag offset and keyboard width self.draggedNoteVisual.y = localPoint.y - dragOffset.y; // Adjust for drag offset } }; self.up = function (x, y, obj) { if (draggedInstrument) { // Convert global touch coordinates to local coordinates relative to PianoRoll container var globalPoint = obj && obj.position ? obj.position : { x: x, y: y }; // Get global position from event object with fallback var localPoint = self.toLocal(globalPoint); // Calculate grid position based on the release position, relative to the noteArea's origin var releaseX = localPoint.x - config.keyboardWidth; var releaseY = localPoint.y; var beatIndex = Math.floor(releaseX / beatWidth); var keyIndex = Math.floor(releaseY / config.noteHeight); // Check if release position is within the note area if (releaseX >= 0 && releaseX < timelineWidth && releaseY >= 0 && releaseY < config.height) { if (beatIndex >= 0 && beatIndex < totalBars * beatsPerBar && keyIndex >= 0 && keyIndex < numKeys) { // Check if a note of this instrument already exists at this position var existingNoteIndex = -1; for (var i = 0; i < self.notes.length; i++) { if (self.notes[i].beatIndex === beatIndex && self.notes[i].keyIndex === keyIndex && self.notes[i].instrument === draggedInstrument.voice) { existingNoteIndex = i; break; } } if (existingNoteIndex !== -1) { // Remove existing note of the same instrument self.removeChild(self.notes[existingNoteIndex].visual); self.notes.splice(existingNoteIndex, 1); } else { // Create new note var noteX = config.keyboardWidth + beatIndex * beatWidth; var noteY = keyIndex * config.noteHeight; var noteVisual = LK.getAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: beatWidth - 2, height: config.noteHeight - 2 }); noteVisual.tint = draggedInstrument.color; noteVisual.x = noteX + 1; noteVisual.y = noteY + 1; self.addChild(noteVisual); var note = { beatIndex: beatIndex, keyIndex: keyIndex, visual: noteVisual, pitch: keyLabels[keyIndex % 12] + Math.floor(keyIndex / 12 + 3), instrument: draggedInstrument.voice // Store the instrument voice }; self.notes.push(note); // Play sound for the placed note (placeholder - map instrument voice to sound) console.log("Placed note:", note); LK.getSound('fx').play(); // Generic sound for now } } } // Remove the dragged note visual if (self.draggedNoteVisual) { self.removeChild(self.draggedNoteVisual); self.draggedNoteVisual = null; } draggedInstrument = null; dragOffset = { x: 0, y: 0 }; } }; 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 // Category texts will be initialized with proper fill colors in updateDisplay method 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 }); // Store original tint self.trackItems[i].bg.originalTint = trackBg.tint; } // 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; if (self.categoryItems[i].text) { self.categoryItems[i].text.fill = isSelected ? "#ff8000" : "#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 ? 0xff8000 : 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); if (item.track) { item.track.fill = isSelected ? "#ff8000" : "#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: self.trackItems[self.selectedTrackIndex - self.visibleTrackStart].bg.originalTint || (self.selectedTrackIndex - self.visibleTrackStart) % 2 === 0 ? 0x252525 : 0x2a2a2a }, { 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; }); var SongMakerApp = Container.expand(function () { var self = Container.call(this); // Config var appWidth = 2048; var appHeight = 2732; // Background var background = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: appWidth, height: appHeight }); background.tint = 0x1a1a1a; // Dark background // App Title var titleText = new Text2('SONG MAKER', { size: 100, fill: 0xFF6600 }); titleText.anchor.set(0.5, 0); titleText.x = appWidth / 2; titleText.y = 100; self.addChild(titleText); // Piano Roll Configuration var pianoRollConfig = { x: 50, y: 250, width: appWidth - 100, height: 1600, noteHeight: 25, noteColors: [0xFF6600, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF], gridColor: 0x333333, keyboardWidth: 120, timelineHeight: 80, instruments: ['Drums', 'Guitar', 'Piano', 'Synthesizer', 'Bass', 'Strings'] }; // Create piano roll instance var pianoRoll = new PianoRoll(pianoRollConfig); pianoRoll.x = pianoRollConfig.x; // Position the container pianoRoll.y = pianoRollConfig.y; // Position the container self.addChild(pianoRoll); // Access notes array from the pianoRoll instance var notes = pianoRoll.notes; // This variable is now accessible var beatWidth = (pianoRollConfig.width - pianoRollConfig.keyboardWidth) / (16 * 4); // Recalculate beatWidth based on pianoRollConfig // Instruments tools container // Instruments tools container var instrumentsToolsY = pianoRollConfig.y + pianoRollConfig.height + 50; var instrumentsToolsHeight = 200; // Define height for instruments tools var instrumentsToolsContainer = self.addChild(new Container()); instrumentsToolsContainer.x = pianoRollConfig.x; instrumentsToolsContainer.y = instrumentsToolsY + 500; var instrumentsToolsBg = instrumentsToolsContainer.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: pianoRollConfig.width, height: instrumentsToolsHeight }); instrumentsToolsBg.tint = 0x333333; instrumentsToolsBg.x = 0; // Relative to container instrumentsToolsBg.y = 0; // Relative to container // Transport controls var transportY = instrumentsToolsY + instrumentsToolsHeight + 50; // Position transport controls below instruments tools var transportBg = self.attachAsset('crossfaderTrack', { anchorX: 0, anchorY: 0, width: pianoRollConfig.width, height: 100 }); transportBg.tint = 0x333333; transportBg.x = pianoRollConfig.x; transportBg.y = transportY; self.addChild(transportBg); // Play button var playButton = self.attachAsset('Startbutton', { anchorX: 0.5, anchorY: 0.5 }); playButton.x = pianoRollConfig.x + 100; playButton.y = transportY + 50; playButton.scaleX = 0.5; playButton.scaleY = 0.5; self.addChild(playButton); var isPlaying = false; var playbackPosition = 0; var playbackTimer = null; playButton.down = function (x, y, obj) { isPlaying = !isPlaying; if (isPlaying) { // Start playback playButton.tint = 0x00FF00; playbackTimer = LK.setInterval(function () { // Update playback position playbackPosition = (playbackPosition + 1) % (16 * 4); // Use totalBars * beatsPerBar // Play notes at current position for (var i = 0; i < notes.length; i++) { if (notes[i].beatIndex === playbackPosition) { // Play sound associated with the instrument voice // This needs a mapping from instrument voice to a sound asset ID // For now, playing a generic sound LK.getSound('fx').play(); // Flash the note var originalTint = notes[i].visual.tint; notes[i].visual.tint = 0xFFFFFF; var noteRef = notes[i]; LK.setTimeout(function () { noteRef.visual.tint = originalTint; }, 100); } } }, 250); // 240 BPM (250ms per beat) } else { // Stop playback playButton.tint = 0xFFFFFF; if (playbackTimer) { LK.clearInterval(playbackTimer); playbackTimer = null; } } }; // Stop button var stopButton = self.attachAsset('Cuebutton', { anchorX: 0.5, anchorY: 0.5 }); stopButton.x = pianoRollConfig.x + 200; stopButton.y = transportY + 50; stopButton.scaleX = 0.5; stopButton.scaleY = 0.5; self.addChild(stopButton); stopButton.down = function (x, y, obj) { isPlaying = false; playbackPosition = 0; playButton.tint = 0xFFFFFF; if (playbackTimer) { LK.clearInterval(playbackTimer); playbackTimer = null; } }; // Clear button var clearButton = self.attachAsset('Effect', { anchorX: 0.5, anchorY: 0.5 }); clearButton.x = pianoRollConfig.x + 300; clearButton.y = transportY + 50; clearButton.scaleX = 0.8; clearButton.scaleY = 0.8; self.addChild(clearButton); var clearText = new Text2('CLEAR', { size: 24, fill: 0xFFFFFF }); clearText.anchor.set(0.5, 0.5); clearText.x = clearButton.x; clearText.y = clearButton.y; self.addChild(clearText); clearButton.down = function (x, y, obj) { // Clear all notes for (var i = 0; i < notes.length; i++) { self.removeChild(notes[i].visual); } notes = []; // Clear the notes array // Visual feedback tween(clearButton, { scaleX: 0.6, scaleY: 0.6 }, { duration: 100, onFinish: function onFinish() { tween(clearButton, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100 }); } }); }; // Tempo display var tempoText = new Text2('BPM: 240', { size: 32, fill: 0xFFFFFF }); tempoText.anchor.set(0, 0.5); tempoText.x = pianoRollConfig.x + 450; tempoText.y = transportY + 50; self.addChild(tempoText); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Add a vertical crossfader track // --- Reflectors (behind discoball) --- var reflectors = []; var numReflectors = 7; var reflectorSpacing = 2048 / (numReflectors + 1); var reflectorY = 80; // Top, but below menu area // Create reflectors but do not add to game yet for (var i = 0; i < numReflectors; i++) { var rx = reflectorSpacing * (i + 1); var reflector = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); reflector.tint = 0xffffff; reflector.alpha = 0.0; reflector.x = rx; reflector.y = reflectorY; reflectors.push(reflector); } // --- Discoball (top of screen) --- // Asset for dancing woman // Asset for dancing man var discoball = new Discoball(); // Add reflectors behind discoball in display order for (var i = 0; i < reflectors.length; i++) { game.addChild(reflectors[i]); } // Insert discoball after reflectors so it appears above them game.addChild(discoball); discoball.x = 2048 / 2; discoball.y = 200 - 69; // Move discoball up by 69 units discoball.scaleX = 0.8; discoball.scaleY = 0.8; // --- Extra white particles for upper half sparkle effect --- var extraWhiteParticles = []; var numExtraParticles = 32; for (var i = 0; i < numExtraParticles; i++) { // Random position in upper half, avoid top 100px (menu) var px = 100 + Math.random() * (2048 - 200); var py = 120 + Math.random() * (1366 - 220); var size = 8 + Math.random() * 10; var particle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: size, height: size }); particle.tint = 0xffffff; particle.alpha = 0.18 + Math.random() * 0.22; particle.x = px; particle.y = py; game.addChild(particle); extraWhiteParticles.push({ obj: particle, baseX: px, baseY: py, phase: Math.random() * Math.PI * 2, baseAlpha: particle.alpha, baseSize: size }); } // Animate extra particles in game.update // --- Fog Effect (upper half) --- var fogEffect = new FogEffect(); game.addChild(fogEffect); fogEffect.y = 0; // Position at the top of the screen // --- Confetti Effect (upper half) --- var confettiEffect = new ConfettiEffect(); game.addChild(confettiEffect); confettiEffect.y = 0; // Position at the top of the screen // --- 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 + 100; 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) < 60 * 60) 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) < 60 * 60) 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) < 60 * 60) 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) < 60 * 60) 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) < 60 * 60) 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) < 60 * 60) 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); } lastTouchObj = obj; // Keep track of the object that was touched down }; 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); } // Only set dragging to null if the released object was the one being dragged if (dragging === getTouchedControl(x, y) || !getTouchedControl(x, y)) { // Check if released on the same object or empty space 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 - calculate angle from center var centerX = dragging.width / 2; var centerY = dragging.height / 2; var angle = Math.atan2(local.y - centerY, local.x - centerX); // Convert to degrees for easier calculation var angleDegrees = angle * (180 / Math.PI); // Normalize to 0-360 range if (angleDegrees < 0) angleDegrees += 360; // Map to -150 to +150 degree range (300 degrees total) var mappedAngle; if (angleDegrees <= 180) { // Right side: map 0-180 to 0-150 mappedAngle = angleDegrees / 180 * 150; } else { // Left side: map 180-360 to -150-0 mappedAngle = (angleDegrees - 360) / 180 * 150; } // Clamp to -150 to +150 degrees mappedAngle = Math.max(-150, Math.min(150, mappedAngle)); // Convert back to radians and apply dragging.rotation = mappedAngle * Math.PI / 180; } 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 X (close window) button to the full screen song maker app top right corner var closeButton = LK.getAsset('X', { anchorX: 1.0, anchorY: 0.0 }); closeButton.scaleX = 2.0; closeButton.scaleY = 2.0; closeButton.x = 2048 - 20; // 20px from right edge closeButton.y = 20; // 20px from top edge game.addChild(closeButton); // Add close functionality to the X button closeButton.down = function (x, y, obj) { // Remove fullscreen song maker app, overlay, and close button if (game.children.indexOf(fullscreenTracklist) !== -1) { game.removeChild(fullscreenTracklist); } if (game.children.indexOf(overlay) !== -1) { game.removeChild(overlay); } if (game.children.indexOf(closeButton) !== -1) { game.removeChild(closeButton); } keyboardApp.visible = false; // Hide the keyboard app }; // 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); } if (game.children.indexOf(closeButton) !== -1) { game.removeChild(closeButton); } 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 + 12; leftFilterKnobHigh.y = leftDeckAsset.y - 100 + 25 + 130; // Add white HIGH text under the HIGH filter knob var highTextLeft = new Text2('HIGH', { size: 24, fill: 0xFFFFFF // White color }); highTextLeft.anchor.set(0.5, 0.5); highTextLeft.x = leftFilterKnobHigh.x; highTextLeft.y = leftFilterKnobHigh.y + leftFilterKnobHigh.height / 2 + 20; // Position under the knob game.addChild(highTextLeft); // Add white FILTER text above the HIGH filter knobs, horizontally between them var filterTextLeft = new Text2('FILTER', { size: 30, fill: 0xFFFFFF // White color }); filterTextLeft.anchor.set(0.5, 0.5); filterTextLeft.x = leftFilterKnobHigh.x + 100; // Position text to the right of the knob for horizontal alignment filterTextLeft.y = leftFilterKnobHigh.y - leftFilterKnobHigh.height / 2 - 40 + 130 - 120; // Position above the knob game.addChild(filterTextLeft); tween(leftFilterKnobHigh, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var leftFilterKnobMid = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftFilterKnobMid); leftFilterKnobMid.x = leftDeckAsset.x + 190 + 400 + 12; leftFilterKnobMid.y = leftDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12; tween(leftFilterKnobMid, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var leftFilterKnobLow = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(leftFilterKnobLow); leftFilterKnobLow.x = leftDeckAsset.x + 190 + 400 + 12; leftFilterKnobLow.y = leftDeckAsset.y + 100 + 120 + 120 + 77; // Add white LOW text under the LOW filter knob var lowTextLeft = new Text2('LOW', { size: 24, fill: 0xFFFFFF // White color }); lowTextLeft.anchor.set(0.5, 0.5); lowTextLeft.x = leftFilterKnobLow.x; lowTextLeft.y = leftFilterKnobLow.y + leftFilterKnobLow.height / 2 + 20; // Position under the knob game.addChild(lowTextLeft); tween(leftFilterKnobLow, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var rightFilterKnobHigh = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightFilterKnobHigh); rightFilterKnobHigh.x = rightDeckAsset.x - 190 - 400 - 12; rightFilterKnobHigh.y = rightDeckAsset.y - 100 + 25 + 130; // Add white HIGH text under the HIGH filter knobs (Right side) var highTextRight = new Text2('HIGH', { size: 24, fill: 0xFFFFFF // White color }); highTextRight.anchor.set(0.5, 0.5); highTextRight.x = rightFilterKnobHigh.x; highTextRight.y = rightFilterKnobHigh.y + rightFilterKnobHigh.height / 2 + 20; // Position under the knob game.addChild(highTextRight); // Add white FILTER text above the HIGH filter knobs, horizontally between them (Right side) var filterTextRight = new Text2('FILTER', { size: 30, fill: 0xFFFFFF // White color }); filterTextRight.anchor.set(0.5, 0.5); filterTextRight.x = rightFilterKnobHigh.x - 100; // Position text to the left of the knob for horizontal alignment filterTextRight.y = rightFilterKnobHigh.y - rightFilterKnobHigh.height / 2 - 40 + 130 - 120; // Position above the knob game.addChild(filterTextRight); tween(rightFilterKnobHigh, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var rightFilterKnobMid = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightFilterKnobMid); rightFilterKnobMid.x = rightDeckAsset.x - 190 - 400 - 12; rightFilterKnobMid.y = rightDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12; tween(rightFilterKnobMid, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var rightFilterKnobLow = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(rightFilterKnobLow); rightFilterKnobLow.x = rightDeckAsset.x - 190 - 400 - 12; rightFilterKnobLow.y = rightDeckAsset.y + 100 + 120 + 120 + 77; // Add white LOW text under the LOW filter knob (Right side) var lowTextRight = new Text2('LOW', { size: 24, fill: 0xFFFFFF // White color }); lowTextRight.anchor.set(0.5, 0.5); lowTextRight.x = rightFilterKnobLow.x; lowTextRight.y = rightFilterKnobLow.y + rightFilterKnobLow.height / 2 + 20; // Position under the knob game.addChild(lowTextRight); tween(rightFilterKnobLow, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); function setupFilterKnobInteraction(knob) { knob.down = function (x, y, obj) { tween.stop(this); // Scale down the previously selected filter knob if it exists and is not the current one if (lastSelectedFilterKnob && lastSelectedFilterKnob !== this) { tween.stop(lastSelectedFilterKnob); tween(lastSelectedFilterKnob, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut }); } lastSelectedFilterKnob = this; tween(this, { scaleX: 2.4, scaleY: 2.4 }, { duration: 100, easing: tween.easeOut }); }; knob.up = function (x, y, obj) { tween.stop(this); // Do not scale down on up, keep enlarged }; } setupFilterKnobInteraction(leftFilterKnobHigh); setupFilterKnobInteraction(leftFilterKnobMid); setupFilterKnobInteraction(leftFilterKnobLow); setupFilterKnobInteraction(rightFilterKnobHigh); setupFilterKnobInteraction(rightFilterKnobMid); setupFilterKnobInteraction(rightFilterKnobLow); // --- 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 lastSelectedFilterKnob = null; // Variable to track the last selected filter knob. 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('editbutton', { 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) { 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 }); } }); }; // Add functionality to edit button editButton.down = function (x, y, obj) { decreasePulseButtonEffect(); // Create fullscreen song maker app var songMakerApp = new SongMakerApp(); game.addChild(songMakerApp); // 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(songMakerApp) - 1); // Add X (close window) button to the full screen song maker app top right corner var closeButton = LK.getAsset('X', { anchorX: 1.0, anchorY: 0.0 }); closeButton.scaleX = 2.0; closeButton.scaleY = 2.0; closeButton.x = 2048 - 20; // 20px from right edge closeButton.y = 20; // 20px from top edge game.addChild(closeButton); // Add close functionality to the X button closeButton.down = function (x, y, obj) { // Remove fullscreen song maker app, overlay, and close button if (game.children.indexOf(songMakerApp) !== -1) { game.removeChild(songMakerApp); } if (game.children.indexOf(overlay) !== -1) { game.removeChild(overlay); } if (game.children.indexOf(closeButton) !== -1) { game.removeChild(closeButton); } }; // Add close functionality when clicking overlay overlay.down = function (x, y, obj) { // Remove fullscreen song maker app and overlay if (game.children.indexOf(songMakerApp) !== -1) { game.removeChild(songMakerApp); } if (game.children.indexOf(overlay) !== -1) { game.removeChild(overlay); } if (game.children.indexOf(closeButton) !== -1) { game.removeChild(closeButton); } }; tween(editButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 80, onFinish: function onFinish() { tween(editButton, { 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 - 25; // 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 - 25; // 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 - 25; // 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 - 25; // 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 - 25; // 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 - 25 + 20 + 20; // Move down by 12 units + 20 units } var editButton = LK.getAsset('editbutton', { anchorX: 0.5, anchorY: 0.5 }); fxButton.y = fxButtonY + 200 + 10; leftFilterKnobMid.y = leftDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12; // Add white MID text under the MID filter knob var midTextLeft = new Text2('MID', { size: 24, fill: 0xFFFFFF // White color }); midTextLeft.anchor.set(0.5, 0.5); midTextLeft.x = leftFilterKnobMid.x; midTextLeft.y = leftFilterKnobMid.y + leftFilterKnobMid.height / 2 + 20; // Position under the knob game.addChild(midTextLeft); tween(leftFilterKnobMid, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var leftFilterKnobLow = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 }); rightFilterKnobMid.y = rightDeckAsset.y + 30 + 30 + 30 + 30 + 50 + 30 + 30 + 12; // Add white MID text under the MID filter knob (Right side) var midTextRight = new Text2('MID', { size: 24, fill: 0xFFFFFF // White color }); midTextRight.anchor.set(0.5, 0.5); midTextRight.x = rightFilterKnobMid.x; midTextRight.y = rightFilterKnobMid.y + rightFilterKnobMid.height / 2 + 20; // Position under the knob game.addChild(midTextRight); tween(rightFilterKnobMid, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); var rightFilterKnobLow = LK.getAsset('filterKnob', { anchorX: 0.5, anchorY: 0.5 });
===================================================================
--- original.js
+++ change.js
@@ -3084,13 +3084,13 @@
var notes = pianoRoll.notes; // This variable is now accessible
var beatWidth = (pianoRollConfig.width - pianoRollConfig.keyboardWidth) / (16 * 4); // Recalculate beatWidth based on pianoRollConfig
// Instruments tools container
// Instruments tools container
- var instrumentsToolsY = pianoRollConfig.y + pianoRollConfig.height + 50 + 700;
+ var instrumentsToolsY = pianoRollConfig.y + pianoRollConfig.height + 50;
var instrumentsToolsHeight = 200; // Define height for instruments tools
var instrumentsToolsContainer = self.addChild(new Container());
instrumentsToolsContainer.x = pianoRollConfig.x;
- instrumentsToolsContainer.y = instrumentsToolsY;
+ instrumentsToolsContainer.y = instrumentsToolsY + 500;
var instrumentsToolsBg = instrumentsToolsContainer.attachAsset('crossfaderTrack', {
anchorX: 0,
anchorY: 0,
width: pianoRollConfig.width,
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