Code edit (1 edits merged)
Please save this source code
User prompt
Add a add a trail when he's dropped and get rid of it when he lands ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'growthFactor')' in or related to this line: 'tween(blueberryGraphics, {' Line Number: 293 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add a throwing effect ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Fix it where blueberry is wide
User prompt
Make it look like it's actually getting thrown with effects ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make it make it so you can throw the blueberry too this is
User prompt
Increase the velocity when the blueberry is falling
User prompt
Make the goldenBerry spawn more often
User prompt
make the player have to collect 100 to win
Code edit (1 edits merged)
Please save this source code
User prompt
Make make it so the the food doesn't go in the red
User prompt
Fix the bug where blueberry would teleport through stuff when you click there
User prompt
Make the game a little easier
User prompt
Stop the bug where blueberry would fall when you're still holding him
User prompt
Make the blueberry roll when he's dropped ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add gravity and ground
Code edit (1 edits merged)
Please save this source code
User prompt
Berry Bounce Adventure
Initial prompt
make a about a living blueberry
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Blueberry = Container.expand(function (characterType) { var self = Container.call(this); self.characterType = characterType || 'classic'; var assetName = 'blueberry_' + self.characterType; var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.baseSize = 80; self.growthFactor = 1.0; self.velocityX = 0; self.velocityY = 0; self.friction = 0.95; self.bounceForce = 1.2; self.lastBounced = false; self.bounceCooldown = 0; // Character-specific properties switch (self.characterType) { case 'red': self.friction = 0.92; // Less friction, more slippery self.bounceForce = 1.4; // Bounces harder break; case 'green': self.friction = 0.98; // More friction, better control break; case 'purple': self.bounceForce = 1.0; // Less bouncy, more stable break; case 'gold': self.friction = 0.90; // Very slippery self.bounceForce = 1.6; // Very bouncy break; default: // classic break; } self.grow = function () { self.growthFactor += 0.03; graphics.scaleX = self.growthFactor; graphics.scaleY = self.growthFactor; }; self.getBounds = function () { var radius = self.baseSize * self.growthFactor / 2; return { left: self.x - radius, right: self.x + radius, top: self.y - radius, bottom: self.y + radius, radius: radius }; }; self.update = function () { // Apply gravity only when not being dragged if (dragNode !== self) { self.velocityY += gravity; } self.x += self.velocityX; self.y += self.velocityY; self.velocityX *= self.friction; self.velocityY *= self.friction; // Continuous rolling based on horizontal movement if (Math.abs(self.velocityX) > 0.05) { var rollDirection = self.velocityX > 0 ? 1 : -1; graphics.rotation += rollDirection * Math.abs(self.velocityX) * 0.02; } var bounds = self.getBounds(); var bounced = false; if (bounds.left <= 0) { self.x = bounds.radius; self.velocityX = Math.abs(self.velocityX) * self.bounceForce; bounced = true; // Add rolling animation when bouncing off left wall if (Math.abs(self.velocityX) > 0.1) { var rollSpeed = Math.abs(self.velocityX) * 0.1; tween(graphics, { rotation: graphics.rotation + Math.PI * 2 }, { duration: 1000 / rollSpeed, easing: tween.linear }); } } if (bounds.right >= 2048) { self.x = 2048 - bounds.radius; self.velocityX = -Math.abs(self.velocityX) * self.bounceForce; bounced = true; // Add rolling animation when bouncing off right wall if (Math.abs(self.velocityX) > 0.1) { var rollSpeed = Math.abs(self.velocityX) * 0.2; tween(graphics, { rotation: graphics.rotation - Math.PI * 2 }, { duration: 1000 / rollSpeed, easing: tween.linear }); } } if (bounds.top <= 0) { self.y = bounds.radius; self.velocityY = Math.abs(self.velocityY) * self.bounceForce; bounced = true; } // Ground collision instead of bottom screen edge if (bounds.bottom >= groundLevel) { self.y = groundLevel - bounds.radius; self.velocityY = -Math.abs(self.velocityY) * self.bounceForce; bounced = true; // Create dust particles when hitting ground with significant velocity if (Math.abs(self.velocityY) > 3 || Math.abs(self.velocityX) > 3) { for (var d = 0; d < 5; d++) { createDustParticle(self.x, groundLevel); } } // Stop trailing when landing if (isTrailing && Math.abs(self.velocityY) < 2 && Math.abs(self.velocityX) < 2) { isTrailing = false; } // Add rolling animation when hitting ground if (Math.abs(self.velocityX) > 0.1) { var rollDirection = self.velocityX > 0 ? 1 : -1; var rollSpeed = Math.abs(self.velocityX) * 0.1; tween(graphics, { rotation: graphics.rotation + rollDirection * Math.PI * 2 }, { duration: 1000 / rollSpeed, easing: tween.linear }); } } // Create trail particles when moving fast if (isTrailing && (Math.abs(self.velocityX) > 1 || Math.abs(self.velocityY) > 1)) { if (LK.ticks % 3 === 0) { // Create particle every 3 frames createTrailParticle(self.x, self.y); } } // Update bounce cooldown if (self.bounceCooldown > 0) { self.bounceCooldown--; } // Only play bounce sound when transitioning from not bounced to bounced // and cooldown has expired and there's significant velocity if (bounced && !self.lastBounced && self.bounceCooldown === 0 && (Math.abs(self.velocityX) > 2 || Math.abs(self.velocityY) > 2)) { LK.getSound('bounce').play(); self.bounceCooldown = 1; // 1 frames cooldown (~0.33 seconds at 60fps) } self.lastBounced = bounced; }; return self; }); var GoldenBerry = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('goldenBerry', { anchorX: 0.5, anchorY: 0.5 }); self.floatOffset = Math.random() * Math.PI * 2; self.floatSpeed = 0.05; self.startY = 0; self.update = function () { self.y = self.startY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 10; }; return self; }); var RedThorn = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('redThorn', { anchorX: 0.5, anchorY: 0.5 }); graphics.rotation = Math.PI / 1; self.pulseOffset = Math.random() * Math.PI * 2; self.pulseSpeed = 0.08; self.update = function () { var pulse = 1 + Math.sin(LK.ticks * self.pulseSpeed + self.pulseOffset) * 0.1; graphics.scaleX = pulse; graphics.scaleY = pulse; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x9edbf3 }); /**** * Game Code ****/ // Game state management var gameState = 'menu'; // 'menu', 'characterSelect', 'playing' var selectedCharacter = 'classic'; var menuContainer = game.addChild(new Container()); var characterSelectContainer = game.addChild(new Container()); characterSelectContainer.visible = false; // Main menu setup var titleText = new Text2('Berry Bounce Adventure!', { size: 120, fill: 0x4169E1 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 600; menuContainer.addChild(titleText); var instructionText = new Text2('Help the hungry berry\neat grapes!\n\nDrag and throw him around\nto collect food and avoid\nthe red thorns!', { size: 80, fill: 0x333333 }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 1000; menuContainer.addChild(instructionText); var startText = new Text2('TAP TO START', { size: 100, fill: 0xFF6B35 }); startText.anchor.set(0.5, 0.5); startText.x = 1024; startText.y = 1600; menuContainer.addChild(startText); // Blueberry setup (initially hidden) var blueberry = game.addChild(new Blueberry(selectedCharacter)); blueberry.x = 1024; blueberry.y = 1366; blueberry.visible = false; var goldenBerries = []; var redThorns = []; var dragNode = null; var lastMouseX = 0; var lastMouseY = 0; var thornSpawnTimer = 0; var berrySpawnTimer = 0; var gravity = 0.8; var groundLevel = 2632; // 100px from bottom of screen var trailParticles = []; var isTrailing = false; function createTrailParticle(x, y) { var particle = LK.getAsset('trailParticle', { width: 20, height: 20, color: 0x87CEEB, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, alpha: 0.8 }); particle.x = x; particle.y = y; particle.life = 30; // frames to live particle.maxLife = 30; trailParticles.push(particle); game.addChild(particle); } var dustParticles = []; function createDustParticle(x, y) { var particle = LK.getAsset('dustParticle', { width: 8, height: 8, color: 0xD2B48C, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, scaleY: 0.2, alpha: 0.6 }); particle.x = x + (Math.random() - 0.5) * 60; particle.y = y; particle.velocityX = (Math.random() - 0.5) * 4; particle.velocityY = -(Math.random() * 3 + 1); particle.life = 40; particle.maxLife = 40; dustParticles.push(particle); game.addChild(particle); } // Menu blueberry for decoration var menuBlueberry = menuContainer.addChild(new Blueberry()); menuBlueberry.x = 1024; menuBlueberry.y = 1300; // Character selection setup var characterSelectTitle = new Text2('SELECT CHARACTER', { size: 100, fill: 0x4169E1 }); characterSelectTitle.anchor.set(0.5, 0.5); characterSelectTitle.x = 1024; characterSelectTitle.y = 400; characterSelectContainer.addChild(characterSelectTitle); var characterTypes = ['classic', 'red', 'green', 'purple', 'gold']; var characterNames = ['Classic', 'Speedy', 'Steady', 'Stable', 'Bouncy']; var characterDescriptions = ['Balanced stats', 'Fast & bouncy', 'Great control', 'Very stable', 'Super bouncy']; var characterPreviewContainers = []; // Set initial character name for (var c = 0; c < characterTypes.length; c++) { var charContainer = characterSelectContainer.addChild(new Container()); var charPreview = charContainer.addChild(new Blueberry(characterTypes[c])); charPreview.x = 200 + c * 350; charPreview.y = 800; charPreview.characterIndex = c; var charName = new Text2(characterNames[c], { size: 60, fill: 0x333333 }); charName.anchor.set(0.5, 0.5); charName.x = charPreview.x; charName.y = charPreview.y + 120; charContainer.addChild(charName); var charDesc = new Text2(characterDescriptions[c], { size: 40, fill: 0x666666 }); charDesc.anchor.set(0.5, 0.5); charDesc.x = charPreview.x; charDesc.y = charPreview.y + 180; charContainer.addChild(charDesc); characterPreviewContainers.push(charContainer); // Add selection indicator var selectionRing = LK.getAsset('selectionRing', { width: 120, height: 120, color: 0xFFD700, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, alpha: 0.3 }); selectionRing.x = charPreview.x; selectionRing.y = charPreview.y; selectionRing.visible = c === 0; // Show for first character initially charContainer.addChild(selectionRing); charContainer.selectionRing = selectionRing; // Animate character previews tween(charPreview, { y: charPreview.y - 20 }, { duration: 1500 + c * 200, easing: tween.easeInOut, repeat: -1, yoyo: true }); } var selectText = new Text2('TAP CHARACTER TO SELECT', { size: 80, fill: 0xFF6B35 }); selectText.anchor.set(0.5, 0.5); selectText.x = 1024; selectText.y = 1200; characterSelectContainer.addChild(selectText); var playText = new Text2('TAP TO PLAY', { size: 100, fill: 0x32CD32 }); playText.anchor.set(0.5, 0.5); playText.x = 1024; playText.y = 1500; characterSelectContainer.addChild(playText); // Animate play text pulsing tween(playText, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, repeat: -1, yoyo: true }); // Animate menu blueberry tween(menuBlueberry, { y: 1250 }, { duration: 2000, easing: tween.easeInOut, repeat: -1, yoyo: true }); // Animate start text pulsing tween(startText, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, repeat: -1, yoyo: true }); var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.visible = false; // Initially hidden // Character name display var characterNameTxt = new Text2('Classic Barry', { size: 80, fill: 0xFFD700 }); characterNameTxt.anchor.set(0.5, 0); characterNameTxt.x = 0; characterNameTxt.y = 150; LK.gui.top.addChild(characterNameTxt); characterNameTxt.visible = false; // Initially hidden function spawnGoldenBerry() { var berry = new GoldenBerry(); var margin = 50; berry.x = margin + Math.random() * (2048 - margin * 2); berry.y = margin + Math.random() * (groundLevel - margin * 2); berry.startY = berry.y; var tooClose = true; var attempts = 0; while (tooClose && attempts < 20) { tooClose = false; var berryBounds = { x: berry.x, y: berry.y, radius: 20 }; var blueberryBounds = blueberry.getBounds(); var dist = Math.sqrt(Math.pow(berry.x - blueberry.x, 2) + Math.pow(berry.y - blueberry.y, 2)); if (dist < blueberryBounds.radius + 100) { berry.x = margin + Math.random() * (2048 - margin * 2); berry.y = margin + Math.random() * (groundLevel - margin * 2); berry.startY = berry.y; tooClose = true; } // Check distance from all red thorns for (var k = 0; k < redThorns.length; k++) { var thornDist = Math.sqrt(Math.pow(berry.x - redThorns[k].x, 2) + Math.pow(berry.y - redThorns[k].y, 2)); if (thornDist < 120) { berry.x = margin + Math.random() * (2048 - margin * 2); berry.y = margin + Math.random() * (groundLevel - margin * 2); berry.startY = berry.y; tooClose = true; break; } // Update trail particles for (var t = trailParticles.length - 1; t >= 0; t--) { var particle = trailParticles[t]; particle.life--; // Fade out particle particle.alpha = particle.life / particle.maxLife * 0.2; particle.scaleX = particle.life / particle.maxLife * 0.1; particle.scaleY = particle.life / particle.maxLife * 0.1; // Remove dead particles if (particle.life <= 0) { particle.destroy(); trailParticles.splice(t, 1); } } } ; attempts++; } goldenBerries.push(berry); game.addChild(berry); } function spawnRedThorn() { var thorn = new RedThorn(); var margin = 50; thorn.x = margin + Math.random() * (2048 - margin * 2); thorn.y = margin + Math.random() * (groundLevel - margin * 2); var tooClose = true; var attempts = 0; while (tooClose && attempts < 15) { tooClose = false; var blueberryBounds = blueberry.getBounds(); var dist = Math.sqrt(Math.pow(thorn.x - blueberry.x, 2) + Math.pow(thorn.y - blueberry.y, 2)); if (dist < blueberryBounds.radius + 200) { thorn.x = margin + Math.random() * (2048 - margin * 2); thorn.y = margin + Math.random() * (groundLevel - margin * 2); tooClose = true; } attempts++; } redThorns.push(thorn); game.addChild(thorn); } function handleMove(x, y, obj) { if (dragNode) { var deltaX = x - lastMouseX; var deltaY = y - lastMouseY; // Instead of teleporting, smoothly move towards target position var targetX = x; var targetY = y; var currentX = dragNode.x; var currentY = dragNode.y; // Smooth interpolation towards target position var lerpFactor = 0.15; dragNode.x = currentX + (targetX - currentX) * lerpFactor; dragNode.y = currentY + (targetY - currentY) * lerpFactor; dragNode.velocityX = deltaX * 0.3; dragNode.velocityY = deltaY * 0.3; } lastMouseX = x; lastMouseY = y; } game.move = handleMove; game.down = function (x, y, obj) { if (gameState === 'menu') { // Transition to character select gameState = 'characterSelect'; menuContainer.visible = false; // Destroy menu blueberry to prevent invisible bouncing menuBlueberry.destroy(); characterSelectContainer.visible = true; return; } else if (gameState === 'characterSelect') { // Check if tapped on a character for (var c = 0; c < characterPreviewContainers.length; c++) { var charContainer = characterPreviewContainers[c]; var charPreview = charContainer.children[0]; var dist = Math.sqrt(Math.pow(x - charPreview.x, 2) + Math.pow(y - charPreview.y, 2)); if (dist < 80) { // Selected a character selectedCharacter = characterTypes[c]; // Update selection rings for (var r = 0; r < characterPreviewContainers.length; r++) { characterPreviewContainers[r].selectionRing.visible = r === c; } // Update character name display var selectedCharacterName = characterNames[c] + ' Barry'; characterNameTxt.setText(selectedCharacterName); return; } } // Check if tapped play button area if (y > 1400 && y < 1600) { startGame(); return; } } dragNode = blueberry; lastMouseX = x; lastMouseY = y; handleMove(x, y, obj); }; game.up = function (x, y, obj) { if (dragNode) { // Calculate throw velocity based on drag distance and speed var throwMultiplier = 0.5; var maxThrowSpeed = 15; // Use the current velocity from dragging as throw velocity var throwVelX = Math.max(-maxThrowSpeed, Math.min(maxThrowSpeed, dragNode.velocityX * throwMultiplier)); var throwVelY = Math.max(-maxThrowSpeed, Math.min(maxThrowSpeed, dragNode.velocityY * throwMultiplier)); // Apply the throw velocity to the blueberry dragNode.velocityX = throwVelX; dragNode.velocityY = throwVelY; // Start trailing effect if there's significant velocity if (Math.abs(throwVelX) > 2 || Math.abs(throwVelY) > 2) { isTrailing = true; } // Add throwing effect - scale bounce animation var blueberryGraphics = dragNode.children[0]; // Get the graphics child var currentGrowthFactor = dragNode.growthFactor; // Store growth factor before clearing dragNode if (blueberryGraphics) { // Stop any existing scale tweens tween.stop(blueberryGraphics, { scaleX: true, scaleY: true }); // Create throwing effect with scale bounce tween(blueberryGraphics, { scaleX: currentGrowthFactor * 1.3, scaleY: currentGrowthFactor * 1.3 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(blueberryGraphics, { scaleX: currentGrowthFactor, scaleY: currentGrowthFactor }, { duration: 200, easing: tween.easeInOut }); } }); } } dragNode = null; }; // Create ground visual (initially hidden) var ground = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0 })); ground.x = 0; ground.y = groundLevel; ground.visible = false; // Game elements start hidden function startGame() { gameState = 'playing'; menuContainer.visible = false; characterSelectContainer.visible = false; // Destroy all character selection blueberries to prevent invisible bouncing for (var c = 0; c < characterPreviewContainers.length; c++) { var charPreview = characterPreviewContainers[c].children[0]; if (charPreview && charPreview.destroy) { charPreview.destroy(); } } // Recreate blueberry with selected character blueberry.destroy(); blueberry = game.addChild(new Blueberry(selectedCharacter)); blueberry.x = 1024; blueberry.y = 1366; blueberry.visible = true; ground.visible = true; scoreTxt.visible = true; characterNameTxt.visible = true; // Spawn initial berries spawnGoldenBerry(); spawnGoldenBerry(); spawnGoldenBerry(); } game.update = function () { // Only update game logic when playing if (gameState !== 'playing') { return; } berrySpawnTimer++; thornSpawnTimer++; if (berrySpawnTimer > 150 && goldenBerries.length < 5) { spawnGoldenBerry(); berrySpawnTimer = 0; } var thornSpawnRate = Math.max(360 - Math.floor(LK.getScore() * 3), 120); if (thornSpawnTimer > thornSpawnRate) { spawnRedThorn(); thornSpawnTimer = 0; } var blueberryBounds = blueberry.getBounds(); for (var i = goldenBerries.length - 1; i >= 0; i--) { var berry = goldenBerries[i]; var dist = Math.sqrt(Math.pow(berry.x - blueberry.x, 2) + Math.pow(berry.y - blueberry.y, 2)); if (dist < blueberryBounds.radius + 20) { LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); blueberry.grow(); LK.getSound('collect').play(); LK.effects.flashObject(berry, 0xFFFFFF, 200); berry.destroy(); goldenBerries.splice(i, 1); if (LK.getScore() >= 100) { LK.showYouWin(); } } } for (var j = 0; j < redThorns.length; j++) { var thorn = redThorns[j]; var dist = Math.sqrt(Math.pow(thorn.x - blueberry.x, 2) + Math.pow(thorn.y - blueberry.y, 2)); if (dist < blueberryBounds.radius + 20) { LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); } } // Update dust particles for (var d = dustParticles.length - 1; d >= 0; d--) { var dustParticle = dustParticles[d]; dustParticle.x += dustParticle.velocityX; dustParticle.y += dustParticle.velocityY; dustParticle.velocityX *= 0.98; dustParticle.velocityY += 0.1; // gravity dustParticle.life--; // Fade out dust particle dustParticle.alpha = dustParticle.life / dustParticle.maxLife * 0.6; dustParticle.scaleX = dustParticle.life / dustParticle.maxLife * 0.2; dustParticle.scaleY = dustParticle.life / dustParticle.maxLife * 0.2; // Remove dead dust particles if (dustParticle.life <= 0) { dustParticle.destroy(); dustParticles.splice(d, 1); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Blueberry = Container.expand(function (characterType) {
var self = Container.call(this);
self.characterType = characterType || 'classic';
var assetName = 'blueberry_' + self.characterType;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSize = 80;
self.growthFactor = 1.0;
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.95;
self.bounceForce = 1.2;
self.lastBounced = false;
self.bounceCooldown = 0;
// Character-specific properties
switch (self.characterType) {
case 'red':
self.friction = 0.92; // Less friction, more slippery
self.bounceForce = 1.4; // Bounces harder
break;
case 'green':
self.friction = 0.98; // More friction, better control
break;
case 'purple':
self.bounceForce = 1.0; // Less bouncy, more stable
break;
case 'gold':
self.friction = 0.90; // Very slippery
self.bounceForce = 1.6; // Very bouncy
break;
default:
// classic
break;
}
self.grow = function () {
self.growthFactor += 0.03;
graphics.scaleX = self.growthFactor;
graphics.scaleY = self.growthFactor;
};
self.getBounds = function () {
var radius = self.baseSize * self.growthFactor / 2;
return {
left: self.x - radius,
right: self.x + radius,
top: self.y - radius,
bottom: self.y + radius,
radius: radius
};
};
self.update = function () {
// Apply gravity only when not being dragged
if (dragNode !== self) {
self.velocityY += gravity;
}
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Continuous rolling based on horizontal movement
if (Math.abs(self.velocityX) > 0.05) {
var rollDirection = self.velocityX > 0 ? 1 : -1;
graphics.rotation += rollDirection * Math.abs(self.velocityX) * 0.02;
}
var bounds = self.getBounds();
var bounced = false;
if (bounds.left <= 0) {
self.x = bounds.radius;
self.velocityX = Math.abs(self.velocityX) * self.bounceForce;
bounced = true;
// Add rolling animation when bouncing off left wall
if (Math.abs(self.velocityX) > 0.1) {
var rollSpeed = Math.abs(self.velocityX) * 0.1;
tween(graphics, {
rotation: graphics.rotation + Math.PI * 2
}, {
duration: 1000 / rollSpeed,
easing: tween.linear
});
}
}
if (bounds.right >= 2048) {
self.x = 2048 - bounds.radius;
self.velocityX = -Math.abs(self.velocityX) * self.bounceForce;
bounced = true;
// Add rolling animation when bouncing off right wall
if (Math.abs(self.velocityX) > 0.1) {
var rollSpeed = Math.abs(self.velocityX) * 0.2;
tween(graphics, {
rotation: graphics.rotation - Math.PI * 2
}, {
duration: 1000 / rollSpeed,
easing: tween.linear
});
}
}
if (bounds.top <= 0) {
self.y = bounds.radius;
self.velocityY = Math.abs(self.velocityY) * self.bounceForce;
bounced = true;
}
// Ground collision instead of bottom screen edge
if (bounds.bottom >= groundLevel) {
self.y = groundLevel - bounds.radius;
self.velocityY = -Math.abs(self.velocityY) * self.bounceForce;
bounced = true;
// Create dust particles when hitting ground with significant velocity
if (Math.abs(self.velocityY) > 3 || Math.abs(self.velocityX) > 3) {
for (var d = 0; d < 5; d++) {
createDustParticle(self.x, groundLevel);
}
}
// Stop trailing when landing
if (isTrailing && Math.abs(self.velocityY) < 2 && Math.abs(self.velocityX) < 2) {
isTrailing = false;
}
// Add rolling animation when hitting ground
if (Math.abs(self.velocityX) > 0.1) {
var rollDirection = self.velocityX > 0 ? 1 : -1;
var rollSpeed = Math.abs(self.velocityX) * 0.1;
tween(graphics, {
rotation: graphics.rotation + rollDirection * Math.PI * 2
}, {
duration: 1000 / rollSpeed,
easing: tween.linear
});
}
}
// Create trail particles when moving fast
if (isTrailing && (Math.abs(self.velocityX) > 1 || Math.abs(self.velocityY) > 1)) {
if (LK.ticks % 3 === 0) {
// Create particle every 3 frames
createTrailParticle(self.x, self.y);
}
}
// Update bounce cooldown
if (self.bounceCooldown > 0) {
self.bounceCooldown--;
}
// Only play bounce sound when transitioning from not bounced to bounced
// and cooldown has expired and there's significant velocity
if (bounced && !self.lastBounced && self.bounceCooldown === 0 && (Math.abs(self.velocityX) > 2 || Math.abs(self.velocityY) > 2)) {
LK.getSound('bounce').play();
self.bounceCooldown = 1; // 1 frames cooldown (~0.33 seconds at 60fps)
}
self.lastBounced = bounced;
};
return self;
});
var GoldenBerry = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('goldenBerry', {
anchorX: 0.5,
anchorY: 0.5
});
self.floatOffset = Math.random() * Math.PI * 2;
self.floatSpeed = 0.05;
self.startY = 0;
self.update = function () {
self.y = self.startY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 10;
};
return self;
});
var RedThorn = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('redThorn', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.rotation = Math.PI / 1;
self.pulseOffset = Math.random() * Math.PI * 2;
self.pulseSpeed = 0.08;
self.update = function () {
var pulse = 1 + Math.sin(LK.ticks * self.pulseSpeed + self.pulseOffset) * 0.1;
graphics.scaleX = pulse;
graphics.scaleY = pulse;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x9edbf3
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'characterSelect', 'playing'
var selectedCharacter = 'classic';
var menuContainer = game.addChild(new Container());
var characterSelectContainer = game.addChild(new Container());
characterSelectContainer.visible = false;
// Main menu setup
var titleText = new Text2('Berry Bounce Adventure!', {
size: 120,
fill: 0x4169E1
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
menuContainer.addChild(titleText);
var instructionText = new Text2('Help the hungry berry\neat grapes!\n\nDrag and throw him around\nto collect food and avoid\nthe red thorns!', {
size: 80,
fill: 0x333333
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1000;
menuContainer.addChild(instructionText);
var startText = new Text2('TAP TO START', {
size: 100,
fill: 0xFF6B35
});
startText.anchor.set(0.5, 0.5);
startText.x = 1024;
startText.y = 1600;
menuContainer.addChild(startText);
// Blueberry setup (initially hidden)
var blueberry = game.addChild(new Blueberry(selectedCharacter));
blueberry.x = 1024;
blueberry.y = 1366;
blueberry.visible = false;
var goldenBerries = [];
var redThorns = [];
var dragNode = null;
var lastMouseX = 0;
var lastMouseY = 0;
var thornSpawnTimer = 0;
var berrySpawnTimer = 0;
var gravity = 0.8;
var groundLevel = 2632; // 100px from bottom of screen
var trailParticles = [];
var isTrailing = false;
function createTrailParticle(x, y) {
var particle = LK.getAsset('trailParticle', {
width: 20,
height: 20,
color: 0x87CEEB,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
particle.x = x;
particle.y = y;
particle.life = 30; // frames to live
particle.maxLife = 30;
trailParticles.push(particle);
game.addChild(particle);
}
var dustParticles = [];
function createDustParticle(x, y) {
var particle = LK.getAsset('dustParticle', {
width: 8,
height: 8,
color: 0xD2B48C,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.6
});
particle.x = x + (Math.random() - 0.5) * 60;
particle.y = y;
particle.velocityX = (Math.random() - 0.5) * 4;
particle.velocityY = -(Math.random() * 3 + 1);
particle.life = 40;
particle.maxLife = 40;
dustParticles.push(particle);
game.addChild(particle);
}
// Menu blueberry for decoration
var menuBlueberry = menuContainer.addChild(new Blueberry());
menuBlueberry.x = 1024;
menuBlueberry.y = 1300;
// Character selection setup
var characterSelectTitle = new Text2('SELECT CHARACTER', {
size: 100,
fill: 0x4169E1
});
characterSelectTitle.anchor.set(0.5, 0.5);
characterSelectTitle.x = 1024;
characterSelectTitle.y = 400;
characterSelectContainer.addChild(characterSelectTitle);
var characterTypes = ['classic', 'red', 'green', 'purple', 'gold'];
var characterNames = ['Classic', 'Speedy', 'Steady', 'Stable', 'Bouncy'];
var characterDescriptions = ['Balanced stats', 'Fast & bouncy', 'Great control', 'Very stable', 'Super bouncy'];
var characterPreviewContainers = [];
// Set initial character name
for (var c = 0; c < characterTypes.length; c++) {
var charContainer = characterSelectContainer.addChild(new Container());
var charPreview = charContainer.addChild(new Blueberry(characterTypes[c]));
charPreview.x = 200 + c * 350;
charPreview.y = 800;
charPreview.characterIndex = c;
var charName = new Text2(characterNames[c], {
size: 60,
fill: 0x333333
});
charName.anchor.set(0.5, 0.5);
charName.x = charPreview.x;
charName.y = charPreview.y + 120;
charContainer.addChild(charName);
var charDesc = new Text2(characterDescriptions[c], {
size: 40,
fill: 0x666666
});
charDesc.anchor.set(0.5, 0.5);
charDesc.x = charPreview.x;
charDesc.y = charPreview.y + 180;
charContainer.addChild(charDesc);
characterPreviewContainers.push(charContainer);
// Add selection indicator
var selectionRing = LK.getAsset('selectionRing', {
width: 120,
height: 120,
color: 0xFFD700,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.3
});
selectionRing.x = charPreview.x;
selectionRing.y = charPreview.y;
selectionRing.visible = c === 0; // Show for first character initially
charContainer.addChild(selectionRing);
charContainer.selectionRing = selectionRing;
// Animate character previews
tween(charPreview, {
y: charPreview.y - 20
}, {
duration: 1500 + c * 200,
easing: tween.easeInOut,
repeat: -1,
yoyo: true
});
}
var selectText = new Text2('TAP CHARACTER TO SELECT', {
size: 80,
fill: 0xFF6B35
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 1200;
characterSelectContainer.addChild(selectText);
var playText = new Text2('TAP TO PLAY', {
size: 100,
fill: 0x32CD32
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1500;
characterSelectContainer.addChild(playText);
// Animate play text pulsing
tween(playText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
repeat: -1,
yoyo: true
});
// Animate menu blueberry
tween(menuBlueberry, {
y: 1250
}, {
duration: 2000,
easing: tween.easeInOut,
repeat: -1,
yoyo: true
});
// Animate start text pulsing
tween(startText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
repeat: -1,
yoyo: true
});
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.visible = false; // Initially hidden
// Character name display
var characterNameTxt = new Text2('Classic Barry', {
size: 80,
fill: 0xFFD700
});
characterNameTxt.anchor.set(0.5, 0);
characterNameTxt.x = 0;
characterNameTxt.y = 150;
LK.gui.top.addChild(characterNameTxt);
characterNameTxt.visible = false; // Initially hidden
function spawnGoldenBerry() {
var berry = new GoldenBerry();
var margin = 50;
berry.x = margin + Math.random() * (2048 - margin * 2);
berry.y = margin + Math.random() * (groundLevel - margin * 2);
berry.startY = berry.y;
var tooClose = true;
var attempts = 0;
while (tooClose && attempts < 20) {
tooClose = false;
var berryBounds = {
x: berry.x,
y: berry.y,
radius: 20
};
var blueberryBounds = blueberry.getBounds();
var dist = Math.sqrt(Math.pow(berry.x - blueberry.x, 2) + Math.pow(berry.y - blueberry.y, 2));
if (dist < blueberryBounds.radius + 100) {
berry.x = margin + Math.random() * (2048 - margin * 2);
berry.y = margin + Math.random() * (groundLevel - margin * 2);
berry.startY = berry.y;
tooClose = true;
}
// Check distance from all red thorns
for (var k = 0; k < redThorns.length; k++) {
var thornDist = Math.sqrt(Math.pow(berry.x - redThorns[k].x, 2) + Math.pow(berry.y - redThorns[k].y, 2));
if (thornDist < 120) {
berry.x = margin + Math.random() * (2048 - margin * 2);
berry.y = margin + Math.random() * (groundLevel - margin * 2);
berry.startY = berry.y;
tooClose = true;
break;
}
// Update trail particles
for (var t = trailParticles.length - 1; t >= 0; t--) {
var particle = trailParticles[t];
particle.life--;
// Fade out particle
particle.alpha = particle.life / particle.maxLife * 0.2;
particle.scaleX = particle.life / particle.maxLife * 0.1;
particle.scaleY = particle.life / particle.maxLife * 0.1;
// Remove dead particles
if (particle.life <= 0) {
particle.destroy();
trailParticles.splice(t, 1);
}
}
}
;
attempts++;
}
goldenBerries.push(berry);
game.addChild(berry);
}
function spawnRedThorn() {
var thorn = new RedThorn();
var margin = 50;
thorn.x = margin + Math.random() * (2048 - margin * 2);
thorn.y = margin + Math.random() * (groundLevel - margin * 2);
var tooClose = true;
var attempts = 0;
while (tooClose && attempts < 15) {
tooClose = false;
var blueberryBounds = blueberry.getBounds();
var dist = Math.sqrt(Math.pow(thorn.x - blueberry.x, 2) + Math.pow(thorn.y - blueberry.y, 2));
if (dist < blueberryBounds.radius + 200) {
thorn.x = margin + Math.random() * (2048 - margin * 2);
thorn.y = margin + Math.random() * (groundLevel - margin * 2);
tooClose = true;
}
attempts++;
}
redThorns.push(thorn);
game.addChild(thorn);
}
function handleMove(x, y, obj) {
if (dragNode) {
var deltaX = x - lastMouseX;
var deltaY = y - lastMouseY;
// Instead of teleporting, smoothly move towards target position
var targetX = x;
var targetY = y;
var currentX = dragNode.x;
var currentY = dragNode.y;
// Smooth interpolation towards target position
var lerpFactor = 0.15;
dragNode.x = currentX + (targetX - currentX) * lerpFactor;
dragNode.y = currentY + (targetY - currentY) * lerpFactor;
dragNode.velocityX = deltaX * 0.3;
dragNode.velocityY = deltaY * 0.3;
}
lastMouseX = x;
lastMouseY = y;
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Transition to character select
gameState = 'characterSelect';
menuContainer.visible = false;
// Destroy menu blueberry to prevent invisible bouncing
menuBlueberry.destroy();
characterSelectContainer.visible = true;
return;
} else if (gameState === 'characterSelect') {
// Check if tapped on a character
for (var c = 0; c < characterPreviewContainers.length; c++) {
var charContainer = characterPreviewContainers[c];
var charPreview = charContainer.children[0];
var dist = Math.sqrt(Math.pow(x - charPreview.x, 2) + Math.pow(y - charPreview.y, 2));
if (dist < 80) {
// Selected a character
selectedCharacter = characterTypes[c];
// Update selection rings
for (var r = 0; r < characterPreviewContainers.length; r++) {
characterPreviewContainers[r].selectionRing.visible = r === c;
}
// Update character name display
var selectedCharacterName = characterNames[c] + ' Barry';
characterNameTxt.setText(selectedCharacterName);
return;
}
}
// Check if tapped play button area
if (y > 1400 && y < 1600) {
startGame();
return;
}
}
dragNode = blueberry;
lastMouseX = x;
lastMouseY = y;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
if (dragNode) {
// Calculate throw velocity based on drag distance and speed
var throwMultiplier = 0.5;
var maxThrowSpeed = 15;
// Use the current velocity from dragging as throw velocity
var throwVelX = Math.max(-maxThrowSpeed, Math.min(maxThrowSpeed, dragNode.velocityX * throwMultiplier));
var throwVelY = Math.max(-maxThrowSpeed, Math.min(maxThrowSpeed, dragNode.velocityY * throwMultiplier));
// Apply the throw velocity to the blueberry
dragNode.velocityX = throwVelX;
dragNode.velocityY = throwVelY;
// Start trailing effect if there's significant velocity
if (Math.abs(throwVelX) > 2 || Math.abs(throwVelY) > 2) {
isTrailing = true;
}
// Add throwing effect - scale bounce animation
var blueberryGraphics = dragNode.children[0]; // Get the graphics child
var currentGrowthFactor = dragNode.growthFactor; // Store growth factor before clearing dragNode
if (blueberryGraphics) {
// Stop any existing scale tweens
tween.stop(blueberryGraphics, {
scaleX: true,
scaleY: true
});
// Create throwing effect with scale bounce
tween(blueberryGraphics, {
scaleX: currentGrowthFactor * 1.3,
scaleY: currentGrowthFactor * 1.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(blueberryGraphics, {
scaleX: currentGrowthFactor,
scaleY: currentGrowthFactor
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
}
dragNode = null;
};
// Create ground visual (initially hidden)
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = groundLevel;
ground.visible = false;
// Game elements start hidden
function startGame() {
gameState = 'playing';
menuContainer.visible = false;
characterSelectContainer.visible = false;
// Destroy all character selection blueberries to prevent invisible bouncing
for (var c = 0; c < characterPreviewContainers.length; c++) {
var charPreview = characterPreviewContainers[c].children[0];
if (charPreview && charPreview.destroy) {
charPreview.destroy();
}
}
// Recreate blueberry with selected character
blueberry.destroy();
blueberry = game.addChild(new Blueberry(selectedCharacter));
blueberry.x = 1024;
blueberry.y = 1366;
blueberry.visible = true;
ground.visible = true;
scoreTxt.visible = true;
characterNameTxt.visible = true;
// Spawn initial berries
spawnGoldenBerry();
spawnGoldenBerry();
spawnGoldenBerry();
}
game.update = function () {
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
berrySpawnTimer++;
thornSpawnTimer++;
if (berrySpawnTimer > 150 && goldenBerries.length < 5) {
spawnGoldenBerry();
berrySpawnTimer = 0;
}
var thornSpawnRate = Math.max(360 - Math.floor(LK.getScore() * 3), 120);
if (thornSpawnTimer > thornSpawnRate) {
spawnRedThorn();
thornSpawnTimer = 0;
}
var blueberryBounds = blueberry.getBounds();
for (var i = goldenBerries.length - 1; i >= 0; i--) {
var berry = goldenBerries[i];
var dist = Math.sqrt(Math.pow(berry.x - blueberry.x, 2) + Math.pow(berry.y - blueberry.y, 2));
if (dist < blueberryBounds.radius + 20) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
blueberry.grow();
LK.getSound('collect').play();
LK.effects.flashObject(berry, 0xFFFFFF, 200);
berry.destroy();
goldenBerries.splice(i, 1);
if (LK.getScore() >= 100) {
LK.showYouWin();
}
}
}
for (var j = 0; j < redThorns.length; j++) {
var thorn = redThorns[j];
var dist = Math.sqrt(Math.pow(thorn.x - blueberry.x, 2) + Math.pow(thorn.y - blueberry.y, 2));
if (dist < blueberryBounds.radius + 20) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Update dust particles
for (var d = dustParticles.length - 1; d >= 0; d--) {
var dustParticle = dustParticles[d];
dustParticle.x += dustParticle.velocityX;
dustParticle.y += dustParticle.velocityY;
dustParticle.velocityX *= 0.98;
dustParticle.velocityY += 0.1; // gravity
dustParticle.life--;
// Fade out dust particle
dustParticle.alpha = dustParticle.life / dustParticle.maxLife * 0.6;
dustParticle.scaleX = dustParticle.life / dustParticle.maxLife * 0.2;
dustParticle.scaleY = dustParticle.life / dustParticle.maxLife * 0.2;
// Remove dead dust particles
if (dustParticle.life <= 0) {
dustParticle.destroy();
dustParticles.splice(d, 1);
}
}
};