📄 hl1_npc_aflock.cpp
字号:
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
// events
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "AI_Default.h"
#include "AI_Task.h"
#include "AI_Schedule.h"
#include "AI_Node.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Route.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "EntityList.h"
#include "activitylist.h"
#include "animation.h"
#include "basecombatweapon.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
#include "hl1_ai_basenpc.h"
#define AFLOCK_MAX_RECRUIT_RADIUS 1024
#define AFLOCK_FLY_SPEED 125
#define AFLOCK_TURN_RATE 75
#define AFLOCK_ACCELERATE 10
#define AFLOCK_CHECK_DIST 192
#define AFLOCK_TOO_CLOSE 100
#define AFLOCK_TOO_FAR 256
//=========================================================
//=========================================================
class CNPC_FlockingFlyerFlock : public CHL1BaseNPC
{
DECLARE_CLASS( CNPC_FlockingFlyerFlock, CHL1BaseNPC );
public:
void Spawn( void );
void Precache( void );
bool KeyValue( const char *szKeyName, const char *szValue );
void SpawnFlock( void );
// Sounds are shared by the flock
static void PrecacheFlockSounds( void );
DECLARE_DATADESC();
int m_cFlockSize;
float m_flFlockRadius;
};
BEGIN_DATADESC( CNPC_FlockingFlyerFlock )
DEFINE_FIELD( CNPC_FlockingFlyerFlock, m_cFlockSize, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_FlockingFlyerFlock, m_flFlockRadius, FIELD_FLOAT ),
END_DATADESC()
class CNPC_FlockingFlyer : public CHL1BaseNPC
{
DECLARE_CLASS( CNPC_FlockingFlyer, CHL1BaseNPC );
public:
void Spawn( void );
void Precache( void );
void SpawnCommonCode( void );
void IdleThink( void );
void BoidAdvanceFrame( void );
void Start( void );
bool FPathBlocked( void );
void FlockLeaderThink( void );
void SpreadFlock( void );
void SpreadFlock2( void );
void MakeSound( void );
void FlockFollowerThink( void );
void Event_Killed( const CTakeDamageInfo &info );
void FallHack( void );
//void Poop ( void ); Adrian - wtf?!
int IsLeader( void ) { return m_pSquadLeader == this; }
int InSquad( void ) { return m_pSquadLeader != NULL; }
int SquadCount( void );
void SquadRemove( CNPC_FlockingFlyer *pRemove );
void SquadUnlink( void );
void SquadAdd( CNPC_FlockingFlyer *pAdd );
void SquadDisband( void );
CNPC_FlockingFlyer *m_pSquadLeader;
CNPC_FlockingFlyer *m_pSquadNext;
bool m_fTurning;// is this boid turning?
bool m_fCourseAdjust;// followers set this flag TRUE to override flocking while they avoid something
bool m_fPathBlocked;// TRUE if there is an obstacle ahead
Vector m_vecReferencePoint;// last place we saw leader
Vector m_vecAdjustedVelocity;// adjusted velocity (used when fCourseAdjust is TRUE)
float m_flGoalSpeed;
float m_flLastBlockedTime;
float m_flFakeBlockedTime;
float m_flAlertTime;
float m_flFlockNextSoundTime;
float m_flTempVar;
DECLARE_DATADESC();
};
BEGIN_DATADESC( CNPC_FlockingFlyer )
DEFINE_FIELD( CNPC_FlockingFlyer, m_pSquadLeader, FIELD_CLASSPTR ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_pSquadNext, FIELD_CLASSPTR ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_fTurning, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_fCourseAdjust, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_fPathBlocked, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_vecReferencePoint, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_vecAdjustedVelocity, FIELD_VECTOR ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_flGoalSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_flLastBlockedTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_flFakeBlockedTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_FlockingFlyer, m_flAlertTime, FIELD_TIME ),
DEFINE_THINKFUNC( CNPC_FlockingFlyer, IdleThink ),
DEFINE_THINKFUNC( CNPC_FlockingFlyer, Start ),
DEFINE_THINKFUNC( CNPC_FlockingFlyer, FlockLeaderThink ),
DEFINE_THINKFUNC( CNPC_FlockingFlyer, FlockFollowerThink ),
DEFINE_THINKFUNC( CNPC_FlockingFlyer, FallHack ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( monster_flyer, CNPC_FlockingFlyer );
LINK_ENTITY_TO_CLASS( monster_flyer_flock, CNPC_FlockingFlyerFlock );
bool CNPC_FlockingFlyerFlock::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "iFlockSize" ) )
{
m_cFlockSize = atoi( szValue );
return true;
}
else if ( FStrEq( szKeyName, "flFlockRadius" ) )
{
m_flFlockRadius = atof( szValue );
return true;
}
else
BaseClass::KeyValue( szKeyName, szValue );
return false;
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyerFlock::Spawn( void )
{
Precache( );
SetRenderColor( 255, 255, 255, 255 );
SpawnFlock();
SetThink( SUB_Remove );
SetNextThink( gpGlobals->curtime + 0.1f );
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyerFlock::Precache( void )
{
//PRECACHE_MODEL("models/aflock.mdl");
engine->PrecacheModel("models/boid.mdl");
PrecacheFlockSounds();
}
void CNPC_FlockingFlyerFlock::SpawnFlock( void )
{
float R = m_flFlockRadius;
int iCount;
Vector vecSpot;
CNPC_FlockingFlyer *pBoid, *pLeader;
pLeader = pBoid = NULL;
for ( iCount = 0 ; iCount < m_cFlockSize ; iCount++ )
{
pBoid = CREATE_ENTITY( CNPC_FlockingFlyer, "monster_flyer" );
if ( !pLeader )
{
// make this guy the leader.
pLeader = pBoid;
pLeader->m_pSquadLeader = pLeader;
pLeader->m_pSquadNext = NULL;
}
vecSpot.x = random->RandomFloat( -R, R );
vecSpot.y = random->RandomFloat( -R, R );
vecSpot.z = random->RandomFloat( 0, 16 );
vecSpot = GetAbsOrigin() + vecSpot;
UTIL_SetOrigin( pBoid, vecSpot);
pBoid->SetMoveType( MOVETYPE_FLY );
pBoid->SpawnCommonCode();
pBoid->RemoveFlag( FL_ONGROUND );
pBoid->SetAbsVelocity( Vector ( 0, 0, 0 ) );
pBoid->SetAbsAngles( GetAbsAngles() );
pBoid->m_flCycle = 0;
pBoid->SetThink( CNPC_FlockingFlyer::IdleThink );
pBoid->SetNextThink( gpGlobals->curtime + 0.2 );
if ( pBoid != pLeader )
{
pLeader->SquadAdd( pBoid );
}
}
}
void CNPC_FlockingFlyerFlock::PrecacheFlockSounds( void )
{
enginesound->PrecacheSound("boid/boid_alert1.wav" );
enginesound->PrecacheSound("boid/boid_alert2.wav" );
enginesound->PrecacheSound("boid/boid_idle1.wav" );
enginesound->PrecacheSound("boid/boid_idle2.wav" );
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer::Spawn( )
{
Precache( );
SpawnCommonCode();
m_flCycle = 0;
SetNextThink( gpGlobals->curtime + 0.1f );
SetThink( IdleThink );
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer::SpawnCommonCode( )
{
m_lifeState = LIFE_ALIVE;
pev->classname = MAKE_STRING("monster_flyer");
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_FLY );
m_takedamage = DAMAGE_NO;
m_iHealth = 1;
m_fPathBlocked = FALSE;// obstacles will be detected
m_flFieldOfView = 0.2;
m_flTempVar = 0;
//SET_MODEL(ENT(pev), "models/aflock.mdl");
SetModel( "models/boid.mdl" );
// UTIL_SetSize(this, Vector(0,0,0), Vector(0,0,0));
UTIL_SetSize(this, Vector(-5,-5,0), Vector(5,5,2));
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer::Precache( )
{
//PRECACHE_MODEL("models/aflock.mdl");
engine->PrecacheModel("models/boid.mdl");
CNPC_FlockingFlyerFlock::PrecacheFlockSounds();
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer :: IdleThink( void )
{
SetNextThink( gpGlobals->curtime + 0.2 );
// see if there's a client in the same pvs as the monster
if ( !FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
{
SetThink( Start );
SetNextThink( gpGlobals->curtime + 0.1f );
}
}
//=========================================================
//
// SquadUnlink(), Unlink the squad pointers.
//
//=========================================================
void CNPC_FlockingFlyer::SquadUnlink( void )
{
m_pSquadLeader = NULL;
m_pSquadNext = NULL;
}
//=========================================================
//
// SquadAdd(), add pAdd to my squad
//
//=========================================================
void CNPC_FlockingFlyer::SquadAdd( CNPC_FlockingFlyer *pAdd )
{
ASSERT( pAdd!=NULL );
ASSERT( !pAdd->InSquad() );
ASSERT( this->IsLeader() );
pAdd->m_pSquadNext = m_pSquadNext;
m_pSquadNext = pAdd;
pAdd->m_pSquadLeader = this;
}
//=========================================================
//
// SquadRemove(), remove pRemove from my squad.
// If I am pRemove, promote m_pSquadNext to leader
//
//=========================================================
void CNPC_FlockingFlyer::SquadRemove( CNPC_FlockingFlyer *pRemove )
{
ASSERT( pRemove!=NULL );
ASSERT( this->IsLeader() );
ASSERT( pRemove->m_pSquadLeader == this );
if ( SquadCount() > 2 )
{
// Removing the leader, promote m_pSquadNext to leader
if ( pRemove == this )
{
CNPC_FlockingFlyer *pLeader = m_pSquadNext;
// copy the enemy LKP to the new leader
// if ( GetEnemy() )
// pLeader->m_vecEnemyLKP = m_vecEnemyLKP;
if ( pLeader )
{
CNPC_FlockingFlyer *pList = pLeader;
while ( pList )
{
pList->m_pSquadLeader = pLeader;
pList = pList->m_pSquadNext;
}
}
SquadUnlink();
}
else // removing a node
{
CNPC_FlockingFlyer *pList = this;
// Find the node before pRemove
while ( pList->m_pSquadNext != pRemove )
{
// assert to test valid list construction
ASSERT( pList->m_pSquadNext != NULL );
pList = pList->m_pSquadNext;
}
// List validity
ASSERT( pList->m_pSquadNext == pRemove );
// Relink without pRemove
pList->m_pSquadNext = pRemove->m_pSquadNext;
// Unlink pRemove
pRemove->SquadUnlink();
}
}
else
SquadDisband();
}
//=========================================================
//
// SquadCount(), return the number of members of this squad
// callable from leaders & followers
//
//=========================================================
int CNPC_FlockingFlyer::SquadCount( void )
{
CNPC_FlockingFlyer *pList = m_pSquadLeader;
int squadCount = 0;
while ( pList )
{
squadCount++;
pList = pList->m_pSquadNext;
}
return squadCount;
}
//=========================================================
//
// SquadDisband(), Unlink all squad members
//
//=========================================================
void CNPC_FlockingFlyer::SquadDisband( void )
{
CNPC_FlockingFlyer *pList = m_pSquadLeader;
CNPC_FlockingFlyer *pNext;
while ( pList )
{
pNext = pList->m_pSquadNext;
pList->SquadUnlink();
pList = pNext;
}
}
//=========================================================
// Start - player enters the pvs, so get things going.
//=========================================================
void CNPC_FlockingFlyer::Start( void )
{
SetNextThink( gpGlobals->curtime + 0.1f );
if ( IsLeader() )
{
SetThink( FlockLeaderThink );
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -