Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

398 lines
12 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "beam_shared.h"
#include "ai_motor.h"
#include "asw_ai_behavior_heal_other.h"
#include "ai_hint.h"
#include "ai_navigator.h"
#include "asw_alien.h"
#include "particle_parse.h"
#include "particles/particles.h"
#include "asw_director.h"
#include "asw_shaman.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define GROUNDTURRET_BEAM_SPRITE "materials/swarm/effects/greenlaser1.vmt"
static int s_pHealEffectIndex;
BEGIN_DATADESC( CAI_ASW_HealOtherBehavior )
DEFINE_FIELD( m_flHealDistance, FIELD_FLOAT ),
DEFINE_FIELD( m_flHealAmount, FIELD_FLOAT ),
DEFINE_FIELD( m_hHealTargetEnt, FIELD_EHANDLE ),
DEFINE_FIELD( m_flDeferUntil, FIELD_TIME ),
END_DATADESC();
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_HealOtherBehavior );
ConVar asw_shaman_aim_ahead_time( "asw_shaman_aim_ahead_time", "1.0", FCVAR_CHEAT, "Look ahead time for shaman's heal target" );
//------------------------------------------------------------------------------
// Purpose: constructor
//------------------------------------------------------------------------------
CAI_ASW_HealOtherBehavior::CAI_ASW_HealOtherBehavior( )
{
m_flHealDistance = 300.0f;
m_flApproachDistance = 100.0f;
m_flHealAmount = 0.10f;
m_flHealConsiderationDistance = 600.0f;
m_flHealConsiderationDistanceSquared = m_flHealConsiderationDistance * m_flHealConsiderationDistance;
m_flDeferUntil = gpGlobals->curtime;
m_bIsHealing = false;
}
//------------------------------------------------------------------------------
// Purpose: function to set up parameters
// Input : szKeyName - the name of the key
// szValue - the value to be set
// Output : returns true of we handled this key
//------------------------------------------------------------------------------
bool CAI_ASW_HealOtherBehavior::KeyValue( const char *szKeyName, const char *szValue )
{
if ( V_stricmp( szKeyName, "heal_distance" ) == 0 )
{
m_flHealDistance = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "approach_distance" ) == 0 )
{
m_flApproachDistance = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "heal_amount" ) == 0 )
{
m_flHealAmount = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "consideration_distance" ) == 0 )
{
m_flHealConsiderationDistance = atof( szValue );
m_flHealConsiderationDistanceSquared = m_flHealConsiderationDistance * m_flHealConsiderationDistance;
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
//------------------------------------------------------------------------------
// Purpose: precaches any additional assets this behavior needs
//------------------------------------------------------------------------------
void CAI_ASW_HealOtherBehavior::Precache( void )
{
BaseClass::Precache();
CBaseEntity::PrecacheModel( GROUNDTURRET_BEAM_SPRITE );
// PRECACHE_INDEX( PARTICLE_SYSTEM, "impact_puddle_pring", s_pHealEffectIndex );
PrecacheParticleSystem( "heal_giver" );
PrecacheParticleSystem( "heal_receiver" );
}
//------------------------------------------------------------------------------
// Purpose: determines if we can use this behavior currently
// Output : returns true if this behavior is able to run
//------------------------------------------------------------------------------
bool CAI_ASW_HealOtherBehavior::CanSelectSchedule()
{
if ( !GetOuter()->IsInterruptable() )
{
return false;
}
if ( HasCondition( COND_HEAL_OTHER_NOT_HAS_TARGET ) )
{
return false;
}
if ( m_flDeferUntil > gpGlobals->curtime )
{
return false;
}
return BaseClass::CanSelectSchedule();
}
//------------------------------------------------------------------------------
// Purpose: sets / clears conditions for when the behavior is active. this is
// generally a larger set of conditions to interrupt any tasks.
//------------------------------------------------------------------------------
void CAI_ASW_HealOtherBehavior::GatherConditions( )
{
BaseClass::GatherConditions();
ClearCondition( COND_HEAL_OTHER_HAS_FULL_HEALTH ); // needed?
if ( GetTarget() != NULL )
{
if ( GetTarget()->m_iHealth == GetTarget()->m_iMaxHealth )
{
SetCondition( COND_HEAL_OTHER_HAS_FULL_HEALTH );
}
}
}
//------------------------------------------------------------------------------
// Purpose: sets / clears conditions for when the behavior is not active. this is
// mainly to have a smaller set of conditions to wake up the behavior.
//------------------------------------------------------------------------------
void CAI_ASW_HealOtherBehavior::GatherConditionsNotActive( )
{
BaseClass::GatherConditionsNotActive();
}
//------------------------------------------------------------------------------
// Purpose: general purpose routine to collect conditions used both during active
// and non-active states of the behavior.
//------------------------------------------------------------------------------
void CAI_ASW_HealOtherBehavior::GatherCommonConditions( )
{
BaseClass::GatherCommonConditions();
// ClearCondition( COND_HEAL_OTHER_HAS_TARGET );
SetCondition( COND_HEAL_OTHER_NOT_HAS_TARGET );
ClearCondition( COND_HEAL_OTHER_TARGET_CHANGED );
ClearCondition( COND_HEAL_OTHER_TARGET_IN_RANGE );
ClearCondition( COND_HEAL_OTHER_NOT_TARGET_IN_RANGE );
CBaseEntity *pBestObject = NULL;
int nPotentialHealthBenefit = 0;
for( int i = 0; i < GetOuter()->GetNumFactions(); i++ )
{
if ( GetOuter()->GetFactionRelationshipDisposition( i ) == D_LIKE )
{
CUtlVector< EHANDLE > *pEntityList = GetOuter()->GetEntitiesInFaction( i );
for( int j = 0; j < pEntityList->Count(); j++ )
{
CBaseEntity *pEntity = pEntityList->Element( j );
if ( pEntity->GetHealth() <= 0 )
continue;
Class_T entClass = pEntity->Classify();
if ( entClass == CLASS_ASW_SHAMAN || entClass == CLASS_ASW_BOOMER || entClass == CLASS_ASW_BOOMERMINI
|| entClass == CLASS_ASW_PARASITE || entClass == CLASS_ASW_BLOB || entClass == CLASS_ASW_BUZZER
|| entClass == CLASS_ASW_RUNNER )
{
continue;
}
if ( GetOuter() != pEntity )
{
Vector vDiff = pEntity->GetAbsOrigin() - GetAbsOrigin();
float flLengthSquared = vDiff.LengthSqr();
if ( flLengthSquared <= m_flHealConsiderationDistanceSquared )
//&& ( !asw_shaman_only_heal_hurt_pEntity->m_iHealth < pEntity->m_iMaxHealth )
{
if ( nPotentialHealthBenefit == 0 || ( pEntity->m_iMaxHealth - pEntity->m_iHealth ) > nPotentialHealthBenefit )
{
nPotentialHealthBenefit = ( pEntity->m_iMaxHealth - pEntity->m_iHealth );
pBestObject = pEntity;
}
}
}
}
}
}
if ( pBestObject )
{
if ( m_hHealTargetEnt != NULL && m_hHealTargetEnt != pBestObject )
{
SetCondition( COND_HEAL_OTHER_TARGET_CHANGED );
}
// SetCondition( COND_HEAL_OTHER_HAS_TARGET );
ClearCondition( COND_HEAL_OTHER_NOT_HAS_TARGET );
m_hHealTargetEnt = pBestObject;
if ( m_hHealTargetEnt.Get() )
{
Vector vPredictedPos = m_hHealTargetEnt->GetAbsOrigin() + m_hHealTargetEnt->GetAbsVelocity() * asw_shaman_aim_ahead_time.GetFloat();
Vector vDiff = vPredictedPos - GetAbsOrigin();
if ( vDiff.Length() < m_flHealDistance )
{
SetCondition( COND_HEAL_OTHER_TARGET_IN_RANGE );
}
else
{
SetCondition( COND_HEAL_OTHER_NOT_TARGET_IN_RANGE );
}
}
}
else
{
if ( m_hHealTargetEnt != NULL && m_hHealTargetEnt != pBestObject )
{
SetCondition( COND_HEAL_OTHER_TARGET_CHANGED );
}
m_hHealTargetEnt = NULL;
}
}
//------------------------------------------------------------------------------
// Purpose: routine called to start when a task initially starts
// Input : pTask - the task structure
//------------------------------------------------------------------------------
void CAI_ASW_HealOtherBehavior::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_HEAL_OTHER_FIND_TARGET:
{
if ( m_hHealTargetEnt )
{
AI_NavGoal_t goal( GOALTYPE_TARGETENT, ACT_RUN, m_flApproachDistance, AIN_YAW_TO_DEST | AIN_UPDATE_TARGET_POS, m_hHealTargetEnt );
SetTarget( m_hHealTargetEnt );
GetNavigator()->SetArrivalDistance( m_flApproachDistance );
GetNavigator()->SetGoal( goal );
}
else
{
TaskFail( FAIL_NO_TARGET );
m_flDeferUntil = gpGlobals->curtime + 3.0f;
}
break;
}
case TASK_HEAL_OTHER_MOVE_TO_TARGET:
{
break;
}
default:
BaseClass::StartTask( pTask );
break;
}
}
void CAI_ASW_HealOtherBehavior::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_HEAL_OTHER_MOVE_TO_TARGET:
{
if ( GetNavigator()->GetGoalType() == GOALTYPE_NONE )
{
TaskComplete();
}
if ( m_hHealTargetEnt.Get() )
{
Vector vDiff = m_hHealTargetEnt->GetAbsOrigin() - GetAbsOrigin();
if ( vDiff.LengthSqr() <= ( m_flApproachDistance * m_flApproachDistance ) )
{
GetNavigator()->StopMoving( true );
GetNavigator()->ClearGoal();
TaskComplete();
}
}
break;
}
default:
BaseClass::RunTask( pTask );
break;
}
}
CBaseEntity* CAI_ASW_HealOtherBehavior::GetCurrentHealTarget()
{
if ( m_hHealTargetEnt.Get() )
{
Vector vDiff = m_hHealTargetEnt->GetAbsOrigin() - GetAbsOrigin();
if ( vDiff.Length() <= m_flHealDistance )
//&& GetOuter()->FInAimCone( m_hHealTargetEnt->BodyTarget( GetOuter()->WorldSpaceCenter() ) )
{
return m_hHealTargetEnt.Get();
}
}
return NULL;
}
//------------------------------------------------------------------------------
// Purpose: routine called to select what schedule we want to run
// Output : returns the schedule id of the schedule we want to run
//------------------------------------------------------------------------------
int CAI_ASW_HealOtherBehavior::SelectSchedule()
{
if ( HasCondition( COND_HEAL_OTHER_NOT_HAS_TARGET ) || HasCondition( COND_HEAL_OTHER_HAS_FULL_HEALTH ) || HasCondition( COND_HEAL_OTHER_NOT_TARGET_IN_RANGE ) || HasCondition( COND_HEAL_OTHER_TARGET_CHANGED ) )
{
return SCHED_HEAL_OTHER_MOVE_TO_CANDIDATE;
}
else
{
return SCHED_HEAL_WAIT;
}
// return BaseClass::SelectSchedule();
}
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_HealOtherBehavior )
DECLARE_TASK( TASK_HEAL_OTHER_FIND_TARGET )
DECLARE_TASK( TASK_HEAL_OTHER_MOVE_TO_TARGET )
// DECLARE_CONDITION( COND_HEAL_OTHER_HAS_TARGET )
DECLARE_CONDITION( COND_HEAL_OTHER_NOT_HAS_TARGET )
DECLARE_CONDITION( COND_HEAL_OTHER_HAS_FULL_HEALTH )
DECLARE_CONDITION( COND_HEAL_OTHER_TARGET_CHANGED )
DECLARE_CONDITION( COND_HEAL_OTHER_TARGET_IN_RANGE )
DECLARE_CONDITION( COND_HEAL_OTHER_NOT_TARGET_IN_RANGE )
//===============================================
//===============================================
DEFINE_SCHEDULE
(
SCHED_HEAL_OTHER_MOVE_TO_CANDIDATE,
" Tasks"
" TASK_HEAL_OTHER_FIND_TARGET 0"
" TASK_HEAL_OTHER_MOVE_TO_TARGET 0"
// " TASK_RUN_PATH 0"
// " TASK_WAIT_FOR_MOVEMENT 0"
// " TASK_SET_SCHEDULE SCHEDULE:SCHED_HEAL_OTHER_DO_HEAL"
""
" Interrupts"
""
" COND_ENEMY_DEAD"
" COND_HEAL_OTHER_TARGET_CHANGED"
// " COND_HEAL_OTHER_TARGET_IN_RANGE"
);
DEFINE_SCHEDULE
(
SCHED_HEAL_WAIT,
" Tasks"
//" TASK_HEAL_OTHER_DO_HEAL 0"
//" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_WAIT 0.1"
""
" Interrupts"
""
// " COND_HEAL_OTHER_HAS_FULL_HEALTH"
// " COND_HEAL_OTHER_TARGET_CHANGED"
// " COND_HEAL_OTHER_NOT_HAS_TARGET"
// " COND_HEAL_OTHER_NOT_TARGET_IN_RANGE"
);
AI_END_CUSTOM_SCHEDULE_PROVIDER()
#include "tier0/memdbgoff.h"