//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "isaverestore.h"
# include "ai_behavior.h"
# include "scripted.h"
# include "env_debughistory.h"
# include "saverestore_utlvector.h"
# include "checksum_crc.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
CGenericClassmap < CAI_BehaviorBase > CAI_BehaviorBase : : m_BehaviorClasses ;
bool g_bBehaviorHost_PreventBaseClassGatherConditions ;
//-----------------------------------------------------------------------------
BEGIN_SIMPLE_DATADESC ( AIChannelScheduleState_t )
DEFINE_FIELD ( bActive , FIELD_BOOLEAN ) ,
// pSchedule
// idealSchedule
// failSchedule
DEFINE_FIELD ( iCurTask , FIELD_INTEGER ) ,
DEFINE_FIELD ( fTaskStatus , FIELD_INTEGER ) ,
DEFINE_FIELD ( timeStarted , FIELD_TIME ) ,
DEFINE_FIELD ( timeCurTaskStarted , FIELD_TIME ) ,
DEFINE_FIELD ( taskFailureCode , FIELD_INTEGER ) ,
DEFINE_FIELD ( bScheduleWasInterrupted , FIELD_BOOLEAN ) ,
END_DATADESC ( )
//-----------------------------------------------------------------------------
// CAI_BehaviorBase
//-----------------------------------------------------------------------------
BEGIN_DATADESC_NO_BASE ( CAI_BehaviorBase )
DEFINE_UTLVECTOR ( m_ScheduleChannels , FIELD_EMBEDDED ) ,
END_DATADESC ( )
//-------------------------------------
CAI_ClassScheduleIdSpace * CAI_BehaviorBase : : GetClassScheduleIdSpace ( )
{
return GetOuter ( ) - > GetClassScheduleIdSpace ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Draw any text overlays (override in subclass to add additional text)
// Input : Previous text offset from the top
// Output : Current text offset from the top
//-----------------------------------------------------------------------------
int CAI_BehaviorBase : : DrawDebugTextOverlays ( int text_offset )
{
char tempstr [ 512 ] ;
if ( GetOuter ( ) - > m_debugOverlays & OVERLAY_TEXT_BIT )
{
Q_snprintf ( tempstr , sizeof ( tempstr ) , " Behv: %s, " , GetName ( ) ) ;
GetOuter ( ) - > EntityText ( text_offset , tempstr , 0 ) ;
text_offset + + ;
for ( int i = 0 ; i < m_ScheduleChannels . Count ( ) ; i + + )
{
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ i ] ;
if ( pScheduleState - > bActive & & pScheduleState - > pSchedule )
{
const char * pName = NULL ;
pName = pScheduleState - > pSchedule - > GetName ( ) ;
if ( ! pName )
{
pName = " Unknown " ;
}
Q_snprintf ( tempstr , sizeof ( tempstr ) , " Schd [%d]: %s, " , i , pName ) ;
GetOuter ( ) - > EntityText ( text_offset , tempstr , 0 ) ;
text_offset + + ;
const Task_t * pTask = GetCurTask ( i ) ;
if ( pTask )
{
Q_snprintf ( tempstr , sizeof ( tempstr ) , " Task [%d]: %s (#%d), " , i , GetSchedulingSymbols ( ) - > TaskIdToSymbol ( GetClassScheduleIdSpace ( ) - > TaskLocalToGlobal ( pTask - > iTask ) ) , pScheduleState - > iCurTask ) ;
}
else
{
Q_snprintf ( tempstr , sizeof ( tempstr ) , " Task [%d]: None " , i ) ;
}
GetOuter ( ) - > EntityText ( text_offset , tempstr , 0 ) ;
text_offset + + ;
}
}
}
return text_offset ;
}
//-------------------------------------
void CAI_BehaviorBase : : GatherConditions ( )
{
Assert ( m_pBackBridge ! = NULL ) ;
m_pBackBridge - > BehaviorBridge_GatherConditions ( ) ;
}
//-------------------------------------
void CAI_BehaviorBase : : OnStartSchedule ( int scheduleType )
{
}
//-------------------------------------
int CAI_BehaviorBase : : SelectSchedule ( )
{
Assert ( m_pBackBridge ! = NULL ) ;
return m_pBackBridge - > BehaviorBridge_SelectSchedule ( ) ;
}
//-------------------------------------
int CAI_BehaviorBase : : SelectFailSchedule ( int failedSchedule , int failedTask , AI_TaskFailureCode_t taskFailCode )
{
m_fOverrode = false ;
return SCHED_NONE ;
}
//-------------------------------------
void CAI_BehaviorBase : : StartTask ( const Task_t * pTask )
{
m_fOverrode = false ;
}
//-------------------------------------
void CAI_BehaviorBase : : RunTask ( const Task_t * pTask )
{
m_fOverrode = false ;
}
//-------------------------------------
int CAI_BehaviorBase : : TranslateSchedule ( int scheduleType )
{
Assert ( m_pBackBridge ! = NULL ) ;
return m_pBackBridge - > BehaviorBridge_TranslateSchedule ( scheduleType ) ;
}
//-------------------------------------
CAI_Schedule * CAI_BehaviorBase : : GetNewSchedule ( int channel )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return NULL ;
}
int scheduleType ;
//
// Schedule selection code here overrides all leaf schedule selection.
//
scheduleType = SelectSchedule ( channel ) ;
CAI_Schedule * pSchedule = GetSchedule ( scheduleType ) ;
if ( pSchedule ! = NULL )
m_ScheduleChannels [ channel ] . idealSchedule = pSchedule - > GetId ( ) ;
return pSchedule ;
}
//-------------------------------------
CAI_Schedule * CAI_BehaviorBase : : GetFailSchedule ( AIChannelScheduleState_t * pScheduleState )
{
int prevSchedule ;
int failedTask ;
if ( pScheduleState - > pSchedule )
prevSchedule = GetClassScheduleIdSpace ( ) - > ScheduleGlobalToLocal ( pScheduleState - > pSchedule - > GetId ( ) ) ;
else
prevSchedule = SCHED_NONE ;
const Task_t * pTask = GetTask ( pScheduleState ) ;
if ( pTask )
failedTask = pTask - > iTask ;
else
failedTask = TASK_INVALID ;
Assert ( AI_IdIsLocal ( prevSchedule ) ) ;
Assert ( AI_IdIsLocal ( failedTask ) ) ;
int scheduleType = SelectFailSchedule ( prevSchedule , failedTask , pScheduleState - > taskFailureCode ) ;
return GetSchedule ( scheduleType ) ;
}
//-------------------------------------
CAI_Schedule * CAI_BehaviorBase : : GetSchedule ( int schedule )
{
if ( ! GetClassScheduleIdSpace ( ) - > IsGlobalBaseSet ( ) )
{
Warning ( " ERROR: %s missing schedule! \n " , GetSchedulingErrorName ( ) ) ;
return g_AI_SchedulesManager . GetScheduleFromID ( SCHED_IDLE_STAND ) ;
}
if ( schedule = = SCHED_NONE )
return NULL ;
if ( AI_IdIsLocal ( schedule ) )
{
schedule = GetClassScheduleIdSpace ( ) - > ScheduleLocalToGlobal ( schedule ) ;
}
if ( schedule = = - 1 )
return NULL ;
return g_AI_SchedulesManager . GetScheduleFromID ( schedule ) ;
}
//-------------------------------------
const Task_t * CAI_BehaviorBase : : GetTask ( AIChannelScheduleState_t * pScheduleState )
{
int iScheduleIndex = pScheduleState - > iCurTask ;
if ( ! pScheduleState - > pSchedule | | iScheduleIndex < 0 | | iScheduleIndex > = pScheduleState - > pSchedule - > NumTasks ( ) )
// iScheduleIndex is not within valid range for the NPC's current schedule.
return NULL ;
return & pScheduleState - > pSchedule - > GetTaskList ( ) [ iScheduleIndex ] ;
}
//-------------------------------------
bool CAI_BehaviorBase : : IsCurSchedule ( int schedule , bool fIdeal )
{
if ( AI_IdIsLocal ( schedule ) )
schedule = GetClassScheduleIdSpace ( ) - > ScheduleLocalToGlobal ( schedule ) ;
return GetOuter ( ) - > IsCurSchedule ( schedule , fIdeal ) ;
}
//-------------------------------------
const char * CAI_BehaviorBase : : GetSchedulingErrorName ( )
{
return " CAI_Behavior " ;
}
//-------------------------------------
void CAI_BehaviorBase : : StartChannel ( int channel )
{
if ( channel < 0 )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return ;
}
m_ScheduleChannels . EnsureCount ( channel + 1 ) ;
m_ScheduleChannels [ channel ] . bActive = true ;
}
//-------------------------------------
void CAI_BehaviorBase : : StopChannel ( int channel )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return ;
}
m_ScheduleChannels [ channel ] . bActive = false ;
}
//-------------------------------------
void CAI_BehaviorBase : : MaintainChannelSchedules ( )
{
for ( int i = 0 ; i < m_ScheduleChannels . Count ( ) ; i + + )
{
MaintainSchedule ( i ) ;
}
}
//-------------------------------------
# define MAX_TASKS_RUN 10
void CAI_BehaviorBase : : MaintainSchedule ( int channel )
{
CAI_Schedule * pNewSchedule ;
bool runTask = true ;
AssertMsg ( m_ScheduleChannels . IsValidIndex ( channel ) , " Bad schedule channel " ) ;
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
if ( ! pScheduleState - > bActive )
{
return ;
}
int i ;
bool bStopProcessing = false ;
for ( i = 0 ; i < MAX_TASKS_RUN & & ! bStopProcessing ; i + + )
{
if ( pScheduleState - > pSchedule ! = NULL & & pScheduleState - > fTaskStatus = = TASKSTATUS_COMPLETE )
{
// Schedule is valid, so advance to the next task if the current is complete.
pScheduleState - > fTaskStatus = TASKSTATUS_NEW ;
pScheduleState - > iCurTask + + ;
}
// validate existing schedule
if ( ! IsScheduleValid ( pScheduleState ) )
{
// Notify the NPC that his schedule is changing
pScheduleState - > bScheduleWasInterrupted = true ;
OnScheduleChange ( channel ) ;
// if ( !m_bConditionsGathered )
// {
// // occurs if a schedule is exhausted within one think
// GatherConditions();
// }
if ( pScheduleState - > taskFailureCode ! = NO_TASK_FAILURE )
{
// Get a fail schedule if the previous schedule failed during execution and
// the NPC is still in its ideal state. Otherwise, the NPC would immediately
// select the same schedule again and fail again.
if ( GetOuter ( ) - > m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
{
DevMsg ( GetOuter ( ) , AIMF_IGNORE_SELECTED , " Behavior %s channel %d (failed) \n " , GetName ( ) , channel ) ;
}
ADD_DEBUG_HISTORY ( HISTORY_AI_DECISIONS , UTIL_VarArgs ( " %s(%d): Behavior %s channel %d (failed) \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > entindex ( ) , GetName ( ) , channel ) ) ;
pNewSchedule = GetFailSchedule ( pScheduleState ) ;
pScheduleState - > idealSchedule = pNewSchedule - > GetId ( ) ;
DevWarning ( 2 , " (%s) Schedule (%s) Failed at %d! \n " , STRING ( GetOuter ( ) - > GetEntityName ( ) ) , pScheduleState - > pSchedule ? pScheduleState - > pSchedule - > GetName ( ) : " GetCurSchedule() == NULL " , pScheduleState - > iCurTask ) ;
SetSchedule ( channel , pNewSchedule ) ;
}
else
{
pNewSchedule = GetNewSchedule ( channel ) ;
SetSchedule ( channel , pNewSchedule ) ;
}
}
if ( ! pScheduleState - > pSchedule )
{
pNewSchedule = GetNewSchedule ( channel ) ;
if ( pNewSchedule )
{
SetSchedule ( channel , pNewSchedule ) ;
}
}
if ( ! pScheduleState - > pSchedule | | pScheduleState - > pSchedule - > NumTasks ( ) = = 0 )
{
return ;
}
if ( pScheduleState - > fTaskStatus = = TASKSTATUS_NEW )
{
if ( pScheduleState - > iCurTask = = 0 )
{
int globalId = pScheduleState - > pSchedule - > GetId ( ) ;
int localId = GetClassScheduleIdSpace ( ) - > ScheduleGlobalToLocal ( globalId ) ;
OnStartSchedule ( channel , ( localId ! = - 1 ) ? localId : globalId ) ;
}
const Task_t * pTask = GetCurTask ( channel ) ;
Assert ( pTask ! = NULL ) ;
if ( GetOuter ( ) - > m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
{
DevMsg ( GetOuter ( ) , AIMF_IGNORE_SELECTED , " Behavior %s channel %d Task: %s \n " , GetName ( ) , channel , GetSchedulingSymbols ( ) - > TaskIdToSymbol ( GetClassScheduleIdSpace ( ) - > TaskLocalToGlobal ( pTask - > iTask ) ) ) ;
}
ADD_DEBUG_HISTORY ( HISTORY_AI_DECISIONS , UTIL_VarArgs ( " %s(%d): Behavior %s channel %d Task: %s \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > entindex ( ) , GetName ( ) , channel , GetSchedulingSymbols ( ) - > TaskIdToSymbol ( GetClassScheduleIdSpace ( ) - > TaskLocalToGlobal ( pTask - > iTask ) ) ) ) ;
pScheduleState - > fTaskStatus = TASKSTATUS_RUN_MOVE_AND_TASK ;
pScheduleState - > taskFailureCode = NO_TASK_FAILURE ;
pScheduleState - > timeCurTaskStarted = gpGlobals - > curtime ;
StartTask ( channel , pTask ) ;
}
if ( ! TaskIsComplete ( channel ) & & pScheduleState - > fTaskStatus ! = TASKSTATUS_NEW )
{
if ( pScheduleState - > fTaskStatus ! = TASKSTATUS_COMPLETE & & pScheduleState - > fTaskStatus ! = TASKSTATUS_RUN_MOVE & & pScheduleState - > taskFailureCode = = NO_TASK_FAILURE & & runTask )
{
const Task_t * pTask = GetCurTask ( channel ) ;
Assert ( pTask ! = NULL ) ;
RunTask ( channel , pTask ) ;
if ( ! TaskIsComplete ( channel ) )
{
bStopProcessing = true ;
}
}
else
{
bStopProcessing = true ;
}
}
}
}
//-------------------------------------
bool CAI_BehaviorBase : : IsScheduleValid ( AIChannelScheduleState_t * pScheduleState )
{
CAI_Schedule * pSchedule = pScheduleState - > pSchedule ;
if ( ! pSchedule | | pSchedule - > NumTasks ( ) = = 0 )
{
return false ;
}
if ( pScheduleState - > iCurTask = = pSchedule - > NumTasks ( ) )
{
return false ;
}
if ( pScheduleState - > taskFailureCode ! = NO_TASK_FAILURE )
{
return false ;
}
//Start out with the base schedule's set interrupt conditions
CAI_ScheduleBits testBits ;
pSchedule - > GetInterruptMask ( & testBits ) ;
testBits . And ( GetOuter ( ) - > AccessConditionBits ( ) , & testBits ) ;
if ( ! testBits . IsAllClear ( ) )
{
// If in developer mode save the interrupt text for debug output
if ( developer . GetInt ( ) )
{
// Find the first non-zero bit
for ( int i = 0 ; i < MAX_CONDITIONS ; i + + )
{
if ( testBits . IsBitSet ( i ) )
{
int channel = pScheduleState - m_ScheduleChannels . Base ( ) ;
const char * pszInterruptText = GetOuter ( ) - > ConditionName ( AI_RemapToGlobal ( i ) ) ;
if ( ! pszInterruptText )
{
pszInterruptText = " (UNKNOWN CONDITION) " ;
}
if ( GetOuter ( ) - > m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
{
DevMsg ( GetOuter ( ) , AIMF_IGNORE_SELECTED , " Behavior %s channel %d Break condition -> %s \n " , pszInterruptText , GetName ( ) , channel ) ;
}
ADD_DEBUG_HISTORY ( HISTORY_AI_DECISIONS , UTIL_VarArgs ( " %s(%d): Behavior %s channel %d Break condition -> %s \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > entindex ( ) , GetName ( ) , channel , pszInterruptText ) ) ;
break ;
}
}
}
return false ;
}
return true ;
}
//-------------------------------------
void CAI_BehaviorBase : : SetSchedule ( int channel , CAI_Schedule * pNewSchedule )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return ;
}
//Assert( pNewSchedule != NULL );
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
pScheduleState - > timeCurTaskStarted = pScheduleState - > timeStarted = gpGlobals - > curtime ;
pScheduleState - > bScheduleWasInterrupted = false ;
pScheduleState - > pSchedule = pNewSchedule ;
pScheduleState - > iCurTask = 0 ;
pScheduleState - > fTaskStatus = TASKSTATUS_NEW ;
pScheduleState - > failSchedule = SCHED_NONE ;
// this is very useful code if you can isolate a test case in a level with a single NPC. It will notify
// you of every schedule selection the NPC makes.
if ( pNewSchedule )
{
if ( GetOuter ( ) - > m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
{
DevMsg ( GetOuter ( ) , AIMF_IGNORE_SELECTED , " Behavior %s channel %d Schedule: %s (time: %.2f) \n " , GetName ( ) , channel , pNewSchedule - > GetName ( ) , gpGlobals - > curtime ) ;
}
ADD_DEBUG_HISTORY ( HISTORY_AI_DECISIONS , UTIL_VarArgs ( " %s(%d): Behavior %s channel %d Schedule: %s (time: %.2f) \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > entindex ( ) , GetName ( ) , channel , pNewSchedule - > GetName ( ) , gpGlobals - > curtime ) ) ;
}
}
//-------------------------------------
bool CAI_BehaviorBase : : SetSchedule ( int channel , int localScheduleID )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return false ;
}
int translatedSchedule = TranslateSchedule ( channel , localScheduleID ) ;
CAI_Schedule * pNewSchedule = GetSchedule ( translatedSchedule ) ;
if ( pNewSchedule )
{
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
if ( AI_IdIsLocal ( localScheduleID ) )
{
pScheduleState - > idealSchedule = GetClassScheduleIdSpace ( ) - > ScheduleLocalToGlobal ( localScheduleID ) ;
}
else
{
pScheduleState - > idealSchedule = localScheduleID ;
}
SetSchedule ( channel , pNewSchedule ) ;
return true ;
}
return false ;
}
//-------------------------------------
void CAI_BehaviorBase : : ClearSchedule ( int channel , const char * szReason )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return ;
}
if ( szReason & & GetOuter ( ) - > m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
{
DevMsg ( GetOuter ( ) , AIMF_IGNORE_SELECTED , " Behavior %s channel %d Schedule cleared: %s \n " , GetName ( ) , channel , szReason ) ;
}
if ( szReason )
{
ADD_DEBUG_HISTORY ( HISTORY_AI_DECISIONS , UTIL_VarArgs ( " %s(%d): Behavior %s channel %d Schedule cleared: %s \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > entindex ( ) , GetName ( ) , channel , szReason ) ) ;
}
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
pScheduleState - > timeCurTaskStarted = pScheduleState - > timeStarted = 0 ;
pScheduleState - > bScheduleWasInterrupted = true ;
pScheduleState - > fTaskStatus = TASKSTATUS_NEW ;
pScheduleState - > idealSchedule = AI_RemapToGlobal ( SCHED_NONE ) ;
pScheduleState - > pSchedule = NULL ;
pScheduleState - > iCurTask = 0 ;
}
//-------------------------------------
CAI_Schedule * CAI_BehaviorBase : : GetCurSchedule ( int channel )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return NULL ;
}
return m_ScheduleChannels [ channel ] . pSchedule ;
}
//-------------------------------------
bool CAI_BehaviorBase : : IsCurSchedule ( int channel , int schedId , bool fIdeal )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return false ;
}
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
if ( ! pScheduleState - > pSchedule )
return ( schedId = = SCHED_NONE | | schedId = = AI_RemapToGlobal ( SCHED_NONE ) ) ;
schedId = ( AI_IdIsLocal ( schedId ) ) ? GetClassScheduleIdSpace ( ) - > ScheduleLocalToGlobal ( schedId ) : schedId ;
if ( fIdeal )
return ( schedId = = pScheduleState - > idealSchedule ) ;
return ( pScheduleState - > pSchedule - > GetId ( ) = = schedId ) ;
}
//-------------------------------------
void CAI_BehaviorBase : : OnScheduleChange ( int channel )
{
}
//-------------------------------------
void CAI_BehaviorBase : : OnStartSchedule ( int channel , int scheduleType )
{
}
//-------------------------------------
int CAI_BehaviorBase : : SelectSchedule ( int channel )
{
return SCHED_NONE ;
}
//-------------------------------------
int CAI_BehaviorBase : : SelectFailSchedule ( int channel , int failedSchedule , int failedTask , AI_TaskFailureCode_t taskFailCode )
{
return SCHED_NONE ;
}
//-------------------------------------
void CAI_BehaviorBase : : TaskComplete ( int channel , bool fIgnoreSetFailedCondition )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return ;
}
if ( fIgnoreSetFailedCondition | | m_ScheduleChannels [ channel ] . taskFailureCode = = NO_TASK_FAILURE )
{
m_ScheduleChannels [ channel ] . taskFailureCode = NO_TASK_FAILURE ;
m_ScheduleChannels [ channel ] . fTaskStatus = TASKSTATUS_COMPLETE ;
}
}
//-------------------------------------
void CAI_BehaviorBase : : TaskFail ( int channel , AI_TaskFailureCode_t code )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return ;
}
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
if ( GetOuter ( ) - > m_debugOverlays & OVERLAY_TASK_TEXT_BIT )
{
DevMsg ( GetOuter ( ) , AIMF_IGNORE_SELECTED , " Behavior %s channel %d: TaskFail -> %s \n " , GetName ( ) , channel , TaskFailureToString ( code ) ) ;
}
ADD_DEBUG_HISTORY ( HISTORY_AI_DECISIONS , UTIL_VarArgs ( " %s(%d): Behavior %s channel %d: TaskFail -> %s \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > entindex ( ) , GetName ( ) , channel , TaskFailureToString ( code ) ) ) ;
pScheduleState - > taskFailureCode = code ;
}
//-------------------------------------
const Task_t * CAI_BehaviorBase : : GetCurTask ( int channel )
{
if ( ! m_ScheduleChannels . IsValidIndex ( channel ) )
{
AssertMsg ( 0 , " Bad schedule channel " ) ;
return false ;
}
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ channel ] ;
int iScheduleIndex = pScheduleState - > iCurTask ;
if ( ! pScheduleState - > pSchedule | | iScheduleIndex < 0 | | iScheduleIndex > = pScheduleState - > pSchedule - > NumTasks ( ) )
// iScheduleIndex is not within valid range for the NPC's current schedule.
return NULL ;
return & pScheduleState - > pSchedule - > GetTaskList ( ) [ iScheduleIndex ] ;
}
//-------------------------------------
void CAI_BehaviorBase : : StartTask ( int channel , const Task_t * pTask )
{
Assert ( 0 ) ;
}
//-------------------------------------
void CAI_BehaviorBase : : RunTask ( int channel , const Task_t * pTask )
{
Assert ( 0 ) ;
}
//-------------------------------------
float CAI_BehaviorBase : : GetJumpGravity ( ) const
{
Assert ( m_pBackBridge ! = NULL ) ;
return m_pBackBridge - > BehaviorBridge_GetJumpGravity ( ) ;
}
//-------------------------------------
bool CAI_BehaviorBase : : IsJumpLegal ( const Vector & startPos , const Vector & apex , const Vector & endPos , float maxUp , float maxDown , float maxDist ) const
{
Assert ( m_pBackBridge ! = NULL ) ;
return m_pBackBridge - > BehaviorBridge_IsJumpLegal ( startPos , apex , endPos , maxUp , maxDown , maxDist ) ;
}
//-------------------------------------
bool CAI_BehaviorBase : : MovementCost ( int moveType , const Vector & vecStart , const Vector & vecEnd , float * pCost )
{
Assert ( m_pBackBridge ! = NULL ) ;
return m_pBackBridge - > BehaviorBridge_MovementCost ( moveType , vecStart , vecEnd , pCost ) ;
}
//-------------------------------------
bool CAI_BehaviorBase : : NotifyChangeBehaviorStatus ( bool fCanFinishSchedule )
{
bool fInterrupt = GetOuter ( ) - > OnBehaviorChangeStatus ( this , fCanFinishSchedule ) ;
if ( ! GetOuter ( ) - > IsInterruptable ( ) )
return false ;
if ( fInterrupt )
{
if ( GetOuter ( ) - > m_hCine )
{
if ( GetOuter ( ) - > m_hCine - > PlayedSequence ( ) )
{
DevWarning ( " NPC: %s canceled running script %s due to behavior change \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > m_hCine - > GetDebugName ( ) ) ;
}
else
{
DevWarning ( " NPC: %s canceled script %s without playing, due to behavior change \n " , GetOuter ( ) - > GetDebugName ( ) , GetOuter ( ) - > m_hCine - > GetDebugName ( ) ) ;
}
GetOuter ( ) - > m_hCine - > CancelScript ( ) ;
}
//!!!HACKHACK
// this is dirty, but it forces NPC to pick a new schedule next time through.
GetOuter ( ) - > ClearSchedule ( " Changed behavior status " ) ;
}
return fInterrupt ;
}
//-------------------------------------
int CAI_BehaviorBase : : Save ( ISave & save )
{
if ( save . WriteAll ( this , GetDataDescMap ( ) ) )
{
SaveChannels ( save ) ;
return 1 ;
}
return 0 ;
}
//-------------------------------------
int CAI_BehaviorBase : : Restore ( IRestore & restore )
{
if ( restore . ReadAll ( this , GetDataDescMap ( ) ) )
{
RestoreChannels ( restore ) ;
return 1 ;
}
return 0 ;
}
//-------------------------------------
//-----------------------------------------------------------------------------
const short AI_BEHAVIOR_CHANNEL_SAVE_HEADER_VERSION = 1 ;
struct AIBehaviorChannelSaveHeader_t
{
AIBehaviorChannelSaveHeader_t ( )
: flags ( 0 ) ,
scheduleCrc ( 0 )
{
szSchedule [ 0 ] = 0 ;
szIdealSchedule [ 0 ] = 0 ;
szFailSchedule [ 0 ] = 0 ;
}
unsigned flags ;
char szSchedule [ 128 ] ;
CRC32_t scheduleCrc ;
char szIdealSchedule [ 128 ] ;
char szFailSchedule [ 128 ] ;
DECLARE_SIMPLE_DATADESC ( ) ;
} ;
//-------------------------------------
BEGIN_SIMPLE_DATADESC ( AIBehaviorChannelSaveHeader_t )
DEFINE_FIELD ( flags , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( szSchedule , FIELD_CHARACTER ) ,
DEFINE_FIELD ( scheduleCrc , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( szIdealSchedule , FIELD_CHARACTER ) ,
DEFINE_AUTO_ARRAY ( szFailSchedule , FIELD_CHARACTER ) ,
END_DATADESC ( )
void CAI_BehaviorBase : : SaveChannels ( ISave & save )
{
AIBehaviorChannelSaveHeader_t saveHeader ;
if ( m_ScheduleChannels . Count ( ) )
{
save . StartBlock ( ) ;
int version = AI_BEHAVIOR_CHANNEL_SAVE_HEADER_VERSION ;
save . WriteInt ( & version ) ;
for ( int i = 0 ; i < m_ScheduleChannels . Count ( ) ; i + + )
{
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ i ] ;
if ( pScheduleState - > pSchedule )
{
const char * pszSchedule = pScheduleState - > pSchedule - > GetName ( ) ;
Assert ( Q_strlen ( pszSchedule ) < sizeof ( saveHeader . szSchedule ) - 1 ) ;
Q_strncpy ( saveHeader . szSchedule , pszSchedule , sizeof ( saveHeader . szSchedule ) ) ;
CRC32_Init ( & saveHeader . scheduleCrc ) ;
CRC32_ProcessBuffer ( & saveHeader . scheduleCrc , ( void * ) pScheduleState - > pSchedule - > GetTaskList ( ) , pScheduleState - > pSchedule - > NumTasks ( ) * sizeof ( Task_t ) ) ;
CRC32_Final ( & saveHeader . scheduleCrc ) ;
}
else
{
saveHeader . szSchedule [ 0 ] = 0 ;
saveHeader . scheduleCrc = 0 ;
}
const int SCHED_NONE_GLOBAL = AI_RemapToGlobal ( SCHED_NONE ) ;
int idealSchedule = ( pScheduleState - > idealSchedule = = SCHED_NONE ) ? SCHED_NONE_GLOBAL : pScheduleState - > idealSchedule ;
Assert ( ! AI_IdIsLocal ( idealSchedule ) ) ;
if ( idealSchedule ! = - 1 & & idealSchedule ! = SCHED_NONE_GLOBAL )
{
CAI_Schedule * pIdealSchedule = GetSchedule ( pScheduleState - > idealSchedule ) ;
if ( pIdealSchedule )
{
const char * pszIdealSchedule = pIdealSchedule - > GetName ( ) ;
Assert ( Q_strlen ( pszIdealSchedule ) < sizeof ( saveHeader . szIdealSchedule ) - 1 ) ;
Q_strncpy ( saveHeader . szIdealSchedule , pszIdealSchedule , sizeof ( saveHeader . szIdealSchedule ) ) ;
}
}
int failSchedule = ( pScheduleState - > failSchedule = = SCHED_NONE ) ? SCHED_NONE_GLOBAL : pScheduleState - > failSchedule ;
Assert ( ! AI_IdIsLocal ( failSchedule ) ) ;
if ( failSchedule ! = - 1 & & failSchedule ! = SCHED_NONE_GLOBAL )
{
CAI_Schedule * pFailSchedule = GetSchedule ( pScheduleState - > failSchedule ) ;
if ( pFailSchedule )
{
const char * pszFailSchedule = pFailSchedule - > GetName ( ) ;
Assert ( Q_strlen ( pszFailSchedule ) < sizeof ( saveHeader . szFailSchedule ) - 1 ) ;
Q_strncpy ( saveHeader . szFailSchedule , pszFailSchedule , sizeof ( saveHeader . szFailSchedule ) ) ;
}
}
save . WriteAll ( & saveHeader ) ;
}
save . EndBlock ( ) ;
}
}
//-------------------------------------
void CAI_BehaviorBase : : RestoreChannels ( IRestore & restore )
{
if ( m_ScheduleChannels . Count ( ) )
{
restore . StartBlock ( ) ;
int version ;
restore . ReadInt ( & version ) ;
AIBehaviorChannelSaveHeader_t saveHeader ;
for ( int i = 0 ; i < m_ScheduleChannels . Count ( ) ; i + + )
{
restore . ReadAll ( & saveHeader ) ;
AIChannelScheduleState_t * pScheduleState = & m_ScheduleChannels [ i ] ;
// Do schedule fix-up
if ( saveHeader . szIdealSchedule [ 0 ] )
{
CAI_Schedule * pIdealSchedule = g_AI_SchedulesManager . GetScheduleByName ( saveHeader . szIdealSchedule ) ;
pScheduleState - > idealSchedule = ( pIdealSchedule ) ? pIdealSchedule - > GetId ( ) : SCHED_NONE ;
}
if ( saveHeader . szFailSchedule [ 0 ] )
{
CAI_Schedule * pFailSchedule = g_AI_SchedulesManager . GetScheduleByName ( saveHeader . szFailSchedule ) ;
pScheduleState - > failSchedule = ( pFailSchedule ) ? pFailSchedule - > GetId ( ) : SCHED_NONE ;
}
bool bDiscardScheduleState = ( saveHeader . szSchedule [ 0 ] = = 0 ) ;
if ( pScheduleState - > taskFailureCode > = NUM_FAIL_CODES )
pScheduleState - > taskFailureCode = FAIL_NO_TARGET ; // must have been a string, gotta punt
if ( ! bDiscardScheduleState )
{
pScheduleState - > pSchedule = g_AI_SchedulesManager . GetScheduleByName ( saveHeader . szSchedule ) ;
if ( pScheduleState - > pSchedule )
{
CRC32_t scheduleCrc ;
CRC32_Init ( & scheduleCrc ) ;
CRC32_ProcessBuffer ( & scheduleCrc , ( void * ) pScheduleState - > pSchedule - > GetTaskList ( ) , pScheduleState - > pSchedule - > NumTasks ( ) * sizeof ( Task_t ) ) ;
CRC32_Final ( & scheduleCrc ) ;
if ( scheduleCrc ! = saveHeader . scheduleCrc )
{
pScheduleState - > pSchedule = NULL ;
}
}
}
if ( ! pScheduleState - > pSchedule )
bDiscardScheduleState = true ;
if ( bDiscardScheduleState )
{
ClearSchedule ( i , " Restoring NPC " ) ;
}
}
restore . EndBlock ( ) ;
}
}
//-------------------------------------
# define BEHAVIOR_SAVE_BLOCKNAME "AI_Behaviors"
# define BEHAVIOR_SAVE_VERSION 2
void CAI_BehaviorBase : : SaveBehaviors ( ISave & save , CAI_BehaviorBase * pCurrentBehavior , CAI_BehaviorBase * * ppBehavior , int nBehaviors , bool bTestIfNPCSave )
{
save . StartBlock ( BEHAVIOR_SAVE_BLOCKNAME ) ;
short temp = BEHAVIOR_SAVE_VERSION ;
save . WriteShort ( & temp ) ;
temp = ( short ) nBehaviors ;
save . WriteShort ( & temp ) ;
for ( int i = 0 ; i < nBehaviors ; i + + )
{
if ( ( ! bTestIfNPCSave | | ppBehavior [ i ] - > ShouldNPCSave ( ) ) )
{
bool bHasDatadesc = ( strcmp ( ppBehavior [ i ] - > GetDataDescMap ( ) - > dataClassName , CAI_BehaviorBase : : m_DataMap . dataClassName ) ! = 0 ) ;
if ( bHasDatadesc )
{
save . StartBlock ( ) ;
save . WriteString ( ppBehavior [ i ] - > GetDataDescMap ( ) - > dataClassName ) ;
bool bIsCurrent = ( pCurrentBehavior = = ppBehavior [ i ] ) ;
save . WriteBool ( & bIsCurrent ) ;
ppBehavior [ i ] - > Save ( save ) ;
save . EndBlock ( ) ;
}
else
{
DevMsg ( " Note: behavior \" %s \" lacks a datadesc and probably won't save/restore correctly \n " , ppBehavior [ i ] - > GetName ( ) ) ; // You need at least an empty datadesc to allow for correct binding on load
}
}
}
save . EndBlock ( ) ;
}
//-------------------------------------
int CAI_BehaviorBase : : RestoreBehaviors ( IRestore & restore , CAI_BehaviorBase * * ppBehavior , int nBehaviors , bool bTestIfNPCSave )
{
int iCurrent = - 1 ;
char szBlockName [ SIZE_BLOCK_NAME_BUF ] ;
restore . StartBlock ( szBlockName ) ;
if ( strcmp ( szBlockName , BEHAVIOR_SAVE_BLOCKNAME ) = = 0 )
{
short version ;
restore . ReadShort ( & version ) ;
if ( version = = BEHAVIOR_SAVE_VERSION )
{
short nToRestore ;
char szClassNameCurrent [ 256 ] ;
restore . ReadShort ( & nToRestore ) ;
for ( int i = 0 ; i < nToRestore ; i + + )
{
restore . StartBlock ( ) ;
restore . ReadString ( szClassNameCurrent , sizeof ( szClassNameCurrent ) , 0 ) ;
bool bIsCurrent ;
restore . ReadBool ( & bIsCurrent ) ;
for ( int j = 0 ; j < nBehaviors ; j + + )
{
if ( ( ! bTestIfNPCSave | | ppBehavior [ j ] - > ShouldNPCSave ( ) ) & & strcmp ( ppBehavior [ j ] - > GetDataDescMap ( ) - > dataClassName , szClassNameCurrent ) = = 0 )
{
if ( bIsCurrent )
iCurrent = j ;
ppBehavior [ j ] - > Restore ( restore ) ;
}
}
restore . EndBlock ( ) ;
}
}
}
restore . EndBlock ( ) ;
return iCurrent ;
}
//-----------------------------------------------------------------------------