extends AbstractPredator2D # FIXME: (general) tracking across wrapping boundary var can_attack: bool = true var desired_rotation: float = self.rotation @onready var sprite = $AnimatedSprite2D @onready var fsm: StateMachine = $StateMachine @onready var attack_cooldown_timer: Timer = $AttackCooldownTimer @onready var wrapper: WrappingManager = $WrappingManager @export var damage: int = 15 @export var attack_range = 40 @export var sight_range = 200 @export var speed = 0.8 func _ready() -> void: health = maxHealth sprite.play("Healthy") # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: # smoothly rotate if self.rotation != self.desired_rotation: self.rotation = lerp_angle(self.rotation, self.desired_rotation, clampf(4 * delta, 0, 1)) func _physics_process(delta: float) -> void: pass # FIXME: (also goes for prey) this is framerate dependent UNLESS called from a _physics function. func move(motion: Vector3, mod: float = 1.0) -> void: move_and_collide(Vector2(motion.x, motion.y).normalized() * self.speed * mod) # Moves along the given vector self.desired_rotation = atan2(motion.y, motion.x) # Apply boundary to new position position = GameManager.get_boundaried_position(position) func try_attack(target: Node) -> void: if not can_attack: return attack(target) func attack(target: Node) -> void: can_attack = false var hit_hittable = false if target.is_in_group("prey") or target.is_in_group("player"): if target.has_method("handle_damage"): target.handle_damage(damage, self) hit_hittable = true elif target.is_in_group("resources"): # TODO: resource handling logic pass if hit_hittable: attack_cooldown_timer.start() func handle_damage(dmg: int, src: Node) -> void: health = max(0, health-dmg) if health == 0: die() if health < maxHealth/2: become_injured() fsm.transition_to_next_state(fsm.States.FLEEING, {"threat": src}) elif health < maxHealth: become_injured() fsm.transition_to_next_state(fsm.States.HUNTING, {"target": src}) func die() -> void: super.die() func become_injured() -> void: sprite.play("Hurt") wrapper.play_sprite("Hurt") func _on_sight_body_entered(body: Node2D) -> void: if fsm.map(fsm.state) != fsm.States.HUNTING and body.is_in_group("prey") or (health < maxHealth and body.is_in_group("player")): fsm.transition_to_next_state(fsm.States.HUNTING, {"target": body}) func _on_attack_cooldown_timer_timeout() -> void: can_attack = true