mirror of
https://github.com/UberGames/EF2GameSource.git
synced 2024-11-27 13:42:00 +00:00
1876 lines
40 KiB
C++
1876 lines
40 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /EF2/Code/DLLs/game/characterstate.cpp $
|
|
// $Revision:: 24 $
|
|
// $Date:: 9/26/03 2:36p $
|
|
//
|
|
// Copyright (C) 1999 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
|
|
#include "_pch_cpp.h"
|
|
#include "characterstate.h"
|
|
#include "animate.h"
|
|
#include "scriptmaster.h"
|
|
|
|
static const char *MoveControl_Names[] =
|
|
{
|
|
"user", // MOVECONTROL_USER
|
|
"legs", // MOVECONTROL_LEGS
|
|
"anim", // MOVECONTROL_ANIM
|
|
"absolute", // MOVECONTROL_ABSOLUTE
|
|
"hanging", // MOVECONTROL_HANGING
|
|
"wallhug", // MOVECONTROL_WALLHUG
|
|
"monkeybars", // MOVECONTROL_MONKEYBARS
|
|
"pipecrawl", // MOVECONTROL_PIPECRAWL
|
|
"pipehang", // MOVECONTROL_PIPEHANG
|
|
"stepup", // MOVECONTROL_STEPUP
|
|
"rope_grab", // MOVECONTROL_ROPE_GRAB
|
|
"rope_release", // MOVECONTROL_ROPE_RELEASE
|
|
"rope_move", // MOVECONTROL_ROPE_MOVE
|
|
"pickupenemy", // MOVECONTROL_PICKUPENEMY
|
|
"push", // MOVECONTROL_PUSH
|
|
"climbwall", // MOVECONTROL_CLIMBWALL
|
|
"useanim", // MOVECONTROL_USEANIM
|
|
"crouch", // MOVECONTROL_CROUCH
|
|
"loopuseanim", // MOVECONTROL_LOOPUSEANIM
|
|
"useobject", // MOVECONTROL_USEOBJECT
|
|
"coolobject", // MOVECONTROL_COOLOBJECT
|
|
"fakeplayer", // MOVECONTROL_FAKEPLAYER
|
|
NULL
|
|
};
|
|
|
|
static const char *Camera_Names[] =
|
|
{
|
|
"topdown", // CAMERA_TOPDOWN
|
|
"behind", // CAMERA_BEHIND
|
|
"front", // CAMERA_FRONT
|
|
"side", // CAMERA_SIDE
|
|
"behind_fixed", // CAMERA_BEHIND_FIXED
|
|
"side_left", // CAMERA_SIDE_LEFT
|
|
"side_right", // CAMERA_SIDE_RIGHT
|
|
"behind_nopitch", // CAMERA_BEHIND_NOPITCH
|
|
NULL
|
|
};
|
|
|
|
Conditional::Conditional( const Condition<Class> &cond ) :
|
|
condition( cond )
|
|
{
|
|
result = false;
|
|
previous_result = false;
|
|
checked = false;
|
|
}
|
|
|
|
Conditional::Conditional( void )
|
|
{
|
|
gi.Error( ERR_DROP, "Conditional created with wrong constructor\n" );
|
|
}
|
|
|
|
Expression::Expression()
|
|
{
|
|
}
|
|
|
|
Expression::Expression( const Expression &exp )
|
|
{
|
|
int i;
|
|
|
|
value = exp.value;
|
|
|
|
for( i = 1; i <= exp.conditions.NumObjects(); i++ )
|
|
{
|
|
conditions.AddObject( exp.conditions.ObjectAt( i ) );
|
|
}
|
|
}
|
|
|
|
Expression::Expression( Script &script, State &state )
|
|
{
|
|
str token;
|
|
condition_t condition;
|
|
int start;
|
|
|
|
value = script.GetToken( true );
|
|
|
|
if ( !script.TokenAvailable( false ) || Q_stricmp( script.GetToken( false ), ":" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting ':' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( false ) )
|
|
{
|
|
token = script.GetToken( true );
|
|
|
|
switch( token[ 0 ] )
|
|
{
|
|
case '!' :
|
|
condition.test = TC_ISFALSE;
|
|
start = 1;
|
|
break;
|
|
|
|
case '+' :
|
|
condition.test = TC_EDGETRUE;
|
|
start = 1;
|
|
break;
|
|
|
|
case '-' :
|
|
condition.test = TC_EDGEFALSE;
|
|
start = 1;
|
|
break;
|
|
|
|
default :
|
|
condition.test = TC_ISTRUE;
|
|
start = 0;
|
|
}
|
|
|
|
if ( token.length() <= start )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Illegal syntax '%s' on line %d.\n", script.Filename(), &token, script.GetLineNumber() );
|
|
condition.condition_index = 0;
|
|
continue;
|
|
}
|
|
|
|
condition.condition_index = state.addCondition( &token[ start ], script );
|
|
if ( !condition.condition_index )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown condition '%s' on line %d.\n", script.Filename(), &token[ start ], script.GetLineNumber() );
|
|
}
|
|
|
|
conditions.AddObject( condition );
|
|
}
|
|
}
|
|
|
|
Expression::Expression( Script &script, FuzzyVar &fuzzyVar )
|
|
{
|
|
str token;
|
|
condition_t condition;
|
|
int start;
|
|
|
|
value = script.GetToken( true );
|
|
points = (float)atof(value.c_str());
|
|
|
|
if ( !script.TokenAvailable( false ) || Q_stricmp( script.GetToken( false ), ":" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting ':' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( false ) )
|
|
{
|
|
token = script.GetToken( true );
|
|
|
|
switch( token[ 0 ] )
|
|
{
|
|
case '!' :
|
|
condition.test = TC_ISFALSE;
|
|
start = 1;
|
|
break;
|
|
|
|
case '+' :
|
|
condition.test = TC_EDGETRUE;
|
|
start = 1;
|
|
break;
|
|
|
|
case '-' :
|
|
condition.test = TC_EDGEFALSE;
|
|
start = 1;
|
|
break;
|
|
|
|
default :
|
|
condition.test = TC_ISTRUE;
|
|
start = 0;
|
|
}
|
|
|
|
if ( token.length() <= start )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Illegal syntax '%s' on line %d.\n", script.Filename(), &token, script.GetLineNumber() );
|
|
condition.condition_index = 0;
|
|
continue;
|
|
}
|
|
|
|
condition.condition_index = fuzzyVar.addCondition( &token[ start ], script );
|
|
if ( !condition.condition_index )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown condition '%s' on line %d.\n", script.Filename(), &token[ start ], script.GetLineNumber() );
|
|
}
|
|
|
|
conditions.AddObject( condition );
|
|
}
|
|
}
|
|
|
|
|
|
bool Expression::getResult( const State &state, Entity &ent, const Container<Conditional *> *sent_conditionals )
|
|
{
|
|
int i;
|
|
condition_t *cond;
|
|
Conditional *conditional;
|
|
|
|
for( i = 1; i <= conditions.NumObjects(); i++ )
|
|
{
|
|
cond = &conditions.ObjectAt( i );
|
|
conditional = sent_conditionals->ObjectAt( cond->condition_index );
|
|
|
|
if ( !conditional || !conditional->getResult( cond->test, ent ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Expression::getResult( const FuzzyVar &fVar, Entity &ent, const Container<Conditional *> *sent_conditionals )
|
|
{
|
|
int i;
|
|
condition_t *cond;
|
|
Conditional *conditional;
|
|
|
|
for( i = 1; i <= conditions.NumObjects(); i++ )
|
|
{
|
|
cond = &conditions.ObjectAt( i );
|
|
conditional = sent_conditionals->ObjectAt( cond->condition_index );
|
|
|
|
if ( !conditional || !conditional->getResult( cond->test, ent ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void State::readNextState( Script &script )
|
|
{
|
|
nextState = script.GetToken( false );
|
|
}
|
|
|
|
void State::readMoveType( Script &script )
|
|
{
|
|
str token;
|
|
const char **name;
|
|
int i;
|
|
|
|
token = script.GetToken( false );
|
|
|
|
for( i = 0, name = MoveControl_Names; *name != NULL; name++, i++ )
|
|
{
|
|
if ( !token.icmp( *name ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( *name == NULL )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown movetype '%s' on line %d.\n", script.Filename(), token.c_str(), script.GetLineNumber() );
|
|
}
|
|
else
|
|
{
|
|
movetype = ( movecontrol_t )i;
|
|
}
|
|
}
|
|
|
|
qboolean State::setCameraType( const str &ctype )
|
|
{
|
|
const char **name;
|
|
int i;
|
|
|
|
for( i = 0, name = Camera_Names; *name != NULL; name++, i++ )
|
|
{
|
|
if ( !ctype.icmp( *name ) )
|
|
{
|
|
cameratype = ( cameratype_t )i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void State::readCamera( Script &script )
|
|
{
|
|
str token;
|
|
|
|
token = script.GetToken( false );
|
|
|
|
if ( !setCameraType( token ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown camera type '%s' on line %d.\n", script.Filename(), token.c_str(), script.GetLineNumber() );
|
|
}
|
|
}
|
|
|
|
void State::readLegs( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
token = script.GetToken( true );
|
|
if ( !Q_stricmp( token.c_str(), "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
script.UnGetToken();
|
|
legAnims.AddObject( Expression( script, *this ) );
|
|
}
|
|
}
|
|
|
|
void State::readTorso( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
token = script.GetToken( true );
|
|
if ( !Q_stricmp( token.c_str(), "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
script.UnGetToken();
|
|
torsoAnims.AddObject( Expression( script, *this ) );
|
|
}
|
|
}
|
|
|
|
void State::readBehavior( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting behavior name on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
behaviorName = script.GetToken( true );
|
|
if ( !getClass( behaviorName ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown behavior '%s' on line %d.\n", script.Filename(), behaviorName.c_str(), script.GetLineNumber() );
|
|
}
|
|
|
|
// Read in the behavior arguments if there are any
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
while ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
while ( !script.AtCloseParen( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "," ) )
|
|
{
|
|
addBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
// Need to burn off last ")" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), ")" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
}
|
|
|
|
// Old Format - For Legacy Data
|
|
while ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
addBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
void State::readHeadBehavior( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting behavior name on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
headBehaviorName = script.GetToken( true );
|
|
if ( !getClass( headBehaviorName ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown behavior '%s' on line %d.\n", script.Filename(), headBehaviorName.c_str(), script.GetLineNumber() );
|
|
}
|
|
|
|
// Read in the behavior arguments if there are any
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
while ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
while ( !script.AtCloseParen( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "," ) )
|
|
{
|
|
addHeadBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
// Need to burn off last ")" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), ")" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
}
|
|
|
|
// Old Format - For Legacy Data
|
|
while ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
addHeadBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
void State::readEyeBehavior( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting behavior name on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
eyeBehaviorName = script.GetToken( true );
|
|
if ( !getClass( eyeBehaviorName ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown behavior '%s' on line %d.\n", script.Filename(), headBehaviorName.c_str(), script.GetLineNumber() );
|
|
}
|
|
|
|
// Read in the behavior arguments if there are any
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
while ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
while ( !script.AtCloseParen( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "," ) )
|
|
{
|
|
addEyeBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
// Need to burn off last ")" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), ")" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
}
|
|
|
|
// Old Format - For Legacy Data
|
|
while ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
addEyeBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
void State::readTorsoBehavior( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting behavior name on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
torsoBehaviorName = script.GetToken( true );
|
|
if ( !getClass( torsoBehaviorName ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown behavior '%s' on line %d.\n", script.Filename(), headBehaviorName.c_str(), script.GetLineNumber() );
|
|
}
|
|
|
|
// Read in the behavior arguments if there are any
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
while ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
while ( !script.AtCloseParen( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "," ) )
|
|
{
|
|
addTorsoBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
// Need to burn off last ")" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), ")" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
}
|
|
|
|
// Old Format - For Legacy Data
|
|
while ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
addTorsoBehaviorParm( token );
|
|
}
|
|
}
|
|
|
|
void State::readTime( Script &script )
|
|
{
|
|
str token;
|
|
|
|
// Old Format - For Legacy Data
|
|
if ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
minTime = (float)atof( token.c_str() );
|
|
}
|
|
|
|
if ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
maxTime = (float)atof( token.c_str() );
|
|
}
|
|
else
|
|
{
|
|
maxTime = minTime;
|
|
}
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
if ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
// Grab Min Time
|
|
token = script.GetToken( false );
|
|
minTime = (float)atof( token.c_str() );
|
|
|
|
// Set a default value for maxTime
|
|
maxTime = minTime;
|
|
|
|
token = script.GetToken( false );
|
|
|
|
if ( !Q_stricmp( token.c_str(), "," ))
|
|
{
|
|
token = script.GetToken( false );
|
|
maxTime = (float)atof( token.c_str() );
|
|
|
|
// Grab Closing Paren
|
|
token = script.GetToken( false );
|
|
}
|
|
|
|
// Check that we have the closing paren, if not - complain
|
|
if ( Q_stricmp( token.c_str(), ")") )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Missing Closing Paren ) on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void State::readStates( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
token = script.GetToken( true );
|
|
if ( !Q_stricmp( token.c_str(), "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
script.UnGetToken();
|
|
states.AddObject( Expression( script, *this ) );
|
|
}
|
|
}
|
|
|
|
void State::ParseAndProcessCommand( const str &command, Entity *target )
|
|
{
|
|
int argc;
|
|
const char *argv[ MAX_COMMANDS ];
|
|
char args[ MAX_COMMANDS ][ MAXTOKEN ];
|
|
Script script;
|
|
Event *event;
|
|
|
|
script.Parse( command, command.length(), "CommandString" );
|
|
|
|
argc = 0;
|
|
while( script.TokenAvailable( false ) )
|
|
{
|
|
if ( argc >= MAX_COMMANDS )
|
|
{
|
|
gi.WDPrintf( "State:ParseAndProcessCommand : Line exceeds %d command limit", MAX_COMMANDS );
|
|
script.SkipToEOL();
|
|
break;
|
|
}
|
|
strcpy( args[ argc ], script.GetToken( false ) );
|
|
argv[ argc ] = args[ argc ];
|
|
argc++;
|
|
}
|
|
|
|
assert( argc > 0 );
|
|
|
|
if ( argc <= 0 )
|
|
return;
|
|
|
|
event = new Event( args[0] );
|
|
event->AddTokens( argc - 1, &argv[ 1 ] );
|
|
target->ProcessEvent( event );
|
|
}
|
|
|
|
void State::ProcessEntryCommands( Entity *target )
|
|
{
|
|
int i,count;
|
|
str command;
|
|
|
|
assert( target );
|
|
if ( !target )
|
|
{
|
|
return;
|
|
}
|
|
|
|
count = entryCommands.NumObjects();
|
|
for( i = 1; i <= count; i++ )
|
|
{
|
|
ParseAndProcessCommand( entryCommands.ObjectAt( i ), target );
|
|
}
|
|
}
|
|
|
|
void State::ProcessExitCommands( Entity *target )
|
|
{
|
|
int i,count;
|
|
str command;
|
|
|
|
assert( target );
|
|
if ( !target )
|
|
{
|
|
return;
|
|
}
|
|
|
|
count = exitCommands.NumObjects();
|
|
for( i = 1; i <= count; i++ )
|
|
{
|
|
ParseAndProcessCommand( exitCommands.ObjectAt( i ), target );
|
|
}
|
|
}
|
|
|
|
void State::readCommands( Script &script, Container<str> &container )
|
|
{
|
|
str token;
|
|
str command;
|
|
qboolean finish_line;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
finish_line = false;
|
|
|
|
while( script.TokenAvailable( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
|
|
//Skip Parens "()" or Commas ","
|
|
while( !Q_stricmp( token.c_str(), "(" ) || !Q_stricmp( token.c_str(), ")" ) || !Q_stricmp( token.c_str(), "," ))
|
|
{
|
|
if ( script.TokenAvailable( false ) )
|
|
token = script.GetToken( false );
|
|
else
|
|
{
|
|
finish_line = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( finish_line )
|
|
break;
|
|
|
|
if ( !Q_stricmp( token.c_str(), "}" ) )
|
|
return;
|
|
|
|
if ( token.length() )
|
|
{
|
|
if ( strstr( token.c_str(), " " ) == NULL )
|
|
{
|
|
command.append( token );
|
|
}
|
|
else
|
|
{
|
|
command.append( "\"" );
|
|
command.append( token );
|
|
command.append( "\"" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
command.append( "\"\"" );
|
|
}
|
|
|
|
command.append( " " );
|
|
}
|
|
|
|
container.AddObject( command );
|
|
command = "";
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
State *State::Evaluate( Entity &ent, Container<Conditional *> *sent_conditionals )
|
|
{
|
|
int i;
|
|
Expression *exp;
|
|
State *state;
|
|
int index;
|
|
|
|
for( i = 1; i <= condition_indexes.NumObjects(); i++ )
|
|
{
|
|
index = condition_indexes.ObjectAt( i );
|
|
sent_conditionals->ObjectAt( index )->clearCheck();
|
|
//conditions.ObjectAt( i )->clearCheck();
|
|
}
|
|
|
|
for( i = 1; i <= states.NumObjects(); i++ )
|
|
{
|
|
exp = &states.ObjectAt( i );
|
|
if ( exp->getResult( *this, ent, sent_conditionals ) )
|
|
{
|
|
state = statemap.FindState( exp->getValue() );
|
|
return state;
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
const char *State::getLegAnim( Entity &ent, Container<Conditional *> *sent_conditionals )
|
|
{
|
|
int i;
|
|
Expression *exp;
|
|
int index;
|
|
|
|
for( i = 1; i <= condition_indexes.NumObjects(); i++ )
|
|
{
|
|
index = condition_indexes.ObjectAt( i );
|
|
sent_conditionals->ObjectAt( index )->clearCheck();
|
|
//conditions.ObjectAt( i )->clearCheck();
|
|
}
|
|
|
|
for( i = 1; i <= legAnims.NumObjects(); i++ )
|
|
{
|
|
exp = &legAnims.ObjectAt( i );
|
|
if ( exp->getResult( *this, ent, sent_conditionals ) )
|
|
{
|
|
return exp->getValue();
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
const char *State::getTorsoAnim( Entity &ent, Container<Conditional *> *sent_conditionals )
|
|
{
|
|
int i;
|
|
Expression *exp;
|
|
int index;
|
|
|
|
for( i = 1; i <= condition_indexes.NumObjects(); i++ )
|
|
{
|
|
index = condition_indexes.ObjectAt( i );
|
|
sent_conditionals->ObjectAt( index )->clearCheck();
|
|
}
|
|
|
|
for( i = 1; i <= torsoAnims.NumObjects(); i++ )
|
|
{
|
|
exp = &torsoAnims.ObjectAt( i );
|
|
if ( exp->getResult( *this, ent, sent_conditionals ) )
|
|
{
|
|
return exp->getValue();
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
const char *State::getBehaviorName( void )
|
|
{
|
|
return behaviorName.c_str();
|
|
}
|
|
|
|
const char *State::getHeadBehaviorName( void )
|
|
{
|
|
return headBehaviorName.c_str();
|
|
}
|
|
|
|
const char *State::getEyeBehaviorName( void )
|
|
{
|
|
return eyeBehaviorName.c_str();
|
|
}
|
|
|
|
const char *State::getTorsoBehaviorName( void )
|
|
{
|
|
return torsoBehaviorName.c_str();
|
|
}
|
|
|
|
float State::getMinTime( void )
|
|
{
|
|
return minTime;
|
|
}
|
|
|
|
float State::getMaxTime( void )
|
|
{
|
|
return maxTime;
|
|
}
|
|
|
|
int State::addCondition( const char *name, Script &script )
|
|
{
|
|
Conditional *condition;
|
|
Condition<Class> *cond;
|
|
int index;
|
|
|
|
str token;
|
|
|
|
condition = NULL;
|
|
cond = statemap.getCondition( name );
|
|
if ( !cond )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
condition = new Conditional( *cond );
|
|
|
|
// Get the paramaters
|
|
// Legacy Format - Parameters use ""
|
|
while ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
condition->addParm( token );
|
|
}
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
while ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
while ( !script.AtCloseParen( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "," ) )
|
|
{
|
|
condition->addParm( token );
|
|
}
|
|
}
|
|
|
|
// Need to burn off last ")" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), ")" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
}
|
|
|
|
// only add a new conditional if a similar one doesn't exist
|
|
index = statemap.findConditional( condition );
|
|
|
|
if ( index )
|
|
{
|
|
// delete the one we just made
|
|
delete condition;
|
|
}
|
|
else
|
|
{
|
|
index = statemap.addConditional( condition );
|
|
}
|
|
|
|
condition_indexes.AddUniqueObject( index );
|
|
|
|
return index;
|
|
}
|
|
|
|
void State::CheckStates( void )
|
|
{
|
|
const char *value;
|
|
int i;
|
|
|
|
if ( !statemap.FindState( nextState.c_str() ) )
|
|
{
|
|
gi.Error( ERR_DROP, "Unknown next state '%s' referenced in state '%s'.\n", nextState.c_str(), getName() );
|
|
}
|
|
|
|
for( i = 1; i <= states.NumObjects(); i++ )
|
|
{
|
|
value = states.ObjectAt( i ).getValue();
|
|
if ( !statemap.FindState( value ) )
|
|
{
|
|
gi.WDPrintf(
|
|
"============\n"
|
|
"ERROR in %s:\n"
|
|
" Possibly Unknown state '%s' referenced in state '%s'. -- Check your includes \n"
|
|
"============",
|
|
statemap.Filename(), value, getName() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void State::GetLegAnims( Container<const char *> *c )
|
|
{
|
|
int i,j;
|
|
qboolean addobj = true;
|
|
|
|
for ( i=1; i<=legAnims.NumObjects(); i++ )
|
|
{
|
|
const char *value = legAnims.ObjectAt( i ).getValue();
|
|
addobj = true;
|
|
|
|
// Check to see if it's already in there
|
|
for ( j=1; j<=c->NumObjects(); j++ )
|
|
{
|
|
if ( !stricmp( c->ObjectAt( j ), value ) )
|
|
{
|
|
addobj = false;
|
|
break;
|
|
}
|
|
}
|
|
if ( addobj )
|
|
c->AddObject( value );
|
|
}
|
|
}
|
|
|
|
void State::GetTorsoAnims( Container<const char *> *c )
|
|
{
|
|
int i,j;
|
|
qboolean addobj = true;
|
|
|
|
for ( i=1; i<=torsoAnims.NumObjects(); i++ )
|
|
{
|
|
const char *value = torsoAnims.ObjectAt( i ).getValue();
|
|
addobj = true;
|
|
|
|
// Check to see if it's already in there
|
|
for ( j=1; j<=c->NumObjects(); j++ )
|
|
{
|
|
if ( !stricmp( c->ObjectAt( j ), value ) )
|
|
{
|
|
addobj = false;
|
|
break;
|
|
}
|
|
}
|
|
if ( addobj )
|
|
c->AddObject( value );
|
|
}
|
|
}
|
|
|
|
qboolean State::IgnoreGlobalStates()
|
|
{
|
|
return ignoreGlobalStates;
|
|
}
|
|
|
|
State::State( const char *statename, Script &script, StateMap &map ) :
|
|
statemap( map )
|
|
|
|
{
|
|
str cmd;
|
|
|
|
name = statename;
|
|
nextState = statename;
|
|
movetype = DEFAULT_MOVETYPE;
|
|
cameratype = DEFAULT_CAMERA;
|
|
//behaviorName = "idle";
|
|
behaviorName = "";
|
|
headBehaviorName = "";
|
|
eyeBehaviorName = "";
|
|
torsoBehaviorName = "";
|
|
|
|
minTime = -1.0f;
|
|
maxTime = -1.0f;
|
|
|
|
ignoreGlobalStates = false;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
cmd = script.GetToken( true );
|
|
if ( !cmd.icmp( "nextstate" ) )
|
|
{
|
|
readNextState( script );
|
|
}
|
|
else if ( !cmd.icmp( "movetype" ) )
|
|
{
|
|
readMoveType( script );
|
|
}
|
|
else if ( !cmd.icmp( "camera" ) )
|
|
{
|
|
readCamera( script );
|
|
}
|
|
else if ( !cmd.icmp( "legs" ) )
|
|
{
|
|
readLegs( script );
|
|
}
|
|
else if ( !cmd.icmp( "torso" ) )
|
|
{
|
|
readTorso( script );
|
|
}
|
|
else if ( !cmd.icmp( "behavior" ) )
|
|
{
|
|
readBehavior( script );
|
|
}
|
|
else if ( !cmd.icmp( "headbehavior" ) )
|
|
{
|
|
readHeadBehavior( script );
|
|
}
|
|
else if ( !cmd.icmp( "eyebehavior" ) )
|
|
{
|
|
readEyeBehavior( script );
|
|
}
|
|
else if ( !cmd.icmp( "torsobehavior" ) )
|
|
{
|
|
readTorsoBehavior( script );
|
|
}
|
|
else if ( !cmd.icmp( "time" ) )
|
|
{
|
|
readTime( script );
|
|
}
|
|
else if ( !cmd.icmp( "states" ) )
|
|
{
|
|
readStates( script );
|
|
}
|
|
else if ( !cmd.icmp( "ignoreglobalstate" ) )
|
|
{
|
|
ignoreGlobalStates = true;
|
|
}
|
|
else if ( !cmd.icmp( "entrycommands" ) )
|
|
{
|
|
readCommands( script, entryCommands );
|
|
}
|
|
else if ( !cmd.icmp( "exitcommands" ) )
|
|
{
|
|
readCommands( script, exitCommands );
|
|
}
|
|
else if ( !cmd.icmp( "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown command '%s' on line %d.\n", script.Filename(), cmd.c_str(), script.GetLineNumber() );
|
|
}
|
|
}
|
|
}
|
|
|
|
StateMap::StateMap( void )
|
|
{
|
|
gi.Error( ERR_DROP, "StateMap created with wrong constructor\n" );
|
|
}
|
|
|
|
StateMap::StateMap( const char *file_name, Condition<Class> *conditions, Container<Conditional *> *conditionals )
|
|
{
|
|
|
|
assert( file_name );
|
|
|
|
filename = file_name;
|
|
|
|
this->current_conditions = conditions;
|
|
|
|
this->current_conditionals = conditionals;
|
|
|
|
Script *script;
|
|
script = new Script(file_name);
|
|
if ( script )
|
|
{
|
|
ReadStates(script);
|
|
delete script;
|
|
script = 0;
|
|
}
|
|
|
|
// Have all the states check themselves to see if they reference any non-existant states.
|
|
int i;
|
|
for( i = 1; i <= stateList.NumObjects(); i++ )
|
|
{
|
|
stateList.ObjectAt( i )->CheckStates();
|
|
}
|
|
}
|
|
|
|
StateMap::~StateMap()
|
|
{
|
|
int i,num;
|
|
|
|
num = stateList.NumObjects();
|
|
for( i=num; i>0; i-- )
|
|
{
|
|
delete stateList.ObjectAt( i );
|
|
}
|
|
stateList.FreeObjectList();
|
|
|
|
num = globalStateList.NumObjects();
|
|
for( i=num; i>0; i-- )
|
|
{
|
|
delete globalStateList.ObjectAt( i );
|
|
}
|
|
|
|
globalStateList.FreeObjectList();
|
|
}
|
|
|
|
Condition<Class> *StateMap::getCondition( const char *name )
|
|
{
|
|
Condition<Class> *c;
|
|
|
|
if ( current_conditions )
|
|
{
|
|
for( c = current_conditions; c->name; c++ )
|
|
{
|
|
if ( !strcmp( c->name, name ) )
|
|
{
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void StateMap::ReadStates( Script *script )
|
|
{
|
|
str cmd;
|
|
str statename;
|
|
State *state;
|
|
const char *includedFile;
|
|
|
|
while( script->TokenAvailable( true ) )
|
|
{
|
|
cmd = script->GetToken( true );
|
|
//if ( cmd == "" )
|
|
// break;
|
|
if ( !cmd.icmp( "state" ) )
|
|
{
|
|
statename = script->GetToken( false );
|
|
state = FindState( statename.c_str() );
|
|
if ( state )
|
|
{
|
|
stateList.RemoveObject( state );
|
|
delete state;
|
|
state = NULL;
|
|
}
|
|
|
|
// parse the state even if we already have it defined
|
|
state = new State( statename.c_str(), *script, *this );
|
|
stateList.AddObject( state );
|
|
}
|
|
else if ( !cmd.icmp( "globalstate" ) )
|
|
{
|
|
statename = script->GetToken( false );
|
|
state = FindGlobalState( statename.c_str() );
|
|
if ( state )
|
|
{
|
|
globalStateList.RemoveObject( state );
|
|
delete state;
|
|
state = NULL;
|
|
}
|
|
|
|
state = new State( statename.c_str(), *script, *this );
|
|
globalStateList.AddObject( state );
|
|
}
|
|
else if ( !cmd.icmp( "$include" ) )
|
|
{
|
|
includedFile = script->GetToken( false );
|
|
Script *newScript = new Script(includedFile);
|
|
if ( !newScript )
|
|
break;
|
|
|
|
ReadStates(newScript);
|
|
|
|
// Copy over defines to the parent script's list
|
|
Container<macro*> *macrolist = newScript->GetMacroList();
|
|
Container<macro*> *mainlist = script->GetMacroList();
|
|
macro *theMacro = 0;
|
|
for( int i = 1; i <= macrolist->NumObjects(); i++)
|
|
{
|
|
theMacro = macrolist->ObjectAt( i );
|
|
macro *addMacro = new macro;
|
|
addMacro->macroName = theMacro->macroName;
|
|
addMacro->macroText = theMacro->macroText;
|
|
mainlist->AddObject( addMacro );
|
|
}
|
|
|
|
// delete our temporary script object
|
|
delete newScript;
|
|
newScript = 0;
|
|
}
|
|
else
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown command '%s' on line %d.\n", script->Filename(), cmd.c_str(), script->GetLineNumber() );
|
|
}
|
|
}
|
|
}
|
|
|
|
int StateMap::findConditional( Conditional *condition )
|
|
{
|
|
int i;
|
|
int j;
|
|
Conditional *c;
|
|
bool found;
|
|
|
|
|
|
// Check for the one special case where we don't want to merge the conditionals
|
|
|
|
if ( strcmp( condition->getName(), "CHANCE" ) == 0 )
|
|
return 0;
|
|
|
|
for( i = 1; i <= current_conditionals->NumObjects(); i++ )
|
|
{
|
|
c = current_conditionals->ObjectAt( i );
|
|
if ( ( c->getName() == condition->getName() ) && ( c->numParms() == condition->numParms() ) )
|
|
{
|
|
found = true;
|
|
for( j = 1; j <= c->numParms(); j++ )
|
|
{
|
|
if ( strcmp( c->getParm( j ), condition->getParm( j ) ) )
|
|
{
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( found )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int StateMap::addConditional( Conditional *condition )
|
|
{
|
|
int index;
|
|
index = current_conditionals->AddObject( condition );
|
|
|
|
return index;
|
|
}
|
|
|
|
Conditional *StateMap::getConditional( const char *name )
|
|
{
|
|
int i;
|
|
Conditional *c;
|
|
Condition<Class> *condition;
|
|
|
|
for( i = 1; i <= current_conditionals->NumObjects(); i++ )
|
|
{
|
|
c = current_conditionals->ObjectAt( i );
|
|
if ( !strcmp( c->getName(), name ) )
|
|
{
|
|
return c;
|
|
}
|
|
}
|
|
|
|
condition = getCondition( name );
|
|
|
|
c = new Conditional( *condition );
|
|
current_conditionals->AddObject( c );
|
|
|
|
return c;
|
|
}
|
|
|
|
State *StateMap::FindState( const char *name )
|
|
{
|
|
int i;
|
|
|
|
for( i = 1; i <= stateList.NumObjects(); i++ )
|
|
{
|
|
if ( !strcmp( stateList.ObjectAt( i )->getName(), name ) )
|
|
{
|
|
return stateList.ObjectAt( i );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
State *StateMap::FindGlobalState( const char *name )
|
|
{
|
|
for ( int i = 1; i <= globalStateList.NumObjects(); i++ )
|
|
{
|
|
if ( !strcmp( globalStateList.ObjectAt( i )->getName(), name ) )
|
|
{
|
|
return globalStateList.ObjectAt( i );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Caching statemaps
|
|
|
|
struct cached_statemap_t
|
|
{
|
|
StateMap *statemap;
|
|
Container<Conditional *> *conditionals;
|
|
};
|
|
|
|
struct cached_fuzzyengine_t
|
|
{
|
|
FuzzyEngine *fengine;
|
|
Container<Conditional *> *conditionals;
|
|
};
|
|
|
|
Container<cached_statemap_t> cached_statemaps;
|
|
Container<cached_fuzzyengine_t> cached_fuzzyengines;
|
|
|
|
StateMap *GetStatemap( const str &filename, Condition<Class> *conditions, Container<Conditional *> *conditionals,
|
|
qboolean reload, qboolean cache_only )
|
|
{
|
|
int i;
|
|
int j;
|
|
cached_statemap_t *cache = NULL;
|
|
cached_statemap_t new_cache;
|
|
qboolean found = false;
|
|
Conditional *new_conditional;
|
|
Conditional *old_conditional;
|
|
Condition<Class> *cond;
|
|
|
|
for( i = 1 ; i <= cached_statemaps.NumObjects() ; i++ )
|
|
{
|
|
cache = &cached_statemaps.ObjectAt( i );
|
|
|
|
if ( strcmp( cache->statemap->Filename(), filename.c_str() ) == 0 )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( found && reload )
|
|
{
|
|
delete cache->statemap;
|
|
delete cache->conditionals;
|
|
|
|
cache->conditionals = new Container<Conditional *>;
|
|
cache->statemap = new StateMap( filename, conditions, cache->conditionals );
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
new_cache.conditionals = new Container<Conditional *>;
|
|
new_cache.statemap = new StateMap( filename, conditions, new_cache.conditionals );
|
|
|
|
cached_statemaps.AddObject( new_cache );
|
|
|
|
cache = &new_cache;
|
|
}
|
|
|
|
// Copy conditionals over
|
|
|
|
if ( !cache_only )
|
|
{
|
|
for( i = 1 ; i <= cache->conditionals->NumObjects() ; i++ )
|
|
{
|
|
old_conditional = cache->conditionals->ObjectAt( i );
|
|
|
|
cond = cache->statemap->getCondition( old_conditional->condition.name );
|
|
|
|
new_conditional = new Conditional( *cond );
|
|
|
|
for( j = 1 ; j <= old_conditional->parmList.NumObjects() ; j++ )
|
|
{
|
|
new_conditional->parmList.AddObject( old_conditional->parmList.ObjectAt( j ) );
|
|
}
|
|
|
|
conditionals->AddObject( new_conditional );
|
|
}
|
|
}
|
|
|
|
return cache->statemap;
|
|
}
|
|
|
|
|
|
FuzzyEngine *GetFuzzyEngine( const str &filename, Condition<Class> *conditions, Container<Conditional *> *conditionals,
|
|
qboolean reload, qboolean cache_only )
|
|
{
|
|
int i;
|
|
int j;
|
|
cached_fuzzyengine_t *cache = NULL;
|
|
cached_fuzzyengine_t new_cache;
|
|
qboolean found = false;
|
|
Conditional *new_conditional;
|
|
Conditional *old_conditional;
|
|
Condition<Class> *cond;
|
|
|
|
for( i = 1 ; i <= cached_fuzzyengines.NumObjects() ; i++ )
|
|
{
|
|
cache = &cached_fuzzyengines.ObjectAt( i );
|
|
|
|
if ( strcmp( cache->fengine->Filename(), filename.c_str() ) == 0 )
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( found && reload )
|
|
{
|
|
delete cache->fengine;
|
|
delete cache->conditionals;
|
|
|
|
cache->conditionals = new Container<Conditional *>;
|
|
cache->fengine = new FuzzyEngine( filename, conditions, cache->conditionals );
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
new_cache.conditionals = new Container<Conditional *>;
|
|
new_cache.fengine = new FuzzyEngine( filename, conditions, new_cache.conditionals );
|
|
|
|
cached_fuzzyengines.AddObject( new_cache );
|
|
|
|
cache = &new_cache;
|
|
}
|
|
|
|
// Copy conditionals over
|
|
|
|
if ( !cache_only )
|
|
{
|
|
for( i = 1 ; i <= cache->conditionals->NumObjects() ; i++ )
|
|
{
|
|
old_conditional = cache->conditionals->ObjectAt( i );
|
|
|
|
cond = cache->fengine->getCondition( old_conditional->condition.name );
|
|
|
|
new_conditional = new Conditional( *cond );
|
|
|
|
for( j = 1 ; j <= old_conditional->parmList.NumObjects() ; j++ )
|
|
{
|
|
new_conditional->parmList.AddObject( old_conditional->parmList.ObjectAt( j ) );
|
|
}
|
|
|
|
conditionals->AddObject( new_conditional );
|
|
}
|
|
}
|
|
|
|
return cache->fengine;
|
|
}
|
|
|
|
void CacheStatemap( const str &filename, Condition<Class> *conditions )
|
|
{
|
|
GetStatemap( filename, conditions, NULL, false, true );
|
|
}
|
|
|
|
void CacheFuzzyEngine( const str &filename, Condition<Class> *conditions )
|
|
{
|
|
GetFuzzyEngine( filename , conditions, NULL, false, true );
|
|
}
|
|
|
|
void StateMap::GetAllAnims( Container<const char *> *c )
|
|
{
|
|
int i;
|
|
|
|
for( i = 1; i <= stateList.NumObjects(); i++ )
|
|
{
|
|
stateList.ObjectAt( i )->GetLegAnims( c );
|
|
stateList.ObjectAt( i )->GetTorsoAnims( c );
|
|
}
|
|
|
|
for( i = 1; i <= globalStateList.NumObjects(); i++ )
|
|
{
|
|
globalStateList.ObjectAt( i )->GetLegAnims( c );
|
|
globalStateList.ObjectAt( i )->GetTorsoAnims( c );
|
|
}
|
|
}
|
|
|
|
void ClearCachedStatemaps( void )
|
|
{
|
|
int i,j,num2;
|
|
cached_statemap_t *cache;
|
|
|
|
num2 = cached_statemaps.NumObjects();
|
|
|
|
for( i=num2 ; i>0; i-- )
|
|
{
|
|
cache = &cached_statemaps.ObjectAt( i );
|
|
|
|
delete cache->statemap;
|
|
|
|
int num = cache->conditionals->NumObjects();
|
|
for ( j=num; j>0; j-- )
|
|
{
|
|
Conditional *cond = cache->conditionals->ObjectAt( j );
|
|
delete cond;
|
|
}
|
|
delete cache->conditionals;
|
|
}
|
|
|
|
cached_statemaps.FreeObjectList();
|
|
}
|
|
|
|
void ClearCachedFuzzyEngines( void )
|
|
{
|
|
int i,j,num2;
|
|
cached_fuzzyengine_t *cache;
|
|
|
|
num2 = cached_fuzzyengines.NumObjects();
|
|
|
|
for( i=num2 ; i>0; i-- )
|
|
{
|
|
cache = &cached_fuzzyengines.ObjectAt( i );
|
|
|
|
delete cache->fengine;
|
|
|
|
int num = cache->conditionals->NumObjects();
|
|
for ( j=num; j>0; j-- )
|
|
{
|
|
Conditional *cond = cache->conditionals->ObjectAt( j );
|
|
delete cond;
|
|
}
|
|
delete cache->conditionals;
|
|
}
|
|
|
|
cached_fuzzyengines.FreeObjectList();
|
|
}
|
|
|
|
//======================================
|
|
// Fuzzy Engine Implementation
|
|
//======================================
|
|
FuzzyEngine::FuzzyEngine( const char *file_name, Condition<Class> *conditions, Container<Conditional *> *conditionals )
|
|
{
|
|
assert( file_name );
|
|
_filename = file_name;
|
|
|
|
this->_current_conditions = conditions;
|
|
this->_current_conditionals = conditionals;
|
|
|
|
ReadFuzzyVars(_filename);
|
|
}
|
|
|
|
void FuzzyEngine::ReadFuzzyVars( const char *filename )
|
|
{
|
|
str cmd;
|
|
str fuzzyVarName;
|
|
Script script;
|
|
FuzzyVar *fuzzyVar;
|
|
|
|
script.LoadFile( filename );
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
cmd = script.GetToken( true );
|
|
if ( !cmd.icmp( "fuzzyvar" ) )
|
|
{
|
|
fuzzyVarName = script.GetToken( false );
|
|
fuzzyVar = FindFuzzyVar( fuzzyVarName.c_str() );
|
|
if ( fuzzyVar )
|
|
{
|
|
_varList.RemoveObject( fuzzyVar );
|
|
gi.Error( ERR_DROP, "%s: Duplicate definition of FuzzyVar '%s' on line %d.\n", filename, fuzzyVarName.c_str(), script.GetLineNumber() );
|
|
}
|
|
// parse the state even if we already have it defined
|
|
fuzzyVar = new FuzzyVar( fuzzyVarName.c_str(), script, *this );
|
|
_varList.AddObject( fuzzyVar );
|
|
}
|
|
else
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown command '%s' on line %d.\n", script.Filename(), cmd.c_str(), script.GetLineNumber() );
|
|
}
|
|
}
|
|
}
|
|
|
|
FuzzyVar *FuzzyEngine::FindFuzzyVar( const char *name )
|
|
{
|
|
int i;
|
|
|
|
for( i = 1; i <= _varList.NumObjects(); i++ )
|
|
{
|
|
if ( !strcmp( _varList.ObjectAt( i )->getName(), name ) )
|
|
{
|
|
return _varList.ObjectAt( i );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int FuzzyEngine::addConditional( Conditional *condition )
|
|
{
|
|
int index;
|
|
index = _current_conditionals->AddObject( condition );
|
|
|
|
return index;
|
|
}
|
|
|
|
int FuzzyEngine::findConditional( Conditional *condition )
|
|
{
|
|
int i;
|
|
int j;
|
|
Conditional *c;
|
|
bool found;
|
|
|
|
|
|
// Check for the one special case where we don't want to merge the conditionals
|
|
|
|
if ( strcmp( condition->getName(), "CHANCE" ) == 0 )
|
|
return 0;
|
|
|
|
for( i = 1; i <= _current_conditionals->NumObjects(); i++ )
|
|
{
|
|
c = _current_conditionals->ObjectAt( i );
|
|
if ( ( c->getName() == condition->getName() ) && ( c->numParms() == condition->numParms() ) )
|
|
{
|
|
found = true;
|
|
for( j = 1; j <= c->numParms(); j++ )
|
|
{
|
|
if ( strcmp( c->getParm( j ), condition->getParm( j ) ) )
|
|
{
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( found )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *FuzzyEngine::Filename()
|
|
{
|
|
return _filename.c_str();
|
|
}
|
|
|
|
Condition<Class> *FuzzyEngine::getCondition( const char *name )
|
|
{
|
|
Condition<Class> *c;
|
|
|
|
if ( _current_conditions )
|
|
{
|
|
for( c = _current_conditions; c->name; c++ )
|
|
{
|
|
if ( !strcmp( c->name, name ) )
|
|
{
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//======================================
|
|
// Fuzzy Var Implementation
|
|
//======================================
|
|
FuzzyVar::FuzzyVar( const char *varname, Script &script, FuzzyEngine &engine ) :
|
|
_fuzzyEngine( engine )
|
|
|
|
{
|
|
str cmd;
|
|
|
|
_name = varname;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
cmd = script.GetToken( true );
|
|
if ( !cmd.icmp( "evaluations" ) )
|
|
{
|
|
readEvaluations( script );
|
|
}
|
|
else if ( !cmd.icmp( "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Unknown command '%s' on line %d.\n", script.Filename(), cmd.c_str(), script.GetLineNumber() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FuzzyVar::readEvaluations( Script &script )
|
|
{
|
|
str token;
|
|
|
|
if ( !script.TokenAvailable( true ) || Q_stricmp( script.GetToken( true ), "{" ) )
|
|
{
|
|
gi.Error( ERR_DROP, "%s: Expecting '{' on line %d.\n", script.Filename(), script.GetLineNumber() );
|
|
}
|
|
|
|
while( script.TokenAvailable( true ) )
|
|
{
|
|
token = script.GetToken( true );
|
|
if ( !Q_stricmp( token.c_str(), "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
script.UnGetToken();
|
|
_evaluations.AddObject( Expression( script, *this ) );
|
|
}
|
|
}
|
|
|
|
float FuzzyVar::Evaluate( Entity &ent, Container<Conditional *> *sent_conditionals )
|
|
{
|
|
int i;
|
|
Expression *exp;
|
|
float points;
|
|
int index;
|
|
|
|
points = 0.0f;
|
|
|
|
for( i = 1; i <= _condition_indexes.NumObjects(); i++ )
|
|
{
|
|
index = _condition_indexes.ObjectAt( i );
|
|
sent_conditionals->ObjectAt( index )->clearCheck();
|
|
}
|
|
|
|
for( i = 1; i <= _evaluations.NumObjects(); i++ )
|
|
{
|
|
exp = &_evaluations.ObjectAt( i );
|
|
if ( exp->getResult( *this, ent, sent_conditionals ) )
|
|
{
|
|
points += exp->getPoints();
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
int FuzzyVar::addCondition( const char *name, Script &script )
|
|
{
|
|
Conditional *condition;
|
|
Condition<Class> *cond;
|
|
int index;
|
|
|
|
str token;
|
|
|
|
condition = NULL;
|
|
cond = _fuzzyEngine.getCondition( name );
|
|
|
|
if ( !cond )
|
|
return 0;
|
|
|
|
|
|
condition = new Conditional( *cond );
|
|
|
|
// Get the paramaters
|
|
// Legacy Format - Parameters use ""
|
|
while ( script.TokenAvailable( false ) && script.AtString( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
condition->addParm( token );
|
|
}
|
|
|
|
// New Format - Parameters use ( x , y , z ) format
|
|
while ( script.TokenAvailable( false ) && script.AtOpenParen( false ) )
|
|
{
|
|
// Need to burn off first "(" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "(" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
|
|
while ( !script.AtCloseParen( false ) )
|
|
{
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), "," ) )
|
|
{
|
|
condition->addParm( token );
|
|
}
|
|
}
|
|
|
|
// Need to burn off last ")" - but check it to be sure
|
|
token = script.GetToken( false );
|
|
if ( Q_stricmp( token.c_str(), ")" ) )
|
|
{
|
|
script.UnGetToken();
|
|
}
|
|
}
|
|
|
|
// only add a new conditional if a similar one doesn't exist
|
|
index = _fuzzyEngine.findConditional( condition );
|
|
|
|
if ( index )
|
|
{
|
|
// delete the one we just made
|
|
delete condition;
|
|
}
|
|
else
|
|
{
|
|
index = _fuzzyEngine.addConditional( condition );
|
|
}
|
|
|
|
_condition_indexes.AddUniqueObject( index );
|
|
|
|
return index;
|
|
}
|
|
|
|
const char *FuzzyVar::getName()
|
|
{
|
|
return _name.c_str();
|
|
}
|