class_name Creature
extends CharacterBody2D

## Emitted when the creature is damaged
signal damaged(amount: int)
## Emitted when the creature is healed
signal healed(amount: int)
## Emitted when the creature dies
signal death
## Emitted when a status effect is applied to the creature
signal status_effect_applied(effect: StatusEffect)
## Emitted when a status effect is removed from the creature
signal status_effect_removed(effect: StatusEffect)

@export_category("Creature")

## The creature's maximum health
@export var max_hp: int = 1

## The sound to play when the creature dies
@export var death_sound: AudioStream = null

## If true, the creature will be freed when it dies
@export var free_on_death: bool = true

@export var uses_gravity: bool = true

## The creature's current health
var hp: int:
	get:
		return hp
	set(value):
		var old_hp = hp

		hp = clampi(value, 0, max_hp)

		if hp < old_hp:
			self.damaged.emit(old_hp - hp)
		elif hp > old_hp:
			self.healed.emit(hp - old_hp)

		if hp == 0:
			self.death.emit()

var _status_effects: Array = []

@onready var default_gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")


func _ready() -> void:
	self.hp = self.max_hp
	self.death.connect(self._creature_on_death)
	if self.has_method("_on_ready"):
		self.call("_on_ready")


func _physics_process(delta: float) -> void:
	if self.uses_gravity:
		self.velocity.y += self.default_gravity * delta
		if self.is_on_floor():
			self.velocity.y = 0
	self.move_and_slide()


func _process(delta: float) -> void:
	for status_effect in self._status_effects:
		status_effect.process(self, delta)


## Damage the creature by the given amount
func take_damage(damage: int) -> void:
	self.hp -= damage


## Heal the creature by the given amount
func heal(amount: int) -> void:
	self.hp += amount


## Callback for when the creature dies
func _creature_on_death() -> void:
	# Play a sound on creature death
	if self.death_sound:
		var audio_player = AudioStreamPlayer2D.new()
		self.get_parent().add_child(audio_player)
		audio_player.stream = self.death_sound
		audio_player.bus = "Sound Effects"
		audio_player.finished.connect(audio_player.queue_free)
		audio_player.play()

	# Destroy on death
	if self.free_on_death:
		self.queue_free()


## Returns true if the creature's health is greater than 0
func is_alive() -> bool:
	return hp > 0


## Reset the creature's health to its maximum
func reset_health() -> void:
	self.hp = self.max_hp


## Apply a status effect to the creature
func apply_status_effect(status_effect: StatusEffect) -> void:
	status_effect.apply(self)


## Remove a status effect from the creature
func remove_status_effect(status_effect: StatusEffect) -> void:
	status_effect.remove(self)
	self._status_effects.erase(status_effect)


## Remove all status effects from the creature
func clear_status_effects() -> void:
	for status_effect in self._status_effects:
		self.remove_status_effect(status_effect)
