User prompt
add a fire function in Drone class. use droneLaserBeam asset
Code edit (14 edits merged)
Please save this source code
User prompt
in updateAttacking, rotate droneScanBar in direction of the knight within the limit of(+/-Math.PI / 6.5)
Code edit (1 edits merged)
Please save this source code
Code edit (4 edits merged)
Please save this source code
User prompt
in updateAttacking, continuously updateRotation to target the knight, don't wait 2000 ms
User prompt
in updateAttacking, continuously updateRotation to target the knight
User prompt
in updateAttacking, updateRotation to target the knight
Code edit (8 edits merged)
Please save this source code
User prompt
log drone init and select target (with log())
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Timeout.tick error: drone.init is not a function' in or related to this line: 'drone.init();' Line Number: 777
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'onFinish')' in or related to this line: 'return tween(self, {' Line Number: 468 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'onFinish')' in or related to this line: 'return tween(self, {' Line Number: 468
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'onFinish')' in or related to this line: 'return tween(self, {' Line Number: 468
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'onFinish')' in or related to this line: 'return tween(self, {' Line Number: 468
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'x')' in or related to this line: 'tween(self, {' Line Number: 338
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'x')' in or related to this line: 'tween(self, {' Line Number: 337
Code edit (8 edits merged)
Please save this source code
User prompt
extract anglediff calculations into a global utility function
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Import the tween plugin var Drone = Container.expand(function () { var self = Container.call(this); 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() { tween(self.droneScanBar, { alpha: 0.3, rotation: Math.PI / 6.5 }, { duration: 1000, 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.addChild(self.debugText); self.fireRange = 300; // Example fire range value self.currentTargetIndex = 0; self.target = { x: 0, y: 0 }; self.lastKnightCheckTime = 0; // Track when we last checked for knight intersection // 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); var distance = Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2)); var moveSpeed = 0.2; // Adjust this value to control drone speed var duration = distance / moveSpeed; // Calculate duration based on distance and speed // Calculate rotation angle before starting movement var dx = self.target.x - self.x; var dy = self.target.y - self.y; // Use the update rotation function with a callback self.updateRotation(Math.atan2(dy, dx), 1000, function () { if (self && self.target) { tween(self, { x: self.target.x, y: self.target.y }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { log("selectTarget end, state = ", self.state); if (self.state === 'scanning') { log("auto call selectTarget"); self.selectTarget(); } } }); } }); }; // 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); // 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') { // Entering scanning state // Reset tint and other properties self.debugText.tint = 0xFFFFFF; // Reset tint to white for other states self.droneScanLaser.tint = 0x00FF00; // Reset tint to green for scanning mode // Stop any current movement tween.stop(self); // Explicitly clear the knight as target self.target = null; self.selectTarget(); // Perform a 360° rotation before returning to normal scanning mode // 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; tween(self, { rotation: startRotation + Math.PI * 2 // Full 360° rotation }, { duration: fullRotationDuration, // Use calculated duration for consistent speed easing: tween.linear, // Linear easing for constant speed onFinish: function onFinish() { // Wait a short time before selecting a new target // This prevents any leftover movement from the attacking mode LK.setTimeout(function () { // Check if we're still in scanning state (could have changed during animation) if (self.state === 'scanning') { // Select a new patrol target self.target = null; // Clear any existing target again self.selectTarget(); } }, 100); } }); } else if (newState === 'attacking') { // Entering attacking state // Change laser tint to red to indicate attacking mode 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.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))); }; // Handle scanning mode behavior self.updateScanning = function () { // Check for intersection with knight if (self.droneScanBar.intersects(knight.boundingBox)) { self.switchState('attacking'); log("Drone switched to attacking mode!"); } else if (self.target) { // Move to selected target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only update rotation if not very close to target if (distance > 10) { var targetAngle = Math.atan2(dy, dx); // Use the dedicated updateRotation function for smooth rotation self.updateRotation(targetAngle, 200, function () {}); } } }; // Handle attacking mode behavior self.updateAttacking = function () { // Continuously update rotation to target the knight var dx = knight.x - self.x; var dy = knight.y - self.y; var targetAngle = Math.atan2(dy, dx); self.updateRotation(targetAngle, 100); // Update rotation with a short duration // Check if knight is still in range if (!self.droneScanLaser.intersects(knight.boundingBox)) { log("Knight lost! Returning to scanning mode."); self.switchState('scanning'); } else { log("Knight still in range."); // Ensure drone is still following the knight // Use the distanceToKnight method instead of calculating directly var distance = self.distanceToKnight(); // If knight has moved significantly, update position if (distance > 550) { log("Knight moved - updating drone position"); self.followKnight(); } } }; self.followKnight = function () { // Cancel any existing movement tweens tween.stop(self); // Start following the 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); }; // Function to update drone rotation to face a target angle self.updateRotation = function (targetAngle, maxDuration, callback) { maxDuration = maxDuration || 500; // Default max duration if not specified // Calculate the angle difference var angleDiff = calculateAngleDiff(targetAngle, self.rotation); var absDiff = Math.abs(angleDiff); // Calculate duration based on angle difference to achieve constant rotation speed // Use radians per second as the speed unit var rotationSpeed = 3; // Radians per second (adjust as needed) var duration = Math.min(absDiff / rotationSpeed * 1000, maxDuration); // Ensure a minimum duration for very small rotations duration = Math.max(duration, 50); var targetRotation = self.rotation + angleDiff; tween(self, { rotation: targetRotation }, { duration: duration, easing: tween.easeInOut, onFinish: callback }); }; // Function to move toward knight 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) { // Even if not moving, still face the knight var angle = Math.atan2(dy, dx); // Update rotation to face the knight self.updateRotation(angle, 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; // Calculate rotation angle var angle = Math.atan2(dy, dx); // First rotate smoothly toward the target, then move self.updateRotation(angle, 400, function () { // Then move toward the target tween(self, { x: targetX, y: targetY }, { duration: distance * 2, // Speed based on distance easing: tween.easeInOut, 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: 100, // Very short duration for responsive updates easing: tween.easeInOut }); } }, 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.init = function () { log("Drone initialized at position:", self.x, self.y); animateScanBar(); // Start the loop loopScanLaserTint(); // Start the loop self.switchState('scanning'); }; }); var Knight = Container.expand(function () { var self = Container.call(this); // 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 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 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); } // 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.speed = 9; self.switchAsset = function (direction) { if (!knightGraphics) { 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 (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 (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; } }; }); 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.drones = []; self.start = function () { // 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 = cornerPosition.x; drone.y = cornerPosition.y; // Store the current target index drone.currentTargetIndex = currentTargetIndex; // Set the initial target to the current corner position drone.target = { x: cornerPosition.x, y: cornerPosition.y }; // Add to container and array middlegroundContainer.addChild(drone); self.drones.push(drone); // Call selectTarget after adding to scene to start movement // This ensures all properties are set before tweening begins LK.setTimeout(function () { drone.init(); }, 100); }; }; /**** * Global variables ****/ var isDebug = true; var gameStarted = false; var droneManager; var backgroundContainer; var middlegroundContainer; var foregroundContainer; var knight; var target; 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 var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.x = 2048 / 2; background.y = 2732 / 2; backgroundContainer.addChild(background); // 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 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(); } } game.down = function (x, y, obj) { var game_position = game.toLocal(obj.global); target.x = game_position.x; target.y = game_position.y; }; game.update = function () { var dx = target.x - knight.x; var dy = target.y - knight.y; var distance = Math.sqrt(dx * dx + dy * dy); var distanceThreshold = 8; // Define a threshold to avoid small movements var directionThreshold = 5; // Define a threshold for direction changes to avoid shaking if (distance > distanceThreshold) { // Set knight to moving state if not already moving if (!knight.isMoving) { knight.isMoving = true; knight.switchAsset(knight.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) { knight.moveToDirection('down-right'); } else if (dy < -directionThreshold) { knight.moveToDirection('up-right'); } else { knight.moveToDirection('right'); } } else { if (dy > directionThreshold) { knight.moveToDirection('down-left'); } else if (dy < -directionThreshold) { knight.moveToDirection('up-left'); } else { knight.moveToDirection('left'); } } } else if (Math.abs(dy) > Math.abs(dx) + directionThreshold) { // Vertical movement dominates if (dy > 0) { if (dx > directionThreshold) { knight.moveToDirection('down-right'); } else if (dx < -directionThreshold) { knight.moveToDirection('down-left'); } else { knight.moveToDirection('down'); } } else { if (dx > directionThreshold) { knight.moveToDirection('up-right'); } else if (dx < -directionThreshold) { knight.moveToDirection('up-left'); } else { knight.moveToDirection('up'); } } } else { // The difference between dx and dy is small, use diagonal movement if (dx > 0 && dy > 0) { knight.moveToDirection('down-right'); } else if (dx > 0 && dy < 0) { knight.moveToDirection('up-right'); } else if (dx < 0 && dy > 0) { knight.moveToDirection('down-left'); } else if (dx < 0 && dy < 0) { knight.moveToDirection('up-left'); } } } else { // Set knight to idle state if currently moving if (knight.isMoving) { knight.isMoving = false; knight.switchAsset(knight.lastDirection || 'down'); // Update asset to idle animation } } // Update knight animation knight.updateAnimation(); }; initializeGame();
===================================================================
--- original.js
+++ change.js
@@ -223,32 +223,26 @@
}
};
// Handle attacking mode behavior
self.updateAttacking = function () {
- // Check every 2 seconds if scan laser still intersects with knight
- var currentTime = Date.now();
- if (currentTime - self.lastKnightCheckTime > 2000) {
- // 2000ms = 2 seconds
- self.lastKnightCheckTime = currentTime;
- // Check if knight is still in range
- if (!self.droneScanLaser.intersects(knight.boundingBox)) {
- log("Knight lost! Returning to scanning mode.");
- self.switchState('scanning');
- } else {
- log("Knight still in range.");
- // Ensure drone is still following the knight
- // Use the distanceToKnight method instead of calculating directly
- var distance = self.distanceToKnight();
- // Continuously update rotation to face the knight
- var dx = knight.x - self.x;
- var dy = knight.y - self.y;
- var targetAngle = Math.atan2(dy, dx);
- self.updateRotation(targetAngle, 200, function () {});
- // If knight has moved significantly, update position
- if (distance > 550) {
- log("Knight moved - updating drone position");
- self.followKnight();
- }
+ // Continuously update rotation to target the knight
+ var dx = knight.x - self.x;
+ var dy = knight.y - self.y;
+ var targetAngle = Math.atan2(dy, dx);
+ self.updateRotation(targetAngle, 100); // Update rotation with a short duration
+ // Check if knight is still in range
+ if (!self.droneScanLaser.intersects(knight.boundingBox)) {
+ log("Knight lost! Returning to scanning mode.");
+ self.switchState('scanning');
+ } else {
+ log("Knight still in range.");
+ // Ensure drone is still following the knight
+ // Use the distanceToKnight method instead of calculating directly
+ var distance = self.distanceToKnight();
+ // If knight has moved significantly, update position
+ if (distance > 550) {
+ log("Knight moved - updating drone position");
+ self.followKnight();
}
}
};
self.followKnight = function () {