forked from FWGS/Paranoia2
302 lines
7.8 KiB
C++
302 lines
7.8 KiB
C++
// Wargon: AI äëÿ êðûñ. Îñíîâàí íà îðèãèíàëüíîì êîäå monster_cockroach.
|
|
// Òàðàêàíû è êðûñû ïåðåæèâóò è íàñ, è íàøèõ ïîòîìêîâ, è òåáÿ òîæå. )
|
|
|
|
#include "extdll.h"
|
|
#include "util.h"
|
|
#include "cbase.h"
|
|
#include "monsters.h"
|
|
#include "schedule.h"
|
|
#include "soundent.h"
|
|
|
|
#define RAT_IDLE 0
|
|
#define RAT_BORED 1
|
|
#define RAT_SCARED_BY_ENT 2
|
|
#define RAT_SCARED_BY_LIGHT 3
|
|
#define RAT_SMELL_FOOD 4
|
|
#define RAT_EAT 5
|
|
|
|
class CRat : public CBaseMonster
|
|
{
|
|
public:
|
|
void Spawn ( void );
|
|
void Precache ( void );
|
|
float MaxYawSpeed( void );
|
|
void EXPORT MonsterThink ( void );
|
|
void Move ( float flInterval );
|
|
void PickNewDest ( int iCondition );
|
|
void EXPORT Touch ( CBaseEntity *pOther );
|
|
void Killed ( entvars_t *pevAttacker, int iGib );
|
|
float m_flLastLightLevel;
|
|
float m_flNextSmellTime;
|
|
int Classify ( void ) { return m_iClass?m_iClass:CLASS_INSECT; }
|
|
void Look ( int iDistance );
|
|
int ISoundMask ( void ) { return bits_SOUND_CARCASS | bits_SOUND_MEAT; }
|
|
BOOL m_fLightHacked;
|
|
int m_iMode;
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( monster_cockroach, CRat );
|
|
|
|
void CRat::Touch( CBaseEntity *pOther )
|
|
{
|
|
if ( pOther->pev->velocity == g_vecZero || !pOther->IsPlayer() )
|
|
return;
|
|
Vector vecSpot;
|
|
TraceResult tr;
|
|
vecSpot = pev->origin + Vector ( 0, 0, 8 );
|
|
UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT( pev ), &tr );
|
|
UTIL_TraceCustomDecal( &tr, "brains", RANDOM_FLOAT( 0.0f, 360.0f ));
|
|
TakeDamage( pOther->pev, pOther->pev, pev->health, DMG_CRUSH );
|
|
}
|
|
|
|
float CRat :: MaxYawSpeed( void )
|
|
{
|
|
return 120.0f;
|
|
}
|
|
|
|
void CRat::Spawn()
|
|
{
|
|
Precache();
|
|
if ( pev->model )
|
|
SET_MODEL( ENT( pev ), STRING( pev->model ) );
|
|
else
|
|
SET_MODEL( ENT( pev ), "models/krisa.mdl" );
|
|
UTIL_SetSize( pev, Vector( -4, -4, 0 ), Vector( 4, 4, 2 ) );
|
|
pev->solid = SOLID_SLIDEBOX;
|
|
pev->movetype = MOVETYPE_STEP;
|
|
m_bloodColor = BLOOD_COLOR_RED;
|
|
pev->health = 1;
|
|
m_flFieldOfView = 0.5;
|
|
m_MonsterState = MONSTERSTATE_NONE;
|
|
MonsterInit();
|
|
SetActivity( ACT_IDLE );
|
|
pev->takedamage = DAMAGE_YES;
|
|
m_fLightHacked = FALSE;
|
|
m_flLastLightLevel = -1;
|
|
m_iMode = RAT_IDLE;
|
|
m_flNextSmellTime = gpGlobals->time;
|
|
}
|
|
|
|
void CRat::Precache()
|
|
{
|
|
if ( pev->model )
|
|
PRECACHE_MODEL( (char*)STRING( pev->model ) );
|
|
else
|
|
PRECACHE_MODEL( "models/krisa.mdl" );
|
|
PRECACHE_SOUND( "roach/rch_die.wav" );
|
|
PRECACHE_SOUND( "roach/rch_walk.wav" );
|
|
PRECACHE_SOUND( "roach/rch_smash.wav" );
|
|
}
|
|
|
|
void CRat::Killed( entvars_t *pevAttacker, int iGib )
|
|
{
|
|
pev->solid = SOLID_NOT;
|
|
pev->takedamage = DAMAGE_NO;
|
|
pev->deadflag = DEAD_DEAD;
|
|
if ( RANDOM_LONG( 0, 4 ) == 1 )
|
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "roach/rch_die.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG( 0, 39 ) );
|
|
else
|
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "roach/rch_smash.wav", 0.7, ATTN_NORM, 0, 80 + RANDOM_LONG( 0, 39 ) );
|
|
CSoundEnt::InsertSound( bits_SOUND_WORLD, pev->origin, 128, 1 );
|
|
CBaseEntity *pOwner = CBaseEntity::Instance( pev->owner );
|
|
if ( pOwner )
|
|
pOwner->DeathNotice( pev );
|
|
SetActivity( ACT_DIESIMPLE );
|
|
if ( pev->health < 0 )
|
|
{
|
|
switch ( RANDOM_LONG( 0, 3 ) )
|
|
{
|
|
case 0:
|
|
{
|
|
SetActivity( ACT_DIEFORWARD );
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
SetActivity( ACT_DIEBACKWARD );
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
SetActivity( ACT_DIE_CHESTSHOT );
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
SetActivity( ACT_DIE_GUTSHOT );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CRat::MonsterThink( void )
|
|
{
|
|
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
|
SetNextThink( RANDOM_FLOAT( 1, 1.5 ) );
|
|
else
|
|
SetNextThink( 0.1 );
|
|
float flInterval = StudioFrameAdvance();
|
|
if ( pev->health > 0 )
|
|
{
|
|
if ( !m_fLightHacked )
|
|
{
|
|
SetNextThink( 1 );
|
|
m_fLightHacked = TRUE;
|
|
return;
|
|
}
|
|
else if ( m_flLastLightLevel < 0 )
|
|
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );
|
|
switch ( m_iMode )
|
|
{
|
|
case RAT_IDLE:
|
|
case RAT_EAT:
|
|
{
|
|
if ( RANDOM_LONG( 0, 2 ) == 1 )
|
|
{
|
|
Look( 150 );
|
|
if ( HasConditions( bits_COND_SEE_FEAR ) )
|
|
{
|
|
Eat( 30 + ( RANDOM_LONG( 0, 14 ) ) );
|
|
PickNewDest( RAT_SCARED_BY_ENT );
|
|
SetActivity( ACT_WALK );
|
|
}
|
|
else if ( RANDOM_LONG( 0, 128 ) == 1 )
|
|
{
|
|
PickNewDest( RAT_BORED );
|
|
SetActivity( ACT_WALK );
|
|
if ( m_iMode == RAT_EAT )
|
|
Eat( 30 + ( RANDOM_LONG ( 0, 14 ) ) );
|
|
}
|
|
}
|
|
if ( m_iMode == RAT_IDLE )
|
|
{
|
|
if ( FShouldEat() )
|
|
Listen();
|
|
if ( GETENTITYILLUM( ENT( pev ) ) > m_flLastLightLevel )
|
|
{
|
|
PickNewDest( RAT_SCARED_BY_LIGHT );
|
|
SetActivity( ACT_WALK );
|
|
}
|
|
else if ( HasConditions( bits_COND_SMELL_FOOD ) )
|
|
{
|
|
CSound *pSound;
|
|
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
|
|
if ( pSound && abs( pSound->m_vecOrigin.z - pev->origin.z ) <= 3 )
|
|
{
|
|
PickNewDest( RAT_SMELL_FOOD );
|
|
SetActivity( ACT_WALK );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case RAT_SCARED_BY_LIGHT:
|
|
{
|
|
if ( GETENTITYILLUM( ENT( pev ) ) <= m_flLastLightLevel )
|
|
{
|
|
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );
|
|
SetActivity( ACT_IDLE );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( m_flGroundSpeed != 0 )
|
|
Move( flInterval );
|
|
}
|
|
|
|
void CRat::PickNewDest( int iCondition )
|
|
{
|
|
Vector vecNewDir;
|
|
Vector vecDest;
|
|
float flDist;
|
|
m_iMode = iCondition;
|
|
if ( m_iMode == RAT_SMELL_FOOD )
|
|
{
|
|
CSound *pSound;
|
|
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
|
|
if ( pSound )
|
|
{
|
|
m_Route[ 0 ].vecLocation.x = pSound->m_vecOrigin.x + ( 3 - RANDOM_LONG( 0, 5 ) );
|
|
m_Route[ 0 ].vecLocation.y = pSound->m_vecOrigin.y + ( 3 - RANDOM_LONG( 0, 5 ) );
|
|
m_Route[ 0 ].vecLocation.z = pSound->m_vecOrigin.z;
|
|
m_Route[ 0 ].iType = bits_MF_TO_LOCATION;
|
|
m_movementGoal = RouteClassify( m_Route[ 0 ].iType );
|
|
return;
|
|
}
|
|
}
|
|
do
|
|
{
|
|
vecNewDir.x = RANDOM_FLOAT( -1, 1 );
|
|
vecNewDir.y = RANDOM_FLOAT( -1, 1 );
|
|
flDist = 256 + ( RANDOM_LONG( 0, 255 ) );
|
|
vecDest = pev->origin + vecNewDir * flDist;
|
|
} while ( ( vecDest - pev->origin ).Length2D() < 128 );
|
|
m_Route[ 0 ].vecLocation.x = vecDest.x;
|
|
m_Route[ 0 ].vecLocation.y = vecDest.y;
|
|
m_Route[ 0 ].vecLocation.z = pev->origin.z;
|
|
m_Route[ 0 ].iType = bits_MF_TO_LOCATION;
|
|
m_movementGoal = RouteClassify( m_Route[ 0 ].iType );
|
|
if ( RANDOM_LONG( 0, 8 ) == 1 )
|
|
EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "roach/rch_walk.wav", 1, ATTN_NORM, 0, 80 + RANDOM_LONG( 0, 39 ) );
|
|
}
|
|
|
|
void CRat::Move( float flInterval )
|
|
{
|
|
float flWaypointDist;
|
|
Vector vecApex;
|
|
flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length2D();
|
|
SetIdealYawToTargetAndUpdate( m_Route[ m_iRouteIndex ].vecLocation, AI_KEEP_YAW_SPEED );
|
|
UTIL_MakeVectors( pev->angles );
|
|
if ( !WALK_MOVE( ENT( pev ), pev->ideal_yaw, 4, WALKMOVE_NORMAL ) )
|
|
PickNewDest( m_iMode );
|
|
WALK_MOVE( ENT( pev ), pev->ideal_yaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL );
|
|
if ( flWaypointDist <= m_flGroundSpeed * flInterval )
|
|
{
|
|
SetActivity( ACT_IDLE );
|
|
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );
|
|
if ( m_iMode == RAT_SMELL_FOOD )
|
|
m_iMode = RAT_EAT;
|
|
else
|
|
m_iMode = RAT_IDLE;
|
|
}
|
|
if ( RANDOM_LONG(0, 128) == 1 && m_iMode != RAT_SCARED_BY_LIGHT && m_iMode != RAT_SMELL_FOOD )
|
|
PickNewDest( FALSE );
|
|
}
|
|
|
|
void CRat::Look( int iDistance )
|
|
{
|
|
CBaseEntity *pSightEnt = NULL;
|
|
CBaseEntity *pPreviousEnt;
|
|
int iSighted = 0;
|
|
ClearConditions( bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR );
|
|
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
|
|
return;
|
|
m_pLink = NULL;
|
|
pPreviousEnt = this;
|
|
while ( ( pSightEnt = UTIL_FindEntityInSphere( pSightEnt, pev->origin, iDistance ) ) != NULL )
|
|
{
|
|
if ( pSightEnt->IsPlayer() || FBitSet( pSightEnt->pev->flags, FL_MONSTER ) )
|
|
{
|
|
if ( !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && pSightEnt->pev->health > 0 )
|
|
{
|
|
pPreviousEnt->m_pLink = pSightEnt;
|
|
pSightEnt->m_pLink = NULL;
|
|
pPreviousEnt = pSightEnt;
|
|
switch ( IRelationship( pSightEnt ) )
|
|
{
|
|
case R_FR:
|
|
iSighted |= bits_COND_SEE_FEAR;
|
|
break;
|
|
case R_NO:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetConditions( iSighted );
|
|
}
|