mirror of
https://github.com/YGGverse/hlsdk-portable.git
synced 2025-03-13 05:51:19 +00:00
1279 lines
26 KiB
C++
1279 lines
26 KiB
C++
/********************************************************
|
||
* *
|
||
* = == 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 == 0)
|
||
{
|
||
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 != 0)
|
||
{
|
||
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 != 0 )
|
||
{
|
||
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 );
|
||
}
|
||
}
|
||
}
|