User prompt
Fix bird swap
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'baseTexture')' in or related to this line: 'if (currentBird.texture.baseTexture.imageUrl && currentBird.texture.baseTexture.imageUrl.includes('6882f89c95da69b8b62e4c36')) {' Line Number: 575
User prompt
Add bird swapper button, icon is 🐦
User prompt
New power up asset
User prompt
Add power up that freezes red pipes that move and slows down time for 30 seconds
User prompt
Make reset score button, icon is spinning Ø ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make fire pipes move left and right no fire instead of shoot. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make fire pipes move instead of shoot. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make fire pipes only have 1 fire pipe, and no other pipes. Also add chance for regular pipes to also have only 1 pipe
User prompt
Fire is fire, fire pipe is fire pipe. Use correct assets
User prompt
Add fire pipes as pipe type that shoots fire to the left of screen
User prompt
Fire for fire, fire pipe for fire pipe
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'x')' in or related to this line: 'if (cx < rect.x) {' Line Number: 384
User prompt
Use fire pipe asset for fire pipes
User prompt
Asset creation failed try again
User prompt
Instead of tint make asset
User prompt
Please fix the bug: 'TypeError: tween.to is not a function' in or related to this line: 'tween.to(fireSprite, {' Line Number: 117 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make red tint be really prominent
User prompt
Make fire pipes have red tint
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'getBoundingRect')' in or related to this line: 'return dx * dx + dy * dy < r * r;' Line Number: 369
User prompt
Add fire pipes, they shoot fire behind them
User prompt
All drinks must be same spot as bird drink
User prompt
New drink again
User prompt
Make drink2 be same placing as bird drink and make drinks save
User prompt
Please fix the bug: 'birdDrinkSprite is not defined' in or related to this line: 'birdDrinkSprite.visible = false;' Line Number: 274
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdSprite = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
var birdDrinkSprite = self.attachAsset('Bird_drink', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
var drink2Sprite = self.attachAsset('Drink_2', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
var drink3Sprite = self.attachAsset('Drink3', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
// Position drink in front of bird
birdDrinkSprite.x = birdSprite.width * 0.7; // Offset to the right (in front)
drink2Sprite.x = birdDrinkSprite.x; // Align drink2Sprite with birdDrinkSprite
drink3Sprite.x = birdDrinkSprite.x; // Align drink3Sprite with birdDrinkSprite
self.currentSprite = birdSprite;
self.radius = birdSprite.width * 0.5 * 0.85; // for collision
self.vy = 0; // vertical speed
self.gravity = 0.6; // gravity per frame (slower fall)
self.flapStrength = -22; // negative = up, less strong flap
// Flap method
self.flap = function () {
self.vy = self.flapStrength;
LK.getSound('flap').play();
};
// Toggle drink method
self.toggleDrink = function () {
// Cycle through no drink, bird drink, drink 2, and drink 3
if (!birdDrinkSprite.visible && !drink2Sprite.visible && !drink3Sprite.visible) {
birdDrinkSprite.visible = true;
drink2Sprite.visible = false;
drink3Sprite.visible = false;
} else if (birdDrinkSprite.visible) {
birdDrinkSprite.visible = false;
drink2Sprite.visible = true;
drink3Sprite.visible = false;
} else if (drink2Sprite.visible) {
birdDrinkSprite.visible = false;
drink2Sprite.visible = false;
drink3Sprite.visible = true;
} else {
birdDrinkSprite.visible = false;
drink2Sprite.visible = false;
drink3Sprite.visible = false;
}
// Save the current drink state to storage
storage.lastDrink = birdDrinkSprite.visible ? 1 : drink2Sprite.visible ? 2 : 0;
};
// Update method
self.update = function () {
self.vy += self.gravity;
self.y += self.vy;
// Rotate bird based on velocity (visual only)
var maxAngle = Math.PI / 4;
var minAngle = -Math.PI / 6;
var angle = self.vy / 40 * maxAngle;
if (angle > maxAngle) angle = maxAngle;
if (angle < minAngle) angle = minAngle;
birdSprite.rotation = angle;
// Keep drink upright and in front
birdDrinkSprite.rotation = 0;
};
return self;
});
// Fire class for standalone fire effects
var Fire = Container.expand(function () {
var self = Container.call(this);
var fireSprite = self.attachAsset('fire', {
anchorX: 0.5,
anchorY: 0.5,
visible: false,
alpha: 0.5
});
self.shootFire = function () {
fireSprite.visible = true;
fireSprite.x = 0; // Reset position when shooting
fireSprite.y = 0;
tween(fireSprite, {
alpha: 1
}, {
duration: 500,
easing: tween.linear,
onFinish: function onFinish() {
tween(fireSprite, {
alpha: 0.5
}, {
duration: 500,
easing: tween.linear
});
}
});
};
self.getBoundingRect = function () {
if (!fireSprite.visible) return null;
return {
x: self.x + fireSprite.x - fireSprite.width * 0.5,
y: self.y + fireSprite.y - fireSprite.height * 0.5,
width: fireSprite.width,
height: fireSprite.height
};
};
self.update = function () {
if (fireSprite.visible) {
fireSprite.x -= pipeSpeed;
if (fireSprite.x < -fireSprite.width) {
fireSprite.visible = false;
}
}
};
return self;
});
var FirePipe = Container.expand(function () {
var self = Container.call(this);
var firePipeSprite = self.attachAsset('FirePipe', {
anchorX: 0.5,
anchorY: 0.5,
visible: false,
alpha: 0.5 // Set initial transparency for tween effect
});
self.startFire = function () {
firePipeSprite.visible = true;
firePipeSprite.alpha = 1;
firePipeSprite.x = 0; // Start at pipe position
firePipeSprite.y = 0; // Center fire vertically
// Tween fire to move left across screen
tween(firePipeSprite, {
x: -2048 // Move fire across entire screen width
}, {
duration: 3000,
// 3 seconds to cross screen
easing: tween.linear,
onFinish: function onFinish() {
firePipeSprite.visible = false;
firePipeSprite.x = 0; // Reset position for next fire
}
});
};
self.getBoundingRect = function () {
if (!firePipeSprite.visible) return null;
return {
x: self.x + firePipeSprite.x - firePipeSprite.width * 0.5,
y: self.y + firePipeSprite.y - firePipeSprite.height * 0.5,
width: firePipeSprite.width,
height: firePipeSprite.height
};
};
self.update = function () {
// Fire movement is now handled by tween, no manual movement needed
};
return self;
});
// PipePair class (top and bottom pipes)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Pipe config
var pipeWidth = 200;
var pipeHeight = 1200;
var gapHeight = 480; // vertical gap between pipes
// Randomly decide if pipes are flipped (top/bottom swap orientation)
var flip = Math.random() < 0.5;
// Randomly decide pipe type: normal (70%) or fire (30%)
var isFirePipe = Math.random() < 0.3;
// For fire pipes, always single pipe. For regular pipes, 30% chance of single pipe
var isSinglePipe = isFirePipe || Math.random() < 0.3;
var pipeAsset = isFirePipe ? 'FirePipe' : 'pipe';
var topPipe = null;
var bottomPipe = null;
// Create pipes based on configuration
if (isSinglePipe) {
// Single pipe - randomly choose top or bottom position
var isTopPosition = Math.random() < 0.5;
if (isTopPosition) {
topPipe = self.attachAsset(pipeAsset, {
anchorX: 0,
anchorY: flip ? 0 : 1,
width: pipeWidth,
height: pipeHeight,
rotation: flip ? Math.PI : 0
});
} else {
bottomPipe = self.attachAsset(pipeAsset, {
anchorX: 0,
anchorY: flip ? 1 : 0,
width: pipeWidth,
height: pipeHeight,
rotation: flip ? Math.PI : 0
});
}
} else {
// Double pipes (traditional setup)
topPipe = self.attachAsset(pipeAsset, {
anchorX: 0,
anchorY: flip ? 0 : 1,
width: pipeWidth,
height: pipeHeight,
rotation: flip ? Math.PI : 0
});
bottomPipe = self.attachAsset(pipeAsset, {
anchorX: 0,
anchorY: flip ? 1 : 0,
width: pipeWidth,
height: pipeHeight,
rotation: flip ? Math.PI : 0
});
}
self.pipeWidth = pipeWidth;
self.gapHeight = gapHeight;
self.passed = false; // for scoring
self.isFirePipe = isFirePipe;
self.isSinglePipe = isSinglePipe;
self.fireTimer = 0;
self.topPipe = topPipe;
self.bottomPipe = bottomPipe;
// Set pipes' vertical positions based on gapY (top of gap)
self.setGap = function (gapY) {
// gapY: y position of top of gap
if (topPipe) topPipe.y = gapY;
if (bottomPipe) bottomPipe.y = gapY + gapHeight;
};
// Move pipes left
self.update = function () {
self.x -= pipeSpeed;
};
// Get bounding rects for collision
self.getTopRect = function () {
if (!topPipe) return null;
return {
x: self.x,
y: topPipe.y - pipeHeight,
width: pipeWidth,
height: pipeHeight
};
};
self.getBottomRect = function () {
if (!bottomPipe) return null;
return {
x: self.x,
y: bottomPipe.y,
width: pipeWidth,
height: pipeHeight
};
};
// Add fire shooting capability for fire pipes
if (isFirePipe) {
var firePipe = new FirePipe();
self.addChild(firePipe);
self.firePipe = firePipe;
self.fireStarted = false; // Track if fire has been started
}
self.update = function () {
self.x -= pipeSpeed;
// Update fire pipe if this is a fire pipe
if (self.isFirePipe && self.firePipe) {
self.firePipe.x = self.x; // Position fire at pipe position
// Center fire in the gap or at pipe level for single pipes
var fireY = bottomPipe ? bottomPipe.y - gapHeight / 2 : topPipe ? topPipe.y + gapHeight / 2 : 1366;
self.firePipe.y = fireY;
self.firePipe.update();
// Start fire when pipe enters screen (only once)
if (!self.fireStarted && self.x < 2048) {
self.fireStarted = true;
self.firePipe.startFire();
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // sky blue
});
/****
* Game Code
****/
// Sound: Hit
// Sound: Score
// Sound: Flap
// Ground: brown box
// Pipe: green box
// Bird: yellow ellipse
// Game constants
var BIRD_START_X = 600;
var BIRD_START_Y = 1200;
var PIPE_INTERVAL = 1300; // px between pipes horizontally (slower spawn rate)
var PIPE_MIN_Y = 350; // min y for top of gap
var PIPE_MAX_Y = 1800; // max y for top of gap
var GROUND_Y = 2732 - 120; // ground top y
var pipeSpeed = 8; // px per frame (slower pipes)
// Game state
var bird;
var pipes = [];
var ground;
var score = 0;
var scoreTxt;
var gameOver = false;
var started = false;
var lastPipeX = 0;
// GUI: Score
scoreTxt = new Text2('0', {
size: 160,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// GUI: Game Over text
var gameOverTxt = new Text2('Game Over', {
size: 180,
fill: 0xFF4444
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.visible = false;
LK.gui.center.addChild(gameOverTxt);
// GUI: Tap to restart
var restartTxt = new Text2('Tap to restart', {
size: 100,
fill: 0xFFFFFF
});
restartTxt.anchor.set(0.5, 0.5);
restartTxt.visible = false;
LK.gui.center.addChild(restartTxt);
// GUI: Drink toggle button
var drinkToggleBtn = new Container();
var drinkBtnBg = drinkToggleBtn.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 150,
height: 150,
tint: 0x4444ff
});
var drinkBtnText = new Text2('🥤', {
size: 80,
fill: 0xFFFFFF
});
drinkBtnText.anchor.set(0.5, 0.5);
drinkToggleBtn.addChild(drinkBtnText);
drinkToggleBtn.x = -100;
drinkToggleBtn.y = 100;
LK.gui.topRight.addChild(drinkToggleBtn);
// Drink toggle handler
drinkToggleBtn.down = function () {
if (bird) {
bird.toggleDrink();
}
};
// Add ground
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: GROUND_Y
});
game.addChild(ground);
// Helper: Reset game state
function resetGame() {
// Remove pipes
for (var i = 0; i < pipes.length; i++) {
pipes[i].destroy();
}
pipes = [];
// Remove bird if exists
if (bird) bird.destroy();
// Reset variables
score = 0;
scoreTxt.setText('0');
gameOver = false;
started = false;
lastPipeX = 0;
// Hide game over
gameOverTxt.visible = false;
restartTxt.visible = false;
// Add bird
bird = new Bird();
bird.x = BIRD_START_X;
bird.y = BIRD_START_Y;
game.addChild(bird);
// Restore score if available
if (typeof storage.lastScore !== "undefined") {
score = storage.lastScore;
scoreTxt.setText(score + '');
}
// Restore drink state if available
if (typeof storage.lastDrink !== "undefined") {
if (storage.lastDrink === 1) {
bird.children[1].visible = true;
bird.children[2].visible = false;
} else if (storage.lastDrink === 2) {
bird.children[1].visible = false;
bird.children[2].visible = true;
} else if (storage.lastDrink === 3) {
bird.children[1].visible = false;
bird.children[2].visible = false;
bird.children[3].visible = true;
} else {
bird.children[1].visible = false;
bird.children[2].visible = false;
bird.children[3].visible = false;
}
}
// Add first pipes
for (var i = 0; i < 3; i++) {
spawnPipe(2048 + i * PIPE_INTERVAL);
}
}
// Helper: Spawn a pipe pair at x
function spawnPipe(x) {
var gapY = PIPE_MIN_Y + Math.floor(Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y));
var pipePair = new PipePair();
pipePair.x = x;
pipePair.setGap(gapY);
pipes.push(pipePair);
game.addChild(pipePair);
lastPipeX = x;
}
// Helper: Check collision between bird and a pipe rect
function birdHitsRect(rect) {
// Return false if rect is null (e.g., when fire sprite is not visible)
if (!rect) return false;
// Bird is a circle, rect is {x, y, width, height}
var cx = bird.x;
var cy = bird.y;
var r = bird.radius;
// Find closest point in rect to bird center
var closestX = cx;
if (cx < rect.x) closestX = rect.x;else if (cx > rect.x + rect.width) closestX = rect.x + rect.width;
var closestY = cy;
if (cy < rect.y) closestY = rect.y;else if (cy > rect.y + rect.height) closestY = rect.y + rect.height;
var dx = cx - closestX;
var dy = cy - closestY;
return dx * dx + dy * dy < r * r;
}
// Helper: End game
function triggerGameOver() {
if (gameOver) return;
gameOver = true;
// Save score and drink state to storage
storage.lastScore = score;
// Access birdDrinkSprite via bird instance if available
storage.lastDrink = bird && bird.hasOwnProperty('children') && bird.children.length > 1 ? bird.children[1].visible ? 1 : bird.children[2].visible ? 2 : bird.children[3].visible ? 3 : 0 : 0;
LK.getSound('hit').play();
gameOverTxt.visible = true;
restartTxt.visible = true;
// Flash screen
LK.effects.flashScreen(0xff0000, 600);
// Show Game Over popup (will pause game)
// LK.showGameOver();
}
// Game tap/flap handler
function handleTap(x, y, obj) {
if (gameOver) {
resetGame();
return;
}
if (!started) {
started = true;
}
bird.flap();
}
// Attach tap handler
game.down = handleTap;
// Main update loop
game.update = function () {
if (gameOver) return;
// Bird update
bird.update();
// Pipes update
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Remove pipes off screen
if (pipe.x + pipe.pipeWidth < 0) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Scoring: passed pipe
if (!pipe.passed && pipe.x + pipe.pipeWidth < bird.x) {
pipe.passed = true;
score += 1;
scoreTxt.setText(score + '');
LK.getSound('score').play();
}
}
// Spawn new pipes
if (pipes.length > 0) {
var rightmost = pipes[pipes.length - 1];
if (2048 - (rightmost.x + rightmost.pipeWidth) >= PIPE_INTERVAL - 10) {
spawnPipe(2048);
}
}
// Collision: pipes
for (var i = 0; i < pipes.length; i++) {
var pipe = pipes[i];
var hitTopPipe = birdHitsRect(pipe.getTopRect());
var hitBottomPipe = birdHitsRect(pipe.getBottomRect());
var hitPipe = hitTopPipe || hitBottomPipe;
var hitFire = pipe.isFirePipe && pipe.firePipe && pipe.firePipe.getBoundingRect && birdHitsRect(pipe.firePipe.getBoundingRect());
if (hitPipe || hitFire) {
triggerGameOver();
return;
}
}
// Collision: ground
if (bird.y + bird.radius > GROUND_Y) {
bird.y = GROUND_Y - bird.radius;
triggerGameOver();
return;
}
// Collision: ceiling
if (bird.y - bird.radius < 0) {
bird.y = bird.radius;
bird.vy = 0;
}
// Out of bounds (left/right)
if (bird.x - bird.radius < 0 || bird.x + bird.radius > 2048) {
triggerGameOver();
return;
}
};
// Initial game state
resetGame(); ===================================================================
--- original.js
+++ change.js
@@ -136,24 +136,23 @@
anchorY: 0.5,
visible: false,
alpha: 0.5 // Set initial transparency for tween effect
});
- self.shootFire = function () {
+ self.startFire = function () {
firePipeSprite.visible = true;
- firePipeSprite.x = -100; // Position fire to the left of the pipe
+ firePipeSprite.alpha = 1;
+ firePipeSprite.x = 0; // Start at pipe position
firePipeSprite.y = 0; // Center fire vertically
+ // Tween fire to move left across screen
tween(firePipeSprite, {
- alpha: 1
+ x: -2048 // Move fire across entire screen width
}, {
- duration: 500,
+ duration: 3000,
+ // 3 seconds to cross screen
easing: tween.linear,
onFinish: function onFinish() {
- tween(firePipeSprite, {
- alpha: 0.5
- }, {
- duration: 500,
- easing: tween.linear
- });
+ firePipeSprite.visible = false;
+ firePipeSprite.x = 0; // Reset position for next fire
}
});
};
self.getBoundingRect = function () {
@@ -165,14 +164,9 @@
height: firePipeSprite.height
};
};
self.update = function () {
- if (firePipeSprite.visible) {
- firePipeSprite.x -= pipeSpeed; // Move fire with the pipe
- if (firePipeSprite.x < -firePipeSprite.width) {
- firePipeSprite.visible = false; // Hide fire when off screen
- }
- }
+ // Fire movement is now handled by tween, no manual movement needed
};
return self;
});
// PipePair class (top and bottom pipes)
@@ -270,23 +264,23 @@
if (isFirePipe) {
var firePipe = new FirePipe();
self.addChild(firePipe);
self.firePipe = firePipe;
+ self.fireStarted = false; // Track if fire has been started
}
self.update = function () {
self.x -= pipeSpeed;
// Update fire pipe if this is a fire pipe
if (self.isFirePipe && self.firePipe) {
- self.firePipe.x = self.x - 100; // Position fire to the left of pipe
+ self.firePipe.x = self.x; // Position fire at pipe position
// Center fire in the gap or at pipe level for single pipes
var fireY = bottomPipe ? bottomPipe.y - gapHeight / 2 : topPipe ? topPipe.y + gapHeight / 2 : 1366;
self.firePipe.y = fireY;
self.firePipe.update();
- // Shoot fire periodically
- self.fireTimer++;
- if (self.fireTimer % 60 === 0) {
- // Every 1 second at 60fps
- self.firePipe.shootFire();
+ // Start fire when pipe enters screen (only once)
+ if (!self.fireStarted && self.x < 2048) {
+ self.fireStarted = true;
+ self.firePipe.startFire();
}
}
};
return self;
Pipe. In-Game asset. 2d. High contrast. No shadows
Bird. In-Game asset. 2d. High contrast. No shadows
Rock bg. In-Game asset. 2d. High contrast. No shadows
Cup with Straw. In-Game asset. 2d. High contrast. No shadows
Coke bottle with straw. In-Game asset. 2d. High contrast. No shadows
Fire. In-Game asset. 2d. High contrast. No shadows
Red pipe. In-Game asset. 2d. High contrast. No shadows