// Created and Implemented by Sumeet Jakatdar #namespace BehaviorStateMachine; // ------------- Behavior State Machine Script API registration ----------- // function RegisterBSMScriptAPIInternal( functionName, scriptFunction ) { if ( !IsDefined( level._bsmscriptfunctions ) ) { level._bsmscriptfunctions = []; } // SUMEET TODO - remove the need of ToLower and have the functionNames defined in .gsh functionName = ToLower( functionName ); Assert( IsDefined( scriptFunction ) && IsDefined( scriptFunction ), "BT - (RegisterBSMScriptAPI) No functionPtr defined or no functionName defined for BSMScript." ); Assert( !IsDefined( level._bsmscriptfunctions[functionName] ), "BT - (RegisterBSMScriptAPI) functionName is already defined for BSMScript." ); level._bsmscriptfunctions[functionName] = scriptFunction; } /* class BehaviorState { var stateName; var asmStateName; var initFunc; var updateFunc; var terminateFunc; var connections; constructor() { connections = []; } destructor() { } function GetConnections() { return connections; } } class BehaviorConnection { var connectionName; var toState; var conditionFunc; var endOfStateConnection; var asmState; constructor() { toState = NULL; } destructor() { } } class BSM { var stateMachineName; var states; var miscData; var currentState; var previousState; constructor() { states = []; } destructor() { } function AddState( stateName, asmStateName, initFunc, updateFunc, terminateFunc ) { assert( !IsDefined( states[stateName] ), "BSM - State is already defined " + stateName ); states[stateName] = new BehaviorState(); states[stateName].initFunc = initFunc; states[stateName].updateFunc = updateFunc; states[stateName].terminateFunc = terminateFunc; states[stateName].asmStateName = asmStateName; states[stateName].stateName = stateName; } function AddConnection( connectionName, fromState, toState, conditionFunc, endOfStateConnection, asmState ) { assert( IsDefined( states[fromState] ), "BSM - State is not defined " + fromState ); assert( IsDefined( states[toState] ), "BSM - State is not defined " + toState ); assert( !IsDefined( states[fromState].connections[connectionName] ), "BSM - Connection is already defined " + connectionName ); states[fromState].connections[connectionName] = new BehaviorConnection(); states[fromState].connections[connectionName].toState = toState; states[fromState].connections[connectionName].conditionFunc = conditionFunc; states[fromState].connections[connectionName].connectionName = connectionName; states[fromState].connections[connectionName].endOfStateConnection = endOfStateConnection; states[fromState].connections[connectionName].asmState = asmState; } function GetStates() { return states; } } function CreateBSM( archetypeName, stateMachineName ) { if( !IsDefined( level.__BSMs ) ) { level.__BSMs = []; } if( !IsDefined( level.__BSMs[archetypeName] ) ) { level.__BSMs[archetypeName] = []; } assert( !IsDefined( level.__BSMs[archetypeName][stateMachineName] ) ); newStateMachine = new BSM(); newStateMachine.stateMachineName = stateMachineName; level.__BSMs[archetypeName][stateMachineName] = newStateMachine; return level.__BSMs[archetypeName][stateMachineName]; } function StartBSM( entity, stateMachineName ) { assert( IsDefined( level.__BSMs[entity.archetype][stateMachineName] ) ); stateMachine = level.__BSMs[entity.archetype][stateMachineName]; assert( !IsDefined( stateMachine.currentState ) ); assert( !IsDefined( stateMachine.previousState ) ); // go through the states, find the first fit foreach ( state in stateMachine.states ) { if( !IsDefined( state.initFunc ) ) { if( IsDefined( state.asmStateName ) ) { newState = state; break; } } else { validState = [[state.initFunc]]( entity ); if( validState ) { // found one state which has a valid ASM, set it as a current substate if( IsDefined( state.asmStateName ) ) { newState = state; break; } } } } if( IsDefined( newState ) ) { stateMachine.currentState = newState; AnimationStateNetworkUtility::RequestState( entity, newState.asmStateName ); return BHTN_RUNNING; } return BHTN_SUCCESS; } function UpdateBSM( entity, stateMachineName ) { assert( IsDefined( level.__BSMs[entity.archetype][stateMachineName] ) ); stateMachine = level.__BSMs[entity.archetype][stateMachineName]; assert( IsDefined( stateMachine.currentState ) ); // run the update function for the current state currentStateUpdatedResult = true; if( IsDefined( stateMachine.currentState.updateFunc ) ) { currentStateUpdatedResult = [[stateMachine.currentState.updateFunc]]( entity ); } // We have a valid running state, go through all the connections ( if there are any ) connections = [[stateMachine.currentState]]->GetConnections(); if( !connections.size ) { if( entity ASMGetStatus() == ASM_STATE_COMPLETE ) { TerminateBSM( entity, stateMachineName ); return BHTN_SUCCESS; } } else { nextValidState = undefined; foreach ( connection in connections ) { // execute endOfStateConnection only when current state is complete or is not valid anymore if( IS_TRUE( connection.endOfStateConnection ) && ( entity ASMGetStatus() != ASM_STATE_COMPLETE || !currentStateUpdatedResult ) ) { /#RecordEntText( "EndOfStateConnection :" + connection.connectionName, entity, RED, "Animscript" );#/ continue; } if( IsDefined( connection.conditionFunc ) ) { // check if the connection is valid validConnection = [[connection.conditionFunc]]( entity ); if( !IsDefined( validConnection ) ) validConnection = true; // Connection is valid, check if the toState has any condition associated with it, and execute it if( validConnection ) { /#RecordEntText( "Connection :" + connection.connectionName, entity, GREEN, "Animscript" );#/ if( !IsDefined( stateMachine.states[connection.toState].initFunc ) ) { nextValidState = stateMachine.states[connection.toState]; break; } else { validToState = [[stateMachine.states[connection.toState].initFunc]]( entity ); if( validToState ) { nextValidState = stateMachine.states[connection.toState]; break; } else { /#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, RED, "Animscript" );#/ } } } else { /#RecordEntText( "Connection :" + connection.connectionName, entity, RED, "Animscript" );#/ } } else { /#RecordEntText( "Connection :" + connection.connectionName, entity, GREEN, "Animscript" );#/ if( !IsDefined( stateMachine.states[connection.toState].initFunc ) ) { nextValidState = stateMachine.states[connection.toState]; break; } else { validToState = [[stateMachine.states[connection.toState].initFunc]]( entity ); if( validToState ) { nextValidState = stateMachine.states[connection.toState]; break; } else { /#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, RED, "Animscript" );#/ } } } } // found a validNextState with ValidConnection, go there now if( IsDefined( nextValidState ) ) { // found a new valid state, execute terminateFunc of the current state if( IsDefined( stateMachine.currentState.terminateFunc ) ) { [[stateMachine.currentState.terminateFunc]]( entity ); } /#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, GREEN, "Animscript" );#/ stateMachine.previousState = stateMachine.currentState; stateMachine.currentState = nextValidState; assert( IsDefined( nextValidState.asmStateName ) ); AnimationStateNetworkUtility::RequestState( entity, nextValidState.asmStateName ); return BHTN_RUNNING; } else { /#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, RED, "Animscript" );#/ } } // if there is no better connection, just update the current substate if( entity ASMGetStatus() == ASM_STATE_COMPLETE ) { TerminateBSM( entity, stateMachineName ); return BHTN_SUCCESS; } else { /#RecordEntText( "CurrentState :" + stateMachine.currentState.stateName, entity, BLUE, "Animscript" );#/ } return BHTN_RUNNING; } function TerminateBSM( entity, stateMachineName ) { assert( IsDefined( level.__BSMs[entity.archetype][stateMachineName] ) ); stateMachine = level.__BSMs[entity.archetype][stateMachineName]; // state machine is terminating and returning control to behavior tree, call terminateFunc of the current state if( IsDefined( stateMachine.currentState ) && IsDefined( stateMachine.currentState.terminateFunc ) ) { [[stateMachine.currentState.terminateFunc]]( entity ); } stateMachine.currentState = undefined; stateMachine.previousState = undefined; return BHTN_SUCCESS; } */