diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl2/npc_enemyfinder.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/hl2/npc_enemyfinder.cpp')
| -rw-r--r-- | game/server/hl2/npc_enemyfinder.cpp | 774 |
1 files changed, 774 insertions, 0 deletions
diff --git a/game/server/hl2/npc_enemyfinder.cpp b/game/server/hl2/npc_enemyfinder.cpp new file mode 100644 index 0000000..02afc08 --- /dev/null +++ b/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" +); |