122 lines
4.6 KiB
GDScript
122 lines
4.6 KiB
GDScript
extends Node
|
|
|
|
enum State { IDLE, CHASE, ATTACK, RETURN }
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
_process_group(delta, EnemyData)
|
|
_process_group(delta, BossData)
|
|
|
|
func _process_group(delta: float, data_source: Node) -> void:
|
|
for entity in data_source.entities:
|
|
if not is_instance_valid(entity) or not data_source.is_alive(entity):
|
|
continue
|
|
var data: Dictionary = data_source.entities[entity]
|
|
if entity.is_in_group("invasion"):
|
|
_force_invasion_target(entity, data)
|
|
var state: int = data["state"]
|
|
match state:
|
|
State.IDLE:
|
|
entity.velocity.x = 0
|
|
entity.velocity.z = 0
|
|
State.CHASE:
|
|
_chase(entity, data, data_source)
|
|
State.ATTACK:
|
|
_attack(entity, data, data_source, delta)
|
|
State.RETURN:
|
|
_return_to_spawn(entity, data, data_source, delta)
|
|
|
|
func _force_invasion_target(entity: Node, data: Dictionary) -> void:
|
|
var tavern: Node = get_tree().get_first_node_in_group("tavern")
|
|
if not tavern:
|
|
return
|
|
data["target"] = tavern
|
|
if data["state"] == State.IDLE or data["state"] == State.RETURN:
|
|
data["state"] = State.CHASE
|
|
|
|
func _chase(entity: Node, data: Dictionary, data_source: Node) -> void:
|
|
if not is_instance_valid(data["target"]):
|
|
data["state"] = State.RETURN
|
|
return
|
|
var base: EnemyStats = data_source.get_base(entity)
|
|
var attack_range: float = base.attack_range
|
|
if data["target"].is_in_group("tavern"):
|
|
attack_range += 3.0
|
|
var dist: float = entity.global_position.distance_to(data["target"].global_position)
|
|
if dist <= attack_range:
|
|
data["state"] = State.ATTACK
|
|
return
|
|
var nav_agent: NavigationAgent3D = entity.get_node_or_null("NavigationAgent3D")
|
|
if not nav_agent:
|
|
return
|
|
nav_agent.target_position = data["target"].global_position
|
|
var next_pos := nav_agent.get_next_path_position()
|
|
var direction: Vector3 = (next_pos - entity.global_position).normalized()
|
|
direction.y = 0
|
|
entity.velocity.x = direction.x * base.speed
|
|
entity.velocity.z = direction.z * base.speed
|
|
_face_target(entity, data["target"])
|
|
|
|
func _attack(entity: Node, data: Dictionary, data_source: Node, delta: float) -> void:
|
|
data["attack_timer"] -= delta
|
|
if not is_instance_valid(data["target"]):
|
|
data["state"] = State.RETURN
|
|
return
|
|
var base: EnemyStats = data_source.get_base(entity)
|
|
var attack_range: float = base.attack_range
|
|
if data["target"].is_in_group("tavern"):
|
|
attack_range += 3.0
|
|
var dist: float = entity.global_position.distance_to(data["target"].global_position)
|
|
if dist > attack_range:
|
|
data["state"] = State.CHASE
|
|
return
|
|
if data["attack_timer"] <= 0:
|
|
data["attack_timer"] = base.attack_cooldown
|
|
var scale: float = data.get("scale", 1.0)
|
|
EventBus.damage_requested.emit(entity, data["target"], base.attack_damage * scale)
|
|
EventBus.attack_executed.emit(entity, entity.global_position, -entity.global_transform.basis.z, base.attack_damage * scale)
|
|
entity.velocity.x = 0
|
|
entity.velocity.z = 0
|
|
_face_target(entity, data["target"])
|
|
|
|
func _face_target(entity: Node3D, target: Node3D) -> void:
|
|
if not is_instance_valid(target):
|
|
return
|
|
var to_target: Vector3 = target.global_position - entity.global_position
|
|
to_target.y = 0
|
|
if to_target.length() < 0.01:
|
|
return
|
|
var yaw: float = atan2(-to_target.x, -to_target.z)
|
|
var delta: float = get_physics_process_delta_time()
|
|
entity.rotation.y = lerp_angle(entity.rotation.y, yaw, 8.0 * delta)
|
|
|
|
func _return_to_spawn(entity: Node, data: Dictionary, data_source: Node, delta: float) -> void:
|
|
var spawn_pos: Vector3 = data["spawn_position"]
|
|
var dist: float = entity.global_position.distance_to(spawn_pos)
|
|
if dist < 1.0:
|
|
data["state"] = State.IDLE
|
|
entity.velocity.x = 0
|
|
entity.velocity.z = 0
|
|
return
|
|
var base: EnemyStats = data_source.get_base(entity)
|
|
var nav_agent: NavigationAgent3D = entity.get_node_or_null("NavigationAgent3D")
|
|
if not nav_agent:
|
|
return
|
|
nav_agent.target_position = spawn_pos
|
|
var next_pos := nav_agent.get_next_path_position()
|
|
var direction: Vector3 = (next_pos - entity.global_position).normalized()
|
|
direction.y = 0
|
|
entity.velocity.x = direction.x * base.speed
|
|
entity.velocity.z = direction.z * base.speed
|
|
_regenerate(entity, data, data_source, delta)
|
|
|
|
func _regenerate(entity: Node, data: Dictionary, data_source: Node, delta: float) -> void:
|
|
var health: float = data["health"]
|
|
var max_health: float = data["max_health"]
|
|
if health < max_health:
|
|
var base: EnemyStats = data_source.get_base(entity)
|
|
var rate: float = base.regen_fast
|
|
if health >= max_health * 0.99:
|
|
rate = base.regen_slow
|
|
health = min(health + max_health * rate * delta, max_health)
|
|
data_source.set_health(entity, health)
|