feat: shared creature root, enemy definitions, defend duty, and player downing

This commit is contained in:
Rijad Zuzo
2026-08-12 00:16:48 +02:00
parent 6de6d25bf5
commit 661d53f31a
23 changed files with 653 additions and 76 deletions
+127
View File
@@ -0,0 +1,127 @@
class_name CreatureVisual
extends Node3D
signal position_changed(creature_id: StringName, active_position: Vector3)
signal arrived_at_sim_position(creature_id: StringName)
const ARRIVAL_DISTANCE := 0.5
const PATH_RETARGET_DISTANCE := 0.8
@export_range(0.5, 12.0, 0.1) var move_speed := 3.5
@export_range(0.05, 1.0, 0.05) var waypoint_reached_distance := 0.3
@export_range(1.0, 20.0, 0.5) var rotation_speed := 8.0
var creature_id: StringName
var sim_position := Vector3.INF
var is_dead_visual := false
var current_path := PackedVector3Array()
var path_index := 0
var path_target := Vector3.INF
var path_request_id := 0
var path_pending := false
func _physics_process(delta: float) -> void:
if is_dead_visual:
return
if not sim_position.is_finite():
return
if _horizontal_distance_to(sim_position) <= ARRIVAL_DISTANCE:
_stop_moving()
return
_ensure_path()
if path_pending:
return
if current_path.is_empty() or path_index >= current_path.size():
_request_path()
return
var next_position := current_path[path_index]
var offset := next_position - global_position
if offset.length() <= waypoint_reached_distance:
path_index += 1
return
var direction := offset.normalized()
global_position = global_position.move_toward(next_position, move_speed * delta)
position_changed.emit(creature_id, global_position)
var target_angle := atan2(direction.x, direction.z)
rotation.y = lerp_angle(rotation.y, target_angle, rotation_speed * delta)
func set_sim_position(position: Vector3) -> void:
if not position.is_finite():
return
var moved := (
not path_target.is_finite()
or _horizontal_distance_to_position(sim_position, position) > PATH_RETARGET_DISTANCE
)
sim_position = position
if moved and not is_dead_visual:
_request_path()
func play_death() -> void:
is_dead_visual = true
_stop_moving()
var tween := create_tween()
tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
tween.tween_property(self, "scale", Vector3(0.001, 0.001, 0.001), 0.45)
tween.tween_callback(queue_free)
func build_visual() -> void:
pass
func _ensure_path() -> void:
if (
path_pending
or (
path_target.is_finite()
and (
_horizontal_distance_to_position(path_target, sim_position)
<= PATH_RETARGET_DISTANCE
)
)
):
return
_request_path()
func _request_path() -> void:
path_request_id += 1
var request_id := path_request_id
path_target = sim_position
current_path = PackedVector3Array()
path_index = 0
path_pending = true
await get_tree().physics_frame
if request_id != path_request_id or is_dead_visual:
return
if _horizontal_distance_to(sim_position) <= ARRIVAL_DISTANCE:
path_pending = false
return
current_path = NavigationServer3D.map_get_path(
get_world_3d().navigation_map, global_position, sim_position, true
)
path_pending = false
if current_path.is_empty():
_stop_moving()
func _stop_moving() -> void:
path_request_id += 1
current_path = PackedVector3Array()
path_index = 0
path_pending = false
path_target = Vector3.INF
func _horizontal_distance_to(target_position: Vector3) -> float:
return _horizontal_distance_to_position(global_position, target_position)
static func _horizontal_distance_to_position(from_position: Vector3, to_position: Vector3) -> float:
return Vector2(from_position.x, from_position.z).distance_to(
Vector2(to_position.x, to_position.z)
)