114 lines
3.5 KiB
GDScript
114 lines
3.5 KiB
GDScript
extends Node
|
|
|
|
const TARGET_RANGE := 20.0
|
|
const COMBAT_TIMEOUT := 3.0
|
|
|
|
var current_target: Node3D = null
|
|
var mouse_press_pos: Vector2 = Vector2.ZERO
|
|
var in_combat := false
|
|
var combat_timer := 0.0
|
|
|
|
@onready var player: CharacterBody3D = get_parent()
|
|
@onready var camera: Camera3D = get_parent().get_node("CameraPivot/Camera3D")
|
|
|
|
func _ready() -> void:
|
|
EventBus.damage_dealt.connect(_on_damage_dealt)
|
|
EventBus.entity_died.connect(_on_entity_died)
|
|
EventBus.enemy_engaged.connect(_on_enemy_engaged)
|
|
|
|
func _process(delta: float) -> void:
|
|
if in_combat:
|
|
combat_timer -= delta
|
|
if combat_timer <= 0:
|
|
in_combat = false
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
|
if event.pressed:
|
|
mouse_press_pos = event.position
|
|
else:
|
|
var drag: float = event.position.distance_to(mouse_press_pos)
|
|
if drag < 5.0:
|
|
_try_target_under_mouse(event.position)
|
|
if event.is_action_pressed("target_next"):
|
|
_cycle_target()
|
|
|
|
func _try_target_under_mouse(mouse_pos: Vector2) -> void:
|
|
var from := camera.project_ray_origin(mouse_pos)
|
|
var to := from + camera.project_ray_normal(mouse_pos) * TARGET_RANGE
|
|
var space := player.get_world_3d().direct_space_state
|
|
var query := PhysicsRayQueryParameters3D.create(from, to)
|
|
query.collision_mask = 4
|
|
query.collide_with_areas = true
|
|
query.collide_with_bodies = false
|
|
var result := space.intersect_ray(query)
|
|
if result:
|
|
var hit_target := result.collider.get_parent() as Node3D
|
|
set_target(hit_target)
|
|
else:
|
|
set_target(null)
|
|
|
|
func _cycle_target() -> void:
|
|
var enemies := get_tree().get_nodes_in_group("enemies")
|
|
if enemies.is_empty():
|
|
set_target(null)
|
|
return
|
|
if current_target == null or current_target not in enemies:
|
|
set_target(enemies[0])
|
|
return
|
|
var idx := enemies.find(current_target)
|
|
var next_idx := (idx + 1) % enemies.size()
|
|
set_target(enemies[next_idx])
|
|
|
|
func set_target(target: Node3D) -> void:
|
|
current_target = target
|
|
EventBus.target_changed.emit(player, target)
|
|
|
|
func _on_enemy_engaged(_enemy: Node, target: Node) -> void:
|
|
if target == player:
|
|
combat_timer = COMBAT_TIMEOUT
|
|
if not in_combat:
|
|
in_combat = true
|
|
if current_target == null:
|
|
_target_nearest()
|
|
|
|
func _on_damage_dealt(_attacker: Node, target: Node, _amount: float) -> void:
|
|
if target == player:
|
|
combat_timer = COMBAT_TIMEOUT
|
|
if not in_combat:
|
|
in_combat = true
|
|
if current_target == null:
|
|
_target_nearest()
|
|
|
|
func _on_entity_died(entity: Node) -> void:
|
|
if entity == current_target:
|
|
set_target(null)
|
|
if in_combat:
|
|
_target_nearest_except(entity)
|
|
|
|
func _target_nearest_except(exclude: Node = null) -> void:
|
|
var enemies := get_tree().get_nodes_in_group("enemies")
|
|
var nearest: Node3D = null
|
|
var nearest_dist: float = INF
|
|
for enemy in enemies:
|
|
if is_instance_valid(enemy) and enemy != exclude:
|
|
var dist: float = player.global_position.distance_to(enemy.global_position)
|
|
if dist < nearest_dist:
|
|
nearest_dist = dist
|
|
nearest = enemy
|
|
if nearest:
|
|
set_target(nearest)
|
|
|
|
func _target_nearest() -> void:
|
|
var enemies := get_tree().get_nodes_in_group("enemies")
|
|
var nearest: Node3D = null
|
|
var nearest_dist: float = INF
|
|
for enemy in enemies:
|
|
if is_instance_valid(enemy):
|
|
var dist: float = player.global_position.distance_to(enemy.global_position)
|
|
if dist < nearest_dist:
|
|
nearest_dist = dist
|
|
nearest = enemy
|
|
if nearest:
|
|
set_target(nearest)
|