User prompt
Create a cloud class that generates clouds off the top third of the screen to the right. The clouds will travel left and be destroyed off of the screen.
User prompt
Use the storage plugin and add a Best score in the bottom left corner. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Increase the size of collision for the seagulls.
Code edit (1 edits merged)
Please save this source code
User prompt
Update wave system as needed with: // In WaveSystem class, where calibration text is created var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.alpha = 0.7; background.scale.set(2, 2); // Adjust scale as needed // Add background first so it's behind the text LK.gui.center.addChild(background); LK.gui.center.addChild(calibrationText); LK.gui.center.addChild(progressText); // Update finishCalibration to remove both text and background function finishCalibration() { // ... existing code ... background.destroy(); calibrationText.destroy(); progressText.destroy(); self.isCalibrating = false; }
User prompt
Please fix the bug: 'Can't find variable: calibrationText' in or related to this line: 'textContainer.addChild(calibrationText);' Line Number: 571
User prompt
Please fix the bug: 'Can't find variable: calibrationText' in or related to this line: 'textContainer.addChild(calibrationText);' Line Number: 571
User prompt
Update wave system with: // In WaveSystem class, where we set up calibration UI var calibrationGroup = Container.expand(function() { var self = Container.call(this); var background = self.attachAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.alpha = 0.7; background.scale.set(2, 2); // Scale background independently // Create a separate container for text to keep its scale independent var textContainer = Container.call(this); self.addChild(textContainer); textContainer.addChild(calibrationText); textContainer.addChild(progressText); // Position text calibrationText.y = -40; progressText.y = 40; return self; }); var calibrationUI = game.addChild(new calibrationGroup()); calibrationUI.x = 2048/2; calibrationUI.y = 400; // Update finishCalibration to remove the group function finishCalibration() { // ... existing calibration code ... calibrationUI.destroy(); self.isCalibrating = false; }
Code edit (1 edits merged)
Please save this source code
User prompt
Increase scale of calibration background to accommodate calibration text.
Code edit (1 edits merged)
Please save this source code
User prompt
Adjust calibration background size to be large enough to accommodate calibration text.
User prompt
Adjust background scale to match calibration text size.
User prompt
Please fix the bug: 'null is not an object (evaluating 'calibrationText.parent.addChild')' in or related to this line: 'calibrationText.parent.addChild(calibrationBackground);' Line Number: 357
User prompt
Update wave system with: var calibrationBackground = self.attachAsset('background', { anchorX: 0.5, anchorY: 0.5 }); calibrationBackground.alpha = 0.7; // Semi-transparent // Add text on top of background calibrationText.parent.addChild(calibrationBackground); calibrationBackground.addChild(calibrationText); calibrationBackground.addChild(progressText); // Adjust text positions relative to background calibrationText.y = -40; progressText.y = 40; // In finishCalibration, the background will be removed with the text function finishCalibration() { // ... rest of finishCalibration code ... calibrationBackground.destroy(); }
User prompt
I’m not seeing the background behind the calibration text. Debug.
User prompt
Use the background asset to create a background behind the calibration text.
User prompt
Increase another 20 pixels.
User prompt
Increase the size of the seagull collision detection a little bit.
User prompt
Down by another 20
User prompt
Adjust mouth tracking down by 50 pixels.
User prompt
Increase the volume threshold to change mouth alpha to 0.45
User prompt
Update with: var CALIBRATION_SAMPLES = 100; // Doubled to 100 samples
User prompt
Start mouth alpha at 0 and change to 1 when facekit volume is greater than 0.35
User prompt
Position mouth over players mouth using facekit. Initialize after Poseidon to appear on top. ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var ParticleSystem = Container.expand(function () { var self = Container.call(this); self.addWaterParticle = function (x, y, type) { var particle = ParticlePool.get(); if (type === 'trail') { // Move spawn point up and behind surfer particle.x = x - 100; // Spawn behind (to the right of) surfer particle.y = y + 10; // Raised up from previous +90 } else { // Keep original splash positioning particle.x = x + Math.random() * 130 - 40; particle.y = y - 20; } particle.init(type); self.addChild(particle); }; self.update = function () { for (var i = self.children.length - 1; i >= 0; i--) { var particle = self.children[i]; if (particle.update) { particle.update(); } // Remove particles that have moved far off screen if (particle.x < -500 || particle.x > 2548 || particle.y < -500 || particle.y > 3232) { ParticlePool["return"](particle); self.removeChild(particle); } } }; return self; }); var Rock = Container.expand(function () { var self = Container.call(this); var rock = self.attachAsset('island', { anchorX: 0.5, anchorY: 1 // Anchor to top since we're stretching upward }); // Set scales independently rock.scaleX = 2; rock.scaleY = 2; // Start with a base scale self.checkCollision = function (surfer) { var rockWidth = self.width; var rockHeight = self.height; var rockTop = self.y - rockHeight; var points = [{ x: self.x, y: rockTop }, { x: self.x - rockWidth * 0.4, y: self.y - rockHeight * 0.1 }, { x: self.x + rockWidth * 0.4, y: self.y - rockHeight * 0.1 }]; var surferBox = { x: surfer.x - 30, y: surfer.y - 30, width: 60, height: 60 }; return isPointInTriangle(surferBox.x, surferBox.y, points) || isPointInTriangle(surferBox.x + surferBox.width, surferBox.y, points) || isPointInTriangle(surferBox.x, surferBox.y + surferBox.height, points) || isPointInTriangle(surferBox.x + surferBox.width, surferBox.y + surferBox.height, points); }; self.update = function () { if (self.lastX === undefined) { self.lastX = self.x; } // Initialize lastX if not set self.x -= 8; if (self.x < -self.width) { self.destroy(); } self.lastX = self.x; // Update lastX }; return self; }); var Seagull = Container.expand(function () { var self = Container.call(this); var seagullGraphics = self.attachAsset('seagull', { anchorX: 0.5, anchorY: 0.5 }); // Circle collision data self.radius = 35; self.collisionX = 0; self.collisionY = 0; self.speed = -5 - Math.random() * 3; // Random speed variation self.y = Math.random() * (2732 * 0.33); // Top third of screen self.x = 2248; // Start off-screen right // Flying animation function flyAnimation() { tween(self, { y: self.y + (Math.random() * 100 - 50) }, { duration: 1000, easing: tween.easeInOut, onFinish: flyAnimation }); // Wing flap effect tween(seagullGraphics, { scaleY: 0.8, y: 5 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(seagullGraphics, { scaleY: 1, y: 0 }, { duration: 500, easing: tween.easeInOut }); } }); } flyAnimation(); self.update = function () { self.x += self.speed; // Update collision circle position self.collisionX = self.x; self.collisionY = self.y; // Remove if off screen if (self.x < -100) { self.destroy(); } // Circle collision with surfer var dx = self.collisionX - surfer.x; var dy = self.collisionY - surfer.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.radius + 40) { // 40 is approximate surfer radius LK.showGameOver(); } }; return self; }); var Surfer = Container.expand(function () { var self = Container.call(this); var BASE_GRAVITY = 1.5; // Reduced for smoother acceleration var MAX_SPEED = 40; // Increased for longer falls var DAMPENING = 0.98; // Less dampening for smoother motion var WAVE_OFFSET = -30; var FIXED_X = 2048 * 0.35; var MAX_AIR_VELOCITY = -25; // Only applies when surfer is above wave var velocityY = 0; var velocityRot = 0; var activeTween = null; // Initialize activeTween variable var lastWaveY = 2732 * 0.85; // Match WaveSystem's baseY var initialized = false; var surferGraphics = self.attachAsset('surfer', { anchorX: 0.5, anchorY: 0.8 }); self.x = FIXED_X; self.y = lastWaveY + WAVE_OFFSET; // Start at base wave height self.setWaveSystem = function (ws) { waveSystem = ws; if (!waveSystem.points) { waveSystem.points = []; } initialized = true; }; self.update = function () { // Add tween cleanup if (activeTween) { tween.stop(activeTween); } if (!waveSystem) { return; } var normalizedX = self.x / 2048; var pointIndex = Math.floor(normalizedX * (waveSystem.points.length - 1)); var nextPointIndex = Math.min(pointIndex + 1, waveSystem.points.length - 1); var progress = normalizedX * (waveSystem.points.length - 1) - pointIndex; var currentPoint = waveSystem.points[pointIndex]; var nextPoint = waveSystem.points[nextPointIndex]; if (currentPoint && nextPoint) { var targetY = currentPoint.y + (nextPoint.y - currentPoint.y) * progress + WAVE_OFFSET; } else { return; // Exit update if points are undefined } var waveRising = targetY < lastWaveY; var aboveWave = self.y < targetY; if (waveRising && !aboveWave) { // Natural wave following - no limits when on the wave velocityY = (targetY - self.y) * 0.2; self.y += velocityY; } else { // In air or falling velocityY += BASE_GRAVITY; velocityY *= DAMPENING; // Only cap velocity when above the wave if (aboveWave && velocityY < MAX_AIR_VELOCITY) { velocityY = MAX_AIR_VELOCITY; } self.y += velocityY; if (self.y > targetY && velocityY > 0) { var fallVelocity = velocityY; // Capture velocity before reset self.y = targetY; velocityY = 0; if (aboveWave && fallVelocity > 15) { // Only splash on significant falls createSplash(); } } } lastWaveY = targetY; var baseRotation = -0.1; // Slight left tilt as base position var waveRotation = Math.sin(LK.ticks / 30) * 0.05; // Gentle wave motion // Calculate vertical movement speed var verticalSpeed = self.y - lastWaveY; // Add rotation based on vertical movement var verticalRotation = 0; if (verticalSpeed < -2) { // Moving up verticalRotation = Math.min(0.3, -verticalSpeed * 0.005); // Changed from 0.01 } else if (verticalSpeed > 2) { // Moving down verticalRotation = Math.max(-0.4, -verticalSpeed * 0.005); // Changed from 0.01 } // Smoothly apply all rotations velocityRot += (baseRotation + waveRotation + verticalRotation - self.rotation) * 0.05; // Changed from 0.1 velocityRot *= 0.9; // Changed from 0.8 for less damping self.rotation += velocityRot; // Create trail particles whenever touching water if (self.y >= targetY) { createTrail(); } var targetAngle = Math.atan2(nextPoint.y - currentPoint.y, nextPoint.x - currentPoint.x); // ... rest of the rotation code ... particles.children.forEach(function (particle) { if (particle.update) { particle.update(); } }); }; self.getScore = function () { return Math.floor(score); }; // Then in the Surfer class, replace the particle system with: var particles = self.addChild(new ParticleSystem()); function createSplash() { for (var i = 0; i < 50; i++) { // Move splash up by 40 to match trail Y position game.particles.addWaterParticle(FIXED_X, self.y + 40, 'splash'); } } function createTrail() { // Create fewer particles but more frequently for trail for (var i = 0; i < 4; i++) { game.particles.addWaterParticle(FIXED_X, self.y, 'trail'); } } return self; }); var WaterParticle = Container.expand(function () { var self = Container.call(this); var particleGraphics = self.attachAsset('wave_point', { anchorX: 0.5, anchorY: 0.5 }); var speed, angle, scale; self.init = function (type) { // Reset properties scale = 1; self.type = type; if (type === 'trail') { speed = Math.random() * 8 + 6; angle = Math.PI + (Math.random() * 0.4 - 0.2); scale = 0.7; particleGraphics.alpha = 0.8; } else { speed = Math.random() * 12 + 8; angle = -Math.PI / 2 + (Math.random() * 1.4 - 0.7); scale = 1.2; particleGraphics.alpha = 0.9; } self.scale.set(scale, scale); }; self.update = function () { scale -= self.type === 'splash' ? 0.04 : 0.02; self.scale.set(scale, scale); if (scale <= 0) { // Instead of destroying, return to pool if (self.parent) { self.parent.removeChild(self); } ParticlePool["return"](self); return; } self.x += Math.cos(angle) * speed; self.y += Math.sin(angle) * speed; }; return self; }); var WaveSystem = Container.expand(function () { var self = Container.call(this); var NUM_POINTS = 40; var SCREEN_WIDTH = 2048; var points = []; var baseY = 2732 * 0.85; var MIN_PITCH = 50; var MAX_PITCH = 300; var calibrationSamples = []; self.isCalibrating = true; var CALIBRATION_SAMPLES = 50; // Increased sample size var CALIBRATION_VOLUME_THRESHOLD = 0.4; // Minimum volume to count as valid input var calibrationStartTime = LK.ticks; var CALIBRATION_TIMEOUT = 300; // 5 seconds (60 ticks per second) var WAVE_HEIGHT = 2000; var lastPitch = 0; // Create calibration message var calibrationText = new Text2("Make some noise!\nHum high and low to calibrate", { size: 60, fill: 0xFFFFFF, align: 'center' }); calibrationText.anchor.set(0.5, 0.5); LK.gui.center.addChild(calibrationText); // Progress indicator var progressText = new Text2("0%", { size: 40, fill: 0xFFFFFF, alpha: 0.8 }); progressText.anchor.set(0.5, 0.5); progressText.y = calibrationText.y + 80; LK.gui.center.addChild(progressText); // Removed visual pitch indicator and labels function updatePitchRange(pitch) { if (facekit.volume > CALIBRATION_VOLUME_THRESHOLD) { calibrationSamples.push(pitch); // Update progress display var progress = Math.min(100, Math.floor(calibrationSamples.length / CALIBRATION_SAMPLES * 100)); progressText.setText(progress + "%"); // Removed pitch indicator position update as visual indicator is no longer used if (calibrationSamples.length >= CALIBRATION_SAMPLES) { finishCalibration(); } } // Check for timeout if (LK.ticks - calibrationStartTime > CALIBRATION_TIMEOUT) { if (calibrationSamples.length < 10) { // Not enough samples // Reset and extend time calibrationSamples = []; calibrationStartTime = LK.ticks; calibrationText.setText("Let's try again!\nMake some noise - hum or speak"); } else { finishCalibration(); // Use what we have } } } function finishCalibration() { var sortedPitches = calibrationSamples.sort(function (a, b) { return a - b; }); MIN_PITCH = sortedPitches[Math.floor(calibrationSamples.length * 0.1)]; MAX_PITCH = sortedPitches[Math.floor(calibrationSamples.length * 0.9)]; // Add padding to range MIN_PITCH = Math.max(30, MIN_PITCH - 10); MAX_PITCH = MAX_PITCH + 20; self.isCalibrating = false; // Show completion message calibrationText.setText("Ready!"); progressText.destroy(); // Fade out completion message tween(calibrationText, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { return calibrationText.destroy(); } }); // Removed pitch meter cleanup console.log("Calibrated pitch range:", MIN_PITCH, "to", MAX_PITCH); } // Enhanced smoothing system var heightBuffer = Array(15).fill(0); var bufferIndex = 0; var lastSmoothedHeight = 0; // Water fill rectangles var waterRects = []; var NUM_WATER_SECTIONS = 80; // Reduced from 80 for (var i = 0; i < NUM_WATER_SECTIONS; i++) { var rect = self.attachAsset('wave_line', { anchorX: 0.5, // Center anchor anchorY: 0, // Top anchor height: 2732, width: SCREEN_WIDTH / NUM_WATER_SECTIONS + 1, // Tiny overlap to prevent pixel gaps tint: 0x0066cc, alpha: 0.3 }); waterRects.push(rect); } // Create wave line segments var lines = []; function createLines() { // Clean up any existing lines first lines.forEach(function (line) { line.destroy(); self.removeChild(line); }); lines = []; // Create new lines for (var i = 0; i < NUM_POINTS - 1; i++) { var line = self.attachAsset('wave_line', { anchorX: 0, anchorY: 0.5, height: 4 }); lines.push(line); } } // Call initial creation createLines(); function gaussianCurve(x) { return Math.exp(-(x * x) / 0.8); } function getSmoothedHeight(newHeight) { heightBuffer[bufferIndex] = newHeight; bufferIndex = (bufferIndex + 1) % heightBuffer.length; // Simplified smoothing calculation var total = 0; for (var i = 0; i < heightBuffer.length; i++) { total += heightBuffer[i]; } var smoothedHeight = total / heightBuffer.length; lastSmoothedHeight = lastSmoothedHeight * 0.7 + smoothedHeight * 0.3; return lastSmoothedHeight; } self.update = function () { // Always calculate wave points first, regardless of calibration state var targetPitch = 0; if (facekit.volume > 0.35) { var normalizedPitch = (facekit.pitch - MIN_PITCH) / (MAX_PITCH - MIN_PITCH); normalizedPitch = Math.max(0, Math.min(1, normalizedPitch)); targetPitch = normalizedPitch * 0.8 + lastPitch * 0.2; } lastPitch += (targetPitch - lastPitch) * 0.2; var smoothedHeight = getSmoothedHeight(lastPitch * WAVE_HEIGHT); points = []; for (var i = 0; i < NUM_POINTS; i++) { var x = i / (NUM_POINTS - 1) * SCREEN_WIDTH; var distanceFromCenter = (x - SCREEN_WIDTH * 0.35) / (SCREEN_WIDTH * 0.45); var heightFactor = gaussianCurve(distanceFromCenter); var y = baseY - smoothedHeight * heightFactor; points.push({ x: x, y: y }); } // Always update water rectangles for (var i = 0; i < NUM_WATER_SECTIONS; i++) { var rect = waterRects[i]; var xPosition = i / NUM_WATER_SECTIONS * SCREEN_WIDTH; var exactPointPosition = xPosition / SCREEN_WIDTH * (NUM_POINTS - 1); var pointIndex = Math.floor(exactPointPosition); var nextPointIndex = Math.min(pointIndex + 1, NUM_POINTS - 1); var progress = exactPointPosition - pointIndex; var y1 = points[pointIndex].y; var y2 = points[nextPointIndex].y; var y = y1 + (y2 - y1) * progress; rect.x = xPosition; rect.y = y; rect.height = 2732 - y; rect.alpha = 0.3 + Math.sin(LK.ticks * 0.02 + i * 0.2) * 0.02; } self.points = points; // Handle calibration if still calibrating if (self.isCalibrating) { if (facekit.volume > CALIBRATION_VOLUME_THRESHOLD) { calibrationSamples.push(facekit.pitch); var progress = Math.min(100, Math.floor(calibrationSamples.length / CALIBRATION_SAMPLES * 100)); progressText.setText(progress + "%"); if (calibrationSamples.length >= CALIBRATION_SAMPLES) { finishCalibration(); } } if (LK.ticks - calibrationStartTime > CALIBRATION_TIMEOUT) { if (calibrationSamples.length < 10) { calibrationSamples = []; calibrationStartTime = LK.ticks; calibrationText.setText("Let's try again!\nMake some noise - hum or speak"); } else { finishCalibration(); } } } else { // Only update score when not calibrating if (facekit.volume > 0.35) { score += smoothedHeight / WAVE_HEIGHT * multiplier; multiplier += 0.001; } else { multiplier = Math.max(1, multiplier - 0.01); } } // Always update wave line segments for (var i = 0; i < lines.length; i++) { var start = points[i]; var end = points[i + 1]; var dx = end.x - start.x; var dy = end.y - start.y; var length = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); lines[i].x += (start.x - lines[i].x) * 0.3; lines[i].y += (start.y - lines[i].y) * 0.3; lines[i].width += (length - lines[i].width) * 0.3; var angleDiff = angle - lines[i].rotation; if (angleDiff > Math.PI) { angleDiff -= Math.PI * 2; } if (angleDiff < -Math.PI) { angleDiff += Math.PI * 2; } lines[i].rotation += angleDiff * 0.3; } }; self.destroy = function () { waterRects.forEach(function (rect) { return rect.destroy(); }); lines.forEach(function (line) { return line.destroy(); }); waterRects = []; lines = []; Container.prototype.destroy.call(this); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 //Init game with black background }); /**** * Game Code ****/ var ParticlePool = { pool: [], maxSize: 200, get: function get() { if (this.pool.length > 0) { var particle = this.pool.pop(); particle.visible = true; return particle; } return new WaterParticle(); }, "return": function _return(particle) { if (this.pool.length < this.maxSize) { // Reset particle properties particle.visible = false; particle.scale.set(1, 1); particle.x = 0; particle.y = 0; particle.rotation = 0; this.pool.push(particle); } else { particle.destroy(); } } }; function isPointInTriangle(px, py, points) { var p0 = points[0], p1 = points[1], p2 = points[2]; var A = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y); var sign = A < 0 ? -1 : 1; var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * px + (p0.x - p2.x) * py) * sign; var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * px + (p1.x - p0.x) * py) * sign; return s > 0 && t > 0 && s + t < 2 * A * sign; } var lastScoreDisplay = -1; var lastMultiplierDisplay = -1; var originalSetInterval = LK.setInterval; LK.setInterval = function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var timer = originalSetInterval.apply(this, args); gameTimers.push(timer); return timer; }; var originalSetTimeout = LK.setTimeout; LK.setTimeout = function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } var timer = originalSetTimeout.apply(this, args); gameTimers.push(timer); return timer; }; var gameTimers = []; var seagullSpawnInterval = 6000; // Spawn every 3 seconds var lastSeagullSpawn = 0; function spawnSeagull() { if (Date.now() - lastSeagullSpawn > seagullSpawnInterval) { var seagull = new Seagull(); game.addChild(seagull); lastSeagullSpawn = Date.now(); seagullSpawnInterval = 4000 + Math.random() * 4000; // Random interval between 2-4 seconds } } var rocks = []; var lastRockTime = 0; var score = 0; // Initialize score variable var multiplier = 1; var lastWaveHeight = 0; var lastWaveUpdate = 0; var devicePerformance = 1; // Default performance multiplier var poseidon = LK.getAsset('poseidon', { anchorX: 0.5, anchorY: 0.5 }); game.addChild(poseidon); var mouth = LK.getAsset('mouth', { anchorX: 0.5, // Center anchor anchorY: 0.5 // Center anchor }); game.addChild(mouth); var waveSystem = game.addChild(new WaveSystem()); var particles = new ParticleSystem(); game.particles = game.addChild(new ParticleSystem()); var surfer = game.addChild(new Surfer()); surfer.setWaveSystem(waveSystem); var scoreText = new Text2("", { size: 100, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); LK.gui.top.addChild(scoreText); var multiplierText = new Text2("", { size: 60, fill: 0xFFFFFF, alpha: 0.8 }); multiplierText.anchor.set(1, 1); LK.gui.bottomRight.addChild(multiplierText); // Game update function var lastUpdateTime = 0; game.update = function () { var now = Date.now(); if (now - lastUpdateTime < 16) { // Don't update faster than 60fps return; } lastUpdateTime = now; // Every 300 frames, log counts if (LK.ticks % 600 === 0) { console.log('Active timers:', gameTimers.length); } if (LK.ticks % 300 === 0) { console.log('Particle count:', game.particles.children.length); console.log('Wave points:', waveSystem.points.length); console.log('Total game children:', game.children.length); console.log('Active tweens:', tween.getActiveTweens ? tween.getActiveTweens().length : 0); } waveSystem.update(); surfer.update(); particles.update(); // Update Poseidon's position to follow the player's face if (facekit.noseTip) { poseidon.x = facekit.noseTip.x; poseidon.y = facekit.noseTip.y; } // Update mouth position to follow the player's mouth if (facekit.mouthCenter) { mouth.x = facekit.mouthCenter.x; mouth.y = facekit.mouthCenter.y; } // Only spawn obstacles if not calibrating if (!waveSystem.isCalibrating) { // Rock spawning if (LK.ticks - lastRockTime > 240 + Math.random() * 360) { lastRockTime = LK.ticks; var rock = game.addChild(new Rock()); rock.x = 2548; // Start off right side rock.y = 2732 + 200; // Place base below screen // Set the vertical scale independently for random height rock.children[0].scaleY = 3 + Math.random() * 4; // Random height between 3x and 7x rocks.push(rock); } // Seagull spawning spawnSeagull(); } // Update and check rocks for (var i = rocks.length - 1; i >= 0; i--) { var rock = rocks[i]; rock.update(); // Remove destroyed rocks if (!rock.parent) { rocks.splice(i, 1); continue; } // Check collision with surfer if (rock.checkCollision(surfer)) { LK.showGameOver(); } } game.children.forEach(function (child) { if (child instanceof Seagull && child.update) { child.update(); } }); var currentScore = Math.floor(score); if (currentScore !== lastScoreDisplay) { lastScoreDisplay = currentScore; scoreText.setText(currentScore.toLocaleString()); } var currentMultiplier = Math.floor(multiplier * 10) / 10; // Round to 1 decimal if (currentMultiplier !== lastMultiplierDisplay) { lastMultiplierDisplay = currentMultiplier; multiplierText.setText("x" + currentMultiplier.toFixed(1)); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var ParticleSystem = Container.expand(function () {
var self = Container.call(this);
self.addWaterParticle = function (x, y, type) {
var particle = ParticlePool.get();
if (type === 'trail') {
// Move spawn point up and behind surfer
particle.x = x - 100; // Spawn behind (to the right of) surfer
particle.y = y + 10; // Raised up from previous +90
} else {
// Keep original splash positioning
particle.x = x + Math.random() * 130 - 40;
particle.y = y - 20;
}
particle.init(type);
self.addChild(particle);
};
self.update = function () {
for (var i = self.children.length - 1; i >= 0; i--) {
var particle = self.children[i];
if (particle.update) {
particle.update();
}
// Remove particles that have moved far off screen
if (particle.x < -500 || particle.x > 2548 || particle.y < -500 || particle.y > 3232) {
ParticlePool["return"](particle);
self.removeChild(particle);
}
}
};
return self;
});
var Rock = Container.expand(function () {
var self = Container.call(this);
var rock = self.attachAsset('island', {
anchorX: 0.5,
anchorY: 1 // Anchor to top since we're stretching upward
});
// Set scales independently
rock.scaleX = 2;
rock.scaleY = 2; // Start with a base scale
self.checkCollision = function (surfer) {
var rockWidth = self.width;
var rockHeight = self.height;
var rockTop = self.y - rockHeight;
var points = [{
x: self.x,
y: rockTop
}, {
x: self.x - rockWidth * 0.4,
y: self.y - rockHeight * 0.1
}, {
x: self.x + rockWidth * 0.4,
y: self.y - rockHeight * 0.1
}];
var surferBox = {
x: surfer.x - 30,
y: surfer.y - 30,
width: 60,
height: 60
};
return isPointInTriangle(surferBox.x, surferBox.y, points) || isPointInTriangle(surferBox.x + surferBox.width, surferBox.y, points) || isPointInTriangle(surferBox.x, surferBox.y + surferBox.height, points) || isPointInTriangle(surferBox.x + surferBox.width, surferBox.y + surferBox.height, points);
};
self.update = function () {
if (self.lastX === undefined) {
self.lastX = self.x;
} // Initialize lastX if not set
self.x -= 8;
if (self.x < -self.width) {
self.destroy();
}
self.lastX = self.x; // Update lastX
};
return self;
});
var Seagull = Container.expand(function () {
var self = Container.call(this);
var seagullGraphics = self.attachAsset('seagull', {
anchorX: 0.5,
anchorY: 0.5
});
// Circle collision data
self.radius = 35;
self.collisionX = 0;
self.collisionY = 0;
self.speed = -5 - Math.random() * 3; // Random speed variation
self.y = Math.random() * (2732 * 0.33); // Top third of screen
self.x = 2248; // Start off-screen right
// Flying animation
function flyAnimation() {
tween(self, {
y: self.y + (Math.random() * 100 - 50)
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: flyAnimation
});
// Wing flap effect
tween(seagullGraphics, {
scaleY: 0.8,
y: 5
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(seagullGraphics, {
scaleY: 1,
y: 0
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
flyAnimation();
self.update = function () {
self.x += self.speed;
// Update collision circle position
self.collisionX = self.x;
self.collisionY = self.y;
// Remove if off screen
if (self.x < -100) {
self.destroy();
}
// Circle collision with surfer
var dx = self.collisionX - surfer.x;
var dy = self.collisionY - surfer.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.radius + 40) {
// 40 is approximate surfer radius
LK.showGameOver();
}
};
return self;
});
var Surfer = Container.expand(function () {
var self = Container.call(this);
var BASE_GRAVITY = 1.5; // Reduced for smoother acceleration
var MAX_SPEED = 40; // Increased for longer falls
var DAMPENING = 0.98; // Less dampening for smoother motion
var WAVE_OFFSET = -30;
var FIXED_X = 2048 * 0.35;
var MAX_AIR_VELOCITY = -25; // Only applies when surfer is above wave
var velocityY = 0;
var velocityRot = 0;
var activeTween = null; // Initialize activeTween variable
var lastWaveY = 2732 * 0.85; // Match WaveSystem's baseY
var initialized = false;
var surferGraphics = self.attachAsset('surfer', {
anchorX: 0.5,
anchorY: 0.8
});
self.x = FIXED_X;
self.y = lastWaveY + WAVE_OFFSET; // Start at base wave height
self.setWaveSystem = function (ws) {
waveSystem = ws;
if (!waveSystem.points) {
waveSystem.points = [];
}
initialized = true;
};
self.update = function () {
// Add tween cleanup
if (activeTween) {
tween.stop(activeTween);
}
if (!waveSystem) {
return;
}
var normalizedX = self.x / 2048;
var pointIndex = Math.floor(normalizedX * (waveSystem.points.length - 1));
var nextPointIndex = Math.min(pointIndex + 1, waveSystem.points.length - 1);
var progress = normalizedX * (waveSystem.points.length - 1) - pointIndex;
var currentPoint = waveSystem.points[pointIndex];
var nextPoint = waveSystem.points[nextPointIndex];
if (currentPoint && nextPoint) {
var targetY = currentPoint.y + (nextPoint.y - currentPoint.y) * progress + WAVE_OFFSET;
} else {
return; // Exit update if points are undefined
}
var waveRising = targetY < lastWaveY;
var aboveWave = self.y < targetY;
if (waveRising && !aboveWave) {
// Natural wave following - no limits when on the wave
velocityY = (targetY - self.y) * 0.2;
self.y += velocityY;
} else {
// In air or falling
velocityY += BASE_GRAVITY;
velocityY *= DAMPENING;
// Only cap velocity when above the wave
if (aboveWave && velocityY < MAX_AIR_VELOCITY) {
velocityY = MAX_AIR_VELOCITY;
}
self.y += velocityY;
if (self.y > targetY && velocityY > 0) {
var fallVelocity = velocityY; // Capture velocity before reset
self.y = targetY;
velocityY = 0;
if (aboveWave && fallVelocity > 15) {
// Only splash on significant falls
createSplash();
}
}
}
lastWaveY = targetY;
var baseRotation = -0.1; // Slight left tilt as base position
var waveRotation = Math.sin(LK.ticks / 30) * 0.05; // Gentle wave motion
// Calculate vertical movement speed
var verticalSpeed = self.y - lastWaveY;
// Add rotation based on vertical movement
var verticalRotation = 0;
if (verticalSpeed < -2) {
// Moving up
verticalRotation = Math.min(0.3, -verticalSpeed * 0.005); // Changed from 0.01
} else if (verticalSpeed > 2) {
// Moving down
verticalRotation = Math.max(-0.4, -verticalSpeed * 0.005); // Changed from 0.01
}
// Smoothly apply all rotations
velocityRot += (baseRotation + waveRotation + verticalRotation - self.rotation) * 0.05; // Changed from 0.1
velocityRot *= 0.9; // Changed from 0.8 for less damping
self.rotation += velocityRot;
// Create trail particles whenever touching water
if (self.y >= targetY) {
createTrail();
}
var targetAngle = Math.atan2(nextPoint.y - currentPoint.y, nextPoint.x - currentPoint.x);
// ... rest of the rotation code ...
particles.children.forEach(function (particle) {
if (particle.update) {
particle.update();
}
});
};
self.getScore = function () {
return Math.floor(score);
};
// Then in the Surfer class, replace the particle system with:
var particles = self.addChild(new ParticleSystem());
function createSplash() {
for (var i = 0; i < 50; i++) {
// Move splash up by 40 to match trail Y position
game.particles.addWaterParticle(FIXED_X, self.y + 40, 'splash');
}
}
function createTrail() {
// Create fewer particles but more frequently for trail
for (var i = 0; i < 4; i++) {
game.particles.addWaterParticle(FIXED_X, self.y, 'trail');
}
}
return self;
});
var WaterParticle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('wave_point', {
anchorX: 0.5,
anchorY: 0.5
});
var speed, angle, scale;
self.init = function (type) {
// Reset properties
scale = 1;
self.type = type;
if (type === 'trail') {
speed = Math.random() * 8 + 6;
angle = Math.PI + (Math.random() * 0.4 - 0.2);
scale = 0.7;
particleGraphics.alpha = 0.8;
} else {
speed = Math.random() * 12 + 8;
angle = -Math.PI / 2 + (Math.random() * 1.4 - 0.7);
scale = 1.2;
particleGraphics.alpha = 0.9;
}
self.scale.set(scale, scale);
};
self.update = function () {
scale -= self.type === 'splash' ? 0.04 : 0.02;
self.scale.set(scale, scale);
if (scale <= 0) {
// Instead of destroying, return to pool
if (self.parent) {
self.parent.removeChild(self);
}
ParticlePool["return"](self);
return;
}
self.x += Math.cos(angle) * speed;
self.y += Math.sin(angle) * speed;
};
return self;
});
var WaveSystem = Container.expand(function () {
var self = Container.call(this);
var NUM_POINTS = 40;
var SCREEN_WIDTH = 2048;
var points = [];
var baseY = 2732 * 0.85;
var MIN_PITCH = 50;
var MAX_PITCH = 300;
var calibrationSamples = [];
self.isCalibrating = true;
var CALIBRATION_SAMPLES = 50; // Increased sample size
var CALIBRATION_VOLUME_THRESHOLD = 0.4; // Minimum volume to count as valid input
var calibrationStartTime = LK.ticks;
var CALIBRATION_TIMEOUT = 300; // 5 seconds (60 ticks per second)
var WAVE_HEIGHT = 2000;
var lastPitch = 0;
// Create calibration message
var calibrationText = new Text2("Make some noise!\nHum high and low to calibrate", {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
calibrationText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(calibrationText);
// Progress indicator
var progressText = new Text2("0%", {
size: 40,
fill: 0xFFFFFF,
alpha: 0.8
});
progressText.anchor.set(0.5, 0.5);
progressText.y = calibrationText.y + 80;
LK.gui.center.addChild(progressText);
// Removed visual pitch indicator and labels
function updatePitchRange(pitch) {
if (facekit.volume > CALIBRATION_VOLUME_THRESHOLD) {
calibrationSamples.push(pitch);
// Update progress display
var progress = Math.min(100, Math.floor(calibrationSamples.length / CALIBRATION_SAMPLES * 100));
progressText.setText(progress + "%");
// Removed pitch indicator position update as visual indicator is no longer used
if (calibrationSamples.length >= CALIBRATION_SAMPLES) {
finishCalibration();
}
}
// Check for timeout
if (LK.ticks - calibrationStartTime > CALIBRATION_TIMEOUT) {
if (calibrationSamples.length < 10) {
// Not enough samples
// Reset and extend time
calibrationSamples = [];
calibrationStartTime = LK.ticks;
calibrationText.setText("Let's try again!\nMake some noise - hum or speak");
} else {
finishCalibration(); // Use what we have
}
}
}
function finishCalibration() {
var sortedPitches = calibrationSamples.sort(function (a, b) {
return a - b;
});
MIN_PITCH = sortedPitches[Math.floor(calibrationSamples.length * 0.1)];
MAX_PITCH = sortedPitches[Math.floor(calibrationSamples.length * 0.9)];
// Add padding to range
MIN_PITCH = Math.max(30, MIN_PITCH - 10);
MAX_PITCH = MAX_PITCH + 20;
self.isCalibrating = false;
// Show completion message
calibrationText.setText("Ready!");
progressText.destroy();
// Fade out completion message
tween(calibrationText, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
return calibrationText.destroy();
}
});
// Removed pitch meter cleanup
console.log("Calibrated pitch range:", MIN_PITCH, "to", MAX_PITCH);
}
// Enhanced smoothing system
var heightBuffer = Array(15).fill(0);
var bufferIndex = 0;
var lastSmoothedHeight = 0;
// Water fill rectangles
var waterRects = [];
var NUM_WATER_SECTIONS = 80; // Reduced from 80
for (var i = 0; i < NUM_WATER_SECTIONS; i++) {
var rect = self.attachAsset('wave_line', {
anchorX: 0.5,
// Center anchor
anchorY: 0,
// Top anchor
height: 2732,
width: SCREEN_WIDTH / NUM_WATER_SECTIONS + 1,
// Tiny overlap to prevent pixel gaps
tint: 0x0066cc,
alpha: 0.3
});
waterRects.push(rect);
}
// Create wave line segments
var lines = [];
function createLines() {
// Clean up any existing lines first
lines.forEach(function (line) {
line.destroy();
self.removeChild(line);
});
lines = [];
// Create new lines
for (var i = 0; i < NUM_POINTS - 1; i++) {
var line = self.attachAsset('wave_line', {
anchorX: 0,
anchorY: 0.5,
height: 4
});
lines.push(line);
}
}
// Call initial creation
createLines();
function gaussianCurve(x) {
return Math.exp(-(x * x) / 0.8);
}
function getSmoothedHeight(newHeight) {
heightBuffer[bufferIndex] = newHeight;
bufferIndex = (bufferIndex + 1) % heightBuffer.length;
// Simplified smoothing calculation
var total = 0;
for (var i = 0; i < heightBuffer.length; i++) {
total += heightBuffer[i];
}
var smoothedHeight = total / heightBuffer.length;
lastSmoothedHeight = lastSmoothedHeight * 0.7 + smoothedHeight * 0.3;
return lastSmoothedHeight;
}
self.update = function () {
// Always calculate wave points first, regardless of calibration state
var targetPitch = 0;
if (facekit.volume > 0.35) {
var normalizedPitch = (facekit.pitch - MIN_PITCH) / (MAX_PITCH - MIN_PITCH);
normalizedPitch = Math.max(0, Math.min(1, normalizedPitch));
targetPitch = normalizedPitch * 0.8 + lastPitch * 0.2;
}
lastPitch += (targetPitch - lastPitch) * 0.2;
var smoothedHeight = getSmoothedHeight(lastPitch * WAVE_HEIGHT);
points = [];
for (var i = 0; i < NUM_POINTS; i++) {
var x = i / (NUM_POINTS - 1) * SCREEN_WIDTH;
var distanceFromCenter = (x - SCREEN_WIDTH * 0.35) / (SCREEN_WIDTH * 0.45);
var heightFactor = gaussianCurve(distanceFromCenter);
var y = baseY - smoothedHeight * heightFactor;
points.push({
x: x,
y: y
});
}
// Always update water rectangles
for (var i = 0; i < NUM_WATER_SECTIONS; i++) {
var rect = waterRects[i];
var xPosition = i / NUM_WATER_SECTIONS * SCREEN_WIDTH;
var exactPointPosition = xPosition / SCREEN_WIDTH * (NUM_POINTS - 1);
var pointIndex = Math.floor(exactPointPosition);
var nextPointIndex = Math.min(pointIndex + 1, NUM_POINTS - 1);
var progress = exactPointPosition - pointIndex;
var y1 = points[pointIndex].y;
var y2 = points[nextPointIndex].y;
var y = y1 + (y2 - y1) * progress;
rect.x = xPosition;
rect.y = y;
rect.height = 2732 - y;
rect.alpha = 0.3 + Math.sin(LK.ticks * 0.02 + i * 0.2) * 0.02;
}
self.points = points;
// Handle calibration if still calibrating
if (self.isCalibrating) {
if (facekit.volume > CALIBRATION_VOLUME_THRESHOLD) {
calibrationSamples.push(facekit.pitch);
var progress = Math.min(100, Math.floor(calibrationSamples.length / CALIBRATION_SAMPLES * 100));
progressText.setText(progress + "%");
if (calibrationSamples.length >= CALIBRATION_SAMPLES) {
finishCalibration();
}
}
if (LK.ticks - calibrationStartTime > CALIBRATION_TIMEOUT) {
if (calibrationSamples.length < 10) {
calibrationSamples = [];
calibrationStartTime = LK.ticks;
calibrationText.setText("Let's try again!\nMake some noise - hum or speak");
} else {
finishCalibration();
}
}
} else {
// Only update score when not calibrating
if (facekit.volume > 0.35) {
score += smoothedHeight / WAVE_HEIGHT * multiplier;
multiplier += 0.001;
} else {
multiplier = Math.max(1, multiplier - 0.01);
}
}
// Always update wave line segments
for (var i = 0; i < lines.length; i++) {
var start = points[i];
var end = points[i + 1];
var dx = end.x - start.x;
var dy = end.y - start.y;
var length = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
lines[i].x += (start.x - lines[i].x) * 0.3;
lines[i].y += (start.y - lines[i].y) * 0.3;
lines[i].width += (length - lines[i].width) * 0.3;
var angleDiff = angle - lines[i].rotation;
if (angleDiff > Math.PI) {
angleDiff -= Math.PI * 2;
}
if (angleDiff < -Math.PI) {
angleDiff += Math.PI * 2;
}
lines[i].rotation += angleDiff * 0.3;
}
};
self.destroy = function () {
waterRects.forEach(function (rect) {
return rect.destroy();
});
lines.forEach(function (line) {
return line.destroy();
});
waterRects = [];
lines = [];
Container.prototype.destroy.call(this);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 //Init game with black background
});
/****
* Game Code
****/
var ParticlePool = {
pool: [],
maxSize: 200,
get: function get() {
if (this.pool.length > 0) {
var particle = this.pool.pop();
particle.visible = true;
return particle;
}
return new WaterParticle();
},
"return": function _return(particle) {
if (this.pool.length < this.maxSize) {
// Reset particle properties
particle.visible = false;
particle.scale.set(1, 1);
particle.x = 0;
particle.y = 0;
particle.rotation = 0;
this.pool.push(particle);
} else {
particle.destroy();
}
}
};
function isPointInTriangle(px, py, points) {
var p0 = points[0],
p1 = points[1],
p2 = points[2];
var A = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
var sign = A < 0 ? -1 : 1;
var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * px + (p0.x - p2.x) * py) * sign;
var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * px + (p1.x - p0.x) * py) * sign;
return s > 0 && t > 0 && s + t < 2 * A * sign;
}
var lastScoreDisplay = -1;
var lastMultiplierDisplay = -1;
var originalSetInterval = LK.setInterval;
LK.setInterval = function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var timer = originalSetInterval.apply(this, args);
gameTimers.push(timer);
return timer;
};
var originalSetTimeout = LK.setTimeout;
LK.setTimeout = function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
var timer = originalSetTimeout.apply(this, args);
gameTimers.push(timer);
return timer;
};
var gameTimers = [];
var seagullSpawnInterval = 6000; // Spawn every 3 seconds
var lastSeagullSpawn = 0;
function spawnSeagull() {
if (Date.now() - lastSeagullSpawn > seagullSpawnInterval) {
var seagull = new Seagull();
game.addChild(seagull);
lastSeagullSpawn = Date.now();
seagullSpawnInterval = 4000 + Math.random() * 4000; // Random interval between 2-4 seconds
}
}
var rocks = [];
var lastRockTime = 0;
var score = 0; // Initialize score variable
var multiplier = 1;
var lastWaveHeight = 0;
var lastWaveUpdate = 0;
var devicePerformance = 1; // Default performance multiplier
var poseidon = LK.getAsset('poseidon', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(poseidon);
var mouth = LK.getAsset('mouth', {
anchorX: 0.5,
// Center anchor
anchorY: 0.5 // Center anchor
});
game.addChild(mouth);
var waveSystem = game.addChild(new WaveSystem());
var particles = new ParticleSystem();
game.particles = game.addChild(new ParticleSystem());
var surfer = game.addChild(new Surfer());
surfer.setWaveSystem(waveSystem);
var scoreText = new Text2("", {
size: 100,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
LK.gui.top.addChild(scoreText);
var multiplierText = new Text2("", {
size: 60,
fill: 0xFFFFFF,
alpha: 0.8
});
multiplierText.anchor.set(1, 1);
LK.gui.bottomRight.addChild(multiplierText);
// Game update function
var lastUpdateTime = 0;
game.update = function () {
var now = Date.now();
if (now - lastUpdateTime < 16) {
// Don't update faster than 60fps
return;
}
lastUpdateTime = now;
// Every 300 frames, log counts
if (LK.ticks % 600 === 0) {
console.log('Active timers:', gameTimers.length);
}
if (LK.ticks % 300 === 0) {
console.log('Particle count:', game.particles.children.length);
console.log('Wave points:', waveSystem.points.length);
console.log('Total game children:', game.children.length);
console.log('Active tweens:', tween.getActiveTweens ? tween.getActiveTweens().length : 0);
}
waveSystem.update();
surfer.update();
particles.update();
// Update Poseidon's position to follow the player's face
if (facekit.noseTip) {
poseidon.x = facekit.noseTip.x;
poseidon.y = facekit.noseTip.y;
}
// Update mouth position to follow the player's mouth
if (facekit.mouthCenter) {
mouth.x = facekit.mouthCenter.x;
mouth.y = facekit.mouthCenter.y;
}
// Only spawn obstacles if not calibrating
if (!waveSystem.isCalibrating) {
// Rock spawning
if (LK.ticks - lastRockTime > 240 + Math.random() * 360) {
lastRockTime = LK.ticks;
var rock = game.addChild(new Rock());
rock.x = 2548; // Start off right side
rock.y = 2732 + 200; // Place base below screen
// Set the vertical scale independently for random height
rock.children[0].scaleY = 3 + Math.random() * 4; // Random height between 3x and 7x
rocks.push(rock);
}
// Seagull spawning
spawnSeagull();
}
// Update and check rocks
for (var i = rocks.length - 1; i >= 0; i--) {
var rock = rocks[i];
rock.update();
// Remove destroyed rocks
if (!rock.parent) {
rocks.splice(i, 1);
continue;
}
// Check collision with surfer
if (rock.checkCollision(surfer)) {
LK.showGameOver();
}
}
game.children.forEach(function (child) {
if (child instanceof Seagull && child.update) {
child.update();
}
});
var currentScore = Math.floor(score);
if (currentScore !== lastScoreDisplay) {
lastScoreDisplay = currentScore;
scoreText.setText(currentScore.toLocaleString());
}
var currentMultiplier = Math.floor(multiplier * 10) / 10; // Round to 1 decimal
if (currentMultiplier !== lastMultiplierDisplay) {
lastMultiplierDisplay = currentMultiplier;
multiplierText.setText("x" + currentMultiplier.toFixed(1));
}
};
A surfer standing and riding on a surfboard. Side profile. Cartoon. Full body. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A peaked blue rock. Cartoon style.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Poseidon’s face. Cartoon style.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
An opened pair of lips as if singing . Light Skin color. Cell shading vector art style. Facing forward. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A tropical fish. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows