source-engine/game/server/hl2/ai_behavior_operator.cpp

479 lines
13 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "entitylist.h"
#include "ai_navigator.h"
#include "ai_behavior_operator.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//=============================================================================
//=============================================================================
// >OPERATOR BEHAVIOR
//=============================================================================
//=============================================================================
BEGIN_DATADESC( CAI_OperatorBehavior )
DEFINE_FIELD( m_hGoalEntity, FIELD_EHANDLE ),
DEFINE_FIELD( m_hPositionEnt, FIELD_EHANDLE ),
DEFINE_FIELD( m_hContextTarget, FIELD_EHANDLE ),
DEFINE_EMBEDDED( m_WatchSeeEntity ),
END_DATADESC();
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAI_OperatorBehavior::CAI_OperatorBehavior()
{
m_hPositionEnt.Set(NULL);
m_hGoalEntity.Set(NULL);
m_hContextTarget.Set(NULL);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#define POSITION_ENT_ALWAYS_SEE_DIST Square(120)
bool CAI_OperatorBehavior::CanSeePositionEntity()
{
CAI_BaseNPC *pOuter = GetOuter();
Assert( m_hPositionEnt.Get() != NULL );
// early out here.
if( !pOuter->QuerySeeEntity(m_hPositionEnt) )
{
m_WatchSeeEntity.Stop();
return false;
}
bool bSpotted = (pOuter->EyePosition().DistToSqr(m_hPositionEnt->GetAbsOrigin()) <= POSITION_ENT_ALWAYS_SEE_DIST);
if ( !bSpotted )
{
bSpotted = ( pOuter->FInViewCone(m_hPositionEnt) && pOuter->FVisible(m_hPositionEnt) );
}
if (bSpotted )
{
// If we haven't seen it up until now, start a timer. If we have seen it, wait for the
// timer to finish. This prevents edge cases where turning on the flashlight makes
// NPC spot the position entity a frame before she spots an enemy.
if ( !m_WatchSeeEntity.IsRunning() )
{
m_WatchSeeEntity.Start( 0.3,0.31 );
return false;
}
if ( !m_WatchSeeEntity.Expired() )
return false;
return true;
}
m_WatchSeeEntity.Stop();
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_OperatorBehavior::IsAtPositionEntity()
{
Vector myPos = GetAbsOrigin();
Vector objectPos = m_hPositionEnt->GetAbsOrigin();
Vector vecDir = objectPos - myPos;
return (vecDir.Length2D() <= 12.0f);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_OperatorBehavior::GatherConditionsNotActive()
{
if( m_hPositionEnt )
{
// If we're not currently the active behavior, we have a position ent, and the
// NPC can see it, coax the AI out of IDLE/ALERT schedules with this condition.
if( CanSeePositionEntity() )
{
SetCondition( COND_IDLE_INTERRUPT );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_OperatorBehavior::GatherConditions( void )
{
if( GetGoalEntity() )
{
if( GetGoalEntity()->GetState() == OPERATOR_STATE_FINISHED )
{
if( IsCurSchedule(SCHED_OPERATOR_OPERATE) )
{
// Break us out of the operator schedule if the operation completes.
SetCondition(COND_PROVOKED);
}
m_hGoalEntity.Set(NULL);
m_hPositionEnt.Set(NULL);
}
else
{
if( CanSeePositionEntity() )
{
ClearCondition( COND_OPERATOR_LOST_SIGHT_OF_POSITION );
}
else
{
SetCondition( COND_OPERATOR_LOST_SIGHT_OF_POSITION );
}
}
}
BaseClass::GatherConditions();
// Ignore player pushing.
ClearCondition( COND_PLAYER_PUSHING );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CAI_OperatorBehavior::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_OPERATOR_OPERATE:
{
// Fire the appropriate output!
switch( GetGoalEntity()->GetState() )
{
case OPERATOR_STATE_NOT_READY:
GetGoalEntity()->m_OnMakeReady.FireOutput(NULL, NULL, 0);
break;
case OPERATOR_STATE_READY:
GetGoalEntity()->m_OnBeginOperating.FireOutput(NULL, NULL, 0);
break;
default:
//!!!HACKHACK
Assert(0);
break;
}
}
TaskComplete();
break;
case TASK_OPERATOR_START_PATH:
{
ChainStartTask(TASK_WALK_PATH);
}
break;
case TASK_OPERATOR_GET_PATH_TO_POSITION:
{
CBaseEntity *pGoal = m_hPositionEnt;
if( !pGoal )
{
TaskFail("ai_goal_operator has no location entity\n");
break;
}
AI_NavGoal_t goal( pGoal->GetAbsOrigin() );
goal.pTarget = pGoal;
if ( GetNavigator()->SetGoal( goal ) == false )
{
TaskFail( "Can't build path\n" );
/*
// Try and get as close as possible otherwise
AI_NavGoal_t nearGoal( GOALTYPE_LOCATION_NEAREST_NODE, m_hTargetObject->GetAbsOrigin(), AIN_DEF_ACTIVITY, 256 );
if ( GetNavigator()->SetGoal( nearGoal, AIN_CLEAR_PREVIOUS_STATE ) )
{
//FIXME: HACK! The internal pathfinding is setting this without our consent, so override it!
ClearCondition( COND_TASK_FAILED );
GetNavigator()->SetArrivalDirection( m_hTargetObject->GetAbsAngles() );
TaskComplete();
return;
}
*/
}
GetNavigator()->SetArrivalDirection( pGoal->GetAbsAngles() );
}
break;
default:
BaseClass::StartTask( pTask );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CAI_OperatorBehavior::RunTask( const Task_t *pTask )
{
/*
switch( pTask->iTask )
{
default:
BaseClass::RunTask( pTask );
break;
}
*/
BaseClass::RunTask( pTask );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CAI_OperatorGoal *CAI_OperatorBehavior::GetGoalEntity()
{
CAI_OperatorGoal *pGoal = dynamic_cast<CAI_OperatorGoal*>(m_hGoalEntity.Get());
// NULL is OK.
return pGoal;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CAI_OperatorBehavior::IsGoalReady()
{
if( GetGoalEntity()->GetState() == OPERATOR_STATE_READY )
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAI_OperatorBehavior::SetParameters( CAI_OperatorGoal *pGoal, CBaseEntity *pPositionEnt, CBaseEntity *pContextTarget )
{
m_hGoalEntity.Set( pGoal );
m_hPositionEnt.Set( pPositionEnt );
m_hContextTarget.Set( pContextTarget );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_OperatorBehavior::CanSelectSchedule()
{
if ( m_hGoalEntity.Get() == NULL )
return false;
if ( m_hPositionEnt.Get() == NULL )
return false;
if( GetGoalEntity()->GetState() == OPERATOR_STATE_FINISHED )
{
m_hGoalEntity.Set(NULL);
m_hPositionEnt.Set(NULL);
return false;
}
if ( !GetOuter()->IsInterruptable() )
return false;
if ( GetOuter()->m_NPCState == NPC_STATE_COMBAT || GetOuter()->m_NPCState == NPC_STATE_SCRIPT )
return false;
// Don't grab NPCs who have been in combat recently
if ( GetOuter()->GetLastEnemyTime() && (gpGlobals->curtime - GetOuter()->GetLastEnemyTime()) < 3.0 )
return false;
if( !CanSeePositionEntity() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CAI_OperatorBehavior::SelectSchedule()
{
if( !IsAtPositionEntity() )
{
GetGoalEntity()->m_OnBeginApproach.FireOutput( GetOuter(), GetOuter(), 0 );
return SCHED_OPERATOR_APPROACH_POSITION;
}
if( GetGoalEntity() && GetGoalEntity()->GetState() != OPERATOR_STATE_FINISHED)
{
if( GetOuter()->GetActiveWeapon() && !GetOuter()->IsWeaponHolstered() )
{
GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED );
return SCHED_OPERATOR_WAIT_FOR_HOLSTER;
}
return SCHED_OPERATOR_OPERATE;
}
return BaseClass::SelectSchedule();
}
//=============================================================================
//=============================================================================
// >AI_GOAL_OPERATOR
//=============================================================================
//=============================================================================
LINK_ENTITY_TO_CLASS( ai_goal_operator, CAI_OperatorGoal );
BEGIN_DATADESC( CAI_OperatorGoal )
DEFINE_KEYFIELD( m_iState, FIELD_INTEGER, "state" ),
DEFINE_KEYFIELD( m_iMoveTo, FIELD_INTEGER, "moveto" ),
DEFINE_KEYFIELD( m_iszContextTarget, FIELD_STRING, "contexttarget" ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "SetStateReady", InputSetStateReady ),
DEFINE_INPUTFUNC( FIELD_VOID, "SetStateFinished", InputSetStateFinished ),
DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
// Outputs
DEFINE_OUTPUT( m_OnBeginApproach, "OnBeginApproach" ),
DEFINE_OUTPUT( m_OnMakeReady, "OnMakeReady" ),
DEFINE_OUTPUT( m_OnBeginOperating, "OnBeginOperating" ),
DEFINE_OUTPUT( m_OnFinished, "OnFinished" ),
END_DATADESC()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_OperatorGoal::EnableGoal( CAI_BaseNPC *pAI )
{
CAI_OperatorBehavior *pBehavior;
if ( !pAI->GetBehavior( &pBehavior ) )
{
return;
}
CBaseEntity *pPosition = gEntList.FindEntityByName(NULL, m_target);
if( !pPosition )
{
DevMsg("ai_goal_operator called %s with invalid position ent!\n", GetDebugName() );
return;
}
CBaseEntity *pContextTarget = NULL;
if( m_iszContextTarget != NULL_STRING )
{
pContextTarget = gEntList.FindEntityByName( NULL, m_iszContextTarget );
}
pBehavior->SetParameters(this, pPosition, pContextTarget);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CAI_OperatorGoal::InputActivate( inputdata_t &inputdata )
{
BaseClass::InputActivate( inputdata );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CAI_OperatorGoal::InputDeactivate( inputdata_t &inputdata )
{
BaseClass::InputDeactivate( inputdata );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_OperatorGoal::InputSetStateReady( inputdata_t &inputdata )
{
m_iState = OPERATOR_STATE_READY;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_OperatorGoal::InputSetStateFinished( inputdata_t &inputdata )
{
m_iState = OPERATOR_STATE_FINISHED;
m_OnFinished.FireOutput( NULL, NULL, 0 );
}
//=============================================================================
//=============================================================================
// >SCHEDULES
//=============================================================================
//=============================================================================
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_OperatorBehavior )
DECLARE_TASK( TASK_OPERATOR_GET_PATH_TO_POSITION )
DECLARE_TASK( TASK_OPERATOR_START_PATH )
DECLARE_TASK( TASK_OPERATOR_OPERATE )
DECLARE_CONDITION( COND_OPERATOR_LOST_SIGHT_OF_POSITION )
//=========================================================
//=========================================================
DEFINE_SCHEDULE
(
SCHED_OPERATOR_APPROACH_POSITION,
" Tasks"
" TASK_OPERATOR_GET_PATH_TO_POSITION 0"
" TASK_OPERATOR_START_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_STOP_MOVING 0"
" "
" Interrupts"
" COND_NEW_ENEMY"
" COND_HEAR_DANGER"
" COND_OPERATOR_LOST_SIGHT_OF_POSITION"
)
//=========================================================
//=========================================================
DEFINE_SCHEDULE
(
SCHED_OPERATOR_OPERATE,
" Tasks"
" TASK_WAIT 0.2" // Allow pending entity I/O to settle
" TASK_OPERATOR_OPERATE 0"
" TASK_WAIT_INDEFINITE 0"
" "
" Interrupts"
" COND_PROVOKED"
)
//=========================================================
//=========================================================
DEFINE_SCHEDULE
(
SCHED_OPERATOR_WAIT_FOR_HOLSTER,
" Tasks"
" TASK_WAIT 1.0"
" "
" Interrupts"
" "
)
AI_END_CUSTOM_SCHEDULE_PROVIDER()