diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/hl2/npc_enemyfinder.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/hl2/npc_enemyfinder.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/npc_enemyfinder.cpp | 774 |
1 files changed, 774 insertions, 0 deletions
diff --git a/mp/src/game/server/hl2/npc_enemyfinder.cpp b/mp/src/game/server/hl2/npc_enemyfinder.cpp new file mode 100644 index 00000000..274e60ea --- /dev/null +++ b/mp/src/game/server/hl2/npc_enemyfinder.cpp @@ -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"
+);
|