/***
*
* 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 .
*
* * * */
//=========================================================
// shocktrooper
//=========================================================
# 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"
# include "sporegrenade.h"
int g_fStrooperQuestion ; // true if an idle grunt asked a question. Cleared when someone answers.
extern DLL_GLOBAL int g_iSkillLevel ;
extern Schedule_t slGruntTakeCover [ ] ;
extern Schedule_t slGruntGrenadeCover [ ] ;
extern Schedule_t slGruntTossGrenadeCover [ ] ;
//=========================================================
// monster-specific DEFINE's
//=========================================================
# define STROOPER_CLIP_SIZE 10 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
# define STROOPER_VOL 0.35 // volume of grunt sounds
# define STROOPER_ATTN ATTN_NORM // attenutation of grunt sentences
# define STROOPER_LIMP_HEALTH 20
# define STROOPER_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot.
# define STROOPER_NUM_HEADS 2 // how many grunt heads are there?
# define STROOPER_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill
# define STROOPER_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences
# define STROOPER_MUZZLEFLASH "sprites / muzzle_shock.spr"
# define STROOPER_SHOCKRIFLE (1 << 0)
# define STROOPER_HANDGRENADE (1 << 1)
# define GUN_GROUP 1
# define GUN_SHOCKFIFLE 0
# define GUN_NONE 1
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
# define STROOPER_AE_RELOAD ( 2 )
# define STROOPER_AE_KICK ( 3 )
# define STROOPER_AE_BURST1 ( 4 )
# define STROOPER_AE_BURST2 ( 5 )
# define STROOPER_AE_BURST3 ( 6 )
# define STROOPER_AE_GREN_TOSS ( 7 )
# define STROOPER_AE_GREN_LAUNCH ( 8 )
# define STROOPER_AE_GREN_DROP ( 9 )
# define STROOPER_AE_CAUGHT_ENEMY ( 10 ) // shocktrooper established sight with an enemy (player only) that had previously eluded the squad.
# define STROOPER_AE_DROP_GUN ( 11 ) // shocktrooper (probably dead) is dropping his shockrifle.
//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
SCHED_STROOPER_SUPPRESS = LAST_COMMON_SCHEDULE + 1 ,
SCHED_STROOPER_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_STROOPER_COVER_AND_RELOAD ,
SCHED_STROOPER_SWEEP ,
SCHED_STROOPER_FOUND_ENEMY ,
SCHED_STROOPER_REPEL ,
SCHED_STROOPER_REPEL_ATTACK ,
SCHED_STROOPER_REPEL_LAND ,
SCHED_STROOPER_WAIT_FACE_ENEMY ,
SCHED_STROOPER_TAKECOVER_FAILED , // special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure.
SCHED_STROOPER_ELOF_FAIL ,
} ;
//=========================================================
// monster-specific tasks
//=========================================================
enum
{
TASK_STROOPER_FACE_TOSS_DIR = LAST_COMMON_TASK + 1 ,
TASK_STROOPER_SPEAK_SENTENCE ,
TASK_STROOPER_CHECK_FIRE ,
} ;
int iStrooperMuzzleFlash ;
//=========================================================
// shocktrooper
//=========================================================
class CStrooper : public CHGrunt
{
public :
void Spawn ( void ) ;
void MonsterThink ( ) ;
void Precache ( void ) ;
int Classify ( void ) ;
BOOL CheckRangeAttack1 ( float flDot , float flDist ) ;
BOOL CheckRangeAttack2 ( float flDot , float flDist ) ;
void HandleAnimEvent ( MonsterEvent_t * pEvent ) ;
void SetObjectCollisionBox ( void )
{
pev - > absmin = pev - > origin + Vector ( - 24 , - 24 , 0 ) ;
pev - > absmax = pev - > origin + Vector ( 24 , 24 , 72 ) ;
}
void SetActivity ( Activity NewActivity ) ;
void DeathSound ( void ) ;
void PainSound ( void ) ;
void IdleSound ( void ) ;
void GibMonster ( void ) ;
void TraceAttack ( entvars_t * pevAttacker , float flDamage , Vector vecDir , TraceResult * ptr , int bitsDamageType ) ;
virtual int SizeForGrapple ( ) { return GRAPPLE_LARGE ; }
void DropShockRoach ( bool gibbed ) ;
int Save ( CSave & save ) ;
int Restore ( CRestore & restore ) ;
Schedule_t * GetSchedule ( void ) ;
Schedule_t * GetScheduleOfType ( int Type ) ;
void SpeakSentence ( ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
BOOL m_fRightClaw ;
float m_rechargeTime ;
float m_blinkTime ;
float m_eyeChangeTime ;
static const char * pGruntSentences [ ] ;
} ;
LINK_ENTITY_TO_CLASS ( monster_shocktrooper , CStrooper )
TYPEDESCRIPTION CStrooper : : m_SaveData [ ] =
{
DEFINE_FIELD ( CStrooper , m_fRightClaw , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( CStrooper , m_rechargeTime , FIELD_TIME ) ,
DEFINE_FIELD ( CStrooper , m_blinkTime , FIELD_TIME ) ,
DEFINE_FIELD ( CStrooper , m_eyeChangeTime , FIELD_TIME ) ,
} ;
IMPLEMENT_SAVERESTORE ( CStrooper , CHGrunt )
const char * CStrooper : : pGruntSentences [ ] =
{
" ST_GREN " , // grenade scared grunt
" ST_ALERT " , // sees player
" ST_MONST " , // sees monster
" ST_COVER " , // running to cover
" ST_THROW " , // about to throw grenade
" ST_CHARGE " , // running out to get the enemy
" ST_TAUNT " , // say rude things
} ;
typedef enum
{
STROOPER_SENT_NONE = - 1 ,
STROOPER_SENT_GREN = 0 ,
STROOPER_SENT_ALERT ,
STROOPER_SENT_MONSTER ,
STROOPER_SENT_COVER ,
STROOPER_SENT_THROW ,
STROOPER_SENT_CHARGE ,
STROOPER_SENT_TAUNT ,
} STROOPER_SENTENCE_TYPES ;
void CStrooper : : SpeakSentence ( void )
{
if ( m_iSentence = = STROOPER_SENT_NONE )
{
// no sentence cued up.
return ;
}
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , pGruntSentences [ m_iSentence ] , STROOPER_SENTENCE_VOLUME , STROOPER_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
}
# define STROOPER_GIB_COUNT 8
//=========================================================
// GibMonster - make gun fly through the air.
//=========================================================
void CStrooper : : GibMonster ( void )
{
if ( GetBodygroup ( GUN_GROUP ) ! = GUN_NONE )
{
DropShockRoach ( true ) ;
}
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " common/bodysplat.wav " , 1 , ATTN_NORM ) ;
if ( CVAR_GET_FLOAT ( " violence_agibs " ) ! = 0 ) // Should never get here, but someone might call it directly
{
CGib : : SpawnRandomGibs ( pev , 6 , " models/strooper_gibs.mdl " , STROOPER_GIB_COUNT ) ; // Throw alien gibs
}
SetThink ( & CBaseEntity : : SUB_Remove ) ;
pev - > nextthink = gpGlobals - > time ;
}
void CStrooper : : IdleSound ( void )
{
if ( FOkToSpeak ( ) & & ( g_fStrooperQuestion | | RANDOM_LONG ( 0 , 1 ) ) )
{
if ( ! g_fStrooperQuestion )
{
// ask question or make statement
switch ( RANDOM_LONG ( 0 , 2 ) )
{
case 0 : // check in
SENTENCEG_PlayRndSz ( ENT ( pev ) , " ST_CHECK " , STROOPER_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
g_fStrooperQuestion = 1 ;
break ;
case 1 : // question
SENTENCEG_PlayRndSz ( ENT ( pev ) , " ST_QUEST " , STROOPER_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
g_fStrooperQuestion = 2 ;
break ;
case 2 : // statement
SENTENCEG_PlayRndSz ( ENT ( pev ) , " ST_IDLE " , STROOPER_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
break ;
}
}
else
{
switch ( g_fStrooperQuestion )
{
case 1 : // check in
SENTENCEG_PlayRndSz ( ENT ( pev ) , " ST_CLEAR " , STROOPER_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
break ;
case 2 : // question
SENTENCEG_PlayRndSz ( ENT ( pev ) , " ST_ANSWER " , STROOPER_SENTENCE_VOLUME , ATTN_NORM , 0 , m_voicePitch ) ;
break ;
}
g_fStrooperQuestion = 0 ;
}
JustSpoke ( ) ;
}
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CStrooper : : Classify ( void )
{
return CLASS_RACEX_SHOCK ;
}
BOOL CStrooper : : CheckRangeAttack1 ( float flDot , float flDist )
{
return m_cAmmoLoaded > = 1 & & CHGrunt : : CheckRangeAttack1 ( flDot , flDist ) ;
}
BOOL CStrooper : : CheckRangeAttack2 ( float flDot , float flDist )
{
if ( ! FBitSet ( pev - > weapons , STROOPER_HANDGRENADE ) )
{
return FALSE ;
}
return CheckRangeAttack2Impl ( gSkillData . strooperGrenadeSpeed , flDot , flDist ) ;
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CStrooper : : HandleAnimEvent ( MonsterEvent_t * pEvent )
{
switch ( pEvent - > event )
{
case STROOPER_AE_DROP_GUN :
{
if ( GetBodygroup ( GUN_GROUP ) ! = GUN_NONE )
{
DropShockRoach ( false ) ;
}
}
break ;
case STROOPER_AE_RELOAD :
m_cAmmoLoaded = m_cClipSize ;
ClearConditions ( bits_COND_NO_AMMO_LOADED ) ;
break ;
case STROOPER_AE_GREN_TOSS :
{
UTIL_MakeVectors ( pev - > angles ) ;
// CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 );
CSporeGrenade : : ShootTimed ( pev , pev - > origin + Vector ( 0 , 0 , 98 ) , 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 STROOPER_AE_GREN_LAUNCH :
case STROOPER_AE_GREN_DROP :
break ;
case STROOPER_AE_BURST1 :
{
if ( m_hEnemy )
{
Vector vecGunPos ;
Vector vecGunAngles ;
GetAttachment ( 0 , vecGunPos , vecGunAngles ) ;
MESSAGE_BEGIN ( MSG_PVS , SVC_TEMPENTITY , vecGunPos ) ;
WRITE_BYTE ( TE_SPRITE ) ;
WRITE_COORD ( vecGunPos . x ) ; // pos
WRITE_COORD ( vecGunPos . y ) ;
WRITE_COORD ( vecGunPos . z ) ;
WRITE_SHORT ( iStrooperMuzzleFlash ) ; // model
WRITE_BYTE ( 4 ) ; // size * 10
WRITE_BYTE ( 128 ) ; // brightness
MESSAGE_END ( ) ;
UTIL_MakeVectors ( pev - > angles ) ;
Vector vecShootOrigin = vecGunPos + gpGlobals - > v_forward * 32 ;
Vector vecShootDir = ShootAtEnemy ( vecShootOrigin ) ;
vecGunAngles = UTIL_VecToAngles ( vecShootDir ) ;
CBaseEntity * pShock = CBaseEntity : : Create ( " shock_beam " , vecShootOrigin , vecGunAngles , edict ( ) ) ;
vecGunAngles . z + = RANDOM_FLOAT ( - 0.05 , 0 ) ;
pShock - > pev - > velocity = vecShootDir * 2000 ;
pShock - > pev - > nextthink = gpGlobals - > time ;
m_cAmmoLoaded - - ;
SetBlending ( 0 , vecGunAngles . x ) ;
// Play fire sound.
EMIT_SOUND ( ENT ( pev ) , CHAN_WEAPON , " weapons/shock_fire.wav " , 1 , ATTN_NORM ) ;
CSoundEnt : : InsertSound ( bits_SOUND_COMBAT , pev - > origin , 384 , 0.3 ) ;
}
}
break ;
case STROOPER_AE_KICK :
{
EMIT_SOUND_DYN ( ENT ( pev ) , CHAN_WEAPON , " zombie/claw_miss2.wav " , 1.0 , ATTN_NORM , 0 , PITCH_NORM + RANDOM_LONG ( - 5 , 5 ) ) ;
CBaseEntity * pHurt = Kick ( ) ;
if ( pHurt )
{
// SOUND HERE!
UTIL_MakeVectors ( pev - > angles ) ;
pHurt - > pev - > punchangle . x = 15 ;
pHurt - > pev - > punchangle . z = ( m_fRightClaw ) ? - 10 : 10 ;
pHurt - > pev - > velocity = pHurt - > pev - > velocity + gpGlobals - > v_forward * 100 + gpGlobals - > v_up * 50 ;
pHurt - > TakeDamage ( pev , pev , gSkillData . strooperDmgKick , DMG_CLUB ) ;
}
m_fRightClaw = ! m_fRightClaw ;
}
break ;
case STROOPER_AE_CAUGHT_ENEMY :
{
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " ST_ALERT " , STROOPER_SENTENCE_VOLUME , STROOPER_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
}
default :
CHGrunt : : HandleAnimEvent ( pEvent ) ;
break ;
}
}
//=========================================================
// Spawn
//=========================================================
void CStrooper : : Spawn ( )
{
Precache ( ) ;
SET_MODEL ( ENT ( pev ) , " models/strooper.mdl " ) ;
UTIL_SetSize ( pev , Vector ( - 24 , - 24 , 0 ) , Vector ( 24 , 24 , 72 ) ) ;
pev - > solid = SOLID_SLIDEBOX ;
pev - > movetype = MOVETYPE_STEP ;
m_bloodColor = BLOOD_COLOR_GREEN ;
pev - > effects = 0 ;
pev - > health = gSkillData . strooperHealth * 2.5 ;
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 = STROOPER_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 = STROOPER_SHOCKRIFLE | STROOPER_HANDGRENADE ;
}
m_cClipSize = gSkillData . strooperMaxCharge ;
m_cAmmoLoaded = m_cClipSize ;
m_fRightClaw = FALSE ;
CTalkMonster : : g_talkWaitTime = 0 ;
m_rechargeTime = gpGlobals - > time + gSkillData . strooperRchgSpeed ;
m_blinkTime = gpGlobals - > time + RANDOM_FLOAT ( 3.0f , 7.0f ) ;
MonsterInit ( ) ;
}
void CStrooper : : MonsterThink ( )
{
if ( m_cAmmoLoaded < m_cClipSize )
{
if ( m_rechargeTime < gpGlobals - > time )
{
m_cAmmoLoaded + + ;
m_rechargeTime = gpGlobals - > time + gSkillData . strooperRchgSpeed ;
}
}
if ( m_blinkTime < = gpGlobals - > time & & pev - > skin = = 0 ) {
pev - > skin = 1 ;
m_blinkTime = gpGlobals - > time + RANDOM_FLOAT ( 3.0f , 7.0f ) ;
m_eyeChangeTime = gpGlobals - > time + 0.1 ;
}
if ( pev - > skin ! = 0 ) {
if ( m_eyeChangeTime < = gpGlobals - > time ) {
m_eyeChangeTime = gpGlobals - > time + 0.1 ;
pev - > skin + + ;
if ( pev - > skin > 3 ) {
pev - > skin = 0 ;
}
}
}
CHGrunt : : MonsterThink ( ) ;
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CStrooper : : Precache ( )
{
PRECACHE_MODEL ( " models/strooper.mdl " ) ;
PRECACHE_MODEL ( " models/strooper_gibs.mdl " ) ;
iStrooperMuzzleFlash = PRECACHE_MODEL ( STROOPER_MUZZLEFLASH ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_attack.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_die1.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_die2.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_die3.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_die4.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_pain1.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_pain2.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_pain3.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_pain4.wav " ) ;
PRECACHE_SOUND ( " shocktrooper/shock_trooper_pain5.wav " ) ;
PRECACHE_SOUND ( " weapons/shock_fire.wav " ) ;
PRECACHE_SOUND ( " weapons/shock_impact.wav " ) ;
PRECACHE_SOUND ( " zombie/claw_miss2.wav " ) ; // because we use the basemonster SWIPE animation event
UTIL_PrecacheOther ( " shock_beam " ) ;
UTIL_PrecacheOther ( " spore " ) ;
UTIL_PrecacheOther ( " monster_shockroach " ) ;
// get voice pitch
if ( RANDOM_LONG ( 0 , 1 ) )
m_voicePitch = 109 + RANDOM_LONG ( 0 , 7 ) ;
else
m_voicePitch = 100 ;
m_iBrassShell = PRECACHE_MODEL ( " models/shell.mdl " ) ; // brass shell
}
//=========================================================
// PainSound
//=========================================================
void CStrooper : : PainSound ( void )
{
if ( gpGlobals - > time > m_flNextPainTime )
{
#if 0
if ( RANDOM_LONG ( 0 , 99 ) < 5 )
{
// pain sentences are rare
if ( FOkToSpeak ( ) )
{
SENTENCEG_PlayRndSz ( ENT ( pev ) , " HG_PAIN " , HGRUNT_SENTENCE_VOLUME , ATTN_NORM , 0 , PITCH_NORM ) ;
JustSpoke ( ) ;
return ;
}
}
# endif
switch ( RANDOM_LONG ( 0 , 4 ) )
{
case 0 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_pain1.wav " , 1 , ATTN_NORM ) ;
break ;
case 1 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_pain2.wav " , 1 , ATTN_NORM ) ;
break ;
case 2 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_pain3.wav " , 1 , ATTN_NORM ) ;
break ;
case 3 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_pain4.wav " , 1 , ATTN_NORM ) ;
break ;
case 4 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_pain5.wav " , 1 , ATTN_NORM ) ;
break ;
}
m_flNextPainTime = gpGlobals - > time + 1 ;
}
}
//=========================================================
// DeathSound
//=========================================================
void CStrooper : : DeathSound ( void )
{
switch ( RANDOM_LONG ( 0 , 3 ) )
{
case 0 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_die1.wav " , 1 , ATTN_IDLE ) ;
break ;
case 1 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_die2.wav " , 1 , ATTN_IDLE ) ;
break ;
case 2 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_die3.wav " , 1 , ATTN_IDLE ) ;
break ;
case 3 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " shocktrooper/shock_trooper_die4.wav " , 1 , ATTN_IDLE ) ;
break ;
}
}
//=========================================================
// TraceAttack - reimplemented in shock trooper because they never have helmets
//=========================================================
void CStrooper : : TraceAttack ( entvars_t * pevAttacker , float flDamage , Vector vecDir , TraceResult * ptr , int bitsDamageType )
{
CSquadMonster : : TraceAttack ( pevAttacker , flDamage , vecDir , ptr , bitsDamageType ) ;
}
void CStrooper : : DropShockRoach ( bool gibbed )
{
Vector vecGunPos ;
Vector vecGunAngles ;
GetAttachment ( 0 , vecGunPos , vecGunAngles ) ;
SetBodygroup ( GUN_GROUP , GUN_NONE ) ;
Vector vecDropAngles ;
// Remove any pitch.
vecDropAngles . x = 0 ;
vecDropAngles . y = vecGunAngles . y ;
vecDropAngles . z = 0 ;
Vector vecPos = pev - > origin ;
if ( gibbed )
vecPos . z + = 32 ;
else
vecPos . z + = 48 ;
// now spawn a shockroach.
CBaseEntity * roach = CBaseEntity : : Create ( " monster_shockroach " , vecPos , vecDropAngles ) ;
if ( roach )
{
if ( ShouldFadeOnDeath ( ) )
roach - > pev - > spawnflags | = SF_MONSTER_FADECORPSE ;
if ( gibbed )
{
roach - > pev - > velocity = Vector ( RANDOM_FLOAT ( - 100.0f , 100.0f ) , RANDOM_FLOAT ( - 100.0f , 100.0f ) , RANDOM_FLOAT ( 200.0f , 300.0f ) ) ;
roach - > pev - > avelocity = Vector ( 0 , RANDOM_FLOAT ( 200.0f , 300.0f ) , 0 ) ;
}
else
{
roach - > pev - > velocity = Vector ( RANDOM_FLOAT ( - 20.0f , 20.0f ) , RANDOM_FLOAT ( - 20.0f , 20.0f ) , RANDOM_FLOAT ( 20.0f , 30.0f ) ) ;
roach - > pev - > avelocity = Vector ( 0 , RANDOM_FLOAT ( 20.0f , 40.0f ) , 0 ) ;
}
}
}
//=========================================================
// SetActivity
//=========================================================
void CStrooper : : SetActivity ( Activity NewActivity )
{
int iSequence = ACTIVITY_NOT_AVAILABLE ;
void * pmodel = GET_MODEL_PTR ( ENT ( pev ) ) ;
switch ( NewActivity )
{
case ACT_RANGE_ATTACK1 :
// shocktrooper is either shooting standing or shooting crouched
if ( m_fStanding )
{
// get aimable sequence
iSequence = LookupSequence ( " standing_mp5 " ) ;
}
else
{
// get crouching shoot
iSequence = LookupSequence ( " crouching_mp5 " ) ;
}
break ;
case ACT_RANGE_ATTACK2 :
// shocktrooper is going to throw a grenade.
// get toss anim
iSequence = LookupSequence ( " throwgrenade " ) ;
break ;
case ACT_RUN :
if ( pev - > health < = STROOPER_LIMP_HEALTH )
{
// limp!
iSequence = LookupActivity ( ACT_RUN_HURT ) ;
}
else
{
iSequence = LookupActivity ( NewActivity ) ;
}
break ;
case ACT_WALK :
if ( pev - > health < = STROOPER_LIMP_HEALTH )
{
// limp!
iSequence = LookupActivity ( ACT_WALK_HURT ) ;
}
else
{
iSequence = LookupActivity ( NewActivity ) ;
}
break ;
case ACT_IDLE :
if ( m_MonsterState = = MONSTERSTATE_COMBAT )
{
NewActivity = ACT_IDLE_ANGRY ;
}
iSequence = LookupActivity ( NewActivity ) ;
break ;
default :
iSequence = LookupActivity ( NewActivity ) ;
break ;
}
m_Activity = NewActivity ; // Go ahead and set this so it doesn't keep trying when the anim is not present
// Set to the desired anim, or default anim if the desired is not present
if ( iSequence > ACTIVITY_NOT_AVAILABLE )
{
if ( pev - > sequence ! = iSequence | | ! m_fSequenceLoops )
{
pev - > frame = 0 ;
}
pev - > sequence = iSequence ; // Set to the reset anim (if it's there)
ResetSequenceInfo ( ) ;
SetYawSpeed ( ) ;
}
else
{
// Not available try to get default anim
ALERT ( at_console , " %s has no sequence for act:%d \n " , STRING ( pev - > classname ) , NewActivity ) ;
pev - > sequence = 0 ; // Set to the reset anim (if it's there)
}
}
//=========================================================
// Get Schedule!
//=========================================================
Schedule_t * CStrooper : : GetSchedule ( void )
{
// clear old sentence
m_iSentence = STROOPER_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_STROOPER_REPEL_LAND ) ;
}
else
{
// repel down a rope,
if ( m_MonsterState = = MONSTERSTATE_COMBAT )
return GetScheduleOfType ( SCHED_STROOPER_REPEL_ATTACK ) ;
else
return GetScheduleOfType ( SCHED_STROOPER_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 ) , " ST_GREN " , STROOPER_SENTENCE_VOLUME , STROOPER_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 ) , " ST_ALERT " , STROOPER_SENTENCE_VOLUME , STROOPER_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 ) , " ST_MONST " , STROOPER_SENTENCE_VOLUME , STROOPER_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType ( SCHED_STROOPER_SUPPRESS ) ;
}
else
{
return GetScheduleOfType ( SCHED_STROOPER_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_STROOPER_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), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
m_iSentence = STROOPER_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 shoot
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_STROOPER_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 ) , " ST_THROW " , STROOPER_SENTENCE_VOLUME , STROOPER_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), "HG_CHARGE", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch);
m_iSentence = STROOPER_SENT_CHARGE ;
//JustSpoke();
}
return GetScheduleOfType ( SCHED_STROOPER_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 ) , " ST_TAUNT " , STROOPER_SENTENCE_VOLUME , STROOPER_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
return GetScheduleOfType ( SCHED_STANDOFF ) ;
}
}
if ( HasConditions ( bits_COND_SEE_ENEMY ) & & ! HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) )
{
return GetScheduleOfType ( SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE ) ;
}
}
}
// no special cases here, call the base class
return CSquadMonster : : GetSchedule ( ) ;
}
//=========================================================
//=========================================================
Schedule_t * CStrooper : : 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 ) , " ST_THROW " , STROOPER_SENTENCE_VOLUME , STROOPER_ATTN , 0 , m_voicePitch ) ;
JustSpoke ( ) ;
}
return slGruntTossGrenadeCover ;
}
else
{
return & slGruntTakeCover [ 0 ] ;
}
}
else
{
if ( RANDOM_LONG ( 0 , 1 ) )
{
return & slGruntTakeCover [ 0 ] ;
}
else
{
return & slGruntGrenadeCover [ 0 ] ;
}
}
}
break ;
default :
{
return CHGrunt : : GetScheduleOfType ( Type ) ;
}
break ;
}
}