Code edit (2 edits merged)
Please save this source code
User prompt
animate the game container scale to 4 when knight dies by using tween with bounce easing ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
animate the game container when knight dies by using tween with bounce easing ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
animate when knight dies by using tween with bounce easing on the game container ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
call LK.showYouWin(); after 2000sec
User prompt
play knight-win before showing youWin screen
Code edit (2 edits merged)
Please save this source code
User prompt
in start(), if self.droneCounter > 3 show LK win
Code edit (3 edits merged)
Please save this source code
User prompt
add a droneCounter in droneManager
Code edit (5 edits merged)
Please save this source code
User prompt
when drone explode stop its scaning sound
Code edit (1 edits merged)
Please save this source code
User prompt
play knight-killed when knight die
User prompt
play drone-explode when drone explode
User prompt
repeat drone-scan sound every second while scanning
User prompt
when scanning play drone-scan sound
User prompt
play drone-beam when drone fire
Code edit (1 edits merged)
Please save this source code
User prompt
play kight hit in knight takeHit
User prompt
Play drone hit when drone takes hit
User prompt
Play knight-attack when attacking
User prompt
Hide debugText when not in debug mode
/**** * 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 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 () { var self = Container.call(this); self.health = isDebug ? 1 : 3; // Initialize health property with a value of 3 var droneGraphics = LK.getAsset('drone', { anchorX: 0.5, anchorY: 0.5 }); var shadowDrone = LK.getAsset('drone', { anchorX: 0.5, anchorY: 0.5 }); 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); self.droneScanLaser = LK.getAsset('droneScanLaser', { anchorX: 0.5, anchorY: 0.5 }); self.droneScanBar = LK.getAsset('droneScanBar', { anchorX: 0, anchorY: 0.5 }); self.droneScanLaser.visible = true; // Always visible self.droneScanLaser.x = droneGraphics.width * 2; self.droneScanLaser.alpha = 0.3; // Set alpha to 0.3 self.addChild(self.droneScanLaser); self.droneScanBar.x = droneGraphics.width / 2; // droneScanLaser.x; self.droneScanBar.blendMode = 3; self.droneScanBar.y = self.droneScanLaser.y; self.addChild(self.droneScanBar); 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.5 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.droneScanBar, { alpha: 0.6, rotation: -Math.PI / 6.5 }, { 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.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') { // 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.update = function () { if (self.state === 'scanning') { self.updateScanning(); } else if (self.state === 'attacking') { self.updateAttacking(); } // 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 = 620 * (1 + 0.33 * 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; // 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 > 1200) { // 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); // 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; // Clamp new position using corners newX = Math.max(corners[0].x, Math.min(newX, corners[3].x)); newY = Math.max(corners[0].y, Math.min(newY, corners[3].y)); // Apply knockback using tween tween(self, { x: newX, y: newY }, { duration: 200, easing: tween.easeOut }); // 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; } // After 1 second, rotate the drone toward the knight LK.setTimeout(function () { // 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 }); // Switch to attacking state if not already attacking if (self.state !== 'attacking') { self.switchState('attacking'); } }, 1000); }; 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(); 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 < 40; 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 = 600; // Duration for particles to move and fade out particles.forEach(function (particle) { particle.x += -30 + Math.random() * 60; particle.y += -30 + Math.random() * 60; tween(particle, { x: particle.x + particle.vx * duration / 500, y: particle.y + particle.vy * duration / 500, alpha: 0.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 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 () { // 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(); tween(self, { tint: 0x000000 }, { duration: 1000, // 1 second duration easing: tween.easeInOut, onFinish: function onFinish() { if (isDied) { return; } isDied = true; var explosion = new ExplosionKnight(); explosion.init(self.x, self.y); 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 () { // 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 = []; self.droneCounter++; 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; } // Spawn a drone in a random corner //var currentTargetIndex = Math.floor(Math.random() * corners.length); //var cornerPosition = corners[currentTargetIndex]; // Create the drone var drone = new Drone(); // 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 = false; 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
@@ -1269,8 +1269,9 @@
LK.getSound('knight-win').play(); // Play knight-win sound
LK.setTimeout(function () {
LK.showYouWin(); // Show win screen after 2000ms delay
}, 2000);
+ return;
}
// Spawn a drone in a random corner
//var currentTargetIndex = Math.floor(Math.random() * corners.length);
//var cornerPosition = corners[currentTargetIndex];
@@ -1297,9 +1298,9 @@
};
/****
* Global variables
****/
-var isDebug = true;
+var isDebug = false;
var isDied = false; // Global flag to track if the knight has died
var gameStarted = false;
var droneManager = new DroneManager();
var backgroundContainer;