Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'var bottomFloorTop = floors.length > 0 ? bottomFloorY - floors[0].height / 2 : bottomFloorY; // Top edge of the bottom floor' Line Number: 497
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'var bottomFloorTop = floors.length > 0 ? bottomFloorY - floors[0].height / 2 : bottomFloorY; // Top edge of the bottom floor' Line Number: 490
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '0')' in or related to this line: 'var bottomFloorTop = bottomFloorY - floors[0].height / 2; // Top edge of the bottom floor' Line Number: 488
User prompt
No. Redo completely the collisions of the dragon and the floors and the positioning of the dragon upons tarting: if falls off the floors / screen.
User prompt
No. Redo completely the collisions of the dragon and the floors and the positioning of the dragon upons tarting: if falls off the floors / screen.
User prompt
No. Redo completely the collisions of the dragon and the floors and the positioning of the dragon upons tarting: if falls off the screen.
User prompt
It did not work. The dragon falls off the floors down.
User prompt
The dragon falls off the screen after i walk a little bit it starts, seems it does not detect collision with floors. Please fix.
User prompt
The dragon falls off the screen when it starts, seems it does not detect collision with floors?
User prompt
The performance is terrible on mobile, as there are many things being checked on a tick level + the tweens. Please, optimize the performance to the max but keeping all the same features intact.
User prompt
For some reasons, sometimes the dragon can't shoot bubbles for quite a long time. Can you please fix that and only that?
User prompt
Remove all .context(...). They don't exist at all!
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'context')' in or related to this line: 'tween(enemy, {' Line Number: 583
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'context')' in or related to this line: 'tween(enemy, {' Line Number: 580
User prompt
Fix these issues: 1. Some enemies don't move. 2. Some platforms seem ephimerous - the dragon player don't stop on them when falling, it crosses them. 3. Sometimes the dragon stops throwing bubbles. Maybe there is something wrong with skipping frames or something like that? Make sure to change only what's necessary.
User prompt
I have several performance issues in the game when jumping with the dragon, possibly due to the tweens and other stuff. Please fix performance issues, simplify, make it lighter to run. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: floors is not defined' in or related to this line: 'for (var i = 0; i < floors.length; i++) {' Line Number: 50
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: bubbles is not defined' in or related to this line: 'bubbles.push(bubble);' Line Number: 103
User prompt
Please fix the bug: 'Uncaught ReferenceError: bubbles is not defined' in or related to this line: 'bubbles.push(bubble);' Line Number: 103
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
Modify the game.down = function () { console.log("click") to only trigger if clicking on start
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Class for Bubbles - optimized for performance var Bubble = Container.expand(function () { var self = Container.call(this); var bubbleGraphics = self.attachAsset('bubble', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -5; self.lastX = 0; self.lastY = 0; self.update = function () { // Store last position self.lastX = self.x; self.lastY = self.y; // Move bubble self.x += self.speed; // Check for intersection with any floor var intersectingFloor = null; for (var i = 0; i < floors.length; i++) { if (self.intersects(floors[i])) { intersectingFloor = floors[i]; break; } } // Handle collision with floor if (intersectingFloor) { // Create a PoppedBubble at the bubble's position var pop = game.addChild(new PoppedBubble()); pop.x = self.x; pop.y = self.y; // Remove from bubbles array before destroying var bubbleIndex = bubbles.indexOf(self); if (bubbleIndex !== -1) { bubbles.splice(bubbleIndex, 1); } // Destroy the bubble self.destroy(); // Destroy the PoppedBubble after 0.5 seconds LK.setTimeout(function () { pop.destroy(); }, 500); } }; }); var CapturingBubble = Container.expand(function () { var self = Container.call(this); var bubbleGraphics = self.attachAsset('bubble', { anchorX: 0.5, anchorY: 0.5 }); self.alpha = 0.5; self.scaleX = 2; self.scaleY = 2; self.update = function () {}; }); //<Assets used in the game will automatically appear here> // Class for the Dragon character - optimized for performance var Dragon = Container.expand(function () { var self = Container.call(this); self.dragonGraphics = self.attachAsset('dragon', { anchorX: 0.5, anchorY: 0.5 }); self.isIntersectingBubble = false; self.lastWasIntersecting = false; self.lastX = 0; self.lastY = 0; self.shootBubble = function () { // Create new bubble and ensure it's properly initialized var bubble = new Bubble(); bubble.x = self.x + 200 * self.scale.x; bubble.y = self.y - 5; bubble.lastX = bubble.x; bubble.lastY = bubble.y; // If the dragon is looking right, the bubble should move to the right if (self.scale.x == 1) { bubble.speed = 25; } else { // If the dragon is looking left, the bubble should move to the left bubble.speed = -25; } // Add bubble to game display list first to ensure it's rendered game.addChild(bubble); // Ensure bubbles array exists before trying to push to it if (!bubbles) { bubbles = []; } // Always add bubble to tracking array bubbles.push(bubble); // Play bubble sound when a bubble is shot LK.getSound('bubble').play(); }; }); // Class for Pop - optimized version var PoppedBubble = Container.expand(function () { var self = Container.call(this); var popGraphics = self.attachAsset('poppedBubble', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x09899d }); /**** * Game Code ****/ function tweenDragonContainer() { tween(dragonOnContainer, { y: dragonOnContainer.y + 300 }, { duration: 1000, onFinish: function onFinish() { tween(dragonOnContainer, { y: dragonOnContainer.y - 300 }, { duration: 1000 }); } }); } var started = false; // Initialize started flag var intervalId = LK.setInterval(function () { if (!started) { tweenDragonContainer(); } else { LK.clearInterval(intervalId); // Clear interval if started is true } }, 4000); var controlPanel = new Container(); var panelBackground = LK.getAsset('game_background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.8 }); controlPanel.addChild(panelBackground); var instructionsText = new Text2('INSTRUCTIONS', { size: 100, fill: 0xffffff, align: "center", font: "Comic Sans MS", fontWeight: "bold" }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 2048 / 2; instructionsText.y = 75; controlPanel.addChild(instructionsText); // Rainbow color array var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x8B00FF]; var colorIndex = 0; // Function to cycle through rainbow colors function cycleRainbowColors() { instructionsText.tint = rainbowColors[colorIndex]; colorIndex = (colorIndex + 1) % rainbowColors.length; } // Set interval to change color less frequently to improve performance LK.setInterval(cycleRainbowColors, 1000); var controlText = new Text2('- Click on 🐲 to shoot 🫧\n- Click above the 🐲 to jump ⬆️.\n- The higher you click, the bigger the jump ⬆️⬆️!', { size: 85, // Increased size for better visibility fill: 0x000000, align: "center", font: "Comic Sans MS" // Adding a cool font and bold style }); controlText.anchor.set(0.5, 0.5); controlText.x = 2048 / 2; controlText.y = 400; controlPanel.addChild(controlText); var understoodButtonBackgroundBg = LK.getAsset('understoodBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 800, width: 2000, alpha: 0.5 }); controlPanel.addChild(understoodButtonBackgroundBg); var dragonOnContainer = LK.getAsset('dragon', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1200, scaleX: 2, scaleY: 2 }); controlPanel.addChild(dragonOnContainer); var bubbleOnContainer = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, alpha: 0.35 }); dragonOnContainer.addChild(bubbleOnContainer); var understoodButton = new Text2('Click anywhere to start', { size: 150, fill: 0x1570e0, align: "center" }); understoodButton.anchor.set(0.5, 0.5); understoodButton.x = 2048 / 2; understoodButton.y = 800; controlPanel.addChild(understoodButton); game.addChild(controlPanel); // Add event listener to the 'Understood' button understoodButton.interactive = true; understoodButton.buttonMode = true; game.down = function (x, y, obj) { controlPanel.destroy(); startGame(); }; // Properly initialize all global variables to prevent undefined errors var bubbles = []; // Initialize bubbles array to store bubble instances var floors = []; // Initialize floors array to store floor instances var behaviours = {}; // Initialize behaviours object var enemies = []; var capturingBubbles = []; var jumpStartY = 0; var isJumping = false; // Initialize jumping state var canJump = true; // Initialize jump permission flag var isFalling = false; // Initialize falling state var lastShot = 0; // Track the last time a bubble was shot var lastClick = 0; // Track the last time the screen was clicked var jumpStartTime = 0; // Initialize jump start time var dragon; // Will be initialized later var background; // Will be initialized with a background image function startGame() { started = true; var level = 1; function restartGame() { // Clear existing platforms and enemies floors.forEach(function (floor) { floor.destroy(); }); floors = []; enemies.forEach(function (enemy) { enemy.destroy(); }); enemies = []; // Recreate floors at the bottom, left, and right margins for (var i = 0; i < 21; i++) { var floor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: i * 100, y: 2732 - 30 }); game.addChild(floor); floors.push(floor); } for (var j = 0; j < 27; j++) { var leftFloor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: 0, y: j * 100 }); game.addChild(leftFloor); floors.push(leftFloor); } for (var k = 0; k < 27; k++) { var rightFloor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 50, y: k * 100 }); game.addChild(rightFloor); floors.push(rightFloor); } // Destroy the existing dragon dragon.destroy(); // Recreate the dragon and add it to the game dragon = game.addChild(new Dragon()); // Reposition dragon at the bottom dragon.x = 1024; // Center horizontally // Careful positioning to ensure floor detection var bottomFloorY = 2732 - 30; // Y coordinate of bottom floor var bottomFloorTop = bottomFloorY - 50; // Top edge of bottom floor dragon.y = bottomFloorTop - dragon.height / 2 - 5; // Position above floor with clearance dragon.lastY = dragon.y; // Initialize lastY position dragon.lastX = dragon.x; // Initialize lastX position // Ensure we're detecting floor properly var isOnFloor = false; for (var i = 0; i < floors.length; i++) { if (Math.abs(floors[i].y - bottomFloorY) < 10 && Math.abs(floors[i].x - dragon.x) < dragon.width / 2 + floors[i].width / 2) { isOnFloor = true; break; } } // Double-check if not on floor, adjust position if (!isOnFloor) { dragon.y = 2500; // Higher position to ensure intersection } // Reset states isFalling = false; isJumping = false; canJump = true; // Add new platforms and enemies for the new level for (var j = 0; j < platformYPositions.length; j++) { var platformLength = Math.floor(Math.random() * (maxPlatformLength - minPlatformLength + 1)) + minPlatformLength; var startX = Math.floor(Math.random() * (2048 - platformLength * 100)); var startY = platformYPositions[j]; var randomTint = Math.floor(Math.random() * 16777215); for (var i = 0; i < platformLength; i++) { var newX = startX + i * 100; if (newX > 1900 || newX < 120) { continue; } var platform = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: startX + i * 100, y: platformYPositions[j] }); platform.tint = randomTint; game.addChild(platform); floors.push(platform); } var enemyType = 'enemy' + (i % 5 + 1); var behaviour = behaviours[enemyType] || easingFunctions[Math.floor(Math.random() * easingFunctions.length)]; behaviours[enemyType] = behaviour; var enemy = LK.getAsset(enemyType, { anchorX: 0.5, anchorY: 0.5, x: startX + 100, y: platformYPositions[j] - 100, type: enemyType }); if (enemyType === 'enemy5' || enemyType === 'enemy2') { enemy.tint = Math.floor(Math.random() * 0x7FFFFF) + 0x808080; } enemy.captured = false; game.addChild(enemy); enemies.push(enemy); } } var background = LK.getAsset('pixel_background_' + level.toString(), { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.95 }); game.addChild(background); // Simplified utility function that replaces all the complex array handlers function getMinMaxFromFloors(floors, property) { var min = Infinity; var max = -Infinity; for (var i = 0; i < floors.length; i++) { var value = floors[i][property]; if (value < min) { min = value; } if (value > max) { max = value; } } return { min: min, max: max }; } var jumpStartTime; // Declare jumpStartTime to track the start of the jump var easingFunctions = [tween.linear, tween.easeIn, tween.easeOut, tween.easeInOut, tween.bounceIn, tween.bounceOut, tween.bounceInOut, tween.elasticIn, tween.elasticOut, tween.elasticInOut, tween.cubicIn, tween.cubicOut, tween.cubitInOut, tween.expoIn, tween.expoOut, tween.expoInOut, tween.sineIn, tween.sineOut, tween.sineInOut]; LK.playMusic('music'); function isStandingOnFloor() { // Check if the dragon is currently standing on a floor - simplified version var dragonBottom = dragon.y + dragon.height / 2; var dragonX = dragon.x; var dragonHalfWidth = dragon.width / 2; // More generous threshold for detecting floor contact var standingThreshold = 15; for (var i = 0; i < floors.length; i++) { var floor = floors[i]; var floorTop = floor.y - floor.height / 2; var floorCenterX = floor.x; var floorHalfWidth = floor.width / 2; // Check horizontal overlap with more tolerance if (Math.abs(floorCenterX - dragonX) < dragonHalfWidth + floorHalfWidth) { // Check vertical proximity - is dragon bottom near or slightly below floor top? // The threshold allows for a small amount of overlap if (Math.abs(dragonBottom - floorTop) <= standingThreshold) { return true; // Simple overlap-based standing detection } } } return false; // Not standing on any detected floor } function updateFall() { // Handles the dragon falling due to gravity and landing on floors. if (isFalling) { // Store the dragon's vertical center position *before* applying gravity dragon.lastY = dragon.y; // Apply gravity effect by increasing the Y position dragon.y += 12; // Slightly reduced fall speed for better detection // Calculate dragon position properties after gravity var dragonBottom = dragon.y + dragon.height / 2; var dragonX = dragon.x; var dragonHalfWidth = dragon.width / 2; // Check for collision with floors for (var i = 0; i < floors.length; i++) { var floor = floors[i]; var floorTop = floor.y - floor.height / 2; var floorCenterX = floor.x; var floorHalfWidth = floor.width / 2; // First, check horizontal overlap for efficiency if (Math.abs(floorCenterX - dragonX) < dragonHalfWidth + floorHalfWidth) { // Then check if dragon bottom is at or past floor top if (dragonBottom >= floorTop && dragonBottom <= floorTop + 20) { // Allow slight overlap // Landing successful - dragon is intersecting floor isJumping = false; // Ensure any jump state is cancelled isFalling = false; // Stop the falling state canJump = true; // Allow jumping again from this floor // Snap the dragon precisely onto the floor top dragon.y = floorTop - dragon.height / 2; // Update lastY to the new position dragon.lastY = dragon.y; return; // Exit early - we've landed } } } // If we get here, dragon is still falling and hasn't landed } } //<Assets used in the game will automatically appear here> // Handle mouse move events to move the dragon game.move = function (x, y, obj) { // Ignore the event if the target is outside the screen or close to its margins if (x > 150 && x < 2048 - 200) { // Set the target x position for the dragon dragon.targetX = x; } }; var isFalling = false; // Initialize isFalling to track dragon's fall state var dragon = game.addChild(new Dragon()); dragon.x = 1024; // Center horizontally // Position dragon safely above the bottom floor to ensure detection dragon.y = 2500; // Higher initial Y to ensure it intersects with floor properly dragon.lastY = dragon.y; // Initialize lastY position dragon.lastX = dragon.x; // Initialize lastX position // Force the dragon to detect floor properly by verifying position var bottomFloorY = 2732 - 30; // Y position of bottom floor var bottomFloorTop = bottomFloorY - 50; // Top edge of bottom floor var dragonBottom = dragon.y + dragon.height / 2; // Ensure the dragon is properly above the bottom floor if (dragonBottom > bottomFloorTop) { // Adjust if necessary to sit precisely on floor dragon.y = bottomFloorTop - dragon.height / 2; dragon.lastY = dragon.y; } isFalling = false; // Ensure dragon doesn't start in falling state isJumping = false; // Ensure dragon doesn't start in jumping state canJump = true; // Dragon should be able to jump immediately if on floor // Handle game updates with improved frame handling game.update = function () { // Always check if dragon should be falling first, before any other updates var standingOnFloor = isStandingOnFloor(); // Check standing state *once* per frame // State transitions based on standing check first - must come before movement if (standingOnFloor) { // Dragon is standing on a floor if (isFalling) { // Just landed from a fall isFalling = false; canJump = true; } if (isJumping) { // Landed during a jump isJumping = false; canJump = true; } // Standing safely - update last position dragon.lastY = dragon.y; } else { // Not standing on a floor if (!isJumping) { // If not jumping, should be falling if (!isFalling) { // Starting to fall isFalling = true; dragon.lastY = dragon.y; } } } // Apply movement only after state is determined if (isJumping) { updateJump(); } else if (isFalling) { updateFall(); } // Horizontal movement - after vertical position is established if (dragon.targetX !== undefined) { var increment = (dragon.targetX - dragon.x) / 5; if (Math.abs(dragon.targetX - dragon.x) > 1) { dragon.x += increment; } // Update dragon's visual orientation if (Math.abs(increment) > 0.1) { dragon.scale.x = increment < 0 ? -1 : 1; } } // Update last X position dragon.lastX = dragon.x; // Update other game elements updateBubbles(); if (LK.ticks % 3 === 0) { updateEnemiesMovement(); } // Game Over Check if (game.gameOverTriggered && !game.gameOverShowing) { game.gameOverShowing = true; } }; // Handle touch events for shooting bubbles var lastShot = 0; var lastClick = 0; var isJumping = false; // Add a variable to track if the dragon is jumping var canJump = true; // Initialize canJump flag to allow jumping game.down = function (x, y, obj) { var now = Date.now(); if (y < dragon.y - dragon.height && canJump && !isJumping) { isJumping = true; // Set isJumping to true when the dragon starts jumping // Play jump sound when the dragon starts jumping LK.getSound('jump').play(); jumpStartY = y; // Capture the Y position of the mouse click jumpStartTime = Date.now(); // Initialize jump start time } else if (!isJumping) { // Reduce cooldown for more responsive shooting and fix bubble creation if (now - lastShot > 300) { // Ensure dragon exists before calling shootBubble if (dragon && typeof dragon.shootBubble === 'function') { dragon.shootBubble(); lastShot = now; } } } lastClick = now; }; function updateEnemiesMovement() { // Process all enemies every frame to ensure consistent movement for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (!enemy) { continue; } if (enemy.captured) { if (enemy.isMoving) { tween.stop(enemy); enemy.isMoving = false; enemy.floating = true; } else if (enemy.floating) { var enemyRef = enemy; // Create a strong reference to avoid context loss // Store reference to current enemy in a closure variable (function (currentEnemy) { tween(currentEnemy, { y: 100 }, { duration: 5000, easing: tween.linear, onFinish: function onFinish() { // Use the closure variable instead of 'this' if (currentEnemy && currentEnemy.children && currentEnemy.children[1]) { currentEnemy.children[1].tint = 0xbf5555; } if (enemies.length === 0) { if (level < 5) { level++; background.destroy(); // Remove the previous background background = LK.getAsset('pixel_background_' + level.toString(), { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 - background.height / 2, alpha: 0.95 }); game.addChild(background); // Add the new background // Combined level text creation to reduce object creation var levelText = new Text2('LEVEL ' + level.toString(), { size: 200, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = 2048 / 2; levelText.y = 2732 / 2; game.addChild(levelText); LK.setTimeout(function () { levelText.destroy(); }, 2000); // Restart game with new platforms and enemies restartGame(); } else { LK.showYouWin(); } } } }); })(enemyRef); } } else { // Initialize movement if not already moving if (!enemy.isMoving && !enemy.tweenInitialized) { enemy.isMoving = true; enemy.tweenInitialized = true; // Find all platform floors for this enemy var platformFloors = []; var platformY = enemy.y + 100; for (var j = 0; j < floors.length; j++) { if (Math.abs(floors[j].y - platformY) < 10 && floors[j].x > 0 && floors[j].x < 2048 - 50) { platformFloors.push(floors[j]); } } // Only proceed if we found platform floors if (platformFloors.length > 0) { // Find min and max X positions var startX = 2048; var endX = 0; for (var j = 0; j < platformFloors.length; j++) { if (platformFloors[j].x < startX) { startX = platformFloors[j].x; } if (platformFloors[j].x > endX) { endX = platformFloors[j].x; } } // Ensure we have a valid range to move in if (startX < endX) { enemy.alpha = 0.75; // Apply alpha when starting to move // Store the platform range for this enemy enemy.startX = startX; enemy.endX = endX; // Start the movement tween // Make sure to attach context to all tweens and store a strong reference to enemy var enemyRef = enemy; // Create a strong reference to avoid context loss // Use immediately invoked function to create closure for this enemy (function (currentEnemy, startPos, endPos) { function moveToStart(enemy) { enemy.isMovingRight = false; tween(enemy, { x: startPos }, { duration: 3000, easing: tween.linear, onFinish: function onFinish() { if (!enemy) { return; } enemy.isMovingRight = true; moveToEnd(enemy); } }); } function moveToEnd(enemy) { tween(enemy, { x: endPos }, { duration: 3000, easing: tween.linear, onFinish: function onFinish() { if (!enemy || enemy.isMovingRight === undefined) { return; } enemy.isMovingRight = false; moveToStart(enemy); } }); } // Start initial movement tween(currentEnemy, { x: endPos }, { duration: 3000, easing: behaviours[currentEnemy.type] || tween.linear, onFinish: function onFinish() { if (!currentEnemy) { return; } moveToStart(currentEnemy); } }); })(enemyRef, enemyRef.startX, enemyRef.endX); enemy.isMovingRight = true; } } } // Update enemy direction based on movement if (enemy.x !== enemy.lastX) { enemy.scale.x = enemy.x > enemy.lastX ? 1 : -1; } // Check for collision with dragon if (!enemy.captured && dragon.intersects(enemy)) { // Play game over sound when the dragon dies LK.getSound('gameover').play(); // Rotate the dragon 90 degrees dragon.rotation = -3 * Math.PI * dragon.scale.x; // Animate the dragon falling down - simplified tween with proper context var dragonRef = dragon; // Create a strong reference to avoid context loss // Use immediately invoked function to create closure (function (dragonToTween) { tween(dragonToTween, { y: 2732 + dragonToTween.height, tint: 0xFF0000 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { LK.showGameOver(); } }); })(dragonRef); } } // Update last known position enemy.lastX = enemy.x; enemy.lastY = enemy.y; } //{50} // End of enemy processing loop // Dragon-enemy collision check is now inside the main enemy loop (lines 601-625), triggered immediately. // The redundant loop below is removed. } //{5r} // End of updateEnemiesMovement function function updateBubbles() { // Ensure bubbles array exists to prevent errors if (!bubbles) { bubbles = []; return; } // Process all bubbles every frame for consistent collision detection for (var i = bubbles.length - 1; i >= 0; i--) { var bubble = bubbles[i]; if (!bubble) { bubbles.splice(i, 1); continue; } // Call the bubble's update method to move it if (bubble.update) { bubble.update(); } // Optimization: Check bubble-bubble intersections less frequently var bubbleDestroyed = false; if (LK.ticks % 3 === 0) { // Check only every 3 frames for (var j = i - 1; j >= 0; j--) { if (j >= bubbles.length) { continue; } // Safety check var otherBubble = bubbles[j]; if (!otherBubble || !otherBubble.parent) { // Check if other bubble still exists // If otherBubble was already processed and removed in this loop iteration, skip if (j < bubbles.length) { bubbles.splice(j, 1); } continue; } if (bubble.intersects(otherBubble)) { // Create PoppedBubbles var pop1 = game.addChild(new PoppedBubble()); pop1.x = bubble.x; pop1.y = bubble.y; var pop2 = game.addChild(new PoppedBubble()); pop2.x = otherBubble.x; pop2.y = otherBubble.y; // Safely destroy and remove bubbles var bubbleId = bubble.id; // Store id in case bubble reference becomes invalid var otherBubbleId = otherBubble.id; bubble.destroy(); otherBubble.destroy(); // Remove from bubbles array by id or index, iterating backwards for (var k = bubbles.length - 1; k >= 0; k--) { if (bubbles[k].id === bubbleId || bubbles[k].id === otherBubbleId) { bubbles.splice(k, 1); } } // Play pop sound LK.getSound('pop').play(); // Destroy PoppedBubbles later LK.setTimeout(function () { if (pop1.parent) { pop1.destroy(); } if (pop2.parent) { pop2.destroy(); } }, 500); bubbleDestroyed = true; // Exit inner loop as 'bubble' is gone break; } } } // Skip further checks if bubble was destroyed in bubble-bubble collision if (bubbleDestroyed || !bubble.parent) { // Check parent as destroy might be async continue; } // Remove bubbles that go off-screen if (bubble.y < -50 || bubble.x < -50 || bubble.x > 2100) { bubble.destroy(); if (i < bubbles.length) { bubbles.splice(i, 1); } } } // End of main bubble loop (i) // --- Collision Checks (Moved outside the main bubble loop for clarity/potential optimization) --- // Check for bubble-enemy collisions for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (!enemy || enemy.captured || !enemy.parent) { // Also check if enemy exists in scene continue; } for (var j = bubbles.length - 1; j >= 0; j--) { var bubble = bubbles[j]; // Ensure bubble exists and hasn't been destroyed in this update cycle if (!bubble || !bubble.parent) { if (j < bubbles.length) { bubbles.splice(j, 1); } // Clean up missing bubbles continue; } if (bubble.intersects(enemy)) { // Destroy the original bubble bubble.destroy(); bubbles.splice(j, 1); // Create a new bubble as a child of the enemy var newBubble = new CapturingBubble(); newBubble.x = 0; // Position relative to the enemy newBubble.y = 0; // Position relative to the enemy enemy.addChild(newBubble); enemy.captured = true; capturingBubbles.push(newBubble); // Since bubble 'j' is gone, and enemy 'i' is captured, // we can break the inner loop and move to the next enemy break; } } } // Check if the dragon is intersecting a standard bubble for (var i = bubbles.length - 1; i >= 0; i--) { var bubble = bubbles[i]; if (!bubble || !bubble.parent) { // Ensure bubble exists if (i < bubbles.length) { bubbles.splice(i, 1); } continue; } if (dragon.intersects(bubble)) { var pop = game.addChild(new PoppedBubble()); pop.x = bubble.x; pop.y = bubble.y; bubble.destroy(); bubbles.splice(i, 1); LK.getSound('pop').play(); LK.setTimeout(function () { if (pop.parent) { pop.destroy(); } }, 500); //{6x} // Use safe destroy // Dragon can only pop one bubble per frame break; } } // Process capturing bubbles (dragon collision and floating) for (var i = capturingBubbles.length - 1; i >= 0; i--) { var capturingBubble = capturingBubbles[i]; if (!capturingBubble || !capturingBubble.parent) { // Ensure bubble exists if (i < capturingBubbles.length) { capturingBubbles.splice(i, 1); } continue; } var enemy = capturingBubble.parent; // Get the enemy parent // Check for dragon intersection first if (dragon.intersects(capturingBubble)) { if (enemy && enemy.parent) { // Ensure enemy parent also exists var pop = game.addChild(new PoppedBubble()); // Use enemy's global position for pop effect var enemyGlobalPos = enemy.parent.toGlobal(enemy.position); pop.x = enemyGlobalPos.x; pop.y = enemyGlobalPos.y; // Find enemy index to remove it var enemyIndex = enemies.indexOf(enemy); if (enemyIndex !== -1) { enemies.splice(enemyIndex, 1); } enemy.destroy(); //{6N} // This also destroys the capturingBubble child capturingBubbles.splice(i, 1); // Remove from capturing array LK.getSound('pop').play(); LK.setTimeout(function () { if (pop.parent) { pop.destroy(); } }, 1000); // Use safe destroy // Since bubble 'i' and its enemy are gone, continue to next bubble continue; } else { // Enemy parent doesn't exist? Clean up bubble. capturingBubble.destroy(); capturingBubbles.splice(i, 1); continue; } } // Update floating behavior if not popped if (enemy && enemy.captured && !enemy.floating) { enemy.y -= 3; // Slightly reduced floating speed } } } //{77} // End of updateBubbles function function updateJump() { if (isJumping) { var jumpHeight = (dragon.y - jumpStartY) / 10; dragon.y -= jumpHeight; // Move dragon upwards based on calculated jump height // More efficient intersection check with early termination var didHitCeiling = false; var dragonX = dragon.x; var dragonTop = dragon.y - dragon.height / 2; var verticalThreshold = 50; // Check floors slightly above var horizontalThreshold = dragon.width * 1.5; for (var i = 0; i < floors.length; i++) { var floor = floors[i]; // Optimization: Only check floors vertically and horizontally close if (Math.abs(floor.y - dragonTop) < verticalThreshold + floor.height && Math.abs(floor.x - dragonX) < horizontalThreshold + floor.width / 2) { if (dragon.intersects(floor)) { var floorBottom = floor.y + floor.height / 2; // Check if hitting the bottom of a platform if (dragonTop <= floorBottom + 10) { // Allow slight overlap didHitCeiling = true; isJumping = false; // Stop jumping when hitting a ceiling isFalling = true; // Start falling when colliding with a floor canJump = false; // Disallow jumping again until the dragon hits a floor // Optional: Slightly push dragon down dragon.y = floorBottom + dragon.height / 2 + 5; break; } } } } // Check if jump duration has exceeded 0.4 seconds OR hit ceiling if (didHitCeiling || Date.now() - jumpStartTime >= 400) { isJumping = false; if (!didHitCeiling) { // Only start falling if max duration reached, not if ceiling hit (already falling) isFalling = true; } } // Note: dragon.lastWasIntersecting is handled by updateFall now } } // BLOCKS // Add a floor to the bottom of the screen var floors = []; for (var i = 0; i < 21; i++) { var floor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: i * 100, y: 2732 - 30 }); game.addChild(floor); floors.push(floor); } // Add continuous floor on the left margin of the screen for (var j = 0; j < 27; j++) { var leftFloor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: 0, y: j * 100 }); game.addChild(leftFloor); floors.push(leftFloor); } // Add continuous floor on the right margin of the screen for (var k = 0; k < 27; k++) { var rightFloor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 50, y: k * 100 }); game.addChild(rightFloor); floors.push(rightFloor); } // Add random platforms with irregular heights and positions var platformYPositions = []; var distinctHeights = [2300, 1900, 1500, 1100, 700]; // Define 4-5 distinct heights var minPlatformLength = 5; var maxPlatformLength = 14; distinctHeights.forEach(function (height) { platformYPositions.push(height); }); // Add random platforms with irregular heights and positions for (var j = 0; j < platformYPositions.length; j++) { var _behaviours$enemyType; var platformLength = Math.floor(Math.random() * (maxPlatformLength - minPlatformLength + 1)) + minPlatformLength; var startX = Math.floor(Math.random() * (2048 - platformLength * 100)); // Random start position var startY = platformYPositions[j]; // Generate a random tint color for the platforms var randomTint = Math.floor(Math.random() * 16777215); for (var i = 0; i < platformLength; i++) { var newX = startX + i * 100; if (newX > 1900 || newX < 120) { continue; } var platform = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: startX + i * 100, y: platformYPositions[j] }); platform.tint = randomTint; // Apply the random tint to the platform game.addChild(platform); floors.push(platform); } // Spawn an enemy on top of each platform var enemyType = 'enemy' + (i % 5 + 1); // Cycle through enemy1 to enemy5 var behaviour = (_behaviours$enemyType = behaviours[enemyType]) !== null && _behaviours$enemyType !== void 0 ? _behaviours$enemyType : easingFunctions[Math.floor(Math.random() * easingFunctions.length)]; // Assign random easing function; behaviours[enemyType] = behaviour; var enemy = LK.getAsset(enemyType, { anchorX: 0.5, anchorY: 0.5, x: startX + 100, y: platformYPositions[j] - 100, type: enemyType }); if (enemyType === 'enemy5' || enemyType === 'enemy2') { enemy.tint = Math.floor(Math.random() * 0x7FFFFF) + 0x808080; // Ensure light tint } enemy.captured = false; game.addChild(enemy); enemies.push(enemy); } /* LEVEL MANAGEMENT 1 */ var levelTextBackground = new Text2('LEVEL ' + level.toString(), { size: 210, fill: 0x000000 }); levelTextBackground.anchor.set(0.5, 0.5); levelTextBackground.x = 2048 / 2; levelTextBackground.y = 2732 / 2; game.addChild(levelTextBackground); LK.setTimeout(function () { levelTextBackground.destroy(); }, 2000); var levelText = new Text2('LEVEL ' + level.toString(), { size: 200, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = 2048 / 2; levelText.y = 2732 / 2; game.addChild(levelText); LK.setTimeout(function () { levelText.destroy(); }, 2000); }
===================================================================
--- original.js
+++ change.js
@@ -289,12 +289,26 @@
// Recreate the dragon and add it to the game
dragon = game.addChild(new Dragon());
// Reposition dragon at the bottom
dragon.x = 1024; // Center horizontally
- // Calculate initial Y: Bottom floor top is 2702 - 50 = 2652. Dragon center Y = 2652 - dragon height/2 = 2652 - 100 = 2552.
- dragon.y = 2552; // Position the dragon precisely on top of the bottom floor
- dragon.lastY = dragon.y; // Initialize lastY position AFTER setting initial Y
+ // Careful positioning to ensure floor detection
+ var bottomFloorY = 2732 - 30; // Y coordinate of bottom floor
+ var bottomFloorTop = bottomFloorY - 50; // Top edge of bottom floor
+ dragon.y = bottomFloorTop - dragon.height / 2 - 5; // Position above floor with clearance
+ dragon.lastY = dragon.y; // Initialize lastY position
dragon.lastX = dragon.x; // Initialize lastX position
+ // Ensure we're detecting floor properly
+ var isOnFloor = false;
+ for (var i = 0; i < floors.length; i++) {
+ if (Math.abs(floors[i].y - bottomFloorY) < 10 && Math.abs(floors[i].x - dragon.x) < dragon.width / 2 + floors[i].width / 2) {
+ isOnFloor = true;
+ break;
+ }
+ }
+ // Double-check if not on floor, adjust position
+ if (!isOnFloor) {
+ dragon.y = 2500; // Higher position to ensure intersection
+ }
// Reset states
isFalling = false;
isJumping = false;
canJump = true;
@@ -366,81 +380,65 @@
var jumpStartTime; // Declare jumpStartTime to track the start of the jump
var easingFunctions = [tween.linear, tween.easeIn, tween.easeOut, tween.easeInOut, tween.bounceIn, tween.bounceOut, tween.bounceInOut, tween.elasticIn, tween.elasticOut, tween.elasticInOut, tween.cubicIn, tween.cubicOut, tween.cubitInOut, tween.expoIn, tween.expoOut, tween.expoInOut, tween.sineIn, tween.sineOut, tween.sineInOut];
LK.playMusic('music');
function isStandingOnFloor() {
- // Check if the dragon is currently standing on a floor.
+ // Check if the dragon is currently standing on a floor - simplified version
var dragonBottom = dragon.y + dragon.height / 2;
var dragonX = dragon.x;
var dragonHalfWidth = dragon.width / 2;
- // How close the dragon's bottom must be to the floor's top to be considered 'standing'.
- // A small positive value allows for slight overlap before gravity takes over.
- var standingThreshold = 5;
+ // More generous threshold for detecting floor contact
+ var standingThreshold = 15;
for (var i = 0; i < floors.length; i++) {
var floor = floors[i];
var floorTop = floor.y - floor.height / 2;
var floorCenterX = floor.x;
var floorHalfWidth = floor.width / 2;
- // 1. Check horizontal overlap: Is the dragon horizontally aligned with the floor?
+ // Check horizontal overlap with more tolerance
if (Math.abs(floorCenterX - dragonX) < dragonHalfWidth + floorHalfWidth) {
- // 2. Check vertical proximity: Is the dragon's bottom edge very close to the floor's top edge?
- // It should be at the floor top or slightly above it (within the threshold).
- if (dragonBottom >= floorTop && dragonBottom <= floorTop + standingThreshold) {
- // 3. Precise check: Use intersects() to confirm actual collision in this precise position.
- // This helps avoid edge cases where proximity is met but shapes don't truly touch.
- if (dragon.intersects(floor)) {
- return true; // Standing confirmed
- }
+ // Check vertical proximity - is dragon bottom near or slightly below floor top?
+ // The threshold allows for a small amount of overlap
+ if (Math.abs(dragonBottom - floorTop) <= standingThreshold) {
+ return true; // Simple overlap-based standing detection
}
}
}
return false; // Not standing on any detected floor
}
function updateFall() {
// Handles the dragon falling due to gravity and landing on floors.
if (isFalling) {
- // Store the dragon's vertical center position *before* applying gravity for collision checks.
+ // Store the dragon's vertical center position *before* applying gravity
dragon.lastY = dragon.y;
- // Apply gravity effect by increasing the Y position.
- dragon.y += 15; // Adjust fall speed as needed
- // Pre-calculate dragon properties for collision checks.
+ // Apply gravity effect by increasing the Y position
+ dragon.y += 12; // Slightly reduced fall speed for better detection
+ // Calculate dragon position properties after gravity
var dragonBottom = dragon.y + dragon.height / 2;
var dragonX = dragon.x;
var dragonHalfWidth = dragon.width / 2;
- // Check for collision with any floor after moving.
+ // Check for collision with floors
for (var i = 0; i < floors.length; i++) {
var floor = floors[i];
var floorTop = floor.y - floor.height / 2;
var floorCenterX = floor.x;
var floorHalfWidth = floor.width / 2;
- // Optimization: Check only floors potentially beneath the dragon's current position.
- // A floor's top must be below where the dragon's top *was* last frame to be collide-able during fall.
- if (floorTop >= dragon.lastY - dragon.height / 2) {
- // 1. Check horizontal overlap: Is the dragon horizontally aligned with the floor?
- if (Math.abs(floorCenterX - dragonX) < dragonHalfWidth + floorHalfWidth) {
- // 2. Precise check: Does the dragon actually intersect the floor *now*?
- if (dragon.intersects(floor)) {
- // 3. Landing condition:
- // - Was the dragon actually moving downwards (y increased)?
- // - Is the dragon's bottom edge now at or below the floor's top edge?
- // This confirms a landing rather than just passing alongside or starting inside.
- if (dragon.y > dragon.lastY && dragonBottom >= floorTop) {
- // Landing successful!
- isJumping = false; // Ensure any jump state is cancelled.
- isFalling = false; // Stop the falling state.
- canJump = true; // Allow jumping again from this floor.
- // Snap the dragon's position precisely onto the floor top.
- // Adjust center Y so the bottom edge aligns with floorTop.
- dragon.y = floorTop - dragon.height / 2;
- // Immediately update lastY to the snapped position to prevent issues in the next frame.
- dragon.lastY = dragon.y;
- return; // Exit the function early as we've landed.
- }
- }
+ // First, check horizontal overlap for efficiency
+ if (Math.abs(floorCenterX - dragonX) < dragonHalfWidth + floorHalfWidth) {
+ // Then check if dragon bottom is at or past floor top
+ if (dragonBottom >= floorTop && dragonBottom <= floorTop + 20) {
+ // Allow slight overlap
+ // Landing successful - dragon is intersecting floor
+ isJumping = false; // Ensure any jump state is cancelled
+ isFalling = false; // Stop the falling state
+ canJump = true; // Allow jumping again from this floor
+ // Snap the dragon precisely onto the floor top
+ dragon.y = floorTop - dragon.height / 2;
+ // Update lastY to the new position
+ dragon.lastY = dragon.y;
+ return; // Exit early - we've landed
}
}
}
- // If the loop completes without returning, the dragon is still falling and hasn't landed.
- // lastY was already updated at the start of the function.
+ // If we get here, dragon is still falling and hasn't landed
}
}
//<Assets used in the game will automatically appear here>
// Handle mouse move events to move the dragon
@@ -453,72 +451,80 @@
};
var isFalling = false; // Initialize isFalling to track dragon's fall state
var dragon = game.addChild(new Dragon());
dragon.x = 1024; // Center horizontally
- // Calculate initial Y: Bottom floor top is 2702 - 50 = 2652. Dragon center Y = 2652 - dragon height/2 = 2652 - 100 = 2552.
- dragon.y = 2552; // Position the dragon precisely on top of the bottom floor
- dragon.lastY = dragon.y; // Initialize lastY position AFTER setting initial Y
+ // Position dragon safely above the bottom floor to ensure detection
+ dragon.y = 2500; // Higher initial Y to ensure it intersects with floor properly
+ dragon.lastY = dragon.y; // Initialize lastY position
dragon.lastX = dragon.x; // Initialize lastX position
+ // Force the dragon to detect floor properly by verifying position
+ var bottomFloorY = 2732 - 30; // Y position of bottom floor
+ var bottomFloorTop = bottomFloorY - 50; // Top edge of bottom floor
+ var dragonBottom = dragon.y + dragon.height / 2;
+ // Ensure the dragon is properly above the bottom floor
+ if (dragonBottom > bottomFloorTop) {
+ // Adjust if necessary to sit precisely on floor
+ dragon.y = bottomFloorTop - dragon.height / 2;
+ dragon.lastY = dragon.y;
+ }
isFalling = false; // Ensure dragon doesn't start in falling state
isJumping = false; // Ensure dragon doesn't start in jumping state
canJump = true; // Dragon should be able to jump immediately if on floor
// Handle game updates with improved frame handling
game.update = function () {
- // 1. Handle Horizontal Movement & Orientation
- if (dragon.targetX !== undefined) {
- var increment = (dragon.targetX - dragon.x) / 5;
- if (Math.abs(dragon.targetX - dragon.x) > 1) {
- dragon.x += increment;
- }
- // Adjust dragon's scale based on targetX position only if moving significantly
- if (Math.abs(increment) > 0.1) {
- dragon.scale.x = increment < 0 ? -1 : 1;
- }
- }
- // Store last horizontal position regardless of movement for consistency
- dragon.lastX = dragon.x;
- // --- Vertical Movement Logic ---
+ // Always check if dragon should be falling first, before any other updates
var standingOnFloor = isStandingOnFloor(); // Check standing state *once* per frame
- // State Transitions based on standing check
+ // State transitions based on standing check first - must come before movement
if (standingOnFloor) {
+ // Dragon is standing on a floor
if (isFalling) {
// Just landed from a fall
isFalling = false;
canJump = true;
- // Snapping should have happened in the last updateFall call
}
if (isJumping) {
- // Landed during a jump (e.g. short hop onto platform)
+ // Landed during a jump
isJumping = false;
canJump = true;
}
- // If standing, ensure lastY is current Y before potentially starting a fall next frame
+ // Standing safely - update last position
dragon.lastY = dragon.y;
} else {
// Not standing on a floor
if (!isJumping) {
- // If not already jumping, start or continue falling
+ // If not jumping, should be falling
if (!isFalling) {
- // Check if we just started falling
+ // Starting to fall
isFalling = true;
- dragon.lastY = dragon.y; // Record Y before gravity applies in updateFall
+ dragon.lastY = dragon.y;
}
}
}
- // Apply vertical movement based on the determined state
+ // Apply movement only after state is determined
if (isJumping) {
- updateJump(); // updateJump handles vertical movement and transition to falling
+ updateJump();
} else if (isFalling) {
- updateFall(); // updateFall handles vertical movement and transition to standing
+ updateFall();
}
- // Note: dragon.lastY is updated within updateJump/updateFall or when standing still.
- // --- Update Other Game Elements ---
- updateBubbles(); // Update bubbles (movement, collisions)
+ // Horizontal movement - after vertical position is established
+ if (dragon.targetX !== undefined) {
+ var increment = (dragon.targetX - dragon.x) / 5;
+ if (Math.abs(dragon.targetX - dragon.x) > 1) {
+ dragon.x += increment;
+ }
+ // Update dragon's visual orientation
+ if (Math.abs(increment) > 0.1) {
+ dragon.scale.x = increment < 0 ? -1 : 1;
+ }
+ }
+ // Update last X position
+ dragon.lastX = dragon.x;
+ // Update other game elements
+ updateBubbles();
if (LK.ticks % 3 === 0) {
- // Update enemies less frequently
- updateEnemiesMovement(); // Handles enemy movement and dragon collision check
+ updateEnemiesMovement();
}
- // Game Over Check (triggered within updateEnemiesMovement)
+ // Game Over Check
if (game.gameOverTriggered && !game.gameOverShowing) {
game.gameOverShowing = true;
}
};
A version of this bubble but exploded
brick, brown color, pixel style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
better wings, pixel style, more contrasted, more visible, blue color
a pixel clouds background. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a pixel clouds background, with mountains, full height full width Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a similar image. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a pixel clouds background, with mountains, full height full width Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A popped blue bobble, pixel style, bubble booble arcade game inspired. In-Game asset. 2d. High contrast. No shadows
A bubble popping, pixel style, retro. In-Game asset. 2d. High contrast. No shadows