import 'dart:math' as math; import 'package:wolf_dart/classes/enemy.dart'; import 'package:wolf_dart/classes/entity.dart'; import 'package:wolf_dart/classes/linear_coordinates.dart'; class BrownGuard extends Enemy { static const double speed = 0.03; bool _hasFiredThisCycle = false; BrownGuard({ required super.x, required super.y, required super.angle, required super.mapId, }) : super( spriteIndex: 50, // Default front-facing idle state: EntityState.idle, ); @override void update({ required int elapsedMs, required LinearCoordinates player, required bool Function(int x, int y) isWalkable, required bool Function(Entity entity) hasLineOfSight, required void Function(int damage) onDamagePlayer, }) { // 1. Wake up if the player is spotted! if (state == EntityState.idle) { if (hasLineOfSight(this)) { state = EntityState.patrolling; lastActionTime = elapsedMs; } } // 2. State-based Logic & Animation if (state == EntityState.idle || state == EntityState.patrolling || state == EntityState.shooting) { double dx = player.x - x; double dy = player.y - y; double distance = math.sqrt(dx * dx + dy * dy); double angleToPlayer = math.atan2(dy, dx); if (state == EntityState.patrolling || state == EntityState.shooting) { angle = angleToPlayer; } double diff = angle - angleToPlayer; 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 (state == EntityState.idle) { spriteIndex = 50 + octant; } else if (state == EntityState.patrolling) { // A. Move towards the player if (distance > 0.8) { double moveX = x + math.cos(angle) * speed; double moveY = y + math.sin(angle) * speed; if (isWalkable(moveX.toInt(), y.toInt())) x = moveX; if (isWalkable(x.toInt(), moveY.toInt())) y = moveY; } // B. Animate the walk cycle int walkFrame = (elapsedMs ~/ 150) % 4; spriteIndex = 58 + (walkFrame * 8) + octant; // C. Decide to shoot! if (distance < 5.0 && elapsedMs - lastActionTime > 2000) { if (hasLineOfSight(this)) { state = EntityState.shooting; lastActionTime = elapsedMs; _hasFiredThisCycle = false; // Arm the weapon } } } else if (state == EntityState.shooting) { int timeShooting = elapsedMs - lastActionTime; if (timeShooting < 150) { spriteIndex = 96; // Aiming } else if (timeShooting < 300) { spriteIndex = 97; // BANG! // DEAL DAMAGE ONCE! if (!_hasFiredThisCycle) { onDamagePlayer(10); // 10 damage per shot _hasFiredThisCycle = true; } } else if (timeShooting < 450) { spriteIndex = 98; // Recoil } else { state = EntityState.patrolling; lastActionTime = elapsedMs; } } } } }