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.
462 lines
13 KiB
462 lines
13 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: NPC does a pounce attack against his enemy when close enough. Uses MeleeAttack2Conditions. |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "beam_shared.h" |
|
#include "ai_motor.h" |
|
#include "asw_ai_behavior_pounce.h" |
|
#include "ai_hint.h" |
|
#include "ai_navigator.h" |
|
#include "ai_memory.h" |
|
#include "asw_alien.h" |
|
#include "asw_marine.h" |
|
#include "asw_gamerules.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
BEGIN_DATADESC( CAI_ASW_PounceBehavior ) |
|
END_DATADESC(); |
|
|
|
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_PounceBehavior ); |
|
|
|
Activity ACT_ALIEN_POUNCE; |
|
Activity ACT_ALIEN_POUNCE_HIT; |
|
Activity ACT_ALIEN_POUNCE_MISS; |
|
|
|
#define POUNCE_THINK_CONTEXT "PounceThinkContext" |
|
|
|
extern ConVar sv_gravity; |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: constructor |
|
//------------------------------------------------------------------------------ |
|
CAI_ASW_PounceBehavior::CAI_ASW_PounceBehavior( ) |
|
{ |
|
m_flMinRange = 0.0f; |
|
m_flMaxRange = 100.0f; |
|
m_flAttackDotAngle = 0.7f; |
|
m_flPounceInterval = 0.0f; |
|
m_flNextPounceTime = 0.0f; |
|
m_flPounceDistance = 300.0f; |
|
m_flPounceDamage = 15.0f; |
|
m_flIgnoreWorldCollisionTime = 0.0f; |
|
m_bMidPounce = 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_PounceBehavior::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
if ( V_stricmp( szKeyName, "min_range" ) == 0 ) |
|
{ |
|
m_flMinRange = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "max_range" ) == 0 ) |
|
{ |
|
m_flMaxRange = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "attack_dot_angle" ) == 0 ) |
|
{ |
|
m_flAttackDotAngle = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "pounce_interval" ) == 0 ) |
|
{ |
|
m_flPounceInterval = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "pounce_distance" ) == 0 ) |
|
{ |
|
m_flPounceDistance = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "pounce_damage" ) == 0 ) |
|
{ |
|
m_flPounceDamage = atof( szValue ); |
|
return true; |
|
} |
|
|
|
|
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: precaches any additional assets this behavior needs |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_PounceBehavior::Precache( void ) |
|
{ |
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_PounceBehavior::Init( ) |
|
{ |
|
CASW_Alien *pNPC = static_cast< CASW_Alien * >( GetOuter() ); |
|
if ( !pNPC ) |
|
{ |
|
return; |
|
} |
|
|
|
pNPC->meleeAttack2.Init( m_flMinRange, m_flMaxRange, m_flAttackDotAngle, true ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: determines if we can use this behavior currently |
|
// Output : returns true if this behavior is able to run |
|
//------------------------------------------------------------------------------ |
|
bool CAI_ASW_PounceBehavior::CanSelectSchedule() |
|
{ |
|
if ( !GetOuter()->IsInterruptable() ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( gpGlobals->curtime < m_flNextPounceTime ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !HasCondition( COND_CAN_MELEE_ATTACK2 ) && !HasCondition( COND_TOO_CLOSE_TO_ATTACK ) ) |
|
{ |
|
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_PounceBehavior::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_PounceBehavior::GatherConditionsNotActive( ) |
|
{ |
|
BaseClass::GatherConditionsNotActive(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: general purpose routine to collect conditions used both during active |
|
// and non-active states of the behavior. |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_PounceBehavior::GatherCommonConditions( ) |
|
{ |
|
BaseClass::GatherCommonConditions(); |
|
} |
|
|
|
|
|
void CAI_ASW_PounceBehavior::BeginScheduleSelection( ) |
|
{ |
|
SetBehaviorParam( m_StatusParm, -1 ); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// 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_PounceBehavior::SelectSchedule() |
|
{ |
|
if ( HasCondition(COND_TOO_CLOSE_TO_ATTACK) ) |
|
return SCHED_BACK_AWAY_FROM_ENEMY; |
|
|
|
m_flNextPounceTime = gpGlobals->curtime + m_flPounceInterval; |
|
|
|
return SCHED_ALIEN_POUNCE; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_PounceBehavior::HandleBehaviorEvent( CBaseEntity *pInflictor, BehaviorEvent_t eEvent, int nParm ) |
|
{ |
|
switch( eEvent ) |
|
{ |
|
case BEHAVIOR_EVENT_POUNCE: |
|
{ |
|
CASW_Alien *pOwner = static_cast< CASW_Alien * >( GetOuter() ); |
|
|
|
if ( pOwner->GetEnemy() ) |
|
{ |
|
// jump straight ahead |
|
Vector vecForward; |
|
AngleVectors( pOwner->EyeAngles(), &vecForward ); |
|
vecForward.z = 0; |
|
vecForward.NormalizeInPlace(); |
|
PounceAttack( false, GetAbsOrigin() + vecForward * m_flPounceDistance ); |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
#define ALIEN_IGNORE_WORLD_COLLISION_TIME 0.5 |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Does a jump attack at the given position. |
|
// Input : bRandomJump - Just hop in a random direction. |
|
// vecPos - Position to jump at, ignored if bRandom is set to true. |
|
// bThrown - |
|
//----------------------------------------------------------------------------- |
|
void CAI_ASW_PounceBehavior::PounceAttack( bool bRandomJump, const Vector &vecPos, bool bThrown, float flBaseHeight, float flAdditionalHeight ) |
|
{ |
|
Vector vecJumpVel; |
|
if ( !bRandomJump ) |
|
{ |
|
float gravity = sv_gravity.GetFloat(); |
|
if ( gravity <= 1 ) |
|
{ |
|
gravity = 1; |
|
} |
|
|
|
// How fast does the headcrab need to travel to reach the position given gravity? |
|
float flActualHeight = vecPos.z - GetAbsOrigin().z; |
|
float height = flActualHeight; |
|
if ( height < 16 ) |
|
{ |
|
height = flBaseHeight; //60; //16; |
|
} |
|
else |
|
{ |
|
float flMaxHeight = bThrown ? 400 : 120; |
|
if ( height > flMaxHeight ) |
|
{ |
|
height = flMaxHeight; |
|
} |
|
} |
|
|
|
// overshoot the jump by an additional 8 inches |
|
// NOTE: This calculation jumps at a position INSIDE the box of the enemy (player) |
|
// so if you make the additional height too high, the crab can land on top of the |
|
// enemy's head. If we want to jump high, we'll need to move vecPos to the surface/outside |
|
// of the enemy's box. |
|
|
|
float additionalHeight = 0; |
|
if ( height < 32 ) |
|
{ |
|
additionalHeight = flAdditionalHeight; |
|
} |
|
|
|
height += additionalHeight; |
|
|
|
// NOTE: This equation here is from vf^2 = vi^2 + 2*a*d |
|
float speed = sqrt( 2 * gravity * height ); |
|
float time = speed / gravity; |
|
|
|
// add in the time it takes to fall the additional height |
|
// So the impact takes place on the downward slope at the original height |
|
time += sqrt( (2 * additionalHeight) / gravity ); |
|
|
|
// Scale the sideways velocity to get there at the right time |
|
VectorSubtract( vecPos, GetAbsOrigin(), vecJumpVel ); |
|
vecJumpVel /= time; |
|
|
|
// Speed to offset gravity at the desired height. |
|
vecJumpVel.z = speed; |
|
|
|
// Don't jump too far/fast. |
|
float flJumpSpeed = vecJumpVel.Length(); |
|
float flMaxSpeed = bThrown ? 1000.0f : 650.0f; |
|
if ( flJumpSpeed > flMaxSpeed ) |
|
{ |
|
vecJumpVel *= flMaxSpeed / flJumpSpeed; |
|
} |
|
} |
|
else |
|
{ |
|
// |
|
// Jump hop, don't care where. |
|
// |
|
Vector forward, up; |
|
AngleVectors( GetLocalAngles(), &forward, NULL, &up ); |
|
vecJumpVel = Vector( forward.x, forward.y, up.z ) * 350; |
|
} |
|
|
|
//AttackSound(); |
|
Leap( vecJumpVel ); |
|
} |
|
|
|
void CAI_ASW_PounceBehavior::Leap( const Vector &vecVel ) |
|
{ |
|
m_bMidPounce = true; |
|
|
|
SetCondition( COND_FLOATING_OFF_GROUND ); |
|
SetGroundEntity( NULL ); |
|
|
|
m_flIgnoreWorldCollisionTime = gpGlobals->curtime + ALIEN_IGNORE_WORLD_COLLISION_TIME; |
|
|
|
if( HasHeadroom() ) |
|
{ |
|
// Take him off ground so engine doesn't instantly reset FL_ONGROUND. |
|
UTIL_SetOrigin( GetOuter(), GetLocalOrigin() + Vector( 0, 0, 1 ) ); |
|
} |
|
|
|
GetOuter()->SetAbsVelocity( vecVel ); |
|
|
|
// think every frame so the player sees the alien where it is |
|
|
|
GetOuter()->SetContextThink( &CASW_Alien::CallBehaviorThink, gpGlobals->curtime, POUNCE_THINK_CONTEXT ); |
|
} |
|
|
|
bool CAI_ASW_PounceBehavior::HasHeadroom() |
|
{ |
|
trace_t tr; |
|
UTIL_TraceEntity( GetOuter(), GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, 1 ), MASK_NPCSOLID, GetOuter(), GetCollisionGroup(), &tr ); |
|
|
|
return (tr.fraction == 1.0); |
|
} |
|
|
|
void CAI_ASW_PounceBehavior::BehaviorThink( void ) |
|
{ |
|
if( GetOuter()->GetFlags() & FL_ONGROUND && gpGlobals->curtime >= m_flIgnoreWorldCollisionTime ) |
|
{ |
|
GetOuter()->SetAbsVelocity( vec3_origin ); |
|
|
|
GetOuter()->SetContextThink( NULL, TICK_NEVER_THINK, POUNCE_THINK_CONTEXT ); |
|
return; |
|
} |
|
|
|
GetOuter()->SetContextThink( &CASW_Alien::CallBehaviorThink, gpGlobals->curtime, POUNCE_THINK_CONTEXT ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: LeapTouch - this is the headcrab's touch function when it is in the air. |
|
// Input : *pOther - |
|
//----------------------------------------------------------------------------- |
|
|
|
void CAI_ASW_PounceBehavior::StartTouch( CBaseEntity *pOther ) |
|
{ |
|
BaseClass::StartTouch( pOther ); |
|
|
|
if ( !m_bMidPounce ) |
|
return; |
|
|
|
if ( !pOther->ShouldCollide( GetCollisionGroup(), GetOuter()->PhysicsSolidMaskForEntity() ) ) |
|
return; |
|
|
|
if ( !g_pGameRules->ShouldCollide( GetCollisionGroup(), pOther->GetCollisionGroup() ) ) |
|
return; |
|
|
|
bool bHit = false; |
|
if ( GetOuter()->IRelationType( pOther ) == D_HT ) |
|
{ |
|
if ( pOther->m_takedamage != DAMAGE_NO && !pOther->IsWorld() ) |
|
{ |
|
Vector vecForceDir = GetOuter()->GetAbsVelocity(); |
|
vecForceDir.NormalizeInPlace(); |
|
// TODO: Check they're not behind me |
|
CASW_Marine *pMarine = CASW_Marine::AsMarine( pOther ); |
|
if ( pMarine ) |
|
{ |
|
pMarine->Stumble( GetOuter(), vecForceDir, false ); |
|
} |
|
|
|
float flDamage = ASWGameRules()->ModifyAlienDamageBySkillLevel( m_flPounceDamage ); |
|
flDamage = MAX( flDamage, 1.0f ); |
|
|
|
CTakeDamageInfo info( GetOuter(), GetOuter(), flDamage, DMG_SLASH ); |
|
const trace_t &touchTrace = GetOuter()->GetTouchTrace(); |
|
CalculateMeleeDamageForce( &info, vecForceDir, touchTrace.endpos ); |
|
pOther->TakeDamage( info ); |
|
|
|
bHit = true; |
|
} |
|
} |
|
else if( !(GetOuter()->GetFlags() & FL_ONGROUND) ) |
|
{ |
|
// Still in the air... |
|
if( gpGlobals->curtime < m_flIgnoreWorldCollisionTime ) |
|
{ |
|
// Headcrabs try to ignore the world, static props, and friends for a |
|
// fraction of a second after they jump. This is because they often brush |
|
// doorframes or props as they leap, and touching those objects turns off |
|
// this touch function, which can cause them to hit the player and not bite. |
|
// A timer probably isn't the best way to fix this, but it's one of our |
|
// safer options at this point (sjb). |
|
return; |
|
} |
|
|
|
if( !pOther->IsSolid() ) |
|
{ |
|
// Touching a trigger or something. |
|
return; |
|
} |
|
} |
|
|
|
GetOuter()->SetAbsVelocity( vec3_origin ); |
|
|
|
if ( bHit ) |
|
{ |
|
if ( GetOuter()->HaveSequenceForActivity( (Activity) ACT_ALIEN_POUNCE_HIT ) ) |
|
{ |
|
GetOuter()->SetActivity( (Activity) ACT_ALIEN_POUNCE_HIT ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( GetOuter()->HaveSequenceForActivity( (Activity) ACT_ALIEN_POUNCE_MISS ) ) |
|
{ |
|
GetOuter()->SetActivity( (Activity) ACT_ALIEN_POUNCE_MISS ); |
|
} |
|
} |
|
|
|
// make sure we're solid |
|
GetOuter()->RemoveSolidFlags( FSOLID_NOT_SOLID ); |
|
|
|
m_bMidPounce = false; |
|
} |
|
|
|
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_PounceBehavior ) |
|
|
|
DECLARE_ACTIVITY( ACT_ALIEN_POUNCE ) |
|
DECLARE_ACTIVITY( ACT_ALIEN_POUNCE_HIT ) |
|
DECLARE_ACTIVITY( ACT_ALIEN_POUNCE_MISS ) |
|
|
|
// forward pounce attack |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_POUNCE, |
|
|
|
" Tasks" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_FACE_ENEMY 0" |
|
" TASK_ANNOUNCE_ATTACK 1" |
|
" TASK_RESET_ACTIVITY 0" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_ALIEN_POUNCE" |
|
|
|
" Interrupts" |
|
" COND_TASK_FAILED" |
|
) |
|
|
|
AI_END_CUSTOM_SCHEDULE_PROVIDER() |
|
|
|
#include "tier0/memdbgoff.h"
|
|
|