User prompt
Please fix the bug: 'ReferenceError: enemyGraphic is not defined' in or related to this line: 'LK.effects.flashObject(enemyGraphic, 0xff0000, 200);' Line Number: 420
User prompt
Please fix the bug: 'ReferenceError: enemyGraphic is not defined' in or related to this line: 'if (enemyGraphic) {' Line Number: 370
User prompt
update as needed with: var Enemy = Container.expand(function(type) { var self = Container.call(this); // For type 1 (running enemy) var enemySprite = self.attachAsset('enemy1', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); var enemySpriteMirrored = self.attachAsset('enemy1', { anchorX: 0.5, anchorY: 0.5, scaleX: -1, // Mirror the sprite alpha: 0 }); // Animation properties self.animTick = 0; self.animRate = 8; // Frames between animation switches self.activate = function(x, y, type) { self.type = type || 1; self.visible = true; self.toDestroy = false; self.x = x; self.y = y; if (self.type === 1) { // Reset animation state self.animTick = 0; enemySprite.alpha = 1; enemySpriteMirrored.alpha = 0; // Set initial scale based on distance from horizon like field elements var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY); var scale = 0.5 + progress; self.scaleX = scale; self.scaleY = scale; } }; self.update = function() { if (self.type === 1) { // Calculate progress and movement like field elements var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY); // Move slightly slower than field elements var speed = (flyingBackground.scrollSpeed * 0.05 + flyingBackground.scrollSpeed * (progress * 2.5)) * 0.8; self.y -= speed; // Update scale based on distance var scale = 0.5 + progress; self.scaleX = scale; self.scaleY = scale; // Running animation self.animTick++; if (self.animTick >= self.animRate) { self.animTick = 0; // Swap sprite visibility enemySprite.alpha = enemySprite.alpha === 0 ? 1 : 0; enemySpriteMirrored.alpha = enemySpriteMirrored.alpha === 0 ? 1 : 0; } } }; // ... rest of the enemy class code });
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: game.flyingBackground is undefined' in or related to this line: 'var horizonY = game.flyingBackground.horizonY;' Line Number: 139
User prompt
Please fix the bug: 'TypeError: game.flyingBackground is undefined' in or related to this line: 'var horizonY = game.flyingBackground.horizonY;' Line Number: 139
Code edit (1 edits merged)
Please save this source code
User prompt
Use the same scale formula that head has for all the dragon asset variables.
User prompt
Apply the scale adjustment in self.update of the dragonHead class to all the dragon assets
Code edit (1 edits merged)
Please save this source code
User prompt
Reduce the firing rate of the fireballs.
User prompt
Update with: self.update = function () { if (!self.active) { return; } // Update position with both vertical and horizontal components self.y += self.speed; self.x += self.vx; // Calculate rotation based on direction of travel // For correct orientation: Math.atan2(dy, dx) - Math.PI/2 // We're subtracting 90 degrees (PI/2) to adjust for the sprite's default orientation var angle = Math.atan2(self.speed, self.vx) - Math.PI/2; self.rotation = angle; // Create fire trail particles if (Math.random() < 0.3) { particlePool.spawn(self.x + (Math.random() * 40 - 20), self.y + 20); } // Recycle when off screen (bottom or sides) if (self.y > 2732 || self.x < -100 || self.x > 2148) { fireballPool.recycle(self); } };
User prompt
Update with: self.update = function () { if (!self.active) { return; } // Update position with both vertical and horizontal components self.y += self.speed; self.x += self.vx; // Calculate rotation based on direction of travel if (self.vx !== 0) { var angle = Math.atan2(self.speed, self.vx); self.rotation = angle; } // Create fire trail particles if (Math.random() < 0.3) { particlePool.spawn(self.x + (Math.random() * 40 - 20), self.y + 20); } // Recycle when off screen (bottom or sides) if (self.y > 2732 || self.x < -100 || self.x > 2148) { fireballPool.recycle(self); } };
Code edit (5 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: self.bottomContainer.sort is not a function. (In 'self.bottomContainer.sort(function (a, b) { __$(344); return a.zIndex - b.zIndex; })', 'self.bottomContainer.sort' is undefined)' in or related to this line: 'self.bottomContainer.sort(function (a, b) {' Line Number: 641
User prompt
Please fix the bug: 'TypeError: self.bottomContainer.sortChildren is not a function. (In 'self.bottomContainer.sortChildren()', 'self.bottomContainer.sortChildren' is undefined)' in or related to this line: 'self.bottomContainer.sortChildren();' Line Number: 641
User prompt
Please fix the bug: 'TypeError: self.bottomContainer.sort is not a function. (In 'self.bottomContainer.sort()', 'self.bottomContainer.sort' is undefined)' in or related to this line: 'self.bottomContainer.sort();' Line Number: 641
User prompt
Please fix the bug: 'TypeError: self.bottomContainer.sortChildren is not a function. (In 'self.bottomContainer.sortChildren()', 'self.bottomContainer.sortChildren' is undefined)' in or related to this line: 'self.bottomContainer.sortChildren();' Line Number: 641
Code edit (1 edits merged)
Please save this source code
Code edit (21 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: objects[i].update is not a function' in or related to this line: 'objects[i].update();' Line Number: 681
Code edit (1 edits merged)
Please save this source code
Code edit (11 edits merged)
Please save this source code
User prompt
update as needed with: var DragonHead = Container.expand(function () { var self = Container.call(this); // Dragon sprites var body = self.attachAsset('dragonBody', { anchorX: 0.5, anchorY: 0.75, scaleX: 2, scaleY: 2 }); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var headOpen = self.attachAsset('dragonHeadOpen', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, alpha: 0 }); // Position tracking variables var baselineY = 2732 * 0.2; // Default position in top fifth var targetX = 2048 / 2; var targetY = baselineY; var maxY = 2732 * 0.4; // Only allow in top 40% of screen var smoothingFactor = 0.12; var prevX = null; var prevY = null; // Rotation variables var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; // Scale tracking var scaleHistory = new Array(5).fill(2); // Start with default scale of 2 var scaleIndex = 0; self.update = function () { // Position tracking if (facekit.noseTip) { targetX = facekit.noseTip.x; // Limit how far down the dragon can go targetY = Math.min(facekit.noseTip.y, maxY); // Initialize previous positions if not set if (prevX === null) { prevX = targetX; prevY = targetY; } // Weighted average between previous and target position var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } else { // Return to baseline position gradually if no face detected if (prevX === null) { prevX = 2048 / 2; prevY = baselineY; } var newX = prevX * (1 - smoothingFactor) + (2048 / 2) * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + baselineY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } // Rest of the update method remains the same // Rotation tracking - CORRECTED VERSION if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } // Scale adjustment based on face size if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; // Adjusted divisor for dragon head // Update rolling average scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; // Calculate average scale var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; // Apply with gentle smoothing (limited range) var targetScale = Math.max(1.5, Math.min(2.5, avgScale)); head.scaleX = head.scaleX * 0.85 + targetScale * 0.15; head.scaleY = head.scaleY * 0.85 + targetScale * 0.15; headOpen.scaleX = head.scaleX; headOpen.scaleY = head.scaleY; } // Toggle dragon head assets based on mouth open state if (facekit && facekit.mouthOpen) { head.alpha = 0; headOpen.alpha = 1; } else { head.alpha = 1; headOpen.alpha = 0; } }; // Rest of the class remains the same function calculateFaceTilt() { // Existing code... } return self; });
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var DragonHead = Container.expand(function () { var self = Container.call(this); // Dragon sprites var body = self.attachAsset('dragonBody', { anchorX: 0.5, anchorY: 0.75, scaleX: 2, scaleY: 2 }); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var headOpen = self.attachAsset('dragonHeadOpen', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, alpha: 0 }); // Position tracking variables var baselineY = 2732 * 0.2; // Default position in top fifth var targetX = 2048 / 2; var targetY = baselineY; var maxY = 2732 * 0.4; // Only allow in top 40% of screen var smoothingFactor = 0.12; var prevX = null; var prevY = null; // Rotation variables var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; // Scale tracking var scaleHistory = new Array(5).fill(2); // Start with default scale of 2 var scaleIndex = 0; self.update = function () { // Position tracking if (facekit.noseTip) { targetX = facekit.noseTip.x; // Limit how far down the dragon can go targetY = Math.min(facekit.noseTip.y, maxY); // Initialize previous positions if not set if (prevX === null) { prevX = targetX; prevY = targetY; } // Weighted average between previous and target position var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } else { // Return to baseline position gradually if no face detected if (prevX === null) { prevX = 2048 / 2; prevY = baselineY; } var newX = prevX * (1 - smoothingFactor) + 2048 / 2 * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + baselineY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } // Rotation tracking - CORRECTED VERSION if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } // Scale adjustment based on face size if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; // Adjusted divisor for dragon head // Update rolling average scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; // Calculate average scale var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; // Apply with gentle smoothing (limited range) var targetScale = Math.max(1.5, Math.min(2.5, avgScale)); head.scaleX = head.scaleX * 0.85 + targetScale * 0.15; head.scaleY = head.scaleY * 0.85 + targetScale * 0.15; headOpen.scaleX = head.scaleX; headOpen.scaleY = head.scaleY; } // Toggle dragon head assets based on mouth open state if (facekit && facekit.mouthOpen) { head.alpha = 0; headOpen.alpha = 1; } else { head.alpha = 1; headOpen.alpha = 0; } }; function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available } return self; }); var Enemy = Container.expand(function (type) { var self = Container.call(this); var assetId = 'enemy1'; if (type === 2) { assetId = 'enemy2'; } if (type === 3) { assetId = 'enemy3'; } self.type = type || 1; var enemyGraphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Different enemy types have different speeds and health switch (self.type) { case 1: self.speed = 2; self.health = 1; break; case 2: self.speed = 1.5; self.health = 2; break; case 3: self.speed = 3; self.health = 1; break; } self.activate = function (x, y, type) { self.type = type || 1; self.visible = true; self.toDestroy = false; // Set position from parameters self.x = x; self.y = y; // Reset scale to maximum when activated self.scaleX = 2.0; // Match maxScale from update method self.scaleY = 2.0; // Update the enemy graphic based on type var assetId = 'enemy1'; if (self.type === 2) { assetId = 'enemy2'; } else if (self.type === 3) { assetId = 'enemy3'; } // Update the existing enemy graphic or create a new one if (enemyGraphic) { enemyGraphic.destroy(); } enemyGraphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Set type-specific properties switch (self.type) { case 1: self.speed = 2; self.health = 1; break; case 2: self.speed = 1.5; self.health = 2; break; case 3: self.speed = 3; self.health = 1; break; } }; self.update = function () { self.y -= self.speed; // Enemies move up the screen toward the player // Only start scaling when actually on screen var maxDistance = 2732; // Screen height var minScale = 0.3; // Smallest size var maxScale = 2.0; // Largest size // Keep full scale until enemy is actually on screen if (self.y <= maxDistance) { var distancePercent = self.y / maxDistance; var newScale = Math.max(0.3, minScale + distancePercent * (maxScale - minScale)); // Ensure minimum scale // Apply scaling self.scaleX = newScale; self.scaleY = newScale; } else { // Keep maximum scale while off-screen self.scaleX = maxScale; self.scaleY = maxScale; } // Also ensure visibility is maintained self.visible = true; }; self.hit = function () { self.health--; if (self.health <= 0) { // Immediate visual feedback LK.effects.flashObject(enemyGraphic, 0xff0000, 200); // Play death effect and IMMEDIATELY recycle if (self.type === 1) { tween(enemyGraphic, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 500, easing: tween.easeOut }); } else if (self.type === 2) { tween(enemyGraphic, { rotation: Math.PI * 4, y: self.y + 300 }, { duration: 700, easing: tween.easeIn }); } else if (self.type === 3) { tween(enemyGraphic, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, easing: tween.easeOut }); } // Immediately recycle - no waiting for animation enemyPool.recycle(self); return true; } return false; }; self.deactivate = function () { self.visible = false; self.toDestroy = false; }; return self; }); var FireParticle = Container.expand(function () { var self = Container.call(this); var particle = self.attachAsset('fireParticle', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.x += self.vx; self.y += self.vy; self.age++; // Update alpha var lifePercent = self.age / self.lifespan; particle.alpha = 1 - lifePercent; // Recycle if lifetime is over OR particle is off screen if (lifePercent >= 1 || self.y < 0 || self.y > 2732 || // Screen height self.x < 0 || self.x > 2048) { // Screen width particlePool.recycle(self); } }; self.activate = function (x, y) { self.x = x; self.y = y; self.visible = true; self.vx = Math.random() * 4 - 2; self.vy = Math.random() * 2 + 1; self.lifespan = 20 + Math.random() * 20; self.age = 0; particle.alpha = 1; }; self.deactivate = function () { self.visible = false; }; return self; }); var Fireball = Container.expand(function () { var self = Container.call(this); var fireballGraphic = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.update = function () { if (!self.active) { return; } self.y += self.speed; // Create fire trail particles if (Math.random() < 0.3) { particlePool.spawn(self.x + (Math.random() * 40 - 20), self.y + 20); } // NEW CODE: Recycle when off screen if (self.y > 2732) { // Screen height fireballPool.recycle(self); } }; self.activate = function (x, y) { self.x = x; self.y = y; self.visible = true; self.active = true; }; self.deactivate = function () { self.visible = false; self.active = false; // Remove array manipulation from here }; return self; }); var FlyingBackground = Container.expand(function () { var self = Container.call(this); // Create a bottom container for field elements (will be rendered first/behind) self.bottomContainer = new Container(); self.addChild(self.bottomContainer); // Store the horizon line self.horizonY = 2732 / 2; self.totalHeight = 2732 - self.horizonY; self.fieldStripes = []; var nbStripes = 30; // Place first stripe slightly below horizon var lastY = self.horizonY + 10; var stripe = self.bottomContainer.attachAsset('fieldStripes', { anchorX: 0.5, anchorY: 0, x: 2048 / 2, y: lastY }); var progress = Math.max(0, lastY - self.horizonY) / self.totalHeight; stripe.height = 1 + 400 * progress; self.fieldStripes.push({ sprite: stripe, progress: progress }); // Place each subsequent stripe using same logic for (var i = 1; i < nbStripes; i++) { var spacing = 400 * (lastY - self.horizonY) / self.totalHeight; var y = lastY + spacing; var stripe = self.bottomContainer.attachAsset('fieldStripes', { anchorX: 0.5, anchorY: 0, x: 2048 / 2, y: y }); var progress = Math.max(0, y - self.horizonY) / self.totalHeight; stripe.height = 1 + 400 * progress; self.fieldStripes.push({ sprite: stripe, progress: progress }); lastY = y; } // Create a top container (will be rendered last/in front) self.topContainer = new Container(); self.addChild(self.topContainer); // Create sky background in top container var sky = self.topContainer.attachAsset('skyBackground', { anchorX: 0.5, anchorY: 1.0, x: 2048 / 2, y: self.horizonY }); // Field scroll speed self.scrollSpeed = 4; // Create clouds self.clouds = []; for (var i = 0; i < 8; i++) { // Start clouds with larger scale farther from horizon var startY = self.horizonY - 400 - Math.random() * 600; var distanceFromHorizon = Math.abs(startY - self.horizonY); var startScale = 0.5 + distanceFromHorizon / 800; var cloud = self.topContainer.attachAsset('cloudShape', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: startY, alpha: 0.8, scaleX: startScale, scaleY: startScale * 0.8 }); // Store with motion properties self.clouds.push({ sprite: cloud, speedY: 1 + Math.random() * 2, speedX: (Math.random() - 0.5) * 1.5, // Slight side-to-side drift targetY: self.horizonY }); } // Create field elements self.fieldElements = []; // Create initial field elements properly distributed for (var j = 0; j < 12; j++) { // Start with elements throughout the lower half of screen var fieldStartY = self.horizonY + Math.random() * (2732 - self.horizonY); var fieldDistanceFromHorizon = Math.abs(fieldStartY - self.horizonY); var fieldStartScale = 0.5 + fieldDistanceFromHorizon / 1000; var fieldElement = self.bottomContainer.attachAsset('fieldElement', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: fieldStartY, scaleX: fieldStartScale, scaleY: fieldStartScale * 0.8 }); // Store with fixed X position and Y-only movement self.fieldElements.push({ sprite: fieldElement, speedY: 2 + Math.random() * 3, initialX: fieldElement.x, targetY: self.horizonY }); } self.update = function () { for (var i = 0; i < self.fieldStripes.length; i++) { var stripe = self.fieldStripes[i]; stripe.progress = Math.max(0, stripe.sprite.y - self.horizonY) / self.totalHeight; var tempSpeed = self.scrollSpeed * 0.05 + self.scrollSpeed * (stripe.progress * 3); stripe.sprite.y -= tempSpeed; } // Sort stripes by Y position (lowest Y value - closest to top of screen - first) self.fieldStripes.sort(function (a, b) { return a.sprite.y - b.sprite.y; }); // Reset any stripes that have moved above horizon for (var i = 0; i < self.fieldStripes.length; i++) { if (self.fieldStripes[i].sprite.y < self.horizonY) { // Find the lowest stripe var lowestStripe = self.fieldStripes[self.fieldStripes.length - 1]; // Position this stripe below the lowest one self.fieldStripes[i].sprite.y = lowestStripe.sprite.y + 400 * (lowestStripe.sprite.y - self.horizonY) / self.totalHeight; // Recalculate progress self.fieldStripes[i].progress = Math.max(0, self.fieldStripes[i].sprite.y - self.horizonY) / self.totalHeight; } } // Re-sort after any repositioning self.fieldStripes.sort(function (a, b) { return a.sprite.y - b.sprite.y; }); // Adjust heights to ensure stripes connect perfectly for (var i = 0; i < self.fieldStripes.length - 1; i++) { var currentStripe = self.fieldStripes[i]; var nextStripe = self.fieldStripes[i + 1]; // Set height to exactly reach the next stripe currentStripe.sprite.height = nextStripe.sprite.y - currentStripe.sprite.y; } // Special case for the last stripe - extend it far enough var lastStripe = self.fieldStripes[self.fieldStripes.length - 1]; lastStripe.sprite.height = Math.max(1 + 400 * lastStripe.progress, self.horizonY + self.totalHeight - lastStripe.sprite.y + 100 // Add some buffer ); // Update clouds for (var i = 0; i < self.clouds.length; i++) { var cloud = self.clouds[i]; // Move cloud toward horizon cloud.sprite.y += cloud.speedY; cloud.sprite.x += cloud.speedX; // Calculate scale based on distance from horizon var distanceFactor = Math.max(0.05, (cloud.sprite.y - self.horizonY) / -500); cloud.sprite.scaleX = distanceFactor; cloud.sprite.scaleY = distanceFactor * 0.8; // Adjust alpha for fade effect near horizon cloud.sprite.alpha = Math.min(0.9, distanceFactor); // Reset cloud if it reaches horizon or gets too small if (cloud.sprite.y >= self.horizonY - 10 || cloud.sprite.scaleX < 0.1) { cloud.sprite.y = self.horizonY - 800 - Math.random() * 400; cloud.sprite.x = Math.random() * 2048; var newDistanceFactor = (cloud.sprite.y - self.horizonY) / -500; cloud.sprite.scaleX = newDistanceFactor; cloud.sprite.scaleY = newDistanceFactor * 0.8; cloud.sprite.alpha = 0.8; } } // Update field elements for (var j = 0; j < self.fieldElements.length; j++) { var element = self.fieldElements[j]; // Calculate progress based on distance from horizon var progress = Math.max(0, element.sprite.y - self.horizonY) / self.totalHeight; // Use the same scrolling speed calculation as the stripes var tempSpeed = self.scrollSpeed * 0.05 + self.scrollSpeed * (progress * 3); // Move field element toward horizon on Y axis only element.sprite.y -= tempSpeed; // Calculate scale based on distance from horizon var fieldDistanceFactor = Math.max(0.05, (element.sprite.y - self.horizonY) / 500); element.sprite.scaleX = fieldDistanceFactor; element.sprite.scaleY = fieldDistanceFactor * 0.8; // Adjust alpha for fade effect near horizon element.sprite.alpha = Math.min(0.9, fieldDistanceFactor); // Reset field element if it reaches horizon if (element.sprite.y <= self.horizonY + 10 || element.sprite.scaleX < 0.1) { // Spawn from bottom of screen element.sprite.y = 2732 + Math.random() * 200; // Randomize X position only on respawn element.initialX = Math.random() * 2048; element.sprite.x = element.initialX; var newFieldDistanceFactor = (element.sprite.y - self.horizonY) / 500; element.sprite.scaleX = newFieldDistanceFactor; element.sprite.scaleY = newFieldDistanceFactor * 0.8; element.sprite.alpha = 0.9; } } }; return self; }); var ObjectPool = Container.expand(function (ObjectClass, size) { var self = Container.call(this); var objects = []; var activeCount = 0; for (var i = 0; i < size; i++) { var obj = new ObjectClass(); obj.visible = false; obj._poolIndex = i; objects.push(obj); self.addChild(obj); } self.spawn = function (x, y, params) { if (activeCount >= objects.length) { return null; } var obj = objects[activeCount]; activeCount++; // Increment AFTER getting object obj.visible = true; obj.activate(x, y, params); return obj; }; self.recycle = function (obj) { if (!obj || obj._poolIndex >= activeCount) { return; } // Already recycled activeCount--; // Decrement count // Swap with last active object if needed if (obj._poolIndex < activeCount) { var temp = objects[activeCount]; objects[activeCount] = obj; objects[obj._poolIndex] = temp; // Update indices temp._poolIndex = obj._poolIndex; obj._poolIndex = activeCount; } obj.deactivate(); obj.visible = false; }; self.update = function () { for (var i = 0; i < activeCount; i++) { objects[i].update(); } }; self.getActiveObjects = function () { return objects.slice(0, activeCount); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ function processCollisions() { var fireballs = fireballPool.getActiveObjects(); var enemies = enemyPool.getActiveObjects(); for (var i = 0; i < fireballs.length; i++) { var fireball = fireballs[i]; for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; var dx = fireball.x - enemy.x; var dy = fireball.y - enemy.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100 * enemy.scaleX) { // Adjusted collision radius if (enemy.hit()) { score += enemy.type * 10; scoreTxt.setText(score); LK.setScore(score); LK.getSound('enemyDefeat').play(); } fireballPool.recycle(fireball); break; } } } } // Set dark blue background for sky effect game.setBackgroundColor(0x2c3e50); // Game state variables var score = 0; var fireballPool = new ObjectPool(Fireball, 100); var particlePool = new ObjectPool(FireParticle, 400); var enemyPool = new ObjectPool(Enemy, 50); var gameActive = true; var isFiring = false; var lastFireTime = 0; var fireRate = 150; // ms between fireballs var enemySpawnRate = 60; // Frames between enemy spawns var difficultyScaling = 0; var flyingBackground = new FlyingBackground(); game.addChild(flyingBackground); // Create dragon head (player character) var dragon = game.addChild(new DragonHead()); dragon.x = 2048 / 2; dragon.y = 2732 * 0.2; // Place dragon at top fifth of screen game.addChild(fireballPool); game.addChild(particlePool); game.addChild(enemyPool); // Score display var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 50; LK.gui.top.addChild(scoreTxt); // Create a function to add fire particles function createFireParticle(x, y) { particlePool.spawn(x, y); } // Function to spawn a fireball function spawnFireball() { if (!gameActive) { return; } var now = Date.now(); if (now - lastFireTime < fireRate) { return; } lastFireTime = now; // Use the new pool's spawn method var fireball = fireballPool.spawn(dragon.x, dragon.y + 50); // Play sound LK.getSound('firebreathSound').play(); } // Function to spawn enemies function spawnEnemy() { if (!gameActive) { return; } var type = Math.floor(Math.random() * 3) + 1; var enemy = enemyPool.spawn(Math.random() * (2048 - 200) + 100, 2732 + 100, type); if (enemy) { enemy.activate(enemy.x, enemy.y, type); // Ensure enemy is activated with correct parameters enemy.visible = true; // Force visibility } } // Game update logic game.update = function () { if (!gameActive) { return; } // Check if mouth is open for fire breathing if (facekit && facekit.mouthOpen) { isFiring = true; spawnFireball(); } else { isFiring = false; } // Spawn enemies at a constant rate if (LK.ticks % enemySpawnRate === 0) { spawnEnemy(); } // Update all pools fireballPool.update(); enemyPool.update(); particlePool.update(); // Process collisions processCollisions(); }; // Start background music LK.playMusic('gameMusic', { fade: { start: 0, end: 0.3, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var DragonHead = Container.expand(function () {
var self = Container.call(this);
// Dragon sprites
var body = self.attachAsset('dragonBody', {
anchorX: 0.5,
anchorY: 0.75,
scaleX: 2,
scaleY: 2
});
var head = self.attachAsset('dragonHead', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
var headOpen = self.attachAsset('dragonHeadOpen', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: 0
});
// Position tracking variables
var baselineY = 2732 * 0.2; // Default position in top fifth
var targetX = 2048 / 2;
var targetY = baselineY;
var maxY = 2732 * 0.4; // Only allow in top 40% of screen
var smoothingFactor = 0.12;
var prevX = null;
var prevY = null;
// Rotation variables
var targetTilt = 0;
var tiltSmoothingFactor = 0.11;
var tiltScaleFactor = 0.09;
// Scale tracking
var scaleHistory = new Array(5).fill(2); // Start with default scale of 2
var scaleIndex = 0;
self.update = function () {
// Position tracking
if (facekit.noseTip) {
targetX = facekit.noseTip.x;
// Limit how far down the dragon can go
targetY = Math.min(facekit.noseTip.y, maxY);
// Initialize previous positions if not set
if (prevX === null) {
prevX = targetX;
prevY = targetY;
}
// Weighted average between previous and target position
var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor;
var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor;
self.x = newX;
self.y = newY;
// Update previous positions
prevX = newX;
prevY = newY;
} else {
// Return to baseline position gradually if no face detected
if (prevX === null) {
prevX = 2048 / 2;
prevY = baselineY;
}
var newX = prevX * (1 - smoothingFactor) + 2048 / 2 * smoothingFactor;
var newY = prevY * (1 - smoothingFactor) + baselineY * smoothingFactor;
self.x = newX;
self.y = newY;
// Update previous positions
prevX = newX;
prevY = newY;
}
// Rotation tracking - CORRECTED VERSION
if (facekit.leftEye && facekit.rightEye) {
targetTilt = calculateFaceTilt() * tiltScaleFactor;
// Limit rotation to ±15 degrees - DON'T convert to radians here
targetTilt = Math.max(-15, Math.min(15, targetTilt));
self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor;
}
// Scale adjustment based on face size
if (facekit.leftEye && facekit.rightEye) {
var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x);
var newScale = eyeDistance / 250; // Adjusted divisor for dragon head
// Update rolling average
scaleHistory[scaleIndex] = newScale;
scaleIndex = (scaleIndex + 1) % scaleHistory.length;
// Calculate average scale
var avgScale = scaleHistory.reduce(function (a, b) {
return a + b;
}, 0) / scaleHistory.length;
// Apply with gentle smoothing (limited range)
var targetScale = Math.max(1.5, Math.min(2.5, avgScale));
head.scaleX = head.scaleX * 0.85 + targetScale * 0.15;
head.scaleY = head.scaleY * 0.85 + targetScale * 0.15;
headOpen.scaleX = head.scaleX;
headOpen.scaleY = head.scaleY;
}
// Toggle dragon head assets based on mouth open state
if (facekit && facekit.mouthOpen) {
head.alpha = 0;
headOpen.alpha = 1;
} else {
head.alpha = 1;
headOpen.alpha = 0;
}
};
function calculateFaceTilt() {
if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) {
// Calculate midpoint between eyes
var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2;
var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2;
// Calculate angle between eye midpoint and mouth, negated to fix direction
var dx = facekit.mouthCenter.x - eyeMidX;
var dy = facekit.mouthCenter.y - eyeMidY;
var angle = -(Math.atan2(dx, dy) * (180 / Math.PI));
// Reduced angle impact
return Math.max(-15, Math.min(15, angle * 0.15));
}
return 0; // Default to straight when face points aren't available
}
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
var assetId = 'enemy1';
if (type === 2) {
assetId = 'enemy2';
}
if (type === 3) {
assetId = 'enemy3';
}
self.type = type || 1;
var enemyGraphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Different enemy types have different speeds and health
switch (self.type) {
case 1:
self.speed = 2;
self.health = 1;
break;
case 2:
self.speed = 1.5;
self.health = 2;
break;
case 3:
self.speed = 3;
self.health = 1;
break;
}
self.activate = function (x, y, type) {
self.type = type || 1;
self.visible = true;
self.toDestroy = false;
// Set position from parameters
self.x = x;
self.y = y;
// Reset scale to maximum when activated
self.scaleX = 2.0; // Match maxScale from update method
self.scaleY = 2.0;
// Update the enemy graphic based on type
var assetId = 'enemy1';
if (self.type === 2) {
assetId = 'enemy2';
} else if (self.type === 3) {
assetId = 'enemy3';
}
// Update the existing enemy graphic or create a new one
if (enemyGraphic) {
enemyGraphic.destroy();
}
enemyGraphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Set type-specific properties
switch (self.type) {
case 1:
self.speed = 2;
self.health = 1;
break;
case 2:
self.speed = 1.5;
self.health = 2;
break;
case 3:
self.speed = 3;
self.health = 1;
break;
}
};
self.update = function () {
self.y -= self.speed; // Enemies move up the screen toward the player
// Only start scaling when actually on screen
var maxDistance = 2732; // Screen height
var minScale = 0.3; // Smallest size
var maxScale = 2.0; // Largest size
// Keep full scale until enemy is actually on screen
if (self.y <= maxDistance) {
var distancePercent = self.y / maxDistance;
var newScale = Math.max(0.3, minScale + distancePercent * (maxScale - minScale)); // Ensure minimum scale
// Apply scaling
self.scaleX = newScale;
self.scaleY = newScale;
} else {
// Keep maximum scale while off-screen
self.scaleX = maxScale;
self.scaleY = maxScale;
}
// Also ensure visibility is maintained
self.visible = true;
};
self.hit = function () {
self.health--;
if (self.health <= 0) {
// Immediate visual feedback
LK.effects.flashObject(enemyGraphic, 0xff0000, 200);
// Play death effect and IMMEDIATELY recycle
if (self.type === 1) {
tween(enemyGraphic, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 500,
easing: tween.easeOut
});
} else if (self.type === 2) {
tween(enemyGraphic, {
rotation: Math.PI * 4,
y: self.y + 300
}, {
duration: 700,
easing: tween.easeIn
});
} else if (self.type === 3) {
tween(enemyGraphic, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
easing: tween.easeOut
});
}
// Immediately recycle - no waiting for animation
enemyPool.recycle(self);
return true;
}
return false;
};
self.deactivate = function () {
self.visible = false;
self.toDestroy = false;
};
return self;
});
var FireParticle = Container.expand(function () {
var self = Container.call(this);
var particle = self.attachAsset('fireParticle', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.age++;
// Update alpha
var lifePercent = self.age / self.lifespan;
particle.alpha = 1 - lifePercent;
// Recycle if lifetime is over OR particle is off screen
if (lifePercent >= 1 || self.y < 0 || self.y > 2732 ||
// Screen height
self.x < 0 || self.x > 2048) {
// Screen width
particlePool.recycle(self);
}
};
self.activate = function (x, y) {
self.x = x;
self.y = y;
self.visible = true;
self.vx = Math.random() * 4 - 2;
self.vy = Math.random() * 2 + 1;
self.lifespan = 20 + Math.random() * 20;
self.age = 0;
particle.alpha = 1;
};
self.deactivate = function () {
self.visible = false;
};
return self;
});
var Fireball = Container.expand(function () {
var self = Container.call(this);
var fireballGraphic = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.update = function () {
if (!self.active) {
return;
}
self.y += self.speed;
// Create fire trail particles
if (Math.random() < 0.3) {
particlePool.spawn(self.x + (Math.random() * 40 - 20), self.y + 20);
}
// NEW CODE: Recycle when off screen
if (self.y > 2732) {
// Screen height
fireballPool.recycle(self);
}
};
self.activate = function (x, y) {
self.x = x;
self.y = y;
self.visible = true;
self.active = true;
};
self.deactivate = function () {
self.visible = false;
self.active = false;
// Remove array manipulation from here
};
return self;
});
var FlyingBackground = Container.expand(function () {
var self = Container.call(this);
// Create a bottom container for field elements (will be rendered first/behind)
self.bottomContainer = new Container();
self.addChild(self.bottomContainer);
// Store the horizon line
self.horizonY = 2732 / 2;
self.totalHeight = 2732 - self.horizonY;
self.fieldStripes = [];
var nbStripes = 30;
// Place first stripe slightly below horizon
var lastY = self.horizonY + 10;
var stripe = self.bottomContainer.attachAsset('fieldStripes', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: lastY
});
var progress = Math.max(0, lastY - self.horizonY) / self.totalHeight;
stripe.height = 1 + 400 * progress;
self.fieldStripes.push({
sprite: stripe,
progress: progress
});
// Place each subsequent stripe using same logic
for (var i = 1; i < nbStripes; i++) {
var spacing = 400 * (lastY - self.horizonY) / self.totalHeight;
var y = lastY + spacing;
var stripe = self.bottomContainer.attachAsset('fieldStripes', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: y
});
var progress = Math.max(0, y - self.horizonY) / self.totalHeight;
stripe.height = 1 + 400 * progress;
self.fieldStripes.push({
sprite: stripe,
progress: progress
});
lastY = y;
}
// Create a top container (will be rendered last/in front)
self.topContainer = new Container();
self.addChild(self.topContainer);
// Create sky background in top container
var sky = self.topContainer.attachAsset('skyBackground', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: self.horizonY
});
// Field scroll speed
self.scrollSpeed = 4;
// Create clouds
self.clouds = [];
for (var i = 0; i < 8; i++) {
// Start clouds with larger scale farther from horizon
var startY = self.horizonY - 400 - Math.random() * 600;
var distanceFromHorizon = Math.abs(startY - self.horizonY);
var startScale = 0.5 + distanceFromHorizon / 800;
var cloud = self.topContainer.attachAsset('cloudShape', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: startY,
alpha: 0.8,
scaleX: startScale,
scaleY: startScale * 0.8
});
// Store with motion properties
self.clouds.push({
sprite: cloud,
speedY: 1 + Math.random() * 2,
speedX: (Math.random() - 0.5) * 1.5,
// Slight side-to-side drift
targetY: self.horizonY
});
}
// Create field elements
self.fieldElements = [];
// Create initial field elements properly distributed
for (var j = 0; j < 12; j++) {
// Start with elements throughout the lower half of screen
var fieldStartY = self.horizonY + Math.random() * (2732 - self.horizonY);
var fieldDistanceFromHorizon = Math.abs(fieldStartY - self.horizonY);
var fieldStartScale = 0.5 + fieldDistanceFromHorizon / 1000;
var fieldElement = self.bottomContainer.attachAsset('fieldElement', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: fieldStartY,
scaleX: fieldStartScale,
scaleY: fieldStartScale * 0.8
});
// Store with fixed X position and Y-only movement
self.fieldElements.push({
sprite: fieldElement,
speedY: 2 + Math.random() * 3,
initialX: fieldElement.x,
targetY: self.horizonY
});
}
self.update = function () {
for (var i = 0; i < self.fieldStripes.length; i++) {
var stripe = self.fieldStripes[i];
stripe.progress = Math.max(0, stripe.sprite.y - self.horizonY) / self.totalHeight;
var tempSpeed = self.scrollSpeed * 0.05 + self.scrollSpeed * (stripe.progress * 3);
stripe.sprite.y -= tempSpeed;
}
// Sort stripes by Y position (lowest Y value - closest to top of screen - first)
self.fieldStripes.sort(function (a, b) {
return a.sprite.y - b.sprite.y;
});
// Reset any stripes that have moved above horizon
for (var i = 0; i < self.fieldStripes.length; i++) {
if (self.fieldStripes[i].sprite.y < self.horizonY) {
// Find the lowest stripe
var lowestStripe = self.fieldStripes[self.fieldStripes.length - 1];
// Position this stripe below the lowest one
self.fieldStripes[i].sprite.y = lowestStripe.sprite.y + 400 * (lowestStripe.sprite.y - self.horizonY) / self.totalHeight;
// Recalculate progress
self.fieldStripes[i].progress = Math.max(0, self.fieldStripes[i].sprite.y - self.horizonY) / self.totalHeight;
}
}
// Re-sort after any repositioning
self.fieldStripes.sort(function (a, b) {
return a.sprite.y - b.sprite.y;
});
// Adjust heights to ensure stripes connect perfectly
for (var i = 0; i < self.fieldStripes.length - 1; i++) {
var currentStripe = self.fieldStripes[i];
var nextStripe = self.fieldStripes[i + 1];
// Set height to exactly reach the next stripe
currentStripe.sprite.height = nextStripe.sprite.y - currentStripe.sprite.y;
}
// Special case for the last stripe - extend it far enough
var lastStripe = self.fieldStripes[self.fieldStripes.length - 1];
lastStripe.sprite.height = Math.max(1 + 400 * lastStripe.progress, self.horizonY + self.totalHeight - lastStripe.sprite.y + 100 // Add some buffer
);
// Update clouds
for (var i = 0; i < self.clouds.length; i++) {
var cloud = self.clouds[i];
// Move cloud toward horizon
cloud.sprite.y += cloud.speedY;
cloud.sprite.x += cloud.speedX;
// Calculate scale based on distance from horizon
var distanceFactor = Math.max(0.05, (cloud.sprite.y - self.horizonY) / -500);
cloud.sprite.scaleX = distanceFactor;
cloud.sprite.scaleY = distanceFactor * 0.8;
// Adjust alpha for fade effect near horizon
cloud.sprite.alpha = Math.min(0.9, distanceFactor);
// Reset cloud if it reaches horizon or gets too small
if (cloud.sprite.y >= self.horizonY - 10 || cloud.sprite.scaleX < 0.1) {
cloud.sprite.y = self.horizonY - 800 - Math.random() * 400;
cloud.sprite.x = Math.random() * 2048;
var newDistanceFactor = (cloud.sprite.y - self.horizonY) / -500;
cloud.sprite.scaleX = newDistanceFactor;
cloud.sprite.scaleY = newDistanceFactor * 0.8;
cloud.sprite.alpha = 0.8;
}
}
// Update field elements
for (var j = 0; j < self.fieldElements.length; j++) {
var element = self.fieldElements[j];
// Calculate progress based on distance from horizon
var progress = Math.max(0, element.sprite.y - self.horizonY) / self.totalHeight;
// Use the same scrolling speed calculation as the stripes
var tempSpeed = self.scrollSpeed * 0.05 + self.scrollSpeed * (progress * 3);
// Move field element toward horizon on Y axis only
element.sprite.y -= tempSpeed;
// Calculate scale based on distance from horizon
var fieldDistanceFactor = Math.max(0.05, (element.sprite.y - self.horizonY) / 500);
element.sprite.scaleX = fieldDistanceFactor;
element.sprite.scaleY = fieldDistanceFactor * 0.8;
// Adjust alpha for fade effect near horizon
element.sprite.alpha = Math.min(0.9, fieldDistanceFactor);
// Reset field element if it reaches horizon
if (element.sprite.y <= self.horizonY + 10 || element.sprite.scaleX < 0.1) {
// Spawn from bottom of screen
element.sprite.y = 2732 + Math.random() * 200;
// Randomize X position only on respawn
element.initialX = Math.random() * 2048;
element.sprite.x = element.initialX;
var newFieldDistanceFactor = (element.sprite.y - self.horizonY) / 500;
element.sprite.scaleX = newFieldDistanceFactor;
element.sprite.scaleY = newFieldDistanceFactor * 0.8;
element.sprite.alpha = 0.9;
}
}
};
return self;
});
var ObjectPool = Container.expand(function (ObjectClass, size) {
var self = Container.call(this);
var objects = [];
var activeCount = 0;
for (var i = 0; i < size; i++) {
var obj = new ObjectClass();
obj.visible = false;
obj._poolIndex = i;
objects.push(obj);
self.addChild(obj);
}
self.spawn = function (x, y, params) {
if (activeCount >= objects.length) {
return null;
}
var obj = objects[activeCount];
activeCount++; // Increment AFTER getting object
obj.visible = true;
obj.activate(x, y, params);
return obj;
};
self.recycle = function (obj) {
if (!obj || obj._poolIndex >= activeCount) {
return;
} // Already recycled
activeCount--; // Decrement count
// Swap with last active object if needed
if (obj._poolIndex < activeCount) {
var temp = objects[activeCount];
objects[activeCount] = obj;
objects[obj._poolIndex] = temp;
// Update indices
temp._poolIndex = obj._poolIndex;
obj._poolIndex = activeCount;
}
obj.deactivate();
obj.visible = false;
};
self.update = function () {
for (var i = 0; i < activeCount; i++) {
objects[i].update();
}
};
self.getActiveObjects = function () {
return objects.slice(0, activeCount);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function processCollisions() {
var fireballs = fireballPool.getActiveObjects();
var enemies = enemyPool.getActiveObjects();
for (var i = 0; i < fireballs.length; i++) {
var fireball = fireballs[i];
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
var dx = fireball.x - enemy.x;
var dy = fireball.y - enemy.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100 * enemy.scaleX) {
// Adjusted collision radius
if (enemy.hit()) {
score += enemy.type * 10;
scoreTxt.setText(score);
LK.setScore(score);
LK.getSound('enemyDefeat').play();
}
fireballPool.recycle(fireball);
break;
}
}
}
}
// Set dark blue background for sky effect
game.setBackgroundColor(0x2c3e50);
// Game state variables
var score = 0;
var fireballPool = new ObjectPool(Fireball, 100);
var particlePool = new ObjectPool(FireParticle, 400);
var enemyPool = new ObjectPool(Enemy, 50);
var gameActive = true;
var isFiring = false;
var lastFireTime = 0;
var fireRate = 150; // ms between fireballs
var enemySpawnRate = 60; // Frames between enemy spawns
var difficultyScaling = 0;
var flyingBackground = new FlyingBackground();
game.addChild(flyingBackground);
// Create dragon head (player character)
var dragon = game.addChild(new DragonHead());
dragon.x = 2048 / 2;
dragon.y = 2732 * 0.2; // Place dragon at top fifth of screen
game.addChild(fireballPool);
game.addChild(particlePool);
game.addChild(enemyPool);
// Score display
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 50;
LK.gui.top.addChild(scoreTxt);
// Create a function to add fire particles
function createFireParticle(x, y) {
particlePool.spawn(x, y);
}
// Function to spawn a fireball
function spawnFireball() {
if (!gameActive) {
return;
}
var now = Date.now();
if (now - lastFireTime < fireRate) {
return;
}
lastFireTime = now;
// Use the new pool's spawn method
var fireball = fireballPool.spawn(dragon.x, dragon.y + 50);
// Play sound
LK.getSound('firebreathSound').play();
}
// Function to spawn enemies
function spawnEnemy() {
if (!gameActive) {
return;
}
var type = Math.floor(Math.random() * 3) + 1;
var enemy = enemyPool.spawn(Math.random() * (2048 - 200) + 100, 2732 + 100, type);
if (enemy) {
enemy.activate(enemy.x, enemy.y, type); // Ensure enemy is activated with correct parameters
enemy.visible = true; // Force visibility
}
}
// Game update logic
game.update = function () {
if (!gameActive) {
return;
}
// Check if mouth is open for fire breathing
if (facekit && facekit.mouthOpen) {
isFiring = true;
spawnFireball();
} else {
isFiring = false;
}
// Spawn enemies at a constant rate
if (LK.ticks % enemySpawnRate === 0) {
spawnEnemy();
}
// Update all pools
fireballPool.update();
enemyPool.update();
particlePool.update();
// Process collisions
processCollisions();
};
// Start background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
A clear blue sky background with no clouds.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a small bush. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a wooden arrow with red feathers and a metal arrow head. Completely vertical orientation. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A small vertical flame. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a black scorch mark on the ground left by a meteor impact. cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A bright spark. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a red heart. cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
An SVG of the word **BOSS** in sharp red font.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
An SVG of the word “Start” written in fire. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows