Pyxus / fray

Fray – Godot Combat Framework
https://fray.pyxus.dev
MIT License
249 stars 11 forks source link

RootState Transition At_End #54

Closed Remi123 closed 1 year ago

Remi123 commented 1 year ago

Root state.gd lign 434

func _can_switch(transition: FrayStateMachineTransition) -> bool:
    return (
        transition.switch_mode == FrayStateMachineTransition.SwitchMode.IMMEDIATE
        or (transition.switch_mode == FrayStateMachineTransition.SwitchMode.AT_END 
        and is_done_processing())
        )

should be

func _can_switch(transition: FrayStateMachineTransition) -> bool:
    return (
        transition.switch_mode == FrayStateMachineTransition.SwitchMode.IMMEDIATE
        or (transition.switch_mode == FrayStateMachineTransition.SwitchMode.AT_END 
        and get_state_current()._is_done_processing_impl()) # <-------
        )

I based my correction on this enum in FrayStateMachineTransition :

enum SwitchMode{
    IMMEDIATE, ## Switch to the next state immediately.
    AT_END, ## Wait for the current state to end, then switch to the beginning of the next state.
}

The current implementation use the FrayRootState's _is_done_processing_impl() function, which basically check if we are at the end state of this root state. But FrayRootState didn't check the current state's _is_done_processing .

combat_sm.add_situation("grounded",FrayRootState.builder()
    .start_at('standing')
    .add_state('dash',dash_state) # custom FrayState that override _is_done_processing to wait for a timer on entering the state.
    .transition_sequence('standing','dash',{sequence="dash"}) # FraySequenceBranch for double tapping
    .transition("dash",'standing',{switch_mode = FrayStateMachineTransition.SwitchMode.AT_END,prereqs=[on_floor]})

I also changed root_state.gd:lign 430 for this

func _goto(to_state: StringName, args: Dictionary) -> void:
    if _ERR_INVALID_STATE(to_state): return

    var prev_state_name := _current_state
    var target_state: RefCounted = get_state(to_state) <----

    if target_state != null && not _current_state.is_empty(): <----
        get_state(_current_state)._exit_impl() <----

    _current_state = to_state
    get_state(_current_state)._enter_impl(args)
    transitioned.emit(prev_state_name, _current_state)

This correctly call _enter_impl() and _exit_impl(), previously it was exiting the target_state before entering it in any transition.