Code edit (4 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
play knight-bonus when bonus taken
Code edit (1 edits merged)
Please save this source code
User prompt
bonus thow move should be slower and look more the result of an explosion βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
when spawned, bonus should be thrown with animation to one of the corners;move should look parabolic (game is in top down view) βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
when spawned, animate bonus movement to one of the corners;move should look parabolic (game is in top down view) βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
bonus spawn move should look parabolic βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
when spawned, animate bonus movement to a random corner βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
knight only collects the bonus if active
User prompt
add a boolean self.active=false; to bonus
Code edit (2 edits merged)
Please save this source code
User prompt
Now when knight boundingbox intersects bonusLight take the bonus (destory bonus and add 2 to health
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Background = Container.expand(function () { var self = Container.call(this); var backgroundGraphics = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(backgroundGraphics); self.x = 2048 / 2; self.y = 2732 / 2; // Move the down handler to the Background class self.down = function (x, y, obj) { var game_position = game.toLocal(obj.global); target.x = Math.max(corners[0].x, Math.min(game_position.x, corners[3].x)); target.y = Math.max(corners[0].y, Math.min(game_position.y, corners[3].y)); }; }); var Bonus = Container.expand(function (x, y) { var self = Container.call(this); self.x = x; self.y = y; // Create shadow for bonus var shadowBonus = LK.getAsset('bonusHealth', { anchorX: 0.5, anchorY: 0.5 }); shadowBonus.alpha = 0.5; shadowBonus.tint = 0x000000; shadowBonus.x = 10; // Offset shadow position shadowBonus.y = 10; // Offset shadow position self.addChild(shadowBonus); self.active = false; var bonusGraphics = LK.getAsset('bonusHealth', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(bonusGraphics); self.bonusLight = LK.getAsset('bonusHealthLight', { anchorX: 0.5, anchorY: 0.5, tint: 0x00FF00 }); self.addChild(self.bonusLight); self.animLight = function () { function loopTint() { tween(self.bonusLight, { tint: 0xFFFFFF }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.bonusLight, { tint: 0x00FF00 }, { duration: 600, easing: tween.easeInOut, onFinish: loopTint }); } }); } loopTint(); }; self.animLight(); self.animMove = function () { function loopMove() { tween(self, { y: self.y + 10 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: self.y - 10 }, { duration: 500, easing: tween.easeInOut, onFinish: loopMove }); } }); } loopMove(); }; self.throwToCorner = function () { // Calculate a random position within range instead of using corners var minDistance = 1024; var maxDistance = 1480; // Generate random angle var randomAngle = Math.random() * Math.PI * 2; // Calculate random distance within range var randomDistance = minDistance + Math.random() * (maxDistance - minDistance); // Calculate target position var targetX = self.x + Math.cos(randomAngle) * randomDistance; var targetY = self.y + Math.sin(randomAngle) * randomDistance; // Ensure target is within the playable area (within corners) targetX = Math.max(borderOffset, Math.min(2048 - borderOffset, targetX)); targetY = Math.max(borderOffset, Math.min(2732 - borderOffset, targetY)); // Calculate control point for parabolic motion var controlX = (self.x + targetX) / 2; var controlY = Math.min(self.y, targetY) - 500; // Control point above the line for a parabolic effect // Initial position var startX = self.x; var startY = self.y; // Create a tween for parabolic motion tween(self, { x: targetX, y: targetY }, { duration: 1200, easing: tween.easeOutQuad, onUpdate: function onUpdate() { // Calculate parabolic path using quadratic Bezier curve formula var t = this.progress; self.x = Math.pow(1 - t, 2) * startX + 2 * (1 - t) * t * controlX + Math.pow(t, 2) * targetX; self.y = Math.pow(1 - t, 2) * startY + 2 * (1 - t) * t * controlY + Math.pow(t, 2) * targetY; // Add rotation for visual effect self.rotation = t * Math.PI * 3; // 1.5 full rotations during flight }, onFinish: function onFinish() { self.rotation = 0; // Reset rotation self.active = true; // Activate bonus after reaching the corner self.animMove(); } }); }; self.throwToCorner(); // Call the function to start the animation }); var ButtonPunch = Container.expand(function () { var self = Container.call(this); var buttonGraphics = LK.getAsset('buttonPunch', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(buttonGraphics); // Add click/tap handler self.down = function () { // Trigger knight attack when button is pressed if (!isDied && knight && knight.health >= 0 && !knight.isAttacking) { knight.attack(); } }; }); // Import the tween plugin var Drone = Container.expand(function (level) { var self = Container.call(this); self.level = level || 0; // Set level to the passed parameter or default to 0 self.health = (isDebug ? 2 : 4) + self.level * 2; // Add level*2 to drone's health var droneWidth = 200; // Increase size based on level var levelGrowth = self.level * self.level * 80; self.droneScanLaser = LK.getAsset('droneScanLaser', { anchorX: 0, anchorY: 0.5 }); self.droneScanLaser.visible = true; // Always visible self.droneScanLaser.x = droneWidth / 2; self.droneScanLaser.alpha = 0.3; // Set alpha to 0.3 self.droneScanLaser.width += levelGrowth; self.droneScanLaser.height += levelGrowth; self.addChild(self.droneScanLaser); self.droneScanBar = LK.getAsset('droneScanBar', { anchorX: 0, anchorY: 0.5 }); self.droneScanBar.x = droneWidth / 2; self.droneScanBar.blendMode = 3; self.droneScanBar.width = self.droneScanLaser.width; self.addChild(self.droneScanBar); var droneGraphics = LK.getAsset('drone', { anchorX: 0.5, anchorY: 0.5, width: droneWidth, height: droneWidth }); var shadowDrone = LK.getAsset('drone', { anchorX: 0.5, anchorY: 0.5, width: droneWidth, height: droneWidth }); self.shadowOffset = { x: -30, y: 40 }; // Define shadow offset property shadowDrone.alpha = 0.5; shadowDrone.tint = 0x000000; shadowDrone.x = -10; shadowDrone.y = 40; self.addChild(shadowDrone); self.addChild(droneGraphics); // Add health bar under the drone self.healthBar = LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 10, color: 0x00FF00 // Green color for health }); self.healthBar.y = droneWidth / 2 + 20; // Position the health bar under the drone self.addChild(self.healthBar); function loopScanLaserTint() { if (self.state !== 'scanning') { return; } tween(self.droneScanLaser, { tint: 0x00FF00 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.droneScanLaser, { tint: 0x00AA00 }, { duration: 1000, easing: tween.easeInOut, onFinish: loopScanLaserTint // Recursively call to loop }); } }); } function animateScanBar() { if (self.state !== 'scanning') { return; } tween(self.droneScanBar, { alpha: 0.3, rotation: Math.PI / 6.75 }, { duration: 600 - self.level * 50, //{V} // Decrease duration to increase frequency with level easing: tween.easeInOut, onFinish: function onFinish() { tween(self.droneScanBar, { alpha: 0.6, rotation: -Math.PI / 6.75 }, { duration: 1000, easing: tween.easeInOut, onFinish: animateScanBar // Recursively call to loop }); // Ensure the tween is started } }); // Ensure the tween is started } self.speed = 5; self.state = ''; // Possible states: scanning, attacking // Add debug text to track drone state self.debugText = new Text2(self.state, { size: 50, fill: 0xFFFFFF }); self.debugText.x = -droneGraphics.width / 2; self.debugText.y = -droneGraphics.height / 2 - 50; self.debugText.visible = isDebug; // Set visibility based on debug mode self.addChild(self.debugText); self.fireRange = 300; // Example fire range value self.fireDelay = 1200; self.currentTargetIndex = -1; self.target = { x: 0, y: 0 }; self.lastKnightCheckTime = 0; // Track when we last checked for knight intersection self.lastFireTime = 0; // Track when we last fired at the knight self.lastRotationTime = 0; // Track the last time we updated rotation self.isRotating = false; // Flag to track if we're currently rotating self.isMoving = false; // Flag to track if we're currently moving self.needsRotation = false; // Flag to track if we need to rotate before moving self.justSwitchedFromAttacking = false; // Flag to track if we just switched from attacking mode // Function to select a new random target self.selectTarget = function () { log("selectTarget...:"); var newTargetIndex; do { newTargetIndex = Math.floor(Math.random() * corners.length); } while (newTargetIndex === self.currentTargetIndex); self.currentTargetIndex = newTargetIndex; self.target = corners[newTargetIndex]; log("New target selected at position:", self.target.x, self.target.y); // Set a flag to indicate the target is new and needs rotation first self.needsRotation = true; // Return the new target (not used currently but could be useful) return self.target; }; // Function to fire a laser beam self.fire = function () { log("Drone firing laser beam..."); LK.getSound('drone-beam').play(); var laserBeam = new LaserBeam(); laserBeam.fire(self.x, self.y, self.rotation); }; // Function to handle state transitions self.switchState = function (newState) { // Don't do anything if the state isn't changing if (self.state === newState) { return; } log("Switching drone state from " + self.state + " to " + newState); // Stop any existing tweens to ensure clean state transition tween.stop(self); // Clear scan sound interval if it exists if (self.scanSoundInterval) { LK.clearInterval(self.scanSoundInterval); self.scanSoundInterval = null; } // Reset movement flags self.isRotating = false; self.isMoving = false; self.justSwitchedFromAttacking = newState === 'scanning' && self.state === 'attacking'; // Update the state self.state = newState; self.debugText.setText(self.state); // Update debug text with current state // Handle entry actions for new state if (newState === 'scanning') { // Play drone-scan sound self.scanSoundInterval = LK.setInterval(function () { LK.getSound('drone-scan').play(); }, 1000); // Entering scanning state self.debugText.tint = 0xFFFFFF; // Reset tint to white for other states self.droneScanLaser.tint = 0x00FF00; // Reset tint to green for scanning mode // Clear current target and set flag to select a new one //self.target = null; //self.needsRotation = true; animateScanBar(); // Select a new target (this will be handled in updateScanning on next update) } else if (newState === 'attacking') { // don't fire directly, consider a part of fire delay self.lastFireTime = Date.now() - self.fireDelay * 0.5; // Entering attacking state self.debugText.tint = 0xFFA500; // Change tint to orange when attacking self.droneScanLaser.tint = 0xFFA500; // Change tint to orange when attacking // Start following the knight self.followKnight(); } }; self.init = function () { log("Drone initialized at position:", self.x, self.y); // Reset state flags self.isRotating = false; self.isMoving = false; self.needsRotation = true; // Start the animations animateScanBar(); loopScanLaserTint(); // Switch to scanning state to begin patrol self.switchState('scanning'); }; self.collectBonus = function collectBonus(bonus) { // Increase knight's health by 2 knight.health += 2; // Play knight-bonus sound LK.getSound('knight-bonus').play(); // Destroy the bonus bonus.destroy(); }; self.update = function () { if (self.state === 'scanning') { self.updateScanning(); } else if (self.state === 'attacking') { self.updateAttacking(); } // Update health bar width and color regardless of state self.healthBar.width = Math.max(0, (self.health + 1) / 4 * 100); // Assuming max health is 4 // Change health bar color based on health var healthPercentage = self.health / 4; // Assuming max health is 4 var red, green; // Transition from green to orange if (healthPercentage > 0.5) { red = Math.min(255, Math.floor((1 - healthPercentage) * 2 * 255)); green = 255; } else { // Transition from orange to red red = 255; green = Math.min(255, Math.floor(healthPercentage * 2 * 255)); } self.healthBar.tint = red << 16 | green << 8; // Combine red and green to form the color // Iterate over all bonuses in the middlegroundContainer for (var i = middlegroundContainer.children.length - 1; i >= 0; i--) { var child = middlegroundContainer.children[i]; if (child instanceof Bonus) { // Check if knight's boundingBox intersects with bonusLight if (child.active && knight.boundingBox.intersects(child.bonusLight)) { self.collectBonus(child); } } } // Update shadow position shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation); shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation); // Update droneScanBar width based on its rotation self.droneScanBar.width = self.droneScanLaser.width * (1 + 0.1 * Math.abs(Math.sin(self.droneScanBar.rotation))); }; // Function to perform a full 360Β° scan self.performFullScan = function (callback) { // Calculate duration based on a consistent rotation speed var scanRotationSpeed = 1.0; // Radians per second (slower for scanning) var fullRotationDuration = Math.PI * 2 / scanRotationSpeed * 1000; // ms // Store current rotation var startRotation = self.rotation; // Mark that we're rotating to prevent updateScanning from starting other rotations self.isRotating = true; log("Performing 360Β° scan"); // Perform a full 360Β° rotation tween(self, { rotation: startRotation + Math.PI * 2 // Full 360Β° rotation }, { duration: fullRotationDuration, easing: tween.linear, // Linear easing for constant speed onFinish: function onFinish() { // Clear rotation flag when done self.isRotating = false; log("Full scan complete"); // Call callback if provided if (callback) { callback(); } } }); }; // Handle scanning mode behavior self.updateScanning = function () { // Check for intersection with knight if (!isDied && self.droneScanBar.intersects(knight.boundingBox)) { self.switchState('attacking'); log("Drone switched to attacking mode!"); return; } // If no target is set, select one if (!self.target) { log("No target set, selecting a new one"); // If we just switched from attacking to scanning mode, do a 360Β° scan first if (self.justSwitchedFromAttacking) { self.justSwitchedFromAttacking = false; self.fire(); // Perform full scan before selecting a new target self.performFullScan(function () { // After scan is complete, select a new target self.selectTarget(); }); return; } // Otherwise just select a new target self.selectTarget(); return; } // Calculate direction and distance to target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If very close to target, select a new one if (distance < 10) { log("Reached target, selecting a new one"); self.selectTarget(); return; } // Calculate target angle var targetAngle = Math.atan2(dy, dx); // Check if rotation is needed var angleDiff = Math.abs(calculateAngleDiff(targetAngle, self.rotation)); // If rotation is needed (more than 0.1 radians difference or flag is set) if (angleDiff > 0.1 || self.needsRotation) { // Only start a new rotation if not already rotating if (!self.isRotating) { log("Rotating to face target"); self.isRotating = true; // Use the updateRotation function with callback to clear isRotating flag self.updateRotation(targetAngle, 600, function () { self.isRotating = false; self.needsRotation = false; log("Rotation complete"); }); } } // If rotation is close enough and we're not currently moving, start movement else if (!self.isMoving) { log("Starting movement to target"); self.isMoving = true; // Calculate duration based on distance var moveSpeed = 0.2; // Adjust this value to control drone speed var duration = distance / moveSpeed; // Start moving toward the target tween(self, { x: self.target.x, y: self.target.y }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { self.isMoving = false; log("Movement complete"); // Don't automatically select a new target, let updateScanning handle it } }); } }; // Handle attacking mode behavior self.updateAttacking = function () { var currentTime = Date.now(); // Fire using separate counter if (currentTime - self.lastFireTime > self.fireDelay) { // Fire every 1.2 seconds self.lastFireTime = currentTime; self.fire(); } var dx = knight.x - self.x; var dy = knight.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); // Drone rotation logic - directly rotate the drone to face the knight // Only do smooth rotation tracking when not already moving to a new position //self.lastRotationTime = currentTime; // Calculate a smoother intermediate angle for natural tracking var currentAngle = self.rotation; var angleDiff = calculateAngleDiff(angle, currentAngle); // Only rotate a portion of the way to the target for smoother tracking // This creates a slight lag effect that looks more natural var partialAngle = currentAngle + angleDiff * 0.3; // Directly set rotation instead of using updateRotation self.rotation = partialAngle; //self.isRotating = false; // No need for callback since we're setting directly // Rotate droneScanBar to point at knight within limits var relativeAngle = calculateAngleDiff(angle, self.rotation); // Clamp the scan bar rotation to the maximum allowed range var maxRotation = Math.PI / 6.5; var clampedRotation = Math.max(-maxRotation, Math.min(maxRotation, relativeAngle)); // Set the scan bar rotation directly self.droneScanBar.rotation = clampedRotation; self.droneScanBar.alpha = 0.1; // First check if knight is still in range - do this every second if (currentTime - self.lastKnightCheckTime > 1000) { self.lastKnightCheckTime = currentTime; // Check if we can still see the knight if (!self.droneScanBar.intersects(knight.boundingBox)) { log("Knight lost! Returning to scanning mode."); self.switchState('scanning'); return; } log("Knight still in range at distance: " + distance); } // Position update logic - if we're not currently moving but need to if (isDied) { log("Knight is dead, returning to scanning mode."); self.switchState('scanning'); return; } if (!self.isMoving && distance > 550) { log("Knight moved - updating drone position"); // Move to a better position self.followKnight(); return; } }; self.followKnight = function () { log("followKnight..."); // Cancel any existing movement tweens tween.stop(self); // Calculate direction to knight var dx = knight.x - self.x; var dy = knight.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); // Set the movement flag self.isMoving = true; self.isRotating = true; // After rotation is complete, start moving toward knight self.moveTowardKnight(); }; // Function to calculate distance to knight self.distanceToKnight = function () { var dx = knight.x - self.x; var dy = knight.y - self.y; return Math.sqrt(dx * dx + dy * dy); }; self.moveTowardKnight = function () { var dx = knight.x - self.x; var dy = knight.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If already within the desired distance, stop moving if (distance <= 500) { return; } // Calculate target position that's 500 units away from knight var ratio = (distance - 500) / distance; var targetX = self.x + dx * ratio; var targetY = self.y + dy * ratio; // Move toward the target tween(self, { x: targetX, y: targetY }, { duration: distance * 2, // Speed based on distance easing: tween.linear, onUpdate: function onUpdate() { // Update rotation to continuously face the knight during movement var currentDx = knight.x - self.x; var currentDy = knight.y - self.y; var currentDistance = Math.sqrt(currentDx * currentDx + currentDy * currentDy); // Only update rotation if not very close to the target if (currentDistance > 10) { var currentAngle = Math.atan2(currentDy, currentDx); // Calculate target rotation with a smaller adjustment for smoothness using the utility function var newRotation = self.rotation + calculateAngleDiff(currentAngle, self.rotation) * 0.1; // Create a separate quick tween for the rotation tween(self, { rotation: newRotation }, { duration: 600, // Very short duration for responsive updates easing: tween.linear }); } }, onFinish: function onFinish() { // Check if we're still in attacking mode before continuing to follow if (self.state === 'attacking') { // Check if knight has moved and we need to follow again if (self.distanceToKnight() > 500) { self.moveTowardKnight(); } } } }); }; self.updateRotation = function (targetAngle, maxDuration, callback) { maxDuration = maxDuration || 500; // Default max duration if not specified // Cancel any existing rotation tweens to prevent conflicts tween.stop(self, 'rotation'); // Normalize angles to be between -PI and PI for proper comparison var normalizedCurrentAngle = self.rotation % (2 * Math.PI); if (normalizedCurrentAngle > Math.PI) { normalizedCurrentAngle -= 2 * Math.PI; } if (normalizedCurrentAngle < -Math.PI) { normalizedCurrentAngle += 2 * Math.PI; } // Normalize target angle var normalizedTargetAngle = targetAngle % (2 * Math.PI); if (normalizedTargetAngle > Math.PI) { normalizedTargetAngle -= 2 * Math.PI; } if (normalizedTargetAngle < -Math.PI) { normalizedTargetAngle += 2 * Math.PI; } // Calculate the angle difference using the normalized angles var angleDiff = calculateAngleDiff(normalizedTargetAngle, normalizedCurrentAngle); var absDiff = Math.abs(angleDiff); // If the angle difference is greater than PI, rotate in the shorter direction if (absDiff > Math.PI) { if (angleDiff > 0) { angleDiff -= 2 * Math.PI; } else { angleDiff += 2 * Math.PI; } } // Calculate duration based on angle difference to achieve constant rotation speed // Use radians per second as the speed unit var rotationSpeed = 6; // Radians per second var duration = Math.min(absDiff / rotationSpeed * 1000, maxDuration); // Ensure a minimum duration for very small rotations duration = Math.max(duration, 50); // Calculate the target rotation by adding the angle difference to the current rotation var targetRotation = self.rotation + angleDiff; // Log the rotation parameters for debugging log("Rotating: angle diff=" + absDiff + ", duration=" + duration + "ms"); // Perform the rotation tween tween(self, { rotation: targetRotation }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { log("Rotation complete"); if (callback) { callback(); } } }); }; self.takeHit = function (bullet) { // Play drone-hit sound LK.getSound('drone-hit').play(); // Flash the drone red to indicate damage droneGraphics.tint = 0xff0000; // Red tint // After a short delay, restore the original tint LK.setTimeout(function () { droneGraphics.tint = 0xFFFFFF; // Restore original color }, 200); // Log hit for debugging log("Drone hit by attack!"); // Reduce health when hit self.health -= 1; // Check if drone is destroyed if (self.health <= 0) { log("Drone destroyed!"); self.explode(); return; } // Switch to attacking state immediately if not already attacking if (self.state !== 'attacking') { self.switchState('attacking'); } // Reset the lastKnightCheckTime to ensure we don't immediately check for knight visibility self.lastKnightCheckTime = Date.now(); // Add knockback effect based on bullet direction var knockbackDistance = 50; var knockbackAngle = bullet.rotation; // Calculate potential new position var newX = self.x + Math.cos(knockbackAngle) * knockbackDistance; var newY = self.y + Math.sin(knockbackAngle) * knockbackDistance; // Check if new position would be too close to a corner var tooCloseToCorner = false; var cornerSafeDistance = 100; // Distance to stay away from corners for (var i = 0; i < corners.length; i++) { var dx = newX - corners[i].x; var dy = newY - corners[i].y; var distToCorner = Math.sqrt(dx * dx + dy * dy); if (distToCorner < cornerSafeDistance) { tooCloseToCorner = true; break; } } // If too close to a corner, adjust the knockback to push away from corner if (tooCloseToCorner) { // Find the center of the playfield var centerX = (corners[0].x + corners[3].x) / 2; var centerY = (corners[0].y + corners[3].y) / 2; // Calculate direction toward center var dxToCenter = centerX - self.x; var dyToCenter = centerY - self.y; var angleToCenter = Math.atan2(dyToCenter, dxToCenter); // Use this angle for knockback instead newX = self.x + Math.cos(angleToCenter) * knockbackDistance; newY = self.y + Math.sin(angleToCenter) * knockbackDistance; } // Clamp new position using corners newX = Math.max(corners[0].x + cornerSafeDistance, Math.min(newX, corners[3].x - cornerSafeDistance)); newY = Math.max(corners[0].y + cornerSafeDistance, Math.min(newY, corners[3].y - cornerSafeDistance)); // Apply knockback using tween tween(self, { x: newX, y: newY }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // After knockback, immediately rotate the drone toward the knight // Calculate angle to knight var dx = knight.x - self.x; var dy = knight.y - self.y; var angleToKnight = Math.atan2(dy, dx); // Calculate the angle difference for proper duration var normalizedCurrentAngle = self.rotation % (2 * Math.PI); if (normalizedCurrentAngle > Math.PI) { normalizedCurrentAngle -= 2 * Math.PI; } if (normalizedCurrentAngle < -Math.PI) { normalizedCurrentAngle += 2 * Math.PI; } var normalizedTargetAngle = angleToKnight % (2 * Math.PI); if (normalizedTargetAngle > Math.PI) { normalizedTargetAngle -= 2 * Math.PI; } if (normalizedTargetAngle < -Math.PI) { normalizedTargetAngle += 2 * Math.PI; } var angleDiff = calculateAngleDiff(normalizedTargetAngle, normalizedCurrentAngle); if (Math.abs(angleDiff) > Math.PI) { if (angleDiff > 0) { angleDiff -= 2 * Math.PI; } else { angleDiff += 2 * Math.PI; } } // Calculate duration based on angle difference var rotationSpeed = 6; // Radians per second var absDiff = Math.abs(angleDiff); var duration = Math.min(absDiff / rotationSpeed * 1000, 500); duration = Math.max(duration, 50); // Ensure minimum duration // Rotate drone to face knight with calculated duration tween(self, { rotation: self.rotation + angleDiff }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { // Switch to attacking state if not already attacking if (self.state !== 'attacking') { self.switchState('attacking'); } // Reset the lastKnightCheckTime to ensure we don't immediately check for knight visibility self.lastKnightCheckTime = Date.now(); } }); } }); }; self.explode = function () { // Simply destroy the drone tween.stop(self); // Stop all tweens on the drone var explosion = new Explosion(); explosion.init(self.x, self.y); middlegroundContainer.addChild(explosion); // Clear scan sound interval if it exists if (self.scanSoundInterval) { LK.clearInterval(self.scanSoundInterval); self.scanSoundInterval = null; } LK.getSound('drone-explode').play(); // Spawn a bonus at the drone's position var bonus = new Bonus(self.x, self.y); middlegroundContainer.addChild(bonus); self.destroy(); droneManager.start(); }; }); var Explosion = Container.expand(function () { var self = Container.call(this); for (var i = 0; i < 30; i++) { var particle = LK.getAsset('explosionParticle', { anchorX: 0.5, anchorY: 0.5 }); var scale = Math.random() * 0.5 + 0.5; // Random scale between 0.5 and 1.0 particle.scale.set(scale); var colors = [0xFF4500, 0xFF6347, 0xFFFFFF, 0xFFFF00, 0x8B0000]; // Array of colors: orange, red, white, yellow, dark red particle.tint = colors[Math.floor(Math.random() * colors.length)]; // Randomly choose a color from the array var angle = Math.random() * Math.PI * 2; // Random direction var speed = Math.random() * 5 + 100; // Random speed between 2 and 7 particle.vx = Math.cos(angle) * speed; particle.vy = Math.sin(angle) * speed; particle.rotation = angle; // Rotate particle in the direction of movement self.addChild(particle); } // Initialize explosion properties self.init = function (x, y) { self.x = x; self.y = y; self.scale.set(0.8); // Start small self.alpha = 1.0; // Fully visible // Animate the explosion var particles = self.children; var duration = 2000; // Duration for particles to move and fade out particles.forEach(function (particle) { tween(particle, { x: particle.x + particle.vx * duration / 1000, y: particle.y + particle.vy * duration / 1000, alpha: 0.0 }, { duration: duration, easing: tween.easeOut }); }); LK.setTimeout(function () { self.destroy(); // Remove explosion after animation }, duration); }; }); var ExplosionKnight = Container.expand(function () { var self = Container.call(this); for (var i = 0; i < 80; i++) { var particle = LK.getAsset('explosionParticle', { anchorX: 0.5, anchorY: 0.5 }); var scale = Math.random() * 0.5 + 0.5; // Random scale between 0.5 and 1.0 particle.scale.set(scale); particle.tint = 0x000000; // Set particle color to black var speed = 100 + Math.random() * 100; // Random speed between 2 and 7 particle.vx = 0; // No horizontal movement particle.vy = -speed; // Negative vertical movement particle.rotation = Math.PI / 2; // Rotate by PI/2 self.addChild(particle); } // Initialize explosion properties self.init = function (x, y) { self.x = x; self.y = y; self.scale.set(0.8); // Start small self.alpha = 1.0; // Fully visible // Animate the explosion var particles = self.children; var duration = 2000; // Duration for particles to move and fade out particles.forEach(function (particle) { particle.x += -40 + Math.random() * 80; particle.y += -40 + Math.random() * 80; tween(particle, { x: particle.x + particle.vx * duration / 500, y: particle.y + particle.vy * duration / 500, alpha: 0.0, scaleY: 0 }, { duration: duration, easing: tween.easeOut }); }); LK.setTimeout(function () { self.destroy(); // Remove explosion after animation }, duration); }; }); var Knight = Container.expand(function () { var self = Container.call(this); self.health = isDebug ? 10 : 3; // Initialize health property with a value of 3 // Add boundingBox for collision detection self.boundingBox = LK.getAsset('boundingBox', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 150, y: -50 }); self.addChild(self.boundingBox); self.boundingBox.alpha = isDebug ? 0.5 : 0; // Set to false if you don't want it visible var directionMapping = { 'down-left': 'dir1', 'left': 'dir2', 'up-left': 'dir3', 'up': 'dir4', 'up-right': 'dir5', 'right': 'dir6', 'down-right': 'dir7', 'down': 'dir8' }; // Pre-create a list of assets for each direction var knightAssets = {}; var shadowAssets = {}; // Shadow assets for the knight var knightIdleAssets = {}; // Idle assets for the knight var shadowIdleAssets = {}; // Shadow idle assets for the knight var knightAttackAssets = {}; // Attack assets for the knight var shadowAttackAssets = {}; // Shadow attack assets for the knight var color = 0xFFFFFF; // Original blue / 0xff4d4d; // Red / 0x9aff9a; // Green // Initialize run animation assets for (var dir in directionMapping) { knightAssets[dir] = []; shadowAssets[dir] = []; // Initialize shadow assets array for each direction knightAttackAssets[dir] = []; // Initialize attack assets array for each direction shadowAttackAssets[dir] = []; // Initialize shadow attack assets array for each direction // Load run animation frames (8 frames) for (var i = 1; i <= 8; i++) { var frameNumber = i.toString().padStart(3, '0'); // Create knight sprite knightAssets[dir].push(LK.getAsset('knight-run-' + directionMapping[dir] + '-' + frameNumber, { anchorX: 0.5, anchorY: 0.5, tint: color })); // Create shadow sprite using the same assets but with modifications var shadowSprite = LK.getAsset('knight-run-' + directionMapping[dir] + '-' + frameNumber, { anchorX: 0.5, anchorY: 0.5 }); // Apply shadow properties shadowSprite.alpha = 0.5; shadowSprite.tint = 0x000000; shadowSprite.rotation = Math.PI / 12; // Rotate by 15 degrees (Ο/12 radians) shadowSprite.scale.y = 0.5; // Flatten the shadow vertically shadowAssets[dir].push(shadowSprite); } // Load attack animation frames (15 frames) for (var i = 1; i <= 15; i++) { var frameNumber = i.toString().padStart(3, '0'); // Create attack sprite knightAttackAssets[dir].push(LK.getAsset('knight-attack-' + directionMapping[dir] + '-' + frameNumber, { anchorX: 0.5, anchorY: 0.5, tint: color })); // Create shadow attack sprite using the same assets but with modifications var shadowAttackSprite = LK.getAsset('knight-attack-' + directionMapping[dir] + '-' + frameNumber, { anchorX: 0.5, anchorY: 0.5 }); // Apply shadow properties shadowAttackSprite.alpha = 0.5; shadowAttackSprite.tint = 0x000000; shadowAttackSprite.rotation = Math.PI / 12; // Rotate by 15 degrees (Ο/12 radians) shadowAttackSprite.scale.y = 0.5; // Flatten the shadow vertically shadowAttackAssets[dir].push(shadowAttackSprite); } // Initialize idle animation assets (one frame per direction) knightIdleAssets[dir] = LK.getAsset('knight-idle-' + directionMapping[dir] + '-001', { anchorX: 0.5, anchorY: 0.5, tint: color }); // Create shadow for idle animation var shadowIdleSprite = LK.getAsset('knight-idle-' + directionMapping[dir] + '-001', { anchorX: 0.5, anchorY: 0.5 }); // Apply shadow properties shadowIdleSprite.alpha = 0.5; shadowIdleSprite.tint = 0x000000; shadowIdleSprite.rotation = Math.PI / 12; shadowIdleSprite.scale.y = 0.5; shadowIdleAssets[dir] = shadowIdleSprite; } var currentFrame = 0; var animationSpeed = 0.2; // Controls how fast the animation plays var frameCounter = 0; var knightGraphics = knightIdleAssets['down']; // Initialize with the 'down' direction idle asset var shadowGraphics = shadowIdleAssets['down']; // Initialize shadow with the 'down' direction idle asset // Add health bar under the knight self.healthBar = LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 10, color: 0x00FF00 // Green color for health }); self.healthBar.y = 60; // Position the health bar under the knight self.addChild(self.healthBar); // Add shadow first (so it appears behind the knight) self.addChild(shadowGraphics); // Then add the knight self.addChild(knightGraphics); knightGraphics.anchor.set(0.5, 0.5); knightGraphics.visible = true; shadowGraphics.visible = true; // Position the shadow slightly offset from the knight var shadowOffsetX = -5; var shadowOffsetY = 20; shadowGraphics.x = shadowOffsetX; shadowGraphics.y = shadowOffsetY; self.lastDirection = 'down'; // Initialize lastDirection to track previous direction self.isMoving = false; // Track if the knight is currently moving self.isAttacking = false; // Track if the knight is currently attacking self.speed = 9; var distanceThreshold = 8; // Define a threshold to avoid small movements var directionThreshold = 5; // Define a threshold for direction changes to avoid shaking self.update = function () { // Update health bar width based on knight's health self.healthBar.width = Math.max(0, (self.health + 1) / 4 * 100); // Assuming max health is 3 // Change health bar color based on health var healthPercentage = self.health / 3; // Assuming max health is 3 var red, green; // Transition from green to orange if (healthPercentage > 0.5) { red = Math.min(255, Math.floor((1 - healthPercentage) * 2 * 255)); green = 255; } else { // Transition from orange to red red = 255; green = Math.min(255, Math.floor(healthPercentage * 2 * 255)); } self.healthBar.tint = red << 16 | green << 8; // Combine red and green to form the color // Don't process movement if attacking if (isDied || self.health < 0 || self.isAttacking) { return; } var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > distanceThreshold) { // Set knight to moving state if not already moving if (!self.isMoving) { self.isMoving = true; self.switchAsset(self.lastDirection || 'down'); // Update asset to running animation } // Determine the primary direction based on the larger component (dx or dy) if (Math.abs(dx) > Math.abs(dy) + directionThreshold) { // Horizontal movement dominates if (dx > 0) { if (dy > directionThreshold) { self.moveToDirection('down-right'); } else if (dy < -directionThreshold) { self.moveToDirection('up-right'); } else { self.moveToDirection('right'); } } else { if (dy > directionThreshold) { self.moveToDirection('down-left'); } else if (dy < -directionThreshold) { self.moveToDirection('up-left'); } else { self.moveToDirection('left'); } } } else if (Math.abs(dy) > Math.abs(dx) + directionThreshold) { // Vertical movement dominates if (dy > 0) { if (dx > directionThreshold) { self.moveToDirection('down-right'); } else if (dx < -directionThreshold) { self.moveToDirection('down-left'); } else { self.moveToDirection('down'); } } else { if (dx > directionThreshold) { self.moveToDirection('up-right'); } else if (dx < -directionThreshold) { self.moveToDirection('up-left'); } else { self.moveToDirection('up'); } } } else { // The difference between dx and dy is small, use diagonal movement if (dx > 0 && dy > 0) { self.moveToDirection('down-right'); } else if (dx > 0 && dy < 0) { self.moveToDirection('up-right'); } else if (dx < 0 && dy > 0) { self.moveToDirection('down-left'); } else if (dx < 0 && dy < 0) { self.moveToDirection('up-left'); } } } else { // Check if knight is not moving and distance to drone is less than 400 if (!self.isMoving) { if (droneManager && droneManager.drones.length > 0) { var drone = droneManager.drones[0]; // Assuming single drone for simplicity var dx = drone.x - self.x; var dy = drone.y - self.y; var distanceToDrone = Math.sqrt(dx * dx + dy * dy); } if (distanceToDrone < 400) { // Calculate angle to orient knight towards drone var angleToDrone = Math.atan2(dy, dx); var directionToDrone; if (angleToDrone >= -Math.PI / 8 && angleToDrone < Math.PI / 8) { directionToDrone = 'right'; } else if (angleToDrone >= Math.PI / 8 && angleToDrone < 3 * Math.PI / 8) { directionToDrone = 'down-right'; } else if (angleToDrone >= 3 * Math.PI / 8 && angleToDrone < 5 * Math.PI / 8) { directionToDrone = 'down'; } else if (angleToDrone >= 5 * Math.PI / 8 && angleToDrone < 7 * Math.PI / 8) { directionToDrone = 'down-left'; } else if (angleToDrone >= 7 * Math.PI / 8 || angleToDrone < -7 * Math.PI / 8) { directionToDrone = 'left'; } else if (angleToDrone >= -7 * Math.PI / 8 && angleToDrone < -5 * Math.PI / 8) { directionToDrone = 'up-left'; } else if (angleToDrone >= -5 * Math.PI / 8 && angleToDrone < -3 * Math.PI / 8) { directionToDrone = 'up'; } else if (angleToDrone >= -3 * Math.PI / 8 && angleToDrone < -Math.PI / 8) { directionToDrone = 'up-right'; } if (directionToDrone) { self.switchAsset(directionToDrone); } } } // Set knight to idle state if currently moving if (self.isMoving) { self.isMoving = false; self.switchAsset(self.lastDirection || 'down'); // Update asset to idle animation } } // Update knight animation self.updateAnimation(); }; self.switchAsset = function (direction) { if (!knightGraphics) { return; } // If currently attacking, don't switch assets if (isDied || self.health < 0 || self.isAttacking) { return; } // Detach current assets // Hide current assets knightGraphics.visible = false; shadowGraphics.visible = false; // Switch to new assets based on direction and movement state if (self.isMoving) { // Use running animation knightGraphics = knightAssets[direction][currentFrame]; shadowGraphics = shadowAssets[direction][currentFrame]; } else { // Use idle animation knightGraphics = knightIdleAssets[direction]; shadowGraphics = shadowIdleAssets[direction]; } self.addChild(shadowGraphics); self.addChild(knightGraphics); // Show new assets knightGraphics.visible = true; shadowGraphics.visible = true; // Position the shadow slightly offset from the knight shadowGraphics.x = shadowOffsetX; shadowGraphics.y = shadowOffsetY; // Update last direction self.lastDirection = direction; }; self.updateAnimation = function () { // If currently attacking, don't switch assets if (isDied || self.health < 0 || self.isAttacking) { return; } if (self.isMoving) { // Only update animation if moving frameCounter += animationSpeed; if (frameCounter >= 1) { frameCounter = 0; currentFrame = (currentFrame + 1) % 8; // Cycle through 8 frames if (self.lastDirection) { self.switchAsset(self.lastDirection); } } } else if (self.lastDirection) { // Make sure we're showing the idle animation when not moving self.switchAsset(self.lastDirection); } }; self.moveToDirection = function (direction) { if (isDied || self.health < 0 || self.isAttacking) { return; } if (self.lastDirection !== direction) { self.switchAsset(direction); self.lastDirection = direction; } switch (direction) { case 'up': self.y -= self.speed; break; case 'down': self.y += self.speed; break; case 'left': self.x -= self.speed; break; case 'right': self.x += self.speed; break; case 'up-left': self.x -= self.speed / Math.sqrt(2); self.y -= self.speed / Math.sqrt(2); break; case 'up-right': self.x += self.speed / Math.sqrt(2); self.y -= self.speed / Math.sqrt(2); break; case 'down-left': self.x -= self.speed / Math.sqrt(2); self.y += self.speed / Math.sqrt(2); break; case 'down-right': self.x += self.speed / Math.sqrt(2); self.y += self.speed / Math.sqrt(2); break; } }; self.takeHit = function (bullet) { if (isDied) { return; } // Flash the knight red to indicate damage self.tint = 0xff0000; // Red tint // After a short delay, restore the original tint LK.setTimeout(function () { self.tint = color; // Restore original color }, 200); // Optional: Add knockback effect based on bullet direction var knockbackDistance = 30; var knockbackAngle = bullet.rotation; // Apply knockback self.x += Math.cos(knockbackAngle) * knockbackDistance; self.y += Math.sin(knockbackAngle) * knockbackDistance; self.health--; if (self.health < 0) { self.dieAnim(); } else { // Play knight-hit sound LK.getSound('knight-hit').play(); } // Log hit for debugging log("Knight hit by laser beam!"); }; self.dieAnim = function () { // Animate knight tint to black LK.getSound('knight-die').play(); punchButton.visible = false; // Hide attackButton when the knight is dead if (isDied) { return; } isDied = true; tween(self, { tint: 0x000000 }, { duration: 1000, // 1 second duration easing: tween.easeInOut, onFinish: function onFinish() { var explosion = new ExplosionKnight(); explosion.init(self.x, self.y - 50); middlegroundContainer.addChild(explosion); LK.getSound('knight-killed').play(); self.destroy(); // Destroy knight after animation LK.setTimeout(function () { LK.showGameOver(); }, 3000); // Call game over after 2 seconds } }); }; self.attack = function () { // Check if already attacking if (self.isAttacking) { return; } // Play knight-attack sound LK.getSound('knight-attack').play(); self.isAttacking = true; log("Knight attacking!"); // Store current direction var direction = self.lastDirection || 'down'; // Get attack animation frames for current direction var attackFrames = knightAttackAssets[direction]; var shadowAttackFrames = shadowAttackAssets[direction]; // Hide current graphics // knightGraphics.visible = false; // shadowGraphics.visible = false; // Add all frames but make them invisible initially for (var i = 0; i < attackFrames.length; i++) { self.addChild(shadowAttackFrames[i]); self.addChild(attackFrames[i]); shadowAttackFrames[i].visible = false; attackFrames[i].visible = false; } // Create attack animation var currentFrame = 0; var attackInterval = LK.setInterval(function () { if (self.isDied) { return; } // Hide previous frame if it exists if (currentFrame > 0) { attackFrames[currentFrame - 1].visible = false; shadowAttackFrames[currentFrame - 1].visible = false; } // Hide current graphics knightGraphics.visible = false; shadowGraphics.visible = false; // Show current frame shadowAttackFrames[currentFrame].visible = true; attackFrames[currentFrame].visible = true; // Check for collision with drones (middle of the attack animation) if (currentFrame === Math.floor(attackFrames.length / 2)) { // Calculate attack range and direction var attackRange = 100; // Range of the knight's attack var attackDirection = direction; var attackAngle; // Determine attack angle based on direction if (attackDirection === 'right') { attackAngle = 0; } else if (attackDirection === 'down-right') { attackAngle = Math.PI / 4; } else if (attackDirection === 'down') { attackAngle = Math.PI / 2; } else if (attackDirection === 'down-left') { attackAngle = 3 * Math.PI / 4; } else if (attackDirection === 'left') { attackAngle = Math.PI; } else if (attackDirection === 'up-left') { attackAngle = 5 * Math.PI / 4; } else if (attackDirection === 'up') { attackAngle = 3 * Math.PI / 2; } else if (attackDirection === 'up-right') { attackAngle = 7 * Math.PI / 4; } // Calculate attack point in front of the knight var attackX = self.x + Math.cos(attackAngle) * attackRange; var attackY = self.y + Math.sin(attackAngle) * attackRange; // Check if any drone is within attack range for (var i = 0; i < droneManager.drones.length; i++) { var drone = droneManager.drones[i]; var dx = drone.x - attackX; var dy = drone.y - attackY; var distance = Math.sqrt(dx * dx + dy * dy); // If drone is within attack range, call its takeHit method if (distance < 120) { // Adjust hit radius as needed drone.takeHit({ rotation: attackAngle // Pass attack angle for knockback direction }); } } } // Move to next frame currentFrame++; // Check if animation is complete if (currentFrame >= attackFrames.length) { // Clear interval LK.clearInterval(attackInterval); // Hide all attack frames for (var i = 0; i < attackFrames.length; i++) { attackFrames[i].visible = false; shadowAttackFrames[i].visible = false; } // Show regular graphics again knightGraphics.visible = true; shadowGraphics.visible = true; // Reset attacking flag self.isAttacking = false; // Reset the global target position to knight's current position target.x = self.x; target.y = self.y; } }, 50); // 50ms per frame for a fast attack animation }; }); var LaserBeam = Container.expand(function () { var self = Container.call(this); var laserBeamGraphics = LK.getAsset('droneLaserBeam', { anchorX: 0.5, anchorY: 0.5, blendMode: 1 }); self.addChild(laserBeamGraphics); self.speed = 1500; // Speed of the laser beam in pixels per second self.fire = function (x, y, rotation) { self.x = x; self.y = y; self.rotation = rotation; middlegroundContainer.addChild(self); // Calculate the distance the laser beam will travel var distance = 3000; // Calculate the duration based on speed var duration = distance / self.speed * 1000; // Convert to milliseconds // Tween the laser beam to move forward tween(self, { x: self.x + Math.cos(self.rotation) * distance, y: self.y + Math.sin(self.rotation) * distance }, { duration: duration, easing: tween.linear, onFinish: function onFinish() { self.destroy(); } }); }; self.update = function () { if (self.intersects(knight.boundingBox)) { knight.takeHit(self); self.destroy(); } }; }); var Room = Container.expand(function () { var self = Container.call(this); var background = LK.getAsset('backgroundWalls', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(background); self.x = 2048 / 2; self.y = 2732 / 2; }); var Target = Container.expand(function () { var self = Container.call(this); self.x = 0; self.y = 0; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 //Init game with black background }); /**** * Game Code ****/ // Utility function to calculate the angle difference function calculateAngleDiff(targetAngle, currentAngle) { var angleDiff = targetAngle - currentAngle; // Normalize angle difference to [-PI, PI] while (angleDiff > Math.PI) { angleDiff -= 2 * Math.PI; } while (angleDiff < -Math.PI) { angleDiff += 2 * Math.PI; } return angleDiff; } //<Write entity 'classes' with empty functions for important behavior here> //<Write imports for supported plugins here> //<Assets used in the game will automatically appear here> //<Write game logic code here, including initializing arrays and variables> var DroneManager = function DroneManager() { var self = this; self.droneCounter = 0; // Initialize droneCounter to track the number of drones self.drones = []; self.start = function () { // Clear previous drones from the scene and the drones array self.drones.forEach(function (drone) { drone.destroy(); }); self.drones = []; // Create the drone var drone = new Drone(self.droneCounter); // Pass the current level to the Drone constructor // Increment counter after drone creation so first drone is level 0 self.droneCounter++; // Check win condition after incrementing counter if (self.droneCounter > 3) { LK.getSound('knight-win').play(); // Play knight-win sound LK.setTimeout(function () { LK.showYouWin(); // Show win screen after 2000ms delay }, 2000); return; } // Explicitly set position before adding to the scene drone.x = Math.random() < 0.5 ? -1024 : 3072; drone.y = 1366; // Store the current target index //drone.currentTargetIndex = currentTargetIndex; // Set the initial target to the current corner position drone.target = { x: 1024, y: 1366 }; // Add to container and array middlegroundContainer.addChild(drone); self.drones.push(drone); // This ensures all properties are set before tweening begins LK.setTimeout(function () { drone.init(); }, 100); }; }; /**** * Global variables ****/ var isDebug = true; var isDied = false; // Global flag to track if the knight has died var gameStarted = false; var droneManager = new DroneManager(); var backgroundContainer; var middlegroundContainer; var foregroundContainer; var knight; var background; // Declare a global variable for the background var target; var punchButton; var runSoundInterval; var borderOffset = 380; var corners = [{ x: borderOffset, y: borderOffset }, { x: 2048 - borderOffset, y: borderOffset }, { x: borderOffset, y: 2732 - borderOffset }, { x: 2048 - borderOffset, y: 2732 - borderOffset }]; function log() { if (isDebug) { console.log.apply(console, arguments); } } function initializeGame() { // Create containers backgroundContainer = new Container(); middlegroundContainer = new Container(); foregroundContainer = new Container(); // Add containers to game game.addChild(backgroundContainer); game.addChild(middlegroundContainer); game.addChild(foregroundContainer); // Initialize background background = new Background(); // Use Background class instead of LK.getAsset backgroundContainer.addChild(background); // Initialize room var room = new Room(); backgroundContainer.addChild(room); // Initialize knight knight = new Knight(); knight.x = 2048 / 2; knight.y = 2732 / 2; middlegroundContainer.addChild(knight); // Initialize target target = new Target(); target.x = knight.x; target.y = knight.y; foregroundContainer.addChild(target); // Initialize punch button punchButton = new ButtonPunch(); punchButton.x = 2048 - 220; punchButton.y = 220; foregroundContainer.addChild(punchButton); // Initialize sound runSoundInterval = LK.setInterval(playRunSound, 350); droneManager = new DroneManager(); LK.setTimeout(function () { gameStarted = true; droneManager.start(); }, 1000); } function playRunSound() { if (knight.isMoving) { LK.getSound('knight-run').play(); } } initializeGame();
===================================================================
--- original.js
+++ change.js
@@ -88,43 +88,47 @@
});
}
loopMove();
};
- self.animMove();
self.throwToCorner = function () {
- // Randomly select one of the corners
- var targetCorner = corners[Math.floor(Math.random() * corners.length)];
- // For a top-down view, we want the object to slide along the ground
- // rather than appearing to jump in the air
- // Calculate the angle between current position and target
- var dx = targetCorner.x - self.x;
- var dy = targetCorner.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- var angle = Math.atan2(dy, dx);
- // Create a tween for realistic sliding motion
+ // Calculate a random position within range instead of using corners
+ var minDistance = 1024;
+ var maxDistance = 1480;
+ // Generate random angle
+ var randomAngle = Math.random() * Math.PI * 2;
+ // Calculate random distance within range
+ var randomDistance = minDistance + Math.random() * (maxDistance - minDistance);
+ // Calculate target position
+ var targetX = self.x + Math.cos(randomAngle) * randomDistance;
+ var targetY = self.y + Math.sin(randomAngle) * randomDistance;
+ // Ensure target is within the playable area (within corners)
+ targetX = Math.max(borderOffset, Math.min(2048 - borderOffset, targetX));
+ targetY = Math.max(borderOffset, Math.min(2732 - borderOffset, targetY));
+ // Calculate control point for parabolic motion
+ var controlX = (self.x + targetX) / 2;
+ var controlY = Math.min(self.y, targetY) - 500; // Control point above the line for a parabolic effect
+ // Initial position
+ var startX = self.x;
+ var startY = self.y;
+ // Create a tween for parabolic motion
tween(self, {
- x: targetCorner.x,
- y: targetCorner.y
+ x: targetX,
+ y: targetY
}, {
- duration: 2000,
+ duration: 1200,
easing: tween.easeOutQuad,
onUpdate: function onUpdate() {
- // Calculate position along a straight line with a slight curve
+ // Calculate parabolic path using quadratic Bezier curve formula
var t = this.progress;
- // Apply a slight curve to the path for more natural movement
- var curveAmount = Math.sin(t * Math.PI) * 30; // Smaller curve amount for subtle effect
- // Calculate base position along straight line
- var newX = self.x + (targetCorner.x - self.x) * t;
- var newY = self.y + (targetCorner.y - self.y) * t;
- // Apply slight perpendicular offset for curved path
- self.x = newX + Math.sin(angle + Math.PI / 2) * curveAmount;
- self.y = newY - Math.cos(angle + Math.PI / 2) * curveAmount;
- // Add slight rotation to simulate the object sliding/rolling
- self.rotation = t * Math.PI * 0.5; // Half rotation during the movement
+ self.x = Math.pow(1 - t, 2) * startX + 2 * (1 - t) * t * controlX + Math.pow(t, 2) * targetX;
+ self.y = Math.pow(1 - t, 2) * startY + 2 * (1 - t) * t * controlY + Math.pow(t, 2) * targetY;
+ // Add rotation for visual effect
+ self.rotation = t * Math.PI * 3; // 1.5 full rotations during flight
},
onFinish: function onFinish() {
- self.rotation = 0; // Reset rotation at the end
+ self.rotation = 0; // Reset rotation
self.active = true; // Activate bonus after reaching the corner
+ self.animMove();
}
});
};
self.throwToCorner(); // Call the function to start the animation
@@ -150,9 +154,9 @@
self.level = level || 0; // Set level to the passed parameter or default to 0
self.health = (isDebug ? 2 : 4) + self.level * 2; // Add level*2 to drone's health
var droneWidth = 200;
// Increase size based on level
- var levelGrowth = self.level * self.level * 100;
+ var levelGrowth = self.level * self.level * 80;
self.droneScanLaser = LK.getAsset('droneScanLaser', {
anchorX: 0,
anchorY: 0.5
});
@@ -351,45 +355,43 @@
};
self.collectBonus = function collectBonus(bonus) {
// Increase knight's health by 2
knight.health += 2;
+ // Play knight-bonus sound
+ LK.getSound('knight-bonus').play();
// Destroy the bonus
bonus.destroy();
};
self.update = function () {
if (self.state === 'scanning') {
self.updateScanning();
} else if (self.state === 'attacking') {
- // Update function to check for intersection between knight and bonusLight
self.updateAttacking();
- //<Write game logic code here, including initializing arrays and variables>
- // Function to handle bonus collection
- game.update = function () {
- // Iterate over all bonuses in the middlegroundContainer
- for (var i = middlegroundContainer.children.length - 1; i >= 0; i--) {
- var child = middlegroundContainer.children[i];
- if (child instanceof Bonus) {
- // Check if knight's boundingBox intersects with bonusLight
- if (child.active && knight.boundingBox.intersects(child.bonusLight)) {
- self.collectBonus(child);
- }
- }
+ }
+ // Update health bar width and color regardless of state
+ self.healthBar.width = Math.max(0, (self.health + 1) / 4 * 100); // Assuming max health is 4
+ // Change health bar color based on health
+ var healthPercentage = self.health / 4; // Assuming max health is 4
+ var red, green;
+ // Transition from green to orange
+ if (healthPercentage > 0.5) {
+ red = Math.min(255, Math.floor((1 - healthPercentage) * 2 * 255));
+ green = 255;
+ } else {
+ // Transition from orange to red
+ red = 255;
+ green = Math.min(255, Math.floor(healthPercentage * 2 * 255));
+ }
+ self.healthBar.tint = red << 16 | green << 8; // Combine red and green to form the color
+ // Iterate over all bonuses in the middlegroundContainer
+ for (var i = middlegroundContainer.children.length - 1; i >= 0; i--) {
+ var child = middlegroundContainer.children[i];
+ if (child instanceof Bonus) {
+ // Check if knight's boundingBox intersects with bonusLight
+ if (child.active && knight.boundingBox.intersects(child.bonusLight)) {
+ self.collectBonus(child);
}
- };
- self.healthBar.width = Math.max(0, (self.health + 1) / 4 * 100); // Assuming max health is 4
- // Change health bar color based on health
- var healthPercentage = self.health / 4; // Assuming max health is 4
- var red, green;
- // Transition from green to orange
- if (healthPercentage > 0.5) {
- red = Math.min(255, Math.floor((1 - healthPercentage) * 2 * 255));
- green = 255;
- } else {
- // Transition from orange to red
- red = 255;
- green = Math.min(255, Math.floor(healthPercentage * 2 * 255));
}
- self.healthBar.tint = red << 16 | green << 8; // Combine red and green to form the color
}
// Update shadow position
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
knight-run
Sound effect
knight-attack
Sound effect
drone-hit
Sound effect
knight-hit
Sound effect
drone-beam
Sound effect
drone-scan
Sound effect
drone-explode
Sound effect
knight-killed
Sound effect
knight-die
Sound effect
knight-win
Sound effect
knight-bonus
Sound effect