Code edit (7 edits merged)
Please save this source code
User prompt
``` // Scale up to max scale then back down var scaleProgress = Math.sin(progress * Math.PI); var currentScale = particle.maxScale * scaleProgress; particle.scale.set(currentScale, currentScale); ``` I want it to scale up progressively (sin) but then stay at maxcale, not scale down
Code edit (6 edits merged)
Please save this source code
User prompt
store currentPunchlineSound and currentPunchlineConfetti when changed and restore them at start
Code edit (8 edits merged)
Please save this source code
User prompt
make particles of ConfettisHaha spawn at random positions on the screen
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: buttonSettings is not defined' in or related to this line: 'var settingsButtonBounds = {' Line Number: 1127
User prompt
it didn't work, use settings button coordinates instead
Code edit (1 edits merged)
Please save this source code
User prompt
in game.down don't make the actions if player tapped on the settinds button
Code edit (5 edits merged)
Please save this source code
User prompt
don't switch troll face when popup is visible
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Text is not a constructor' in or related to this line: 'self.soundText = new Text("None", {' Line Number: 271
Code edit (1 edits merged)
Please save this source code
Code edit (19 edits merged)
Please save this source code
User prompt
when popup is shown hide trollface, restore it when popup is hidden
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var particle = new Graphics();' Line Number: 121
Code edit (1 edits merged)
Please save this source code
Code edit (14 edits merged)
Please save this source code
User prompt
when settings is open, bypassTracking
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var ButtonPunchline = Container.expand(function () { var self = Container.call(this); // Attach the ButtonPunchline asset var buttonGraphics = self.attachAsset('ButtonPunchline', { anchorX: 0.5, anchorY: 0.5 }); // Add down event handler for punchline button self.down = function (x, y, obj) { // Play sound if enabled if (soundEnabled) { // Play the currently selected punchline sound if (punchlineSounds[currentPunchlineSound].asset) { LK.getSound(punchlineSounds[currentPunchlineSound].asset).play(); } } // Create confetti effect if enabled if (confettiEnabled) { var confetti; // Create the appropriate confetti effect based on the selected type switch (punchlineConfettis[currentPunchlineConfetti]["class"]) { case "ConfettisParty": confetti = new ConfettisParty(); break; case "ConfettisHaha": confetti = new ConfettisHaha(); break; case "ConfettisSmiley": confetti = new ConfettisSmiley(); break; default: confetti = new Confetti(); break; } // Initialize the confetti effect confetti.createParticles(self.x, self.y - 100); foregroundContainer.addChild(confetti); // Update confetti particles var updateInterval = LK.setInterval(function () { if (confetti.active) { confetti.update(); } else { LK.clearInterval(updateInterval); } }, 16); } }; return self; }); var ButtonSettings = Container.expand(function () { var self = Container.call(this); // Attach the ButtonSettings asset var buttonGraphics = self.attachAsset('ButtonSettingsBackground', { anchorX: 0.5, anchorY: 0.5, tint: 0x000000, alpha: 0.5 }); var buttonGraphics = self.attachAsset('ButtonSettings', { anchorX: 0.5, anchorY: 0.5 }); // Add down event handler to show settings popup self.down = function (x, y, obj) { // Toggle settings popup visibility if (settingsPopup) { settingsPopup.visible = !settingsPopup.visible; bypassTracking = settingsPopup.visible; // Toggle bypassTracking based on visibility trollFace.visible = !settingsPopup.visible; // Hide trollface when settings popup is shown } // Stop event propagation to prevent changing the face style if (obj.event && typeof obj.event.stopPropagation === 'function') { obj.event.stopPropagation(); } }; // Define any additional properties or methods for the ButtonSettings here return self; }); var Confetti = Container.expand(function () { var self = Container.call(this); // Properties self.particles = []; self.particleCount = 50; self.colors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF]; self.gravity = 0.5; self.active = false; // Create confetti particles self.createParticles = function (x, y) { // Clear existing particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; // Create new particles for (var i = 0; i < self.particleCount; i++) { var particle = LK.getAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }); particle.tint = self.colors[Math.floor(Math.random() * self.colors.length)]; particle.scale.set(0.2, 0.2); // Set initial position particle.x = x; particle.y = y; // Set random velocity particle.vx = (Math.random() - 0.5) * 20; particle.vy = (Math.random() - 0.5) * 20 - 10; // Add to container self.addChild(particle); self.particles.push(particle); } self.active = true; }; // Update particles self.update = function () { if (!self.active) { return; } var allSettled = true; for (var i = 0; i < self.particles.length; i++) { var particle = self.particles[i]; // Apply gravity particle.vy += self.gravity; // Update position particle.x += particle.vx; particle.y += particle.vy; // Check if particle is still moving if (particle.vy < 10) { allSettled = false; } // Check if particle is off screen if (particle.y > 2732) { particle.vy = 0; particle.vx = 0; } } // If all particles have settled, stop updating if (allSettled) { self.active = false; // Remove particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; } }; return self; }); var ConfettisHaha = Container.expand(function () { var self = Container.call(this); // Properties self.particles = []; self.particleCount = 10; self.active = false; self.screenWidth = 2048; self.screenHeight = 2732; // Create confetti particles self.createParticles = function (x, y) { // Clear existing particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; // Create new particles for (var i = 0; i < self.particleCount; i++) { // Randomly select one of the Haha assets var assetIndex = Math.floor(Math.random() * 3) + 1; var particle = LK.getAsset('ConfettisHaha' + assetIndex, { anchorX: 0.5, anchorY: 0.5 }); // Set initial position randomly on the screen particle.x = Math.random() * self.screenWidth; particle.y = Math.random() * self.screenHeight; // Set initial scale and alpha particle.scale.set(0.5, 0.5); particle.alpha = 0; // Set animation parameters particle.duration = 3 + Math.random() * 3; // 1-3 seconds particle.age = 0; particle.maxScale = 1 + Math.random() * 1; // Add to container self.addChild(particle); self.particles.push(particle); } self.active = true; }; // Update particles self.update = function () { if (!self.active) { return; } var activeParticles = false; for (var i = 0; i < self.particles.length; i++) { var particle = self.particles[i]; // Update age particle.age += 1 / 60; // Assuming 60fps // Calculate progress (0 to 1) var progress = particle.age / particle.duration; if (progress < 1) { activeParticles = true; // Fade in during first half, fade out during second half if (progress < 0.5) { particle.alpha = progress * 2; // 0 to 1 } else { particle.alpha = 2 - progress * 2; // 1 to 0 } // Scale up progressively to max scale and then stay there var scaleProgress = progress < 0.5 ? progress * 2 : 1; // 0 to 1 during first half, then stay at 1 var currentScale = particle.maxScale * scaleProgress; particle.scale.set(currentScale, currentScale); } } // If no active particles, stop updating if (!activeParticles) { self.active = false; // Remove particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; } }; return self; }); var ConfettisParty = Container.expand(function () { var self = Container.call(this); // Properties self.particles = []; self.particleCount = 100; self.colors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF]; self.gravity = 0.5; self.active = false; self.screenWidth = 2048; self.screenHeight = 2732; // Create confetti particles self.createParticles = function (x, y) { // Clear existing particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; // Create new particles for (var i = 0; i < self.particleCount; i++) { var particle = LK.getAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }); // Random color particle.tint = self.colors[Math.floor(Math.random() * self.colors.length)]; // Random scale between 0.1 and 0.4 var scale = 0.1 + Math.random() * 0.3; particle.scale.set(scale, scale); // Random rotation particle.rotation = Math.random() * Math.PI * 2; // Set initial position at left or right border var startFromLeft = Math.random() > 0.5; particle.x = startFromLeft ? 0 : self.screenWidth; particle.y = Math.random() * (self.screenHeight / 2); // Top half of screen // Set velocity toward upper center var centerX = self.screenWidth / 2; var centerY = self.screenHeight / 4; // Upper quarter // Calculate angle to center var dx = centerX - particle.x; var dy = centerY - particle.y; var angle = Math.atan2(dy, dx); // Set velocity based on angle with some randomness var speed = 5 + Math.random() * 10; particle.vx = Math.cos(angle) * speed; particle.vy = Math.sin(angle) * speed; // Add to container self.addChild(particle); self.particles.push(particle); } self.active = true; }; // Update particles self.update = function () { if (!self.active) { return; } var activeParticles = false; for (var i = 0; i < self.particles.length; i++) { var particle = self.particles[i]; // Apply gravity after particles reach their peak if (particle.vy > 0) { particle.vy += self.gravity; } else { // Slow down upward movement particle.vy += self.gravity * 0.5; } // Update position particle.x += particle.vx; particle.y += particle.vy; // Rotate particle particle.rotation += 0.05; // Check if particle is still active if (particle.y < self.screenHeight) { activeParticles = true; } } // If no active particles, stop updating if (!activeParticles) { self.active = false; // Remove particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; } }; return self; }); var ConfettisSmiley = Container.expand(function () { var self = Container.call(this); // Properties self.particles = []; self.particleCount = 10; self.active = false; self.screenWidth = 2048; self.screenHeight = 2732; // Create confetti particles self.createParticles = function (x, y) { // Clear existing particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; // Create new particles for (var i = 0; i < self.particleCount; i++) { // Randomly select one of the Smiley assets var assetIndex = Math.floor(Math.random() * 3) + 1; var particle = LK.getAsset('ConfettisSmiley' + assetIndex, { anchorX: 0.5, anchorY: 0.5 }); // Set initial position at left or right border var startFromLeft = Math.random() > 0.5; particle.x = startFromLeft ? -50 : self.screenWidth + 50; particle.y = 200 + Math.random() * (self.screenHeight - 400); // Random Y position with margin // Set horizontal velocity particle.vx = startFromLeft ? 5 + Math.random() * 10 : -(5 + Math.random() * 10); // Set rotation speed (faster for faster moving particles) particle.rotationSpeed = particle.vx * (startFromLeft ? 0.05 : -0.05); // Set scale var scale = 0.3 + Math.random() * 0.7; particle.scale.set(scale, scale); // Add delay before starting movement particle.delay = Math.random() * 2; // 0-2 seconds delay // Add to container self.addChild(particle); self.particles.push(particle); } self.active = true; }; // Update particles self.update = function () { if (!self.active) { return; } var activeParticles = false; for (var i = 0; i < self.particles.length; i++) { var particle = self.particles[i]; // Handle delay if (particle.delay > 0) { particle.delay -= 1 / 60; // Assuming 60fps activeParticles = true; continue; } // Update position particle.x += particle.vx; // Rotate particle (rolling effect) particle.rotation += particle.rotationSpeed; // Check if particle is still on screen (with margin) if (particle.vx > 0 && particle.x < self.screenWidth + 100 || particle.vx < 0 && particle.x > -100) { activeParticles = true; } } // If no active particles, stop updating if (!activeParticles) { self.active = false; // Remove particles for (var i = 0; i < self.particles.length; i++) { if (self.particles[i].parent) { self.particles[i].parent.removeChild(self.particles[i]); } } self.particles = []; } }; return self; }); var DebugPoints = Container.expand(function () { var self = Container.call(this); // Create points for face tracking debugging self.points = { leftEye: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }), rightEye: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }), noseTip: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }), mouthCenter: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }), upperLip: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }), lowerLip: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }), chin: self.attachAsset('debugFacePoints', { anchorX: 0.5, anchorY: 0.5 }) }; // Update debug points to match face points self.update = function () { if (!facekit) { return; } if (facekit.leftEye) { self.points.leftEye.x = facekit.leftEye.x; self.points.leftEye.y = facekit.leftEye.y; } if (facekit.rightEye) { self.points.rightEye.x = facekit.rightEye.x; self.points.rightEye.y = facekit.rightEye.y; } if (facekit.noseTip) { self.points.noseTip.x = facekit.noseTip.x; self.points.noseTip.y = facekit.noseTip.y; } if (facekit.mouthCenter) { self.points.mouthCenter.x = facekit.mouthCenter.x; self.points.mouthCenter.y = facekit.mouthCenter.y; } if (facekit.upperLip) { self.points.upperLip.x = facekit.upperLip.x; self.points.upperLip.y = facekit.upperLip.y; } if (facekit.lowerLip) { self.points.lowerLip.x = facekit.lowerLip.x; self.points.lowerLip.y = facekit.lowerLip.y; } if (facekit.chin) { self.points.chin.x = facekit.chin.x; self.points.chin.y = facekit.chin.y; } }; return self; }); var SettingsPopup = Container.expand(function () { var self = Container.call(this); var popupGraphics = self.attachAsset('FrameSettingsPopup', { anchorX: 0.5, anchorY: 0.5 }); var popupGraphics = self.attachAsset('FrameSettingsTitle', { anchorX: 0.5, anchorY: 0.5, y: -1100, blendMode: 0 }); // Create sound toggle container self.soundToggle = new Container(); self.soundToggle.x = -450; self.soundToggle.y = -700; self.addChild(self.soundToggle); // Add sound icon var soundIcon = self.soundToggle.attachAsset('IconSound', { anchorX: 0.5, anchorY: 0.5, x: -0 }); // Add click handler for sound icon soundIcon.down = function (x, y, obj) { // Play the current sound if it's not "None" and sound is enabled if (soundEnabled && punchlineSounds[currentPunchlineSound].asset) { LK.getSound(punchlineSounds[currentPunchlineSound].asset).play(); } }; // Add left chevron button self.soundLeftChevronButton = self.soundToggle.attachAsset('ButtonLeftChevron', { anchorX: 0.5, anchorY: 0.5, x: 220, y: 0 }); // Add click handler for left chevron button self.soundLeftChevronButton.down = function (x, y, obj) { self.changeSound(-1); }; // Add sound text that displays "None" by default self.soundText = new Text2("None", { size: 80, fill: 0x333344, align: "left", fontWeight: "bold" }); self.soundText.x = 320; self.soundText.y = -40; self.soundToggle.addChild(self.soundText); // Add right chevron button self.soundRightChevronButton = self.soundToggle.attachAsset('ButtonRightChevron', { anchorX: 0.5, anchorY: 0.5, x: 920, y: 0 }); // Add click handler for right chevron button self.soundRightChevronButton.down = function (x, y, obj) { self.changeSound(1); }; // Function to change the selected sound self.changeSound = function (direction) { // Update the current sound index currentPunchlineSound += direction; storage.currentPunchlineSound = currentPunchlineSound; // Cycle through the sounds instead of clamping if (currentPunchlineSound < 0) { currentPunchlineSound = punchlineSounds.length - 1; } else if (currentPunchlineSound >= punchlineSounds.length) { currentPunchlineSound = 0; } // Update the sound text self.soundText.setText(punchlineSounds[currentPunchlineSound].name); // Play the sound if it's not "None" and sound is enabled if (soundEnabled && punchlineSounds[currentPunchlineSound].asset) { LK.getSound(punchlineSounds[currentPunchlineSound].asset).play(); } }; // Initialize sound text with current selection self.soundText.setText(punchlineSounds[currentPunchlineSound].name); // Create confetti toggle container self.confettiToggle = new Container(); self.confettiToggle.x = -450; self.confettiToggle.y = -300; self.addChild(self.confettiToggle); // Add confetti icon var confettiIcon = self.confettiToggle.attachAsset('IconConfetti', { anchorX: 0.5, anchorY: 0.5, x: 0 }); // Add left chevron button self.confettiLeftChevronButton = self.confettiToggle.attachAsset('ButtonLeftChevron', { anchorX: 0.5, anchorY: 0.5, x: 220, y: 0 }); // Add click handler for left chevron button self.confettiLeftChevronButton.down = function (x, y, obj) { self.changeConfetti(-1); }; // Add confetti text that displays "None" by default self.confettiText = new Text2("None", { size: 80, fill: 0x333344, align: "left", fontWeight: "bold" }); self.confettiText.x = 320; self.confettiText.y = -40; self.confettiToggle.addChild(self.confettiText); // Add right chevron button self.confettiRightChevronButton = self.confettiToggle.attachAsset('ButtonRightChevron', { anchorX: 0.5, anchorY: 0.5, x: 920, y: 0 }); // Add click handler for right chevron button self.confettiRightChevronButton.down = function (x, y, obj) { self.changeConfetti(1); }; // Function to change the selected confetti effect self.changeConfetti = function (direction) { // Update the current confetti index currentPunchlineConfetti += direction; storage.currentPunchlineConfetti = currentPunchlineConfetti; // Cycle through the confetti effects if (currentPunchlineConfetti < 0) { currentPunchlineConfetti = punchlineConfettis.length - 1; } else if (currentPunchlineConfetti >= punchlineConfettis.length) { currentPunchlineConfetti = 0; } // Update the confetti text self.confettiText.setText(punchlineConfettis[currentPunchlineConfetti].name); }; // Initialize confetti text with current selection self.confettiText.setText(punchlineConfettis[currentPunchlineConfetti].name); // Define any additional properties or methods for the SettingsPopup here return self; }); var TrollFace = Container.expand(function () { var self = Container.call(this); self.scales = { head: { x: 1, y: 1 }, leftEye: { x: 1, y: 1 }, rightEye: { x: 1, y: 1 }, upperLip: { x: 1, y: 1 }, lowerLip: { x: 1, y: 1 } }; // Properties self.currentStyle = 1; self.currentOffsets = trollFaceOffsets[self.currentStyle - 1]; self.elements = {}; // Initialize cached position objects to reuse self.positionCache = { head: { x: 0, y: 0, scaleX: 1, scaleY: 1 }, leftEye: { x: 0, y: 0, scaleX: 1, scaleY: 1 }, rightEye: { x: 0, y: 0, scaleX: 1, scaleY: 1 }, upperLip: { x: 0, y: 0, scaleX: 1, scaleY: 1 }, lowerLip: { x: 0, y: 0, scaleX: 1, scaleY: 1 } }; // Initialize the troll face elements self.initialize = function (style) { // Clear previous elements self.removeAllElements(); // Create new elements for the selected style self.createFaceElements(style || self.currentStyle); }; // Create face elements for a specific style self.createFaceElements = function (style) { // Create head self.elements.head = self.attachAsset('trollHead' + style, { anchorX: 0.5, anchorY: 0.5, scale: 1, alpha: 1 // DEBUG }); // Create eyes self.elements.leftEye = self.attachAsset('trollLeftEye' + style, { anchorX: 0.5, anchorY: 0.5, scale: 1 }); self.elements.rightEye = self.attachAsset('trollRightEye' + style, { anchorX: 0.5, anchorY: 0.5, scale: 1 }); // Create upper lip self.elements.upperLip = self.attachAsset('trollUpperLip' + style, { anchorX: 0.5, anchorY: 0.5, scale: 1 }); // Create lower lip self.elements.lowerLip = self.attachAsset('trollLowerLip' + style, { anchorX: 0.5, anchorY: 0.5, scale: 1 }); }; // Remove all face elements self.removeAllElements = function () { if (self.elements.head) { self.removeChild(self.elements.head); } if (self.elements.leftEye) { self.removeChild(self.elements.leftEye); } if (self.elements.rightEye) { self.removeChild(self.elements.rightEye); } if (self.elements.upperLip) { self.removeChild(self.elements.upperLip); } if (self.elements.lowerLip) { self.removeChild(self.elements.lowerLip); } }; // Change to the next troll face style self.nextStyle = function (forcedStyle) { self.currentStyle = forcedStyle || self.currentStyle % 3 + 1; self.initialize(); self.currentOffsets = trollFaceOffsets[self.currentStyle - 1]; // Calculate face scale once and reuse var baseScale = self.calculateFaceScale(facekit); // Calculate all scales upfront self.scales = { head: { x: baseScale * self.currentOffsets.head.sx, y: baseScale * self.currentOffsets.head.sy }, leftEye: { x: baseScale * self.currentOffsets.leftEye.sx, y: baseScale * self.currentOffsets.leftEye.sy }, rightEye: { x: baseScale * self.currentOffsets.rightEye.sx, y: baseScale * self.currentOffsets.rightEye.sy }, upperLip: { x: baseScale * self.currentOffsets.upperLip.sx, y: baseScale * self.currentOffsets.upperLip.sy }, lowerLip: { x: baseScale * self.currentOffsets.lowerLip.sx, y: baseScale * self.currentOffsets.lowerLip.sy } }; return self.currentStyle; }; // Helper function to clamp a value between min and max self.clampPosition = function (value, min, max) { return Math.min(Math.max(value, min), max); }; // Helper function to ensure scale is an object self.ensureScaleIsObject = function (element) { if (_typeof(element.scale) !== 'object') { element.scale = { x: 1, y: 1 }; } }; // Helper function to update a face element self.updateFaceElement = function (elementName, x, y, scaleX, scaleY, makeVisible) { var element = self.elements[elementName]; if (!element) { return; } // Ensure scale is an object self.ensureScaleIsObject(element); // Apply position with clamping using scaled boundaries var elementOffset = self.currentOffsets[elementName]; element.x = self.clampPosition(x, elementOffset.minX * currentEyeDistance, elementOffset.maxX * currentEyeDistance); element.y = self.clampPosition(y, elementOffset.minY * currentEyeDistance, elementOffset.maxY * currentEyeDistance); // Apply scale element.scale.x = scaleX; element.scale.y = scaleY; // Set visibility if needed if (makeVisible) { element.visible = true; } }; // Helper function to update all face elements self.updateAllFaceElements = function (kit, makeVisible, useDefaultScales) { var elementNames = ['head', 'leftEye', 'rightEye', 'upperLip', 'lowerLip']; var positions = self.positionCache; // Calculate positions for all elements positions.head.x = 0; positions.head.y = 0; positions.head.scaleX = useDefaultScales ? 1 : self.scales.head.x; positions.head.scaleY = useDefaultScales ? 1 : self.scales.head.y; // Get the rotation angle for constraint calculations var rotationAngle = self.rotation; var cosAngle = Math.cos(-rotationAngle); // Negative to counter-rotate var sinAngle = Math.sin(-rotationAngle); // For other elements, calculate based on kit positions for (var i = 1; i < elementNames.length; i++) { var name = elementNames[i]; var kitElement = kit[name]; if (kitElement) { var scaleX, scaleY; // Determine which scale to use based on element type and useDefaultScales flag if (useDefaultScales) { scaleX = 1 * self.currentOffsets[name].sx; scaleY = 1 * self.currentOffsets[name].sy; } else { if (name === 'leftEye') { scaleX = self.scales.leftEye.x; scaleY = self.scales.leftEye.y; } else if (name === 'rightEye') { scaleX = self.scales.rightEye.x; scaleY = self.scales.rightEye.y; } else if (name === 'upperLip') { scaleX = self.scales.upperLip.x; scaleY = self.scales.upperLip.y; } else if (name === 'lowerLip') { scaleX = self.scales.lowerLip.x; scaleY = self.scales.lowerLip.y; } } // Calculate position using relative offsets scaled by eye distance var rawX = kitElement.x - self.x + self.currentOffsets[name].x * currentEyeDistance; var rawY = kitElement.y - self.y + self.currentOffsets[name].y * currentEyeDistance; // Apply rotation constraints to maintain relative positions // This counter-rotates the positions to keep elements in proper alignment positions[name].x = rawX * cosAngle - rawY * sinAngle; positions[name].y = rawX * sinAngle + rawY * cosAngle; positions[name].scaleX = scaleX; positions[name].scaleY = scaleY; } } // Update each element with calculated positions for (var j = 0; j < elementNames.length; j++) { var elemName = elementNames[j]; if (self.elements[elemName] && positions[elemName]) { var pos = positions[elemName]; self.updateFaceElement(elemName, pos.x, pos.y, pos.scaleX, pos.scaleY, makeVisible); } } // Handle mouth open adjustment if (kit.mouthOpen && self.elements.lowerLip) { //self.elements.lowerLip.scale.y = self.scales.lip.y * 1.5; } }; // Update face elements to match real face self.updateFacePosition = function () { if (!facekit) { return; } // If in first phase of centering, directly apply stored positions with translation if (self.isFirstPhaseCentering && self.relativePositions) { // Apply stored positions to each element for (var name in self.elements) { if (self.relativePositions[name]) { var element = self.elements[name]; element.x = self.relativePositions[name].x; element.y = self.relativePositions[name].y; element.scale.x = self.relativePositions[name].scaleX; element.scale.y = self.relativePositions[name].scaleY; } } // Set rotation to stored value self.rotation = self.relativePositions.rotation; return; // Skip the normal update process } // Get kit based on tracking state var kit = bypassTracking ? fakeCamera : facekitMgr.isTracking ? facekitMgr.currentFacekit : facekit; // If re-centering, use fakeCamera data for positioning without changing bypassTracking if (self.isRecentering) { kit = fakeCamera; } // Update global eye distance once per frame currentEyeDistance = self.getEyeDistance(kit); var baseScale = self.calculateFaceScale(kit); // Calculate face rotation - use FacekitManager if tracking, otherwise use local calculation var rotation = facekitMgr.isTracking ? facekitMgr.currentRotation + Math.PI : self.calculateFaceRotation(kit); // If re-centering or in first phase, reset rotation to 0 if (self.isRecentering || self.isFirstPhaseCentering) { rotation = 0; } // Update scales self.scales = { head: { x: baseScale * self.currentOffsets.head.sx, y: baseScale * self.currentOffsets.head.sy }, leftEye: { x: baseScale * self.currentOffsets.leftEye.sx, y: baseScale * self.currentOffsets.leftEye.sy }, rightEye: { x: baseScale * self.currentOffsets.rightEye.sx, y: baseScale * self.currentOffsets.rightEye.sy }, upperLip: { x: baseScale * self.currentOffsets.upperLip.sx, y: baseScale * self.currentOffsets.upperLip.sy }, lowerLip: { x: baseScale * self.currentOffsets.lowerLip.sx, y: baseScale * self.currentOffsets.lowerLip.sy } }; if (bypassTracking) { self.x = 2048 / 2; self.y = 2732 / 2; self.rotation = 0; // Reset rotation in bypass mode // Use the global fakeCamera with dynamic scales self.updateAllFaceElements(fakeCamera, true, false); return; } // Apply rotation to the entire troll face container self.rotation = rotation; // Update all elements with kit self.updateAllFaceElements(kit, true, false); }; // Calculate scale based on eye distance self.calculateFaceScale = function (kit) { return currentEyeDistance * 0.005; }; // Calculate face rotation angle based on eye positions self.calculateFaceRotation = function (kit) { if (kit && kit.leftEye && kit.rightEye) { // Get eye positions var leftEye = kit.leftEye; var rightEye = kit.rightEye; // Calculate angle - simpler approach // This gives us the angle of the line connecting the eyes var angle = Math.atan2(rightEye.y - leftEye.y, rightEye.x - leftEye.x); // More efficient angle wrapping using modulo var angleDiff = angle - currentRotation; // Normalize the difference to [-PI, PI] range // Using efficient modulo approach rather than while loops angleDiff = (angleDiff + Math.PI) % (2 * Math.PI) - Math.PI; // Apply smoothing to the rotation currentRotation = currentRotation + angleDiff * 0.2; return currentRotation + Math.PI; } return 0; // Default rotation (no rotation) }; // Get eye distance from kit self.getEyeDistance = function (kit) { if (kit && kit.leftEye && kit.rightEye) { var dx = kit.leftEye.x - kit.rightEye.x; var dy = kit.leftEye.y - kit.rightEye.y; return Math.sqrt(dx * dx + dy * dy); } return 200; // Default eye distance }; // Add a property to track if we're in re-centering mode self.isRecentering = false; // Add a property to track if we're in the first phase of centering (maintaining relative positions) self.isFirstPhaseCentering = false; // Store relative positions of elements during centering self.relativePositions = {}; // Capture current face positions for first phase centering self.captureCurrentPositions = function () { // Store the exact current positions and scales of all elements if (!self.elements) { return; } // Create a snapshot of the current state self.relativePositions = { // Store positions and scales for each element head: self.elements.head ? { x: self.elements.head.x, y: self.elements.head.y, scaleX: self.elements.head.scale.x, scaleY: self.elements.head.scale.y } : null, leftEye: self.elements.leftEye ? { x: self.elements.leftEye.x, y: self.elements.leftEye.y, scaleX: self.elements.leftEye.scale.x, scaleY: self.elements.leftEye.scale.y } : null, rightEye: self.elements.rightEye ? { x: self.elements.rightEye.x, y: self.elements.rightEye.y, scaleX: self.elements.rightEye.scale.x, scaleY: self.elements.rightEye.scale.y } : null, upperLip: self.elements.upperLip ? { x: self.elements.upperLip.x, y: self.elements.upperLip.y, scaleX: self.elements.upperLip.scale.x, scaleY: self.elements.upperLip.scale.y } : null, lowerLip: self.elements.lowerLip ? { x: self.elements.lowerLip.x, y: self.elements.lowerLip.y, scaleX: self.elements.lowerLip.scale.x, scaleY: self.elements.lowerLip.scale.y } : null, // Store current rotation rotation: self.rotation, // Store current container position containerX: self.x, containerY: self.y }; }; return self; }); /**** * Initialize Game ****/ // FacekitManager: handles face tracking, smoothing, and fallback var game = new LK.Game({ backgroundColor: 0xFFFFFF }); /**** * Game Code ****/ // FacekitManager: handles face tracking, smoothing, and fallback /**** * Global Variables ****/ var FacekitManager = function FacekitManager() { var self = new Container(); // Public properties self.currentFacekit = null; // Current smoothed face data self.isTracking = false; // Current tracking state self.currentRotation = 0; // Current smoothed rotation (accessible directly for performance) self.smoothingFactor = 0.5; // Default smoothing factor for facial features self.headSmoothingFactor = 0.2; // Separate smoothing factor for head position (noseTip) self.lastLipPosition = null; // Last lower lip position for tracking detection self.trackingStoppedCounter = 100; // Counter for tracking detection self.trackingStoppedDelay = 100; // Delay threshold for tracking detection // Default positions - defined inline var defaultFacekit = { leftEye: { x: 1380, y: 958 }, rightEye: { x: 673, y: 970 }, upperLip: { x: 1027, y: 1610 }, lowerLip: { x: 1030, y: 1613 }, noseTip: { x: 1024, y: 1366 }, mouthOpen: false }; // Initialize manager self.initialize = function (fakeFacekit) { // Use provided fallback or default self.fallbackFacekit = fakeFacekit || defaultFacekit; // Initialize current data as reference to fallback self.currentFacekit = self.fallbackFacekit; // Create a reusable object for smoothing calculations self.smoothFacekit = { leftEye: { x: 0, y: 0 }, rightEye: { x: 0, y: 0 }, upperLip: { x: 0, y: 0 }, lowerLip: { x: 0, y: 0 }, noseTip: { x: 0, y: 0 }, mouthOpen: false }; return self; }; // Process new tracking data and update tracking status self.updateTrackingStatus = function (rawFacekit) { // Check if there's valid face data var hasFaceData = !!(rawFacekit && rawFacekit.lowerLip); if (!hasFaceData) { self.isTracking = false; return false; } // Check if lower lip position has changed using existing tracking mechanism if (self.lastLipPosition === rawFacekit.lowerLip.y) { self.trackingStoppedCounter--; if (self.trackingStoppedCounter <= 0) { self.isTracking = false; self.trackingStoppedCounter = self.trackingStoppedDelay; // Reset delay } } else { self.isTracking = true; self.lastLipPosition = rawFacekit.lowerLip.y; self.trackingStoppedCounter = self.trackingStoppedDelay; // Reset delay } // If tracking, process the face data if (self.isTracking) { // If tracking just started, initialize smooth values if (self.currentFacekit === self.fallbackFacekit) { self._initSmoothValues(rawFacekit); } // Apply smoothing to each facial feature self._smoothValues(rawFacekit); // Set currentFacekit to the smoothed values self.currentFacekit = self.smoothFacekit; } else { // Use fallback when not tracking self.currentFacekit = self.fallbackFacekit; } // Return tracking state return self.isTracking; }; // Initialize smooth values with raw data self._initSmoothValues = function (rawFacekit) { // Directly set initial values from raw data for (var key in rawFacekit) { if (rawFacekit[key] && self.smoothFacekit[key] && typeof rawFacekit[key].x !== 'undefined') { self.smoothFacekit[key].x = rawFacekit[key].x; self.smoothFacekit[key].y = rawFacekit[key].y; } } // Initialize rotation directly if (rawFacekit.leftEye && rawFacekit.rightEye) { self.currentRotation = Math.atan2(rawFacekit.rightEye.y - rawFacekit.leftEye.y, rawFacekit.rightEye.x - rawFacekit.leftEye.x); } }; // Apply smoothing to all values self._smoothValues = function (rawFacekit) { // Smooth positions (reuse existing objects) for (var key in rawFacekit) { if (rawFacekit[key] && self.smoothFacekit[key] && typeof rawFacekit[key].x !== 'undefined') { // Apply position smoothing directly with different factors for head vs. other elements var factor = key === 'noseTip' ? self.headSmoothingFactor : self.smoothingFactor; self.smoothFacekit[key].x += (rawFacekit[key].x - self.smoothFacekit[key].x) * factor; self.smoothFacekit[key].y += (rawFacekit[key].y - self.smoothFacekit[key].y) * factor; } } // Calculate and smooth rotation if (rawFacekit.leftEye && rawFacekit.rightEye) { var newAngle = Math.atan2(rawFacekit.rightEye.y - rawFacekit.leftEye.y, rawFacekit.rightEye.x - rawFacekit.leftEye.x); // Improved angle normalization to prevent full rotations - no while loops var angleDiff = newAngle - self.currentRotation; // Efficiently handle -PI/PI boundary crossing (replaces the while loops) if (angleDiff > Math.PI) { angleDiff -= 2 * Math.PI; } else if (angleDiff < -Math.PI) { angleDiff += 2 * Math.PI; } // Apply safeguard against large changes var maxRotationPerFrame = Math.PI / 10; // Limit rotation change // Clamp the rotation change to prevent sudden large rotations if (angleDiff > maxRotationPerFrame) { angleDiff = maxRotationPerFrame; } if (angleDiff < -maxRotationPerFrame) { angleDiff = -maxRotationPerFrame; } // Apply smoothing self.currentRotation += angleDiff * self.smoothingFactor; // Normalize the result angle to ensure it stays in -PI to PI range // Using efficient modulo approach rather than while loops self.currentRotation = (self.currentRotation + 3 * Math.PI) % (2 * Math.PI) - Math.PI; } // Copy mouth state self.smoothFacekit.mouthOpen = rawFacekit.mouthOpen; }; return self; }; var debugMode = true; // DEBUG MODE DEBUG MODE DEBUG MODE var bypassTracking = false; // Global variable to bypass face tracking var currentEyeDistance = 200; // Global variable to store current eye distance var currentRotation = 0; // Current smoothed rotation value var settingsPopup; // Global variable for settings popup var soundEnabled = true; // Global variable to track if sound is enabled var confettiEnabled = true; // Global variable to track if confetti is enabled var punchlineSounds = [{ name: "None", asset: "" }, { name: "Rimshot", asset: "punchlineSound1" }, { name: "Trombone 1", asset: "punchlineSound2" }, { name: "Trombone 2", asset: "punchlineSound3" }, { name: "Laugh", asset: "punchlineSound4" }, { name: "Crowd Laugh", asset: "punchlineSound5" }]; // Array to store available punchline sounds var punchlineConfettis = [{ name: "Party", "class": "ConfettisParty" }, { name: "Haha", "class": "ConfettisHaha" }, { name: "Smiley", "class": "ConfettisSmiley" }]; // Array to store available punchline confetti effects var currentPunchlineSound = storage.currentPunchlineSound || 1; // Current selected punchline sound index var currentPunchlineConfetti = storage.currentPunchlineConfetti || 0; // Current selected punchline confetti index var facekitMgr; var background; var instructionText; var styleText; var trollFace; var debugPoints; var backgroundContainer; var middlegroundContainer; var foregroundContainer; var isTrackingFace = false; // Global flag to track face detection state var targetPosition; var lastNosePosition = null; // Global variable to store last facekit.noseTip.x var trackingStoppedDelay = 100; var trackingStoppedCounter = trackingStoppedDelay; var buttonSettings; var settingsButtonBounds; var trollFaceOffsets = [{ head: { x: 0, y: 0, sx: 1, sy: 1 }, leftEye: { x: 0.13, y: 0.5, sx: 0.4, sy: 0.4, minX: 0.7, maxX: 0.9, minY: -0.5, maxY: 0 }, rightEye: { x: 0.2, y: 0.5, sx: 0.6, sy: 0.6, minX: -0.5, maxX: -0.2, minY: -0.5, maxY: 0 }, upperLip: { x: 0.25, y: 0.05, sx: 0.9, sy: 0.9, minX: -1, maxX: 1, minY: 0, maxY: 0.42 }, lowerLip: { x: 0.25, y: 0.06, sx: 0.9, sy: 0.9, minX: -1, maxX: 0.25, minY: 0, maxY: 0.9 } }, { head: { x: 0, y: 0, sx: 1, sy: 1 }, leftEye: { x: 0, y: 0.5, sx: 0.6, sy: 0.6, minX: 0.2, maxX: 0.7, minY: 0, maxY: 0.6 }, rightEye: { x: 0.1, y: 0.3, sx: 0.6, sy: 0.6, minX: -0.7, maxX: 0.1, minY: 0, maxY: 0.4 }, upperLip: { x: -0.2, y: 0.1, sx: 0.5, sy: 0.5, minX: -0.3, maxX: 0, minY: -1, maxY: 1 }, lowerLip: { x: -0.10, y: 0.12, sx: 0.5, sy: 0.5, minX: -0.2, maxX: 0, minY: 0, maxY: 0.8 } }, { head: { x: 0, y: 0, sx: 1, sy: 1 }, leftEye: { x: 0.13, y: -0.02, sx: 0.8, sy: 0.8, minX: 0.6, maxX: 0.72, minY: -0.75, maxY: -0.6 }, rightEye: { x: 0.15, y: -0.02, sx: 0.8, sy: 0.8, minX: -0.6, maxX: -0.3, minY: -0.75, maxY: -0.6 }, upperLip: { x: 0.1, y: -0.3, sx: 1, sy: 1, minX: -0.5, maxX: 0.5, minY: -1.0, maxY: 1.0 }, lowerLip: { x: 0.15, y: -0.10, sx: 0.93, sy: 0.93, minX: -0.5, maxX: 0.5, minY: -1.0, maxY: 0.55 } }]; // Define fakeCamera globally with fixed positions for testing var fakeCamera = { leftEye: { x: 1380, y: 958 }, rightEye: { x: 673, y: 970 }, upperLip: { x: 1027, y: 1610 }, lowerLip: { x: 1030, y: 1613 }, mouthOpen: false, noseTip: { x: 1024, y: 1366 } }; // Global object for bypass tracking mode function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } /**** * Game Functions ****/ // Handle tap anywhere on the screen to change face game.down = function (x, y, obj) { console.log("Game Down:", obj); // Check if the tap is within the settings button coordinates if (settingsPopup.visible || x >= settingsButtonBounds.x && x <= settingsButtonBounds.x + settingsButtonBounds.width && y >= settingsButtonBounds.y && y <= settingsButtonBounds.y + settingsButtonBounds.height) { return; } // Face style changing is now handled by the faceContainer's down event }; // Update function called every frame game.update = function () { if (bypassTracking) { // When bypassing tracking, position the troll face at the center and update its elements trollFace.updateFacePosition(); return; } if (!facekit || !facekit.noseTip) { return; } // Update tracking status and get smoothed face data var isCurrentlyTracking = facekitMgr.updateTrackingStatus(facekit); // Update tracking state UI if changed if (isCurrentlyTracking !== isTrackingFace) { isTrackingFace = isCurrentlyTracking; instructionText.setText(isCurrentlyTracking ? "Tracking..." : "No Face found"); } // Update troll face position to match real face if (isTrackingFace) { // Reset isRecentering flag when face tracking is detected trollFace.isRecentering = false; trollFace.isFirstPhaseCentering = false; // Clear stored positions to prevent any lingering effects trollFace.relativePositions = {}; // Reset head position to default (0,0) relative to container if (trollFace.elements && trollFace.elements.head) { trollFace.elements.head.x = 0; trollFace.elements.head.y = 0; } // Use the smoothed nose tip position trollFace.x = facekitMgr.currentFacekit.noseTip.x; trollFace.y = facekitMgr.currentFacekit.noseTip.y; // Use the original updateFacePosition with smoothed data trollFace.updateFacePosition(); trollFace.isCentered = false; trollFace.isCentering = false; } else { // If face is not detected, return the face to the center if (!trollFace.isCentered) { if (trollFace.isCentering) { // Don't exit the update function, just skip starting a new tween // This allows other updates to continue // Continue updating face elements during centering trollFace.updateFacePosition(); } else { trollFace.isCentering = true; // First phase: Capture current positions and maintain them during animation trollFace.captureCurrentPositions(); trollFace.isFirstPhaseCentering = true; trollFace.isRecentering = false; //LK.effects.flashScreen(0xFFFFFF, 300); // Flash screen tween(trollFace, { x: 2048 / 2, y: 2732 / 2 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Second phase: Switch to default positions trollFace.isFirstPhaseCentering = false; trollFace.isRecentering = true; trollFace.isCentered = true; trollFace.isCentering = false; // First call updateFacePosition to calculate proper scales // This won't affect positions yet since we'll animate them trollFace.updateFacePosition(); // Get positions directly from the updateAllFaceElements function var fakePositions = {}; // Store the original updateFaceElement function var originalUpdateFaceElement = trollFace.updateFaceElement; // Override the function temporarily to capture positions trollFace.updateFaceElement = function (name, x, y, scaleX, scaleY) { // Store the position and scale that would be applied fakePositions[name] = { x: x, y: y, scaleX: scaleX, scaleY: scaleY }; }; // Call the function to calculate positions without applying them trollFace.updateAllFaceElements(fakeCamera, false, false); // Restore the original function trollFace.updateFaceElement = originalUpdateFaceElement; // Animate each element independently to the correct positions for (var name in trollFace.elements) { if (trollFace.elements[name] && fakePositions[name]) { var element = trollFace.elements[name]; var target = fakePositions[name]; // Create tween for this element tween(element, { x: target.x, y: target.y, scaleX: target.scaleX, scaleY: target.scaleY }, { duration: 500, easing: tween.easeOut }); } } } }); } } // Update face tracking state if (isTrackingFace) { isTrackingFace = false; instructionText.setText("No Face found"); } } // If in debug mode, display the face elements positions if (debugMode) { // Clear existing debug points if (debugPoints) { for (var i = 0; i < debugPoints.children.length; i++) { debugPoints.children[i].alpha = 0; } } // Draw debug points for each face element if (facekit) { var pointIndex = 0; // Draw a point for each facial feature for (var key in facekit) { if (facekit[key] && typeof facekit[key].x !== 'undefined') { var point = debugPoints.children[pointIndex++]; if (point) { point.x = facekit[key].x; point.y = facekit[key].y; point.alpha = 1; } } } // Display rotation in degrees instructionText.setText("Rotation: " + Math.round(trollFace.rotation * 180 / Math.PI) + "Ā°"); } } }; function initializeGame() { // Initialize game // Create containers for layering backgroundContainer = new Container(); middlegroundContainer = new Container(); foregroundContainer = new Container(); // Add containers to game game.addChild(backgroundContainer); game.addChild(middlegroundContainer); game.addChild(foregroundContainer); // Initialize FacekitManager facekitMgr = new FacekitManager(); facekitMgr.initialize(fakeCamera); // Global target position for the troll face targetPosition = { x: 2048 / 2, y: 2732 / 2 }; // Setup background background = LK.getAsset('whiteBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, visible: true }); backgroundContainer.addChild(background); // Setup UI text instructionText = new Text2('Tap anywhere to change troll face', { size: 50, fill: 0xFF0000 }); instructionText.anchor.set(0.5, 0); LK.gui.top.addChild(instructionText); instructionText.y = 1800; instructionText.x = -500; // Load the last used style from storage var lastStyle = storage.lastTrollStyle || 3; // Create a container for the face var faceContainer = new Container(); var faceContainerBg = LK.getAsset('whiteBackground', { anchorX: 0, anchorY: 0, x: 0, y: 230, width: 2048, height: 2732 - 230 - 430, visible: true }); faceContainer.addChild(faceContainerBg); middlegroundContainer.addChild(faceContainer); // Create the troll face trollFace = new TrollFace(); trollFace.currentStyle = lastStyle; trollFace.initialize(); trollFace.nextStyle(lastStyle); faceContainer.addChild(trollFace); // Add tap handler to the face container faceContainer.down = function (x, y, obj) { // Switch to the next troll face style var newStyle = trollFace.nextStyle(); // Save the current style to storage storage.lastTrollStyle = newStyle; // Update the style text styleText.setText('Style: ' + newStyle + '/3'); // Play switch sound LK.getSound('switchTroll').play(); // If no face tracking, ensure the face is displayed if (!facekit || !facekit.noseTip || !facekitMgr.isTracking) { // Set bypass tracking temporarily to ensure face is displayed var originalBypass = bypassTracking; bypassTracking = true; // Update face position to show the face trollFace.updateFacePosition(); // Restore original bypass value bypassTracking = originalBypass; } }; // Initialize tracking state isTrackingFace = false; // Add ButtonPunchline to the foreground at the bottom center var buttonPunchline = new ButtonPunchline(); buttonPunchline.x = 2048 / 2; // Center horizontally buttonPunchline.y = 2732 - 300; // Position at the bottom foregroundContainer.addChild(buttonPunchline); // Add ButtonSettings to the foreground at the bottom right buttonSettings = new ButtonSettings(); buttonSettings.x = 2048 - 180; // Position at the bottom right buttonSettings.y = 120; // Position at the bottom foregroundContainer.addChild(buttonSettings); settingsButtonBounds = { x: buttonSettings.x - buttonSettings.width / 2, y: buttonSettings.y - buttonSettings.height / 2, width: buttonSettings.width, height: buttonSettings.height }; // Initialize the settings popup and add it to foreground settingsPopup = new SettingsPopup(); settingsPopup.x = 2048 / 2; // Center horizontally settingsPopup.y = 2732 / 2; // Center vertically settingsPopup.visible = false; // Initially not visible foregroundContainer.addChild(settingsPopup); // Add style text styleText = new Text2('Style: ' + lastStyle + '/3', { size: 50, fill: 0xFF0000 }); styleText.anchor.set(0.5, 0); LK.gui.top.addChild(styleText); styleText.y = 1800; // Debug mode (turn on for development, off for production) debugPoints = null; if (debugMode) { debugPoints = new DebugPoints(); foregroundContainer.addChild(debugPoints); // Log facekit to console every second LK.setInterval(function () { console.log(facekit); if (facekit.lowerLip) { var elementOffset = trollFace.currentOffsets.lowerLip; var eyeDistance = trollFace.getEyeDistance(facekit); console.log("lowerLip y:", facekit.lowerLip.y, "minY:", elementOffset.minY * eyeDistance, "maxY:", elementOffset.maxY * eyeDistance); } // Display rotation angle in degrees for debugging var rotationDegrees = Math.round(trollFace.rotation * (180 / Math.PI)); instructionText.setText("le:".concat(Math.round(facekit.leftEye.x), ",").concat(Math.round(facekit.leftEye.y), " / ") + "re:".concat(Math.round(facekit.rightEye.x), ",").concat(Math.round(facekit.rightEye.y), " / ") + "ul:".concat(Math.round(facekit.upperLip.x), ",").concat(Math.round(facekit.upperLip.y), " / ") + "ll:".concat(Math.round(facekit.lowerLip.x), ",").concat(Math.round(facekit.lowerLip.y), " / ") + "rot:".concat(rotationDegrees, "Ā°")); }, 1000); } } // Initialize the game initializeGame();
===================================================================
--- original.js
+++ change.js
@@ -220,10 +220,10 @@
particle.alpha = progress * 2; // 0 to 1
} else {
particle.alpha = 2 - progress * 2; // 1 to 0
}
- // Scale up to max scale and stay at max scale
- var scaleProgress = Math.min(Math.sin(progress * Math.PI), 1);
+ // Scale up progressively to max scale and then stay there
+ var scaleProgress = progress < 0.5 ? progress * 2 : 1; // 0 to 1 during first half, then stay at 1
var currentScale = particle.maxScale * scaleProgress;
particle.scale.set(currentScale, currentScale);
}
}