1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-09 18:48:51 +08:00
hl2sdk/game/server/swarm/asw_ai_behavior_explode.cpp
2010-07-22 01:46:14 -05:00

482 lines
14 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "beam_shared.h"
#include "ai_motor.h"
#include "asw_ai_behavior_explode.h"
#include "ai_hint.h"
#include "ai_navigator.h"
#include "ai_memory.h"
#include "asw_alien.h"
#include "asw_boomer_blob.h"
#include "particle_parse.h"
#include "asw_gamerules.h"
#include "asw_boomer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_DATADESC( CAI_ASW_ExplodeBehavior )
END_DATADESC();
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_ExplodeBehavior );
//------------------------------------------------------------------------------
// Purpose: constructor
//------------------------------------------------------------------------------
CAI_ASW_ExplodeBehavior::CAI_ASW_ExplodeBehavior( )
{
m_ExplodeType = EXPLODE_TYPE_PUFF_PROJECTILES;
m_flRange = 100.0f;
m_nMaxProjectiles = 3;
m_flMinVelocity = 150.0f;
m_flMaxVelocity = 300.0f;
m_AttachName = UTL_INVAL_SYMBOL;
m_nAttachCount = 0;
m_flStartBuildup = -1.0f;
m_flStartExplodeTime = -1.0f;
m_flMaxBuildupTime = 3.0f;
m_flBuildupApproachDist = 200.0f;
m_flDamageAmount = 10.0f;
m_flDamageRadius = 200.0f; // NOTE: this gets overridden
}
//------------------------------------------------------------------------------
// 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_ExplodeBehavior::KeyValue( const char *szKeyName, const char *szValue )
{
if ( V_stricmp( szKeyName, "type" ) == 0 )
{
if ( V_stricmp( szValue, "suicide" ) == 0 )
{
m_ExplodeType = EXPLODE_TYPE_SUICIDE;
}
return true;
}
else if ( V_stricmp( szKeyName, "range" ) == 0 )
{
m_flRange = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "min_velocity" ) == 0 )
{
m_flMinVelocity = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "max_velocity" ) == 0 )
{
m_flMaxVelocity = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "attach_name" ) == 0 )
{
m_AttachName = GetSymbol( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "attach_count" ) == 0 )
{
m_nAttachCount = atoi( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "max_projectiles" ) == 0 )
{
m_nMaxProjectiles = atoi( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "max_buildup_time" ) == 0 )
{
m_flMaxBuildupTime = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "damage" ) == 0 )
{
m_flDamageAmount = atof( szValue );
return true;
}
else if ( V_stricmp( szKeyName, "radius" ) == 0 )
{
m_flDamageRadius = atof( szValue );
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
//------------------------------------------------------------------------------
// Purpose: precaches any additional assets this behavior needs
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::Precache( void )
{
BaseClass::Precache();
}
//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::Init( )
{
CASW_Alien *pNPC = static_cast< CASW_Alien * >( GetOuter() );
if ( !pNPC )
{
return;
}
if ( pNPC->Classify() != CLASS_ASW_BOOMER )
{
Error( "Explode behavior added to non-boomer alien class\n" );
}
pNPC->meleeAttack2.Init( 0.0f, m_flRange, COMBAT_COND_NO_FACING_CHECK, true );
}
//------------------------------------------------------------------------------
// Purpose: determines if we can use this behavior currently
// Output : returns true if this behavior is able to run
//------------------------------------------------------------------------------
bool CAI_ASW_ExplodeBehavior::CanSelectSchedule()
{
if ( !GetOuter()->IsInterruptable() )
{
return false;
}
if ( !HasCondition( COND_CAN_MELEE_ATTACK2 ) )
{
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_ExplodeBehavior::GatherConditions( )
{
BaseClass::GatherConditions();
}
//------------------------------------------------------------------------------
// 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_ExplodeBehavior::GatherConditionsNotActive( )
{
BaseClass::GatherConditionsNotActive();
}
//------------------------------------------------------------------------------
// Purpose: general purpose routine to collect conditions used both during active
// and non-active states of the behavior.
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::GatherCommonConditions( )
{
BaseClass::GatherCommonConditions();
}
void CAI_ASW_ExplodeBehavior::MoveTowardsPlayer( )
{
CBaseEntity *pEnemy = GetEnemy();
Activity IdealActivity = ACT_IDLE;
if ( pEnemy )
{
AI_NavGoal_t goal( GOALTYPE_ENEMY, ACT_RUN, m_flBuildupApproachDist, AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE, AIN_DEF_TARGET );
float flDistSq = ( GetAbsOrigin() - pEnemy->GetAbsOrigin() ).LengthSqr();
if ( flDistSq > ( m_flBuildupApproachDist * m_flBuildupApproachDist ) && GetNavigator()->SetGoal( goal, AIN_NO_PATH_TASK_FAIL ) )
{
IdealActivity = ACT_RUN;
GetNavigator()->SetMovementActivity( IdealActivity );
}
}
GetOuter()->SetIdealActivity( IdealActivity );
}
//------------------------------------------------------------------------------
// Purpose: routine called to start when a task initially starts
// Input : pTask - the task structure
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::StartTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_EXPLODE_PREPARE_BUILDUP:
{
GetOuter()->SetIdealActivity( ACT_PREP_EXPLODE );
CASW_Boomer *pBoomer = assert_cast<CASW_Boomer*>( GetOuter() );
pBoomer->SetInflating( true );
m_flStartExplodeTime = gpGlobals->curtime;
}
break;
case TASK_EXPLODE_BEGIN_BUILDUP:
{
MoveTowardsPlayer();
CASW_Boomer *pBoomer = assert_cast<CASW_Boomer*>( GetOuter() );
pBoomer->SetInflating( true );
// GetOuter()->RestartGesture( ACT_EXPLODE, true, false );
//GetOuter()->AddGesture( ACT_EXPLODE, false );
// m_iSecondaryLayer = GetOuter()->AddLayeredSequence( GetOuter()->SelectWeightedSequence( ACT_EXPLODE ), 0 );
// GetOuter()->SetLayerWeight( m_iSecondaryLayer, 0.0 );
// GetOuter()->SetLayerPlaybackRate( m_iSecondaryLayer, 1.0 );
m_flStartBuildup = gpGlobals->curtime;
m_flEndBuildup = m_flStartBuildup + m_flMaxBuildupTime;
}
break;
case TASK_EXPLODE_KILL_SELF:
{
CTakeDamageInfo info( GetOuter(), GetOuter(), GetOuter()->GetMaxHealth() * 2, DMG_GENERIC );
GetOuter()->m_takedamage = DAMAGE_YES;
GetOuter()->OnTakeDamage( info );
DoExplosion();
}
break;
case TASK_EXPLODE_SUICIDE:
{
CTakeDamageInfo info( GetOuter(), GetOuter(), GetOuter()->GetMaxHealth() * 2, DMG_GENERIC );
GetOuter()->m_takedamage = DAMAGE_YES;
GetOuter()->OnTakeDamage( info );
}
break;
default:
BaseClass::StartTask( pTask );
break;
}
}
//------------------------------------------------------------------------------
// Purpose: routine called every frame when a task is running
// Input : pTask - the task structure
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::RunTask( const Task_t *pTask )
{
switch( pTask->iTask )
{
case TASK_EXPLODE_PREPARE_BUILDUP:
if ( GetOuter()->IsActivityFinished() )
{
TaskComplete();
}
break;
case TASK_EXPLODE_BEGIN_BUILDUP:
if ( m_flEndBuildup <= gpGlobals->curtime )
{
TaskComplete();
}
else if ( GetNavigator()->GetGoalType() == GOALTYPE_NONE || !GetNavigator()->IsGoalActive() )
{
MoveTowardsPlayer();
}
break;
case TASK_EXPLODE_KILL_SELF:
TaskComplete();
break;
case TASK_EXPLODE_SUICIDE:
TaskComplete();
break;
default:
BaseClass::RunTask( pTask );
break;
}
}
//------------------------------------------------------------------------------
// 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_ExplodeBehavior::SelectSchedule()
{
if ( m_ExplodeType == EXPLODE_TYPE_SUICIDE )
{
return SCHED_EXPLODE_SUICIDE;
}
else
{
return SCHED_EXPLODE_PREPARE;
}
}
//------------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::DoExplosion( )
{
m_ExplodeType = EXPLODE_TYPE_EXPLODED; // prevent recursion
Vector vPosition = GetAbsOrigin();
int *pnAvailList = ( int * )stackalloc( sizeof( int ) * m_nAttachCount );
int nNumAvail = -1;
if ( m_flStartExplodeTime < 0.0f )
{
return;
}
for( int i = 0; i < m_nAttachCount; i++ )
{
pnAvailList[ i ] = i + 1;
}
int nPrepSequence = GetOuter()->SelectWeightedSequence( ACT_PREP_EXPLODE );
float flPrepTime = GetOuter()->SequenceDuration( nPrepSequence );
float flPercent = ( gpGlobals->curtime - m_flStartExplodeTime ) / ( m_flMaxBuildupTime + flPrepTime );
int nCount = ( m_nMaxProjectiles + 1 ) * flPercent;
nCount = clamp( nCount, 1, m_nMaxProjectiles );
m_flStartExplodeTime = m_flStartBuildup = -1.0f; // to prevent a double call
for( int i = 0; i < nCount; i++ )
{
Vector vSpawn;
QAngle vAngles;
Vector vVelocity;
//char temp[ 64 ];
int nPosition;
if ( nNumAvail < 0 )
{
nNumAvail = m_nAttachCount - 1;
}
nPosition = RandomInt( 0, nNumAvail );
int nSelected = pnAvailList[ nPosition ];
pnAvailList[ nPosition ] = pnAvailList[ nNumAvail ];
pnAvailList[ nNumAvail ] = nSelected;
nNumAvail--;
//V_snprintf( temp, sizeof( temp ), "%s%02d", GetSymbolText( m_AttachName ), nSelected );
GetOuter()->GetAttachment( "attach_explosion", vSpawn, vAngles );
Vector vCenter = vSpawn;
Vector vOffset = RandomVector( -10, 10 );
if ( vOffset == vec3_origin )
{
vOffset = Vector( 1, 0, 1 );
}
vSpawn += vOffset;
vVelocity = vSpawn - vCenter;
vVelocity.NormalizeInPlace();
vVelocity.z = fabs( vVelocity.z );
//Msg( "Velocity base = %f %f %f\n", VectorExpand( vVelocity ) );
vVelocity *= RandomFloat( m_flMinVelocity, m_flMaxVelocity );
vAngles = vec3_angle;
CASW_Boomer_Blob::Boomer_Blob_Create( m_flDamageAmount, m_flDamageRadius, 0, vSpawn, vAngles, vVelocity, AngularImpulse( 0.0f, 0.0f, 0.0f ), GetOuter() );
}
}
//------------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::DoSuicideExplosion( )
{
m_ExplodeType = EXPLODE_TYPE_EXPLODED; // prevent recursion
// explosion effects
DispatchParticleEffect( "mortar_explosion", GetAbsOrigin(), Vector( m_flDamageRadius, 0.0f, 0.0f ), QAngle( 0.0f, 0.0f, 0.0f ) );
GetOuter()->EmitSound( "ASWGrenade.Explode" );
ASWGameRules()->RadiusDamage( CTakeDamageInfo( GetOuter(), GetOuter(), m_flDamageAmount, DMG_BLAST ), GetAbsOrigin(), m_flDamageRadius, CLASS_NONE, NULL );
}
//------------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//------------------------------------------------------------------------------
void CAI_ASW_ExplodeBehavior::HandleBehaviorEvent( CBaseEntity *pInflictor, BehaviorEvent_t eEvent, int nParm )
{
switch( eEvent )
{
case BEHAVIOR_EVENT_EXPLODE:
switch( m_ExplodeType )
{
case EXPLODE_TYPE_SUICIDE:
DoSuicideExplosion();
break;
case EXPLODE_TYPE_PUFF_PROJECTILES:
DoExplosion();
break;
}
break;
}
}
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_ExplodeBehavior )
DECLARE_TASK( TASK_EXPLODE_KILL_SELF )
DECLARE_TASK( TASK_EXPLODE_PREPARE_BUILDUP )
DECLARE_TASK( TASK_EXPLODE_BEGIN_BUILDUP )
DECLARE_TASK( TASK_EXPLODE_SUICIDE )
DEFINE_SCHEDULE
(
SCHED_EXPLODE_PREPARE,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_EXPLODE_PREPARE_BUILDUP 0"
" TASK_EXPLODE_BEGIN_BUILDUP 0"
" TASK_EXPLODE_KILL_SELF 0"
""
" Interrupts"
);
DEFINE_SCHEDULE
(
SCHED_EXPLODE_SUICIDE,
" Tasks"
" TASK_EXPLODE_SUICIDE 0"
""
" Interrupts"
);
AI_END_CUSTOM_SCHEDULE_PROVIDER()
#include "tier0/memdbgoff.h"