Add casting animation phases and stabilize held rod

This commit is contained in:
Alexander Sellite 2026-08-09 13:31:30 -04:00
parent 9e6536adcd
commit ff536e62a6
5 changed files with 97 additions and 13 deletions

View file

@ -363,15 +363,15 @@ func handle_escape_action() -> bool:
func set_local_menu_input_suppressed(
owner: StringName,
input_owner: StringName,
suppressed: bool,
) -> void:
if owner.is_empty():
if input_owner.is_empty():
return
if suppressed:
_local_menu_input_owners[owner] = true
_local_menu_input_owners[input_owner] = true
else:
_local_menu_input_owners.erase(owner)
_local_menu_input_owners.erase(input_owner)
if suppressed and state == FishingState.WAITING_FOR_BITE:
_withdrawal_input_held = false
_stop_reeling_audio()
@ -627,6 +627,7 @@ func _begin_aiming(player: PlayerType) -> void:
_cast_charge = 0.0
_cast_target = _calculate_cast_target(minimum_cast_distance)
state = FishingState.AIMING_CAST
_active_player.set_casting_visual()
status_changed.emit("")
_cast_path_is_clear = is_cast_path_clear(
_get_cast_launch_position(),
@ -813,6 +814,8 @@ func _confirm_cast() -> void:
# released so the active bobber/fishing event remains anchored.
_active_player.set_movement_enabled(false)
state = FishingState.CASTING
if _active_player != null:
_active_player.set_release_visual()
status_changed.emit("")
_presentation.begin_cast(
_cast_target,
@ -1102,7 +1105,7 @@ func _on_catch_encounter_updated(
barrier_health: PackedInt32Array,
barrier_max_health: PackedInt32Array,
active_barrier_index: int,
visible: bool,
encounter_visible: bool,
) -> void:
catch_display_changed.emit(
progress,
@ -1111,9 +1114,9 @@ func _on_catch_encounter_updated(
barrier_health,
barrier_max_health,
active_barrier_index,
visible
encounter_visible
)
if state != FishingState.FIGHTING or not visible:
if state != FishingState.FIGHTING or not encounter_visible:
return

View file

@ -62,6 +62,7 @@ func show_cast(origin: Vector3, target: Vector3) -> void:
_bobber.visible = true
_line.visible = true
_owner.set_active_item_is_rod(true)
_owner.set_release_visual()
_redraw_line()
_cast_tween = create_tween()
_cast_tween.set_trans(Tween.TRANS_SINE)
@ -99,6 +100,7 @@ func show_bite() -> void:
func play_return(showcase_catch: FishCatch = null) -> void:
if _owner != null and is_instance_valid(_owner):
_owner.set_fighting_visual(false)
_owner.set_fishing_visual(false)
if not _active or _bobber == null:
cleanup()
return_completed.emit()
@ -135,6 +137,7 @@ func cleanup() -> void:
_bobber_idle_elapsed = 0.0
if _owner != null and is_instance_valid(_owner):
_owner.set_fighting_visual(false)
_owner.set_fishing_visual(false)
_owner.end_catch_showcase(Callable(), true)
if _bobber != null:
_bobber.visible = false
@ -162,6 +165,8 @@ func _set_cast_sample(
func _on_cast_finished() -> void:
_cast_tween = null
_bobber_idle_elapsed = 0.0
if _owner != null and is_instance_valid(_owner):
_owner.set_fishing_visual(true)
_apply_bobber_idle_motion()
_redraw_line()

View file

@ -41,10 +41,19 @@ const CHARACTER_IDLE_SIT_ANIMATION: StringName = &"idle_sit"
const CHARACTER_IDLE_SIT_SHOW_ANIMATION: StringName = &"idle_sit_show"
const CHARACTER_WALKING_ANIMATION: StringName = &"walking"
const CHARACTER_WALKING_SHOW_ANIMATION: StringName = &"walking_show"
const CHARACTER_CASTING_ANIMATION: StringName = &"casting"
const CHARACTER_RELEASE_ANIMATION: StringName = &"release"
const CHARACTER_FISHING_ANIMATION: StringName = &"fishing"
const CHARACTER_FISHING_SIT_ANIMATION: StringName = &"fishing_sit"
const CHARACTER_FIGHTING_ANIMATION: StringName = &"fighting"
const CHARACTER_FIGHTING_SIT_ANIMATION: StringName = &"fighting_sit"
enum FishingVisualPhase {
NONE,
CASTING,
RELEASE,
FISHING,
}
const FIGHTING_EYES_ID: String = "alligator_eyes"
const BASE_REEL_SPEED: float = 0.16
# The target Android handheld exposes its physical right trigger through
@ -121,9 +130,24 @@ func set_fighting_visual(active: bool) -> void:
func set_fishing_visual(active: bool) -> void:
if _fishing_visual_active == active:
_set_fishing_visual_phase(
FishingVisualPhase.FISHING if active else FishingVisualPhase.NONE
)
func set_casting_visual() -> void:
_set_fishing_visual_phase(FishingVisualPhase.CASTING)
func set_release_visual() -> void:
_set_fishing_visual_phase(FishingVisualPhase.RELEASE)
func _set_fishing_visual_phase(phase: FishingVisualPhase) -> void:
if _fishing_visual_phase == phase:
return
_fishing_visual_active = active
_fishing_visual_phase = phase
_fishing_visual_active = phase != FishingVisualPhase.NONE
_character_animation_name = &""
_update_character_animation()
@ -283,8 +307,12 @@ var _held_fish_visible: bool = false
var _showcase_animation_active: bool = false
var _fighting_visual_active: bool = false
var _fishing_visual_active: bool = false
var _fishing_visual_phase: FishingVisualPhase = FishingVisualPhase.NONE
var _fishing_rod: Node3D
var _fishing_rod_tip: Marker3D
var _fishing_rod_skeleton: Skeleton3D
var _fishing_rod_hand_bone: int = -1
var _fishing_rod_relative_basis: Basis = Basis.IDENTITY
var _controller_mapping_manager: ControllerMappingManagerType
@ -334,6 +362,38 @@ func _initialize_fishing_rod() -> void:
_fishing_rod_tip = attachment.get_node(
"FishingRod/FishingRodTip"
) as Marker3D
_fishing_rod_skeleton = skeleton
_fishing_rod_hand_bone = skeleton.find_bone(&"hand.R")
if _fishing_rod_hand_bone < 0:
push_error("Player character right-hand bone is unavailable.")
return
_fishing_rod_relative_basis = (
skeleton.global_basis.orthonormalized().inverse()
* _fishing_rod.global_basis.orthonormalized()
)
_fishing_rod.top_level = true
_sync_fishing_rod_transform()
func _sync_fishing_rod_transform() -> void:
if (
_fishing_rod == null
or _fishing_rod_skeleton == null
or _fishing_rod_hand_bone < 0
):
return
var skeleton_transform := _fishing_rod_skeleton.global_transform
var hand_pose := _fishing_rod_skeleton.get_bone_global_pose(
_fishing_rod_hand_bone
)
var fixed_basis := (
skeleton_transform.basis.orthonormalized()
* _fishing_rod_relative_basis
)
_fishing_rod.global_transform = Transform3D(
fixed_basis.scaled(skeleton_transform.basis.get_scale().abs()),
(skeleton_transform * hand_pose).origin,
)
func _initialize_held_item_attachment() -> void:
@ -363,6 +423,7 @@ func _initialize_held_item_attachment() -> void:
func _physics_process(delta: float) -> void:
_sync_fishing_rod_transform()
if _network_interpolation_enabled:
_update_network_interpolation(delta)
return
@ -455,6 +516,7 @@ func _physics_process(delta: float) -> void:
func _process(delta: float) -> void:
_sync_fishing_rod_transform()
_update_character_animation()
# The hand bone supplies the attachment position, but its animated wrist
# rotation should not turn the flat fish/catch artwork edge-on. Keep each
@ -588,10 +650,24 @@ func _update_character_animation() -> void:
&"idle_sit_loop",
]
else:
requested_animation = [
CHARACTER_FISHING_ANIMATION,
CHARACTER_IDLE_ANIMATION,
]
match _fishing_visual_phase:
FishingVisualPhase.CASTING:
requested_animation = [
CHARACTER_CASTING_ANIMATION,
CHARACTER_FISHING_ANIMATION,
CHARACTER_IDLE_ANIMATION,
]
FishingVisualPhase.RELEASE:
requested_animation = [
CHARACTER_RELEASE_ANIMATION,
CHARACTER_FISHING_ANIMATION,
CHARACTER_IDLE_ANIMATION,
]
_:
requested_animation = [
CHARACTER_FISHING_ANIMATION,
CHARACTER_IDLE_ANIMATION,
]
elif _sitting:
if _held_fish_visible:
requested_animation = [