bull legs not clipped, arena not scaled

This commit is contained in:
2026-05-25 19:39:02 +03:00
parent 80a35a4553
commit 30136a9b90
5 changed files with 117 additions and 278 deletions
+106 -14
View File
@@ -1,6 +1,6 @@
extends CharacterBody3D
enum State { WANDER, RAGDOLL }
enum State { WANDER, FLEE, DODGE, RAGDOLL }
var _state: State = State.WANDER
var _skeleton: Skeleton3D = null
@@ -8,6 +8,10 @@ var _sim: PhysicalBoneSimulator3D = null
var _anim_player: AnimationPlayer = null
var _wander_target: Vector3 = Vector3.ZERO
var _idle_timer: float = 0.0
var _bull: CharacterBody3D = null
var _dodge_dir: Vector3 = Vector3.ZERO
var _dodge_timer: float = 0.0
var _dodge_cd: float = 0.0
@onready var _mesh: Node3D = $matador_v02
@onready var _hit_area: Area3D = $HitArea
@@ -35,13 +39,42 @@ func _ready() -> void:
func _physics_process(delta: float) -> void:
if _bull == null:
var players := get_tree().get_nodes_in_group(&"player")
if not players.is_empty():
_bull = players[0] as CharacterBody3D
if not is_on_floor():
velocity += get_gravity() * delta
_dodge_cd = maxf(0.0, _dodge_cd - delta)
if _state != State.RAGDOLL and _bull != null:
_update_ai_state()
match _state:
State.WANDER: _tick_wander(delta)
State.FLEE: _tick_flee(delta)
State.DODGE: _tick_dodge(delta)
State.RAGDOLL: _tick_ragdoll(delta)
func _update_ai_state() -> void:
var dist := global_position.distance_to(_bull.global_position)
match _state:
State.WANDER:
if dist < DP.f("mat_flee_range"):
_state = State.FLEE
State.FLEE:
if dist < DP.f("mat_dodge_range") and _dodge_cd <= 0.0:
_start_dodge()
elif dist > DP.f("mat_flee_range") * 1.3:
_state = State.WANDER
_pick_wander_target()
State.DODGE:
pass
func _tick_wander(delta: float) -> void:
if _idle_timer > 0.0:
_idle_timer -= delta
@@ -67,9 +100,54 @@ func _tick_wander(delta: float) -> void:
move_and_slide()
func _tick_ragdoll(delta: float) -> void:
velocity.x = move_toward(velocity.x, 0.0, 15.0 * delta)
velocity.z = move_toward(velocity.z, 0.0, 15.0 * delta)
func _tick_flee(delta: float) -> void:
var away := (global_position - _bull.global_position)
away.y = 0.0
if away.length() < 0.01:
away = Vector3(randf() - 0.5, 0.0, randf() - 0.5)
away = away.normalized()
var spd := DP.f("mat_flee_speed")
velocity.x = away.x * spd
velocity.z = away.z * spd
_mesh.rotation.y = lerp_angle(_mesh.rotation.y, atan2(velocity.x, velocity.z), delta * 8.0)
_play_anim(_WALK_ANIM)
move_and_slide()
func _start_dodge() -> void:
_state = State.DODGE
var bull_vel_flat := Vector3(_bull.velocity.x, 0.0, _bull.velocity.z)
var bull_fwd: Vector3
if bull_vel_flat.length() > 1.0:
bull_fwd = bull_vel_flat.normalized()
else:
bull_fwd = (_bull.global_position - global_position)
bull_fwd.y = 0.0
bull_fwd = bull_fwd.normalized()
# Step sideways relative to the bull's travel direction, biased slightly backward
var side := 1.0 if randf() > 0.5 else -1.0
var perp := bull_fwd.rotated(Vector3.UP, PI * 0.5 * side)
_dodge_dir = (perp * 0.7 + (-bull_fwd) * 0.3).normalized()
_dodge_timer = DP.f("mat_dodge_duration")
func _tick_dodge(delta: float) -> void:
_dodge_timer -= delta
if _dodge_timer <= 0.0:
_state = State.FLEE
_dodge_cd = DP.f("mat_dodge_cooldown")
return
var spd := DP.f("mat_dodge_speed")
velocity.x = _dodge_dir.x * spd
velocity.z = _dodge_dir.z * spd
_mesh.rotation.y = lerp_angle(_mesh.rotation.y, atan2(velocity.x, velocity.z), delta * 12.0)
_play_anim(_WALK_ANIM)
move_and_slide()
func _tick_ragdoll(_delta: float) -> void:
velocity.x = 0.0
velocity.z = 0.0
move_and_slide()
@@ -90,18 +168,30 @@ func _enter_ragdoll(hit_dir: Vector3, bull_speed: float) -> void:
hit_dir.y = 0.0
hit_dir = hit_dir.normalized()
# Cap lateral slide so matadors don't cross the arena; fixed upward pop
var lateral: float = minf(bull_speed * 0.2, 4.0)
velocity = Vector3(hit_dir.x * lateral, 2.0, hit_dir.z * lateral)
# Keep the CharacterBody3D stationary: physics bones simulate relative to the
# skeleton root, so sliding the root makes every bone pose diverge.
velocity = Vector3.ZERO
# Disable body capsule so a fast bull doesn't bounce off a ragdolling matador
_body_col.disabled = true
# Remove from collision layer so the bull passes through, but keep the
# shape enabled — move_and_slide() still needs it to detect the floor.
collision_layer = 0
if _anim_player:
_anim_player.stop()
_anim_player.pause() # pause keeps current frame; stop() resets to T-pose
if not _sim:
return
# Teleport each physics body to its current bone world transform, then
# enable per-bone simulation. _sim.active alone does NOT set simulate_physics;
# without it _process_modification() skips every bone and writes nothing.
for child: Node in _sim.get_children():
if not (child is PhysicalBone3D):
continue
var pb := child as PhysicalBone3D
var bone_idx := _skeleton.find_bone(pb.bone_name)
if bone_idx >= 0:
pb.global_transform = _skeleton.global_transform * _skeleton.get_bone_global_pose(bone_idx)
pb.simulate_physics = true
_sim.active = true
var strength := DP.f("mat_ragdoll_impulse")
for child: Node in _sim.get_children():
@@ -181,10 +271,12 @@ func _setup_physical_bones() -> PhysicalBoneSimulator3D:
if _skeleton.find_bone(bname) == -1:
continue
var pb := PhysicalBone3D.new()
pb.bone_name = bname
pb.joint_type = PhysicalBone3D.JOINT_TYPE_PIN
pb.linear_damp = 1.0
pb.angular_damp = 1.0
pb.bone_name = bname
pb.joint_type = PhysicalBone3D.JOINT_TYPE_PIN
pb.linear_damp = 3.0
pb.angular_damp = 4.0
pb.collision_layer = 1
pb.collision_mask = 1
var cs := CollisionShape3D.new()
var use_capsule: bool = entry.size() >= 3
if use_capsule: