363 lines
15 KiB
Plaintext
363 lines
15 KiB
Plaintext
// 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;
|
|
}
|
|
|
|
*/ |