hlsdk-xash3d/dlls/aflock.cpp

919 lines
24 KiB
C++
Raw Permalink Normal View History

2016-06-04 15:24:23 +02:00
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "squadmonster.h"
2016-07-31 15:48:50 +02:00
#define AFLOCK_MAX_RECRUIT_RADIUS 1024
2019-10-13 13:49:25 +02:00
#define AFLOCK_FLY_SPEED 125.0f
2016-06-04 15:24:23 +02:00
#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 CFlockingFlyerFlock : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void SpawnFlock( void );
2016-07-31 15:48:50 +02:00
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
2016-06-04 15:24:23 +02:00
// Sounds are shared by the flock
2016-07-31 15:48:50 +02:00
static void PrecacheFlockSounds( void );
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
int m_cFlockSize;
float m_flFlockRadius;
2016-06-04 15:24:23 +02:00
};
2016-07-31 15:48:50 +02:00
TYPEDESCRIPTION CFlockingFlyerFlock::m_SaveData[] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD( CFlockingFlyerFlock, m_cFlockSize, FIELD_INTEGER ),
DEFINE_FIELD( CFlockingFlyerFlock, m_flFlockRadius, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CFlockingFlyerFlock, CBaseMonster )
2016-06-04 15:24:23 +02:00
//=========================================================
//=========================================================
class CFlockingFlyer : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void SpawnCommonCode( void );
void EXPORT IdleThink( void );
void BoidAdvanceFrame( void );
void EXPORT FormFlock( void );
void EXPORT Start( void );
void EXPORT FlockLeaderThink( void );
void EXPORT FlockFollowerThink( void );
void EXPORT FallHack( void );
void MakeSound( void );
void AlertFlock( void );
void SpreadFlock( void );
void SpreadFlock2( void );
void Killed( entvars_t *pevAttacker, int iGib );
void Poop ( void );
BOOL FPathBlocked( void );
//void KeyValue( KeyValueData *pkvd );
2016-07-31 15:48:50 +02:00
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
2016-06-04 15:24:23 +02:00
int IsLeader( void ) { return m_pSquadLeader == this; }
2016-07-31 15:48:50 +02:00
int InSquad( void ) { return m_pSquadLeader != NULL; }
int SquadCount( void );
2016-06-04 15:24:23 +02:00
void SquadRemove( CFlockingFlyer *pRemove );
void SquadUnlink( void );
void SquadAdd( CFlockingFlyer *pAdd );
void SquadDisband( void );
CFlockingFlyer *m_pSquadLeader;
CFlockingFlyer *m_pSquadNext;
2016-07-31 15:48:50 +02:00
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;
2016-06-04 15:24:23 +02:00
};
LINK_ENTITY_TO_CLASS( monster_flyer, CFlockingFlyer )
LINK_ENTITY_TO_CLASS( monster_flyer_flock, CFlockingFlyerFlock )
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
TYPEDESCRIPTION CFlockingFlyer::m_SaveData[] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD( CFlockingFlyer, m_pSquadLeader, FIELD_CLASSPTR ),
DEFINE_FIELD( CFlockingFlyer, m_pSquadNext, FIELD_CLASSPTR ),
DEFINE_FIELD( CFlockingFlyer, m_fTurning, FIELD_BOOLEAN ),
DEFINE_FIELD( CFlockingFlyer, m_fCourseAdjust, FIELD_BOOLEAN ),
DEFINE_FIELD( CFlockingFlyer, m_fPathBlocked, FIELD_BOOLEAN ),
DEFINE_FIELD( CFlockingFlyer, m_vecReferencePoint, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CFlockingFlyer, m_vecAdjustedVelocity, FIELD_VECTOR ),
DEFINE_FIELD( CFlockingFlyer, m_flGoalSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CFlockingFlyer, m_flLastBlockedTime, FIELD_TIME ),
DEFINE_FIELD( CFlockingFlyer, m_flFakeBlockedTime, FIELD_TIME ),
DEFINE_FIELD( CFlockingFlyer, m_flAlertTime, FIELD_TIME ),
2016-07-31 15:48:50 +02:00
//DEFINE_FIELD( CFlockingFlyer, m_flFlockNextSoundTime, FIELD_TIME ), // don't need to save
2016-06-04 15:24:23 +02:00
};
IMPLEMENT_SAVERESTORE( CFlockingFlyer, CBaseMonster )
2016-06-04 15:24:23 +02:00
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyerFlock::KeyValue( KeyValueData *pkvd )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( FStrEq( pkvd->szKeyName, "iFlockSize" ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_cFlockSize = atoi( pkvd->szValue );
2016-06-04 15:24:23 +02:00
pkvd->fHandled = TRUE;
}
2016-07-31 15:48:50 +02:00
else if( FStrEq( pkvd->szKeyName, "flFlockRadius" ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_flFlockRadius = atof( pkvd->szValue );
2016-06-04 15:24:23 +02:00
pkvd->fHandled = TRUE;
}
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyerFlock::Spawn()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
Precache();
2016-06-04 15:24:23 +02:00
SpawnFlock();
2016-07-31 15:48:50 +02:00
REMOVE_ENTITY( ENT( pev ) ); // dump the spawn ent
2016-06-04 15:24:23 +02:00
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyerFlock::Precache()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
//PRECACHE_MODEL( "models/aflock.mdl" );
2016-08-13 09:16:23 +02:00
PRECACHE_MODEL( "models/boid.mdl" );
2016-06-04 15:24:23 +02:00
PrecacheFlockSounds();
}
2016-07-31 15:48:50 +02:00
void CFlockingFlyerFlock::PrecacheFlockSounds( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
PRECACHE_SOUND( "boid/boid_alert1.wav" );
PRECACHE_SOUND( "boid/boid_alert2.wav" );
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
PRECACHE_SOUND( "boid/boid_idle1.wav" );
PRECACHE_SOUND( "boid/boid_idle2.wav" );
2016-06-04 15:24:23 +02:00
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyerFlock::SpawnFlock( void )
2016-06-04 15:24:23 +02:00
{
float R = m_flFlockRadius;
int iCount;
Vector vecSpot;
CFlockingFlyer *pBoid, *pLeader;
pLeader = pBoid = NULL;
2016-07-31 15:48:50 +02:00
for( iCount = 0; iCount < m_cFlockSize; iCount++ )
2016-06-04 15:24:23 +02:00
{
pBoid = GetClassPtr( (CFlockingFlyer *)NULL );
2016-07-31 15:48:50 +02:00
if( !pLeader )
2016-06-04 15:24:23 +02:00
{
// make this guy the leader.
pLeader = pBoid;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
pLeader->m_pSquadLeader = pLeader;
pLeader->m_pSquadNext = NULL;
}
vecSpot.x = RANDOM_FLOAT( -R, R );
vecSpot.y = RANDOM_FLOAT( -R, R );
2019-10-13 13:49:25 +02:00
vecSpot.z = RANDOM_FLOAT( 0.0f, 16.0f );
2016-06-04 15:24:23 +02:00
vecSpot = pev->origin + vecSpot;
2016-07-31 15:48:50 +02:00
UTIL_SetOrigin( pBoid->pev, vecSpot );
2016-06-04 15:24:23 +02:00
pBoid->pev->movetype = MOVETYPE_FLY;
pBoid->SpawnCommonCode();
pBoid->pev->flags &= ~FL_ONGROUND;
pBoid->pev->velocity = g_vecZero;
2016-07-31 15:48:50 +02:00
pBoid->pev->angles = pev->angles;
2016-06-04 15:24:23 +02:00
pBoid->pev->frame = 0;
2019-10-13 13:49:25 +02:00
pBoid->pev->nextthink = gpGlobals->time + 0.2f;
2016-07-31 15:48:50 +02:00
pBoid->SetThink( &CFlockingFlyer::IdleThink );
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if( pBoid != pLeader )
2016-06-04 15:24:23 +02:00
{
pLeader->SquadAdd( pBoid );
}
}
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::Spawn()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
Precache();
2016-06-04 15:24:23 +02:00
SpawnCommonCode();
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
pev->frame = 0;
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-06-04 15:24:23 +02:00
SetThink( &CFlockingFlyer::IdleThink );
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::Precache()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
//PRECACHE_MODEL( "models/aflock.mdl" );
PRECACHE_MODEL( "models/boid.mdl" );
2016-06-04 15:24:23 +02:00
CFlockingFlyerFlock::PrecacheFlockSounds();
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::MakeSound( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( m_flAlertTime > gpGlobals->time )
2016-06-04 15:24:23 +02:00
{
// make agitated sounds
switch ( RANDOM_LONG( 0, 1 ) )
{
2016-07-31 15:48:50 +02:00
case 0:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "boid/boid_alert1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "boid/boid_alert2.wav", 1, ATTN_NORM );
break;
2016-06-04 15:24:23 +02:00
}
return;
}
// make normal sound
2016-07-31 15:48:50 +02:00
switch( RANDOM_LONG( 0, 1 ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
case 0:
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle1.wav", 1, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle2.wav", 1, ATTN_NORM );
break;
2016-06-04 15:24:23 +02:00
}
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::Killed( entvars_t *pevAttacker, int iGib )
2016-06-04 15:24:23 +02:00
{
CFlockingFlyer *pSquad;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
pSquad = (CFlockingFlyer *)m_pSquadLeader;
2016-07-31 15:48:50 +02:00
while( pSquad )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
pSquad->m_flAlertTime = gpGlobals->time + 15.0f;
2016-06-04 15:24:23 +02:00
pSquad = (CFlockingFlyer *)pSquad->m_pSquadNext;
}
2016-07-31 15:48:50 +02:00
if( m_pSquadLeader )
2016-06-04 15:24:23 +02:00
{
m_pSquadLeader->SquadRemove( this );
}
pev->deadflag = DEAD_DEAD;
pev->framerate = 0;
pev->effects = EF_NOINTERP;
2016-07-31 15:48:50 +02:00
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
2016-06-04 15:24:23 +02:00
pev->movetype = MOVETYPE_TOSS;
SetThink( &CFlockingFlyer::FallHack );
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::FallHack( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( pev->flags & FL_ONGROUND )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !FClassnameIs ( pev->groundentity, "worldspawn" ) )
2016-06-04 15:24:23 +02:00
{
pev->flags &= ~FL_ONGROUND;
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-06-04 15:24:23 +02:00
}
else
{
pev->velocity = g_vecZero;
SetThink( NULL );
}
}
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SpawnCommonCode()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
pev->deadflag = DEAD_NO;
pev->classname = MAKE_STRING( "monster_flyer" );
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
2016-06-04 15:24:23 +02:00
pev->takedamage = DAMAGE_NO;
2016-07-31 15:48:50 +02:00
pev->health = 1;
2016-06-04 15:24:23 +02:00
m_fPathBlocked = FALSE;// obstacles will be detected
2019-10-13 13:49:25 +02:00
m_flFieldOfView = 0.2f;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
//SET_MODEL( ENT( pev ), "models/aflock.mdl" );
SET_MODEL( ENT( pev ), "models/boid.mdl" );
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
//UTIL_SetSize( pev, Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ) );
UTIL_SetSize( pev, Vector( -5.0f, -5.0f, 0.0f ), Vector( 5.0f, 5.0f, 2.0f ) );
2016-06-04 15:24:23 +02:00
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::BoidAdvanceFrame()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
float flapspeed = ( pev->speed - pev->armorvalue ) / AFLOCK_ACCELERATE;
2019-10-13 13:49:25 +02:00
pev->armorvalue = pev->armorvalue * 0.8f + pev->speed * 0.2f;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
if( flapspeed < 0.0f )
2016-07-31 15:48:50 +02:00
flapspeed = -flapspeed;
2019-10-13 13:49:25 +02:00
if( flapspeed < 0.25f )
flapspeed = 0.25f;
if( flapspeed > 1.9f )
flapspeed = 1.9f;
2016-06-04 15:24:23 +02:00
pev->framerate = flapspeed;
// lean
2019-10-13 13:49:25 +02:00
pev->avelocity.x = -( pev->angles.x + flapspeed * 5.0f );
2016-06-04 15:24:23 +02:00
// bank
2016-07-31 15:48:50 +02:00
pev->avelocity.z = -( pev->angles.z + pev->avelocity.y );
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
// pev->framerate = flapspeed;
2019-10-13 13:49:25 +02:00
StudioFrameAdvance( 0.1f );
2016-06-04 15:24:23 +02:00
}
//=========================================================
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::IdleThink( void )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.2f;
2016-06-04 15:24:23 +02:00
// see if there's a client in the same pvs as the monster
2016-07-31 15:48:50 +02:00
if( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
2016-06-04 15:24:23 +02:00
{
SetThink( &CFlockingFlyer::Start );
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-06-04 15:24:23 +02:00
}
}
//=========================================================
// Start - player enters the pvs, so get things going.
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::Start( void )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if( IsLeader() )
2016-06-04 15:24:23 +02:00
{
SetThink( &CFlockingFlyer::FlockLeaderThink );
}
else
{
SetThink( &CFlockingFlyer::FlockFollowerThink );
}
/*
2016-07-31 15:48:50 +02:00
Vector vecTakeOff;
vecTakeOff = Vector( 0, 0, 0 );
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
vecTakeOff.z = 50 + RANDOM_FLOAT( 0, 100 );
vecTakeOff.x = 20 - RANDOM_FLOAT( 0, 40 );
vecTakeOff.y = 20 - RANDOM_FLOAT( 0, 40 );
2016-06-04 15:24:23 +02:00
pev->velocity = vecTakeOff;
pev->speed = pev->velocity.Length();
pev->sequence = 0;
*/
2016-07-31 15:48:50 +02:00
SetActivity( ACT_FLY );
ResetSequenceInfo();
BoidAdvanceFrame();
2016-06-04 15:24:23 +02:00
pev->speed = AFLOCK_FLY_SPEED;// no delay!
}
//=========================================================
// Leader boid calls this to form a flock from surrounding boids
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::FormFlock( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !InSquad() )
2016-06-04 15:24:23 +02:00
{
// I am my own leader
m_pSquadLeader = this;
m_pSquadNext = NULL;
int squadCount = 1;
CBaseEntity *pEntity = NULL;
2016-07-31 15:48:50 +02:00
while( ( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, AFLOCK_MAX_RECRUIT_RADIUS ) ) != NULL )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
CBaseMonster *pRecruit = pEntity->MyMonsterPointer();
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine )
2016-06-04 15:24:23 +02:00
{
// Can we recruit this guy?
2016-07-31 15:48:50 +02:00
if( FClassnameIs ( pRecruit->pev, "monster_flyer" ) )
2016-06-04 15:24:23 +02:00
{
squadCount++;
SquadAdd( (CFlockingFlyer *)pRecruit );
}
}
}
}
SetThink( &CFlockingFlyer::IdleThink );// now that flock is formed, go to idle and wait for a player to come along.
pev->nextthink = gpGlobals->time;
}
//=========================================================
// Searches for boids that are too close and pushes them away
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SpreadFlock()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
Vector vecDir;
float flSpeed;// holds vector magnitude while we fiddle with the direction
2016-06-04 15:24:23 +02:00
CFlockingFlyer *pList = m_pSquadLeader;
2016-07-31 15:48:50 +02:00
while( pList )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
2016-06-04 15:24:23 +02:00
{
// push the other away
2016-07-31 15:48:50 +02:00
vecDir = pList->pev->origin - pev->origin;
2016-06-04 15:24:23 +02:00
vecDir = vecDir.Normalize();
// store the magnitude of the other boid's velocity, and normalize it so we
// can average in a course that points away from the leader.
flSpeed = pList->pev->velocity.Length();
pList->pev->velocity = pList->pev->velocity.Normalize();
2019-10-13 13:49:25 +02:00
pList->pev->velocity = ( pList->pev->velocity + vecDir ) * 0.5f;
2016-06-04 15:24:23 +02:00
pList->pev->velocity = pList->pev->velocity * flSpeed;
}
pList = pList->m_pSquadNext;
}
}
//=========================================================
// Alters the caller's course if he's too close to others
//
// This function should **ONLY** be called when Caller's velocity is normalized!!
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SpreadFlock2()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
Vector vecDir;
2016-06-04 15:24:23 +02:00
CFlockingFlyer *pList = m_pSquadLeader;
2016-07-31 15:48:50 +02:00
while( pList )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
vecDir = pev->origin - pList->pev->origin;
2016-06-04 15:24:23 +02:00
vecDir = vecDir.Normalize();
2016-07-31 15:48:50 +02:00
pev->velocity = pev->velocity + vecDir;
2016-06-04 15:24:23 +02:00
}
pList = pList->m_pSquadNext;
}
}
//=========================================================
// FBoidPathBlocked - returns TRUE if there is an obstacle ahead
//=========================================================
2016-07-31 15:48:50 +02:00
BOOL CFlockingFlyer::FPathBlocked()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
TraceResult tr;
Vector vecDist;// used for general measurements
Vector vecDir;// used for general measurements
BOOL fBlocked;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if( m_flFakeBlockedTime > gpGlobals->time )
2016-06-04 15:24:23 +02:00
{
m_flLastBlockedTime = gpGlobals->time;
return TRUE;
}
// use VELOCITY, not angles, not all boids point the direction they are flying
//vecDir = UTIL_VecToAngles( pevBoid->velocity );
UTIL_MakeVectors ( pev->angles );
fBlocked = FALSE;// assume the way ahead is clear
// check for obstacle ahead
2016-07-31 15:48:50 +02:00
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
2019-10-13 13:49:25 +02:00
if( tr.flFraction != 1.0f )
2016-06-04 15:24:23 +02:00
{
m_flLastBlockedTime = gpGlobals->time;
fBlocked = TRUE;
}
// extra wide checks
2019-10-13 13:49:25 +02:00
UTIL_TraceLine( pev->origin + gpGlobals->v_right * 12.0f, pev->origin + gpGlobals->v_right * 12.0f + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
if( tr.flFraction != 1.0f )
2016-06-04 15:24:23 +02:00
{
m_flLastBlockedTime = gpGlobals->time;
fBlocked = TRUE;
}
2019-10-13 13:49:25 +02:00
UTIL_TraceLine( pev->origin - gpGlobals->v_right * 12.0f, pev->origin - gpGlobals->v_right * 12.0f + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
if( tr.flFraction != 1.0f )
2016-06-04 15:24:23 +02:00
{
m_flLastBlockedTime = gpGlobals->time;
fBlocked = TRUE;
}
2019-10-13 13:49:25 +02:00
if( !fBlocked && gpGlobals->time - m_flLastBlockedTime > 6.0f )
2016-06-04 15:24:23 +02:00
{
// not blocked, and it's been a few seconds since we've actually been blocked.
2016-07-31 15:48:50 +02:00
m_flFakeBlockedTime = gpGlobals->time + RANDOM_LONG( 1, 3 );
2016-06-04 15:24:23 +02:00
}
return fBlocked;
}
//=========================================================
// Leader boids use this think every tenth
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::FlockLeaderThink( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
TraceResult tr;
Vector vecDist;// used for general measurements
Vector vecDir;// used for general measurements
float flLeftSide;
float flRightSide;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-07-31 15:48:50 +02:00
UTIL_MakeVectors( pev->angles );
2016-06-04 15:24:23 +02:00
// is the way ahead clear?
2016-07-31 15:48:50 +02:00
if( !FPathBlocked () )
2016-06-04 15:24:23 +02:00
{
// if the boid is turning, stop the trend.
2016-07-31 15:48:50 +02:00
if( m_fTurning )
2016-06-04 15:24:23 +02:00
{
m_fTurning = FALSE;
pev->avelocity.y = 0;
}
m_fPathBlocked = FALSE;
2016-07-31 15:48:50 +02:00
if( pev->speed <= AFLOCK_FLY_SPEED )
2019-10-13 13:49:25 +02:00
pev->speed += 5.0f;
2016-06-04 15:24:23 +02:00
pev->velocity = gpGlobals->v_forward * pev->speed;
2016-07-31 15:48:50 +02:00
BoidAdvanceFrame();
2016-06-04 15:24:23 +02:00
return;
}
// IF we get this far in the function, the leader's path is blocked!
m_fPathBlocked = TRUE;
2016-07-31 15:48:50 +02:00
if( !m_fTurning )// something in the way and boid is not already turning to avoid
2016-06-04 15:24:23 +02:00
{
// measure clearance on left and right to pick the best dir to turn
2016-07-31 15:48:50 +02:00
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = ( tr.vecEndPos - pev->origin );
2016-06-04 15:24:23 +02:00
flRightSide = vecDist.Length();
2016-07-31 15:48:50 +02:00
UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = tr.vecEndPos - pev->origin;
2016-06-04 15:24:23 +02:00
flLeftSide = vecDist.Length();
// turn right if more clearance on right side
2016-07-31 15:48:50 +02:00
if( flRightSide > flLeftSide )
2016-06-04 15:24:23 +02:00
{
pev->avelocity.y = -AFLOCK_TURN_RATE;
m_fTurning = TRUE;
}
// default to left turn :)
2016-07-31 15:48:50 +02:00
else if( flLeftSide > flRightSide )
2016-06-04 15:24:23 +02:00
{
pev->avelocity.y = AFLOCK_TURN_RATE;
m_fTurning = TRUE;
}
else
{
// equidistant. Pick randomly between left and right.
m_fTurning = TRUE;
2016-07-31 15:48:50 +02:00
if( RANDOM_LONG( 0, 1 ) == 0 )
2016-06-04 15:24:23 +02:00
{
pev->avelocity.y = AFLOCK_TURN_RATE;
}
else
{
pev->avelocity.y = -AFLOCK_TURN_RATE;
}
}
}
2016-07-31 15:48:50 +02:00
SpreadFlock();
2016-06-04 15:24:23 +02:00
pev->velocity = gpGlobals->v_forward * pev->speed;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
// check and make sure we aren't about to plow into the ground, don't let it happen
2016-07-31 15:48:50 +02:00
UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_up * 16, ignore_monsters, ENT( pev ), &tr );
2019-10-13 13:49:25 +02:00
if( tr.flFraction != 1.0f && pev->velocity.z < 0.0f )
pev->velocity.z = 0.0f;
2016-06-04 15:24:23 +02:00
// maybe it did, though.
2016-07-31 15:48:50 +02:00
if( FBitSet( pev->flags, FL_ONGROUND ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
UTIL_SetOrigin( pev, pev->origin + Vector( 0, 0, 1 ) );
2016-06-04 15:24:23 +02:00
pev->velocity.z = 0;
}
2016-07-31 15:48:50 +02:00
if( m_flFlockNextSoundTime < gpGlobals->time )
2016-06-04 15:24:23 +02:00
{
MakeSound();
m_flFlockNextSoundTime = gpGlobals->time + RANDOM_FLOAT( 1, 3 );
}
BoidAdvanceFrame( );
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
return;
}
//=========================================================
// follower boids execute this code when flocking
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::FlockFollowerThink( void )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
TraceResult tr;
Vector vecDist;
Vector vecDir;
Vector vecDirToLeader;
float flDistToLeader;
2016-06-04 15:24:23 +02:00
2019-10-13 13:49:25 +02:00
pev->nextthink = gpGlobals->time + 0.1f;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if( IsLeader() || !InSquad() )
2016-06-04 15:24:23 +02:00
{
// the leader has been killed and this flyer suddenly finds himself the leader.
SetThink( &CFlockingFlyer::FlockLeaderThink );
return;
}
vecDirToLeader = ( m_pSquadLeader->pev->origin - pev->origin );
flDistToLeader = vecDirToLeader.Length();
// match heading with leader
pev->angles = m_pSquadLeader->pev->angles;
//
// We can see the leader, so try to catch up to it
//
2016-07-31 15:48:50 +02:00
if( FInViewCone ( m_pSquadLeader ) )
2016-06-04 15:24:23 +02:00
{
// if we're too far away, speed up
2016-07-31 15:48:50 +02:00
if( flDistToLeader > AFLOCK_TOO_FAR )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 1.5f;
2016-06-04 15:24:23 +02:00
}
// if we're too close, slow down
2016-07-31 15:48:50 +02:00
else if( flDistToLeader < AFLOCK_TOO_CLOSE )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5f;
2016-06-04 15:24:23 +02:00
}
}
else
{
// wait up! the leader isn't out in front, so we slow down to let him pass
2019-10-13 13:49:25 +02:00
m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5f;
2016-06-04 15:24:23 +02:00
}
SpreadFlock2();
pev->speed = pev->velocity.Length();
pev->velocity = pev->velocity.Normalize();
// if we are too far from leader, average a vector towards it into our current velocity
2016-07-31 15:48:50 +02:00
if( flDistToLeader > AFLOCK_TOO_FAR )
2016-06-04 15:24:23 +02:00
{
vecDirToLeader = vecDirToLeader.Normalize();
2019-10-13 13:49:25 +02:00
pev->velocity = (pev->velocity + vecDirToLeader) * 0.5f;
2016-06-04 15:24:23 +02:00
}
// clamp speeds and handle acceleration
2019-10-13 13:49:25 +02:00
if( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2.0f )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
m_flGoalSpeed = AFLOCK_FLY_SPEED * 2.0f;
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
if( pev->speed < m_flGoalSpeed )
2016-06-04 15:24:23 +02:00
{
pev->speed += AFLOCK_ACCELERATE;
}
2016-07-31 15:48:50 +02:00
else if( pev->speed > m_flGoalSpeed )
2016-06-04 15:24:23 +02:00
{
pev->speed -= AFLOCK_ACCELERATE;
}
pev->velocity = pev->velocity * pev->speed;
BoidAdvanceFrame( );
}
2016-07-31 15:48:50 +02:00
/*
2016-06-04 15:24:23 +02:00
// Is this boid's course blocked?
2016-07-31 15:48:50 +02:00
if( FBoidPathBlocked( pev ) )
2016-06-04 15:24:23 +02:00
{
// course is still blocked from last time. Just keep flying along adjusted
// velocity
2016-07-31 15:48:50 +02:00
if( m_fCourseAdjust )
2016-06-04 15:24:23 +02:00
{
pev->velocity = m_vecAdjustedVelocity * pev->speed;
return;
}
else // set course adjust flag and calculate adjusted velocity
{
m_fCourseAdjust = TRUE;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
// use VELOCITY, not angles, not all boids point the direction they are flying
//vecDir = UTIL_VecToAngles( pev->velocity );
2016-07-31 15:48:50 +02:00
//UTIL_MakeVectors( vecDir );
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
UTIL_MakeVectors( pev->angles );
2016-06-04 15:24:23 +02:00
// measure clearance on left and right to pick the best dir to turn
2016-07-31 15:48:50 +02:00
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = tr.vecEndPos - pev->origin;
2016-06-04 15:24:23 +02:00
flRightSide = vecDist.Length();
2016-07-31 15:48:50 +02:00
UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
vecDist = tr.vecEndPos - pev->origin;
2016-06-04 15:24:23 +02:00
flLeftSide = vecDist.Length();
// slide right if more clearance on right side
2016-07-31 15:48:50 +02:00
if( flRightSide > flLeftSide )
2016-06-04 15:24:23 +02:00
{
m_vecAdjustedVelocity = gpGlobals->v_right;
}
// else slide left
else
{
2019-10-13 13:49:25 +02:00
m_vecAdjustedVelocity = gpGlobals->v_right * -1.0f;
2016-06-04 15:24:23 +02:00
}
}
return;
}
// if we make it this far, boids path is CLEAR!
m_fCourseAdjust = FALSE;
*/
//=========================================================
//
// SquadUnlink(), Unlink the squad pointers.
//
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SquadUnlink( void )
2016-06-04 15:24:23 +02:00
{
m_pSquadLeader = NULL;
2016-07-31 15:48:50 +02:00
m_pSquadNext = NULL;
2016-06-04 15:24:23 +02:00
}
//=========================================================
//
// SquadAdd(), add pAdd to my squad
//
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SquadAdd( CFlockingFlyer *pAdd )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ASSERT( pAdd != NULL );
2016-06-04 15:24:23 +02:00
ASSERT( !pAdd->InSquad() );
ASSERT( this->IsLeader() );
pAdd->m_pSquadNext = m_pSquadNext;
m_pSquadNext = pAdd;
pAdd->m_pSquadLeader = this;
}
2016-06-04 15:24:23 +02:00
//=========================================================
//
// SquadRemove(), remove pRemove from my squad.
// If I am pRemove, promote m_pSquadNext to leader
//
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SquadRemove( CFlockingFlyer *pRemove )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ASSERT( pRemove != NULL );
2016-06-04 15:24:23 +02:00
ASSERT( this->IsLeader() );
ASSERT( pRemove->m_pSquadLeader == this );
2016-07-31 15:48:50 +02:00
if( SquadCount() > 2 )
2016-06-04 15:24:23 +02:00
{
// Removing the leader, promote m_pSquadNext to leader
2016-07-31 15:48:50 +02:00
if( pRemove == this )
2016-06-04 15:24:23 +02:00
{
CFlockingFlyer *pLeader = m_pSquadNext;
2016-07-31 15:48:50 +02:00
if( pLeader )
2016-06-04 15:24:23 +02:00
{
// copy the enemy LKP to the new leader
pLeader->m_vecEnemyLKP = m_vecEnemyLKP;
2016-06-04 15:24:23 +02:00
CFlockingFlyer *pList = pLeader;
2016-07-31 15:48:50 +02:00
while( pList )
2016-06-04 15:24:23 +02:00
{
pList->m_pSquadLeader = pLeader;
pList = pList->m_pSquadNext;
}
}
SquadUnlink();
}
else // removing a node
{
CFlockingFlyer *pList = this;
// Find the node before pRemove
2016-07-31 15:48:50 +02:00
while( pList->m_pSquadNext != pRemove )
2016-06-04 15:24:23 +02:00
{
// 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();
}
2016-06-04 15:24:23 +02:00
//=========================================================
//
// SquadCount(), return the number of members of this squad
// callable from leaders & followers
//
//=========================================================
2016-07-31 15:48:50 +02:00
int CFlockingFlyer::SquadCount( void )
2016-06-04 15:24:23 +02:00
{
CFlockingFlyer *pList = m_pSquadLeader;
int squadCount = 0;
2016-07-31 15:48:50 +02:00
while( pList )
2016-06-04 15:24:23 +02:00
{
squadCount++;
pList = pList->m_pSquadNext;
}
return squadCount;
}
//=========================================================
//
// SquadDisband(), Unlink all squad members
//
//=========================================================
2016-07-31 15:48:50 +02:00
void CFlockingFlyer::SquadDisband( void )
2016-06-04 15:24:23 +02:00
{
CFlockingFlyer *pList = m_pSquadLeader;
CFlockingFlyer *pNext;
2016-07-31 15:48:50 +02:00
while( pList )
2016-06-04 15:24:23 +02:00
{
pNext = pList->m_pSquadNext;
pList->SquadUnlink();
pList = pNext;
}
}