mirror of
https://github.com/celisej567/source-engine.git
synced 2026-01-04 18:09:53 +03:00
1
This commit is contained in:
774
game/server/hl2/npc_enemyfinder.cpp
Normal file
774
game/server/hl2/npc_enemyfinder.cpp
Normal file
@@ -0,0 +1,774 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
|
||||
// events
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "basecombatcharacter.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "decals.h"
|
||||
#include "IEffects.h"
|
||||
#include "ai_squad.h"
|
||||
#include "ai_utils.h"
|
||||
#include "ai_senses.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define SF_ENEMY_FINDER_CHECK_VIS (1 << 16)
|
||||
#define SF_ENEMY_FINDER_APC_VIS (1 << 17)
|
||||
#define SF_ENEMY_FINDER_SHORT_MEMORY (1 << 18)
|
||||
#define SF_ENEMY_FINDER_ENEMY_ALLOWED (1 << 19)
|
||||
|
||||
ConVar ai_debug_enemyfinders( "ai_debug_enemyfinders", "0" );
|
||||
|
||||
|
||||
class CNPC_EnemyFinder : public CAI_BaseNPC
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNPC_EnemyFinder, CAI_BaseNPC );
|
||||
|
||||
CNPC_EnemyFinder()
|
||||
{
|
||||
m_PlayerFreePass.SetOuter( this );
|
||||
}
|
||||
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
void StartNPC ( void );
|
||||
void PrescheduleThink();
|
||||
bool ShouldAlwaysThink();
|
||||
void UpdateEfficiency( bool bInPVS ) { SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL ); SetMoveEfficiency( AIME_NORMAL ); }
|
||||
void GatherConditions( void );
|
||||
bool ShouldChooseNewEnemy();
|
||||
bool IsValidEnemy( CBaseEntity *pTarget );
|
||||
bool CanBeAnEnemyOf( CBaseEntity *pEnemy ) { return HasSpawnFlags( SF_ENEMY_FINDER_ENEMY_ALLOWED ); }
|
||||
bool FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker );
|
||||
Class_T Classify( void );
|
||||
bool CanBeSeenBy( CAI_BaseNPC *pNPC ) { return CanBeAnEnemyOf( pNPC ); } // allows entities to be 'invisible' to NPC senses.
|
||||
|
||||
virtual int SelectSchedule( void );
|
||||
virtual void DrawDebugGeometryOverlays( void );
|
||||
|
||||
// Input handlers.
|
||||
void InputTurnOn( inputdata_t &inputdata );
|
||||
void InputTurnOff( inputdata_t &inputdata );
|
||||
|
||||
virtual void Wake( bool bFireOutput = true );
|
||||
|
||||
private:
|
||||
int m_nStartOn;
|
||||
float m_flMinSearchDist;
|
||||
float m_flMaxSearchDist;
|
||||
CAI_FreePass m_PlayerFreePass;
|
||||
CSimpleSimTimer m_ChooseEnemyTimer;
|
||||
|
||||
bool m_bEnemyStatus;
|
||||
|
||||
COutputEvent m_OnLostEnemies;
|
||||
COutputEvent m_OnAcquireEnemies;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
DEFINE_CUSTOM_AI;
|
||||
};
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( npc_enemyfinder, CNPC_EnemyFinder );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Custom schedules.
|
||||
//-----------------------------------------------------------------------------
|
||||
enum
|
||||
{
|
||||
SCHED_EFINDER_SEARCH = LAST_SHARED_SCHEDULE,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_AI( npc_enemyfinder, CNPC_EnemyFinder );
|
||||
|
||||
BEGIN_DATADESC( CNPC_EnemyFinder )
|
||||
|
||||
DEFINE_EMBEDDED( m_PlayerFreePass ),
|
||||
DEFINE_EMBEDDED( m_ChooseEnemyTimer ),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUT( m_nStartOn, FIELD_INTEGER, "StartOn" ),
|
||||
DEFINE_INPUT( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ),
|
||||
DEFINE_INPUT( m_flMinSearchDist, FIELD_FLOAT, "MinSearchDist" ),
|
||||
DEFINE_INPUT( m_flMaxSearchDist, FIELD_FLOAT, "MaxSearchDist" ),
|
||||
|
||||
DEFINE_FIELD( m_bEnemyStatus, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnLostEnemies, "OnLostEnemies"),
|
||||
DEFINE_OUTPUT( m_OnAcquireEnemies, "OnAcquireEnemies"),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::InitCustomSchedules( void )
|
||||
{
|
||||
INIT_CUSTOM_AI( CNPC_EnemyFinder );
|
||||
|
||||
ADD_CUSTOM_SCHEDULE( CNPC_EnemyFinder, SCHED_EFINDER_SEARCH );
|
||||
AI_LOAD_SCHEDULE( CNPC_EnemyFinder, SCHED_EFINDER_SEARCH );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Input handler for turning the enemy finder on.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::InputTurnOn( inputdata_t &inputdata )
|
||||
{
|
||||
SetThink( &CNPC_EnemyFinder::CallNPCThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Input handler for turning the enemy finder off.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::InputTurnOff( inputdata_t &inputdata )
|
||||
{
|
||||
SetThink(NULL);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::Precache( void )
|
||||
{
|
||||
PrecacheModel( "models/player.mdl" );
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetModel( "models/player.mdl" );
|
||||
// This is a dummy model that is never used!
|
||||
UTIL_SetSize(this, vec3_origin, vec3_origin);
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetBloodColor( DONT_BLEED );
|
||||
SetGravity( 0.0 );
|
||||
m_iHealth = 1;
|
||||
|
||||
AddFlag( FL_NPC );
|
||||
|
||||
SetSolid( SOLID_NONE );
|
||||
|
||||
m_bEnemyStatus = false;
|
||||
|
||||
if (m_flFieldOfView < -1.0)
|
||||
{
|
||||
DevMsg("ERROR: EnemyFinder field of view must be between -1.0 and 1.0\n");
|
||||
m_flFieldOfView = 0.5;
|
||||
}
|
||||
else if (m_flFieldOfView > 1.0)
|
||||
{
|
||||
DevMsg("ERROR: EnemyFinder field of view must be between -1.0 and 1.0\n");
|
||||
m_flFieldOfView = 1.0;
|
||||
}
|
||||
CapabilitiesAdd ( bits_CAP_SQUAD );
|
||||
|
||||
NPCInit();
|
||||
|
||||
// Set this after NPCInit()
|
||||
m_takedamage = DAMAGE_NO;
|
||||
AddEffects( EF_NODRAW );
|
||||
m_NPCState = NPC_STATE_ALERT; // always alert
|
||||
|
||||
SetViewOffset( vec3_origin );
|
||||
if ( m_flMaxSearchDist )
|
||||
{
|
||||
SetDistLook( m_flMaxSearchDist );
|
||||
}
|
||||
|
||||
if ( HasSpawnFlags( SF_ENEMY_FINDER_SHORT_MEMORY ) )
|
||||
{
|
||||
GetEnemies()->SetEnemyDiscardTime( 0.2 );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_EnemyFinder::SelectSchedule( void )
|
||||
{
|
||||
return SCHED_EFINDER_SEARCH;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::Wake( bool bFireOutput )
|
||||
{
|
||||
BaseClass::Wake( bFireOutput );
|
||||
|
||||
//Enemy finder is not allowed to become visible.
|
||||
AddEffects( EF_NODRAW );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
bool CNPC_EnemyFinder::FVisible( CBaseEntity *pTarget, int traceMask, CBaseEntity **ppBlocker )
|
||||
{
|
||||
float flTargetDist = GetAbsOrigin().DistTo( pTarget->GetAbsOrigin() );
|
||||
if ( flTargetDist < m_flMinSearchDist)
|
||||
return false;
|
||||
|
||||
if ( m_flMaxSearchDist && flTargetDist > m_flMaxSearchDist)
|
||||
return false;
|
||||
|
||||
if ( !FBitSet( m_spawnflags, SF_ENEMY_FINDER_CHECK_VIS) )
|
||||
return true;
|
||||
|
||||
if ( !HasSpawnFlags(SF_ENEMY_FINDER_APC_VIS) )
|
||||
{
|
||||
bool bIsVisible = BaseClass::FVisible( pTarget, traceMask, ppBlocker );
|
||||
|
||||
if ( bIsVisible && pTarget == m_PlayerFreePass.GetPassTarget() )
|
||||
bIsVisible = m_PlayerFreePass.ShouldAllowFVisible( bIsVisible );
|
||||
|
||||
return bIsVisible;
|
||||
}
|
||||
|
||||
// Make sure I can see the target from my position
|
||||
trace_t tr;
|
||||
|
||||
// Trace from launch position to target position.
|
||||
// Use position above actual barral based on vertical launch speed
|
||||
Vector vStartPos = GetAbsOrigin();
|
||||
Vector vEndPos = pTarget->EyePosition();
|
||||
|
||||
CBaseEntity *pVehicle = NULL;
|
||||
if ( pTarget->IsPlayer() )
|
||||
{
|
||||
CBasePlayer *pPlayer = assert_cast<CBasePlayer*>(pTarget);
|
||||
pVehicle = pPlayer->GetVehicleEntity();
|
||||
}
|
||||
|
||||
CTraceFilterSkipTwoEntities traceFilter( pTarget, pVehicle, COLLISION_GROUP_NONE );
|
||||
AI_TraceLine( vStartPos, vEndPos, MASK_SHOT, &traceFilter, &tr );
|
||||
if ( ppBlocker )
|
||||
{
|
||||
*ppBlocker = tr.m_pEnt;
|
||||
}
|
||||
return (tr.fraction == 1.0);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CNPC_EnemyFinder::ShouldChooseNewEnemy()
|
||||
{
|
||||
if ( m_ChooseEnemyTimer.Expired() )
|
||||
{
|
||||
m_ChooseEnemyTimer.Set( 0.3 );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Override base class to check range and visibility
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
bool CNPC_EnemyFinder::IsValidEnemy( CBaseEntity *pTarget )
|
||||
{
|
||||
float flTargetDist = GetAbsOrigin().DistTo( pTarget->GetAbsOrigin() );
|
||||
if (flTargetDist < m_flMinSearchDist)
|
||||
return false;
|
||||
|
||||
if ( m_flMaxSearchDist && flTargetDist > m_flMaxSearchDist)
|
||||
return false;
|
||||
|
||||
if ( !FBitSet( m_spawnflags, SF_ENEMY_FINDER_CHECK_VIS) )
|
||||
return true;
|
||||
|
||||
if ( GetSenses()->DidSeeEntity( pTarget ) )
|
||||
return true;
|
||||
|
||||
// Trace from launch position to target position.
|
||||
// Use position above actual barral based on vertical launch speed
|
||||
Vector vStartPos = GetAbsOrigin();
|
||||
Vector vEndPos = pTarget->EyePosition();
|
||||
|
||||
// Test our line of sight to the target
|
||||
trace_t tr;
|
||||
AI_TraceLOS( vStartPos, vEndPos, this, &tr );
|
||||
|
||||
// If the player is in a vehicle, see if we can see that instead
|
||||
if ( pTarget->IsPlayer() )
|
||||
{
|
||||
CBasePlayer *pPlayer = assert_cast<CBasePlayer*>(pTarget);
|
||||
if ( tr.m_pEnt == pPlayer->GetVehicleEntity() )
|
||||
return true;
|
||||
}
|
||||
|
||||
// Line must be clear
|
||||
if ( tr.fraction == 1.0f || tr.m_pEnt == pTarget )
|
||||
return true;
|
||||
|
||||
// Otherwise we can't see anything
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::StartNPC ( void )
|
||||
{
|
||||
AddSpawnFlags(SF_NPC_FALL_TO_GROUND); // this prevents CAI_BaseNPC from slamming the finder to
|
||||
// the ground just because it's not MOVETYPE_FLY
|
||||
BaseClass::StartNPC();
|
||||
|
||||
if ( AI_IsSinglePlayer() && m_PlayerFreePass.GetParams().duration > 0.1 )
|
||||
{
|
||||
m_PlayerFreePass.SetPassTarget( UTIL_PlayerByIndex(1) );
|
||||
|
||||
AI_FreePassParams_t freePassParams = m_PlayerFreePass.GetParams();
|
||||
|
||||
freePassParams.coverDist = 120;
|
||||
freePassParams.peekEyeDist = 1.75;
|
||||
freePassParams.peekEyeDistZ = 4;
|
||||
|
||||
m_PlayerFreePass.SetParams( freePassParams );
|
||||
}
|
||||
|
||||
if (!m_nStartOn)
|
||||
{
|
||||
SetThink(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::PrescheduleThink()
|
||||
{
|
||||
BaseClass::PrescheduleThink();
|
||||
|
||||
bool bHasEnemies = GetEnemies()->NumEnemies() > 0;
|
||||
|
||||
if ( GetEnemies()->NumEnemies() > 0 )
|
||||
{
|
||||
//If I haven't seen my enemy in half a second then we'll assume he's gone.
|
||||
if ( gpGlobals->curtime - GetEnemyLastTimeSeen() >= 0.5f )
|
||||
{
|
||||
bHasEnemies = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_bEnemyStatus != bHasEnemies )
|
||||
{
|
||||
if ( bHasEnemies )
|
||||
{
|
||||
m_OnAcquireEnemies.FireOutput( this, this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_OnLostEnemies.FireOutput( this, this );
|
||||
}
|
||||
|
||||
m_bEnemyStatus = bHasEnemies;
|
||||
}
|
||||
|
||||
if( ai_debug_enemyfinders.GetBool() )
|
||||
{
|
||||
m_debugOverlays |= OVERLAY_BBOX_BIT;
|
||||
|
||||
if( IsInSquad() && GetSquad()->NumMembers() > 1 )
|
||||
{
|
||||
AISquadIter_t iter;
|
||||
CAI_BaseNPC *pSquadmate = m_pSquad ? m_pSquad->GetFirstMember( &iter ) : NULL;
|
||||
while ( pSquadmate )
|
||||
{
|
||||
NDebugOverlay::Line( WorldSpaceCenter(), pSquadmate->EyePosition(), 255, 255, 0, false, 0.1f );
|
||||
pSquadmate = m_pSquad->GetNextMember( &iter );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CNPC_EnemyFinder::ShouldAlwaysThink()
|
||||
{
|
||||
if ( BaseClass::ShouldAlwaysThink() )
|
||||
return true;
|
||||
|
||||
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
||||
if ( pPlayer && IRelationType( pPlayer ) == D_HT )
|
||||
{
|
||||
float playerDistSqr = GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() );
|
||||
|
||||
if ( !m_flMaxSearchDist || playerDistSqr <= Square(m_flMaxSearchDist) )
|
||||
{
|
||||
if ( !FBitSet( m_spawnflags, SF_ENEMY_FINDER_CHECK_VIS) )
|
||||
return true;
|
||||
|
||||
if ( playerDistSqr <= Square( 50 * 12 ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::GatherConditions()
|
||||
{
|
||||
// This works with old data because need to do before base class so as to not choose as enemy
|
||||
m_PlayerFreePass.Update();
|
||||
BaseClass::GatherConditions();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//
|
||||
//
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
Class_T CNPC_EnemyFinder::Classify( void )
|
||||
{
|
||||
if ( GetSquad() )
|
||||
{
|
||||
AISquadIter_t iter;
|
||||
CAI_BaseNPC *pSquadmate = GetSquad()->GetFirstMember( &iter );
|
||||
while ( pSquadmate )
|
||||
{
|
||||
if ( pSquadmate != this && !pSquadmate->ClassMatches( GetClassname() ) )
|
||||
{
|
||||
return pSquadmate->Classify();
|
||||
}
|
||||
pSquadmate = GetSquad()->GetNextMember( &iter );
|
||||
}
|
||||
}
|
||||
|
||||
return CLASS_NONE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Add a visualizer to the text, if turned on
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinder::DrawDebugGeometryOverlays( void )
|
||||
{
|
||||
// Turn on npc_relationships if we're displaying text
|
||||
int oldDebugOverlays = m_debugOverlays;
|
||||
if ( m_debugOverlays & OVERLAY_TEXT_BIT )
|
||||
{
|
||||
m_debugOverlays |= OVERLAY_NPC_RELATION_BIT;
|
||||
}
|
||||
|
||||
// Draw our base overlays
|
||||
BaseClass::DrawDebugGeometryOverlays();
|
||||
|
||||
// Restore the old values
|
||||
m_debugOverlays = oldDebugOverlays;
|
||||
}
|
||||
|
||||
ConVar ai_ef_hate_npc_frequency( "ai_ef_hate_npc_frequency", "5" );
|
||||
ConVar ai_ef_hate_npc_duration( "ai_ef_hate_npc_duration", "1.5" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Derived class with a few changes that make the Combine Cannon behave the
|
||||
// way we want.
|
||||
//-----------------------------------------------------------------------------
|
||||
#define EF_COMBINE_CANNON_HATE_TIME_INVALID -1
|
||||
static CUtlVector<CBaseEntity*> s_ListEnemyfinders;
|
||||
|
||||
class CNPC_EnemyFinderCombineCannon : public CNPC_EnemyFinder
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNPC_EnemyFinderCombineCannon, CNPC_EnemyFinder );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CNPC_EnemyFinderCombineCannon()
|
||||
{
|
||||
m_flTimeNextHateNPC = gpGlobals->curtime;
|
||||
m_flTimeStopHateNPC = EF_COMBINE_CANNON_HATE_TIME_INVALID;
|
||||
};
|
||||
|
||||
public:
|
||||
void Spawn();
|
||||
void Activate();
|
||||
void UpdateOnRemove();
|
||||
bool FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker );
|
||||
bool IsValidEnemy( CBaseEntity *pTarget );
|
||||
void GatherConditions();
|
||||
|
||||
void InputSetWideFOVForSeconds( inputdata_t &inputdata );
|
||||
|
||||
public:
|
||||
float m_flTimeNextHateNPC;
|
||||
float m_flTimeStopHateNPC;
|
||||
float m_flOriginalFOV;
|
||||
float m_flTimeWideFOV; // If this is > gpGlobals->curtime, we have 180 degree viewcone.
|
||||
string_t m_iszSnapToEnt;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( npc_enemyfinder_combinecannon, CNPC_EnemyFinderCombineCannon );
|
||||
|
||||
BEGIN_DATADESC( CNPC_EnemyFinderCombineCannon )
|
||||
DEFINE_FIELD( m_flTimeNextHateNPC, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flTimeStopHateNPC, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flOriginalFOV, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flTimeWideFOV, FIELD_TIME ),
|
||||
DEFINE_KEYFIELD( m_iszSnapToEnt, FIELD_STRING, "snaptoent" ),
|
||||
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetWideFOVForSeconds", InputSetWideFOVForSeconds ),
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinderCombineCannon::Spawn()
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
m_flOriginalFOV = m_flFieldOfView;
|
||||
m_flTimeWideFOV = -1.0f;
|
||||
|
||||
if( m_iszSnapToEnt != NULL_STRING )
|
||||
{
|
||||
CBaseEntity *pSnapToEnt = gEntList.FindEntityByName( NULL, m_iszSnapToEnt );
|
||||
|
||||
if( pSnapToEnt != NULL )
|
||||
{
|
||||
//!!!HACKHACK - this eight-inch offset puts this enemyfinder perfectly on-bore
|
||||
// with the prefab for a func_tank_combinecannon
|
||||
UTIL_SetOrigin( this, pSnapToEnt->WorldSpaceCenter() + Vector( 0, 0, 8) );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( this, "Enemyfinder %s can't snap to %s because it doesn't exist\n", GetDebugName(), STRING(m_iszSnapToEnt) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinderCombineCannon::Activate()
|
||||
{
|
||||
BaseClass::Activate();
|
||||
|
||||
// See if I'm in the list of Combine enemyfinders
|
||||
// If not, add me.
|
||||
if( s_ListEnemyfinders.Find(this) == -1 )
|
||||
{
|
||||
s_ListEnemyfinders.AddToTail(this);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinderCombineCannon::UpdateOnRemove()
|
||||
{
|
||||
BaseClass::UpdateOnRemove();
|
||||
|
||||
// See if I'm in the list of Combine enemyfinders
|
||||
int index = s_ListEnemyfinders.Find(this);
|
||||
if( index != -1 )
|
||||
{
|
||||
s_ListEnemyfinders.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNPC_EnemyFinderCombineCannon::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
|
||||
{
|
||||
#if 1
|
||||
CBaseEntity *pBlocker = NULL;
|
||||
bool result;
|
||||
|
||||
if(ppBlocker == NULL)
|
||||
{
|
||||
// Whoever called this didn't care about the blocker, but we do.
|
||||
// So substitute our local pBlocker pointer and don't disturb ppBlocker
|
||||
result = BaseClass::FVisible( pEntity, traceMask, &pBlocker );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the ppBlocker to our local pBlocker pointer, but do not
|
||||
// disturb the ppBlocker that was passed to us.
|
||||
result = BaseClass::FVisible( pEntity, traceMask, ppBlocker );
|
||||
pBlocker = (*ppBlocker);
|
||||
}
|
||||
|
||||
if(pEntity->IsPlayer() && result == false)
|
||||
{
|
||||
// IF we are trying to see the player, but we don't, examine the blocker
|
||||
// and see the player anyway if we can hurt the blocker.
|
||||
if(pBlocker != NULL)
|
||||
{
|
||||
if( pBlocker->m_takedamage >= DAMAGE_YES ) // also DAMAGE_AIM
|
||||
{
|
||||
// Anytime the line of sight is blocked by something I can hurt, I have line of sight.
|
||||
// This will make the func_tank_combinecannon shoot the blocking object. This will
|
||||
// continue until the gun bores through to the player or clears all interposing breakables
|
||||
// and finds its progress impeded by something truly solid. So lie, and say we CAN see the player.
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Ignore NPC's most of the time when the player is a potential target.
|
||||
// Go through short periods of time where NPCs may distract me
|
||||
//
|
||||
// ALSO- ignore NPC's (focus only on the player) when I'm in
|
||||
// wide viewcone mode.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNPC_EnemyFinderCombineCannon::IsValidEnemy( CBaseEntity *pTarget )
|
||||
{
|
||||
if( m_flTimeWideFOV > gpGlobals->curtime && !pTarget->IsPlayer() )
|
||||
{
|
||||
// ONLY allowed to hate the player when I'm in hyper-vigilant wide FOV mode.
|
||||
// This forces all guns in outland_09 to shoot at the player once any
|
||||
// gun has fired at the player. This allows the other guns to specifically
|
||||
// kill zombies most of the time, but immediately turn their attention to the
|
||||
// player when necessary (by ignoring everything else)
|
||||
return pTarget->IsPlayer();
|
||||
}
|
||||
|
||||
bool bResult = BaseClass::IsValidEnemy( pTarget );
|
||||
|
||||
if( bResult && !pTarget->IsPlayer() )
|
||||
{
|
||||
// This is a valid enemy, but we have to make sure no other enemyfinders for
|
||||
// combine cannons are currently attacking it.
|
||||
int i;
|
||||
for( i = 0 ; i < s_ListEnemyfinders.Count() ; i++ )
|
||||
{
|
||||
if( s_ListEnemyfinders[i] == this )
|
||||
continue;
|
||||
|
||||
if( s_ListEnemyfinders[i]->GetEnemy() == pTarget )
|
||||
return false;// someone else is already busy with this target.
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
||||
int iPlayerRelationPriority = -1;
|
||||
|
||||
if( pPlayer != NULL )
|
||||
{
|
||||
iPlayerRelationPriority = IRelationPriority(pPlayer);
|
||||
}
|
||||
|
||||
if( bResult == true && pTarget->IsNPC() && pPlayer != NULL && FInViewCone( pPlayer ) )
|
||||
{
|
||||
if( HasCondition(COND_SEE_PLAYER) )
|
||||
{
|
||||
// The player is visible! Immediately ignore all NPCs as enemies.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The base class wants to call this a valid enemy. We may choose to interfere
|
||||
// If the player is in my viewcone. That means that my func_tank could potentially
|
||||
// harass the player. This means I should meter the time I spend shooting at npcs
|
||||
// NPCs so that I can focus on the player.
|
||||
if( m_flTimeStopHateNPC != EF_COMBINE_CANNON_HATE_TIME_INVALID )
|
||||
{
|
||||
// We currently hate NPC's. But is it time to stop?
|
||||
if( gpGlobals->curtime > m_flTimeStopHateNPC )
|
||||
{
|
||||
// Don't interfere with the result
|
||||
m_flTimeStopHateNPC = EF_COMBINE_CANNON_HATE_TIME_INVALID;
|
||||
m_flTimeNextHateNPC = gpGlobals->curtime + ai_ef_hate_npc_frequency.GetFloat();
|
||||
return bResult;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do not hate NPCs at the moment. Is it time to turn it on?
|
||||
if( gpGlobals->curtime > m_flTimeNextHateNPC )
|
||||
{
|
||||
m_flTimeStopHateNPC = gpGlobals->curtime + ai_ef_hate_npc_duration.GetFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stop harassing player to attack something else higher priority.
|
||||
if( IRelationPriority(pTarget) > iPlayerRelationPriority )
|
||||
return bResult;
|
||||
}
|
||||
|
||||
// Make this enemy invalid.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Control the width of my viewcone
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinderCombineCannon::GatherConditions()
|
||||
{
|
||||
if( m_flTimeWideFOV > gpGlobals->curtime )
|
||||
{
|
||||
// I'm in a hyper-vigilant period of time where I get a 270 degree viewcone
|
||||
m_flFieldOfView = -0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flFieldOfView = m_flOriginalFOV;
|
||||
}
|
||||
|
||||
BaseClass::GatherConditions();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_EnemyFinderCombineCannon::InputSetWideFOVForSeconds( inputdata_t &inputdata )
|
||||
{
|
||||
m_flTimeWideFOV = gpGlobals->curtime + inputdata.value.Float();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Schedules
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_EFINDER_SEARCH
|
||||
//=========================================================
|
||||
AI_DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_EFINDER_SEARCH,
|
||||
|
||||
" Tasks"
|
||||
" TASK_WAIT_RANDOM 0.5 "
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
);
|
||||
Reference in New Issue
Block a user