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.
454 lines
12 KiB
454 lines
12 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: NPC does a jump attack against his enemy when close enough. |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "beam_shared.h" |
|
#include "ai_motor.h" |
|
#include "ai_moveprobe.h" |
|
#include "asw_ai_behavior_jump.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" |
|
|
|
//Debug visualization |
|
ConVar asw_debug_jump_behavior( "asw_debug_jump_behavior", "0" ); |
|
|
|
BEGIN_DATADESC( CAI_ASW_JumpBehavior ) |
|
END_DATADESC(); |
|
|
|
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_JumpBehavior ); |
|
|
|
Activity ACT_ALIEN_JUMP; |
|
Activity ACT_ALIEN_LAND; |
|
|
|
extern ConVar sv_gravity; |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: constructor |
|
//------------------------------------------------------------------------------ |
|
CAI_ASW_JumpBehavior::CAI_ASW_JumpBehavior( ) |
|
{ |
|
m_bForcedStuckJump = false; |
|
m_bHasDoneAirAttack = false; |
|
m_flJumpTime = 0.0f; |
|
m_vecSavedJump.Init(); |
|
m_vecLastJumpAttempt.Init(); |
|
m_flMinRange = 512.0f; |
|
m_flJumpTimeMin = 2.0f; |
|
m_flJumpTimeMax = 6.0f; |
|
m_flJumpDamage = 4.0f; |
|
m_flDamageDistance = 100.0f; |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// 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_JumpBehavior::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, "jump_delay_min" ) == 0 ) |
|
{ |
|
m_flJumpTimeMin = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "jump_delay_max" ) == 0 ) |
|
{ |
|
m_flJumpTimeMax = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "damage_distance" ) == 0 ) |
|
{ |
|
m_flDamageDistance = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "jump_damage" ) == 0 ) |
|
{ |
|
m_flJumpDamage = atof( szValue ); |
|
return true; |
|
} |
|
|
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: precaches any additional assets this behavior needs |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_JumpBehavior::Precache( void ) |
|
{ |
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_JumpBehavior::Init( ) |
|
{ |
|
CASW_Alien *pNPC = static_cast< CASW_Alien * >( GetOuter() ); |
|
if ( !pNPC ) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: determines if we can use this behavior currently |
|
// Output : returns true if this behavior is able to run |
|
//------------------------------------------------------------------------------ |
|
bool CAI_ASW_JumpBehavior::CanSelectSchedule() |
|
{ |
|
if ( !GetOuter()->IsInterruptable() ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !ShouldJump() ) |
|
{ |
|
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_JumpBehavior::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_JumpBehavior::GatherConditionsNotActive( ) |
|
{ |
|
BaseClass::GatherConditionsNotActive(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: general purpose routine to collect conditions used both during active |
|
// and non-active states of the behavior. |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_JumpBehavior::GatherCommonConditions( ) |
|
{ |
|
//New Enemy? Try to jump at him. |
|
if ( HasCondition( COND_NEW_ENEMY ) ) |
|
{ |
|
m_flJumpTime = 0.0f; |
|
} |
|
|
|
BaseClass::GatherCommonConditions(); |
|
} |
|
|
|
|
|
void CAI_ASW_JumpBehavior::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_JumpBehavior::SelectSchedule() |
|
{ |
|
return SCHED_ALIEN_JUMP; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_JumpBehavior::HandleBehaviorEvent( CBaseEntity *pInflictor, BehaviorEvent_t eEvent, int nParm ) |
|
{ |
|
switch( eEvent ) |
|
{ |
|
case BEHAVIOR_EVENT_JUMP: |
|
{ |
|
if ( m_bForcedStuckJump == false ) |
|
{ |
|
//Don't jump if we're not on the ground |
|
if ( ( GetOuter()->GetFlags() & FL_ONGROUND ) == false ) |
|
return; |
|
} |
|
|
|
//Take us off the ground |
|
SetGroundEntity( NULL ); |
|
GetOuter()->SetAbsVelocity( m_vecSavedJump ); |
|
|
|
m_bForcedStuckJump = false; |
|
m_bHasDoneAirAttack = false; |
|
|
|
//Setup our jump time so that we don't try it again too soon |
|
m_flJumpTime = gpGlobals->curtime + random->RandomInt( m_flJumpTimeMin, m_flJumpTimeMax ); |
|
|
|
break; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CAI_ASW_JumpBehavior::StartTask( const Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_FACE_JUMP: |
|
break; |
|
|
|
case TASK_JUMP: |
|
if ( CheckLanding() ) |
|
{ |
|
TaskComplete(); |
|
} |
|
|
|
break; |
|
|
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pTask - |
|
//----------------------------------------------------------------------------- |
|
void CAI_ASW_JumpBehavior::RunTask( const Task_t *pTask ) |
|
{ |
|
// some state that needs be set each frame |
|
if ( GetOuter()->GetFlags() & FL_ONGROUND ) |
|
{ |
|
m_bHasDoneAirAttack = false; |
|
} |
|
|
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_FACE_JUMP: |
|
{ |
|
Vector jumpDir = m_vecSavedJump; |
|
VectorNormalize( jumpDir ); |
|
|
|
QAngle jumpAngles; |
|
VectorAngles( jumpDir, jumpAngles ); |
|
|
|
GetMotor()->SetIdealYawAndUpdate( jumpAngles[YAW], AI_KEEP_YAW_SPEED ); |
|
GetOuter()->SetTurnActivity(); |
|
|
|
if ( GetMotor()->DeltaIdealYaw() < 2 ) |
|
{ |
|
TaskComplete(); |
|
} |
|
} |
|
|
|
break; |
|
|
|
case TASK_JUMP: |
|
if ( CheckLanding() ) |
|
{ |
|
TaskComplete(); |
|
} |
|
|
|
break; |
|
|
|
default: |
|
BaseClass::RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CAI_ASW_JumpBehavior::ShouldJump( void ) |
|
{ |
|
if ( GetOuter()->GetEnemy() == NULL ) |
|
return false; |
|
|
|
//Too soon to try to jump |
|
if ( m_flJumpTime > gpGlobals->curtime ) |
|
return false; |
|
|
|
// only jump if you're on the ground |
|
if (!(GetOuter()->GetFlags() & FL_ONGROUND) || GetOuter()->GetNavType() == NAV_JUMP ) |
|
return false; |
|
|
|
// Don't jump if I'm not allowed |
|
if ( ( CapabilitiesGet() & bits_CAP_MOVE_JUMP ) == false ) |
|
return false; |
|
|
|
Vector vEnemyForward, vForward; |
|
|
|
GetEnemy()->GetVectors( &vEnemyForward, NULL, NULL ); |
|
GetOuter()->GetVectors( &vForward, NULL, NULL ); |
|
|
|
float flDot = DotProduct( vForward, vEnemyForward ); |
|
|
|
if ( flDot < 0.5f ) |
|
flDot = 0.5f; |
|
|
|
Vector vecPredictedPos; |
|
|
|
//Get our likely position in two seconds |
|
UTIL_PredictedPosition( GetEnemy(), flDot * 2.5f, &vecPredictedPos ); |
|
|
|
// Don't jump if we're already near the target |
|
if ( ( GetAbsOrigin() - vecPredictedPos ).LengthSqr() < (m_flMinRange*m_flMinRange) ) |
|
return false; |
|
|
|
//Don't retest if the target hasn't moved enough |
|
//FIXME: Check your own distance from last attempt as well |
|
if ( ( ( m_vecLastJumpAttempt - vecPredictedPos ).LengthSqr() ) < (128*128) ) |
|
{ |
|
m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f ); |
|
return false; |
|
} |
|
|
|
Vector targetDir = ( vecPredictedPos - GetAbsOrigin() ); |
|
VectorNormalize( targetDir ); |
|
|
|
Vector targetPos = vecPredictedPos + ( targetDir * (GetHullWidth()*4.0f) ); |
|
|
|
//if ( CDroneAntlionRepellant::IsPositionRepellantFree( targetPos ) == false ) |
|
// return false; |
|
|
|
// Try the jump |
|
AIMoveTrace_t moveTrace; |
|
GetOuter()->GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), targetPos, GetOuter()->GetAITraceMask(), GetOuter()->GetNavTargetEntity(), &moveTrace ); |
|
|
|
//See if it succeeded |
|
if ( IsMoveBlocked( moveTrace.fStatus ) ) |
|
{ |
|
if ( asw_debug_jump_behavior.GetInt() == 2 ) |
|
{ |
|
NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 5 ); |
|
NDebugOverlay::Line( GetAbsOrigin(), targetPos, 255, 0, 0, 0, 5 ); |
|
} |
|
|
|
m_flJumpTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f ); |
|
return false; |
|
} |
|
|
|
if ( asw_debug_jump_behavior.GetInt() == 2 ) |
|
{ |
|
NDebugOverlay::Box( targetPos, GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 5 ); |
|
NDebugOverlay::Line( GetAbsOrigin(), targetPos, 0, 255, 0, 0, 5 ); |
|
} |
|
|
|
//Save this jump in case the next time fails |
|
m_vecSavedJump = moveTrace.vJumpVelocity; |
|
m_vecLastJumpAttempt = targetPos; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Monitor the alien's jump to play the proper landing sequence |
|
//----------------------------------------------------------------------------- |
|
bool CAI_ASW_JumpBehavior::CheckLanding( void ) |
|
{ |
|
trace_t tr; |
|
Vector testPos; |
|
|
|
//Amount of time to predict forward |
|
const float timeStep = 0.1f; |
|
|
|
//Roughly looks one second into the future |
|
testPos = GetAbsOrigin() + ( GetOuter()->GetAbsVelocity() * timeStep ); |
|
testPos[2] -= ( 0.5 * sv_gravity.GetFloat() * GetGravity() * timeStep * timeStep); |
|
|
|
if ( asw_debug_jump_behavior.GetInt() == 2 ) |
|
{ |
|
NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f ); |
|
NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f ); |
|
} |
|
|
|
// Look below |
|
AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), GetOuter()->GetAITraceMask(), GetOuter(), COLLISION_GROUP_NONE, &tr ); |
|
|
|
//See if we're about to contact, or have already contacted the ground |
|
if ( ( tr.fraction != 1.0f ) || ( GetOuter()->GetFlags() & FL_ONGROUND ) ) |
|
{ |
|
int sequence = GetOuter()->SelectWeightedSequence( (Activity)ACT_ALIEN_LAND ); |
|
if ( GetSequence() != sequence ) |
|
{ |
|
CASW_Alien *pNPC = static_cast< CASW_Alien * >( GetOuter() ); |
|
|
|
pNPC->VacateStrategySlot(); |
|
pNPC->SetIdealActivity( (Activity) ACT_ALIEN_LAND ); |
|
//pNPC->Land(); |
|
|
|
if ( GetEnemy() && GetEnemy()->IsPlayer() ) |
|
{ |
|
CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() ); |
|
|
|
if ( pPlayer && pPlayer->IsInAVehicle() == false ) |
|
{ |
|
//pNPC->MeleeAttack( m_flDamageDistance, m_flJumpDamage, QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) ); |
|
} |
|
} |
|
|
|
GetOuter()->SetAbsVelocity( GetOuter()->GetAbsVelocity() * 0.33f ); |
|
return false; |
|
} |
|
|
|
return GetOuter()->IsActivityFinished(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_JumpBehavior ) |
|
|
|
DECLARE_ACTIVITY( ACT_ALIEN_JUMP ) |
|
DECLARE_ACTIVITY( ACT_ALIEN_LAND ) |
|
|
|
DECLARE_TASK( TASK_FACE_JUMP ) |
|
DECLARE_TASK( TASK_JUMP ) |
|
|
|
// forward jump attack |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_ALIEN_JUMP, |
|
|
|
" Tasks" |
|
" TASK_STOP_MOVING 0" |
|
" TASK_FACE_JUMP 0" |
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_ALIEN_JUMP" |
|
" TASK_JUMP 0" |
|
"" |
|
" Interrupts" |
|
" COND_TASK_FAILED" |
|
) |
|
|
|
AI_END_CUSTOM_SCHEDULE_PROVIDER() |
|
|
|
#include "tier0/memdbgoff.h"
|
|
|