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.
488 lines
12 KiB
488 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_flick.h" |
|
#include "ai_hint.h" |
|
#include "ai_navigator.h" |
|
#include "ai_memory.h" |
|
#include "asw_alien.h" |
|
#include "movevars_shared.h" |
|
#include "asw_marine.h" |
|
#include "asw_player.h" |
|
#include "asw_director.h" |
|
#include "asw_gamerules.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
BEGIN_DATADESC( CAI_ASW_FlickBehavior ) |
|
END_DATADESC(); |
|
|
|
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_FlickBehavior ); |
|
|
|
|
|
//#define DRAW_DEBUG 1 |
|
|
|
|
|
typedef struct SFlickInfo |
|
{ |
|
Activity m_Activity; |
|
float m_flMinDot; |
|
float m_flMaxDot; |
|
} TFlickInfo; |
|
|
|
#define MAX_FLICKS 4 |
|
|
|
static TFlickInfo FlickInfo[ MAX_FLICKS ] = |
|
{ |
|
{ ACT_FLICK_LEFT, 0.45f, 0.8f }, |
|
{ ACT_FLICK_LEFT_MIDDLE, 0.0f, 0.45f }, |
|
{ ACT_FLICK_RIGHT_MIDDLE, -0.45f, 0.0f }, |
|
{ ACT_FLICK_RIGHT, -0.8f, -0.45f } |
|
}; |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: constructor |
|
//------------------------------------------------------------------------------ |
|
CAI_ASW_FlickBehavior::CAI_ASW_FlickBehavior( ) |
|
{ |
|
m_flDistance = -1; |
|
m_flDistanceSq = -1; |
|
m_flMinDamage = 1.0f; |
|
m_flMaxDamage = 1.0f; |
|
m_pPickedFlick = NULL; |
|
m_flNextFlickCheck = 0.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_FlickBehavior::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
if ( V_stricmp( szKeyName, "distance" ) == 0 ) |
|
{ |
|
m_flDistance = atof( szValue ); |
|
m_flDistanceSq = m_flDistance * m_flDistance; |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "propel_distance" ) == 0 ) |
|
{ |
|
m_flPropelDistance = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "propel_height" ) == 0 ) |
|
{ |
|
m_flPropelHeight = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "min_damage" ) == 0 ) |
|
{ |
|
m_flMinDamage = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "max_damage" ) == 0 ) |
|
{ |
|
m_flMaxDamage = atof( szValue ); |
|
return true; |
|
} |
|
|
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: precaches any additional assets this behavior needs |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::Precache( void ) |
|
{ |
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::Init( ) |
|
{ |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: determines if we can use this behavior currently |
|
// Output : returns true if this behavior is able to run |
|
//------------------------------------------------------------------------------ |
|
bool CAI_ASW_FlickBehavior::CanSelectSchedule() |
|
{ |
|
if ( !GetOuter()->IsInterruptable() ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( HasCondition( COND_SHIELD_CAN_FLICK ) == false ) |
|
{ |
|
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_FlickBehavior::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_FlickBehavior::GatherConditionsNotActive( ) |
|
{ |
|
BaseClass::GatherConditionsNotActive(); |
|
|
|
if ( m_flNextFlickCheck < gpGlobals->curtime ) |
|
{ |
|
if ( GetFlickActivity() != NULL ) |
|
{ |
|
SetCondition( COND_SHIELD_CAN_FLICK ); |
|
} |
|
|
|
m_flNextFlickCheck = gpGlobals->curtime + 1.0f; |
|
} |
|
} |
|
|
|
|
|
void CAI_ASW_FlickBehavior::BeginScheduleSelection( ) |
|
{ |
|
m_pPickedFlick = NULL; |
|
} |
|
|
|
|
|
void CAI_ASW_FlickBehavior::EndScheduleSelection( ) |
|
{ |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: general purpose routine to collect conditions used both during active |
|
// and non-active states of the behavior. |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::GatherCommonConditions( ) |
|
{ |
|
BaseClass::GatherCommonConditions(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: routine called to start when a task initially starts |
|
// Input : pTask - the task structure |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::StartTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_SHIELD_FLICK: |
|
{ |
|
ClearCondition( COND_SHIELD_CAN_FLICK ); |
|
m_pPickedFlick = GetFlickActivity(); |
|
if ( m_pPickedFlick != NULL ) |
|
{ |
|
GetOuter()->RestartGesture( m_pPickedFlick->m_Activity ); |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: routine called every frame when a task is running |
|
// Input : pTask - the task structure |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::RunTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_SHIELD_FLICK: |
|
if ( !m_pPickedFlick || GetOuter()->IsPlayingGesture( m_pPickedFlick->m_Activity ) == false ) |
|
{ |
|
TaskComplete(); |
|
m_pPickedFlick = NULL; |
|
} |
|
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_FlickBehavior::SelectSchedule() |
|
{ |
|
return SCHED_SHIELD_FLICK; |
|
} |
|
|
|
|
|
TFlickInfo *CAI_ASW_FlickBehavior::GetFlickActivity( ) |
|
{ |
|
Vector vRightNPC, vForwardNPC, vForwardEnemy; |
|
int nCount = 0; |
|
int nFlickTotal[ MAX_FLICKS ]; |
|
|
|
for( int i = 0; i < MAX_FLICKS; i++ ) |
|
{ |
|
nFlickTotal[ i ] = 0; |
|
} |
|
|
|
AngleVectors( GetAbsAngles(), &vForwardNPC, &vRightNPC, NULL ); |
|
#ifdef DRAW_DEBUG |
|
UTIL_AddDebugLine( GetAbsOrigin(), GetAbsOrigin() + vForwardNPC * 300.0f, true, false ); |
|
#endif // #ifdef DRAW_DEBUG |
|
|
|
AIEnemiesIter_t iter; |
|
for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) ) |
|
{ |
|
CBaseEntity *pEntity = pEMemory->hEnemy; |
|
|
|
Vector vDelta = GetAbsOrigin() - pEntity->GetAbsOrigin(); |
|
float flLenSq = vDelta.LengthSqr(); |
|
|
|
if ( flLenSq > m_flDistanceSq ) |
|
{ |
|
continue; |
|
} |
|
|
|
#ifdef DRAW_DEBUG |
|
UTIL_AddDebugLine( GetAbsOrigin(), pEntity->GetAbsOrigin(), true, false ); |
|
#endif // #ifdef DRAW_DEBUG |
|
|
|
vForwardEnemy = vDelta; |
|
vForwardEnemy.NormalizeInPlace(); |
|
|
|
float flResult = vForwardNPC.Dot( vForwardEnemy ); |
|
if ( flResult > 0.0f ) |
|
{ // we are behind |
|
continue; |
|
} |
|
|
|
flResult = vRightNPC.Dot( vForwardEnemy ); |
|
for( int j = 0; j < MAX_FLICKS; j++ ) |
|
{ |
|
if ( flResult >= FlickInfo[ j ].m_flMinDot && flResult <= FlickInfo[ j ].m_flMaxDot ) |
|
{ // we are within the flick angle of this arm |
|
nFlickTotal[ j ]++; |
|
nCount++; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( nCount > 0 ) |
|
{ |
|
int nTotal = 0; |
|
|
|
nCount = RandomInt( 0, nCount - 1 ); |
|
for( int j = 0; j < MAX_FLICKS; j++ ) |
|
{ |
|
nTotal += nFlickTotal[ j ]; |
|
if ( nCount < nTotal ) |
|
{ |
|
return &FlickInfo[ j ]; |
|
} |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::TryFlicking( CBaseEntity *pEntity ) |
|
{ |
|
Vector vRightNPC, vForwardNPC, vForwardEnemy; |
|
Vector vDelta = GetAbsOrigin() - pEntity->GetAbsOrigin(); |
|
float flLenSq = vDelta.LengthSqr(); |
|
|
|
if ( flLenSq > m_flDistanceSq ) |
|
{ |
|
return; |
|
} |
|
|
|
vForwardEnemy = vDelta; |
|
vForwardEnemy.NormalizeInPlace(); |
|
AngleVectors( GetAbsAngles(), &vForwardNPC, &vRightNPC, NULL ); |
|
|
|
float flResult = vForwardNPC.Dot( vForwardEnemy ); |
|
if ( flResult > 0.0f ) |
|
{ // we are behind |
|
return; |
|
} |
|
|
|
flResult = vRightNPC.Dot( vForwardEnemy ); |
|
if ( flResult < m_pPickedFlick->m_flMinDot || flResult > m_pPickedFlick->m_flMaxDot ) |
|
{ // we are not within the flick angle of the flicking arm |
|
return; |
|
} |
|
|
|
CASW_Alien *pOwner = dynamic_cast< CASW_Alien * >( GetOuter() ); |
|
float flMinDamage = ASWGameRules()->ModifyAlienDamageBySkillLevel( m_flMinDamage ); |
|
float flMaxDamage = ASWGameRules()->ModifyAlienDamageBySkillLevel( m_flMaxDamage ); |
|
|
|
flMinDamage = ( flMinDamage < 1.0f ? 1.0f : flMinDamage ); |
|
flMaxDamage = ( flMaxDamage < 1.0f ? 1.0f : flMaxDamage ); |
|
|
|
CTakeDamageInfo info( pOwner, pOwner, RandomFloat( flMinDamage, flMaxDamage ), DMG_GENERIC ); |
|
Vector killDir = pEntity->GetAbsOrigin() - GetAbsOrigin(); |
|
VectorNormalize( killDir ); |
|
info.SetDamageForce( killDir ); |
|
info.SetDamagePosition( GetAbsOrigin() ); |
|
|
|
float flTime = sqrt( ( 2.0f * m_flPropelHeight / sv_gravity.GetFloat() ) ); |
|
|
|
vDelta = -vDelta; |
|
vDelta.z = 0.0f; |
|
vDelta.NormalizeInPlace(); |
|
vDelta *= m_flPropelDistance / ( flTime * 2.0f ); |
|
vDelta.z = sv_gravity.GetFloat() * flTime; |
|
|
|
CASW_Player *pPlayer = dynamic_cast< CASW_Player * >( pEntity ); |
|
if ( pPlayer ) |
|
{ |
|
CASW_Marine *pBaseMarine = pPlayer->GetMarine(); |
|
// CASW_Marine *pBaseMarine = CASW_Marine::AsMarine( pEntity ); |
|
if ( pBaseMarine ) |
|
{ |
|
pBaseMarine->TakeDamage( info ); |
|
pBaseMarine->m_bNoAirControl = true; |
|
pBaseMarine->SetAbsVelocity( vDelta ); |
|
pBaseMarine->SetGroundEntity( NULL ); |
|
return; |
|
} |
|
} |
|
else |
|
{ |
|
pEntity->TakeDamage( info ); |
|
} |
|
|
|
pEntity->SetAbsVelocity( vDelta ); |
|
pEntity->SetGroundEntity( NULL ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::Flick( ) |
|
{ |
|
#if 0 |
|
for( int i = 0; i < ASWDirector()->GetNPCListCount(); i++ ) |
|
{ |
|
CASW_Alien *pAlien = ASWDirector()->GetNPCFromList( i ); |
|
|
|
if ( !pAlien || !pAlien->IsAlive() ) |
|
{ |
|
continue; |
|
} |
|
|
|
if ( GetOuter() != pAlien ) |
|
{ |
|
TryFlicking( pAlien ); |
|
} |
|
} |
|
|
|
for ( int i = 0; i < ASW_MAX_READY_PLAYERS; i++ ) |
|
{ |
|
// found a connected player? |
|
CASW_Player *pOtherPlayer = dynamic_cast< CASW_Player * >( UTIL_PlayerByIndex( i + 1 ) ); |
|
// if they're not connected, skip them |
|
if ( !pOtherPlayer || !pOtherPlayer->IsConnected() ) |
|
{ |
|
continue; |
|
} |
|
|
|
TryFlicking( pOtherPlayer ); |
|
} |
|
#else |
|
AIEnemiesIter_t iter; |
|
for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) ) |
|
{ |
|
CBaseEntity *pEntity = pEMemory->hEnemy; |
|
|
|
if ( pEntity->IsAlive() && GetOuter() != pEntity ) |
|
{ |
|
TryFlicking( pEntity ); |
|
} |
|
} |
|
#endif |
|
// turn this into an area of affect |
|
// reverse his velocity - if he is coming towards the shield bug, add that back into his outgoing to increase it |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_FlickBehavior::HandleBehaviorEvent( CBaseEntity *pInflictor, BehaviorEvent_t eEvent, int nParm ) |
|
{ |
|
switch( eEvent ) |
|
{ |
|
case BEHAVIOR_EVENT_FLICK: |
|
Flick(); |
|
break; |
|
} |
|
} |
|
|
|
|
|
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_FlickBehavior ) |
|
|
|
DECLARE_TASK( TASK_SHIELD_FLICK ) |
|
|
|
DECLARE_CONDITION( COND_SHIELD_CAN_FLICK ) |
|
|
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_SHIELD_FLICK, |
|
" Tasks" |
|
" TASK_SHIELD_FLICK 0" |
|
"" |
|
" Interrupts" |
|
" " |
|
); |
|
|
|
AI_END_CUSTOM_SCHEDULE_PROVIDER() |
|
|
|
#include "tier0/memdbgoff.h"
|
|
|