import 'dart:math' as math; import 'package:wolf_dart/classes/linear_coordinates.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; import 'package:wolf_dart/features/entities/entity.dart'; class Dog extends Enemy { static const double speed = 0.05; // Dogs are much faster than guards! bool _hasBittenThisCycle = false; Dog({ required super.x, required super.y, required super.angle, required super.mapId, }) : super( spriteIndex: 99, // Dogs start at index 99 in VSWAP state: EntityState.idle, ); static Dog? trySpawn(int objId, double x, double y, int difficultyLevel) { bool canSpawn = false; switch (difficultyLevel) { case 0: canSpawn = objId >= 116 && objId <= 119; break; case 1: canSpawn = objId >= 152 && objId <= 155; break; case 2: canSpawn = objId >= 188 && objId <= 191; break; case 3: canSpawn = objId >= 224 && objId <= 227; break; } if (canSpawn) { return Dog( x: x, y: y, angle: Enemy.getInitialAngle(objId), mapId: objId, ); } return null; } @override void update({ required int elapsedMs, required LinearCoordinates player, required bool Function(int x, int y) isWalkable, required void Function(int damage) onDamagePlayer, }) { if (state == EntityState.idle) { if (hasLineOfSight(player, isWalkable)) { state = EntityState.patrolling; lastActionTime = elapsedMs; } } if (state == EntityState.idle || state == EntityState.patrolling || state == EntityState.shooting) { // "Shooting" here means biting 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 = 99 + octant; // Base dog standing sprite } else if (state == EntityState.patrolling) { 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; } // Dogs only have 4 walking angles, alternating legs int walkFrame = (elapsedMs ~/ 100) % 4; spriteIndex = 107 + (walkFrame * 8) + octant; // Dog Bite Attack (Must be practically touching the player) if (distance < 1.0 && elapsedMs - lastActionTime > 1000) { state = EntityState.shooting; lastActionTime = elapsedMs; _hasBittenThisCycle = false; } } else if (state == EntityState.shooting) { int timeAttacking = elapsedMs - lastActionTime; if (timeAttacking < 200) { spriteIndex = 139; // Jumping/Biting frame if (!_hasBittenThisCycle) { onDamagePlayer(5); // Dogs do less damage than guards _hasBittenThisCycle = true; } } else { state = EntityState.patrolling; lastActionTime = elapsedMs; } } } } }