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