|
|
/******************************************************** |
|
|
* * |
|
|
* = == rpggrunt.cpp == = * |
|
|
* * |
|
|
* par Julien * |
|
|
* * |
|
|
********************************************************/ |
|
|
|
|
|
//================================ |
|
|
// |
|
|
// rpggrunt : code du soldat rpg |
|
|
// |
|
|
//================================ |
|
|
|
|
|
|
|
|
//================================ |
|
|
// includes |
|
|
// |
|
|
//================================ |
|
|
|
|
|
#include "extdll.h" |
|
|
#include "util.h" |
|
|
#include "cbase.h" |
|
|
#include "monsters.h" |
|
|
#include "schedule.h" |
|
|
#include "animation.h" |
|
|
#include "weapons.h" |
|
|
#include "soundent.h" |
|
|
|
|
|
|
|
|
#ifndef RPG_H |
|
|
#include "rpg.h" //modif de Julien |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
//===================================================== |
|
|
// Bodygroups |
|
|
// |
|
|
//===================================================== |
|
|
|
|
|
#define RPG_GROUP_BODY 0 |
|
|
#define RPG_GROUP_WEAPON 1 |
|
|
#define RPG_GROUP_RLEFT 2 |
|
|
#define RPG_GROUP_RRIGHT 3 |
|
|
#define RPG_GROUP_HEAD 4 |
|
|
|
|
|
#define RPG_SUB_RBACK 0 |
|
|
#define RPG_SUB_RHAND 1 |
|
|
#define RPG_SUB_RHAND_OPEN 2 |
|
|
#define RPG_SUB_RGUN 3 |
|
|
#define RPG_SUB_RNO 4 |
|
|
|
|
|
#define HEAD1 0 |
|
|
#define HEAD2 1 |
|
|
#define HEAD_NO 2 |
|
|
|
|
|
//===================================================== |
|
|
// Monster's anim events |
|
|
// Constantes associ |
|
|
//===================================================== |
|
|
|
|
|
#define RPG_AE_FIRE ( 1 ) |
|
|
#define RPG_AE_RELOAD ( 2 ) |
|
|
#define RPG_AE_RHAND ( 3 ) |
|
|
#define RPG_AE_DROPGUN ( 4 ) |
|
|
#define RPG_AE_STEP ( 5 ) |
|
|
#define RPG_AE_BODYDROP ( 6 ) |
|
|
#define RPG_AE_OPENRPG ( 7 ) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//===================================================== |
|
|
// Schedule types : |
|
|
// Attitudes sp |
|
|
//===================================================== |
|
|
enum |
|
|
{ |
|
|
SCHED_RPG_RANGE_ATTACK1 = LAST_COMMON_SCHEDULE + 1, |
|
|
SCHED_RPG_RELOAD, |
|
|
SCHED_RPG_TAKE_COVER_RELOAD, |
|
|
SCHED_RPG_CHASE_ENEMY_FAILED, |
|
|
}; |
|
|
|
|
|
//========================================================= |
|
|
// Tasks : |
|
|
// t |
|
|
//========================================================= |
|
|
enum |
|
|
{ |
|
|
TASK_RPG_CROUCH = LAST_COMMON_TASK + 1, |
|
|
// TASK_RPG_STAND, |
|
|
TASK_RPG_FIRE, |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define RPG_VOICE_VOLUME 0.8 //volume de la voix ( 0 - 1 ) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//===================================================== |
|
|
// D |
|
|
// CRpggrunt |
|
|
//===================================================== |
|
|
|
|
|
|
|
|
class CRpggrunt : public CBaseMonster |
|
|
{ |
|
|
public: |
|
|
void Spawn( void ); |
|
|
void Precache( void ); |
|
|
int Classify ( void ) { return CLASS_HUMAN_MILITARY; } |
|
|
int IRelationship ( CBaseEntity *pTarget ); |
|
|
|
|
|
void SetYawSpeed( void ); |
|
|
void HandleAnimEvent( MonsterEvent_t *pEvent ); |
|
|
void CheckAmmo ( void ); |
|
|
void Shoot ( void ); |
|
|
|
|
|
void SetActivity ( Activity NewActivity ); |
|
|
void StartTask ( Task_t *pTask ); |
|
|
void RunTask ( Task_t *pTask ); |
|
|
|
|
|
BOOL FOkToSpeak( void ); |
|
|
void JustSpoke( void ); |
|
|
void IdleSound( void ); |
|
|
void DeathSound ( void ); |
|
|
int ISoundMask ( void ); |
|
|
|
|
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); |
|
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); |
|
|
void MakeGib ( int body, entvars_t *pevAttacker ); |
|
|
|
|
|
void GibMonster( void ); |
|
|
|
|
|
Schedule_t *GetSchedule( void ); |
|
|
Schedule_t *GetScheduleOfType ( int Type ); |
|
|
|
|
|
BOOL CheckRangeAttack1 ( float flDot , float flDist ); |
|
|
|
|
|
CUSTOM_SCHEDULES; |
|
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
int Save( CSave &save ); |
|
|
int Restore( CRestore &restore ); |
|
|
|
|
|
|
|
|
BOOL m_bStanding; |
|
|
BOOL m_bAmmoLoaded; |
|
|
float m_talkWaitTime; |
|
|
|
|
|
}; |
|
|
LINK_ENTITY_TO_CLASS( monster_rpg_grunt, CRpggrunt ); |
|
|
|
|
|
TYPEDESCRIPTION CRpggrunt::m_SaveData[] = |
|
|
{ |
|
|
DEFINE_FIELD( CRpggrunt, m_bStanding, FIELD_BOOLEAN ), |
|
|
DEFINE_FIELD( CRpggrunt, m_bAmmoLoaded, FIELD_BOOLEAN ), |
|
|
}; |
|
|
|
|
|
IMPLEMENT_SAVERESTORE( CRpggrunt, CBaseMonster ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//==================================================== |
|
|
// Vitesse de rotation |
|
|
// en degres par seconde |
|
|
//==================================================== |
|
|
|
|
|
void CRpggrunt :: SetYawSpeed ( void ) |
|
|
{ |
|
|
int ys; |
|
|
|
|
|
switch ( m_Activity ) |
|
|
{ |
|
|
case ACT_IDLE: |
|
|
case ACT_RUN: |
|
|
ys = 150; |
|
|
break; |
|
|
case ACT_WALK: |
|
|
case ACT_TURN_LEFT: |
|
|
case ACT_TURN_RIGHT: |
|
|
ys = 180; |
|
|
break; |
|
|
default: |
|
|
ys = 90; |
|
|
break; |
|
|
} |
|
|
pev->yaw_speed = ys; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//==================================================== |
|
|
// Spawn() |
|
|
// |
|
|
//==================================================== |
|
|
|
|
|
void CRpggrunt :: Spawn() |
|
|
{ |
|
|
Precache( ); |
|
|
|
|
|
SET_MODEL(ENT(pev), "models/rpggrunt.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->health = gSkillData.RpggruntHealth; |
|
|
|
|
|
// pev->view_ofs = Vector ( 0, 0, 6 );// position of the eyes relative to monster's origin. |
|
|
m_flFieldOfView = 0.2; |
|
|
m_MonsterState = MONSTERSTATE_NONE; |
|
|
pev->effects = 0; |
|
|
|
|
|
m_bStanding = 1; |
|
|
m_bAmmoLoaded = 1; |
|
|
|
|
|
|
|
|
m_afCapability = bits_CAP_HEAR | |
|
|
bits_CAP_RANGE_ATTACK1 | |
|
|
bits_CAP_AUTO_DOORS | |
|
|
bits_CAP_OPEN_DOORS; |
|
|
|
|
|
SetBodygroup( RPG_GROUP_RLEFT, RPG_SUB_RGUN); |
|
|
|
|
|
if ( RANDOM_LONG(0,1) ) |
|
|
SetBodygroup ( RPG_GROUP_HEAD, HEAD1 ); |
|
|
else |
|
|
SetBodygroup ( RPG_GROUP_HEAD, HEAD2 ); |
|
|
|
|
|
|
|
|
MonsterInit(); |
|
|
|
|
|
m_flDistLook = 4096; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//================================= |
|
|
// Precache () |
|
|
// |
|
|
//================================= |
|
|
|
|
|
void CRpggrunt :: Precache() |
|
|
{ |
|
|
PRECACHE_MODEL("models/rpggrunt.mdl"); |
|
|
PRECACHE_MODEL("models/hg_gibs.mdl"); |
|
|
|
|
|
PRECACHE_SOUND( "hgrunt/gr_die1.wav" ); |
|
|
PRECACHE_SOUND( "hgrunt/gr_die2.wav" ); |
|
|
PRECACHE_SOUND( "hgrunt/gr_die3.wav" ); |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
//==================================== |
|
|
// ISoundMask |
|
|
// |
|
|
//==================================== |
|
|
|
|
|
int CRpggrunt :: ISoundMask ( void ) |
|
|
{ |
|
|
return bits_SOUND_WORLD | |
|
|
bits_SOUND_COMBAT | |
|
|
bits_SOUND_PLAYER | |
|
|
bits_SOUND_DANGER; |
|
|
} |
|
|
|
|
|
|
|
|
//================================= |
|
|
// CheckAmmo () |
|
|
// Controle des munitions |
|
|
//================================= |
|
|
|
|
|
void CRpggrunt :: CheckAmmo ( void ) |
|
|
{ |
|
|
// ALERT ( at_console, "CHECK AMMO : %i\n" , m_bAmmoLoaded ); |
|
|
if ( m_bAmmoLoaded == FALSE ) |
|
|
{ |
|
|
// ALERT ( at_console, "NO AMMO LOADED\n" ); |
|
|
SetConditions(bits_COND_NO_AMMO_LOADED); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//=============================== |
|
|
// FOkToSpeak |
|
|
// |
|
|
//=============================== |
|
|
|
|
|
|
|
|
BOOL CRpggrunt :: FOkToSpeak( void ) |
|
|
{ |
|
|
if (gpGlobals->time <= m_talkWaitTime) |
|
|
return FALSE; |
|
|
|
|
|
return TRUE; |
|
|
} |
|
|
|
|
|
|
|
|
//=================================== |
|
|
// IdleSound |
|
|
// |
|
|
//=================================== |
|
|
|
|
|
void CRpggrunt :: IdleSound( void ) |
|
|
{ |
|
|
if (FOkToSpeak()) |
|
|
{ |
|
|
SENTENCEG_PlayRndSz( ENT(pev), "RPG_IDLE", RPG_VOICE_VOLUME, ATTN_NORM, 0, 100); |
|
|
JustSpoke(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//========================================================= |
|
|
// DeathSound |
|
|
// supprime toute sequence deja en cours |
|
|
//========================================================= |
|
|
void CRpggrunt :: DeathSound ( void ) |
|
|
{ |
|
|
switch ( RANDOM_LONG(0,2) ) |
|
|
{ |
|
|
case 0: |
|
|
EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die1.wav", 1, ATTN_IDLE ); |
|
|
break; |
|
|
case 1: |
|
|
EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die2.wav", 1, ATTN_IDLE ); |
|
|
break; |
|
|
case 2: |
|
|
EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die3.wav", 1, ATTN_IDLE ); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================ |
|
|
// JustSpoke |
|
|
// |
|
|
//================================ |
|
|
|
|
|
void CRpggrunt :: JustSpoke( void ) |
|
|
{ |
|
|
m_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(1.5, 2.0); |
|
|
} |
|
|
|
|
|
|
|
|
//================================ |
|
|
// IRelationship |
|
|
// |
|
|
//================================ |
|
|
|
|
|
int CRpggrunt::IRelationship ( CBaseEntity *pTarget ) |
|
|
{ |
|
|
if ( FClassnameIs( pTarget->pev, "monster_gargantua" ) ) |
|
|
{ |
|
|
return R_NM; |
|
|
} |
|
|
|
|
|
if ( FClassnameIs( pTarget->pev, "vehicle_tank" ) ) |
|
|
{ |
|
|
if ( pTarget->Classify() == CLASS_NONE ) |
|
|
return R_NO; |
|
|
return R_HT; |
|
|
} |
|
|
|
|
|
return CBaseMonster::IRelationship( pTarget ); |
|
|
} |
|
|
|
|
|
|
|
|
//=================================== |
|
|
// CheckRangeAttack1 |
|
|
// tire toujours si l ennemi est visible |
|
|
//=================================== |
|
|
|
|
|
BOOL CRpggrunt :: CheckRangeAttack1 ( float flDot, float flDist ) |
|
|
{ |
|
|
TraceResult tr; |
|
|
UTIL_TraceLine( BodyTarget(pev->origin), m_hEnemy->BodyTarget(pev->origin), ignore_monsters, ignore_glass, ENT(pev), &tr); |
|
|
|
|
|
if ( tr.flFraction == 1.0 ) |
|
|
{ |
|
|
return TRUE; |
|
|
} |
|
|
return FALSE; |
|
|
|
|
|
/* if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) ) |
|
|
{ |
|
|
return TRUE; |
|
|
} |
|
|
return FALSE; |
|
|
*/ |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
// Handle Anim Event : |
|
|
// |
|
|
//================================================ |
|
|
|
|
|
void CRpggrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) |
|
|
{ |
|
|
|
|
|
switch (pEvent->event) |
|
|
{ |
|
|
case RPG_AE_FIRE: |
|
|
{ |
|
|
m_bAmmoLoaded = 0; |
|
|
SetBodygroup( RPG_GROUP_RLEFT, RPG_SUB_RBACK); |
|
|
Shoot(); |
|
|
break; |
|
|
} |
|
|
case RPG_AE_RELOAD : |
|
|
{ |
|
|
// ALERT ( at_console, "RECHARGE\n"); |
|
|
m_bAmmoLoaded = 1; |
|
|
ClearConditions(bits_COND_NO_AMMO_LOADED); |
|
|
SetBodygroup( RPG_GROUP_RLEFT, RPG_SUB_RGUN); |
|
|
break; |
|
|
} |
|
|
case RPG_AE_RHAND : |
|
|
{ |
|
|
SetBodygroup( RPG_GROUP_RLEFT, RPG_SUB_RHAND); |
|
|
break; |
|
|
} |
|
|
case RPG_AE_DROPGUN: |
|
|
{ |
|
|
SetBodygroup( RPG_GROUP_RLEFT, RPG_SUB_RNO); |
|
|
SetBodygroup( RPG_GROUP_RRIGHT, 1); |
|
|
SetBodygroup( RPG_GROUP_WEAPON, 1); |
|
|
|
|
|
Vector vecGunPos; |
|
|
Vector vecGunAngles; |
|
|
|
|
|
GetAttachment( 0, vecGunPos, vecGunAngles ); |
|
|
DropItem( "weapon_rpg", vecGunPos, vecGunAngles ); |
|
|
break; |
|
|
} |
|
|
case RPG_AE_STEP: |
|
|
{ |
|
|
switch ( RANDOM_LONG(0,3) ) |
|
|
{ |
|
|
case 0: EMIT_SOUND ( ENT(pev), CHAN_BODY, "player/pl_step1.wav", 1, ATTN_NORM); break; |
|
|
case 1: EMIT_SOUND ( ENT(pev), CHAN_BODY, "player/pl_step2.wav", 1, ATTN_NORM); break; |
|
|
case 2: EMIT_SOUND ( ENT(pev), CHAN_BODY, "player/pl_step3.wav", 1, ATTN_NORM); break; |
|
|
case 3: EMIT_SOUND ( ENT(pev), CHAN_BODY, "player/pl_step4.wav", 1, ATTN_NORM); break; |
|
|
} |
|
|
break; |
|
|
} |
|
|
case RPG_AE_BODYDROP: |
|
|
{ |
|
|
if ( pev->flags & FL_ONGROUND ) |
|
|
{ |
|
|
if ( RANDOM_LONG( 0, 1 ) == 0 ) |
|
|
{ |
|
|
EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM, 0, 90 ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM, 0, 90 ); |
|
|
} |
|
|
} |
|
|
break; |
|
|
} |
|
|
case RPG_AE_OPENRPG: |
|
|
{ |
|
|
SetBodygroup( RPG_GROUP_RLEFT, RPG_SUB_RHAND_OPEN); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
//=============================================== |
|
|
// Shoot |
|
|
// Tir de la roquette |
|
|
//=============================================== |
|
|
|
|
|
void CRpggrunt :: Shoot ( void ) |
|
|
{ |
|
|
if (m_hEnemy == NULL) |
|
|
{ |
|
|
return; |
|
|
} |
|
|
|
|
|
Vector vecShootOrigin; |
|
|
Vector zeroVector(0,0,0); |
|
|
GetAttachment( 0, vecShootOrigin, zeroVector ); |
|
|
|
|
|
Vector vecShootDir = m_hEnemy->Center() - vecShootOrigin; |
|
|
// UTIL_ParticleEffect ( vecShootOrigin, UTIL_VecToAngles( vecShootDir ), 600, 255 ); // effet super mario hyper flashy tendance, mais dplac |
|
|
|
|
|
Vector VecShootAng = UTIL_VecToAngles( vecShootDir ); |
|
|
VecShootAng.x = - VecShootAng.x; |
|
|
|
|
|
|
|
|
|
|
|
CRpgRocket *pRocket = CRpgRocket::CreateRpgRocket( vecShootOrigin, VecShootAng, NULL/*this*/, NULL ); |
|
|
pRocket->pev->classname = MAKE_STRING("rpggrunt_rocket"); |
|
|
pRocket->m_pTargetMonster = m_hEnemy; |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
// TraceAttack |
|
|
// Ricochets sur le casque et les roquettes |
|
|
//================================================ |
|
|
|
|
|
void CRpggrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) |
|
|
{ |
|
|
if ( FClassnameIs( ENT( pevAttacker ), "rpggrunt_rocket" ) ) |
|
|
{ |
|
|
return; //pour eviter que le soldat ne meure en tirant sur un ennemi proche |
|
|
} |
|
|
|
|
|
// casque |
|
|
if (ptr->iHitgroup == 11) |
|
|
{ |
|
|
flDamage -= 20; |
|
|
if (flDamage <= 0) |
|
|
{ |
|
|
UTIL_Ricochet( ptr->vecEndPos, 1.0 ); |
|
|
flDamage = 0.01; |
|
|
} |
|
|
} |
|
|
|
|
|
if ( ( pev->health - ( flDamage ) <= 0 ) && IsAlive() && m_iHasGibbed == 0 && ptr->iHitgroup == HITGROUP_HEAD ) |
|
|
{ |
|
|
SetBodygroup( RPG_GROUP_HEAD, HEAD_NO ); |
|
|
MakeGib ( 1, pevAttacker ); |
|
|
} |
|
|
|
|
|
|
|
|
CBaseMonster :: TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); |
|
|
} |
|
|
|
|
|
|
|
|
void CRpggrunt :: MakeGib ( int body, entvars_t *pevAttacker ) |
|
|
{ |
|
|
|
|
|
if ( m_iHasGibbed == 1 ) |
|
|
return; |
|
|
m_iHasGibbed = 1; |
|
|
|
|
|
CGib *pGib = GetClassPtr( (CGib *)NULL ); |
|
|
pGib->Spawn( "models/hg_gibs.mdl" ); |
|
|
pGib->m_bloodColor = BLOOD_COLOR_RED; |
|
|
pGib->pev->body = body; |
|
|
|
|
|
pGib->pev->origin = pev->origin + Vector ( 0, 0, 40 ); |
|
|
pGib->pev->velocity = ( Center() - pevAttacker->origin).Normalize() * 300; |
|
|
|
|
|
pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); |
|
|
pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); |
|
|
|
|
|
} |
|
|
|
|
|
//================================================ |
|
|
// TakeDamage |
|
|
// |
|
|
//================================================ |
|
|
|
|
|
|
|
|
int CRpggrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) |
|
|
{ |
|
|
|
|
|
/* if ( ENT(pevAttacker) == edict() ) |
|
|
{ |
|
|
flDamage = 10; //pour eviter que le soldat ne meure en tirant sur un ennemi proche |
|
|
} |
|
|
*/ |
|
|
return CBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//========================================================= |
|
|
// GibMonster - overriden |
|
|
// Pour la projection du lance-roquettes quand le rpggrunt |
|
|
// meurt par DMG_BLAST ou DMG_CRUSH |
|
|
// |
|
|
//========================================================= |
|
|
|
|
|
|
|
|
void CRpggrunt :: GibMonster ( void ) |
|
|
{ |
|
|
if ( GetBodygroup( 1 ) != 0 ) |
|
|
return; |
|
|
|
|
|
Vector vecGunPos = GetGunPosition (); |
|
|
Vector vecGunAngles; |
|
|
|
|
|
Vector zeroVector(0,0,0); |
|
|
GetAttachment( 0, zeroVector, vecGunAngles ); |
|
|
DropItem( "weapon_rpg", vecGunPos, vecGunAngles ); |
|
|
|
|
|
CBaseMonster :: GibMonster(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//=============================================== |
|
|
// Set activity |
|
|
// |
|
|
// determine l animation a effectuer en fonction |
|
|
// de l activite demandee - sert notamment pour |
|
|
// les anims pouvant etre jouees accroupi |
|
|
// ou debout |
|
|
//=============================================== |
|
|
|
|
|
void CRpggrunt :: SetActivity ( Activity NewActivity ) |
|
|
{ |
|
|
int iSequence = ACTIVITY_NOT_AVAILABLE; |
|
|
void *pmodel = GET_MODEL_PTR( ENT(pev) ); |
|
|
|
|
|
switch ( NewActivity) |
|
|
{ |
|
|
case ACT_IDLE: |
|
|
{ |
|
|
if ( m_bStanding == 0 ) |
|
|
{ |
|
|
NewActivity = ACT_CROUCHIDLE; |
|
|
} |
|
|
iSequence = LookupActivity ( NewActivity ); |
|
|
break; |
|
|
} |
|
|
case ACT_FLINCH_HEAD: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "flinch_head" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_flinch_head" ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case ACT_FLINCH_LEFTARM: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "flinch_arm_left" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_flinch_arm_left" ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case ACT_FLINCH_RIGHTARM: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "flinch_arm_right" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_flinch_arm_right" ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case ACT_FLINCH_LEFTLEG: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "flinch_leg_left" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_flinch_leg_left" ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case ACT_FLINCH_RIGHTLEG: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "flinch_leg_right" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_flinch_leg_right" ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case ACT_TURN_LEFT: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "180L" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_180L" ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case ACT_TURN_RIGHT: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
iSequence = LookupSequence( "180R" ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
iSequence = LookupSequence( "crouch_180R" ); |
|
|
} |
|
|
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) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//========================================================= |
|
|
// Start task |
|
|
// s execute avant le lancement de chaque tache |
|
|
//========================================================= |
|
|
|
|
|
|
|
|
void CRpggrunt :: StartTask ( Task_t *pTask ) |
|
|
{ |
|
|
m_iTaskStatus = TASKSTATUS_RUNNING; |
|
|
|
|
|
switch ( pTask->iTask ) |
|
|
{ |
|
|
case TASK_RPG_CROUCH: |
|
|
{ |
|
|
if ( m_bStanding == 1 ) |
|
|
{ |
|
|
m_bStanding = 0; |
|
|
m_IdealActivity = ACT_CROUCH; |
|
|
break; |
|
|
} |
|
|
else |
|
|
{ |
|
|
TaskComplete(); |
|
|
} |
|
|
break; |
|
|
} |
|
|
|
|
|
/* case TASK_RPG_STAND: |
|
|
{ |
|
|
if ( m_bStanding == 0 ) |
|
|
{ |
|
|
m_bStanding = 1; |
|
|
} |
|
|
|
|
|
TaskComplete(); |
|
|
break; |
|
|
}*/ |
|
|
|
|
|
case TASK_RPG_FIRE: |
|
|
{ |
|
|
m_IdealActivity = ACT_RANGE_ATTACK1; |
|
|
break; |
|
|
} |
|
|
|
|
|
case TASK_RUN_PATH: |
|
|
case TASK_WALK_PATH: |
|
|
{ |
|
|
m_bStanding = 1; |
|
|
CBaseMonster :: StartTask( pTask ); |
|
|
break; |
|
|
} |
|
|
|
|
|
default: |
|
|
CBaseMonster :: StartTask( pTask ); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
//========================================================= |
|
|
// RunTask |
|
|
//========================================================= |
|
|
void CRpggrunt :: RunTask ( Task_t *pTask ) |
|
|
{ |
|
|
switch ( pTask->iTask ) |
|
|
{ |
|
|
case TASK_RPG_CROUCH: |
|
|
{ |
|
|
if ( m_fSequenceFinished ) |
|
|
{ |
|
|
TaskComplete(); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case TASK_RPG_FIRE: |
|
|
{ |
|
|
if (m_hEnemy != NULL) |
|
|
{ |
|
|
Vector vecShootDir = m_hEnemy->Center() - Center(); |
|
|
Vector angDir = UTIL_VecToAngles( vecShootDir ); |
|
|
SetBlending( 0, angDir.x ); |
|
|
} |
|
|
|
|
|
MakeIdealYaw ( m_vecEnemyLKP ); |
|
|
ChangeYaw ( pev->yaw_speed ); |
|
|
|
|
|
if ( m_fSequenceFinished ) |
|
|
{ |
|
|
// m_Activity = ACT_IDLE; |
|
|
TaskComplete(); |
|
|
} |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
{ |
|
|
CBaseMonster :: RunTask( pTask ); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
//================================================ |
|
|
// |
|
|
// Intelligence artificielle |
|
|
// |
|
|
//================================================ |
|
|
//================================================ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
// Tableaux des taches |
|
|
// |
|
|
//================================================ |
|
|
|
|
|
|
|
|
//========================================================= |
|
|
// RangeAttack : tir |
|
|
|
|
|
Task_t tlRpgRangeAttack1[] = |
|
|
{ |
|
|
{ TASK_STOP_MOVING, (float)0 }, |
|
|
{ TASK_RPG_CROUCH, (float)0 }, |
|
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
|
{ TASK_RPG_FIRE, (float)0 }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgRangeAttack1[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgRangeAttack1, |
|
|
ARRAYSIZE ( tlRpgRangeAttack1 ), |
|
|
0, |
|
|
0, |
|
|
"RpgRangeAttack1" |
|
|
}, |
|
|
}; |
|
|
|
|
|
|
|
|
//========================================================= |
|
|
// Reload : rechargement |
|
|
|
|
|
Task_t tlRpgReload[] = |
|
|
{ |
|
|
{ TASK_STOP_MOVING, (float)0 }, |
|
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
|
{ TASK_RPG_CROUCH, (float)0 }, |
|
|
{ TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, |
|
|
|
|
|
}; |
|
|
|
|
|
Schedule_t slRpgReload[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgReload, |
|
|
ARRAYSIZE ( tlRpgReload ), |
|
|
0, |
|
|
0, |
|
|
|
|
|
"RpgReload" |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
//======================================================== |
|
|
// Cover from best sound : s eloigne d un son de danger |
|
|
|
|
|
Task_t tlRpgTakeCoverFromBestSound[] = |
|
|
{ |
|
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ORIGIN }, |
|
|
{ TASK_STOP_MOVING, (float)0 }, |
|
|
{ TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, |
|
|
{ TASK_STORE_LASTPOSITION, (float)0 }, |
|
|
{ TASK_RUN_PATH, (float)0 }, |
|
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 }, |
|
|
{ TASK_TURN_LEFT, (float)179 }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgTakeCoverFromBestSound[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgTakeCoverFromBestSound, |
|
|
ARRAYSIZE ( tlRpgTakeCoverFromBestSound ), |
|
|
bits_COND_NEW_ENEMY | |
|
|
bits_COND_CAN_RANGE_ATTACK1 | |
|
|
bits_COND_HEAVY_DAMAGE , |
|
|
|
|
|
0, |
|
|
"RpgTakeCoverFromBestSound" |
|
|
}, |
|
|
}; |
|
|
|
|
|
//======================================================== |
|
|
// Take Cover Reload : se met a couvert de l ennemi pour recharger |
|
|
|
|
|
Task_t tlRpgTakeCoverReload[] = |
|
|
{ |
|
|
{ TASK_STOP_MOVING, (float)0 }, |
|
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_RPG_RELOAD }, |
|
|
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 }, |
|
|
{ TASK_STORE_LASTPOSITION, (float)0 }, |
|
|
{ TASK_RUN_PATH, (float)0 }, |
|
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 }, |
|
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
|
{ TASK_SET_SCHEDULE, (float)SCHED_RPG_RELOAD }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgTakeCoverReload[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgTakeCoverReload, |
|
|
ARRAYSIZE ( tlRpgTakeCoverReload ), |
|
|
bits_COND_HEAVY_DAMAGE | |
|
|
bits_COND_HEAR_SOUND, |
|
|
|
|
|
bits_SOUND_DANGER, |
|
|
"RpgTakeCoverReload" |
|
|
}, |
|
|
}; |
|
|
|
|
|
//======================================================== |
|
|
// Take Cover : se met a couvert de l ennemi |
|
|
|
|
|
Task_t tlRpgTakeCoverFromEnnemy[] = |
|
|
{ |
|
|
{ TASK_STOP_MOVING, (float)0 }, |
|
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_COMBAT_FACE }, |
|
|
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 }, |
|
|
{ TASK_STORE_LASTPOSITION, (float)0 }, |
|
|
{ TASK_RUN_PATH, (float)0 }, |
|
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 }, |
|
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgTakeCoverFromEnnemy[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgTakeCoverFromEnnemy, |
|
|
ARRAYSIZE ( tlRpgTakeCoverFromEnnemy ), |
|
|
bits_COND_HEAVY_DAMAGE | |
|
|
bits_COND_HEAR_SOUND, |
|
|
|
|
|
bits_SOUND_DANGER, |
|
|
"RpgTakeCoverFromEnnemy" |
|
|
}, |
|
|
}; |
|
|
|
|
|
//========================================================= |
|
|
// tlRpgTakeCoverFromOrigin |
|
|
|
|
|
Task_t tlRpgTakeCoverFromOrigin[] = |
|
|
{ |
|
|
{ TASK_STOP_MOVING, (float)0 }, |
|
|
// { TASK_SET_FAIL_SCHEDULE, (float)SCHED_COMBAT_FACE }, |
|
|
{ TASK_FIND_COVER_FROM_ORIGIN, (float)0 }, |
|
|
{ TASK_STORE_LASTPOSITION, (float)0 }, |
|
|
{ TASK_RUN_PATH, (float)0 }, |
|
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 }, |
|
|
{ TASK_TURN_LEFT, (float)179 }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgTakeCoverFromOrigin[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgTakeCoverFromOrigin, |
|
|
ARRAYSIZE ( tlRpgTakeCoverFromOrigin ), |
|
|
bits_COND_NEW_ENEMY | |
|
|
bits_COND_CAN_RANGE_ATTACK1 , |
|
|
|
|
|
bits_SOUND_DANGER, |
|
|
"RpgTakeCoverFromOrigin" |
|
|
}, |
|
|
}; |
|
|
|
|
|
//========================================= |
|
|
// Chase enemy schedule |
|
|
|
|
|
Task_t tlRpgChaseEnemy[] = |
|
|
{ |
|
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_RPG_CHASE_ENEMY_FAILED}, |
|
|
{ TASK_GET_PATH_TO_ENEMY, (float)0 }, |
|
|
{ TASK_RUN_PATH, (float)0 }, |
|
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgChaseEnemy[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgChaseEnemy, |
|
|
ARRAYSIZE ( tlRpgChaseEnemy ), |
|
|
bits_COND_NEW_ENEMY | |
|
|
bits_COND_LIGHT_DAMAGE | |
|
|
bits_COND_CAN_RANGE_ATTACK1 | |
|
|
bits_COND_HEAR_SOUND, |
|
|
|
|
|
bits_SOUND_DANGER, |
|
|
"RpgChaseEnemy" |
|
|
}, |
|
|
}; |
|
|
|
|
|
|
|
|
//========================================= |
|
|
// Chase failed |
|
|
|
|
|
Task_t tlRpgChaseFailed[] = |
|
|
{ |
|
|
{ TASK_SET_FAIL_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ORIGIN }, |
|
|
{ TASK_GET_PATH_TO_LASTPOSITION, (float)0 }, |
|
|
{ TASK_RUN_PATH, (float)0 }, |
|
|
{ TASK_WAIT_FOR_MOVEMENT, (float)0 }, |
|
|
{ TASK_CLEAR_LASTPOSITION, (float)0 }, |
|
|
}; |
|
|
|
|
|
Schedule_t slRpgChaseFailed[] = |
|
|
{ |
|
|
{ |
|
|
tlRpgChaseFailed, |
|
|
ARRAYSIZE ( tlRpgChaseFailed ), |
|
|
bits_COND_NEW_ENEMY | |
|
|
bits_COND_LIGHT_DAMAGE | |
|
|
bits_COND_CAN_RANGE_ATTACK1 | |
|
|
bits_COND_HEAR_SOUND, |
|
|
|
|
|
bits_SOUND_DANGER, |
|
|
"RpgChaseFailed" |
|
|
}, |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
// definition des tableaux de taches |
|
|
// |
|
|
//================================================ |
|
|
|
|
|
|
|
|
|
|
|
DEFINE_CUSTOM_SCHEDULES( CRpggrunt ) |
|
|
{ |
|
|
slRpgRangeAttack1, |
|
|
slRpgReload, |
|
|
slRpgTakeCoverFromBestSound, |
|
|
slRpgTakeCoverReload, |
|
|
slRpgTakeCoverFromEnnemy, |
|
|
slRpgTakeCoverFromOrigin, |
|
|
slRpgChaseEnemy, |
|
|
slRpgChaseFailed, |
|
|
}; |
|
|
|
|
|
IMPLEMENT_CUSTOM_SCHEDULES( CRpggrunt, CBaseMonster ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
// Gestion des comportements |
|
|
// |
|
|
//================================================ |
|
|
|
|
|
|
|
|
|
|
|
Schedule_t *CRpggrunt :: GetSchedule( void ) |
|
|
{ |
|
|
if ( HasConditions(bits_COND_HEAR_SOUND) ) |
|
|
{ |
|
|
CSound *pSound = PBestSound(); |
|
|
|
|
|
ASSERT( pSound != NULL ); |
|
|
if ( pSound) |
|
|
{ |
|
|
if (pSound->m_iType & bits_SOUND_DANGER) |
|
|
{ |
|
|
if (FOkToSpeak()) |
|
|
{ |
|
|
SENTENCEG_PlayRndSz( ENT(pev), "RPG_GREN", RPG_VOICE_VOLUME, ATTN_NORM, 0, 100); |
|
|
JustSpoke(); |
|
|
} |
|
|
|
|
|
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
switch ( m_MonsterState ) |
|
|
{ |
|
|
case MONSTERSTATE_COMBAT: |
|
|
{ |
|
|
if ( HasConditions(bits_COND_NEW_ENEMY) ) |
|
|
{ |
|
|
if (FOkToSpeak()) |
|
|
{ |
|
|
SENTENCEG_PlayRndSz( ENT(pev), "RPG_ALERT", RPG_VOICE_VOLUME, ATTN_NORM, 0, 100); |
|
|
JustSpoke(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ( HasConditions( bits_COND_LIGHT_DAMAGE ) ) |
|
|
{ |
|
|
int iPercent = RANDOM_LONG(0,99); |
|
|
|
|
|
if ( iPercent <= 75 && m_hEnemy != NULL ) |
|
|
{ |
|
|
ClearConditions( bits_COND_LIGHT_DAMAGE ); |
|
|
|
|
|
if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) && !HasConditions ( bits_COND_NO_AMMO_LOADED ) ) |
|
|
return GetScheduleOfType( SCHED_RPG_RANGE_ATTACK1 ); |
|
|
|
|
|
else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) |
|
|
return GetScheduleOfType( SCHED_RPG_RELOAD ); |
|
|
|
|
|
else |
|
|
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
return GetScheduleOfType( SCHED_SMALL_FLINCH ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) |
|
|
{ |
|
|
return GetScheduleOfType( SCHED_RPG_TAKE_COVER_RELOAD ); |
|
|
} |
|
|
|
|
|
else if ( HasConditions(bits_COND_CAN_RANGE_ATTACK1) && !HasConditions ( bits_COND_NO_AMMO_LOADED ) ) |
|
|
{ |
|
|
if (FOkToSpeak()) |
|
|
{ |
|
|
SENTENCEG_PlayRndSz( ENT(pev), "RPG_CHARGE", RPG_VOICE_VOLUME, ATTN_NORM, 0, 100); |
|
|
JustSpoke(); |
|
|
} |
|
|
|
|
|
return GetScheduleOfType( SCHED_RPG_RANGE_ATTACK1 ); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
return CBaseMonster :: GetSchedule(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================ |
|
|
// Gestion des comportements |
|
|
// |
|
|
//================================================ |
|
|
|
|
|
|
|
|
|
|
|
Schedule_t* CRpggrunt :: GetScheduleOfType ( int Type ) |
|
|
{ |
|
|
switch ( Type ) |
|
|
{ |
|
|
|
|
|
case SCHED_RPG_RELOAD: |
|
|
{ |
|
|
return &slRpgReload[ 0 ]; |
|
|
} |
|
|
|
|
|
case SCHED_TAKE_COVER_FROM_BEST_SOUND: |
|
|
{ |
|
|
return &slRpgTakeCoverFromBestSound[ 0 ]; |
|
|
} |
|
|
|
|
|
case SCHED_RPG_TAKE_COVER_RELOAD: |
|
|
{ |
|
|
return &slRpgTakeCoverReload[ 0 ]; |
|
|
} |
|
|
|
|
|
case SCHED_TAKE_COVER_FROM_ENEMY: |
|
|
{ |
|
|
if (FOkToSpeak()) |
|
|
{ |
|
|
SENTENCEG_PlayRndSz( ENT(pev), "RPG_COVER", RPG_VOICE_VOLUME, ATTN_NORM, 0, 100); |
|
|
JustSpoke(); |
|
|
} |
|
|
return &slRpgTakeCoverFromEnnemy[ 0 ]; |
|
|
} |
|
|
case SCHED_TAKE_COVER_FROM_ORIGIN: |
|
|
{ |
|
|
return &slRpgTakeCoverFromOrigin[ 0 ]; |
|
|
} |
|
|
|
|
|
case SCHED_RPG_RANGE_ATTACK1: |
|
|
{ |
|
|
return &slRpgRangeAttack1[ 0 ]; |
|
|
} |
|
|
case SCHED_CHASE_ENEMY: |
|
|
{ |
|
|
return &slRpgChaseEnemy[ 0 ]; |
|
|
} |
|
|
case SCHED_RPG_CHASE_ENEMY_FAILED: |
|
|
{ |
|
|
return &slRpgChaseFailed[ 0 ]; |
|
|
} |
|
|
default: |
|
|
{ |
|
|
return CBaseMonster :: GetScheduleOfType ( Type ); |
|
|
} |
|
|
} |
|
|
}
|
|
|
|