From 7a0143cb80d8808f2c6ce316cbd2e498fb56d83e Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Fri, 13 Mar 2026 19:35:00 +0100 Subject: [PATCH] Added guard attack Signed-off-by: Hans Kokx --- lib/classes/entity.dart | 2 + lib/features/renderer/renderer.dart | 70 +++++++++++++++++++---------- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/lib/classes/entity.dart b/lib/classes/entity.dart index fb7d39a..cf784c5 100644 --- a/lib/classes/entity.dart +++ b/lib/classes/entity.dart @@ -7,6 +7,7 @@ class Entity { double angle; EntityState state; int mapId; + int lastActionTime; Entity({ required this.x, @@ -15,5 +16,6 @@ class Entity { this.angle = 0.0, this.state = EntityState.staticObj, this.mapId = 0, + this.lastActionTime = 0, }); } diff --git a/lib/features/renderer/renderer.dart b/lib/features/renderer/renderer.dart index 03eadcd..512066c 100644 --- a/lib/features/renderer/renderer.dart +++ b/lib/features/renderer/renderer.dart @@ -282,65 +282,87 @@ class _WolfRendererState extends State _spaceWasPressed = isSpacePressed; // --- 4. UPDATE ENTITY LOGIC --- - const double enemySpeed = 0.03; // Slightly slower than the player + const double enemySpeed = 0.03; for (Entity entity in entities) { // 1. Wake up if the player is spotted! if (entity.state == EntityState.idle) { if (_hasLineOfSight(entity)) { entity.state = EntityState.patrolling; + entity.lastActionTime = + elapsed.inMilliseconds; // Start the attack timer } } // 2. State-based Logic & Animation if (entity.state == EntityState.idle || - entity.state == EntityState.patrolling) { + entity.state == EntityState.patrolling || + entity.state == EntityState.shooting) { + // NEW: Include shooting state + double dx = player.x - entity.x; double dy = player.y - entity.y; double distance = math.sqrt(dx * dx + dy * dy); double angleToPlayer = math.atan2(dy, dx); - // If patrolling, force the guard to turn and face the player - if (entity.state == EntityState.patrolling) { + // If patrolling or shooting, force the guard to face the player + if (entity.state == EntityState.patrolling || + entity.state == EntityState.shooting) { entity.angle = angleToPlayer; } - // Calculate the octant for the 8-directional sprites + // Calculate the octant for the 8-directional sprites (used for idle/patrol) double diff = entity.angle - angleToPlayer; - while (diff <= -math.pi) { - diff += 2 * math.pi; - } - while (diff > math.pi) { - diff -= 2 * math.pi; - } + while (diff <= -math.pi) diff += 2 * math.pi; + while (diff > math.pi) diff -= 2 * math.pi; int octant = ((diff + (math.pi / 8)) / (math.pi / 4)).floor() % 8; if (octant < 0) octant += 8; if (entity.state == EntityState.idle) { - // Standing still entity.spriteIndex = 50 + octant; } else if (entity.state == EntityState.patrolling) { - // A. Move towards the player (stop if they get too close) + // A. Move towards the player if (distance > 0.8) { double moveX = entity.x + math.cos(entity.angle) * enemySpeed; double moveY = entity.y + math.sin(entity.angle) * enemySpeed; - // Basic collision so they slide along walls instead of phasing through - if (_isWalkable(moveX.toInt(), entity.y.toInt())) { - entity.x = moveX; - } - if (_isWalkable(entity.x.toInt(), moveY.toInt())) { - entity.y = moveY; - } + if (_isWalkable(moveX.toInt(), entity.y.toInt())) entity.x = moveX; + if (_isWalkable(entity.x.toInt(), moveY.toInt())) entity.y = moveY; } - // B. Animate the walk cycle (4 steps) - // We use the elapsed time to change the frame every 150ms + // B. Animate the walk cycle int walkFrame = (elapsed.inMilliseconds ~/ 150) % 4; - - // Frames start at 58. Each of the 4 walk steps has 8 directional sprites. entity.spriteIndex = 58 + (walkFrame * 8) + octant; + + // C. Decide to shoot! + // If close enough (distance < 5.0) AND 2 seconds have passed since last action + if (distance < 5.0 && + elapsed.inMilliseconds - entity.lastActionTime > 2000) { + if (_hasLineOfSight(entity)) { + entity.state = EntityState.shooting; + entity.lastActionTime = + elapsed.inMilliseconds; // Reset timer for the animation + } + } + } + // NEW: The Attack Sequence! + else if (entity.state == EntityState.shooting) { + // Calculate exactly how many milliseconds we've been shooting + int timeShooting = elapsed.inMilliseconds - entity.lastActionTime; + + if (timeShooting < 150) { + entity.spriteIndex = 96; // Aiming + } else if (timeShooting < 300) { + entity.spriteIndex = 97; // BANG! (Muzzle flash) + } else if (timeShooting < 450) { + entity.spriteIndex = 98; // Recoil + } else { + // Done shooting! Go back to chasing. + entity.state = EntityState.patrolling; + entity.lastActionTime = + elapsed.inMilliseconds; // Reset cooldown timer + } } } }