1169 lines
28 KiB
C++
1169 lines
28 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /EF2/Code/DLLs/game/RageAI.cpp $
|
|
// $Revision:: 51 $
|
|
// $Author:: Singlis $
|
|
// $Date:: 9/26/03 2:36p $
|
|
//
|
|
// Copyright (C) 2001 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
//
|
|
// DESCRIPTION:
|
|
// The Rage AI System, will house several components that will make the AI function
|
|
// such as the Strategos, Personality, etc...
|
|
//
|
|
#include "_pch_cpp.h"
|
|
//#include "g_local.h"
|
|
#include "RageAI.h"
|
|
#include "player.h"
|
|
|
|
void Strategos::DoArchive( Archiver &arc , Actor *actor )
|
|
{
|
|
Archive( arc );
|
|
}
|
|
|
|
void Strategos::Archive( Archiver &arc )
|
|
{
|
|
arc.ArchiveFloat( &_sightBasedHate );
|
|
arc.ArchiveFloat( &_nextEvaluateTime );
|
|
arc.ArchiveFloat( &_evaluateInterval );
|
|
}
|
|
|
|
//=================================================================================
|
|
// Default Strategos Implementation
|
|
//=================================================================================
|
|
|
|
//
|
|
// Name: DefaultStrategos()
|
|
// Parameters: None
|
|
// Description: Constructor
|
|
//
|
|
DefaultStrategos::DefaultStrategos()
|
|
{
|
|
// Should always use other constructor
|
|
gi.Error( ERR_FATAL, "DefaultStrategos::DefaultStrategos -- Default Constructor Called" );
|
|
}
|
|
|
|
|
|
//
|
|
// Name: DefaultStrategos()
|
|
// Parameters: Actor *actor
|
|
// Description: Constructor
|
|
//
|
|
DefaultStrategos::DefaultStrategos( Actor *actor )
|
|
: _checkInConeDistMax( 128 )
|
|
{
|
|
//Initialize our Actor
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "DefaultStrategos::DefaultStrategos -- actor is NULL" );
|
|
|
|
SetSightBasedHate( DEFAULT_SIGHT_BASED_HATE );
|
|
SetEvaluateInterval( DEFAULT_EVALUATE_INTERVAL );
|
|
SetNextEvaluateTime( 0.0f );
|
|
}
|
|
|
|
//
|
|
// Name: ~DefaultStrategos
|
|
// Parameters: None
|
|
// Description: Destructor
|
|
//
|
|
DefaultStrategos::~DefaultStrategos()
|
|
{
|
|
|
|
}
|
|
|
|
//
|
|
// Name: NotifySightStatusChanged
|
|
// Parameters: Entity *enemy
|
|
// qboolean canSee
|
|
// Description: Adjusts hate based on the canSee value
|
|
//
|
|
void DefaultStrategos::NotifySightStatusChanged( Entity *enemy , qboolean canSee )
|
|
{
|
|
//if ( canSee )
|
|
// act->enemyManager->AdjustHate( enemy , GetSightBasedHate() );
|
|
//else
|
|
// act->enemyManager->AdjustHate( enemy , -GetSightBasedHate() );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: NotifyDamageChanged
|
|
// Parameters: Entity *enemy
|
|
// float damage
|
|
// Description: Adjusts hate based on the damage value
|
|
//
|
|
void DefaultStrategos::NotifyDamageChanged( Entity *enemy , float damage )
|
|
{
|
|
//act->enemyManager->AdjustHate( enemy , damage );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: Evaluate()
|
|
// Parameters: None
|
|
// Description: Evaluates the world to determine enemy and strategy
|
|
//
|
|
void DefaultStrategos::Evaluate()
|
|
{
|
|
// Here we will be doing all sorts of evaluation, however, there right now,
|
|
// all we have is enemy stuff, so we'll just use that
|
|
_EvaluateWorld();
|
|
//_EvaluateEnemies();
|
|
_EvaluatePackages();
|
|
}
|
|
|
|
|
|
//
|
|
// Name: _EvaluateEnemies
|
|
// Parameters: None
|
|
// Description: Sets the currentEnemy
|
|
//
|
|
void DefaultStrategos::_EvaluateEnemies()
|
|
{
|
|
if ( act->enemyManager->GetCurrentEnemy() && (level.time < GetNextEvaluateTime() || act->enemyManager->IsLockedOnCurrentEnemy() ))
|
|
return;
|
|
|
|
// For right now, all we're going to do is set our enemy to the enemy
|
|
// Highest on the hate list. I plan to add more sophisticated heuristics later
|
|
// but right now, I want to make sure the system is up and running.
|
|
act->enemyManager->FindHighestHateEnemy();
|
|
SetNextEvaluateTime( level.time + GetEvaluateInterval() );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: _EvaluatePackages
|
|
// Parameters: None
|
|
// Description: Determines which behavior package to execute
|
|
//
|
|
void DefaultStrategos::_EvaluatePackages()
|
|
{
|
|
|
|
if ( !act->fuzzyEngine || !act->fuzzyEngine_active )
|
|
return;
|
|
|
|
if ( PackageList.NumObjects() < 1 )
|
|
return;
|
|
|
|
act->packageManager->EvaluatePackages(act->fuzzyEngine );
|
|
|
|
int newPackageIndex;
|
|
newPackageIndex = act->packageManager->GetHighestScoringPackage();
|
|
|
|
BehaviorPackageType_t* package;
|
|
|
|
package = PackageList.ObjectAt( newPackageIndex );
|
|
|
|
// If we have a package AND our current statemap name is NOT the same as our selected package name
|
|
if ( package && Q_stricmp( act->statemap_name.c_str(), package->stateFile.c_str() ) )
|
|
{
|
|
Event *event;
|
|
|
|
event = new Event( EV_Actor_Statemap );
|
|
event->AddString( package->stateFile.c_str() ); //FileName
|
|
event->AddString( "START" ); //IdleState
|
|
event->AddInteger( 0 ); //Loading( false )
|
|
event->AddInteger( 1 ); //packageChanged ( true )
|
|
act->ProcessEvent( event );
|
|
|
|
//act->packageManager->SetLastExecutionTime(newPackageIndex);
|
|
act->packageManager->UpdateCurrentPackageIndex( newPackageIndex );
|
|
act->statemap_name = package->stateFile;
|
|
}
|
|
}
|
|
|
|
|
|
void DefaultStrategos::_EvaluateWorld()
|
|
{
|
|
_CheckForInTheWay();
|
|
_CheckForInConeOfFire();
|
|
}
|
|
|
|
void DefaultStrategos::_CheckForInConeOfFire()
|
|
{
|
|
Vector dir;
|
|
Vector check;
|
|
float relativeYaw;
|
|
float distance;
|
|
Sentient *teammate;
|
|
Player *player;
|
|
Actor *actor;
|
|
|
|
player = NULL;
|
|
teammate = NULL;
|
|
|
|
|
|
// Should really loop through all teammates, but I'm most concerned about the
|
|
// player right now.
|
|
for ( int i = 1 ; i <= TeamMateList.NumObjects() ; i++ )
|
|
{
|
|
teammate = TeamMateList.ObjectAt( i );
|
|
|
|
if ( !teammate || teammate->entnum == act->entnum )
|
|
continue;
|
|
|
|
dir = act->origin - teammate->origin;
|
|
//dir = teammate->origin - act->origin;
|
|
distance = dir.length();
|
|
|
|
if ( distance < _checkInConeDistMax )
|
|
{
|
|
dir = dir.toAngles();
|
|
|
|
if ( teammate->isSubclassOf( Player ) )
|
|
{
|
|
_checkYawMin = -5.0f;
|
|
_checkYawMax = 5.0f;
|
|
|
|
|
|
player = (Player*)teammate;
|
|
check = player->GetVAngles();
|
|
}
|
|
else
|
|
{
|
|
|
|
_checkYawMin = -5.0f;
|
|
_checkYawMax = 5.0f;
|
|
|
|
actor = (Actor*)teammate;
|
|
check = actor->movementSubsystem->getAnimDir();
|
|
check = check.toAngles();
|
|
}
|
|
|
|
|
|
relativeYaw = AngleNormalize180( AngleNormalize180(check[YAW]) - AngleNormalize180(dir[YAW]) );
|
|
|
|
if ( (relativeYaw <= _checkYawMax) && (relativeYaw >= _checkYawMin ) )
|
|
{
|
|
act->SetActorFlag( ACTOR_FLAG_IN_CONE_OF_FIRE , true );
|
|
|
|
if ( teammate->isSubclassOf(Player) && distance <= 128 )
|
|
act->SetActorFlag( ACTOR_FLAG_IN_PLAYER_CONE_OF_FIRE , true );
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
act->SetActorFlag( ACTOR_FLAG_IN_CONE_OF_FIRE , false );
|
|
act->SetActorFlag( ACTOR_FLAG_IN_PLAYER_CONE_OF_FIRE , false );
|
|
|
|
}
|
|
|
|
void DefaultStrategos::_CheckForInTheWay()
|
|
{
|
|
Player *player;
|
|
Vector playerToSelf;
|
|
Vector playerVelocity;
|
|
Vector dir;
|
|
Vector check;
|
|
float relativeYaw;
|
|
float dist;
|
|
|
|
player = GetPlayer( 0 );
|
|
|
|
if (!player)
|
|
return;
|
|
|
|
playerToSelf = act->origin - player->origin;
|
|
dist = playerToSelf.length();
|
|
|
|
playerVelocity = player->velocity;
|
|
|
|
float speed;
|
|
speed = playerVelocity.length();
|
|
speed = speed * .75;
|
|
|
|
if ( dist > 200 )
|
|
return;
|
|
|
|
if ( dist > speed )
|
|
return;
|
|
|
|
if ( DotProduct( playerVelocity , playerToSelf ) <= 0 )
|
|
return;
|
|
|
|
if ( act->EntityHasFireType( player, FT_BULLET ) || act->EntityHasFireType( player, FT_PROJECTILE ) )
|
|
{
|
|
_checkYawMin = -30.0f;
|
|
_checkYawMax = 30.0f;
|
|
|
|
check = player->GetVAngles();
|
|
dir = playerToSelf.toAngles();
|
|
|
|
relativeYaw = AngleNormalize180( AngleNormalize180(check[YAW]) - AngleNormalize180(dir[YAW]) );
|
|
|
|
if ( (relativeYaw <= _checkYawMax) && (relativeYaw >= _checkYawMin ) )
|
|
{
|
|
act->AddStateFlag( STATE_FLAG_TOUCHED_BY_PLAYER );
|
|
act->AddStateFlag( STATE_FLAG_IN_THE_WAY );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
float DefaultStrategos::GetCheckYawMax()
|
|
{
|
|
return _checkYawMax;
|
|
}
|
|
|
|
float DefaultStrategos::GetCheckYawMin()
|
|
{
|
|
return _checkYawMin;
|
|
}
|
|
|
|
float DefaultStrategos::GetCheckInConeDistMax()
|
|
{
|
|
return _checkInConeDistMax;
|
|
}
|
|
|
|
void DefaultStrategos::SetCheckInConeDistMax( float distance )
|
|
{
|
|
assert( distance >= 0 );
|
|
_checkInConeDistMax = distance;
|
|
}
|
|
|
|
//
|
|
// Name: Attack
|
|
// Parameters: Entity *enemy
|
|
// Description: Sets the current enemy, and locks on to it
|
|
//
|
|
void DefaultStrategos::Attack( Entity *enemy )
|
|
{
|
|
act->enemyManager->SetCurrentEnemy( enemy );
|
|
act->enemyManager->LockOnCurrentEnemy( true );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
// Name: SetBehaviorPackage()
|
|
// Class: DefaultStrategos
|
|
//
|
|
// Description: Sets the actor's statemap to the specified behavior package
|
|
//
|
|
// Parameters: const str &packageName
|
|
//
|
|
// Returns: None
|
|
//--------------------------------------------------------------
|
|
void DefaultStrategos::SetBehaviorPackage( const str &packageName )
|
|
{
|
|
int index;
|
|
BehaviorPackageType_t* package;
|
|
|
|
index = act->packageManager->GetPackageIndex( packageName );
|
|
if ( index == -1 )
|
|
{
|
|
assert ( index != -1 );
|
|
gi.WDPrintf( "Actor %s, Does not have package %s registered\n" , act->targetname.c_str() , packageName.c_str() );
|
|
}
|
|
|
|
|
|
package = PackageList.ObjectAt( index );
|
|
|
|
// If we have a package AND our current statemap name is NOT the same as our selected package name
|
|
if ( package && stricmp( act->statemap_name.c_str(), package->stateFile.c_str() ) )
|
|
{
|
|
Event *event;
|
|
|
|
event = new Event( EV_Actor_Statemap );
|
|
event->AddString( package->stateFile.c_str() ); //FileName
|
|
event->AddString( "START" ); //IdleState
|
|
event->AddInteger( 0 ); //Loading( false )
|
|
event->AddInteger( 1 ); //packageChanged ( true )
|
|
act->ProcessEvent( event );
|
|
act->packageManager->UpdateCurrentPackageIndex( index );
|
|
act->statemap_name = package->stateFile;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Name: DoArchive
|
|
// Parameters: Archiver &arc
|
|
// Actor *actor
|
|
// Description: Sets the Actor pointer during archiving
|
|
//
|
|
void DefaultStrategos::DoArchive( Archiver &arc , Actor *actor )
|
|
{
|
|
//Initialize our Actor
|
|
Archive( arc );
|
|
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "DefaultStrategos::DoArchive -- actor is NULL" );
|
|
|
|
//SetSightBasedHate( DEFAULT_SIGHT_BASED_HATE );
|
|
//SetEvaluateInterval( DEFAULT_EVALUATE_INTERVAL );
|
|
//SetNextEvaluateTime( 0.0f );
|
|
}
|
|
|
|
void DefaultStrategos::Archive( Archiver &arc )
|
|
{
|
|
Strategos::Archive( arc );
|
|
|
|
arc.ArchiveFloat( &_checkYawMin );
|
|
arc.ArchiveFloat( &_checkYawMax );
|
|
arc.ArchiveFloat( &_checkInConeDistMax );
|
|
}
|
|
|
|
|
|
//=================================================================================
|
|
// PackageManager Implementation
|
|
//=================================================================================
|
|
|
|
|
|
//
|
|
// Name: PackageManager
|
|
// Parameters: None
|
|
// Description: Constructor
|
|
//
|
|
PackageManager::PackageManager()
|
|
{
|
|
// Should always use other constructor
|
|
gi.Error( ERR_FATAL, "PackageManager::PackageManager -- Default Constructor Called" );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: PackageManager
|
|
// Parameters: Actor *actor
|
|
// Description: Constructor
|
|
//
|
|
PackageManager::PackageManager( Actor *actor )
|
|
{
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "PackageManager::PackageManager -- actor is NULL" );
|
|
|
|
_currentPackageIndex = -1;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: ~PackageManager
|
|
// Parameters: None
|
|
// Description: Destructor
|
|
//
|
|
PackageManager::~PackageManager()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: RegisterPackage
|
|
// Parameters: str &packageName
|
|
// Description: Adds the package to the _BehaviorPackages container
|
|
//
|
|
void PackageManager::RegisterPackage( const str &packageName )
|
|
{
|
|
BehaviorPackageEntry_t pEntry;
|
|
BehaviorPackageType_t *package;
|
|
BehaviorPackageEntry_t *checkEntry;
|
|
|
|
for ( int i = 1 ; i <= PackageList.NumObjects() ; i++ )
|
|
{
|
|
package = PackageList.ObjectAt( i );
|
|
|
|
if ( !Q_stricmp( packageName.c_str() , package->packageName.c_str() ) )
|
|
{
|
|
// We have a match, let's check to make sure we're not doubling up
|
|
for ( int j = 1 ; j <= _BehaviorPackages.NumObjects() ; j++ )
|
|
{
|
|
checkEntry = &_BehaviorPackages.ObjectAt( j );
|
|
|
|
if ( checkEntry->packageIndex == i )
|
|
return;
|
|
}
|
|
|
|
// We don't have a match, so lets add the package
|
|
pEntry.currentScore = 0;
|
|
pEntry.lastScore = 0;
|
|
pEntry.lastTimeExecuted = 0.0f;
|
|
pEntry.priority = 0.001f;
|
|
pEntry.packageIndex = i;
|
|
|
|
_BehaviorPackages.AddObject( pEntry );
|
|
|
|
// Make sure state machine gets cached properly
|
|
|
|
CacheResource( package->stateFile, act );
|
|
G_CacheStateMachineAnims( act, package->stateFile );
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Name: UnregisterPackage
|
|
// Parameters: str &packageName
|
|
// Description: Removes the package from the _BehaviorPackages container
|
|
//
|
|
void PackageManager::UnregisterPackage( const str &packageName )
|
|
{
|
|
BehaviorPackageEntry_t pEntry;
|
|
BehaviorPackageType_t *package;
|
|
int index = 0;
|
|
int i;
|
|
|
|
for ( i = 1 ; i <= PackageList.NumObjects() ; i++ )
|
|
{
|
|
package = PackageList.ObjectAt( i );
|
|
if ( !Q_stricmp( packageName.c_str() , package->packageName.c_str() ) )
|
|
index = i;
|
|
}
|
|
|
|
for ( i = _BehaviorPackages.NumObjects() ; i > 0 ; i-- )
|
|
{
|
|
pEntry = _BehaviorPackages.ObjectAt( i );
|
|
if ( pEntry.packageIndex == index )
|
|
_BehaviorPackages.RemoveObjectAt( i );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Name: GetHighestScoringPackage
|
|
// Parameters: None
|
|
// Description: Returns the index of the highest scoring package
|
|
//
|
|
int PackageManager::GetHighestScoringPackage()
|
|
{
|
|
BehaviorPackageEntry_t pEntry;
|
|
float points;
|
|
int packageIndex = -1;
|
|
|
|
points = 0;
|
|
|
|
for ( int i = 1 ; i <= _BehaviorPackages.NumObjects() ; i++ )
|
|
{
|
|
pEntry = _BehaviorPackages.ObjectAt( i );
|
|
|
|
if ( pEntry.currentScore > points )
|
|
{
|
|
points = pEntry.currentScore;
|
|
packageIndex = pEntry.packageIndex;
|
|
}
|
|
}
|
|
|
|
return packageIndex;
|
|
}
|
|
|
|
|
|
int PackageManager::GetCurrentFVarIndex()
|
|
{
|
|
return _currentFVarIndex;
|
|
}
|
|
|
|
float PackageManager::GetCurrentFVarLastExecuteTime()
|
|
{
|
|
return _currentFVarLastExecuteTime;
|
|
}
|
|
|
|
//
|
|
// Name: EvaluatePackages
|
|
// Parameters: FuzzyEngine *fEngine
|
|
// Description: Runs the evaluations in the fuzzyengine to score the behavior packages
|
|
//
|
|
void PackageManager::EvaluatePackages( FuzzyEngine *fEngine )
|
|
{
|
|
/*
|
|
Will likely need to pass in the _fuzzyEngine to here.
|
|
We then use the packageIndex to index into the globalpackagelist
|
|
to get the string name, we then use the string name and the
|
|
_fuzzyEngine to get the correct _fuzzyVar. We then fuzzyVar->evaluate()
|
|
assigning the points as necessary
|
|
|
|
_fuzzyVar->evaluate keeps a running total of all the points accumulated and after
|
|
all the evaluations are complete, it returns the total
|
|
*/
|
|
|
|
FuzzyVar *currentFVar;
|
|
BehaviorPackageEntry_t *currentEntry;
|
|
BehaviorPackageType_t *package;
|
|
|
|
for ( int i = 1 ; i <= _BehaviorPackages.NumObjects() ; i++ )
|
|
{
|
|
currentEntry = &_BehaviorPackages.ObjectAt( i );
|
|
|
|
_currentFVarIndex = currentEntry->packageIndex;
|
|
_currentFVarLastExecuteTime = currentEntry->lastTimeExecuted;
|
|
|
|
package = PackageList.ObjectAt( _currentFVarIndex );
|
|
|
|
|
|
currentFVar = fEngine->FindFuzzyVar( package->packageName.c_str() );
|
|
|
|
if ( currentFVar )
|
|
{
|
|
currentEntry->lastScore = currentEntry->currentScore;
|
|
currentEntry->currentScore = currentFVar->Evaluate( *act , &act->fuzzy_conditionals );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void PackageManager::SetLastExecutionTime(int packageIndex)
|
|
{
|
|
BehaviorPackageEntry_t *currentEntry;
|
|
|
|
|
|
for ( int i = 1 ; i <= _BehaviorPackages.NumObjects() ; i++ )
|
|
{
|
|
currentEntry = &_BehaviorPackages.ObjectAt( i );
|
|
|
|
if ( currentEntry->packageIndex == packageIndex )
|
|
{
|
|
currentEntry->lastTimeExecuted = level.time;
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void PackageManager::UpdateCurrentPackageIndex( int packageIndex )
|
|
{
|
|
if ( _currentPackageIndex > -1 )
|
|
SetLastExecutionTime( _currentPackageIndex );
|
|
|
|
_currentPackageIndex = packageIndex;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
// Name: GetCurrentPackageName()
|
|
// Class: PackageManager
|
|
//
|
|
// Description: Gets the name of the current behavior package
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: str
|
|
//--------------------------------------------------------------
|
|
str PackageManager::GetCurrentPackageName()
|
|
{
|
|
BehaviorPackageType_t* package;
|
|
package = PackageList.ObjectAt( _currentPackageIndex );
|
|
|
|
return package->packageName;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Name: GetPackageIndex()
|
|
// Class: PackageManager
|
|
//
|
|
// Description: Returns the package index for the packageName.
|
|
// We iterate through our list of registered behavior
|
|
// packages because: First, if the packages are registered
|
|
// we know that the actor can use them. Secondly, by only
|
|
// having to be concerned about the packages we have specifically
|
|
// registered we can cut way down on string compares.
|
|
//
|
|
// Parameters: const str &packageName
|
|
//
|
|
// Returns: int
|
|
//--------------------------------------------------------------
|
|
int PackageManager::GetPackageIndex( const str &packageName )
|
|
{
|
|
BehaviorPackageEntry_t pEntry;
|
|
BehaviorPackageType_t *package;
|
|
|
|
// We do this so that we only have to do str compares on behavior
|
|
// packages we have registered -- Not the whole Package List
|
|
for ( int i = 1 ; i <= _BehaviorPackages.NumObjects() ; i++ )
|
|
{
|
|
pEntry = _BehaviorPackages.ObjectAt( i );
|
|
package = PackageList.ObjectAt( pEntry.packageIndex );
|
|
|
|
if ( !stricmp( package->packageName.c_str() , packageName.c_str() ) )
|
|
return pEntry.packageIndex;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Name: DoArchive
|
|
// Parameters: Archiver &arc
|
|
// Actor *actor
|
|
// Description: Sets the Actor pointer during archiving
|
|
//
|
|
void PackageManager::DoArchive( Archiver &arc , Actor *actor )
|
|
{
|
|
int i;
|
|
BehaviorPackageEntry_t *pEntry;
|
|
BehaviorPackageType_t *package;
|
|
int numPackages;
|
|
str packageName;
|
|
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "PackageManager::DoArchive -- actor is NULL" );
|
|
|
|
if ( arc.Loading() )
|
|
{
|
|
arc.ArchiveInteger( &numPackages );
|
|
|
|
for ( i = 1 ; i <= numPackages ; i++ )
|
|
{
|
|
arc.ArchiveString( &packageName );
|
|
|
|
RegisterPackage( packageName );
|
|
|
|
// The package we just added should always be the last one
|
|
|
|
pEntry = &_BehaviorPackages.ObjectAt( _BehaviorPackages.NumObjects() );
|
|
|
|
arc.ArchiveFloat( &pEntry->currentScore );
|
|
arc.ArchiveFloat( &pEntry->lastScore );
|
|
arc.ArchiveFloat( &pEntry->lastTimeExecuted );
|
|
arc.ArchiveFloat( &pEntry->priority );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
numPackages = _BehaviorPackages.NumObjects();
|
|
arc.ArchiveInteger( &numPackages );
|
|
|
|
for ( i = 1 ; i <= _BehaviorPackages.NumObjects() ; i++ )
|
|
{
|
|
pEntry = &_BehaviorPackages.ObjectAt( i );
|
|
|
|
package = PackageList.ObjectAt( pEntry->packageIndex );
|
|
|
|
arc.ArchiveString( &package->packageName );
|
|
|
|
arc.ArchiveFloat( &pEntry->currentScore );
|
|
arc.ArchiveFloat( &pEntry->lastScore );
|
|
arc.ArchiveFloat( &pEntry->lastTimeExecuted );
|
|
arc.ArchiveFloat( &pEntry->priority );
|
|
}
|
|
}
|
|
|
|
arc.ArchiveInteger( &_currentFVarIndex );
|
|
arc.ArchiveFloat( &_currentFVarLastExecuteTime );
|
|
arc.ArchiveInteger( &_currentPackageIndex );
|
|
}
|
|
|
|
|
|
|
|
//=================================================================================
|
|
// Personality Implementation
|
|
//=================================================================================
|
|
|
|
|
|
//
|
|
// Name: Personality()
|
|
// Parameters: None
|
|
// Description: Constructor
|
|
//
|
|
Personality::Personality()
|
|
{
|
|
// Should always use other constructor
|
|
gi.Error( ERR_FATAL, "Personality::Personality -- Default Constructor Called" );
|
|
}
|
|
|
|
|
|
//
|
|
// Name: Personality()
|
|
// Parameters: Actor *actor
|
|
// Description: Constructor
|
|
//
|
|
Personality::Personality( Actor *actor )
|
|
{
|
|
//Initialize our Actor
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "Personality::Personality -- actor is NULL" );
|
|
|
|
_aggressiveness = 0.0f;
|
|
_talkiness = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Name: ~Personality()
|
|
// Parameters: None
|
|
// Description: Destructor
|
|
//
|
|
Personality::~Personality()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Personality::SetAggressiveness( float aggressiveness )
|
|
{
|
|
_aggressiveness = _clampValue( aggressiveness );
|
|
}
|
|
|
|
float Personality::GetAggressiveness()
|
|
{
|
|
return _aggressiveness;
|
|
}
|
|
|
|
|
|
void Personality::SetTalkiness( float talkiness )
|
|
{
|
|
_talkiness = _clampValue( talkiness );
|
|
}
|
|
|
|
float Personality::GetTalkiness()
|
|
{
|
|
return _talkiness;
|
|
}
|
|
|
|
float Personality::_clampValue( float value )
|
|
{
|
|
if ( value > 1.0f )
|
|
value = 1.0f;
|
|
|
|
if ( value < 0.0f )
|
|
value = 0.0f;
|
|
|
|
return value;
|
|
}
|
|
|
|
float Personality::GetTendency( const str &tendencyName )
|
|
{
|
|
Tendency_t tendency;
|
|
|
|
// First Make sure we don't already have this tendency
|
|
for ( int i = 1 ; i <= _Tendencies.NumObjects() ; i++ )
|
|
{
|
|
tendency = _Tendencies.ObjectAt( i );
|
|
|
|
if ( !Q_stricmp( tendencyName.c_str() , tendency.tendencyName.c_str() ) )
|
|
{
|
|
return tendency.tendencyValue;
|
|
}
|
|
|
|
}
|
|
|
|
// gi.WDPrintf( "Actor %s, Entnum %d , has no tendency named %s", act->TargetName(), act->entnum , tendencyName.c_str() );
|
|
return 0.0f;
|
|
}
|
|
|
|
//
|
|
// Name: DoArchive
|
|
// Parameters: Archiver &arc
|
|
// Actor *actor
|
|
// Description: Sets the Actor pointer during archiving
|
|
//
|
|
void Personality::DoArchive( Archiver &arc , Actor *actor )
|
|
{
|
|
Archive( arc );
|
|
if ( actor )
|
|
act = actor;
|
|
else
|
|
gi.Error( ERR_FATAL, "Personality::DoArchive -- actor is NULL" );
|
|
|
|
}
|
|
|
|
void Personality::Archive( Archiver &arc )
|
|
{
|
|
int i;
|
|
int j;
|
|
int num;
|
|
str packageName;
|
|
BehaviorPackageType_t *package;
|
|
PackageTendency_t *tendency;
|
|
PackageTendency_t tempTendency;
|
|
|
|
Tendency_t tempGeneralTendency;
|
|
Tendency_t *generalTendency;
|
|
|
|
tempTendency.packageIndex = 0;
|
|
tempTendency.tendency = 0.0f;
|
|
tempTendency.lastTendencyCheck = 0.0f;
|
|
|
|
arc.ArchiveFloat( &_aggressiveness );
|
|
arc.ArchiveFloat( &_talkiness );
|
|
|
|
arc.ArchiveFloat( &_anger );
|
|
arc.ArchiveFloat( &_fear );
|
|
|
|
if ( arc.Saving() )
|
|
{
|
|
num = _PackageTendencies.NumObjects();
|
|
|
|
arc.ArchiveInteger( &num );
|
|
|
|
for ( i = 1 ; i <= num ; i++ )
|
|
{
|
|
tendency = &_PackageTendencies.ObjectAt( i );
|
|
|
|
package = PackageList.ObjectAt( tendency->packageIndex );
|
|
|
|
arc.ArchiveString( &package->packageName );
|
|
arc.ArchiveFloat( &tendency->tendency );
|
|
arc.ArchiveFloat( &tendency->lastTendencyCheck );
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arc.ArchiveInteger( &num );
|
|
|
|
_PackageTendencies.ClearObjectList();
|
|
_PackageTendencies.Resize( num );
|
|
|
|
for ( i = 1 ; i <= num ; i++ )
|
|
{
|
|
_PackageTendencies.AddObject( tempTendency );
|
|
|
|
tendency = &_PackageTendencies.ObjectAt( i );
|
|
|
|
arc.ArchiveString( &packageName );
|
|
|
|
// Find index
|
|
|
|
tendency->packageIndex = -1;
|
|
|
|
for ( j = 1 ; j <= PackageList.NumObjects() ; j++ )
|
|
{
|
|
package = PackageList.ObjectAt( j );
|
|
|
|
if ( stricmp( packageName.c_str() , package->packageName.c_str() ) == 0 )
|
|
{
|
|
tendency->packageIndex = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert( tendency->packageIndex != -1 );
|
|
|
|
arc.ArchiveFloat( &tendency->tendency );
|
|
arc.ArchiveFloat( &tendency->lastTendencyCheck );
|
|
}
|
|
}
|
|
|
|
|
|
if ( arc.Saving() )
|
|
{
|
|
num = _Tendencies.NumObjects();
|
|
|
|
arc.ArchiveInteger( &num );
|
|
|
|
for ( i = 1 ; i <= num ; i++ )
|
|
{
|
|
generalTendency = &_Tendencies.ObjectAt( i );
|
|
|
|
arc.ArchiveString( &generalTendency->tendencyName );
|
|
arc.ArchiveFloat( &generalTendency->tendencyValue );
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arc.ArchiveInteger( &num );
|
|
|
|
_Tendencies.ClearObjectList();
|
|
_Tendencies.Resize( num );
|
|
|
|
for ( i = 1 ; i <= num ; i++ )
|
|
{
|
|
_Tendencies.AddObject( tempGeneralTendency );
|
|
|
|
generalTendency = &_Tendencies.ObjectAt( i );
|
|
|
|
arc.ArchiveString( &generalTendency->tendencyName );
|
|
arc.ArchiveFloat( &generalTendency->tendencyValue );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Personality::SetBehaviorTendency( const str &packageName , float tendency )
|
|
{
|
|
PackageTendency_t pEntry;
|
|
BehaviorPackageType_t *package;
|
|
PackageTendency_t *checkEntry;
|
|
|
|
for ( int i = 1 ; i <= PackageList.NumObjects() ; i++ )
|
|
{
|
|
package = PackageList.ObjectAt( i );
|
|
|
|
if ( !Q_stricmp( packageName.c_str() , package->packageName.c_str() ) )
|
|
{
|
|
// We have a match, let's check to make sure we're not doubling up
|
|
for ( int j = 1 ; j <= _PackageTendencies.NumObjects() ; j++ )
|
|
{
|
|
checkEntry = &_PackageTendencies.ObjectAt( j );
|
|
|
|
if ( checkEntry->packageIndex == i )
|
|
{
|
|
checkEntry->tendency = tendency;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
// We don't have a match, so lets add the package
|
|
pEntry.lastTendencyCheck = 0.0f;
|
|
pEntry.tendency = tendency;
|
|
pEntry.packageIndex = i;
|
|
|
|
_PackageTendencies.AddObject( pEntry );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Personality::SetTendency( const str &tendencyName , float tendencyValue )
|
|
{
|
|
Tendency_t *currentTendency;
|
|
Tendency_t tendency;
|
|
|
|
// First Make sure we don't already have this tendency
|
|
for ( int i = 1 ; i <= _Tendencies.NumObjects() ; i++ )
|
|
{
|
|
currentTendency = &_Tendencies.ObjectAt( i );
|
|
|
|
if ( !Q_stricmp( tendencyName.c_str() , currentTendency->tendencyName.c_str() ) )
|
|
{
|
|
currentTendency->tendencyValue = tendencyValue;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
tendency.tendencyName = tendencyName;
|
|
tendency.tendencyValue = tendencyValue;
|
|
|
|
_Tendencies.AddObject( tendency );
|
|
}
|
|
|
|
qboolean Personality::WantsToExecuteCurrentPackage(float interval)
|
|
{
|
|
int currentIndex;
|
|
PackageTendency_t *checkEntry;
|
|
|
|
currentIndex = act->packageManager->GetCurrentFVarIndex();
|
|
|
|
for ( int i = 1 ; i <= _PackageTendencies.NumObjects() ; i++ )
|
|
{
|
|
checkEntry = &_PackageTendencies.ObjectAt( i );
|
|
|
|
if ( checkEntry->packageIndex == currentIndex )
|
|
{
|
|
if ( level.time > checkEntry->lastTendencyCheck + interval )
|
|
return _wantsToExecutePackage(checkEntry);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
qboolean Personality::_wantsToExecutePackage(PackageTendency_t *tendency )
|
|
{
|
|
float percent_chance;
|
|
float chance;
|
|
|
|
tendency->lastTendencyCheck = level.time;
|
|
|
|
percent_chance = tendency->tendency;
|
|
chance = G_Random();
|
|
|
|
return ( chance < percent_chance );
|
|
|
|
}
|
|
|
|
qboolean Personality::ExecutedPackageInLastTimeFrame(float interval)
|
|
{
|
|
float lastExecutionTime;
|
|
|
|
lastExecutionTime = act->packageManager->GetCurrentFVarLastExecuteTime();
|
|
|
|
if ( level.time <= lastExecutionTime + interval )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//======================================
|
|
// Global Functions
|
|
//======================================
|
|
void FillBehaviorPackageList( void )
|
|
{
|
|
Script script;
|
|
const char *token;
|
|
char *buf;
|
|
BehaviorPackageType_t *packageType;
|
|
|
|
// Check if we even HAVE a BehaviorPackages.txt file
|
|
if ( gi.FS_ReadFile( "global/BehaviorPackages.txt", ( void ** )&buf, true ) == -1 )
|
|
return;
|
|
|
|
script.LoadFile( "global/BehaviorPackages.txt" );
|
|
|
|
if ( script.length < 0 )
|
|
return;
|
|
|
|
|
|
while ( script.TokenAvailable ( true ) )
|
|
{
|
|
token = script.GetToken(false);
|
|
|
|
if (!Q_stricmp( token , "Package" ) )
|
|
{
|
|
packageType = 0;
|
|
packageType = new BehaviorPackageType_t;
|
|
|
|
if ( !packageType )
|
|
gi.Error( ERR_FATAL, "FillBehaviorPackageList -- could not create packageType" );
|
|
|
|
//Set the name and file
|
|
packageType->packageName = script.GetToken(false);
|
|
packageType->stateFile = script.GetToken(false);
|
|
|
|
PackageList.AddObject( packageType );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
script.Close();
|
|
}
|
|
|
|
void ClearBehaviorPackageList( void )
|
|
{
|
|
for ( int i = PackageList.NumObjects() ; i > 0 ; i-- )
|
|
{
|
|
delete PackageList.ObjectAt( i );
|
|
PackageList.RemoveObjectAt( i );
|
|
}
|
|
|
|
//Check that everything is clear
|
|
if ( PackageList.NumObjects() > 0 )
|
|
gi.Error( ERR_FATAL, "ClearBehaviorPackageList -- PackageList is NOT empty." );
|
|
}
|
|
|
|
|