extends AbstractPredator # FIXME: (general) tracking across wrapping boundary var starting_pos var can_attack: bool = true @onready var fsm: StateMachine = $StateMachine @onready var attack_cooldown_timer: Timer = $AttackCooldownTimer @onready var wrapper: WrappingManager = $WrappingManager @onready var collision: CharacterBody2D = $Collision @onready var sprite = $"Collision/Sprite" @export var damage: int = 15 @export var attack_range = 40 @export var sight_range = 200 func _ready() -> void: health = maxHealth sprite.play("Healthy") if starting_pos: collision.set_position(starting_pos) func move(motion: Vector3, mod: float = 1.0) -> void: collision.move(motion, mod) func set_position(pos: Vector2) -> void: if collision: collision.set_position(pos) else: starting_pos = pos 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