/***
*
* Copyright ( c ) 1996 - 2001 , 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 .
*
* * * */
//=========================================================
// terror
//=========================================================
//=========================================================
// Hit groups!
//=========================================================
/*
1 - Head
2 - Stomach
3 - Gun
*/
# include "extdll.h"
# include "plane.h"
# include "util.h"
# include "cbase.h"
# include "monsters.h"
# include "schedule.h"
# include "animation.h"
# include "squadmonster.h"
# include "weapons.h"
# include "talkmonster.h"
# include "soundent.h"
# include "effects.h"
# include "customentity.h"
# include "hgrunt.h"
int g_fTerrorQuestion ; // true if an idle grunt asked a question. Cleared when someone answers.
extern DLL_GLOBAL int g_iSkillLevel ;
//=========================================================
// monster-specific DEFINE's
//=========================================================
# define TERROR_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
# define TERROR_VOL 0.35 // volume of grunt sounds
# define TERROR_ATTN ATTN_NORM // attenutation of grunt sentences
# define TERROR_LIMP_HEALTH 20
# define TERROR_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot.
# define TERROR_NUM_HEADS 2 // how many grunt heads are there?
# define TERROR_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
# define TERROR_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences
# define TERROR_MP5 ( 1 << 0)
# define TERROR_HANDGRENADE ( 1 << 1)
# define TERROR_GRENADELAUNCHER ( 1 << 2)
# define TERROR_SHOTGUN ( 1 << 3)
# define HEAD_GROUP 1
# define HEAD_TERROR 0
# define HEAD_COMMANDER 1
# define HEAD_SHOTGUN 2
# define HEAD_M203 3
# define GUN_GROUP 2
# define GUN_MP5 0
# define GUN_SHOTGUN 1
# define GUN_NONE 2
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
# define TERROR_AE_RELOAD ( 2 )
# define TERROR_AE_KICK ( 3 )
# define TERROR_AE_BURST1 ( 4 )
# define TERROR_AE_BURST2 ( 5 )
# define TERROR_AE_BURST3 ( 6 )
# define TERROR_AE_GREN_TOSS ( 7 )
# define TERROR_AE_GREN_LAUNCH ( 8 )
# define TERROR_AE_GREN_DROP ( 9 )
# define TERROR_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad.
# define TERROR_AE_DROP_GUN ( 11) // grunt (probably dead) is dropping his mp5.
//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
SCHED_TERROR_SUPPRESS = LAST_COMMON_SCHEDULE + 1 ,
SCHED_TERROR_ESTABLISH_LINE_OF_FIRE , // move to a location to set up an attack against the enemy. (usually when a friendly is in the way).
SCHED_TERROR_COVER_AND_RELOAD ,
SCHED_TERROR_SWEEP ,
SCHED_TERROR_FOUND_ENEMY ,
SCHED_TERROR_REPEL ,
SCHED_TERROR_REPEL_ATTACK ,
SCHED_TERROR_REPEL_LAND ,
SCHED_TERROR_WAIT_FACE_ENEMY ,
SCHED_TERROR_TAKECOVER_FAILED , // special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure.
SCHED_TERROR_ELOF_FAIL ,
} ;
//=========================================================
// monster-specific tasks
//=========================================================
enum
{
TASK_TERROR_FACE_TOSS_DIR = LAST_COMMON_TASK + 1 ,
TASK_TERROR_SPEAK_SENTENCE ,
TASK_TERROR_CHECK_FIRE ,
} ;
//=========================================================
// monster-specific conditions
//=========================================================
# define bits_COND_TERROR_NOFIRE ( bits_COND_SPECIAL1 )
class CTerror : public CHGrunt
{
public :
void Spawn ( void ) ;
void Precache ( void ) ;
//int Classify ( void );
void HandleAnimEvent ( MonsterEvent_t * pEvent ) ;
void IdleSound ( void ) ;
void GibMonster ( void ) ;
void SpeakSentence ( void ) ;
Schedule_t * GetSchedule ( void ) ;
Schedule_t * GetScheduleOfType ( int Type ) ;
int TakeDamage ( entvars_t * pevInflictor , entvars_t * pevAttacker , float flDamage , int bitsDamageType ) ;
int IRelationship ( CBaseEntity * pTarget ) ;
static const char * pTerrorSentences [ ] ;
} ;
LINK_ENTITY_TO_CLASS ( monster_human_terror , CTerror ) ;
const char * CTerror : : pTerrorSentences [ ] =
{
" TR_GREN " , // grenade scared grunt
" TR_ALERT " , // sees player
" TR_MONSTER " , // sees monster
" TR_COVER " , // running to cover
" TR_THROW " , // about to throw grenade
" TR_CHARGE " , // running out to get the enemy
" TR_TAUNT " , // say rude things
} ;
typedef enum
{
TERROR_SENT_NONE = - 1 ,
TERROR_SENT_GREN = 0 ,
TERROR_SENT_ALERT ,
TERROR_SENT_MONSTER ,
TERROR_SENT_COVER ,
TERROR_SENT_THROW ,
TERROR_SENT_CHARGE ,
TERROR_SENT_TAUNT ,
} TERROR_SENTENCE_TYPES ;
//=========================================================
// Speak Sentence - say your cued up sentence.
//
// Some grunt sentences (take cover and charge) rely on actually
// being able to execute the intended action. It's really lame
// when a grunt says 'COVER ME' and then doesn't move. The problem
// is that the sentences were played when the decision to TRY
// to move to cover was made. Now the sentence is played after
// we know for sure that there is a valid path. The schedule
// may still fail but in most cases, well after the grunt has
// started moving.
//=========================================================
void CTerror : : SpeakSentence ( void )
{
if ( m_iSentence = = TERROR_SENT_NONE )
{
// no sentence cued up.
return ;
}
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , pTerrorSentences [ m_iSentence ] , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
}
//=========================================================
// IRelationship - overridden because Alien Terrors are
// Human Terror's nemesis.
//=========================================================
int CTerror : : IRelationship ( CBaseEntity * pTarget )
{
if ( FClassnameIs ( pTarget - > pev , " monster_alien_grunt " ) | | ( FClassnameIs ( pTarget - > pev , " monster_gargantua " ) ) )
{
return R_NM ;
}
return CSquadMonster : : IRelationship ( pTarget ) ;
}
//=========================================================
// GibMonster - make gun fly through the air.
//=========================================================
void CTerror : : GibMonster ( void )
{
/* Vector vecGunPos;
Vector vecGunAngles ;
if ( GetBodygroup ( 2 ) ! = 2 )
{ // throw a gun if the grunt has one
GetAttachment ( 0 , vecGunPos , vecGunAngles ) ;
CBaseEntity * pGun ;
if ( FBitSet ( pev - > weapons , TERROR_SHOTGUN ) )
{
pGun = DropItem ( " weapon_barneyshotgun " , vecGunPos , vecGunAngles ) ;
}
else
{
pGun = DropItem ( " weapon_barney9mmar " , vecGunPos , vecGunAngles ) ;
}
if ( pGun )
{
pGun - > pev - > velocity = Vector ( RANDOM_FLOAT ( - 100 , 100 ) , RANDOM_FLOAT ( - 100 , 100 ) , RANDOM_FLOAT ( 200 , 300 ) ) ;
pGun - > pev - > avelocity = Vector ( 0 , RANDOM_FLOAT ( 200 , 400 ) , 0 ) ;
}
if ( FBitSet ( pev - > weapons , TERROR_GRENADELAUNCHER ) )
{
pGun = DropItem ( " ammo_ARgrenades " , vecGunPos , vecGunAngles ) ;
if ( pGun )
{
pGun - > pev - > velocity = Vector ( RANDOM_FLOAT ( - 100 , 100 ) , RANDOM_FLOAT ( - 100 , 100 ) , RANDOM_FLOAT ( 200 , 300 ) ) ;
pGun - > pev - > avelocity = Vector ( 0 , RANDOM_FLOAT ( 200 , 400 ) , 0 ) ;
}
}
}
*/
CBaseMonster : : GibMonster ( ) ;
}
//=========================================================
// TakeDamage - overridden for the grunt because the grunt
// needs to forget that he is in cover if he's hurt. (Obviously
// not in a safe place anymore).
//=========================================================
int CTerror : : TakeDamage ( entvars_t * pevInflictor , entvars_t * pevAttacker , float flDamage , int bitsDamageType )
{
Forget ( bits_MEMORY_INCOVER ) ;
return CSquadMonster : : TakeDamage ( pevInflictor , pevAttacker , flDamage , bitsDamageType ) ;
}
void CTerror : : IdleSound ( void )
{
if ( FOkToSpeak ( ) & & ( g_fTerrorQuestion | | RANDOM_LONG ( 0 , 1 ) ) )
{
if ( ! g_fTerrorQuestion )
{
// ask question or make statement
switch ( RANDOM_LONG ( 0 , 2 ) )
{
case 0 : // check in
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_CHECK " , TERROR_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
g_fTerrorQuestion = 1 ;
break ;
case 1 : // question
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_QUEST " , TERROR_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
g_fTerrorQuestion = 2 ;
break ;
case 2 : // statement
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_IDLE " , TERROR_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
break ;
}
}
else
{
switch ( g_fTerrorQuestion )
{
case 1 : // check in
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_CLEAR " , TERROR_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
break ;
case 2 : // question
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_ANSWER " , TERROR_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
break ;
}
g_fTerrorQuestion = 0 ;
}
JustSpoke ( ) ;
}
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CTerror : : HandleAnimEvent ( MonsterEvent_t * pEvent )
{
Vector vecShootDir ;
Vector vecShootOrigin ;
switch ( pEvent - > event )
{
case TERROR_AE_DROP_GUN :
break ;
case TERROR_AE_RELOAD :
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " hgrunt/gr_reload1.wav " , 1 , ATTN_NORM ) ;
m_cAmmoLoaded = m_cClipSize ;
ClearConditions ( bits_COND_NO_AMMO_LOADED ) ;
break ;
case TERROR_AE_GREN_TOSS :
{
UTIL_MakeVectors ( pev - > angles ) ;
// CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 );
CGrenade : : ShootTimed ( pev , GetGunPosition ( ) , m_vecTossVelocity , 3.5 ) ;
m_fThrowGrenade = FALSE ;
m_flNextGrenadeCheck = gpGlobals - > time + 6 ; // wait six seconds before even looking again to see if a grenade can be thrown.
// !!!LATER - when in a group, only try to throw grenade if ordered.
}
break ;
case TERROR_AE_GREN_LAUNCH :
{
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " weapons/glauncher.wav " , 0.8 , ATTN_NORM ) ;
CGrenade : : ShootContact ( pev , GetGunPosition ( ) , m_vecTossVelocity , gSkillData . plrDmgM203Grenade ) ;
m_fThrowGrenade = FALSE ;
if ( g_iSkillLevel = = SKILL_HARD )
m_flNextGrenadeCheck = gpGlobals - > time + RANDOM_FLOAT ( 2 , 5 ) ; // wait a random amount of time before shooting again
else
m_flNextGrenadeCheck = gpGlobals - > time + 6 ; // wait six seconds before even looking again to see if a grenade can be thrown.
}
break ;
case TERROR_AE_GREN_DROP :
{
UTIL_MakeVectors ( pev - > angles ) ;
CGrenade : : ShootTimed ( pev , pev - > origin + gpGlobals - > v_forward * 17 - gpGlobals - > v_right * 27 + gpGlobals - > v_up * 6 , g_vecZero , 3 ) ;
}
break ;
case TERROR_AE_BURST1 :
{
if ( FBitSet ( pev - > weapons , TERROR_MP5 ) )
{
Shoot ( ) ;
// the first round of the three round burst plays the sound and puts a sound in the world sound list.
if ( RANDOM_LONG ( 0 , 1 ) )
{
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " hgrunt/gr_mgun1.wav " , 1 , ATTN_NORM ) ;
}
else
{
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " hgrunt/gr_mgun2.wav " , 1 , ATTN_NORM ) ;
}
}
else
{
Shotgun ( ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " weapons/sbarrel1.wav " , 1 , ATTN_NORM ) ;
}
CSoundEnt : : InsertSound ( bits_SOUND_COMBAT , pev - > origin , 384 , 0.3 ) ;
}
break ;
case TERROR_AE_BURST2 :
case TERROR_AE_BURST3 :
Shoot ( ) ;
break ;
case TERROR_AE_KICK :
{
CBaseEntity * pHurt = Kick ( ) ;
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors ( pev - > angles ) ;
pHurt - > pev - > punchangle . x = 15 ;
pHurt - > pev - > velocity = pHurt - > pev - > velocity + gpGlobals - > v_forward * 100 + gpGlobals - > v_up * 50 ;
pHurt - > TakeDamage ( pev , pev , gSkillData . hgruntDmgKick , DMG_CLUB ) ;
}
}
break ;
case TERROR_AE_CAUGHT_ENEMY :
{
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_ALERT " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
}
break ;
default :
CSquadMonster : : HandleAnimEvent ( pEvent ) ;
break ;
}
}
//=========================================================
// Spawn
//=========================================================
void CTerror : : Spawn ( )
{
Precache ( ) ;
SET_MODEL ( ENT ( pev ) , " models/terror.mdl " ) ;
UTIL_SetSize ( pev , VEC_HUMAN_HULL_MIN , VEC_HUMAN_HULL_MAX ) ;
pev - > solid = SOLID_SLIDEBOX ;
pev - > movetype = MOVETYPE_STEP ;
m_bloodColor = BLOOD_COLOR_RED ;
pev - > effects = 0 ;
pev - > health = gSkillData . hgruntHealth ;
m_flFieldOfView = 0.2 ; // indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE ;
m_flNextGrenadeCheck = gpGlobals - > time + 1 ;
m_flNextPainTime = gpGlobals - > time ;
m_iSentence = TERROR_SENT_NONE ;
m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP ;
m_fEnemyEluded = FALSE ;
m_fFirstEncounter = TRUE ; // this is true when the grunt spawns, because he hasn't encountered an enemy yet.
m_HackedGunPos = Vector ( 0 , 0 , 55 ) ;
if ( pev - > weapons = = 0 )
{
// initialize to original values
pev - > weapons = TERROR_MP5 | TERROR_HANDGRENADE ;
// pev->weapons = TERROR_SHOTGUN;
// pev->weapons = TERROR_9MMM41A | TERROR_GRENADELAUNCHER;
}
if ( FBitSet ( pev - > weapons , TERROR_SHOTGUN ) )
{
SetBodygroup ( GUN_GROUP , GUN_SHOTGUN ) ;
m_cClipSize = 8 ;
}
else
{
m_cClipSize = 48 ;
}
m_cAmmoLoaded = m_cClipSize ;
if ( FBitSet ( pev - > weapons , TERROR_SHOTGUN ) )
{
SetBodygroup ( HEAD_GROUP , HEAD_SHOTGUN ) ;
}
else if ( FBitSet ( pev - > weapons , TERROR_GRENADELAUNCHER ) )
{
SetBodygroup ( HEAD_GROUP , HEAD_M203 ) ;
}
CTalkMonster : : g_talkWaitTime = 0 ;
MonsterInit ( ) ;
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CTerror : : Precache ( )
{
PRECACHE_MODEL ( " models/terror.mdl " ) ;
PRECACHE_SOUND ( " hgrunt/gr_mgun1.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_mgun2.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_die1.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_die2.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_die3.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_pain1.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_pain2.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_pain3.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_pain4.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_pain5.wav " ) ;
PRECACHE_SOUND ( " hgrunt/gr_reload1.wav " ) ;
PRECACHE_SOUND ( " weapons/glauncher.wav " ) ;
PRECACHE_SOUND ( " weapons/sbarrel1.wav " ) ;
PRECACHE_SOUND ( " zombie/claw_miss2.wav " ) ; // because we use the basemonster SWIPE animation event
// get voice pitch
m_voicePitch = 120 + RANDOM_LONG ( 0 , 2 ) ;
m_iBrassShell = PRECACHE_MODEL ( " models/shell.mdl " ) ; // brass shell
m_iShotgunShell = PRECACHE_MODEL ( " models/shotgunshell.mdl " ) ;
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
extern Schedule_t slIdleStand [ ] ;
extern Schedule_t slGruntFail [ ] ;
extern Schedule_t slGruntCombatFail [ ] ;
extern Schedule_t slGruntVictoryDance [ ] ;
extern Schedule_t slGruntEstablishLineOfFire [ ] ;
extern Schedule_t slGruntFoundEnemy [ ] ;
extern Schedule_t slGruntCombatFace [ ] ;
extern Schedule_t slGruntSignalSuppress [ ] ;
extern Schedule_t slGruntSuppress [ ] ;
extern Schedule_t slGruntWaitInCover [ ] ;
extern Schedule_t slGruntTakeCover [ ] ;
extern Schedule_t slGruntGrenadeCover [ ] ;
extern Schedule_t slGruntTossGrenadeCover [ ] ;
extern Schedule_t slGruntTakeCoverFromBestSound [ ] ;
extern Schedule_t slGruntHideReload [ ] ;
extern Schedule_t slGruntSweep [ ] ;
extern Schedule_t slGruntRangeAttack1A [ ] ;
extern Schedule_t slGruntRangeAttack1B [ ] ;
extern Schedule_t slGruntRangeAttack2 [ ] ;
extern Schedule_t slGruntRepel [ ] ;
extern Schedule_t slGruntRepelAttack [ ] ;
extern Schedule_t slGruntRepelLand [ ] ;
//=========================================================
// Get Schedule!
//=========================================================
Schedule_t * CTerror : : GetSchedule ( void )
{
// clear old sentence
m_iSentence = TERROR_SENT_NONE ;
// flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling.
if ( pev - > movetype = = MOVETYPE_FLY & & m_MonsterState ! = MONSTERSTATE_PRONE )
{
if ( pev - > flags & FL_ONGROUND )
{
// just landed
pev - > movetype = MOVETYPE_STEP ;
return GetScheduleOfType ( SCHED_TERROR_REPEL_LAND ) ;
}
else
{
// repel down a rope,
if ( m_MonsterState = = MONSTERSTATE_COMBAT )
return GetScheduleOfType ( SCHED_TERROR_REPEL_ATTACK ) ;
else
return GetScheduleOfType ( SCHED_TERROR_REPEL ) ;
}
}
// grunts place HIGH priority on running away from danger sounds.
if ( HasConditions ( bits_COND_HEAR_SOUND ) )
{
CSound * pSound ;
pSound = PBestSound ( ) ;
ASSERT ( pSound ! = NULL ) ;
if ( pSound )
{
if ( pSound - > m_iType & bits_SOUND_DANGER )
{
// dangerous sound nearby!
//!!!KELLY - currently, this is the grunt's signal that a grenade has landed nearby,
// and the grunt should find cover from the blast
// good place for "SHIT!" or some other colorful verbal indicator of dismay.
// It's not safe to play a verbal order here "Scatter", etc cause
// this may only affect a single individual in a squad.
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_GREN " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_BEST_SOUND ) ;
}
/*
if ( ! HasConditions ( bits_COND_SEE_ENEMY ) & & ( pSound - > m_iType & ( bits_SOUND_PLAYER | bits_SOUND_COMBAT ) ) )
{
MakeIdealYaw ( pSound - > m_vecOrigin ) ;
}
*/
}
}
switch ( m_MonsterState )
{
case MONSTERSTATE_COMBAT :
{
// dead enemy
if ( HasConditions ( bits_COND_ENEMY_DEAD ) )
{
// call base class, all code to handle dead enemies is centralized there.
return CBaseMonster : : GetSchedule ( ) ;
}
// new enemy
if ( HasConditions ( bits_COND_NEW_ENEMY ) )
{
if ( InSquad ( ) )
{
MySquadLeader ( ) - > m_fEnemyEluded = FALSE ;
if ( ! IsLeader ( ) )
{
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ) ;
}
else
{
//!!!KELLY - the leader of a squad of grunts has just seen the player or a
// monster and has made it the squad's enemy. You
// can check pev->flags for FL_CLIENT to determine whether this is the player
// or a monster. He's going to immediately start
// firing, though. If you'd like, we can make an alternate "first sight"
// schedule where the leader plays a handsign anim
// that gives us enough time to hear a short sentence or spoken command
// before he starts pluggin away.
if ( FOkToSpeak ( ) ) // && RANDOM_LONG(0,1))
{
if ( ( m_hEnemy ! = 0 ) & & m_hEnemy - > IsPlayer ( ) )
// player
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_ALERT " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
else if ( ( m_hEnemy ! = 0 ) & &
( m_hEnemy - > Classify ( ) ! = CLASS_PLAYER_ALLY ) & &
( m_hEnemy - > Classify ( ) ! = CLASS_HUMAN_PASSIVE ) & &
( m_hEnemy - > Classify ( ) ! = CLASS_MACHINE ) )
// monster
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_MONST " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType ( SCHED_TERROR_SUPPRESS ) ;
}
else
{
return GetScheduleOfType ( SCHED_TERROR_ESTABLISH_LINE_OF_FIRE ) ;
}
}
}
}
// no ammo
else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) )
{
//!!!KELLY - this individual just realized he's out of bullet ammo.
// He's going to try to find cover to run to and reload, but rarely, if
// none is available, he'll drop and reload in the open here.
return GetScheduleOfType ( SCHED_TERROR_COVER_AND_RELOAD ) ;
}
// damaged just a little
else if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) )
{
// if hurt:
// 90% chance of taking cover
// 10% chance of flinch.
int iPercent = RANDOM_LONG ( 0 , 99 ) ;
if ( iPercent < = 90 & & m_hEnemy ! = 0 )
{
// only try to take cover if we actually have an enemy!
//!!!KELLY - this grunt was hit and is going to run to cover.
if ( FOkToSpeak ( ) ) // && RANDOM_LONG(0,1))
{
//SENTENCEG_PlayRndSz( ENT(pev), "TR_COVER", TERROR_SENTENCE_VOLUME, TERROR_ATTN, 0, m_voicePitch);
m_iSentence = TERROR_SENT_COVER ;
//JustSpoke();
}
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ) ;
}
else
{
return GetScheduleOfType ( SCHED_SMALL_FLINCH ) ;
}
}
// can kick
else if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) )
{
return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ) ;
}
// can grenade launch
else if ( FBitSet ( pev - > weapons , TERROR_GRENADELAUNCHER ) & & HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) & & OccupySlot ( bits_SLOTS_HGRUNT_GRENADE ) )
{
// shoot a grenade if you can
return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ) ;
}
// can shoot
else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
if ( InSquad ( ) )
{
// if the enemy has eluded the squad and a squad member has just located the enemy
// and the enemy does not see the squad member, issue a call to the squad to waste a
// little time and give the player a chance to turn.
if ( MySquadLeader ( ) - > m_fEnemyEluded & & ! HasConditions ( bits_COND_ENEMY_FACING_ME ) )
{
MySquadLeader ( ) - > m_fEnemyEluded = FALSE ;
return GetScheduleOfType ( SCHED_TERROR_FOUND_ENEMY ) ;
}
}
if ( OccupySlot ( bits_SLOTS_HGRUNT_ENGAGE ) )
{
// try to take an available ENGAGE slot
return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ) ;
}
else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) & & OccupySlot ( bits_SLOTS_HGRUNT_GRENADE ) )
{
// throw a grenade if can and no engage slots are available
return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ) ;
}
else
{
// hide!
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ) ;
}
}
// can't see enemy
else if ( HasConditions ( bits_COND_ENEMY_OCCLUDED ) )
{
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) & & OccupySlot ( bits_SLOTS_HGRUNT_GRENADE ) )
{
//!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_THROW " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ) ;
}
else if ( OccupySlot ( bits_SLOTS_HGRUNT_ENGAGE ) )
{
//!!!KELLY - grunt cannot see the enemy and has just decided to
// charge the enemy's position.
if ( FOkToSpeak ( ) ) // && RANDOM_LONG(0,1))
{
//SENTENCEG_PlayRndSz( ENT(pev), "TR_CHARGE", TERROR_SENTENCE_VOLUME, TERROR_ATTN, 0, m_voicePitch);
m_iSentence = TERROR_SENT_CHARGE ;
//JustSpoke();
}
return GetScheduleOfType ( SCHED_TERROR_ESTABLISH_LINE_OF_FIRE ) ;
}
else
{
//!!!KELLY - grunt is going to stay put for a couple seconds to see if
// the enemy wanders back out into the open, or approaches the
// grunt's covered position. Good place for a taunt, I guess?
if ( FOkToSpeak ( ) & & RANDOM_LONG ( 0 , 1 ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_TAUNT " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
return GetScheduleOfType ( SCHED_STANDOFF ) ;
}
}
if ( HasConditions ( bits_COND_SEE_ENEMY ) & & ! HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType ( SCHED_TERROR_ESTABLISH_LINE_OF_FIRE ) ;
}
}
default :
break ;
}
// no special cases here, call the base class
return CSquadMonster : : GetSchedule ( ) ;
}
//=========================================================
//=========================================================
Schedule_t * CTerror : : GetScheduleOfType ( int Type )
{
switch ( Type )
{
case SCHED_TAKE_COVER_FROM_ENEMY :
{
if ( InSquad ( ) )
{
if ( g_iSkillLevel = = SKILL_HARD & & HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) & & OccupySlot ( bits_SLOTS_HGRUNT_GRENADE ) )
{
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " TR_THROW " , TERROR_SENTENCE_VOLUME , TERROR_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
return slGruntTossGrenadeCover ;
}
else
{
return & slGruntTakeCover [ 0 ] ;
}
}
else
{
if ( RANDOM_LONG ( 0 , 1 ) )
{
return & slGruntTakeCover [ 0 ] ;
}
else
{
return & slGruntGrenadeCover [ 0 ] ;
}
}
}
case SCHED_TAKE_COVER_FROM_BEST_SOUND :
{
return & slGruntTakeCoverFromBestSound [ 0 ] ;
}
case SCHED_TERROR_TAKECOVER_FAILED :
{
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) & & OccupySlot ( bits_SLOTS_HGRUNT_ENGAGE ) )
{
return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ) ;
}
return GetScheduleOfType ( SCHED_FAIL ) ;
}
break ;
case SCHED_TERROR_ELOF_FAIL :
{
// human grunt is unable to move to a position that allows him to attack the enemy.
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ) ;
}
break ;
case SCHED_TERROR_ESTABLISH_LINE_OF_FIRE :
{
return & slGruntEstablishLineOfFire [ 0 ] ;
}
break ;
case SCHED_RANGE_ATTACK1 :
{
// randomly stand or crouch
if ( RANDOM_LONG ( 0 , 9 ) = = 0 )
m_fStanding = RANDOM_LONG ( 0 , 1 ) ;
if ( m_fStanding )
return & slGruntRangeAttack1B [ 0 ] ;
else
return & slGruntRangeAttack1A [ 0 ] ;
}
case SCHED_RANGE_ATTACK2 :
{
return & slGruntRangeAttack2 [ 0 ] ;
}
case SCHED_COMBAT_FACE :
{
return & slGruntCombatFace [ 0 ] ;
}
case SCHED_TERROR_WAIT_FACE_ENEMY :
{
return & slGruntWaitInCover [ 0 ] ;
}
case SCHED_TERROR_SWEEP :
{
return & slGruntSweep [ 0 ] ;
}
case SCHED_TERROR_COVER_AND_RELOAD :
{
return & slGruntHideReload [ 0 ] ;
}
case SCHED_TERROR_FOUND_ENEMY :
{
return & slGruntFoundEnemy [ 0 ] ;
}
case SCHED_VICTORY_DANCE :
{
if ( InSquad ( ) )
{
if ( ! IsLeader ( ) )
{
return & slGruntFail [ 0 ] ;
}
}
return & slGruntVictoryDance [ 0 ] ;
}
case SCHED_TERROR_SUPPRESS :
{
if ( m_hEnemy - > IsPlayer ( ) & & m_fFirstEncounter )
{
m_fFirstEncounter = FALSE ; // after first encounter, leader won't issue handsigns anymore when he has a new enemy
return & slGruntSignalSuppress [ 0 ] ;
}
else
{
return & slGruntSuppress [ 0 ] ;
}
}
case SCHED_FAIL :
{
if ( m_hEnemy ! = 0 )
{
// grunt has an enemy, so pick a different default fail schedule most likely to help recover.
return & slGruntCombatFail [ 0 ] ;
}
return & slGruntFail [ 0 ] ;
}
case SCHED_TERROR_REPEL :
{
if ( pev - > velocity . z > - 128 )
pev - > velocity . z - = 32 ;
return & slGruntRepel [ 0 ] ;
}
case SCHED_TERROR_REPEL_ATTACK :
{
if ( pev - > velocity . z > - 128 )
pev - > velocity . z - = 32 ;
return & slGruntRepelAttack [ 0 ] ;
}
case SCHED_TERROR_REPEL_LAND :
{
return & slGruntRepelLand [ 0 ] ;
}
default :
{
return CSquadMonster : : GetScheduleOfType ( Type ) ;
}
}
}
//=========================================================
// CTerrorRepel - when triggered, spawns a monster_human_grunt
// repelling down a line.
//=========================================================
class CTerrorRepel : public CBaseMonster
{
public :
void Spawn ( void ) ;
void Precache ( void ) ;
void EXPORT RepelUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
int m_iSpriteTexture ; // Don't save, precache
} ;
LINK_ENTITY_TO_CLASS ( monster_terror_repel , CTerrorRepel ) ;
void CTerrorRepel : : Spawn ( void )
{
Precache ( ) ;
pev - > solid = SOLID_NOT ;
SetUse ( & CTerrorRepel : : RepelUse ) ;
}
void CTerrorRepel : : Precache ( void )
{
UTIL_PrecacheOther ( " monster_human_terror " ) ;
m_iSpriteTexture = PRECACHE_MODEL ( " sprites/rope.spr " ) ;
}
void CTerrorRepel : : RepelUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
TraceResult tr ;
UTIL_TraceLine ( pev - > origin , pev - > origin + Vector ( 0 , 0 , - 4096.0 ) , dont_ignore_monsters , ENT ( pev ) , & tr ) ;
CBaseEntity * pEntity = Create ( " monster_human_terror " , pev - > origin , pev - > angles ) ;
CBaseMonster * pTerror = pEntity - > MyMonsterPointer ( ) ;
pTerror - > pev - > movetype = MOVETYPE_FLY ;
pTerror - > pev - > velocity = Vector ( 0 , 0 , RANDOM_FLOAT ( - 196 , - 128 ) ) ;
pTerror - > SetActivity ( ACT_GLIDE ) ;
// UNDONE: position?
pTerror - > m_vecLastPosition = tr . vecEndPos ;
CBeam * pBeam = CBeam : : BeamCreate ( " sprites/rope.spr " , 10 ) ;
pBeam - > PointEntInit ( pev - > origin + Vector ( 0 , 0 , 112 ) , pTerror - > entindex ( ) ) ;
pBeam - > SetFlags ( BEAM_FSOLID ) ;
pBeam - > SetColor ( 255 , 255 , 255 ) ;
pBeam - > SetThink ( & CBeam : : SUB_Remove ) ;
pBeam - > pev - > nextthink = gpGlobals - > time + - 4096.0f * tr . flFraction / pTerror - > pev - > velocity . z + 0.5f ;
UTIL_Remove ( this ) ;
}
//=========================================================
// DEAD TERROR PROP
//=========================================================
class CDeadTerror : public CBaseMonster
{
public :
void Spawn ( void ) ;
int Classify ( void ) { return CLASS_HUMAN_MILITARY ; }
void KeyValue ( KeyValueData * pkvd ) ;
int m_iPose ; // which sequence to display -- temporary, don't need to save
static const char * m_szPoses [ 3 ] ;
} ;
const char * CDeadTerror : : m_szPoses [ ] = { " deadstomach " , " deadside " , " deadsitting " } ;
void CDeadTerror : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " pose " ) )
{
m_iPose = atoi ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseMonster : : KeyValue ( pkvd ) ;
}
LINK_ENTITY_TO_CLASS ( monster_terror_dead , CDeadTerror ) ;
//=========================================================
// ********** DeadTerror SPAWN **********
//=========================================================
void CDeadTerror : : Spawn ( void )
{
PRECACHE_MODEL ( " models/terror.mdl " ) ;
SET_MODEL ( ENT ( pev ) , " models/terror.mdl " ) ;
pev - > effects = 0 ;
pev - > yaw_speed = 8 ;
pev - > sequence = 0 ;
m_bloodColor = BLOOD_COLOR_RED ;
pev - > sequence = LookupSequence ( m_szPoses [ m_iPose ] ) ;
if ( pev - > sequence = = - 1 )
{
ALERT ( at_console , " Dead terror with bad pose \n " ) ;
}
// Corpses have less health
pev - > health = 8 ;
// map old bodies onto new bodies
switch ( pev - > body )
{
case 0 : // Terror with Gun
pev - > body = 0 ;
pev - > skin = 0 ;
SetBodygroup ( HEAD_GROUP , HEAD_TERROR ) ;
SetBodygroup ( GUN_GROUP , GUN_MP5 ) ;
break ;
case 1 : // Commander with Gun
pev - > body = 0 ;
pev - > skin = 0 ;
SetBodygroup ( HEAD_GROUP , HEAD_COMMANDER ) ;
SetBodygroup ( GUN_GROUP , GUN_MP5 ) ;
break ;
case 2 : // Terror no Gun
pev - > body = 0 ;
pev - > skin = 0 ;
SetBodygroup ( HEAD_GROUP , HEAD_TERROR ) ;
SetBodygroup ( GUN_GROUP , GUN_NONE ) ;
break ;
case 3 : // Commander no Gun
pev - > body = 0 ;
pev - > skin = 0 ;
SetBodygroup ( HEAD_GROUP , HEAD_COMMANDER ) ;
SetBodygroup ( GUN_GROUP , GUN_NONE ) ;
break ;
}
MonsterInitDead ( ) ;
}