2018-08-29 20:38:13 +00:00
/**************************************************************************/
/* animation_node_state_machine.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-06-25 19:21:57 +00:00
# include "animation_node_state_machine.h"
2022-09-23 11:52:21 +00:00
# include "scene/main/window.h"
2018-06-25 19:21:57 +00:00
/////////////////////////////////////////////////
void AnimationNodeStateMachineTransition : : set_switch_mode ( SwitchMode p_mode ) {
switch_mode = p_mode ;
}
AnimationNodeStateMachineTransition : : SwitchMode AnimationNodeStateMachineTransition : : get_switch_mode ( ) const {
return switch_mode ;
}
2022-10-06 15:44:59 +00:00
void AnimationNodeStateMachineTransition : : set_advance_mode ( AdvanceMode p_mode ) {
advance_mode = p_mode ;
2018-06-25 19:21:57 +00:00
}
2022-10-06 15:44:59 +00:00
AnimationNodeStateMachineTransition : : AdvanceMode AnimationNodeStateMachineTransition : : get_advance_mode ( ) const {
return advance_mode ;
2018-06-25 19:21:57 +00:00
}
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachineTransition : : set_advance_condition ( const StringName & p_condition ) {
String cs = p_condition ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( cs . contains ( " / " ) | | cs . contains ( " : " ) ) ;
2018-08-20 16:38:18 +00:00
advance_condition = p_condition ;
2021-12-09 09:42:46 +00:00
if ( ! cs . is_empty ( ) ) {
2018-08-20 16:38:18 +00:00
advance_condition_name = " conditions/ " + cs ;
} else {
advance_condition_name = StringName ( ) ;
}
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " advance_condition_changed " ) ) ;
2018-08-20 16:38:18 +00:00
}
StringName AnimationNodeStateMachineTransition : : get_advance_condition ( ) const {
return advance_condition ;
}
StringName AnimationNodeStateMachineTransition : : get_advance_condition_name ( ) const {
return advance_condition_name ;
}
2022-04-20 10:36:54 +00:00
void AnimationNodeStateMachineTransition : : set_advance_expression ( const String & p_expression ) {
advance_expression = p_expression ;
String advance_expression_stripped = advance_expression . strip_edges ( ) ;
if ( advance_expression_stripped = = String ( ) ) {
expression . unref ( ) ;
return ;
}
if ( expression . is_null ( ) ) {
expression . instantiate ( ) ;
}
expression - > parse ( advance_expression_stripped ) ;
}
String AnimationNodeStateMachineTransition : : get_advance_expression ( ) const {
return advance_expression ;
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachineTransition : : set_xfade_time ( float p_xfade ) {
ERR_FAIL_COND ( p_xfade < 0 ) ;
2022-07-28 08:31:23 +00:00
xfade_time = p_xfade ;
2018-06-25 19:21:57 +00:00
emit_changed ( ) ;
}
float AnimationNodeStateMachineTransition : : get_xfade_time ( ) const {
2022-07-28 08:31:23 +00:00
return xfade_time ;
}
void AnimationNodeStateMachineTransition : : set_xfade_curve ( const Ref < Curve > & p_curve ) {
xfade_curve = p_curve ;
}
Ref < Curve > AnimationNodeStateMachineTransition : : get_xfade_curve ( ) const {
return xfade_curve ;
2018-06-25 19:21:57 +00:00
}
2023-01-10 09:15:16 +00:00
void AnimationNodeStateMachineTransition : : set_reset ( bool p_reset ) {
reset = p_reset ;
emit_changed ( ) ;
}
bool AnimationNodeStateMachineTransition : : is_reset ( ) const {
return reset ;
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachineTransition : : set_priority ( int p_priority ) {
priority = p_priority ;
emit_changed ( ) ;
}
int AnimationNodeStateMachineTransition : : get_priority ( ) const {
return priority ;
}
void AnimationNodeStateMachineTransition : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_switch_mode " , " mode " ) , & AnimationNodeStateMachineTransition : : set_switch_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_switch_mode " ) , & AnimationNodeStateMachineTransition : : get_switch_mode ) ;
2022-10-06 15:44:59 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_advance_mode " , " mode " ) , & AnimationNodeStateMachineTransition : : set_advance_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_advance_mode " ) , & AnimationNodeStateMachineTransition : : get_advance_mode ) ;
2018-06-25 19:21:57 +00:00
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_advance_condition " , " name " ) , & AnimationNodeStateMachineTransition : : set_advance_condition ) ;
ClassDB : : bind_method ( D_METHOD ( " get_advance_condition " ) , & AnimationNodeStateMachineTransition : : get_advance_condition ) ;
2018-06-25 19:21:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_xfade_time " , " secs " ) , & AnimationNodeStateMachineTransition : : set_xfade_time ) ;
ClassDB : : bind_method ( D_METHOD ( " get_xfade_time " ) , & AnimationNodeStateMachineTransition : : get_xfade_time ) ;
2022-07-28 08:31:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_xfade_curve " , " curve " ) , & AnimationNodeStateMachineTransition : : set_xfade_curve ) ;
ClassDB : : bind_method ( D_METHOD ( " get_xfade_curve " ) , & AnimationNodeStateMachineTransition : : get_xfade_curve ) ;
2023-01-10 09:15:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_reset " , " reset " ) , & AnimationNodeStateMachineTransition : : set_reset ) ;
ClassDB : : bind_method ( D_METHOD ( " is_reset " ) , & AnimationNodeStateMachineTransition : : is_reset ) ;
2018-06-25 19:21:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_priority " , " priority " ) , & AnimationNodeStateMachineTransition : : set_priority ) ;
ClassDB : : bind_method ( D_METHOD ( " get_priority " ) , & AnimationNodeStateMachineTransition : : get_priority ) ;
2022-04-20 10:36:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_advance_expression " , " text " ) , & AnimationNodeStateMachineTransition : : set_advance_expression ) ;
ClassDB : : bind_method ( D_METHOD ( " get_advance_expression " ) , & AnimationNodeStateMachineTransition : : get_advance_expression ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " xfade_time " , PROPERTY_HINT_RANGE , " 0,240,0.01,suffix:s " ) , " set_xfade_time " , " get_xfade_time " ) ;
2022-07-28 08:31:23 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " xfade_curve " , PROPERTY_HINT_RESOURCE_TYPE , " Curve " ) , " set_xfade_curve " , " get_xfade_curve " ) ;
2023-01-10 09:15:16 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " reset " ) , " set_reset " , " is_reset " ) ;
2022-04-20 10:36:54 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " priority " , PROPERTY_HINT_RANGE , " 0,32,1 " ) , " set_priority " , " get_priority " ) ;
ADD_GROUP ( " Switch " , " " ) ;
2021-05-22 02:30:58 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " switch_mode " , PROPERTY_HINT_ENUM , " Immediate,Sync,At End " ) , " set_switch_mode " , " get_switch_mode " ) ;
2022-04-20 10:36:54 +00:00
ADD_GROUP ( " Advance " , " advance_ " ) ;
2022-10-06 15:44:59 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " advance_mode " , PROPERTY_HINT_ENUM , " Disabled,Enabled,Auto " ) , " set_advance_mode " , " get_advance_mode " ) ;
2020-02-20 21:58:05 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING_NAME , " advance_condition " ) , " set_advance_condition " , " get_advance_condition " ) ;
2022-04-20 10:36:54 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " advance_expression " , PROPERTY_HINT_EXPRESSION , " " ) , " set_advance_expression " , " get_advance_expression " ) ;
2018-06-25 19:21:57 +00:00
2018-06-21 12:47:07 +00:00
BIND_ENUM_CONSTANT ( SWITCH_MODE_IMMEDIATE ) ;
BIND_ENUM_CONSTANT ( SWITCH_MODE_SYNC ) ;
BIND_ENUM_CONSTANT ( SWITCH_MODE_AT_END ) ;
2018-08-20 16:38:18 +00:00
2022-10-06 15:44:59 +00:00
BIND_ENUM_CONSTANT ( ADVANCE_MODE_DISABLED ) ;
BIND_ENUM_CONSTANT ( ADVANCE_MODE_ENABLED ) ;
BIND_ENUM_CONSTANT ( ADVANCE_MODE_AUTO ) ;
2018-08-20 16:38:18 +00:00
ADD_SIGNAL ( MethodInfo ( " advance_condition_changed " ) ) ;
2018-06-25 19:21:57 +00:00
}
AnimationNodeStateMachineTransition : : AnimationNodeStateMachineTransition ( ) {
}
2018-08-20 16:38:18 +00:00
////////////////////////////////////////////////////////
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachinePlayback : : _set_current ( AnimationNodeStateMachine * p_state_machine , const StringName & p_state ) {
current = p_state ;
if ( current = = StringName ( ) ) {
group_start_transition = Ref < AnimationNodeStateMachineTransition > ( ) ;
group_end_transition = Ref < AnimationNodeStateMachineTransition > ( ) ;
return ;
}
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > find_node_by_path ( current ) ;
if ( ! anodesm . is_valid ( ) ) {
group_start_transition = Ref < AnimationNodeStateMachineTransition > ( ) ;
group_end_transition = Ref < AnimationNodeStateMachineTransition > ( ) ;
return ;
}
Vector < int > indices = p_state_machine - > find_transition_to ( current ) ;
int group_start_size = indices . size ( ) ;
if ( group_start_size ) {
group_start_transition = p_state_machine - > get_transition ( indices [ 0 ] ) ;
} else {
group_start_transition = Ref < AnimationNodeStateMachineTransition > ( ) ;
}
indices = p_state_machine - > find_transition_from ( current ) ;
int group_end_size = indices . size ( ) ;
if ( group_end_size ) {
group_end_transition = p_state_machine - > get_transition ( indices [ 0 ] ) ;
} else {
group_end_transition = Ref < AnimationNodeStateMachineTransition > ( ) ;
}
// Validation.
if ( anodesm - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
indices = anodesm - > find_transition_from ( anodesm - > start_node ) ;
int anodesm_start_size = indices . size ( ) ;
indices = anodesm - > find_transition_to ( anodesm - > end_node ) ;
int anodesm_end_size = indices . size ( ) ;
if ( group_start_size > 1 ) {
WARN_PRINT_ED ( " There are two or more transitions to the Grouped AnimationNodeStateMachine in AnimationNodeStateMachine: " + base_path + " , which may result in unintended transitions. " ) ;
}
if ( group_end_size > 1 ) {
WARN_PRINT_ED ( " There are two or more transitions from the Grouped AnimationNodeStateMachine in AnimationNodeStateMachine: " + base_path + " , which may result in unintended transitions. " ) ;
}
if ( anodesm_start_size > 1 ) {
WARN_PRINT_ED ( " There are two or more transitions from the Start of Grouped AnimationNodeStateMachine in AnimationNodeStateMachine: " + base_path + current + " , which may result in unintended transitions. " ) ;
}
if ( anodesm_end_size > 1 ) {
WARN_PRINT_ED ( " There are two or more transitions to the End of Grouped AnimationNodeStateMachine in AnimationNodeStateMachine: " + base_path + current + " , which may result in unintended transitions. " ) ;
}
if ( anodesm_start_size ! = group_start_size ) {
ERR_PRINT_ED ( " There is a mismatch in the number of start transitions in and out of the Grouped AnimationNodeStateMachine on AnimationNodeStateMachine: " + base_path + current + " . " ) ;
}
if ( anodesm_end_size ! = group_end_size ) {
ERR_PRINT_ED ( " There is a mismatch in the number of end transitions in and out of the Grouped AnimationNodeStateMachine on AnimationNodeStateMachine: " + base_path + current + " . " ) ;
}
}
}
void AnimationNodeStateMachinePlayback : : _set_grouped ( bool p_is_grouped ) {
is_grouped = p_is_grouped ;
}
2023-01-10 09:15:16 +00:00
void AnimationNodeStateMachinePlayback : : travel ( const StringName & p_state , bool p_reset_on_teleport ) {
2023-02-18 02:02:28 +00:00
ERR_FAIL_COND_EDMSG ( is_grouped , " Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine. " ) ;
ERR_FAIL_COND_EDMSG ( String ( p_state ) . contains ( " /Start " ) | | String ( p_state ) . contains ( " /End " ) , " Grouped AnimationNodeStateMachinePlayback doesn't allow to play Start/End directly. Instead, play the prev or next state of group in the parent AnimationNodeStateMachine. " ) ;
_travel_main ( p_state , p_reset_on_teleport ) ;
}
void AnimationNodeStateMachinePlayback : : start ( const StringName & p_state , bool p_reset ) {
ERR_FAIL_COND_EDMSG ( is_grouped , " Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine. " ) ;
ERR_FAIL_COND_EDMSG ( String ( p_state ) . contains ( " /Start " ) | | String ( p_state ) . contains ( " /End " ) , " Grouped AnimationNodeStateMachinePlayback doesn't allow to play Start/End directly. Instead, play the prev or next state of group in the parent AnimationNodeStateMachine. " ) ;
_start_main ( p_state , p_reset ) ;
}
void AnimationNodeStateMachinePlayback : : next ( ) {
ERR_FAIL_COND_EDMSG ( is_grouped , " Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine. " ) ;
_next_main ( ) ;
}
void AnimationNodeStateMachinePlayback : : stop ( ) {
ERR_FAIL_COND_EDMSG ( is_grouped , " Grouped AnimationNodeStateMachinePlayback must be handled by parent AnimationNodeStateMachinePlayback. You need to retrieve the parent Root/Nested AnimationNodeStateMachine. " ) ;
_stop_main ( ) ;
}
void AnimationNodeStateMachinePlayback : : _travel_main ( const StringName & p_state , bool p_reset_on_teleport ) {
2023-01-10 09:15:16 +00:00
travel_request = p_state ;
reset_request_on_teleport = p_reset_on_teleport ;
2018-08-20 16:38:18 +00:00
stop_request = false ;
}
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachinePlayback : : _start_main ( const StringName & p_state , bool p_reset ) {
2023-01-10 09:15:16 +00:00
travel_request = StringName ( ) ;
2023-02-18 02:02:28 +00:00
path . clear ( ) ;
2023-01-10 09:15:16 +00:00
reset_request = p_reset ;
2018-08-20 16:38:18 +00:00
start_request = p_state ;
stop_request = false ;
}
2020-05-14 12:29:06 +00:00
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachinePlayback : : _next_main ( ) {
2023-01-10 09:15:16 +00:00
next_request = true ;
}
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachinePlayback : : _stop_main ( ) {
2018-08-20 16:38:18 +00:00
stop_request = true ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
bool AnimationNodeStateMachinePlayback : : is_playing ( ) const {
return playing ;
}
2020-05-14 12:29:06 +00:00
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachinePlayback : : is_end ( ) const {
return current = = " End " & & fading_from = = StringName ( ) ;
}
2018-08-20 16:38:18 +00:00
StringName AnimationNodeStateMachinePlayback : : get_current_node ( ) const {
return current ;
}
2020-05-14 12:29:06 +00:00
2023-01-22 06:50:53 +00:00
StringName AnimationNodeStateMachinePlayback : : get_fading_from_node ( ) const {
2018-08-20 16:38:18 +00:00
return fading_from ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
Vector < StringName > AnimationNodeStateMachinePlayback : : get_travel_path ( ) const {
return path ;
}
2020-05-14 12:29:06 +00:00
2023-04-24 22:21:32 +00:00
TypedArray < StringName > AnimationNodeStateMachinePlayback : : _get_travel_path ( ) const {
return Variant ( get_travel_path ( ) ) . operator Array ( ) ;
}
2018-08-20 16:38:18 +00:00
float AnimationNodeStateMachinePlayback : : get_current_play_pos ( ) const {
return pos_current ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
float AnimationNodeStateMachinePlayback : : get_current_length ( ) const {
return len_current ;
}
2022-10-21 17:35:25 +00:00
float AnimationNodeStateMachinePlayback : : get_fade_from_play_pos ( ) const {
return pos_fade_from ;
}
float AnimationNodeStateMachinePlayback : : get_fade_from_length ( ) const {
return len_fade_from ;
}
float AnimationNodeStateMachinePlayback : : get_fading_time ( ) const {
return fading_time ;
}
float AnimationNodeStateMachinePlayback : : get_fading_pos ( ) const {
return fading_pos ;
}
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachinePlayback : : _clear_path_children ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , bool p_test_only ) {
List < AnimationNode : : ChildNode > child_nodes ;
p_state_machine - > get_child_nodes ( & child_nodes ) ;
for ( int i = 0 ; i < child_nodes . size ( ) ; i + + ) {
Ref < AnimationNodeStateMachine > anodesm = child_nodes [ i ] . node ;
if ( anodesm . is_valid ( ) & & anodesm - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
Ref < AnimationNodeStateMachinePlayback > playback = p_tree - > get ( base_path + child_nodes [ i ] . name + " /playback " ) ;
ERR_FAIL_COND ( ! playback . is_valid ( ) ) ;
playback - > _set_base_path ( base_path + child_nodes [ i ] . name + " / " ) ;
if ( p_test_only ) {
playback = playback - > duplicate ( ) ;
}
playback - > path . clear ( ) ;
playback - > _clear_path_children ( p_tree , anodesm . ptr ( ) , p_test_only ) ;
if ( current ! = child_nodes [ i ] . name ) {
playback - > _start ( anodesm . ptr ( ) ) ; // Can restart.
}
}
}
}
void AnimationNodeStateMachinePlayback : : _start_children ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , const String & p_path , bool p_test_only ) {
if ( p_state_machine - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
return ; // This function must be fired only by the top state machine, do nothing in child state machine.
}
Vector < String > temp_path = p_path . split ( " / " ) ;
if ( temp_path . size ( ) > 1 ) {
for ( int i = 1 ; i < temp_path . size ( ) ; i + + ) {
String concatenated ;
for ( int j = 0 ; j < i ; j + + ) {
concatenated + = temp_path [ j ] + ( j = = i - 1 ? " " : " / " ) ;
}
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > find_node_by_path ( concatenated ) ;
if ( anodesm . is_valid ( ) & & anodesm - > get_state_machine_type ( ) ! = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
ERR_FAIL_MSG ( " Root/Nested AnimationNodeStateMachine can't have path from parent AnimationNodeStateMachine. " ) ;
}
Ref < AnimationNodeStateMachinePlayback > playback = p_tree - > get ( base_path + concatenated + " /playback " ) ;
ERR_FAIL_COND ( ! playback . is_valid ( ) ) ;
playback - > _set_base_path ( base_path + concatenated + " / " ) ;
if ( p_test_only ) {
playback = playback - > duplicate ( ) ;
}
playback - > _start_main ( temp_path [ i ] , i = = temp_path . size ( ) - 1 ? reset_request : false ) ;
}
reset_request = false ;
}
}
bool AnimationNodeStateMachinePlayback : : _travel_children ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , const String & p_path , bool p_is_allow_transition_to_self , bool p_is_parent_same_state , bool p_test_only ) {
if ( p_state_machine - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
return false ; // This function must be fired only by the top state machine, do nothing in child state machine.
}
Vector < String > temp_path = p_path . split ( " / " ) ;
Vector < ChildStateMachineInfo > children ;
bool found_route = true ;
bool is_parent_same_state = p_is_parent_same_state ;
if ( temp_path . size ( ) > 1 ) {
for ( int i = 1 ; i < temp_path . size ( ) ; i + + ) {
String concatenated ;
for ( int j = 0 ; j < i ; j + + ) {
concatenated + = temp_path [ j ] + ( j = = i - 1 ? " " : " / " ) ;
}
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > find_node_by_path ( concatenated ) ;
if ( anodesm . is_valid ( ) & & anodesm - > get_state_machine_type ( ) ! = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
ERR_FAIL_V_MSG ( false , " Root/Nested AnimationNodeStateMachine can't have path from parent AnimationNodeStateMachine. " ) ;
}
Ref < AnimationNodeStateMachinePlayback > playback = p_tree - > get ( base_path + concatenated + " /playback " ) ;
ERR_FAIL_COND_V ( ! playback . is_valid ( ) , false ) ;
playback - > _set_base_path ( base_path + concatenated + " / " ) ;
if ( p_test_only ) {
playback = playback - > duplicate ( ) ;
}
if ( ! playback - > is_playing ( ) ) {
playback - > _start ( anodesm . ptr ( ) ) ;
}
ChildStateMachineInfo child_info ;
child_info . playback = playback ;
// Process for the case that parent state is changed.
bool child_found_route = true ;
bool is_current_same_state = temp_path [ i ] = = playback - > get_current_node ( ) ;
if ( ! is_parent_same_state ) {
// Force travel to end current child state machine.
String child_path = " / " + playback - > get_current_node ( ) ;
while ( true ) {
Ref < AnimationNodeStateMachine > child_anodesm = p_state_machine - > find_node_by_path ( concatenated + child_path ) ;
if ( ! child_anodesm . is_valid ( ) | | child_anodesm - > get_state_machine_type ( ) ! = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
break ;
}
Ref < AnimationNodeStateMachinePlayback > child_playback = p_tree - > get ( base_path + concatenated + child_path + " /playback " ) ;
ERR_FAIL_COND_V ( ! child_playback . is_valid ( ) , false ) ;
child_playback - > _set_base_path ( base_path + concatenated + " / " ) ;
if ( p_test_only ) {
child_playback = child_playback - > duplicate ( ) ;
}
child_playback - > _travel_main ( " End " ) ;
child_found_route & = child_playback - > _travel ( p_tree , child_anodesm . ptr ( ) , false , p_test_only ) ;
child_path + = " / " + child_playback - > get_current_node ( ) ;
}
// Force restart target state machine.
playback - > _start ( anodesm . ptr ( ) ) ;
}
is_parent_same_state = is_current_same_state ;
bool is_deepest_state = i = = temp_path . size ( ) - 1 ;
child_info . is_reset = is_deepest_state ? reset_request_on_teleport : false ;
playback - > _travel_main ( temp_path [ i ] , child_info . is_reset ) ;
if ( playback - > _make_travel_path ( p_tree , anodesm . ptr ( ) , is_deepest_state ? p_is_allow_transition_to_self : false , child_info . path , p_test_only ) ) {
found_route & = child_found_route ;
} else {
child_info . path . push_back ( temp_path [ i ] ) ;
found_route = false ;
}
children . push_back ( child_info ) ;
}
reset_request_on_teleport = false ;
}
if ( found_route ) {
for ( int i = 0 ; i < children . size ( ) ; i + + ) {
children . write [ i ] . playback - > clear_path ( ) ;
for ( int j = 0 ; j < children [ i ] . path . size ( ) ; j + + ) {
children . write [ i ] . playback - > push_path ( children [ i ] . path [ j ] ) ;
}
}
} else {
for ( int i = 0 ; i < children . size ( ) ; i + + ) {
children . write [ i ] . playback - > _travel_main ( StringName ( ) , children [ i ] . is_reset ) ; // Clear travel.
if ( children [ i ] . path . size ( ) ) {
children . write [ i ] . playback - > _start_main ( children [ i ] . path [ children [ i ] . path . size ( ) - 1 ] , children [ i ] . is_reset ) ;
}
}
}
return found_route ;
}
void AnimationNodeStateMachinePlayback : : _start ( AnimationNodeStateMachine * p_state_machine ) {
playing = true ;
_set_current ( p_state_machine , start_request ! = StringName ( ) ? start_request : p_state_machine - > start_node ) ;
teleport_request = true ;
stop_request = false ;
start_request = StringName ( ) ;
}
bool AnimationNodeStateMachinePlayback : : _travel ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , bool p_is_allow_transition_to_self , bool p_test_only ) {
return _make_travel_path ( p_tree , p_state_machine , p_is_allow_transition_to_self , path , p_test_only ) ;
}
String AnimationNodeStateMachinePlayback : : _validate_path ( AnimationNodeStateMachine * p_state_machine , const String & p_path ) {
if ( p_state_machine - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
return p_path ; // Grouped state machine doesn't allow validat-able request.
}
String target = p_path ;
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > find_node_by_path ( target ) ;
while ( anodesm . is_valid ( ) & & anodesm - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
Vector < int > indices = anodesm - > find_transition_from ( anodesm - > start_node ) ;
if ( indices . size ( ) ) {
target = target + " / " + anodesm - > get_transition_to ( indices [ 0 ] ) ; // Find next state of Start.
} else {
break ; // There is no transition in Start state of grouped state machine.
}
anodesm = p_state_machine - > find_node_by_path ( target ) ;
}
return target ;
}
bool AnimationNodeStateMachinePlayback : : _make_travel_path ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , bool p_is_allow_transition_to_self , Vector < StringName > & r_path , bool p_test_only ) {
StringName travel = travel_request ;
travel_request = StringName ( ) ;
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( ! playing ) {
_start ( p_state_machine ) ;
}
ERR_FAIL_COND_V ( ! p_state_machine - > states . has ( travel ) , false ) ;
ERR_FAIL_COND_V ( ! p_state_machine - > states . has ( current ) , false ) ;
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( current = = travel ) {
return ! p_is_allow_transition_to_self ;
2020-05-14 14:41:43 +00:00
}
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
Vector < StringName > new_path ;
2019-06-26 13:08:25 +00:00
Vector2 current_pos = p_state_machine - > states [ current ] . position ;
2023-02-18 02:02:28 +00:00
Vector2 target_pos = p_state_machine - > states [ travel ] . position ;
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
bool found_route = false ;
2022-05-13 13:04:37 +00:00
HashMap < StringName , AStarCost > cost_map ;
2018-08-20 16:38:18 +00:00
List < int > open_list ;
2023-02-18 02:02:28 +00:00
// Build open list.
2019-06-26 13:08:25 +00:00
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
2022-10-06 15:44:59 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > get_advance_mode ( ) = = AnimationNodeStateMachineTransition : : ADVANCE_MODE_DISABLED ) {
2018-12-16 14:43:20 +00:00
continue ;
}
2023-02-18 02:02:28 +00:00
if ( p_state_machine - > transitions [ i ] . from = = current ) {
2018-08-20 16:38:18 +00:00
open_list . push_back ( i ) ;
2023-02-18 02:02:28 +00:00
float cost = p_state_machine - > states [ p_state_machine - > transitions [ i ] . to ] . position . distance_to ( current_pos ) ;
2019-06-26 13:08:25 +00:00
cost * = p_state_machine - > transitions [ i ] . transition - > get_priority ( ) ;
2018-08-20 16:38:18 +00:00
AStarCost ap ;
ap . prev = current ;
ap . distance = cost ;
2023-02-18 02:02:28 +00:00
cost_map [ p_state_machine - > transitions [ i ] . to ] = ap ;
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( p_state_machine - > transitions [ i ] . to = = travel ) { // Prematurely found it! :D
found_route = true ;
break ;
2018-08-20 16:38:18 +00:00
}
}
}
2023-02-18 02:02:28 +00:00
// Begin astar.
2018-08-20 16:38:18 +00:00
while ( ! found_route ) {
if ( open_list . size ( ) = = 0 ) {
2023-02-18 02:02:28 +00:00
break ; // No path found.
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
// Find the last cost transition.
2020-04-01 23:20:12 +00:00
List < int > : : Element * least_cost_transition = nullptr ;
2018-08-20 16:38:18 +00:00
float least_cost = 1e20 ;
for ( List < int > : : Element * E = open_list . front ( ) ; E ; E = E - > next ( ) ) {
2023-02-18 02:02:28 +00:00
float cost = cost_map [ p_state_machine - > transitions [ E - > get ( ) ] . to ] . distance ;
cost + = p_state_machine - > states [ p_state_machine - > transitions [ E - > get ( ) ] . to ] . position . distance_to ( target_pos ) ;
2018-08-20 16:38:18 +00:00
if ( cost < least_cost ) {
least_cost_transition = E ;
2019-08-06 21:27:25 +00:00
least_cost = cost ;
2018-08-20 16:38:18 +00:00
}
}
2023-02-18 02:02:28 +00:00
StringName transition_prev = p_state_machine - > transitions [ least_cost_transition - > get ( ) ] . from ;
StringName transition = p_state_machine - > transitions [ least_cost_transition - > get ( ) ] . to ;
2018-08-20 16:38:18 +00:00
2019-06-26 13:08:25 +00:00
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
2022-10-06 15:44:59 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > get_advance_mode ( ) = = AnimationNodeStateMachineTransition : : ADVANCE_MODE_DISABLED ) {
2018-12-16 14:43:20 +00:00
continue ;
}
2023-02-18 02:02:28 +00:00
if ( p_state_machine - > transitions [ i ] . from ! = transition | | p_state_machine - > transitions [ i ] . to = = transition_prev ) {
continue ; // Not interested on those.
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
float distance = p_state_machine - > states [ p_state_machine - > transitions [ i ] . from ] . position . distance_to ( p_state_machine - > states [ p_state_machine - > transitions [ i ] . to ] . position ) ;
2019-06-26 13:08:25 +00:00
distance * = p_state_machine - > transitions [ i ] . transition - > get_priority ( ) ;
2023-02-18 02:02:28 +00:00
distance + = cost_map [ p_state_machine - > transitions [ i ] . from ] . distance ;
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( cost_map . has ( p_state_machine - > transitions [ i ] . to ) ) {
// Oh this was visited already, can we win the cost?
if ( distance < cost_map [ p_state_machine - > transitions [ i ] . to ] . distance ) {
cost_map [ p_state_machine - > transitions [ i ] . to ] . distance = distance ;
cost_map [ p_state_machine - > transitions [ i ] . to ] . prev = p_state_machine - > transitions [ i ] . from ;
2018-08-20 16:38:18 +00:00
}
} else {
2023-02-18 02:02:28 +00:00
// Add to open list.
2018-08-20 16:38:18 +00:00
AStarCost ac ;
2023-02-18 02:02:28 +00:00
ac . prev = p_state_machine - > transitions [ i ] . from ;
2018-08-20 16:38:18 +00:00
ac . distance = distance ;
2023-02-18 02:02:28 +00:00
cost_map [ p_state_machine - > transitions [ i ] . to ] = ac ;
2018-08-20 16:38:18 +00:00
open_list . push_back ( i ) ;
2023-02-18 02:02:28 +00:00
if ( p_state_machine - > transitions [ i ] . to = = travel ) {
2018-08-20 16:38:18 +00:00
found_route = true ;
break ;
}
}
}
if ( found_route ) {
break ;
}
open_list . erase ( least_cost_transition ) ;
}
2023-02-18 02:02:28 +00:00
// Check child grouped state machine.
if ( found_route ) {
// Make path.
StringName at = travel ;
while ( at ! = current ) {
new_path . push_back ( at ) ;
at = cost_map [ at ] . prev ;
}
new_path . reverse ( ) ;
// Check internal paths of child grouped state machine.
// For example:
// [current - End] - [Start - End] - [Start - End] - [Start - target]
String current_path = current ;
int len = new_path . size ( ) + 1 ;
for ( int i = 0 ; i < len ; i + + ) {
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > find_node_by_path ( current_path ) ;
if ( anodesm . is_valid ( ) & & anodesm - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
Ref < AnimationNodeStateMachinePlayback > playback = p_tree - > get ( base_path + current_path + " /playback " ) ;
ERR_FAIL_COND_V ( ! playback . is_valid ( ) , false ) ;
playback - > _set_base_path ( base_path + current_path + " / " ) ;
if ( p_test_only ) {
playback = playback - > duplicate ( ) ;
}
if ( i > 0 ) {
playback - > _start ( anodesm . ptr ( ) ) ;
}
if ( i > = new_path . size ( ) ) {
break ; // Tracing has been finished, needs to break.
}
playback - > _travel_main ( " End " ) ;
if ( ! playback - > _travel ( p_tree , anodesm . ptr ( ) , false , p_test_only ) ) {
found_route = false ;
break ;
}
}
if ( i > = new_path . size ( ) ) {
break ; // Tracing has been finished, needs to break.
}
current_path = new_path [ i ] ;
}
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
// Finally, rewrite path if route is found.
if ( found_route ) {
r_path = new_path ;
return true ;
} else {
return false ;
}
2018-08-20 16:38:18 +00:00
}
2023-07-20 15:34:06 +00:00
double AnimationNodeStateMachinePlayback : : process ( const String & p_base_path , AnimationNodeStateMachine * p_state_machine , const AnimationMixer : : PlaybackInfo p_playback_info , bool p_test_only ) {
double rem = _process ( p_base_path , p_state_machine , p_playback_info , p_test_only ) ;
2023-01-10 09:15:16 +00:00
start_request = StringName ( ) ;
next_request = false ;
stop_request = false ;
reset_request_on_teleport = false ;
return rem ;
}
2023-07-20 15:34:06 +00:00
double AnimationNodeStateMachinePlayback : : _process ( const String & p_base_path , AnimationNodeStateMachine * p_state_machine , const AnimationMixer : : PlaybackInfo p_playback_info , bool p_test_only ) {
2023-02-18 02:02:28 +00:00
_set_base_path ( p_base_path ) ;
2023-07-20 15:34:06 +00:00
AnimationTree * tree = p_state_machine - > process_state - > tree ;
double p_time = p_playback_info . time ;
bool p_seek = p_playback_info . seeked ;
bool p_is_external_seeking = p_playback_info . is_external_seeking ;
2022-08-03 00:09:33 +00:00
2023-02-18 02:02:28 +00:00
// Check seek to 0 (means reset) by parent AnimationNode.
if ( p_time = = 0 & & p_seek & & ! p_is_external_seeking ) {
if ( p_state_machine - > state_machine_type ! = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_NESTED | | is_end ( ) | | ! playing ) {
// Restart state machine.
if ( p_state_machine - > get_state_machine_type ( ) ! = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
path . clear ( ) ;
_clear_path_children ( tree , p_state_machine , p_test_only ) ;
2023-09-30 01:37:35 +00:00
_start ( p_state_machine ) ;
2023-02-18 02:02:28 +00:00
}
reset_request = true ;
2018-08-20 16:38:18 +00:00
} else {
2023-02-18 02:02:28 +00:00
// Reset current state.
reset_request = true ;
teleport_request = true ;
2018-08-20 16:38:18 +00:00
}
}
2023-02-18 02:02:28 +00:00
if ( stop_request ) {
start_request = StringName ( ) ;
travel_request = StringName ( ) ;
path . clear ( ) ;
2018-08-20 16:38:18 +00:00
playing = false ;
return 0 ;
}
2023-02-18 02:02:28 +00:00
if ( ! playing & & start_request ! = StringName ( ) & & travel_request ! = StringName ( ) ) {
return 0 ;
}
// Process start/travel request.
if ( start_request ! = StringName ( ) | | travel_request ! = StringName ( ) ) {
if ( p_state_machine - > get_state_machine_type ( ) ! = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
_clear_path_children ( tree , p_state_machine , p_test_only ) ;
}
}
2018-08-20 16:38:18 +00:00
if ( start_request ! = StringName ( ) ) {
2023-02-18 02:02:28 +00:00
path . clear ( ) ;
String start_target = _validate_path ( p_state_machine , start_request ) ;
Vector < String > start_path = String ( start_target ) . split ( " / " ) ;
start_request = start_path [ 0 ] ;
if ( start_path . size ( ) ) {
_start_children ( tree , p_state_machine , start_target , p_test_only ) ;
}
// Teleport to start.
2023-01-10 09:15:16 +00:00
if ( p_state_machine - > states . has ( start_request ) ) {
2023-02-18 02:02:28 +00:00
_start ( p_state_machine ) ;
2018-08-20 16:38:18 +00:00
} else {
2023-01-10 09:15:16 +00:00
StringName node = start_request ;
ERR_FAIL_V_MSG ( 0 , " No such node: ' " + node + " ' " ) ;
}
2023-02-18 02:02:28 +00:00
}
if ( travel_request ! = StringName ( ) ) {
// Fix path.
String travel_target = _validate_path ( p_state_machine , travel_request ) ;
Vector < String > travel_path = travel_target . split ( " / " ) ;
travel_request = travel_path [ 0 ] ;
StringName temp_travel_request = travel_request ; // For the case that can't travel.
// Process children.
Vector < StringName > new_path ;
bool can_travel = _make_travel_path ( tree , p_state_machine , travel_path . size ( ) < = 1 ? p_state_machine - > is_allow_transition_to_self ( ) : false , new_path , p_test_only ) ;
if ( travel_path . size ( ) ) {
if ( can_travel ) {
can_travel = _travel_children ( tree , p_state_machine , travel_target , p_state_machine - > is_allow_transition_to_self ( ) , travel_path [ 0 ] = = current , p_test_only ) ;
2021-01-05 14:56:06 +00:00
} else {
2023-02-18 02:02:28 +00:00
_start_children ( tree , p_state_machine , travel_target , p_test_only ) ;
2021-01-05 14:56:06 +00:00
}
2023-02-18 02:02:28 +00:00
}
// Process to travel.
if ( can_travel ) {
path = new_path ;
2023-01-10 09:15:16 +00:00
} else {
2023-02-18 02:02:28 +00:00
// Can't travel, then teleport.
if ( p_state_machine - > states . has ( temp_travel_request ) ) {
path . clear ( ) ;
if ( current ! = temp_travel_request | | reset_request_on_teleport ) {
_set_current ( p_state_machine , temp_travel_request ) ;
reset_request = reset_request_on_teleport ;
teleport_request = true ;
2023-01-10 09:15:16 +00:00
}
2023-02-18 02:02:28 +00:00
} else {
ERR_FAIL_V_MSG ( 0 , " No such node: ' " + temp_travel_request + " ' " ) ;
2023-01-10 09:15:16 +00:00
}
2018-08-20 16:38:18 +00:00
}
}
2023-07-20 15:34:06 +00:00
AnimationMixer : : PlaybackInfo pi = p_playback_info ;
2023-02-18 02:02:28 +00:00
if ( teleport_request ) {
teleport_request = false ;
// Clear fadeing on teleport.
fading_from = StringName ( ) ;
fading_pos = 0 ;
// Init current length.
pos_current = 0 ; // Overwritten suddenly in main process.
2023-07-20 15:34:06 +00:00
pi . time = 0 ;
pi . seeked = true ;
pi . is_external_seeking = false ;
pi . weight = 0 ;
len_current = p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , true ) ;
2023-02-18 02:02:28 +00:00
// Don't process first node if not necessary, insteads process next node.
_transition_to_next_recursive ( tree , p_state_machine , p_test_only ) ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
// Check current node existence.
2019-06-26 13:08:25 +00:00
if ( ! p_state_machine - > states . has ( current ) ) {
2023-02-18 02:02:28 +00:00
playing = false ; // Current does not exist.
_set_current ( p_state_machine , StringName ( ) ) ;
2018-08-20 16:38:18 +00:00
return 0 ;
}
2023-02-18 02:02:28 +00:00
// Special case for grouped state machine Start/End to make priority with parent blend (means don't treat Start and End states as RESET animations).
bool is_start_of_group = false ;
bool is_end_of_group = false ;
if ( ! p_state_machine - > are_ends_reset ( ) | | p_state_machine - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
is_start_of_group = fading_from = = p_state_machine - > start_node ;
is_end_of_group = current = = p_state_machine - > end_node ;
}
// Calc blend amount by cross-fade.
float fade_blend = 1.0 ;
if ( fading_time & & fading_from ! = StringName ( ) ) {
2019-06-26 13:08:25 +00:00
if ( ! p_state_machine - > states . has ( fading_from ) ) {
2018-08-20 16:38:18 +00:00
fading_from = StringName ( ) ;
} else {
if ( ! p_seek ) {
fading_pos + = p_time ;
}
fade_blend = MIN ( 1.0 , fading_pos / fading_time ) ;
}
}
2022-07-28 08:31:23 +00:00
if ( current_curve . is_valid ( ) ) {
2022-07-24 16:47:57 +00:00
fade_blend = current_curve - > sample ( fade_blend ) ;
2022-07-28 08:31:23 +00:00
}
2023-02-18 02:02:28 +00:00
fade_blend = Math : : is_zero_approx ( fade_blend ) ? CMP_EPSILON : fade_blend ;
if ( is_start_of_group ) {
fade_blend = 1.0 ;
} else if ( is_end_of_group ) {
fade_blend = 0.0 ;
}
2023-01-10 09:15:16 +00:00
2023-02-18 02:02:28 +00:00
// Main process.
double rem = 0.0 ;
2023-07-20 15:34:06 +00:00
pi = p_playback_info ;
pi . weight = fade_blend ;
2023-02-18 02:02:28 +00:00
if ( reset_request ) {
reset_request = false ;
2023-07-20 15:34:06 +00:00
pi . time = 0 ;
pi . seeked = true ;
len_current = p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , p_test_only ) ;
2023-02-18 02:02:28 +00:00
rem = len_current ;
} else {
2023-07-20 15:34:06 +00:00
rem = p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , p_test_only ) ; // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
2023-02-18 02:02:28 +00:00
}
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
// Cross-fade process.
2018-08-20 16:38:18 +00:00
if ( fading_from ! = StringName ( ) ) {
2022-12-25 23:29:13 +00:00
double fade_blend_inv = 1.0 - fade_blend ;
2023-02-18 02:02:28 +00:00
fade_blend_inv = Math : : is_zero_approx ( fade_blend_inv ) ? CMP_EPSILON : fade_blend_inv ;
if ( is_start_of_group ) {
fade_blend_inv = 0.0 ;
} else if ( is_end_of_group ) {
fade_blend_inv = 1.0 ;
}
2022-10-21 17:35:25 +00:00
float fading_from_rem = 0.0 ;
2023-07-20 15:34:06 +00:00
pi = p_playback_info ;
pi . weight = fade_blend_inv ;
2023-02-18 02:02:28 +00:00
if ( _reset_request_for_fading_from ) {
_reset_request_for_fading_from = false ;
2023-07-20 15:34:06 +00:00
pi . time = 0 ;
pi . seeked = true ;
2022-10-21 17:35:25 +00:00
}
2023-12-05 15:42:31 +00:00
fading_from_rem = p_state_machine - > blend_node ( p_state_machine - > states [ fading_from ] . node , fading_from , pi , AnimationNode : : FILTER_IGNORE , true , p_test_only ) ; // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
2022-10-21 17:35:25 +00:00
2023-02-18 02:02:28 +00:00
// Guess playback position.
if ( fading_from_rem > len_fade_from ) { /// Weird but ok.
len_fade_from = fading_from_rem ;
2022-10-21 17:35:25 +00:00
}
2023-02-18 02:02:28 +00:00
pos_fade_from = len_fade_from - fading_from_rem ;
if ( fading_pos > = fading_time ) {
fading_from = StringName ( ) ; // Finish fading.
2022-12-25 23:29:13 +00:00
}
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
// Guess playback position.
if ( rem > len_current ) { // Weird but ok.
2018-08-20 16:38:18 +00:00
len_current = rem ;
}
2023-02-18 02:02:28 +00:00
pos_current = len_current - rem ;
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
// Find next and see when to transition.
_transition_to_next_recursive ( tree , p_state_machine , p_test_only ) ;
2018-08-20 16:38:18 +00:00
2023-04-25 09:32:36 +00:00
// Predict remaining time.
2023-02-18 02:02:28 +00:00
double remain = rem ; // If we can't predict the end of state machine, the time remaining must be INFINITY.
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( p_state_machine - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_NESTED ) {
// There is no next transition.
if ( ! p_state_machine - > has_transition_from ( current ) ) {
if ( fading_from ! = StringName ( ) ) {
remain = MAX ( rem , fading_time - fading_pos ) ;
} else {
remain = rem ;
2018-12-16 14:43:20 +00:00
}
2023-02-18 02:02:28 +00:00
return remain ;
}
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
if ( current = = p_state_machine - > end_node ) {
if ( fading_from ! = StringName ( ) ) {
remain = MAX ( 0 , fading_time - fading_pos ) ;
} else {
remain = 0 ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
return remain ;
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
if ( ! is_end ( ) ) {
return HUGE_LENGTH ;
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
return remain ;
}
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachinePlayback : : _transition_to_next_recursive ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , bool p_test_only ) {
_reset_request_for_fading_from = false ;
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
bool is_state_changed = false ;
2018-12-16 14:43:20 +00:00
2023-07-20 15:34:06 +00:00
AnimationMixer : : PlaybackInfo pi ;
2023-02-18 02:02:28 +00:00
NextInfo next ;
2023-07-07 05:33:08 +00:00
Vector < StringName > transition_path ;
transition_path . push_back ( current ) ;
2023-02-18 02:02:28 +00:00
while ( true ) {
next = _find_next ( p_tree , p_state_machine ) ;
2023-07-07 05:33:08 +00:00
if ( transition_path . has ( next . node ) ) {
WARN_PRINT_ONCE_ED ( " AnimationNodeStateMachinePlayback: " + base_path + " playback aborts the transition by detecting one or more looped transitions in the same frame to prevent to infinity loop. You may need to check the transition settings. " ) ;
break ; // Maybe infinity loop, do nothing more.
2023-02-18 02:02:28 +00:00
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
if ( ! _can_transition_to_next ( p_tree , p_state_machine , next , p_test_only ) ) {
break ; // Finish transition.
}
2023-07-07 05:33:08 +00:00
transition_path . push_back ( next . node ) ;
2023-02-18 02:02:28 +00:00
is_state_changed = true ;
// Setting for fading.
if ( next . xfade ) {
// Time to fade.
fading_from = current ;
fading_time = next . xfade ;
fading_pos = 0 ;
} else {
if ( reset_request ) {
// There is no possibility of processing doubly. Now we can apply reset actually in here.
2023-07-20 15:34:06 +00:00
pi . time = 0 ;
pi . seeked = true ;
pi . is_external_seeking = false ;
pi . weight = 0 ;
p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , p_test_only ) ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
fading_from = StringName ( ) ;
fading_time = 0 ;
fading_pos = 0 ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
// If it came from path, remove path.
if ( path . size ( ) ) {
path . remove_at ( 0 ) ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
// Update current status.
_set_current ( p_state_machine , next . node ) ;
current_curve = next . curve ;
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
_reset_request_for_fading_from = reset_request ; // To avoid processing doubly, it must be reset in the fading process within _process().
reset_request = next . is_reset ;
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
pos_fade_from = pos_current ;
len_fade_from = len_current ;
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
if ( next . switch_mode = = AnimationNodeStateMachineTransition : : SWITCH_MODE_SYNC ) {
2023-07-20 15:34:06 +00:00
pi . time = MIN ( pos_current , len_current ) ;
pi . seeked = true ;
pi . is_external_seeking = false ;
pi . weight = 0 ;
2023-12-05 15:42:31 +00:00
p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , p_test_only ) ;
2023-02-18 02:02:28 +00:00
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
// Just get length to find next recursive.
double rem = 0.0 ;
2023-07-20 15:34:06 +00:00
pi . time = 0 ;
pi . is_external_seeking = false ;
pi . weight = 0 ;
2023-02-18 02:02:28 +00:00
if ( next . is_reset ) {
2023-07-20 15:34:06 +00:00
pi . seeked = true ;
len_current = p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , true ) ; // Just retrieve remain length, don't process.
2023-02-18 02:02:28 +00:00
rem = len_current ;
} else {
2023-07-20 15:34:06 +00:00
pi . seeked = false ;
rem = p_state_machine - > blend_node ( p_state_machine - > states [ current ] . node , current , pi , AnimationNode : : FILTER_IGNORE , true , true ) ; // Just retrieve remain length, don't process.
2023-02-18 02:02:28 +00:00
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
// Guess playback position.
if ( rem > len_current ) { // Weird but ok.
len_current = rem ;
}
pos_current = len_current - rem ;
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
// Fading must be processed.
if ( fading_time ) {
break ;
2018-12-16 14:43:20 +00:00
}
}
2023-02-18 02:02:28 +00:00
return is_state_changed ;
}
bool AnimationNodeStateMachinePlayback : : _can_transition_to_next ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , NextInfo p_next , bool p_test_only ) {
if ( p_next . node = = StringName ( ) ) {
return false ;
}
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( next_request ) {
// Process request only once.
next_request = false ;
// Next request must be applied to only deepest state machine.
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > find_node_by_path ( current ) ;
if ( anodesm . is_valid ( ) & & anodesm - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
Ref < AnimationNodeStateMachinePlayback > playback = p_tree - > get ( base_path + current + " /playback " ) ;
ERR_FAIL_COND_V ( ! playback . is_valid ( ) , false ) ;
playback - > _set_base_path ( base_path + current + " / " ) ;
if ( p_test_only ) {
playback = playback - > duplicate ( ) ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
playback - > _next_main ( ) ;
// Then, fadeing should be end.
fading_from = StringName ( ) ;
fading_pos = 0 ;
2019-03-17 13:12:27 +00:00
} else {
2023-02-18 02:02:28 +00:00
return true ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
}
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
if ( fading_from ! = StringName ( ) ) {
return false ;
}
2022-08-03 00:09:33 +00:00
2023-02-18 02:02:28 +00:00
if ( current ! = p_state_machine - > start_node & & p_next . switch_mode = = AnimationNodeStateMachineTransition : : SWITCH_MODE_AT_END ) {
return pos_current > = len_current - p_next . xfade ;
}
return true ;
}
2022-08-03 00:09:33 +00:00
2023-02-18 02:02:28 +00:00
Ref < AnimationNodeStateMachineTransition > AnimationNodeStateMachinePlayback : : _check_group_transition ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine , const AnimationNodeStateMachine : : Transition & p_transition , Ref < AnimationNodeStateMachine > & r_state_machine , bool & r_bypass ) const {
Ref < AnimationNodeStateMachineTransition > temp_transition ;
Ref < AnimationNodeStateMachinePlayback > parent_playback ;
if ( r_state_machine - > get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
if ( p_transition . from = = " Start " ) {
parent_playback = _get_parent_playback ( p_tree ) ;
if ( parent_playback . is_valid ( ) ) {
r_bypass = true ;
temp_transition = parent_playback - > _get_group_start_transition ( ) ;
2023-01-10 09:15:16 +00:00
}
2023-02-18 02:02:28 +00:00
} else if ( p_transition . to = = " End " ) {
parent_playback = _get_parent_playback ( p_tree ) ;
if ( parent_playback . is_valid ( ) ) {
temp_transition = parent_playback - > _get_group_end_transition ( ) ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
}
if ( temp_transition . is_valid ( ) ) {
r_state_machine = _get_parent_state_machine ( p_tree ) ;
return temp_transition ;
2018-08-20 16:38:18 +00:00
}
}
2023-02-18 02:02:28 +00:00
return p_transition . transition ;
}
2018-08-20 16:38:18 +00:00
2023-02-18 02:02:28 +00:00
AnimationNodeStateMachinePlayback : : NextInfo AnimationNodeStateMachinePlayback : : _find_next ( AnimationTree * p_tree , AnimationNodeStateMachine * p_state_machine ) const {
NextInfo next ;
if ( path . size ( ) ) {
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
Ref < AnimationNodeStateMachine > anodesm = p_state_machine ;
bool bypass = false ;
Ref < AnimationNodeStateMachineTransition > ref_transition = _check_group_transition ( p_tree , p_state_machine , p_state_machine - > transitions [ i ] , anodesm , bypass ) ;
if ( ref_transition - > get_advance_mode ( ) = = AnimationNodeStateMachineTransition : : ADVANCE_MODE_DISABLED ) {
continue ;
}
if ( p_state_machine - > transitions [ i ] . from = = current & & p_state_machine - > transitions [ i ] . to = = path [ 0 ] ) {
next . node = path [ 0 ] ;
next . xfade = ref_transition - > get_xfade_time ( ) ;
next . curve = ref_transition - > get_xfade_curve ( ) ;
next . switch_mode = ref_transition - > get_switch_mode ( ) ;
next . is_reset = ref_transition - > is_reset ( ) ;
}
}
2018-12-16 14:43:20 +00:00
} else {
2023-02-18 02:02:28 +00:00
int auto_advance_to = - 1 ;
float priority_best = 1e20 ;
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
Ref < AnimationNodeStateMachine > anodesm = p_state_machine ;
bool bypass = false ;
Ref < AnimationNodeStateMachineTransition > ref_transition = _check_group_transition ( p_tree , p_state_machine , p_state_machine - > transitions [ i ] , anodesm , bypass ) ;
if ( ref_transition - > get_advance_mode ( ) = = AnimationNodeStateMachineTransition : : ADVANCE_MODE_DISABLED ) {
continue ;
}
if ( p_state_machine - > transitions [ i ] . from = = current & & ( _check_advance_condition ( anodesm , ref_transition ) | | bypass ) ) {
if ( ref_transition - > get_priority ( ) < = priority_best ) {
priority_best = ref_transition - > get_priority ( ) ;
auto_advance_to = i ;
}
2022-08-09 19:55:07 +00:00
}
2018-12-16 14:43:20 +00:00
}
2023-02-18 02:02:28 +00:00
if ( auto_advance_to ! = - 1 ) {
next . node = p_state_machine - > transitions [ auto_advance_to ] . to ;
Ref < AnimationNodeStateMachine > anodesm = p_state_machine ;
bool bypass = false ;
Ref < AnimationNodeStateMachineTransition > ref_transition = _check_group_transition ( p_tree , p_state_machine , p_state_machine - > transitions [ auto_advance_to ] , anodesm , bypass ) ;
next . xfade = ref_transition - > get_xfade_time ( ) ;
next . curve = ref_transition - > get_xfade_curve ( ) ;
next . switch_mode = ref_transition - > get_switch_mode ( ) ;
next . is_reset = ref_transition - > is_reset ( ) ;
}
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
return next ;
2018-08-20 16:38:18 +00:00
}
2018-12-16 14:43:20 +00:00
bool AnimationNodeStateMachinePlayback : : _check_advance_condition ( const Ref < AnimationNodeStateMachine > state_machine , const Ref < AnimationNodeStateMachineTransition > transition ) const {
2022-10-06 15:44:59 +00:00
if ( transition - > get_advance_mode ( ) ! = AnimationNodeStateMachineTransition : : ADVANCE_MODE_AUTO ) {
return false ;
2018-12-16 14:43:20 +00:00
}
StringName advance_condition_name = transition - > get_advance_condition_name ( ) ;
2022-10-06 15:44:59 +00:00
if ( advance_condition_name ! = StringName ( ) & & ! bool ( state_machine - > get_parameter ( advance_condition_name ) ) ) {
return false ;
2018-12-16 14:43:20 +00:00
}
2022-04-20 10:36:54 +00:00
if ( transition - > expression . is_valid ( ) ) {
AnimationTree * tree_base = state_machine - > get_animation_tree ( ) ;
2023-09-09 15:52:40 +00:00
ERR_FAIL_NULL_V ( tree_base , false ) ;
2022-04-20 10:36:54 +00:00
2022-10-07 09:13:43 +00:00
NodePath advance_expression_base_node_path = tree_base - > get_advance_expression_base_node ( ) ;
Node * expression_base = tree_base - > get_node_or_null ( advance_expression_base_node_path ) ;
2022-04-20 10:36:54 +00:00
if ( expression_base ) {
Ref < Expression > exp = transition - > expression ;
2022-07-20 11:44:08 +00:00
bool ret = exp - > execute ( Array ( ) , expression_base , false , Engine : : get_singleton ( ) - > is_editor_hint ( ) ) ; // Avoids allowing the user to crash the system with an expression by only allowing const calls.
2022-10-06 15:44:59 +00:00
if ( exp - > has_execute_failed ( ) | | ! ret ) {
return false ;
2022-04-20 10:36:54 +00:00
}
2022-10-06 15:44:59 +00:00
} else {
WARN_PRINT_ONCE ( " Animation transition has a valid expression, but no expression base node was set on its AnimationTree. " ) ;
2022-04-20 10:36:54 +00:00
}
}
2022-10-06 15:44:59 +00:00
return true ;
2018-12-16 14:43:20 +00:00
}
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachinePlayback : : clear_path ( ) {
path . clear ( ) ;
}
void AnimationNodeStateMachinePlayback : : push_path ( const StringName & p_state ) {
path . push_back ( p_state ) ;
}
void AnimationNodeStateMachinePlayback : : _set_base_path ( const String & p_base_path ) {
base_path = p_base_path ;
}
Ref < AnimationNodeStateMachinePlayback > AnimationNodeStateMachinePlayback : : _get_parent_playback ( AnimationTree * p_tree ) const {
if ( base_path . is_empty ( ) ) {
return Ref < AnimationNodeStateMachinePlayback > ( ) ;
}
Vector < String > split = base_path . split ( " / " ) ;
ERR_FAIL_COND_V_MSG ( split . size ( ) < 2 , Ref < AnimationNodeStateMachinePlayback > ( ) , " Path is too short. " ) ;
StringName self_path = split [ split . size ( ) - 2 ] ;
split . remove_at ( split . size ( ) - 2 ) ;
String playback_path = String ( " / " ) . join ( split ) + " playback " ;
Ref < AnimationNodeStateMachinePlayback > playback = p_tree - > get ( playback_path ) ;
if ( ! playback . is_valid ( ) ) {
ERR_PRINT_ONCE ( " Can't get parent AnimationNodeStateMachinePlayback with path: " + playback_path + " . Maybe there is no Root/Nested AnimationNodeStateMachine in the parent of the Grouped AnimationNodeStateMachine. " ) ;
return Ref < AnimationNodeStateMachinePlayback > ( ) ;
}
if ( playback - > get_current_node ( ) ! = self_path ) {
return Ref < AnimationNodeStateMachinePlayback > ( ) ;
}
return playback ;
}
Ref < AnimationNodeStateMachine > AnimationNodeStateMachinePlayback : : _get_parent_state_machine ( AnimationTree * p_tree ) const {
if ( base_path . is_empty ( ) ) {
return Ref < AnimationNodeStateMachine > ( ) ;
}
Vector < String > split = base_path . split ( " / " ) ;
ERR_FAIL_COND_V_MSG ( split . size ( ) < 3 , Ref < AnimationNodeStateMachine > ( ) , " Path is too short. " ) ;
split = split . slice ( 1 , split . size ( ) - 2 ) ;
2023-07-20 15:34:06 +00:00
Ref < AnimationNode > root = p_tree - > get_root_animation_node ( ) ;
2023-02-18 02:02:28 +00:00
ERR_FAIL_COND_V_MSG ( root . is_null ( ) , Ref < AnimationNodeStateMachine > ( ) , " There is no root AnimationNode in AnimationTree: " + String ( p_tree - > get_name ( ) ) ) ;
String anodesm_path = String ( " / " ) . join ( split ) ;
Ref < AnimationNodeStateMachine > anodesm = ! anodesm_path . size ( ) ? root : root - > find_node_by_path ( anodesm_path ) ;
ERR_FAIL_COND_V_MSG ( anodesm . is_null ( ) , Ref < AnimationNodeStateMachine > ( ) , " Can't get state machine with path: " + anodesm_path ) ;
return anodesm ;
}
Ref < AnimationNodeStateMachineTransition > AnimationNodeStateMachinePlayback : : _get_group_start_transition ( ) const {
ERR_FAIL_COND_V_MSG ( group_start_transition . is_null ( ) , Ref < AnimationNodeStateMachineTransition > ( ) , " Group start transition is null. " ) ;
return group_start_transition ;
}
Ref < AnimationNodeStateMachineTransition > AnimationNodeStateMachinePlayback : : _get_group_end_transition ( ) const {
ERR_FAIL_COND_V_MSG ( group_end_transition . is_null ( ) , Ref < AnimationNodeStateMachineTransition > ( ) , " Group end transition is null. " ) ;
return group_end_transition ;
}
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachinePlayback : : _bind_methods ( ) {
2023-01-10 09:15:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " travel " , " to_node " , " reset_on_teleport " ) , & AnimationNodeStateMachinePlayback : : travel , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " start " , " node " , " reset " ) , & AnimationNodeStateMachinePlayback : : start , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " next " ) , & AnimationNodeStateMachinePlayback : : next ) ;
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " stop " ) , & AnimationNodeStateMachinePlayback : : stop ) ;
ClassDB : : bind_method ( D_METHOD ( " is_playing " ) , & AnimationNodeStateMachinePlayback : : is_playing ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_node " ) , & AnimationNodeStateMachinePlayback : : get_current_node ) ;
2020-09-19 02:59:25 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_current_play_position " ) , & AnimationNodeStateMachinePlayback : : get_current_play_pos ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_length " ) , & AnimationNodeStateMachinePlayback : : get_current_length ) ;
2023-01-22 06:50:53 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_fading_from_node " ) , & AnimationNodeStateMachinePlayback : : get_fading_from_node ) ;
2023-04-24 22:21:32 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_travel_path " ) , & AnimationNodeStateMachinePlayback : : _get_travel_path ) ;
2018-08-20 16:38:18 +00:00
}
AnimationNodeStateMachinePlayback : : AnimationNodeStateMachinePlayback ( ) {
2023-02-18 02:02:28 +00:00
set_local_to_scene ( true ) ; // Only one per instantiated scene.
default_transition . instantiate ( ) ;
default_transition - > set_xfade_time ( 0 ) ;
default_transition - > set_reset ( true ) ;
default_transition - > set_advance_mode ( AnimationNodeStateMachineTransition : : ADVANCE_MODE_AUTO ) ;
default_transition - > set_switch_mode ( AnimationNodeStateMachineTransition : : SWITCH_MODE_IMMEDIATE ) ;
2018-08-20 16:38:18 +00:00
}
2018-06-25 19:21:57 +00:00
///////////////////////////////////////////////////////
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachine : : get_parameter_list ( List < PropertyInfo > * r_list ) const {
2023-02-18 02:02:28 +00:00
r_list - > push_back ( PropertyInfo ( Variant : : OBJECT , playback , PROPERTY_HINT_RESOURCE_TYPE , " AnimationNodeStateMachinePlayback " , PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ALWAYS_DUPLICATE ) ) ; // Don't store this object in .tres, it always needs to be made as unique object.
2018-08-20 16:38:18 +00:00
List < StringName > advance_conditions ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
StringName ac = transitions [ i ] . transition - > get_advance_condition_name ( ) ;
2020-04-01 23:20:12 +00:00
if ( ac ! = StringName ( ) & & advance_conditions . find ( ac ) = = nullptr ) {
2018-08-20 16:38:18 +00:00
advance_conditions . push_back ( ac ) ;
}
}
advance_conditions . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & E : advance_conditions ) {
2021-07-16 03:45:57 +00:00
r_list - > push_back ( PropertyInfo ( Variant : : BOOL , E ) ) ;
2018-08-20 16:38:18 +00:00
}
}
Variant AnimationNodeStateMachine : : get_parameter_default_value ( const StringName & p_parameter ) const {
if ( p_parameter = = playback ) {
Ref < AnimationNodeStateMachinePlayback > p ;
2021-06-17 22:03:09 +00:00
p . instantiate ( ) ;
2018-08-20 16:38:18 +00:00
return p ;
} else {
2023-02-18 02:02:28 +00:00
return false ; // Advance condition.
}
}
bool AnimationNodeStateMachine : : is_parameter_read_only ( const StringName & p_parameter ) const {
if ( p_parameter = = playback ) {
return true ;
2018-08-20 16:38:18 +00:00
}
2023-02-18 02:02:28 +00:00
return false ;
2018-08-20 16:38:18 +00:00
}
void AnimationNodeStateMachine : : add_node ( const StringName & p_name , Ref < AnimationNode > p_node , const Vector2 & p_position ) {
2018-06-25 19:21:57 +00:00
ERR_FAIL_COND ( states . has ( p_name ) ) ;
ERR_FAIL_COND ( p_node . is_null ( ) ) ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( String ( p_name ) . contains ( " / " ) ) ;
2018-06-25 19:21:57 +00:00
2022-09-29 09:53:28 +00:00
State state_new ;
state_new . node = p_node ;
state_new . position = p_position ;
2018-08-20 16:38:18 +00:00
2022-09-29 09:53:28 +00:00
states [ p_name ] = state_new ;
2018-06-25 19:21:57 +00:00
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2018-08-20 16:38:18 +00:00
2022-07-28 20:56:41 +00:00
p_node - > connect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) , CONNECT_REFERENCE_COUNTED ) ;
2023-02-04 15:29:34 +00:00
p_node - > connect ( " animation_node_renamed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_renamed ) , CONNECT_REFERENCE_COUNTED ) ;
p_node - > connect ( " animation_node_removed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_removed ) , CONNECT_REFERENCE_COUNTED ) ;
2018-06-25 19:21:57 +00:00
}
2020-03-16 16:42:29 +00:00
void AnimationNodeStateMachine : : replace_node ( const StringName & p_name , Ref < AnimationNode > p_node ) {
ERR_FAIL_COND ( states . has ( p_name ) = = false ) ;
ERR_FAIL_COND ( p_node . is_null ( ) ) ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( String ( p_name ) . contains ( " / " ) ) ;
2020-03-16 16:42:29 +00:00
{
Ref < AnimationNode > node = states [ p_name ] . node ;
if ( node . is_valid ( ) ) {
2020-12-05 21:01:27 +00:00
node - > disconnect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) ) ;
2023-02-04 15:29:34 +00:00
node - > disconnect ( " animation_node_renamed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_renamed ) ) ;
node - > disconnect ( " animation_node_removed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_removed ) ) ;
2020-03-16 16:42:29 +00:00
}
}
states [ p_name ] . node = p_node ;
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2020-03-16 16:42:29 +00:00
2022-07-28 20:56:41 +00:00
p_node - > connect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) , CONNECT_REFERENCE_COUNTED ) ;
2023-02-04 15:29:34 +00:00
p_node - > connect ( " animation_node_renamed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_renamed ) , CONNECT_REFERENCE_COUNTED ) ;
p_node - > connect ( " animation_node_removed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_removed ) , CONNECT_REFERENCE_COUNTED ) ;
2020-03-16 16:42:29 +00:00
}
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachine : : set_state_machine_type ( StateMachineType p_state_machine_type ) {
state_machine_type = p_state_machine_type ;
emit_changed ( ) ;
emit_signal ( SNAME ( " tree_changed " ) ) ;
notify_property_list_changed ( ) ;
}
AnimationNodeStateMachine : : StateMachineType AnimationNodeStateMachine : : get_state_machine_type ( ) const {
return state_machine_type ;
}
2023-01-31 12:17:29 +00:00
void AnimationNodeStateMachine : : set_allow_transition_to_self ( bool p_enable ) {
allow_transition_to_self = p_enable ;
}
bool AnimationNodeStateMachine : : is_allow_transition_to_self ( ) const {
return allow_transition_to_self ;
}
2023-02-18 02:02:28 +00:00
void AnimationNodeStateMachine : : set_reset_ends ( bool p_enable ) {
reset_ends = p_enable ;
}
bool AnimationNodeStateMachine : : are_ends_reset ( ) const {
return reset_ends ;
}
2018-12-16 14:43:20 +00:00
bool AnimationNodeStateMachine : : can_edit_node ( const StringName & p_name ) const {
if ( states . has ( p_name ) ) {
return ! ( states [ p_name ] . node - > is_class ( " AnimationNodeStartState " ) | | states [ p_name ] . node - > is_class ( " AnimationNodeEndState " ) ) ;
}
return true ;
}
2018-06-25 19:21:57 +00:00
Ref < AnimationNode > AnimationNodeStateMachine : : get_node ( const StringName & p_name ) const {
2023-02-18 02:02:28 +00:00
ERR_FAIL_COND_V_EDMSG ( ! states . has ( p_name ) , Ref < AnimationNode > ( ) , String ( p_name ) + " is not found current state. " ) ;
2018-06-25 19:21:57 +00:00
2018-08-20 16:38:18 +00:00
return states [ p_name ] . node ;
2018-06-25 19:21:57 +00:00
}
StringName AnimationNodeStateMachine : : get_node_name ( const Ref < AnimationNode > & p_node ) const {
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
if ( E . value . node = = p_node ) {
return E . key ;
2018-06-25 19:21:57 +00:00
}
}
2018-08-20 16:38:18 +00:00
ERR_FAIL_V ( StringName ( ) ) ;
}
void AnimationNodeStateMachine : : get_child_nodes ( List < ChildNode > * r_child_nodes ) {
Vector < StringName > nodes ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
nodes . push_back ( E . key ) ;
2018-08-20 16:38:18 +00:00
}
nodes . sort_custom < StringName : : AlphCompare > ( ) ;
for ( int i = 0 ; i < nodes . size ( ) ; i + + ) {
ChildNode cn ;
cn . name = nodes [ i ] ;
cn . node = states [ cn . name ] . node ;
r_child_nodes - > push_back ( cn ) ;
}
2018-06-25 19:21:57 +00:00
}
bool AnimationNodeStateMachine : : has_node ( const StringName & p_name ) const {
return states . has ( p_name ) ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : remove_node ( const StringName & p_name ) {
ERR_FAIL_COND ( ! states . has ( p_name ) ) ;
2018-12-16 14:43:20 +00:00
if ( ! can_edit_node ( p_name ) ) {
return ;
2018-06-25 19:21:57 +00:00
}
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2023-02-18 02:02:28 +00:00
if ( transitions [ i ] . from = = p_name | | transitions [ i ] . to = = p_name ) {
2018-12-16 14:43:20 +00:00
remove_transition_by_index ( i ) ;
2018-06-25 19:21:57 +00:00
i - - ;
}
}
2018-12-16 14:43:20 +00:00
{
Ref < AnimationNode > node = states [ p_name ] . node ;
ERR_FAIL_COND ( node . is_null ( ) ) ;
node - > disconnect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) ) ;
2023-02-04 15:29:34 +00:00
node - > disconnect ( " animation_node_renamed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_renamed ) ) ;
node - > disconnect ( " animation_node_removed " , callable_mp ( this , & AnimationNodeStateMachine : : _animation_node_removed ) ) ;
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
states . erase ( p_name ) ;
2018-08-20 16:38:18 +00:00
2023-02-04 15:29:34 +00:00
emit_signal ( SNAME ( " animation_node_removed " ) , get_instance_id ( ) , p_name ) ;
2018-06-25 19:21:57 +00:00
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2018-06-25 19:21:57 +00:00
}
void AnimationNodeStateMachine : : rename_node ( const StringName & p_name , const StringName & p_new_name ) {
ERR_FAIL_COND ( ! states . has ( p_name ) ) ;
ERR_FAIL_COND ( states . has ( p_new_name ) ) ;
2018-12-16 14:43:20 +00:00
ERR_FAIL_COND ( ! can_edit_node ( p_name ) ) ;
2018-06-25 19:21:57 +00:00
states [ p_new_name ] = states [ p_name ] ;
states . erase ( p_name ) ;
2022-07-31 21:47:19 +00:00
_rename_transitions ( p_name , p_new_name ) ;
2018-06-25 19:21:57 +00:00
2023-02-04 15:29:34 +00:00
emit_signal ( SNAME ( " animation_node_renamed " ) , get_instance_id ( ) , p_name , p_new_name ) ;
2022-12-20 21:21:17 +00:00
emit_changed ( ) ;
emit_signal ( SNAME ( " tree_changed " ) ) ;
2018-12-16 14:43:20 +00:00
}
2018-06-25 19:21:57 +00:00
2022-07-31 21:47:19 +00:00
void AnimationNodeStateMachine : : _rename_transitions ( const StringName & p_name , const StringName & p_new_name ) {
2018-12-16 14:43:20 +00:00
if ( updating_transitions ) {
return ;
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
updating_transitions = true ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . from = = p_name ) {
transitions . write [ i ] . from = p_new_name ;
}
if ( transitions [ i ] . to = = p_name ) {
transitions . write [ i ] . to = p_new_name ;
}
}
2022-12-20 21:21:17 +00:00
updating_transitions = false ;
2018-06-25 19:21:57 +00:00
}
void AnimationNodeStateMachine : : get_node_list ( List < StringName > * r_nodes ) const {
List < StringName > nodes ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
nodes . push_back ( E . key ) ;
2018-06-25 19:21:57 +00:00
}
nodes . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & E : nodes ) {
2021-07-16 03:45:57 +00:00
r_nodes - > push_back ( E ) ;
2018-06-25 19:21:57 +00:00
}
}
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachine : : has_transition ( const StringName & p_from , const StringName & p_to ) const {
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . from = = p_from & & transitions [ i ] . to = = p_to ) {
return true ;
}
}
return false ;
2018-12-16 14:43:20 +00:00
}
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachine : : has_transition_from ( const StringName & p_from ) const {
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . from = = p_from ) {
return true ;
}
}
return false ;
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachine : : has_transition_to ( const StringName & p_to ) const {
2018-06-25 19:21:57 +00:00
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2023-02-18 02:02:28 +00:00
if ( transitions [ i ] . to = = p_to ) {
2018-06-25 19:21:57 +00:00
return true ;
2020-05-14 14:41:43 +00:00
}
2018-06-25 19:21:57 +00:00
}
return false ;
}
int AnimationNodeStateMachine : : find_transition ( const StringName & p_from , const StringName & p_to ) const {
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2023-02-18 02:02:28 +00:00
if ( transitions [ i ] . from = = p_from & & transitions [ i ] . to = = p_to ) {
2018-06-25 19:21:57 +00:00
return i ;
2020-05-14 14:41:43 +00:00
}
2018-06-25 19:21:57 +00:00
}
return - 1 ;
}
2023-02-18 02:02:28 +00:00
Vector < int > AnimationNodeStateMachine : : find_transition_from ( const StringName & p_from ) const {
Vector < int > ret ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . from = = p_from ) {
ret . push_back ( i ) ;
2018-12-16 14:43:20 +00:00
}
}
2023-02-18 02:02:28 +00:00
return ret ;
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
Vector < int > AnimationNodeStateMachine : : find_transition_to ( const StringName & p_to ) const {
Vector < int > ret ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . to = = p_to ) {
ret . push_back ( i ) ;
2018-12-16 14:43:20 +00:00
}
2023-02-18 02:02:28 +00:00
}
return ret ;
}
2018-12-16 14:43:20 +00:00
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachine : : _can_connect ( const StringName & p_name ) {
if ( states . has ( p_name ) ) {
2018-12-16 14:43:20 +00:00
return true ;
}
2022-09-29 09:53:28 +00:00
String node_name = p_name ;
Vector < String > path = node_name . split ( " / " ) ;
2018-12-16 14:43:20 +00:00
if ( path . size ( ) < 2 ) {
return false ;
}
return false ;
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : add_transition ( const StringName & p_from , const StringName & p_to , const Ref < AnimationNodeStateMachineTransition > & p_transition ) {
2018-12-16 14:43:20 +00:00
if ( updating_transitions ) {
return ;
}
2023-02-18 02:02:28 +00:00
ERR_FAIL_COND ( p_from = = end_node | | p_to = = start_node ) ;
ERR_FAIL_COND ( p_from = = p_to ) ;
ERR_FAIL_COND ( ! _can_connect ( p_from ) ) ;
ERR_FAIL_COND ( ! _can_connect ( p_to ) ) ;
2018-06-25 19:21:57 +00:00
ERR_FAIL_COND ( p_transition . is_null ( ) ) ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2023-02-18 02:02:28 +00:00
ERR_FAIL_COND ( transitions [ i ] . from = = p_from & & transitions [ i ] . to = = p_to ) ;
2018-12-16 14:43:20 +00:00
}
updating_transitions = true ;
2018-06-25 19:21:57 +00:00
Transition tr ;
2023-02-18 02:02:28 +00:00
tr . from = p_from ;
tr . to = p_to ;
2018-06-25 19:21:57 +00:00
tr . transition = p_transition ;
2022-07-28 20:56:41 +00:00
tr . transition - > connect ( " advance_condition_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) , CONNECT_REFERENCE_COUNTED ) ;
2018-08-20 16:38:18 +00:00
2018-06-25 19:21:57 +00:00
transitions . push_back ( tr ) ;
2018-12-16 14:43:20 +00:00
updating_transitions = false ;
2018-06-25 19:21:57 +00:00
}
Ref < AnimationNodeStateMachineTransition > AnimationNodeStateMachine : : get_transition ( int p_transition ) const {
ERR_FAIL_INDEX_V ( p_transition , transitions . size ( ) , Ref < AnimationNodeStateMachineTransition > ( ) ) ;
return transitions [ p_transition ] . transition ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
StringName AnimationNodeStateMachine : : get_transition_from ( int p_transition ) const {
ERR_FAIL_INDEX_V ( p_transition , transitions . size ( ) , StringName ( ) ) ;
return transitions [ p_transition ] . from ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
StringName AnimationNodeStateMachine : : get_transition_to ( int p_transition ) const {
ERR_FAIL_INDEX_V ( p_transition , transitions . size ( ) , StringName ( ) ) ;
return transitions [ p_transition ] . to ;
}
2023-02-18 02:02:28 +00:00
bool AnimationNodeStateMachine : : is_transition_across_group ( int p_transition ) const {
ERR_FAIL_INDEX_V ( p_transition , transitions . size ( ) , false ) ;
if ( get_state_machine_type ( ) = = AnimationNodeStateMachine : : STATE_MACHINE_TYPE_GROUPED ) {
if ( transitions [ p_transition ] . from = = " Start " | | transitions [ p_transition ] . to = = " End " ) {
return true ;
}
}
return false ;
}
2018-06-25 19:21:57 +00:00
int AnimationNodeStateMachine : : get_transition_count ( ) const {
return transitions . size ( ) ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : remove_transition ( const StringName & p_from , const StringName & p_to ) {
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2023-02-18 02:02:28 +00:00
if ( transitions [ i ] . from = = p_from & & transitions [ i ] . to = = p_to ) {
2018-12-16 14:43:20 +00:00
remove_transition_by_index ( i ) ;
2018-06-25 19:21:57 +00:00
return ;
}
}
}
2018-12-16 14:43:20 +00:00
void AnimationNodeStateMachine : : remove_transition_by_index ( const int p_transition ) {
2018-08-20 16:38:18 +00:00
ERR_FAIL_INDEX ( p_transition , transitions . size ( ) ) ;
2018-12-16 14:43:20 +00:00
Transition tr = transitions [ p_transition ] ;
2020-02-21 17:28:45 +00:00
transitions . write [ p_transition ] . transition - > disconnect ( " advance_condition_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) ) ;
2021-07-03 22:17:03 +00:00
transitions . remove_at ( p_transition ) ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
Vector < String > path_from = String ( tr . from ) . split ( " / " ) ;
Vector < String > path_to = String ( tr . to ) . split ( " / " ) ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
List < Vector < String > > paths ;
paths . push_back ( path_from ) ;
paths . push_back ( path_to ) ;
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
void AnimationNodeStateMachine : : _remove_transition ( const Ref < AnimationNodeStateMachineTransition > p_transition ) {
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . transition = = p_transition ) {
remove_transition_by_index ( i ) ;
return ;
}
}
2018-06-25 19:21:57 +00:00
}
void AnimationNodeStateMachine : : set_graph_offset ( const Vector2 & p_offset ) {
graph_offset = p_offset ;
}
Vector2 AnimationNodeStateMachine : : get_graph_offset ( ) const {
return graph_offset ;
}
2023-07-20 15:34:06 +00:00
double AnimationNodeStateMachine : : _process ( const AnimationMixer : : PlaybackInfo p_playback_info , bool p_test_only ) {
2022-09-29 09:53:28 +00:00
Ref < AnimationNodeStateMachinePlayback > playback_new = get_parameter ( playback ) ;
ERR_FAIL_COND_V ( playback_new . is_null ( ) , 0.0 ) ;
2023-02-18 02:02:28 +00:00
playback_new - > _set_grouped ( state_machine_type = = STATE_MACHINE_TYPE_GROUPED ) ;
if ( p_test_only ) {
playback_new = playback_new - > duplicate ( ) ; // Don't process original when testing.
}
2023-07-20 15:34:06 +00:00
return playback_new - > process ( node_state . base_path , this , p_playback_info , p_test_only ) ;
2018-06-25 19:21:57 +00:00
}
String AnimationNodeStateMachine : : get_caption ( ) const {
return " StateMachine " ;
}
2023-02-18 02:02:28 +00:00
Ref < AnimationNode > AnimationNodeStateMachine : : get_child_by_name ( const StringName & p_name ) const {
2018-08-20 16:38:18 +00:00
return get_node ( p_name ) ;
2018-06-25 19:21:57 +00:00
}
bool AnimationNodeStateMachine : : _set ( const StringName & p_name , const Variant & p_value ) {
2022-09-29 09:53:28 +00:00
String prop_name = p_name ;
if ( prop_name . begins_with ( " states/ " ) ) {
String node_name = prop_name . get_slicec ( ' / ' , 1 ) ;
String what = prop_name . get_slicec ( ' / ' , 2 ) ;
2018-06-25 19:21:57 +00:00
if ( what = = " node " ) {
Ref < AnimationNode > anode = p_value ;
if ( anode . is_valid ( ) ) {
add_node ( node_name , p_value ) ;
}
return true ;
}
if ( what = = " position " ) {
if ( states . has ( node_name ) ) {
2018-08-20 16:38:18 +00:00
states [ node_name ] . position = p_value ;
2018-06-25 19:21:57 +00:00
}
return true ;
}
2022-09-29 09:53:28 +00:00
} else if ( prop_name = = " transitions " ) {
2018-06-25 19:21:57 +00:00
Array trans = p_value ;
ERR_FAIL_COND_V ( trans . size ( ) % 3 ! = 0 , false ) ;
for ( int i = 0 ; i < trans . size ( ) ; i + = 3 ) {
add_transition ( trans [ i ] , trans [ i + 1 ] , trans [ i + 2 ] ) ;
}
return true ;
2022-09-29 09:53:28 +00:00
} else if ( prop_name = = " graph_offset " ) {
2018-06-25 19:21:57 +00:00
set_graph_offset ( p_value ) ;
return true ;
}
return false ;
}
bool AnimationNodeStateMachine : : _get ( const StringName & p_name , Variant & r_ret ) const {
2022-09-29 09:53:28 +00:00
String prop_name = p_name ;
if ( prop_name . begins_with ( " states/ " ) ) {
String node_name = prop_name . get_slicec ( ' / ' , 1 ) ;
String what = prop_name . get_slicec ( ' / ' , 2 ) ;
2018-06-25 19:21:57 +00:00
if ( what = = " node " ) {
2018-12-16 14:43:20 +00:00
if ( states . has ( node_name ) & & can_edit_node ( node_name ) ) {
2018-08-20 16:38:18 +00:00
r_ret = states [ node_name ] . node ;
2018-06-25 19:21:57 +00:00
return true ;
}
}
if ( what = = " position " ) {
if ( states . has ( node_name ) ) {
2018-08-20 16:38:18 +00:00
r_ret = states [ node_name ] . position ;
2018-06-25 19:21:57 +00:00
return true ;
}
}
2022-09-29 09:53:28 +00:00
} else if ( prop_name = = " transitions " ) {
2018-06-25 19:21:57 +00:00
Array trans ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
String from = transitions [ i ] . from ;
String to = transitions [ i ] . to ;
trans . push_back ( from ) ;
trans . push_back ( to ) ;
trans . push_back ( transitions [ i ] . transition ) ;
2018-06-25 19:21:57 +00:00
}
r_ret = trans ;
return true ;
2022-09-29 09:53:28 +00:00
} else if ( prop_name = = " graph_offset " ) {
2018-06-25 19:21:57 +00:00
r_ret = get_graph_offset ( ) ;
return true ;
}
return false ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : _get_property_list ( List < PropertyInfo > * p_list ) const {
List < StringName > names ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
names . push_back ( E . key ) ;
2018-06-25 19:21:57 +00:00
}
names . sort_custom < StringName : : AlphCompare > ( ) ;
2022-09-29 09:53:28 +00:00
for ( const StringName & prop_name : names ) {
p_list - > push_back ( PropertyInfo ( Variant : : OBJECT , " states/ " + prop_name + " /node " , PROPERTY_HINT_RESOURCE_TYPE , " AnimationNode " , PROPERTY_USAGE_NO_EDITOR ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : VECTOR2 , " states/ " + prop_name + " /position " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) ) ;
2018-06-25 19:21:57 +00:00
}
2021-11-03 22:06:17 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : ARRAY , " transitions " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : VECTOR2 , " graph_offset " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) ) ;
2023-02-18 02:02:28 +00:00
for ( PropertyInfo & E : * p_list ) {
_validate_property ( E ) ;
}
}
void AnimationNodeStateMachine : : _validate_property ( PropertyInfo & p_property ) const {
if ( p_property . name = = " allow_transition_to_self " | | p_property . name = = " reset_ends " ) {
if ( state_machine_type = = STATE_MACHINE_TYPE_GROUPED ) {
p_property . usage = PROPERTY_USAGE_NONE ;
}
}
2018-06-25 19:21:57 +00:00
}
2021-02-11 17:18:45 +00:00
void AnimationNodeStateMachine : : reset_state ( ) {
states . clear ( ) ;
transitions . clear ( ) ;
playback = " playback " ;
2018-12-16 14:43:20 +00:00
start_node = " Start " ;
end_node = " End " ;
2021-02-11 17:18:45 +00:00
graph_offset = Vector2 ( ) ;
2018-12-16 14:43:20 +00:00
Ref < AnimationNodeStartState > s ;
s . instantiate ( ) ;
State start ;
start . node = s ;
start . position = Vector2 ( 200 , 100 ) ;
states [ start_node ] = start ;
Ref < AnimationNodeEndState > e ;
e . instantiate ( ) ;
State end ;
end . node = e ;
end . position = Vector2 ( 900 , 100 ) ;
states [ end_node ] = end ;
2021-02-11 17:18:45 +00:00
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2021-02-11 17:18:45 +00:00
}
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachine : : set_node_position ( const StringName & p_name , const Vector2 & p_position ) {
ERR_FAIL_COND ( ! states . has ( p_name ) ) ;
states [ p_name ] . position = p_position ;
}
Vector2 AnimationNodeStateMachine : : get_node_position ( const StringName & p_name ) const {
ERR_FAIL_COND_V ( ! states . has ( p_name ) , Vector2 ( ) ) ;
return states [ p_name ] . position ;
}
void AnimationNodeStateMachine : : _tree_changed ( ) {
2022-12-20 21:21:17 +00:00
emit_changed ( ) ;
2023-02-04 15:29:34 +00:00
AnimationRootNode : : _tree_changed ( ) ;
}
void AnimationNodeStateMachine : : _animation_node_renamed ( const ObjectID & p_oid , const String & p_old_name , const String & p_new_name ) {
AnimationRootNode : : _animation_node_renamed ( p_oid , p_old_name , p_new_name ) ;
}
void AnimationNodeStateMachine : : _animation_node_removed ( const ObjectID & p_oid , const StringName & p_node ) {
AnimationRootNode : : _animation_node_removed ( p_oid , p_node ) ;
2018-08-20 16:38:18 +00:00
}
2024-01-06 15:16:10 +00:00
void AnimationNodeStateMachine : : get_argument_options ( const StringName & p_function , int p_idx , List < String > * r_options ) const {
String pf = p_function ;
bool add_state_options = false ;
if ( p_idx = = 0 ) {
add_state_options = ( pf = = " get_node " | | pf = = " has_node " | | pf = = " rename_node " | | pf = = " remove_node " | | pf = = " replace_node " | | pf = = " set_node_position " | | pf = = " get_node_position " ) ;
} else if ( p_idx < = 1 ) {
add_state_options = ( pf = = " has_transition " | | pf = = " add_transition " | | pf = = " remove_transition " ) ;
}
if ( add_state_options ) {
for ( KeyValue < StringName , State > E : states ) {
r_options - > push_back ( String ( E . key ) . quote ( ) ) ;
}
}
AnimationRootNode : : get_argument_options ( p_function , p_idx , r_options ) ;
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : _bind_methods ( ) {
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_node " , " name " , " node " , " position " ) , & AnimationNodeStateMachine : : add_node , DEFVAL ( Vector2 ( ) ) ) ;
2020-03-16 16:42:29 +00:00
ClassDB : : bind_method ( D_METHOD ( " replace_node " , " name " , " node " ) , & AnimationNodeStateMachine : : replace_node ) ;
2018-06-25 19:21:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_node " , " name " ) , & AnimationNodeStateMachine : : get_node ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_node " , " name " ) , & AnimationNodeStateMachine : : remove_node ) ;
ClassDB : : bind_method ( D_METHOD ( " rename_node " , " name " , " new_name " ) , & AnimationNodeStateMachine : : rename_node ) ;
ClassDB : : bind_method ( D_METHOD ( " has_node " , " name " ) , & AnimationNodeStateMachine : : has_node ) ;
ClassDB : : bind_method ( D_METHOD ( " get_node_name " , " node " ) , & AnimationNodeStateMachine : : get_node_name ) ;
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_node_position " , " name " , " position " ) , & AnimationNodeStateMachine : : set_node_position ) ;
ClassDB : : bind_method ( D_METHOD ( " get_node_position " , " name " ) , & AnimationNodeStateMachine : : get_node_position ) ;
2018-08-20 13:58:53 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_transition " , " from " , " to " ) , & AnimationNodeStateMachine : : has_transition ) ;
2018-06-25 19:21:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_transition " , " from " , " to " , " transition " ) , & AnimationNodeStateMachine : : add_transition ) ;
ClassDB : : bind_method ( D_METHOD ( " get_transition " , " idx " ) , & AnimationNodeStateMachine : : get_transition ) ;
ClassDB : : bind_method ( D_METHOD ( " get_transition_from " , " idx " ) , & AnimationNodeStateMachine : : get_transition_from ) ;
ClassDB : : bind_method ( D_METHOD ( " get_transition_to " , " idx " ) , & AnimationNodeStateMachine : : get_transition_to ) ;
ClassDB : : bind_method ( D_METHOD ( " get_transition_count " ) , & AnimationNodeStateMachine : : get_transition_count ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_transition_by_index " , " idx " ) , & AnimationNodeStateMachine : : remove_transition_by_index ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_transition " , " from " , " to " ) , & AnimationNodeStateMachine : : remove_transition ) ;
2019-04-07 11:21:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_graph_offset " , " offset " ) , & AnimationNodeStateMachine : : set_graph_offset ) ;
2018-06-25 19:21:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_graph_offset " ) , & AnimationNodeStateMachine : : get_graph_offset ) ;
2023-01-31 12:17:29 +00:00
2023-02-18 02:02:28 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_state_machine_type " , " state_machine_type " ) , & AnimationNodeStateMachine : : set_state_machine_type ) ;
ClassDB : : bind_method ( D_METHOD ( " get_state_machine_type " ) , & AnimationNodeStateMachine : : get_state_machine_type ) ;
2023-01-31 12:17:29 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_allow_transition_to_self " , " enable " ) , & AnimationNodeStateMachine : : set_allow_transition_to_self ) ;
ClassDB : : bind_method ( D_METHOD ( " is_allow_transition_to_self " ) , & AnimationNodeStateMachine : : is_allow_transition_to_self ) ;
2023-02-18 02:02:28 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_reset_ends " , " enable " ) , & AnimationNodeStateMachine : : set_reset_ends ) ;
ClassDB : : bind_method ( D_METHOD ( " are_ends_reset " ) , & AnimationNodeStateMachine : : are_ends_reset ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " state_machine_type " , PROPERTY_HINT_ENUM , " Root,Nested,Grouped " ) , " set_state_machine_type " , " get_state_machine_type " ) ;
2023-01-31 12:17:29 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " allow_transition_to_self " ) , " set_allow_transition_to_self " , " is_allow_transition_to_self " ) ;
2023-02-18 02:02:28 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " reset_ends " ) , " set_reset_ends " , " are_ends_reset " ) ;
BIND_ENUM_CONSTANT ( STATE_MACHINE_TYPE_ROOT ) ;
BIND_ENUM_CONSTANT ( STATE_MACHINE_TYPE_NESTED ) ;
BIND_ENUM_CONSTANT ( STATE_MACHINE_TYPE_GROUPED ) ;
2018-06-25 19:21:57 +00:00
}
AnimationNodeStateMachine : : AnimationNodeStateMachine ( ) {
2018-12-16 14:43:20 +00:00
Ref < AnimationNodeStartState > s ;
s . instantiate ( ) ;
State start ;
start . node = s ;
start . position = Vector2 ( 200 , 100 ) ;
states [ start_node ] = start ;
Ref < AnimationNodeEndState > e ;
e . instantiate ( ) ;
State end ;
end . node = e ;
end . position = Vector2 ( 900 , 100 ) ;
states [ end_node ] = end ;
2018-06-25 19:21:57 +00:00
}