Bull roll ability, console screen, tests
This commit is contained in:
+60
-11
@@ -18,6 +18,7 @@ var _sim: PhysicalBoneSimulator3D = null
|
||||
var _anim_player: AnimationPlayer = null
|
||||
var _wander_target: Vector3 = Vector3.ZERO
|
||||
var _idle_timer: float = 0.0
|
||||
var _idle_anim: StringName = _ANIM_IDLE
|
||||
var _bull: CharacterBody3D = null
|
||||
var _step_dir: Vector3 = Vector3.ZERO
|
||||
var _brace_timer: float = 0.0
|
||||
@@ -26,6 +27,7 @@ var _dodge_cd: float = 0.0
|
||||
var _will_dodge: bool = true
|
||||
var _attack_timer: float = 0.0
|
||||
var _swing_timer: float = 0.0
|
||||
var _lunge_timer: float = 0.0
|
||||
var _roll_timer: float = 0.0
|
||||
var _roll_dir: Vector3 = Vector3.ZERO
|
||||
var _throw_timer: float = 0.0
|
||||
@@ -57,6 +59,10 @@ const _ANIM_ATTACK: StringName = &"Attack"
|
||||
const _ANIM_ROLL: StringName = &"Roll"
|
||||
const _ANIM_DRAW: StringName = &"Draw_weapon"
|
||||
|
||||
# Idle taunt clips — one is picked at random each time the matador pauses to
|
||||
# taunt while wandering, so the crowd doesn't see the same gesture every time.
|
||||
const _ANIM_TAUNTS: Array[StringName] = [&"Taunt", &"Taunt_B", &"Taunt_C"]
|
||||
|
||||
# Fraction of the draw clip at which the hand grabs the sword — the sword
|
||||
# reparents from holster to hand at this point in the animation.
|
||||
const _DRAW_GRAB_AT: float = 0.55
|
||||
@@ -82,7 +88,7 @@ func _ready() -> void:
|
||||
if _skeleton:
|
||||
_sim = MatadorRagdoll.build(_skeleton)
|
||||
if _anim_player:
|
||||
for anim in [_ANIM_RUN, _ANIM_IDLE, _ANIM_ATTACK, _ANIM_ROLL]:
|
||||
for anim in [_ANIM_RUN, _ANIM_ATTACK, _ANIM_ROLL] + _ANIM_TAUNTS:
|
||||
_ensure_loop(anim)
|
||||
if not _anim_player.has_animation(_ANIM_RUN):
|
||||
push_warning("Matador: expected animations not found. Available: %s" %
|
||||
@@ -125,6 +131,10 @@ func _acquire_bull() -> void:
|
||||
func _physics_process(delta: float) -> void:
|
||||
_acquire_bull()
|
||||
|
||||
if DP.b("show_raycasts") and _steer_dir != Vector3.ZERO:
|
||||
var o := global_position + Vector3.UP * 0.6
|
||||
DebugDraw.ray(o, o + _steer_dir * _STEER_LOOKAHEAD, Color(0.3, 1.0, 0.9))
|
||||
|
||||
if not is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
|
||||
@@ -266,7 +276,7 @@ func _tick_wander(delta: float) -> void:
|
||||
if _drawing or _sheathing or _idle_timer > 0.0:
|
||||
_idle_timer = maxf(0.0, _idle_timer - delta)
|
||||
_decelerate(10.0, delta)
|
||||
_play_anim(_ANIM_IDLE)
|
||||
_play_anim(_idle_anim)
|
||||
move_and_slide()
|
||||
return
|
||||
|
||||
@@ -274,6 +284,7 @@ func _tick_wander(delta: float) -> void:
|
||||
_wander_target.x - global_position.x, 0.0, _wander_target.z - global_position.z)
|
||||
if to_target.length() < 0.8:
|
||||
_idle_timer = randf_range(DP.f("mat_idle_min"), DP.f("mat_idle_max"))
|
||||
_idle_anim = _pick_taunt()
|
||||
_pick_wander_target()
|
||||
else:
|
||||
var dir := _steer_clear(to_target.normalized(), delta)
|
||||
@@ -340,17 +351,23 @@ func _tick_brace(delta: float) -> void:
|
||||
_dodge_cd = DP.f("mat_dodge_cooldown")
|
||||
|
||||
|
||||
# Pase phase: sharp lateral step, then immediately exploit with an attack
|
||||
# Pase phase: sharp lateral step biased toward the bull so the drawn blade sweeps
|
||||
# across its path as it charges by; face the bull so the sword points at it.
|
||||
func _tick_sidestep(delta: float) -> void:
|
||||
_step_timer -= delta
|
||||
if _step_timer <= 0.0:
|
||||
_start_attack()
|
||||
return
|
||||
var dir := _step_dir
|
||||
var to_bull := _bull.global_position - global_position
|
||||
to_bull.y = 0.0
|
||||
if to_bull.length() > 0.1:
|
||||
dir = (_step_dir + to_bull.normalized() * DP.f("mat_pass_lunge")).normalized()
|
||||
var spd := DP.f("mat_step_speed")
|
||||
velocity.x = _step_dir.x * spd
|
||||
velocity.z = _step_dir.z * spd
|
||||
_face_velocity(delta, _TURN_SHARP)
|
||||
_play_anim(_ANIM_ROLL)
|
||||
velocity.x = dir.x * spd
|
||||
velocity.z = dir.z * spd
|
||||
_face_point(_bull.global_position, delta, _TURN_SHARP)
|
||||
_play_anim(_ANIM_ATTACK)
|
||||
move_and_slide()
|
||||
|
||||
|
||||
@@ -380,9 +397,14 @@ func _tick_attack(delta: float) -> void:
|
||||
# ends the matador re-closes if the bull slipped away, or stabs again if not.
|
||||
if _swing_timer <= 0.0 and dist <= DP.f("mat_attack_min_dist") and _sword_in_hand:
|
||||
_swing_timer = _anim_length(_ANIM_ATTACK, 0.9)
|
||||
_lunge_timer = DP.f("mat_lunge_time")
|
||||
|
||||
if _swing_timer > 0.0:
|
||||
_decelerate(22.0, delta)
|
||||
_lunge_timer = maxf(0.0, _lunge_timer - delta)
|
||||
if _lunge_timer > 0.0 and dist > 0.1:
|
||||
_accelerate(to_bull.normalized(), DP.f("mat_lunge_speed"), delta)
|
||||
else:
|
||||
_decelerate(22.0, delta)
|
||||
_play_anim(_ANIM_ATTACK)
|
||||
else:
|
||||
var dir := _steer_clear(to_bull.normalized(), delta)
|
||||
@@ -533,7 +555,18 @@ func _release_sword() -> void:
|
||||
cs.rotation_degrees = Vector3(90.0, 0.0, 0.0)
|
||||
rb.add_child(cs)
|
||||
|
||||
var dir := (_throw_dir + Vector3.UP * 0.12).normalized()
|
||||
# Lead the target: aim where the bull will be when the sword arrives, so a
|
||||
# moving bull isn't simply behind the throw by the time it lands.
|
||||
var aim := _throw_dir
|
||||
if is_instance_valid(_bull):
|
||||
var speed := maxf(DP.f("mat_throw_speed"), 0.1)
|
||||
var flat := _bull.global_position - gx.origin
|
||||
flat.y = 0.0
|
||||
var lead := (_bull.global_position + _bull.velocity * (flat.length() / speed)) - gx.origin
|
||||
lead.y = 0.0
|
||||
if lead.length() > 0.1:
|
||||
aim = lead.normalized()
|
||||
var dir := (aim + Vector3.UP * 0.12).normalized()
|
||||
rb.linear_velocity = dir * DP.f("mat_throw_speed")
|
||||
rb.angular_velocity = dir.cross(Vector3.UP).normalized() * -DP.f("mat_throw_spin")
|
||||
|
||||
@@ -559,6 +592,11 @@ func _tick_ragdoll(_delta: float) -> void:
|
||||
move_and_slide()
|
||||
|
||||
|
||||
# Current state as its enum name (WANDER / FLEE / …), for the debug state overlay.
|
||||
func ai_state_name() -> String:
|
||||
return State.keys()[_state]
|
||||
|
||||
|
||||
func apply_ability_hit(hit_dir: Vector3, strength: float, up_boost: float = 0.0) -> void:
|
||||
if _state == State.RAGDOLL or _state == State.ROLL:
|
||||
return
|
||||
@@ -585,7 +623,7 @@ func _on_body_entered(body: Node3D) -> void:
|
||||
|
||||
|
||||
func _on_blade_hit(body: Node3D) -> void:
|
||||
if _state == State.RAGDOLL or _state != State.ATTACK:
|
||||
if _state != State.ATTACK and _state != State.SIDESTEP:
|
||||
return
|
||||
if not body.is_in_group(&"player"):
|
||||
return
|
||||
@@ -709,6 +747,16 @@ func _ensure_loop(anim_name: StringName) -> void:
|
||||
anim.loop_mode = Animation.LOOP_LINEAR
|
||||
|
||||
|
||||
func _pick_taunt() -> StringName:
|
||||
var choices: Array[StringName] = []
|
||||
for anim in _ANIM_TAUNTS:
|
||||
if _anim_player != null and _anim_player.has_animation(anim):
|
||||
choices.append(anim)
|
||||
if choices.is_empty():
|
||||
return _ANIM_IDLE
|
||||
return choices[randi() % choices.size()]
|
||||
|
||||
|
||||
func _anim_length(anim_name: StringName, fallback: float) -> float:
|
||||
if _anim_player and _anim_player.has_animation(anim_name):
|
||||
return _anim_player.get_animation(anim_name).length
|
||||
@@ -807,7 +855,8 @@ func _spawn_sword() -> void:
|
||||
# THROW snaps the blade out (its arm cock-back covers the motion); everything
|
||||
# else animates with the Draw_weapon clip — forward to draw, reversed to sheathe.
|
||||
func _wants_sword_drawn() -> bool:
|
||||
return _state == State.ATTACK or _state == State.THROW
|
||||
return _state == State.ATTACK or _state == State.THROW \
|
||||
or _state == State.BRACE or _state == State.SIDESTEP
|
||||
|
||||
|
||||
func _update_sword_carry() -> void:
|
||||
|
||||
Reference in New Issue
Block a user