stick-the-quick/characters/abilities/SpinGlideAbility.gd

102 lines
2.8 KiB
GDScript

class_name SpinGlideAbility extends RunnerAbility
const PUNT_ACCELERATION: float = 30.0
const DROP_KICK_FACTOR: float = 3.0
const KNOCKBACK_ATTENUATION: float = 0.125
@export var animation_in_water: StringName
@export var timeout: float = 1.0
@export var drop_factor: float = 1.0
var saved_gravity_scale: float
var ending: bool
var timer: float
var in_water: bool
func begin(runner: Runner) -> void:
saved_gravity_scale = runner.gravity_scale
runner.gravity_scale = 0.01
runner.linear_velocity = runner.linear_velocity.slide(runner.up_normal)
ending = false
timer = timeout
in_water = false
func integrate_forces(
runner: Runner,
body_state: PhysicsDirectBodyState3D
) -> void:
if (
in_water or body_state.total_gravity.dot(Vector3.UP) > 0.0
) and not ending:
in_water = true
if runner.animation_player.current_animation != animation_in_water:
runner.animation_player.play(animation_in_water)
body_state.linear_velocity += drop_factor*runner.basis.z/2.0
body_state.linear_velocity -= (
runner.up_normal *
(runner.impetus*runner.basis).z *
runner.run_acceleration *
body_state.step
)
timer -= body_state.step
if timer <= 0.0 or (
body_state.total_gravity.dot(Vector3.UP) < 0.0
):
on_repeated_input(runner)
elif not runner.wall_normal.is_zero_approx():
runner.change_state(&'wall_slide')
elif ending:
if not runner.floor_normal.is_zero_approx():
runner.audio_player.stream = runner.land_sound
runner.audio_player.play()
runner.change_state(&'run')
else:
timer -= body_state.step
if timer <= 0.0:
on_repeated_input(runner)
for i in body_state.get_contact_count():
var other := body_state.get_contact_collider_object(i)
if other is Runner:
var rother := other as Runner
rother.knockback(
runner.global_position,
runner.mass*PUNT_ACCELERATION*(
DROP_KICK_FACTOR if ending else 1.0
)*KNOCKBACK_ATTENUATION
)
elif other is RigidBody3D:
var rother := other as RigidBody3D
rother.apply_force(
runner.mass*(
runner.impetus + runner.up_normal
).normalized()*PUNT_ACCELERATION*(
DROP_KICK_FACTOR if ending else 1.0
),
body_state.get_contact_collider_position(i) -
rother.global_position
)
func orientation(runner: Runner) -> Vector3:
if in_water:
return runner.linear_velocity
else:
return runner.basis.z
func animation_speed(runner: Runner) -> float:
return 1.0 + runner.linear_velocity.length()/20.0
func end(runner: Runner) -> void:
runner.gravity_scale = saved_gravity_scale
func on_repeated_input(runner: Runner) -> void:
if in_water:
runner.change_state(&'swim')
elif not ending:
runner.audio_player.stream = runner.jump_sound
runner.audio_player.play()
runner.gravity_scale = saved_gravity_scale
runner.linear_velocity -= (
drop_factor*runner.top_run_speed*runner.up_normal
)
ending = true