User prompt
update boss as needed with: var Boss = Container.expand(function() { var self = Container.call(this); // States var ENTERING = 'entering'; var WALKING = 'walking'; var ARROW_ATTACK = 'arrow_attack'; var CHARGING_LEFT = 'charging_left'; var CHARGING_RIGHT = 'charging_right'; var FIST_LEFT = 'fist_left'; var FIST_RIGHT = 'fist_right'; var RETRACTING_LEFT = 'retracting_left'; var RETRACTING_RIGHT = 'retracting_right'; var DYING = 'dying'; // Main sprite var body = self.attachAsset('boss1', { anchorX: 0.5, anchorY: 0, scale: 2 }); // Properties self.health = 100; self.state = ENTERING; self.stateDuration = 0; self.bobOffset = 0; self.bobSpeed = 0.02; self.lastBobOffset = 0; self.activeFist = null; self.attackTimer = 0; self.attackDelay = 120; // Position at creation self.x = 2048 / 2; self.y = 2732; // Start at bottom of screen // Update logic self.update = function() { self.stateDuration++; self.bobOffset = Math.sin(LK.ticks * self.bobSpeed) * 15; switch(self.state) { case ENTERING: self.y -= 2; if (self.y <= 2732 / 2) { self.state = WALKING; self.attackTimer = self.attackDelay; } break; // ... rest of the states remain the same ... } self.lastBobOffset = self.bobOffset; }; self.damage = function(amount) { self.health -= amount; bossHealthBar.updateHealth(self.health / 100); body.tint = 0xFF0000; LK.setTimeout(function() { body.tint = 0xFFFFFF; }, 5); if (self.health <= 0 && self.state !== DYING) { self.state = DYING; self.stateDuration = 0; game.shake(20, 1000); } }; return self; });
Code edit (22 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: boss is null' in or related to this line: 'boss.update();' Line Number: 2110
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
add more variance
User prompt
add a bit of variance to defeat particle scale
User prompt
reduce their velocity a little bit
User prompt
increas the velocity of defeat particles
User prompt
increase the life time of defeat particles
User prompt
increase the amount of defeat particles spawned when enemy or enemytwo is defeated
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
update enemy two class as needed with: self.update = function() { if (!self.visible) return; switch(self.state) { case 'approaching': // existing approaching code... break; case 'holding': // existing holding code... break; case 'falling': self.rotation += self.rotationSpeed; self.fallSpeed += self.acceleration; self.y += self.fallSpeed; // Check for ground collision if (self.y >= dragonShadow.y && !self.hasExploded) { self.hasExploded = true; for (var i = 0; i < 20; i++) { defeatParticlePool.spawn(self.x, self.y, 0xff0000); } enemyTwoPool.recycle(self); } break; } if (self.shadow) { self.shadow.update(self); } };
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (6 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: enemy is undefined' in or related to this line: 'if (!enemy.visible) {' Line Number: 609
User prompt
Please fix the bug: 'TypeError: enemy is undefined' in or related to this line: 'if (!enemy.visible) {' Line Number: 609
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
triple the lifespan of defeat particles
Code edit (2 edits merged)
Please save this source code
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Cloud = Container.expand(function () {
var self = Container.call(this);
var cloud = self.attachAsset('cloudShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.activate = function (x, y, scale, alpha) {
self.x = x;
self.y = y;
self.visible = true;
self.speedY = 1 + Math.random() * 2;
self.speedX = (Math.random() - 0.5) * 1.5;
self.targetY = self.horizonY;
cloud.scaleX = scale;
cloud.scaleY = scale * 0.8;
cloud.alpha = alpha || 0.8;
};
self.deactivate = function () {
self.visible = false;
};
return self;
});
var DefeatParticle = Container.expand(function () {
var self = Container.call(this);
var particle = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15
});
self.activate = function (x, y, tint) {
self.x = x + (Math.random() * 40 - 20); // Wider initial spread
self.y = y + (Math.random() * 40 - 20); // Wider initial spread
self.visible = true;
self.age = 0;
self.lifespan = 75; // Tripled lifespan for longer visual impact
// More dramatic explosion velocities
var angle = Math.PI * 2 * Math.random();
var speed = 18 + Math.random() * 20; // Much higher speed for bigger burst
self.vx = Math.cos(angle) * speed;
self.vy = Math.sin(angle) * speed;
self.rotation = Math.random() * Math.PI * 2;
particle.alpha = 1;
// Larger, more varied particle sizes
particle.scaleX = 0.4 + Math.random() * 0.4;
particle.scaleY = 0.4 + Math.random() * 0.4;
// Apply tint if provided
if (tint !== undefined) {
particle.tint = tint;
} else {
particle.tint = 0xffffff; // Default white
}
};
self.update = function () {
// Check bounds first
if (self.y < -50 || self.y > 2732 || self.x < -50 || self.x > 2048) {
defeatParticlePool.recycle(self);
return;
}
self.x += self.vx;
self.y += self.vy;
self.age++;
// Keep strong deceleration
self.vx *= 0.87;
self.vy *= 0.87;
var lifePercent = self.age / self.lifespan;
// Fade out curve
particle.alpha = Math.max(0, 1 - lifePercent * 1.7);
// More dramatic shrink effect as particles fade
var scale = (0.5 - 0.3 * lifePercent) * (1 - lifePercent * 0.5);
particle.scaleX = scale;
particle.scaleY = scale;
if (lifePercent >= 1) {
defeatParticlePool.recycle(self);
}
};
self.deactivate = function () {
self.visible = false;
// Reset tint when deactivated
particle.tint = 0xffffff;
};
return self;
});
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
});
var lookLeft = self.attachAsset('dragonLook', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: -2,
// Mirror for left direction
scaleY: 2,
alpha: 0
});
var lookRight = self.attachAsset('dragonLook', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: 0
});
var lookLeftOpen = self.attachAsset('dragonLookOpen', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: -2,
// Mirror for left direction
scaleY: 2,
alpha: 0
});
var lookRightOpen = self.attachAsset('dragonLookOpen', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: 0
});
self.currentScale = 2; // Starting scale
var horizontalSpeed = 0;
// Position tracking variables
var targetX = 2048 / 2;
var targetY = 2732 * 0.2;
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;
targetY = facekit.noseTip.y - 400;
// 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;
if (prevX !== null) {
horizontalSpeed = Math.abs(self.x - prevX);
}
self.currentShakeSpeed = horizontalSpeed;
// 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;
}
if (facekit.leftEye && facekit.rightEye && facekit.noseTip) {
// Calculate face direction by comparing horizontal positions
var faceCenter = (facekit.leftEye.x + facekit.rightEye.x) / 2;
var noseDiff = facekit.noseTip.x - faceCenter;
// Calculate eye alignment for head rotation
var eyeYDiff = facekit.rightEye.y - facekit.leftEye.y;
// Reset all direction indicators
lookLeft.alpha = 0;
lookRight.alpha = 0;
lookLeftOpen.alpha = 0;
lookRightOpen.alpha = 0;
// Combined detection logic
var showingTurnedHead = false;
if (noseDiff < -120 && Math.abs(eyeYDiff) < 30 || eyeYDiff > 40) {
// Looking left (nose to left OR right eye lower)
if (facekit.mouthOpen) {
lookLeftOpen.alpha = 1;
} else {
lookLeft.alpha = 1;
}
showingTurnedHead = true;
} else if (noseDiff > 120 && Math.abs(eyeYDiff) < 30 || eyeYDiff < -40) {
// Looking right (nose to right OR left eye lower)
if (facekit.mouthOpen) {
lookRightOpen.alpha = 1;
} else {
lookRight.alpha = 1;
}
showingTurnedHead = true;
}
// Set original head visibility based on whether a turned head is showing
if (showingTurnedHead) {
head.alpha = 0;
headOpen.alpha = 0;
} else {
// Only then handle regular head visibility based on mouth state
if (facekit && facekit.mouthOpen) {
head.alpha = 0;
headOpen.alpha = 1;
} else {
head.alpha = 1;
headOpen.alpha = 0;
}
}
}
// 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;
self.currentScale = head.scaleX; // Track the current scale
head.scaleY = head.scaleY * 0.85 + targetScale * 0.15;
headOpen.scaleX = headOpen.scaleX * 0.85 + targetScale * 0.15;
headOpen.scaleY = headOpen.scaleY * 0.85 + targetScale * 0.15;
body.scaleX = body.scaleX * 0.85 + targetScale * 0.15;
body.scaleY = body.scaleY * 0.85 + targetScale * 0.15;
lookLeft.scaleX = lookLeft.scaleX * 0.85 - targetScale * 0.15;
lookLeft.scaleY = lookLeft.scaleY * 0.85 + targetScale * 0.15;
lookRight.scaleX = lookRight.scaleX * 0.85 + targetScale * 0.15;
lookRight.scaleY = lookRight.scaleY * 0.85 + targetScale * 0.15;
lookLeftOpen.scaleX = lookLeftOpen.scaleX * 0.85 - targetScale * 0.15;
lookLeftOpen.scaleY = lookLeftOpen.scaleY * 0.85 + targetScale * 0.15;
lookRightOpen.scaleX = lookRightOpen.scaleX * 0.85 + targetScale * 0.15;
lookRightOpen.scaleY = lookRightOpen.scaleY * 0.85 + targetScale * 0.15;
}
};
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
}
self.flashDamage = function () {
var dragonSprites = [body]; // body
// Add whichever head sprite is currently visible
if (head && head.alpha > 0) {
dragonSprites.push(head);
}
if (headOpen && headOpen.alpha > 0) {
dragonSprites.push(headOpen);
}
if (lookLeft && lookLeft.alpha > 0) {
dragonSprites.push(lookLeft);
}
if (lookRight && lookRight.alpha > 0) {
dragonSprites.push(lookRight);
}
if (lookLeftOpen && lookLeftOpen.alpha > 0) {
dragonSprites.push(lookLeftOpen);
}
if (lookRightOpen && lookRightOpen.alpha > 0) {
dragonSprites.push(lookRightOpen);
}
dragonSprites.forEach(function (sprite) {
sprite.tint = 0xFF0000;
LK.setTimeout(function () {
sprite.tint = 0xFFFFFF;
}, 10);
});
};
return self;
});
var DragonShadow = Container.expand(function () {
var self = Container.call(this);
var shadow = self.attachAsset('dragonShadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5,
scaleX: 1,
scaleY: 0.3
});
var positionSmoothing = 0.12;
var scaleSmoothing = 0.08;
self.update = function (dragonX, dragonY, dragonScale) {
if (dragonX !== undefined && dragonY !== undefined && dragonScale !== undefined) {
// X position follow
self.x += (dragonX - self.x) * positionSmoothing;
// Y position based on scale (Z-depth)
var baseY = 2732 * 0.6;
var scaleOffset = (dragonScale - 1) * 300;
var targetY = baseY + scaleOffset;
self.y += (targetY - self.y) * positionSmoothing;
// Scale calculation based on dragon's Y position
var heightRatio = dragonY / 2732;
var scaleMultiplier = 0.2 + heightRatio * 1.5;
// Add Y-position based size adjustment (0-20% increase based on Y position)
var yPositionScale = 1 + self.y / 2732 * 0.2;
var targetScaleX = scaleMultiplier * yPositionScale;
var targetScaleY = scaleMultiplier * 0.3 * yPositionScale;
var shadow = self.getChildAt(0); // Get the shadow sprite
shadow.scaleX += (targetScaleX - shadow.scaleX) * scaleSmoothing;
shadow.scaleY += (targetScaleY - shadow.scaleY) * scaleSmoothing;
shadow.alpha = 0.3 + heightRatio * 0.2;
}
};
return self;
});
var DustParticle = Container.expand(function () {
var self = Container.call(this);
var dust = self.attachAsset('dust', {
anchorX: 0.5,
anchorY: 0.5
});
self.activate = function (x, y) {
self.x = x + (Math.random() * 20 - 10);
self.y = y;
self.visible = true;
self.age = 0;
self.lifespan = 60; // Fixed lifespan for consistency
dust.alpha = 0.9;
// Vertical velocity - move upward
self.vy = -1.5; // Faster upward movement
self.vx = (Math.random() - 0.5) * 5; // Slightly more horizontal movement
// Add rotation for visual interest
self.rotation = Math.random() * Math.PI * 2;
};
self.update = function () {
// Update position
self.x += self.vx;
self.y += self.vy;
self.age++;
// Calculate life percentage
var lifePercent = self.age / self.lifespan;
// Fade out
dust.alpha = 0.8 * (1 - lifePercent);
// Grow slightly as it rises
var scale = 1 + lifePercent * 0.2; // Linear growth from 0.2 to 0.3
dust.scaleX = scale;
dust.scaleY = scale;
// Recycle when lifetime is over
if (lifePercent >= 1) {
dustParticlePool.recycle(self);
}
};
self.deactivate = function () {
self.visible = false;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
// States
var RUNNING = 'running';
var LEAPING = 'leaping';
var ATTACHED = 'attached';
var FALLING = 'falling';
// Running enemy sprites
var runSprite = self.attachAsset('enemy1', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
var runSpriteMirrored = self.attachAsset('enemy1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: -1,
alpha: 0
});
// Properties
self.health = 1;
self.rotationSpeed = 0;
self.animTick = 0;
self.animRate = 30;
self.pulseRate = self.animRate * 2;
self.state = RUNNING;
self.leapSpeed = 10;
self.targetX = 0;
self.targetY = 0;
self.hasExploded = false; // Add this flag
self.activate = function (x, y) {
self.visible = true;
self.x = x;
self.y = y;
self.state = RUNNING;
self.rotation = 0; // Reset rotation
self.health = 1;
self.rotationSpeed = 0;
self.fallSpeed = 0;
self.acceleration = 0;
self.leapDuration = 0;
self.shadow = enemyShadowPool.spawn(x, y, self.scaleX);
self.isPulsedOut = false;
self.hasExploded = false; // Reset explosion flag
// If there was a tween running, stop it
if (self.currentTween) {
self.currentTween.stop();
self.currentTween = null;
}
// Reset animation state
self.animTick = 0;
runSprite.alpha = 1;
runSpriteMirrored.alpha = 0;
// Set initial scale based on distance from horizon
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
var scale = 0.5 + progress;
self.scaleX = scale;
self.scaleY = scale;
};
function checkLeapDistance() {
// Get distance on Y-axis only
var dy = Math.abs(self.y - dragonShadow.y);
// Get relative scales as depth indicators
var enemyDepth = self.scaleX;
var shadowDepth = dragonShadow.scaleX;
// Leap when on same Y-plane regardless of X position
var depthDiff = Math.abs(enemyDepth - shadowDepth);
var yThreshold = 10; // Close on the Y-axis
// If the enemy is on screen and at the right Y position
if (dy < yThreshold && depthDiff < 0.3 && self.x > 0 && self.x < 2048) {
startLeap();
return true;
}
return false;
}
function startLeap() {
self.state = LEAPING;
// Store where the dragon was when leap started
self.leapTargetX = dragon.x;
self.leapTargetY = dragon.y;
var dx = self.leapTargetX - self.x;
var dy = self.leapTargetY - self.y;
self.initialLeapDistance = Math.sqrt(dx * dx + dy * dy);
self.initialScale = self.scaleX;
self.isPulsedOut = false;
runSprite.alpha = 1;
runSpriteMirrored.alpha = 0;
}
self.update = function () {
if (!self.visible) {
return;
}
switch (self.state) {
case RUNNING:
// Existing running code
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
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;
runSprite.alpha = runSprite.alpha === 0 ? 1 : 0;
runSpriteMirrored.alpha = runSpriteMirrored.alpha === 0 ? 1 : 0;
}
// In the Enemy class RUNNING state section, replace the dust spawning code with:
if (LK.ticks % 10 === 0 && self.y < 2732) {
// More frequent spawning (every 10 ticks)
var footY = self.y + 50 * self.scaleY;
dustParticlePool.spawn(self.x, footY);
}
// Check for leap
checkLeapDistance();
break;
case LEAPING:
// Calculate progress towards original leap target
var dx = self.leapTargetX - self.x;
var dy = self.leapTargetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if we've reached or passed the target Y position OR if we're very close to the peak of the leap
if (self.y <= self.leapTargetY || distance < 50 || self.leapDuration > 100) {
// Added timeout condition
var dragonDx = dragon.x - self.x;
var dragonDy = dragon.y - self.y;
var dragonDistance = Math.sqrt(dragonDx * dragonDx + dragonDy * dragonDy);
if (dragonDistance > 300) {
self.state = FALLING;
self.rotationSpeed = (Math.random() * 0.1 + 0.05) * (Math.random() < 0.5 ? 1 : -1);
self.fallSpeed = 2;
self.acceleration = 0.2;
} else {
self.state = ATTACHED;
}
break;
}
// Continue leap motion
if (distance > 10) {
var dirX = dx / distance;
var dirY = dy / distance;
var speedMultiplier = Math.min(1.5, distance / 100);
self.x += dirX * self.leapSpeed * speedMultiplier;
self.y += dirY * self.leapSpeed * speedMultiplier;
self.scaleX = self.initialScale * 0.75;
self.scaleY = self.initialScale * 0.75;
}
// Increment leap duration for safety timeout
self.leapDuration = (self.leapDuration || 0) + 1;
break;
case ATTACHED:
// Stick to dragon position
self.x = dragon.x;
self.y = dragon.y;
// Check for shake-off
if (dragon.currentShakeSpeed > 30) {
// Threshold for shake-off
self.state = FALLING;
self.rotationSpeed = (Math.random() * 0.1 + 0.05) * (Math.random() < 0.5 ? 1 : -1);
self.fallSpeed = 2;
self.acceleration = 0.2;
score += self.type * 5; // Bonus points for shake-off
scoreTxt.setText(score);
LK.setScore(score);
break;
}
// Pulsing animation
self.animTick++;
if (self.animTick >= self.pulseRate) {
self.animTick = 0;
// Pulse between 0.7 and 0.8 scale
tween(self, {
scaleX: self.initialScale * (self.isPulsedOut ? 0.7 : 0.8),
scaleY: self.initialScale * (self.isPulsedOut ? 0.7 : 0.8)
}, {
duration: 200,
easing: tween.easeOut
});
self.isPulsedOut = !self.isPulsedOut;
dragon.flashDamage();
}
break;
case FALLING:
self.rotation += self.rotationSpeed;
self.fallSpeed += self.acceleration;
self.y += self.fallSpeed;
// Check for shadow collision with explosion flag
if (self.y >= dragonShadow.y && !self.hasExploded) {
self.hasExploded = true;
// Create MUCH bigger explosion with red tint
for (var i = 0; i < 60; i++) {
defeatParticlePool.spawn(self.x, self.y, 0xff0000); // Red tint (0xff0000)
}
enemyPool.recycle(self);
}
break;
}
if (self.shadow) {
self.shadow.update(self);
}
};
self.hit = function () {
self.health--;
if (self.health <= 0) {
if (self.state !== FALLING) {
self.state = FALLING;
self.rotationSpeed = (Math.random() * 0.1 + 0.05) * (Math.random() < 0.5 ? 1 : -1);
self.fallSpeed = 2;
self.acceleration = 0.2;
return true;
}
}
return false;
};
self.deactivate = function () {
self.visible = false;
runSprite.alpha = 1;
runSpriteMirrored.alpha = 0;
if (self.shadow) {
enemyShadowPool.recycle(self.shadow);
self.shadow = null;
}
};
return self;
});
var EnemyArrow = Container.expand(function () {
var self = Container.call(this);
var arrow = self.attachAsset('enemyArrow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
self.speed = 8;
self.update = function () {
if (!self.active) {
return;
}
self.x += self.vx;
self.y += self.vy;
// Point arrow in direction of travel
self.rotation = Math.atan2(self.vy, self.vx) + Math.PI / 2;
if (self.y < 0 || self.y > 2732 || self.x < 0 || self.x > 2048) {
enemyArrowPool.recycle(self);
}
};
self.activate = function (x, y, targetX, targetY) {
self.x = x;
self.y = y;
self.visible = true;
self.active = true;
// Calculate direction to target
var dx = targetX - x;
var dy = targetY - y;
var dist = Math.sqrt(dx * dx + dy * dy);
self.vx = dx / dist * self.speed;
self.vy = dy / dist * self.speed;
};
self.deactivate = function () {
self.visible = false;
self.active = false;
};
return self;
});
var EnemyShadow = Container.expand(function () {
var self = Container.call(this);
var shadow = self.attachAsset('enemyShadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: 1,
scaleY: 0.3
});
// In the EnemyShadow class, update method (around line 1018)
self.update = function (enemy) {
if (!enemy || !enemy.visible || enemy.hasExploded) {
self.visible = false;
return;
}
self.visible = true;
// Always follow enemy's X position
self.x = enemy.x;
// For running enemies, shadow is slightly below them
if (enemy.state === 'running') {
self.y = enemy.y + 200; // Shadow slightly below enemy
}
// For leaping enemies, shadow moves to dragon shadow position
else if (enemy.state === 'leaping') {
self.y = dragonShadow.y + 50; // Position just below dragon shadow
}
// For attached enemies, follow dragon shadow
else if (enemy.state === 'attached') {
self.y = dragonShadow.y + 50; // Keep following dragon shadow
}
// For falling enemies, shadow holds last position
else if (enemy.state === 'falling') {
// Y position stays the same
}
// Special case for EnemyTwo type
else if (enemy.type === 2) {
self.y = enemy.y + 600; // Position higher for enemy type 2
}
// Scale shadow based on enemy's scale
shadow.scaleX = enemy.scaleX * 1.2;
shadow.scaleY = enemy.scaleX * 0.3;
};
self.activate = function (x, y, enemyScale) {
self.x = x;
self.y = y + 200; // Initially position shadow slightly below enemy
self.visible = true;
shadow.scaleX = (enemyScale || 1) * 1.2;
shadow.scaleY = (enemyScale || 1) * 0.3;
};
self.deactivate = function () {
self.visible = false;
};
return self;
});
var EnemyTwo = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
// Properties
self.health = 1;
self.type = 2; // For scoring
self.state = 'approaching'; // approaching, holding
self.holdDistance = 800;
self.bobOffset = 0;
self.bobSpeed = 0.05;
self.lastShotTime = 0;
self.shotInterval = 2000; // 2 seconds
self.activate = function (x, y) {
self.visible = true;
self.x = x;
self.y = y;
self.state = 'approaching';
self.health = 1;
self.rotation = 0;
self.hasExploded = false; // Make sure this flag is set
// Ensure shadow is created and stored
if (!self.shadow) {
self.shadow = enemyShadowPool.spawn(x, y, self.scaleX);
}
// Moving direction
self.moveRight = x < 1024; // If spawned on left side, move right
// Initial scale based on distance from horizon
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
self.scaleX = 0.5 + progress;
self.scaleY = 0.5 + progress;
};
self.update = function () {
if (!self.visible) {
return;
}
switch (self.state) {
case 'approaching':
// Bobbing motion
self.bobOffset = Math.sin(LK.ticks * self.bobSpeed) * 50;
// Move towards dragon
var dx = dragon.x - self.x;
var dy = dragon.y - self.y + self.bobOffset;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.holdDistance) {
self.x += dx * 0.01; // Reduced from 0.02
self.y += dy * 0.01; // Reduced from 0.02
// Modified scale calculation to maintain larger size
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
self.scaleX = 0.7 + progress * 0.5; // Modified from 0.5 + progress
self.scaleY = 0.7 + progress * 0.5; // Modified from 0.5 + progress
} else {
self.state = 'holding';
}
break;
case 'holding':
// Maintain position relative to dragon
var dx = dragon.x - self.x;
var targetX = self.x + dx * 0.01;
// Keep on screen
targetX = Math.max(100, Math.min(1948, targetX));
self.x = targetX;
// Stay below dragon with offset
var targetY = dragon.y + 800; // Stay 200 pixels below dragon
var currentY = self.y - self.bobOffset; // Remove bobbing to get true position
self.y = currentY + (targetY - currentY) * 0.1; // Smooth movement to target
// Apply bobbing to final position
self.y += self.bobOffset;
// Shoot at intervals
var now = Date.now();
if (now - self.lastShotTime >= self.shotInterval) {
self.shoot();
self.lastShotTime = now;
}
break;
case 'falling':
self.rotation += self.rotationSpeed;
self.fallSpeed += self.acceleration;
self.y += self.fallSpeed;
// Check for ground collision
if (self.shadow && self.y >= self.shadow.y && !self.hasExploded) {
self.hasExploded = true;
for (var i = 0; i < 60; i++) {
defeatParticlePool.spawn(self.x, self.y, 0xff0000);
}
enemyTwoPool.recycle(self);
}
break;
}
if (self.shadow) {
self.shadow.update(self);
}
};
self.shoot = function () {
var arrow = enemyArrowPool.spawn(self.x, self.y);
if (arrow) {
arrow.activate(self.x, self.y, dragon.x, dragon.y);
}
};
self.hit = function () {
self.health--;
if (self.health <= 0) {
if (Math.random() < 0.5) {
// Mid-air explosion
for (var i = 0; i < 60; i++) {
defeatParticlePool.spawn(self.x, self.y, 0xff0000);
}
enemyTwoPool.recycle(self);
} else {
// Fall and explode
self.state = 'falling';
self.rotationSpeed = (Math.random() * 0.1 + 0.05) * (Math.random() < 0.5 ? 1 : -1);
self.fallSpeed = 2;
self.acceleration = 0.2;
}
return true;
}
return false;
};
self.deactivate = function () {
self.visible = false;
if (self.shadow) {
enemyShadowPool.recycle(self.shadow);
self.shadow = null;
}
};
return self;
});
var FieldElement = Container.expand(function () {
var self = Container.call(this);
var element = self.attachAsset('fieldElement', {
anchorX: 0.5,
anchorY: 0.5
});
self.activate = function (x, y, scale, alpha) {
self.x = x;
self.y = y;
self.visible = true;
self.speedY = 2 + Math.random() * 3;
self.initialX = x;
element.scaleX = scale;
element.scaleY = scale * 0.8;
element.alpha = alpha || 0.9;
};
self.deactivate = function () {
self.visible = 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() * 8 - 4; // Doubled velocity range
self.vy = -(Math.random() * 4 + 2); // Make particles move upward
self.lifespan = 45 + Math.random() * 30; // Increased lifespan
self.age = 0;
particle.alpha = 1;
// Add scaling
particle.scaleX = 0.5 + Math.random() * 0.5;
particle.scaleY = 0.5 + Math.random() * 0.5;
};
self.deactivate = function () {
self.visible = false;
};
return self;
});
// In the Fireball class, update the properties and update method
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.vx = 0;
// Add these new properties
self.weaveDirection = 1; // Will be set to 1 or -1 on activate
self.weaveAmplitude = 70; // Adjust this to control width of weave
self.weaveFrequency = 0.2; // Adjust this to control speed of weave
self.initialY = 0; // Track starting Y position
self.update = function () {
if (!self.active) {
return;
}
// Update base position along trajectory
self.y += self.speed;
self.x += self.vx;
// Add weaving motion
var distanceTraveled = self.y - self.initialY;
var weaveOffset = Math.sin(distanceTraveled * self.weaveFrequency) * self.weaveAmplitude * self.weaveDirection;
self.x += weaveOffset;
// Calculate rotation based only on base trajectory, ignoring weave
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);
}
// In Fireball class update method, replace the recycle check with:
// In Fireball class update method, replace the recycle check with:
var groundImpactY = 2732 * 0.9;
// Make impact decision only when first crossing the threshold
if (self.y > groundImpactY && self.lastY <= groundImpactY && !self.hasImpacted && self.x > 100 && self.x < 1948) {
if (Math.random() < 0.5) {
self.hasImpacted = true;
flamePool.spawn(self.x, groundImpactY);
scorchPool.spawn(self.x, groundImpactY);
// Spawn more particles
for (var i = 0; i < 15; i++) {
// Increased from 8 to 15
var particle = particlePool.spawn(self.x + (Math.random() * 40 - 20), groundImpactY);
if (particle) {
particle.vy = -(Math.random() * 6 + 3); // More upward velocity
particle.vx = (Math.random() - 0.5) * 8; // More spread
}
}
fireballPool.recycle(self);
return;
}
}
// Track last Y position
self.lastY = self.y;
// Only recycle if off screen
if (self.y > 2732 || self.x < -100 || self.x > 2148) {
fireballPool.recycle(self);
}
};
self.activate = function (x, y) {
self.x = x;
self.y = y;
self.initialY = y;
self.lastY = y;
self.visible = true;
self.active = true;
self.hasImpacted = false;
self.vx = 0;
self.rotation = 0;
fireballGraphic.alpha = 1;
};
self.deactivate = function () {
self.visible = false;
self.active = false;
// Clear any references that might cause memory leaks
self.currentTween = null;
};
return self;
});
var FlameEffect = Container.expand(function () {
var self = Container.call(this);
var flame = self.attachAsset('flame', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1,
scaleY: 0.5
});
self.activate = function (x, y) {
self.x = x;
self.y = y;
self.visible = true;
self.age = 0;
flame.scaleX = 0.75;
flame.scaleY = 0.5;
var initialHeight = flame.height * flame.scaleY;
var targetScale = 2.5;
if (self.currentTween) {
self.currentTween.stop();
}
self.currentTween = tween(flame, {
scaleY: targetScale,
y: -(initialHeight * (targetScale - 0.5)) / 2,
// Offset upward to compensate for scaling
alpha: 1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
flamePool.recycle(self);
}
});
};
self.deactivate = function () {
self.visible = false;
if (self.currentTween) {
self.currentTween.stop();
self.currentTween = null;
}
flame.y = 0; // Reset position
};
return self;
});
var FlyingBackground = Container.expand(function () {
var self = Container.call(this);
// Create bottom container
self.bottomContainer = new Container();
self.addChild(self.bottomContainer);
// Store the horizon line
self.horizonY = 2732 / 2;
self.totalHeight = 2732 - self.horizonY;
self.fieldStripes = [];
// Create field stripes (keep this the same as original)
var nbStripes = 30;
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
});
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 top container
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 + 10
});
// Field scroll speed
self.scrollSpeed = 4;
// Create object pools
self.cloudPool = new ObjectPool(Cloud, 15);
self.fieldElementPool = new ObjectPool(FieldElement, 20);
// Add pools to containers
self.topContainer.addChild(self.cloudPool);
self.bottomContainer.addChild(self.fieldElementPool);
// Initialize clouds
for (var i = 0; i < 8; i++) {
var startY = self.horizonY - 400 - Math.random() * 600;
var distanceFromHorizon = Math.abs(startY - self.horizonY);
var startScale = 0.5 + distanceFromHorizon / 800;
var cloud = self.cloudPool.spawn(Math.random() * 2048, startY, startScale, 0.8);
if (cloud) {
cloud.horizonY = self.horizonY;
}
}
// Initialize field elements
for (var j = 0; j < 12; j++) {
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.fieldElementPool.spawn(Math.random() * 2048, fieldStartY, fieldStartScale, 0.9);
if (fieldElement) {
fieldElement.horizonY = self.horizonY;
}
}
self.update = function () {
// Update field stripes (keep original code)
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
self.fieldStripes.sort(function (a, b) {
return a.sprite.y - b.sprite.y;
});
// Reset stripes that moved above horizon
for (var i = 0; i < self.fieldStripes.length; i++) {
if (self.fieldStripes[i].sprite.y < self.horizonY) {
var lowestStripe = self.fieldStripes[self.fieldStripes.length - 1];
self.fieldStripes[i].sprite.y = lowestStripe.sprite.y + 400 * (lowestStripe.sprite.y - self.horizonY) / self.totalHeight;
self.fieldStripes[i].progress = Math.max(0, self.fieldStripes[i].sprite.y - self.horizonY) / self.totalHeight;
}
}
// Re-sort after repositioning
self.fieldStripes.sort(function (a, b) {
return a.sprite.y - b.sprite.y;
});
// Adjust stripe heights
for (var i = 0; i < self.fieldStripes.length - 1; i++) {
var currentStripe = self.fieldStripes[i];
var nextStripe = self.fieldStripes[i + 1];
currentStripe.sprite.height = nextStripe.sprite.y - currentStripe.sprite.y;
}
// Handle last stripe
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);
// Update clouds and field elements
updateCloudPool();
updateFieldElementPool();
};
function updateCloudPool() {
var activeObjects = self.cloudPool.getActiveObjects();
for (var i = 0; i < activeObjects.length; i++) {
var cloud = activeObjects[i];
// Move cloud toward horizon
cloud.y += cloud.speedY;
cloud.x += cloud.speedX;
// Get the cloud's sprite
var sprite = cloud.getChildAt(0);
// Calculate scale based on distance from horizon
var distanceFactor = Math.max(0.05, (cloud.y - self.horizonY) / -500);
sprite.scaleX = distanceFactor;
sprite.scaleY = distanceFactor * 0.8;
// Adjust alpha for fade effect near horizon
sprite.alpha = Math.min(0.9, distanceFactor);
// Reset cloud if it reaches horizon or gets too small
if (cloud.y >= self.horizonY - 10 || sprite.scaleX < 0.1) {
// Recycle and spawn new cloud
self.cloudPool.recycle(cloud);
var newCloud = self.cloudPool.spawn(Math.random() * 2048, self.horizonY - 800 - Math.random() * 400, (self.horizonY - 800 - Math.random() * 400 - self.horizonY) / -500, 0.8);
if (newCloud) {
newCloud.horizonY = self.horizonY;
}
}
}
}
function updateFieldElementPool() {
var activeObjects = self.fieldElementPool.getActiveObjects();
for (var j = 0; j < activeObjects.length; j++) {
var element = activeObjects[j];
// Calculate progress based on distance from horizon
var progress = Math.max(0, element.y - self.horizonY) / self.totalHeight;
// Calculate speed using same logic as stripes
var tempSpeed = self.scrollSpeed * 0.05 + self.scrollSpeed * (progress * 3);
// Move element toward horizon
element.y -= tempSpeed;
// Get the element's sprite
var sprite = element.getChildAt(0);
// Calculate scale based on distance from horizon
var fieldDistanceFactor = Math.max(0.05, (element.y - self.horizonY) / 300);
sprite.scaleX = fieldDistanceFactor * 1.2;
sprite.scaleY = fieldDistanceFactor * 1.2;
// Adjust alpha for fade effect
//sprite.alpha = Math.min(0.9, fieldDistanceFactor);
// Reset element if it reaches horizon
if (element.y <= self.horizonY + 10 || sprite.scaleX < 0.1) {
// Recycle and spawn new element
self.fieldElementPool.recycle(element);
// Calculate position for new element
var section = Math.floor(Math.random() * 8);
var sectionWidth = 2048 / 8;
var newX = section * sectionWidth + Math.random() * sectionWidth;
var newY = 2732 + Math.random() * 600;
var newScale = (newY - self.horizonY) / 300;
var newElement = self.fieldElementPool.spawn(newX, newY, newScale, 0.9);
if (newElement) {
newElement.horizonY = self.horizonY;
newElement.initialX = newX;
}
}
element.zIndex = element.y;
}
// After updating all elements, sort them by z-index
activeObjects.sort(function (a, b) {
return a.y - b.y; // Sort by y position (smaller y values first)
});
// Remove and re-add in the sorted order
for (var k = 0; k < activeObjects.length; k++) {
var element = activeObjects[k];
self.bottomContainer.removeChild(element);
self.bottomContainer.addChild(element);
}
}
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();
if (typeof obj.update !== 'function') {
obj.update = function () {}; // Provide a default update method if not defined
}
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 || typeof obj._poolIndex !== 'number' || obj._poolIndex >= activeCount) {
return;
}
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.recycleAll = function () {
while (activeCount > 0) {
self.recycle(objects[0]);
}
};
self.update = function () {
for (var i = 0; i < activeCount; i++) {
objects[i].update();
}
};
self.getActiveObjects = function () {
return objects.slice(0, activeCount);
};
return self;
});
var ScorchMark = Container.expand(function () {
var self = Container.call(this);
var mark = self.attachAsset('scorchMark', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.activate = function (x, y) {
self.x = x;
self.y = y;
self.visible = true;
self.speedY = flyingBackground.scrollSpeed;
// Initial scale based on distance from horizon
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
mark.scaleX = 0.5 + progress;
mark.scaleY = (0.5 + progress) * 0.8;
};
self.update = function () {
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
// Drastically reduce base speed to match field elements
var tempSpeed = 0.2 + progress * 3;
self.y -= tempSpeed;
mark.scaleX = 0.5 + progress;
mark.scaleY = (0.5 + progress) * 0.8;
mark.alpha = Math.min(0.8, progress);
if (self.y <= flyingBackground.horizonY) {
scorchPool.recycle(self);
}
};
self.deactivate = function () {
self.visible = false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function processCollisions() {
var fireballs = fireballPool.getActiveObjects();
var enemies = enemyPool.getActiveObjects();
var enemiesTwo = enemyTwoPool.getActiveObjects();
// Process enemy arrows first
var arrows = enemyArrowPool.getActiveObjects();
// Replace the existing arrow collision check with:
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
var dx = arrow.x - dragon.x;
var dy = arrow.y - dragon.y;
// Only check collisions if arrow isn't below dragon
if (arrow.y <= dragon.y + 50) {
// Allow slight vertical tolerance
// Wider horizontal check, original vertical bounds
var horizontalDistance = Math.abs(dx);
var verticalDistance = Math.abs(dy);
// Check if within wing range (wider horizontally)
if (horizontalDistance < 300 && verticalDistance < 75) {
enemyArrowPool.recycle(arrow);
// Flash dragon red (keep existing code)
dragon.flashDamage();
}
}
}
// Process fireballs against both enemy types
for (var i = fireballs.length - 1; i >= 0; i--) {
var fireball = fireballs[i];
// Check against type 1 enemies
for (var j = enemies.length - 1; j >= 0; 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) {
if (enemy.hit()) {
score += enemy.type * 10;
scoreTxt.setText(score);
LK.setScore(score);
LK.getSound('enemyDefeat').play();
}
fireballPool.recycle(fireball);
break;
}
}
// Check against type 2 enemies
for (var j = enemiesTwo.length - 1; j >= 0; j--) {
var enemy = enemiesTwo[j];
var dx = fireball.x - enemy.x;
var dy = fireball.y - enemy.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 170 * enemy.scaleX) {
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, 25);
var defeatParticlePool = new ObjectPool(DefeatParticle, 200);
var dustParticlePool = new ObjectPool(DustParticle, 100);
var enemyShadowPool = new ObjectPool(EnemyShadow, 50);
var enemyArrowPool = new ObjectPool(EnemyArrow, 50);
var enemyTwoPool = new ObjectPool(EnemyTwo, 25);
var flamePool = new ObjectPool(FlameEffect, 20);
var scorchPool = new ObjectPool(ScorchMark, 30);
var gameActive = true;
var isFiring = false;
var lastFireTime = 0;
var fireRate = 250; // ms between fireballs
var enemySpawnRate = 120; // 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
var dragonShadow = new DragonShadow();
game.addChild(dragonShadow);
game.addChild(enemyShadowPool);
game.addChild(fireballPool);
game.addChild(particlePool);
game.addChild(enemyPool);
game.addChild(enemyTwoPool);
game.addChild(defeatParticlePool);
game.addChild(dustParticlePool);
game.addChild(enemyArrowPool);
game.addChild(flamePool);
game.addChild(scorchPool);
game.cleanupPools = function () {
fireballPool.recycleAll();
particlePool.recycleAll();
enemyPool.recycleAll();
enemyTwoPool.recycleAll();
defeatParticlePool.recycleAll();
dustParticlePool.recycleAll();
enemyShadowPool.recycleAll();
enemyArrowPool.recycleAll();
scorchPool.recycleAll();
flamePool.recycleAll();
};
// 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
// Update the spawnFireball function
function spawnFireball() {
if (!gameActive) {
return;
}
var now = Date.now();
if (now - lastFireTime < fireRate) {
return;
}
lastFireTime = now;
// Initialize horizontal velocity components
var angleOffset = 0;
// Get direction from dragon head facing - match thresholds with DragonHead class
if (facekit.leftEye && facekit.rightEye && facekit.noseTip) {
var faceCenter = (facekit.leftEye.x + facekit.rightEye.x) / 2;
var noseDiff = facekit.noseTip.x - faceCenter;
var eyeYDiff = facekit.rightEye.y - facekit.leftEye.y;
// Keep the original thresholds but require more pronounced movement
// to trigger the sideways shots (higher threshold values)
if (noseDiff < -120 && Math.abs(eyeYDiff) < 30 || eyeYDiff > 40) {
// Facing left - fireballs go left (maintain strong angle)
angleOffset = -8; // Original value for strong effect
} else if (noseDiff > 120 && Math.abs(eyeYDiff) < 30 || eyeYDiff < -40) {
// Facing right - fireballs go right (maintain strong angle)
angleOffset = 8; // Original value for strong effect
}
}
// Keep the original tilt impact for consistent feel
if (dragon.rotation) {
angleOffset += dragon.rotation * 5; // Original value
}
// Use the new pool's spawn method
var fireball = fireballPool.spawn(dragon.x, dragon.y + 50);
if (fireball) {
fireball.vx = angleOffset;
// Add this line to alternate weave direction
fireball.weaveDirection = lastFireTime % (fireRate * 2) === 0 ? 1 : -1;
}
// Play sound
LK.getSound('firebreathSound').play();
}
// Function to spawn enemies
function spawnEnemy() {
if (!gameActive) {
return;
}
var isTypeTwo = Math.random() < 0.4; // 40% chance for EnemyTwo
if (isTypeTwo) {
// Spawn from sides, about 500px from bottom
var spawnX = Math.random() < 0.5 ? -50 : 2098;
var enemy = enemyTwoPool.spawn(spawnX, 2732 - 500);
if (enemy) {
enemy.visible = true;
}
} else {
var enemy = enemyPool.spawn(Math.random() * (2048 - 200) + 100, 2732 + 100);
if (enemy) {
enemy.visible = true;
}
}
}
// 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();
}
dragonShadow.update(dragon.x, dragon.y, dragon.currentScale);
// Update all pools
fireballPool.update();
enemyPool.update();
particlePool.update();
defeatParticlePool.update();
dustParticlePool.update();
flamePool.update();
scorchPool.update();
// Process collisions
processCollisions();
};
// Start background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.8,
duration: 1000
}
}); ===================================================================
--- original.js
+++ change.js
@@ -276,8 +276,36 @@
return Math.max(-15, Math.min(15, angle * 0.15));
}
return 0; // Default to straight when face points aren't available
}
+ self.flashDamage = function () {
+ var dragonSprites = [body]; // body
+ // Add whichever head sprite is currently visible
+ if (head && head.alpha > 0) {
+ dragonSprites.push(head);
+ }
+ if (headOpen && headOpen.alpha > 0) {
+ dragonSprites.push(headOpen);
+ }
+ if (lookLeft && lookLeft.alpha > 0) {
+ dragonSprites.push(lookLeft);
+ }
+ if (lookRight && lookRight.alpha > 0) {
+ dragonSprites.push(lookRight);
+ }
+ if (lookLeftOpen && lookLeftOpen.alpha > 0) {
+ dragonSprites.push(lookLeftOpen);
+ }
+ if (lookRightOpen && lookRightOpen.alpha > 0) {
+ dragonSprites.push(lookRightOpen);
+ }
+ dragonSprites.forEach(function (sprite) {
+ sprite.tint = 0xFF0000;
+ LK.setTimeout(function () {
+ sprite.tint = 0xFFFFFF;
+ }, 10);
+ });
+ };
return self;
});
var DragonShadow = Container.expand(function () {
var self = Container.call(this);
@@ -379,8 +407,9 @@
self.health = 1;
self.rotationSpeed = 0;
self.animTick = 0;
self.animRate = 30;
+ self.pulseRate = self.animRate * 2;
self.state = RUNNING;
self.leapSpeed = 10;
self.targetX = 0;
self.targetY = 0;
@@ -511,9 +540,9 @@
// Stick to dragon position
self.x = dragon.x;
self.y = dragon.y;
// Check for shake-off
- if (dragon.currentShakeSpeed > 40) {
+ if (dragon.currentShakeSpeed > 30) {
// Threshold for shake-off
self.state = FALLING;
self.rotationSpeed = (Math.random() * 0.1 + 0.05) * (Math.random() < 0.5 ? 1 : -1);
self.fallSpeed = 2;
@@ -524,9 +553,9 @@
break;
}
// Pulsing animation
self.animTick++;
- if (self.animTick >= self.animRate) {
+ if (self.animTick >= self.pulseRate) {
self.animTick = 0;
// Pulse between 0.7 and 0.8 scale
tween(self, {
scaleX: self.initialScale * (self.isPulsedOut ? 0.7 : 0.8),
@@ -535,8 +564,9 @@
duration: 200,
easing: tween.easeOut
});
self.isPulsedOut = !self.isPulsedOut;
+ dragon.flashDamage();
}
break;
case FALLING:
self.rotation += self.rotationSpeed;
@@ -545,9 +575,9 @@
// Check for shadow collision with explosion flag
if (self.y >= dragonShadow.y && !self.hasExploded) {
self.hasExploded = true;
// Create MUCH bigger explosion with red tint
- for (var i = 0; i < 20; i++) {
+ for (var i = 0; i < 60; i++) {
defeatParticlePool.spawn(self.x, self.y, 0xff0000); // Red tint (0xff0000)
}
enemyPool.recycle(self);
}
@@ -757,9 +787,9 @@
self.y += self.fallSpeed;
// Check for ground collision
if (self.shadow && self.y >= self.shadow.y && !self.hasExploded) {
self.hasExploded = true;
- for (var i = 0; i < 20; i++) {
+ for (var i = 0; i < 60; i++) {
defeatParticlePool.spawn(self.x, self.y, 0xff0000);
}
enemyTwoPool.recycle(self);
}
@@ -779,9 +809,9 @@
self.health--;
if (self.health <= 0) {
if (Math.random() < 0.5) {
// Mid-air explosion
- for (var i = 0; i < 20; i++) {
+ for (var i = 0; i < 60; i++) {
defeatParticlePool.spawn(self.x, self.y, 0xff0000);
}
enemyTwoPool.recycle(self);
} else {
@@ -849,13 +879,16 @@
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.vx = Math.random() * 8 - 4; // Doubled velocity range
+ self.vy = -(Math.random() * 4 + 2); // Make particles move upward
+ self.lifespan = 45 + Math.random() * 30; // Increased lifespan
self.age = 0;
particle.alpha = 1;
+ // Add scaling
+ particle.scaleX = 0.5 + Math.random() * 0.5;
+ particle.scaleY = 0.5 + Math.random() * 0.5;
};
self.deactivate = function () {
self.visible = false;
};
@@ -901,13 +934,15 @@
if (Math.random() < 0.5) {
self.hasImpacted = true;
flamePool.spawn(self.x, groundImpactY);
scorchPool.spawn(self.x, groundImpactY);
- for (var i = 0; i < 8; i++) {
- var particle = particlePool.spawn(self.x, groundImpactY);
+ // Spawn more particles
+ for (var i = 0; i < 15; i++) {
+ // Increased from 8 to 15
+ var particle = particlePool.spawn(self.x + (Math.random() * 40 - 20), groundImpactY);
if (particle) {
- particle.vy = -(Math.random() * 4 + 2);
- particle.vx = (Math.random() - 0.5) * 4;
+ particle.vy = -(Math.random() * 6 + 3); // More upward velocity
+ particle.vx = (Math.random() - 0.5) * 8; // More spread
}
}
fireballPool.recycle(self);
return;
@@ -944,25 +979,30 @@
var self = Container.call(this);
var flame = self.attachAsset('flame', {
anchorX: 0.5,
anchorY: 1.0,
- scaleX: 0.1,
- scaleY: 0.1
+ scaleX: 1,
+ scaleY: 0.5
});
self.activate = function (x, y) {
self.x = x;
self.y = y;
self.visible = true;
self.age = 0;
+ flame.scaleX = 0.75;
+ flame.scaleY = 0.5;
+ var initialHeight = flame.height * flame.scaleY;
+ var targetScale = 2.5;
if (self.currentTween) {
self.currentTween.stop();
}
self.currentTween = tween(flame, {
- scaleX: 1.5,
- scaleY: 1.5,
- alpha: 0
+ scaleY: targetScale,
+ y: -(initialHeight * (targetScale - 0.5)) / 2,
+ // Offset upward to compensate for scaling
+ alpha: 1
}, {
- duration: 600,
+ duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
flamePool.recycle(self);
}
@@ -973,8 +1013,9 @@
if (self.currentTween) {
self.currentTween.stop();
self.currentTween = null;
}
+ flame.y = 0; // Reset position
};
return self;
});
var FlyingBackground = Container.expand(function () {
@@ -1242,9 +1283,11 @@
mark.scaleY = (0.5 + progress) * 0.8;
};
self.update = function () {
var progress = Math.max(0, self.y - flyingBackground.horizonY) / (2732 - flyingBackground.horizonY);
- self.y -= self.speedY * (0.05 + progress * 3);
+ // Drastically reduce base speed to match field elements
+ var tempSpeed = 0.2 + progress * 3;
+ self.y -= tempSpeed;
mark.scaleX = 0.5 + progress;
mark.scaleY = (0.5 + progress) * 0.8;
mark.alpha = Math.min(0.8, progress);
if (self.y <= flyingBackground.horizonY) {
@@ -1272,24 +1315,25 @@
var enemies = enemyPool.getActiveObjects();
var enemiesTwo = enemyTwoPool.getActiveObjects();
// Process enemy arrows first
var arrows = enemyArrowPool.getActiveObjects();
+ // Replace the existing arrow collision check with:
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
var dx = arrow.x - dragon.x;
var dy = arrow.y - dragon.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance < 75) {
- enemyArrowPool.recycle(arrow);
- // Flash dragon red
- var dragonSprites = [dragon.getChildAt(0)];
- dragonSprites.forEach(function (sprite) {
- sprite.tint = 0xFF0000;
- LK.setTimeout(function () {
- sprite.tint = 0xFFFFFF;
- }, 10);
- });
- break;
+ // Only check collisions if arrow isn't below dragon
+ if (arrow.y <= dragon.y + 50) {
+ // Allow slight vertical tolerance
+ // Wider horizontal check, original vertical bounds
+ var horizontalDistance = Math.abs(dx);
+ var verticalDistance = Math.abs(dy);
+ // Check if within wing range (wider horizontally)
+ if (horizontalDistance < 300 && verticalDistance < 75) {
+ enemyArrowPool.recycle(arrow);
+ // Flash dragon red (keep existing code)
+ dragon.flashDamage();
+ }
}
}
// Process fireballs against both enemy types
for (var i = fireballs.length - 1; i >= 0; i--) {
@@ -1316,9 +1360,9 @@
var enemy = enemiesTwo[j];
var dx = fireball.x - enemy.x;
var dy = fireball.y - enemy.y;
var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance < 150 * enemy.scaleX) {
+ if (distance < 170 * enemy.scaleX) {
if (enemy.hit()) {
score += enemy.type * 10;
scoreTxt.setText(score);
LK.setScore(score);
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