aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/npc_turret_ground.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/hl2/npc_turret_ground.cpp
downloadsource-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_turret_ground.cpp')
-rw-r--r--mp/src/game/server/hl2/npc_turret_ground.cpp704
1 files changed, 704 insertions, 0 deletions
diff --git a/mp/src/game/server/hl2/npc_turret_ground.cpp b/mp/src/game/server/hl2/npc_turret_ground.cpp
new file mode 100644
index 00000000..445766a6
--- /dev/null
+++ b/mp/src/game/server/hl2/npc_turret_ground.cpp
@@ -0,0 +1,704 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Combine gun turret that emerges from a trapdoor in the ground.
+//
+//=============================================================================//
+#include "cbase.h"
+#include "npc_turret_ground.h"
+#include "ai_default.h"
+#include "ai_task.h"
+#include "ai_schedule.h"
+#include "ai_hull.h"
+#include "ai_senses.h"
+#include "ai_memory.h"
+#include "soundent.h"
+#include "game.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "npcevent.h"
+#include "IEffects.h"
+#include "ammodef.h"
+#include "beam_shared.h"
+#include "explode.h"
+#include "te_effect_dispatch.h"
+
+#define GROUNDTURRET_BEAM_SPRITE "materials/effects/bluelaser2.vmt"
+
+#define GROUNDTURRET_VIEWCONE 60.0f // (degrees)
+#define GROUNDTURRET_RETIRE_TIME 7.0f
+
+ConVar ai_newgroundturret ( "ai_newgroundturret", "0" );
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+LINK_ENTITY_TO_CLASS( npc_turret_ground, CNPC_GroundTurret );
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_DATADESC( CNPC_GroundTurret )
+ DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
+ DEFINE_FIELD( m_pSmoke, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_vecSpread, FIELD_VECTOR ),
+ DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flTimeNextShoot, FIELD_TIME ),
+ DEFINE_FIELD( m_flTimeLastSawEnemy, FIELD_TIME ),
+ DEFINE_FIELD( m_iDeathSparks, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bHasExploded, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flSensingDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flTimeNextPing, FIELD_TIME ),
+ DEFINE_FIELD( m_bSeeEnemy, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_vecClosedPos, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecLightOffset, FIELD_POSITION_VECTOR ),
+
+ DEFINE_THINKFUNC( DeathEffects ),
+
+ DEFINE_OUTPUT( m_OnAreaClear, "OnAreaClear" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+
+ // DEFINE_FIELD( m_ShotSounds, FIELD_SHORT ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::Precache( void )
+{
+ PrecacheModel( GROUNDTURRET_BEAM_SPRITE );
+ PrecacheModel( "models/combine_turrets/ground_turret.mdl" );
+
+ PrecacheScriptSound( "NPC_CeilingTurret.Deploy" );
+ m_ShotSounds = PrecacheScriptSound( "NPC_FloorTurret.ShotSounds" );
+ PrecacheScriptSound( "NPC_FloorTurret.Die" );
+ PrecacheScriptSound( "NPC_FloorTurret.Ping" );
+ PrecacheScriptSound( "DoSpark" );
+
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::Spawn( void )
+{
+ Precache();
+
+ UTIL_SetModel( this, "models/combine_turrets/ground_turret.mdl" );
+
+ SetNavType( NAV_FLY );
+ SetSolid( SOLID_VPHYSICS );
+
+ SetBloodColor( DONT_BLEED );
+ m_iHealth = 125;
+ m_flFieldOfView = cos( ((GROUNDTURRET_VIEWCONE / 2.0f) * M_PI / 180.0f) );
+ m_NPCState = NPC_STATE_NONE;
+
+ m_vecSpread.x = 0.5;
+ m_vecSpread.y = 0.5;
+ m_vecSpread.z = 0.5;
+
+ CapabilitiesClear();
+
+ AddEFlags( EFL_NO_DISSOLVE );
+
+ NPCInit();
+
+ CapabilitiesAdd( bits_CAP_SIMPLE_RADIUS_DAMAGE );
+
+ m_iAmmoType = GetAmmoDef()->Index( "PISTOL" );
+
+ m_pSmoke = NULL;
+
+ m_bHasExploded = false;
+ m_bEnabled = false;
+
+ if( ai_newgroundturret.GetBool() )
+ {
+ m_flSensingDist = 384;
+ SetDistLook( m_flSensingDist );
+ }
+ else
+ {
+ m_flSensingDist = 2048;
+ }
+
+ if( !GetParent() )
+ {
+ DevMsg("ERROR! npc_ground_turret with no parent!\n");
+ UTIL_Remove(this);
+ return;
+ }
+
+ m_flTimeNextShoot = gpGlobals->curtime;
+ m_flTimeNextPing = gpGlobals->curtime;
+
+ m_vecClosedPos = GetAbsOrigin();
+
+ StudioFrameAdvance();
+
+ Vector vecPos;
+
+ GetAttachment( "eyes", vecPos );
+ SetViewOffset( vecPos - GetAbsOrigin() );
+
+ GetAttachment( "light", vecPos );
+ m_vecLightOffset = vecPos - GetAbsOrigin();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CNPC_GroundTurret::CreateVPhysics( void )
+{
+ //Spawn our physics hull
+ if ( !VPhysicsInitStatic() )
+ {
+ DevMsg( "npc_turret_ground unable to spawn physics object!\n" );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::PrescheduleThink()
+{
+ if( UTIL_FindClientInPVS(edict()) )
+ {
+ SetNextThink( gpGlobals->curtime + 0.03f );
+ }
+ else
+ {
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+Class_T CNPC_GroundTurret::Classify( void )
+{
+ if( !IsOpen() )
+ {
+ // NPC's should disregard me if I'm closed.
+ return CLASS_NONE;
+ }
+ else
+ {
+ return CLASS_COMBINE;
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::PostNPCInit()
+{
+ BaseClass::PostNPCInit();
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+int CNPC_GroundTurret::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ if( !info.GetInflictor() )
+ {
+ return 0;
+ }
+
+ // Only take damage from self (kill input from my bullseye) or missiles.
+ if( info.GetInflictor() != this && info.GetInflictor()->Classify() != CLASS_MISSILE )
+ {
+ return 0;
+ }
+
+ CTakeDamageInfo infoCopy = info;
+
+ if( info.GetInflictor() == this )
+ {
+ // Taking damage from myself, make sure it's fatal.
+ infoCopy.SetDamage( GetHealth() );
+ infoCopy.SetDamageType( DMG_REMOVENORAGDOLL | DMG_GENERIC );
+ }
+
+ return BaseClass::OnTakeDamage_Alive( infoCopy );
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::Event_Killed( const CTakeDamageInfo &info )
+{
+ BaseClass::Event_Killed( info );
+
+ if ( m_pSmoke != NULL )
+ return;
+
+ m_pSmoke = SmokeTrail::CreateSmokeTrail();
+
+ if ( m_pSmoke )
+ {
+ m_pSmoke->m_SpawnRate = 18;
+ m_pSmoke->m_ParticleLifetime = 3.0;
+ m_pSmoke->m_StartSize = 8;
+ m_pSmoke->m_EndSize = 32;
+ m_pSmoke->m_SpawnRadius = 16;
+ m_pSmoke->m_MinSpeed = 8;
+ m_pSmoke->m_MaxSpeed = 32;
+ m_pSmoke->m_Opacity = 0.6;
+
+ m_pSmoke->m_StartColor.Init( 0.25f, 0.25f, 0.25f );
+ m_pSmoke->m_EndColor.Init( 0, 0, 0 );
+ m_pSmoke->SetLifetime( 30.0f );
+ m_pSmoke->FollowEntity( this );
+ }
+
+ m_iDeathSparks = random->RandomInt( 6, 12 );
+
+ SetThink( &CNPC_GroundTurret::DeathEffects );
+ SetNextThink( gpGlobals->curtime + 1.5f );
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::DeathEffects()
+{
+ if( !m_bHasExploded )
+ {
+ //ExplosionCreate( GetAbsOrigin(), QAngle( 0, 0, 1 ), this, 150, 150, false );
+ CTakeDamageInfo info;
+ DeathSound( info );
+ m_bHasExploded = true;
+ SetNextThink( gpGlobals->curtime + 0.5 );
+ }
+ else
+ {
+ // Sparks
+ EmitSound( "DoSpark" );
+ m_iDeathSparks--;
+
+ if( m_iDeathSparks == 0 )
+ {
+ SetThink(NULL);
+ return;
+ }
+
+ SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.5, 2.5 ) );
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::DeathSound( const CTakeDamageInfo &info )
+{
+ EmitSound("NPC_FloorTurret.Die");
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
+{
+#if 1
+ //BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType );
+ UTIL_Tracer( vecTracerSrc, tr.endpos, 0, TRACER_DONT_USE_ATTACHMENT, 5000, true, "AR2Tracer" );
+#else
+ CBeam *pBeam;
+ int width = 2;
+
+ pBeam = CBeam::BeamCreate( GROUNDTURRET_BEAM_SPRITE, width );
+ if ( !pBeam )
+ return;
+
+ pBeam->SetStartPos( vecTracerSrc );
+ pBeam->SetEndPos( tr.endpos );
+ pBeam->SetWidth( width );
+ pBeam->SetEndWidth( width / 4.0f );
+
+ pBeam->SetBrightness( 100 );
+ pBeam->SetColor( 0, 145+random->RandomInt( -16, 16 ), 255 );
+ pBeam->RelinkBeam();
+ pBeam->LiveForTime( random->RandomFloat( 0.2f, 0.5f ) );
+#endif
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::GatherConditions()
+{
+ if( !IsEnabled() )
+ {
+ return;
+ }
+
+ if( !IsOpen() && !UTIL_FindClientInPVS( edict() ) )
+ {
+ return;
+ }
+
+ // Throw away old enemies so the turret can retire
+ AIEnemiesIter_t iter;
+
+ for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) )
+ {
+ if( pEMemory->timeLastSeen < gpGlobals->curtime - GROUNDTURRET_RETIRE_TIME )
+ {
+ pEMemory->hEnemy = NULL;
+ }
+ }
+
+ BaseClass::GatherConditions();
+
+ if( GetEnemy() && HasCondition(COND_SEE_ENEMY) )
+ {
+ m_flTimeLastSawEnemy = gpGlobals->curtime;
+ }
+ else
+ {
+ if( gpGlobals->curtime - m_flTimeLastSawEnemy >= GROUNDTURRET_RETIRE_TIME )
+ {
+ m_OnAreaClear.FireOutput(this, this);
+ m_flTimeLastSawEnemy = FLT_MAX;
+ return;
+ }
+ }
+
+ if( HasCondition( COND_SEE_ENEMY ) )
+ {
+ m_bSeeEnemy = true;
+ }
+ else
+ {
+ m_bSeeEnemy = false;
+ }
+
+ if( GetEnemy() && m_bSeeEnemy && IsEnabled() )
+ {
+ if( m_flTimeNextShoot < gpGlobals->curtime )
+ {
+ Shoot();
+ }
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+Vector CNPC_GroundTurret::EyePosition()
+{
+ if( ai_newgroundturret.GetBool() )
+ {
+ return GetAbsOrigin() + Vector( 0, 0, 6 );
+ }
+
+ return GetAbsOrigin() + GetViewOffset();
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+bool CNPC_GroundTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
+{
+ if ( BaseClass::FVisible( pEntity, traceMask, ppBlocker ) )
+ return true;
+ if ( ( pEntity->GetAbsOrigin().AsVector2D() - GetAbsOrigin().AsVector2D() ).LengthSqr() < Square(10*12) &&
+ FInViewCone( pEntity->GetAbsOrigin() ) &&
+ BaseClass::FVisible( pEntity->GetAbsOrigin() + Vector( 0, 0, 1 ), traceMask, ppBlocker ) )
+ return true;
+ return false;
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+bool CNPC_GroundTurret::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC)
+{
+ float flDist;
+
+ flDist = (pEntity->GetAbsOrigin() - EyePosition()).Length2DSqr();
+
+ if( flDist <= m_flSensingDist * m_flSensingDist )
+ {
+ return BaseClass::QuerySeeEntity(pEntity, bOnlyHateOrFearIfNPC);
+ }
+
+ return false;
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+bool CNPC_GroundTurret::IsEnabled()
+{
+ if( ai_newgroundturret.GetBool() )
+ {
+ return true;
+ }
+
+ return m_bEnabled;
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+bool CNPC_GroundTurret::IsOpen()
+{
+ // The method is hacky but in the end, this does actually give
+ // us a pretty good idea if the turret is open or closed.
+ return( fabs(GetAbsOrigin().z - m_vecClosedPos.z ) > 1.0f );
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::StartTask( const Task_t *pTask )
+{
+ switch( pTask->iTask )
+ {
+ case TASK_GROUNDTURRET_SCAN:
+ Scan();
+ break;
+
+ default:
+ BaseClass::StartTask( pTask );
+ break;
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CNPC_GroundTurret::RunTask( const Task_t *pTask )
+{
+ switch( pTask->iTask )
+ {
+ case TASK_GROUNDTURRET_SCAN:
+ Scan();
+ break;
+
+ default:
+ BaseClass::RunTask( pTask );
+ break;
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+int CNPC_GroundTurret::SelectSchedule( void )
+{
+ return SCHED_GROUND_TURRET_IDLE;
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+int CNPC_GroundTurret::TranslateSchedule( int scheduleType )
+{
+ switch( scheduleType )
+ {
+ case SCHED_IDLE_STAND:
+ return SCHED_GROUND_TURRET_IDLE;
+ break;
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override base class activiites
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+Activity CNPC_GroundTurret::NPC_TranslateActivity( Activity activity )
+{
+ return ACT_IDLE;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::Shoot()
+{
+ FireBulletsInfo_t info;
+
+ Vector vecSrc = EyePosition();
+ Vector vecDir;
+
+ GetVectors( &vecDir, NULL, NULL );
+
+ for( int i = 0 ; i < 1 ; i++ )
+ {
+ info.m_vecSrc = vecSrc;
+
+ if( i > 0 || !GetEnemy()->IsPlayer() )
+ {
+ // Subsequent shots or shots at non-players random
+ GetVectors( &info.m_vecDirShooting, NULL, NULL );
+ info.m_vecSpread = m_vecSpread;
+ }
+ else
+ {
+ // First shot is at the enemy.
+ info.m_vecDirShooting = GetActualShootTrajectory( vecSrc );
+ info.m_vecSpread = VECTOR_CONE_PRECALCULATED;
+ }
+
+ info.m_iTracerFreq = 1;
+ info.m_iShots = 1;
+ info.m_pAttacker = this;
+ info.m_flDistance = MAX_COORD_RANGE;
+ info.m_iAmmoType = m_iAmmoType;
+
+ FireBullets( info );
+ }
+
+ // Do the AR2 muzzle flash
+ CEffectData data;
+ data.m_nEntIndex = entindex();
+ data.m_nAttachmentIndex = LookupAttachment( "eyes" );
+ data.m_flScale = 1.0f;
+ data.m_fFlags = MUZZLEFLASH_COMBINE;
+ DispatchEffect( "MuzzleFlash", data );
+
+ EmitSound( "NPC_FloorTurret.ShotSounds", m_ShotSounds );
+
+ if( IsX360() )
+ {
+ m_flTimeNextShoot = gpGlobals->curtime + 0.2;
+ }
+ else
+ {
+ m_flTimeNextShoot = gpGlobals->curtime + 0.09;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::ProjectBeam( const Vector &vecStart, const Vector &vecDir, int width, int brightness, float duration )
+{
+ CBeam *pBeam;
+ pBeam = CBeam::BeamCreate( GROUNDTURRET_BEAM_SPRITE, width );
+ if ( !pBeam )
+ return;
+
+ trace_t tr;
+ AI_TraceLine( vecStart, vecStart + vecDir * m_flSensingDist, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+
+ pBeam->SetStartPos( tr.endpos );
+ pBeam->SetEndPos( tr.startpos );
+ pBeam->SetWidth( width );
+ pBeam->SetEndWidth( 0.1 );
+ pBeam->SetFadeLength( 16 );
+
+ pBeam->SetBrightness( brightness );
+ pBeam->SetColor( 0, 145+random->RandomInt( -16, 16 ), 255 );
+ pBeam->RelinkBeam();
+ pBeam->LiveForTime( duration );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::Scan()
+{
+ if( m_bSeeEnemy )
+ {
+ // Using a bool for this check because the condition gets wiped out by changing schedules.
+ return;
+ }
+
+ if( IsOpeningOrClosing() )
+ {
+ // Moving.
+ return;
+ }
+
+ if( !IsOpen() )
+ {
+ // Closed
+ return;
+ }
+
+ if( !UTIL_FindClientInPVS(edict()) )
+ {
+ return;
+ }
+
+ if( gpGlobals->curtime >= m_flTimeNextPing )
+ {
+ EmitSound( "NPC_FloorTurret.Ping" );
+ m_flTimeNextPing = gpGlobals->curtime + 1.0f;
+ }
+
+ QAngle scanAngle;
+ Vector forward;
+ Vector vecEye = GetAbsOrigin() + m_vecLightOffset;
+
+ // Draw the outer extents
+ scanAngle = GetAbsAngles();
+ scanAngle.y += (GROUNDTURRET_VIEWCONE / 2.0f);
+ AngleVectors( scanAngle, &forward, NULL, NULL );
+ ProjectBeam( vecEye, forward, 1, 30, 0.1 );
+
+ scanAngle = GetAbsAngles();
+ scanAngle.y -= (GROUNDTURRET_VIEWCONE / 2.0f);
+ AngleVectors( scanAngle, &forward, NULL, NULL );
+ ProjectBeam( vecEye, forward, 1, 30, 0.1 );
+
+ // Draw a sweeping beam
+ scanAngle = GetAbsAngles();
+ scanAngle.y += (GROUNDTURRET_VIEWCONE / 2.0f) * sin( gpGlobals->curtime * 3.0f );
+
+ AngleVectors( scanAngle, &forward, NULL, NULL );
+ ProjectBeam( vecEye, forward, 1, 30, 0.3 );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::InputEnable( inputdata_t &inputdata )
+{
+ m_bEnabled = true;
+
+ // Because the turret might not ever ACQUIRE an enemy, we need to arrange to
+ // retire after a few seconds.
+ m_flTimeLastSawEnemy = gpGlobals->curtime;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_GroundTurret::InputDisable( inputdata_t &inputdata )
+{
+ m_bEnabled = false;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Schedules
+//
+//-----------------------------------------------------------------------------
+
+AI_BEGIN_CUSTOM_NPC( npc_groundturret, CNPC_GroundTurret )
+
+ DECLARE_TASK( TASK_GROUNDTURRET_SCAN );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_GROUND_TURRET_IDLE,
+
+ " Tasks "
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ " TASK_GROUNDTURRET_SCAN 0"
+ ""
+ " Interrupts "
+ " COND_NEW_ENEMY"
+ " COND_SEE_ENEMY"
+ " COND_LOST_ENEMY"
+ )
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_GROUND_TURRET_ATTACK,
+
+ " Tasks "
+ " TASK_WAIT_INDEFINITE 0"
+ ""
+ " Interrupts "
+ " COND_NEW_ENEMY"
+ " COND_LOST_ENEMY"
+ " COND_SEE_ENEMY"
+ )
+
+AI_END_CUSTOM_NPC()
+