hlsdk-xash3d/dlls/rpggrunt.cpp

1279 lines
26 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************
* *
* = == 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 );
}
}
}