feat: added moving killable dummy

This commit is contained in:
DottsGit
2026-06-06 00:31:43 -04:00
parent 0706d5ca73
commit d0716f72e8
3 changed files with 194 additions and 0 deletions
+7
View File
@@ -227,6 +227,13 @@ func _build_target_dummy() -> void:
killable.position = Vector3(5, 0, -10)
add_child(killable)
var walking = load("res://entities/walking_dummy.gd").new()
walking.name = "WalkingDummy"
walking.position = Vector3(15, 0, -10)
walking.point_a = Vector3(15, 0, -10)
walking.point_b = Vector3(15, 0, 10)
add_child(walking)
# ── Lighting ──────────────────────────────────────────────────────────────────
func _build_lighting() -> void:
+186
View File
@@ -0,0 +1,186 @@
extends CharacterBody3D
class_name WalkingDummy
var visual_node: Node3D
var col_shape: CollisionShape3D
var label: Label3D
var max_health: float = 100.0
var health: float = 100.0
var max_shield: float = 100.0
var shield: float = 100.0
var is_dead: bool = false
var ragdoll_instance: Node3D = null
var pending_impulse: Vector3 = Vector3.ZERO
@export var point_a: Vector3 = Vector3(15, 0, -10)
@export var point_b: Vector3 = Vector3(15, 0, 10)
@export var walk_speed: float = 11.0
var _target_point: Vector3
func _ready() -> void:
add_to_group("enemies")
_target_point = point_b
col_shape = CollisionShape3D.new()
col_shape.shape = CapsuleShape3D.new()
col_shape.shape.radius = 0.5
col_shape.shape.height = 2.0
col_shape.position = Vector3(0, 1.0, 0)
add_child(col_shape)
visual_node = load("res://characters/humanoid_model.gd").new()
visual_node.name = "HumanoidModel"
visual_node.color = Color(0.8, 0.1, 0.5) # Purple-pink for walking
add_child(visual_node)
label = Label3D.new()
label.pixel_size = 0.01
label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
label.position = Vector3(0, 2.3, 0)
label.modulate = Color(1, 1, 1)
add_child(label)
_update_label()
func _physics_process(delta: float) -> void:
if is_dead:
return
# Basic gravity
if not is_on_floor():
velocity.y -= 9.8 * delta
var target_dir = (_target_point - global_position)
target_dir.y = 0 # Ignore vertical distance
if target_dir.length() < 0.5:
# Reached point, swap
if _target_point == point_b:
_target_point = point_a
else:
_target_point = point_b
target_dir = (_target_point - global_position)
target_dir.y = 0
var move_dir = target_dir.normalized()
velocity.x = move_dir.x * walk_speed
velocity.z = move_dir.z * walk_speed
# Turn to face direction
if move_dir.length_squared() > 0.1:
var target_y_rot = atan2(move_dir.x, move_dir.z)
visual_node.rotation.y = lerp_angle(visual_node.rotation.y, target_y_rot, 10.0 * delta)
move_and_slide()
if visual_node and visual_node.has_method("update_state"):
var current_h_speed = Vector2(velocity.x, velocity.z).length()
visual_node.update_state("ground", current_h_speed)
func take_damage(amount: float, hit_position: Vector3, source: Node = null) -> void:
if is_dead:
return
var dmg_taken = amount
if shield > 0:
if shield >= amount:
shield -= amount
amount = 0
else:
amount -= shield
shield = 0
if amount > 0:
health -= amount
_update_label()
if source and source.has_method("spawn_damage_number"):
source.spawn_damage_number(dmg_taken, hit_position)
if health <= 0:
_die(hit_position, source, dmg_taken)
else:
_wiggle()
func _update_label() -> void:
label.text = "HP: %.0f / SH: %.0f" % [health, shield]
func apply_impulse(impulse: Vector3) -> void:
pending_impulse += impulse
func _die(_hit_position: Vector3, source: Node, damage_amount: float) -> void:
is_dead = true
visual_node.visible = false
col_shape.set_deferred("disabled", true)
label.visible = false
# Stop movement immediately
velocity = Vector3.ZERO
# Spawn ragdoll
var ragdoll_script = preload("res://characters/procedural_ragdoll.gd")
if ragdoll_script:
ragdoll_instance = ragdoll_script.new()
get_tree().current_scene.add_child(ragdoll_instance)
ragdoll_instance.global_position = global_position
ragdoll_instance.build_ragdoll(Color(0.8, 0.1, 0.5))
# Calculate impulse
var linear_vel = Vector3.ZERO
var impulse = Vector3.ZERO
if pending_impulse.length_squared() > 0.01:
linear_vel = pending_impulse * 0.25
impulse = pending_impulse * 0.2
pending_impulse = Vector3.ZERO
else:
var hit_dir = Vector3.BACK
if source and source is CharacterBody3D:
var cam = source.get_node_or_null("HeadPivot/Camera3D")
if cam:
hit_dir = (_hit_position - cam.global_position).normalized()
linear_vel = hit_dir * (damage_amount * 0.02)
impulse = hit_dir * (damage_amount * 0.5)
get_tree().create_timer(0.01).timeout.connect(func():
if is_instance_valid(ragdoll_instance):
# The ragdoll also inherits the current walking velocity natively
ragdoll_instance.apply_initial_velocities(velocity + linear_vel, impulse)
)
get_tree().create_timer(3.0).timeout.connect(func():
_respawn()
)
func _respawn() -> void:
if is_instance_valid(ragdoll_instance):
ragdoll_instance.queue_free()
health = max_health
shield = max_shield
is_dead = false
# Respawn at point A and reset target
global_position = point_a
_target_point = point_b
visual_node.visible = true
col_shape.set_deferred("disabled", false)
label.visible = true
_update_label()
func _wiggle() -> void:
var tween = create_tween()
var orig_pos = Vector3.ZERO
var wiggle_dir = Vector3(randf_range(-0.1, 0.1), 0, randf_range(-0.1, 0.1))
tween.tween_property(visual_node, "position", orig_pos + wiggle_dir, 0.05)
tween.tween_property(visual_node, "position", orig_pos - wiggle_dir, 0.05)
tween.tween_property(visual_node, "position", orig_pos, 0.05)
+1
View File
@@ -0,0 +1 @@
uid://cvoitc10wcuul