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
User prompt
when tracking, change drone debugTextto orange too. DON'T USE fill PROPERY but tint
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.debugText.style.fill = 0xFFA500; // Change debugText tint to orange when tracking' Line Number: 372
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.debugText.style.fill = 0xFFA500; // Change debugText tint to orange when tracking' Line Number: 370
User prompt
when tracking, change drone debugText tint to orange too
/****
* 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.rotation = Math.PI / 12;
//shadowDrone.scale.y = 0.5;
shadowDrone.x = -10;
shadowDrone.y = 40;
self.addChild(shadowDrone);
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.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() {
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
});
}
});
}
loopScanLaserTint(); // Start the loop
self.droneScanLaser.alpha = 0.3; // Set alpha to 0.3
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
}
animateScanBar(); // Start the loop
self.addChild(droneGraphics);
self.speed = 5;
self.state = 'scanning'; // 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 - 20;
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
self.selectTarget = function () {
var newTargetIndex;
do {
newTargetIndex = Math.floor(Math.random() * corners.length);
} while (newTargetIndex === self.currentTargetIndex);
self.currentTargetIndex = newTargetIndex;
self.target = corners[newTargetIndex];
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;
tween(self, {
rotation: Math.atan2(dy, dx)
}, {
duration: 1000,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
},
onFinish: function onFinish() {
tween(self, {
x: self.target.x,
y: self.target.y
}, {
duration: duration,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.selectTarget();
}
});
}
});
};
self.update = function () {
self.debugText.setText(self.state); // Update debug text with current state
if (self.state === 'attacking') {
self.debugText.tint = 0xFFA500; // Change tint to orange when attacking
self.droneScanLaser.tint = 0xFFA500; // Change tint to orange when attacking
} else {
self.debugText.tint = 0xFFFFFF; // Reset tint to white for other states
self.droneScanLaser.tint = 0x00FF00; // Reset tint to green for scanning mode
}
if (self.state === 'scanning') {
self.updateScanning();
} else if (self.state === 'attacking') {
self.updateAttacking();
}
};
// Handle scanning mode behavior
self.updateScanning = function () {
// Check for intersection with knight
if (self.droneScanBar.intersects(knight.boundingBox)) {
self.state = 'attacking';
log("Drone switched to attacking mode!");
// Call followKnight once when state changes to attacking
self.followKnight();
return;
}
// We don't need the distance calculation and threshold check anymore
// since the tween handles movement and calls selectTarget when done
// We still want to update rotation during movement for a more natural look
if (self.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);
// Calculate target rotation using the utility function
var targetRotation = self.rotation + calculateAngleDiff(targetAngle, self.rotation);
// Use tween for rotation instead of direct manipulation
tween(self, {
rotation: targetRotation
}, {
duration: 200,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
}
});
}
// 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 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.state = 'scanning';
// Stop any current movement
tween.stop(self);
// Reset tint and other properties
self.droneScanLaser.tint = 0x00FF00;
// Explicitly clear the knight as target
self.target = null;
// Perform a 360° rotation before returning to scanning mode
// Store current rotation
var startRotation = self.rotation;
tween(self, {
rotation: startRotation + Math.PI * 2 // Full 360° rotation
}, {
duration: 1000,
// 1 second for the full rotation
easing: tween.easeInOut,
onUpdate: function onUpdate() {
// Update shadow position during rotation
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
},
onFinish: function onFinish() {
// Wait a short time before selecting a new target
// This prevents any leftover movement from the attacking mode
LK.setTimeout(function () {
// Resume normal patrol behavior and select a new random corner
// The timeout ensures we're fully out of the attacking mode
if (self.state === 'scanning') {
// This double-check is necessary to ensure we truly switch modes
self.target = null; // Clear any existing target again
self.selectTarget();
}
}, 100);
}
});
} 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);
// Set state to attacking
self.state = 'attacking';
// 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 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);
// Calculate target rotation using the utility function
var targetRotation = self.rotation + calculateAngleDiff(angle, self.rotation);
tween(self, {
rotation: targetRotation
}, {
duration: 500,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
}
});
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);
// Calculate the target rotation using the utility function
var targetRotation = self.rotation + calculateAngleDiff(angle, self.rotation);
// First rotate smoothly toward the target
tween(self, {
rotation: targetRotation
}, {
duration: 400,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
},
onFinish: function onFinish() {
// 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 shadow position
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
// 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 knight has moved and we need to follow again
if (self.distanceToKnight() > 500) {
self.moveTowardKnight();
}
}
});
}
});
};
});
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.selectTarget();
}, 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
@@ -83,9 +83,9 @@
}
animateScanBar(); // Start the loop
self.addChild(droneGraphics);
self.speed = 5;
- self.state = 'scanning'; // Possible states: scanning, tracking, attacking
+ self.state = 'scanning'; // Possible states: scanning, attacking
// Add debug text to track drone state
self.debugText = new Text2(self.state, {
size: 50,
fill: 0xFFFFFF
@@ -137,204 +137,216 @@
});
};
self.update = function () {
self.debugText.setText(self.state); // Update debug text with current state
- if (self.state === 'tracking') {
- self.debugText.tint = 0xFFA500; // Change tint to orange when tracking
- self.droneScanLaser.tint = 0xFFA500; // Change tint to orange when tracking
+ if (self.state === 'attacking') {
+ self.debugText.tint = 0xFFA500; // Change tint to orange when attacking
+ self.droneScanLaser.tint = 0xFFA500; // Change tint to orange when attacking
} else {
self.debugText.tint = 0xFFFFFF; // Reset tint to white for other states
self.droneScanLaser.tint = 0x00FF00; // Reset tint to green for scanning mode
}
if (self.state === 'scanning') {
- // Check for intersection with knight
- if (self.droneScanLaser.intersects(knight.boundingBox)) {
- self.state = 'attacking';
- log("Drone switched to attacking mode!");
- // Call followKnight once when state changes to attacking
- self.followKnight();
- return;
- }
- // We don't need the distance calculation and threshold check anymore
- // since the tween handles movement and calls selectTarget when done
- // We still want to update rotation during movement for a more natural look
- if (self.target) {
- var dx = self.target.x - self.x;
- var dy = self.target.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- // Update rotation to face the target while moving
- if (distance > 10) {
- // Only update if not very close to target
- var targetAngle = Math.atan2(dy, dx);
- // Gradually adjust rotation towards target angle
- var angleDiff = targetAngle - self.rotation;
- // Normalize angle difference to [-PI, PI]
- while (angleDiff > Math.PI) {
- angleDiff -= 2 * Math.PI;
- }
- while (angleDiff < -Math.PI) {
- angleDiff += 2 * Math.PI;
- }
- // Adjust rotation by a fraction of the difference for smooth turning
- self.rotation += angleDiff * 0.1;
- }
- // 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)));
- } else if (self.state === 'attacking' || self.state === 'tracking') {
- // 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.state = 'scanning';
- // Stop any current movement
- tween.stop(self);
- // Reset tint and other properties
- self.droneScanLaser.tint = 0x00FF00;
- // Wait a short time before selecting a new target
- // This prevents any leftover movement from the tracking mode
- LK.setTimeout(function () {
- // Resume normal patrol behavior
- self.selectTarget();
- }, 100);
- } else {
- log("Knight still in range.");
- // Ensure drone is still following the knight
- if (self.state === 'tracking') {
- // Calculate distance to knight
- var dx = knight.x - self.x;
- var dy = knight.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- // If knight has moved significantly, update position
- if (distance > 550) {
- log("Knight moved - updating drone position");
- self.followKnight();
- }
- }
- }
- }
+ self.updateScanning();
+ } else if (self.state === 'attacking') {
+ self.updateAttacking();
}
};
- self.followKnight = function () {
- // Cancel any existing movement tweens
- tween.stop(self);
- // Set state to tracking
- self.state = 'tracking';
- // Function to calculate distance to knight
- function distanceToKnight() {
- var dx = knight.x - self.x;
- var dy = knight.y - self.y;
- return Math.sqrt(dx * dx + dy * dy);
+ // Handle scanning mode behavior
+ self.updateScanning = function () {
+ // Check for intersection with knight
+ if (self.droneScanBar.intersects(knight.boundingBox)) {
+ self.state = 'attacking';
+ log("Drone switched to attacking mode!");
+ // Call followKnight once when state changes to attacking
+ self.followKnight();
+ return;
}
- // Function to move toward knight
- function moveTowardKnight() {
- var dx = knight.x - self.x;
- var dy = knight.y - self.y;
+ // We don't need the distance calculation and threshold check anymore
+ // since the tween handles movement and calls selectTarget when done
+ // We still want to update rotation during movement for a more natural look
+ if (self.target) {
+ var dx = self.target.x - self.x;
+ var dy = self.target.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);
- // Ensure smooth rotation by taking the shortest path
- var angleDiff = angle - self.rotation;
- // Normalize angle difference to [-PI, PI]
- while (angleDiff > Math.PI) {
- angleDiff -= 2 * Math.PI;
- }
- while (angleDiff < -Math.PI) {
- angleDiff += 2 * Math.PI;
- }
- // Calculate the target rotation by adding the normalized difference
- var targetRotation = self.rotation + angleDiff;
+ // Only update rotation if not very close to target
+ if (distance > 10) {
+ var targetAngle = Math.atan2(dy, dx);
+ // Calculate target rotation using the utility function
+ var targetRotation = self.rotation + calculateAngleDiff(targetAngle, self.rotation);
+ // Use tween for rotation instead of direct manipulation
tween(self, {
rotation: targetRotation
}, {
- duration: 500,
+ duration: 200,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
}
});
- 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);
- // Ensure smooth rotation by taking the shortest path
- var angleDiff = angle - self.rotation;
- // Normalize angle difference to [-PI, PI]
- while (angleDiff > Math.PI) {
- angleDiff -= 2 * Math.PI;
+ // 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 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.state = 'scanning';
+ // Stop any current movement
+ tween.stop(self);
+ // Reset tint and other properties
+ self.droneScanLaser.tint = 0x00FF00;
+ // Explicitly clear the knight as target
+ self.target = null;
+ // Perform a 360° rotation before returning to scanning mode
+ // Store current rotation
+ var startRotation = self.rotation;
+ tween(self, {
+ rotation: startRotation + Math.PI * 2 // Full 360° rotation
+ }, {
+ duration: 1000,
+ // 1 second for the full rotation
+ easing: tween.easeInOut,
+ onUpdate: function onUpdate() {
+ // Update shadow position during rotation
+ shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
+ shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
+ },
+ onFinish: function onFinish() {
+ // Wait a short time before selecting a new target
+ // This prevents any leftover movement from the attacking mode
+ LK.setTimeout(function () {
+ // Resume normal patrol behavior and select a new random corner
+ // The timeout ensures we're fully out of the attacking mode
+ if (self.state === 'scanning') {
+ // This double-check is necessary to ensure we truly switch modes
+ self.target = null; // Clear any existing target again
+ self.selectTarget();
+ }
+ }, 100);
+ }
+ });
+ } 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();
+ }
}
- while (angleDiff < -Math.PI) {
- angleDiff += 2 * Math.PI;
- }
- // Calculate the target rotation by adding the normalized difference
- var targetRotation = self.rotation + angleDiff;
- // First rotate smoothly toward the target
+ }
+ };
+ self.followKnight = function () {
+ // Cancel any existing movement tweens
+ tween.stop(self);
+ // Set state to attacking
+ self.state = 'attacking';
+ // 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 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);
+ // Calculate target rotation using the utility function
+ var targetRotation = self.rotation + calculateAngleDiff(angle, self.rotation);
tween(self, {
rotation: targetRotation
}, {
- duration: 400,
+ duration: 500,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
- },
- onFinish: function onFinish() {
- // Then move toward the target
- tween(self, {
- x: targetX,
- y: targetY
- }, {
- duration: distance * 2,
- // Speed based on distance
- easing: tween.easeInOut,
- onUpdate: function onUpdate() {
- // During movement, continuously update rotation to face knight
- var currentDx = knight.x - self.x;
- var currentDy = knight.y - self.y;
- if (Math.sqrt(currentDx * currentDx + currentDy * currentDy) > 10) {
- var currentAngle = Math.atan2(currentDy, currentDx);
- // Smooth rotation adjustment
- var currentAngleDiff = currentAngle - self.rotation;
- // Normalize angle difference
- while (currentAngleDiff > Math.PI) {
- currentAngleDiff -= 2 * Math.PI;
- }
- while (currentAngleDiff < -Math.PI) {
- currentAngleDiff += 2 * Math.PI;
- }
- // Apply a small adjustment each update for smooth turning
- self.rotation += currentAngleDiff * 0.05;
- // Update shadow
- shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
- shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
- }
- },
- onFinish: function onFinish() {
- // Check if knight has moved and we need to follow again
- if (distanceToKnight() > 500) {
- moveTowardKnight();
- }
- }
- });
}
});
+ return;
}
- // Start following the knight
- moveTowardKnight();
+ // 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);
+ // Calculate the target rotation using the utility function
+ var targetRotation = self.rotation + calculateAngleDiff(angle, self.rotation);
+ // First rotate smoothly toward the target
+ tween(self, {
+ rotation: targetRotation
+ }, {
+ duration: 400,
+ easing: tween.easeInOut,
+ onUpdate: function onUpdate() {
+ shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
+ shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
+ },
+ onFinish: function onFinish() {
+ // 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 shadow position
+ shadowDrone.x = self.shadowOffset.x * Math.cos(self.rotation);
+ shadowDrone.y = self.shadowOffset.y * Math.sin(self.rotation);
+ // 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 knight has moved and we need to follow again
+ if (self.distanceToKnight() > 500) {
+ self.moveTowardKnight();
+ }
+ }
+ });
+ }
+ });
};
});
var Knight = Container.expand(function () {
var self = Container.call(this);
@@ -523,8 +535,20 @@
/****
* 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>