You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
714 lines
15 KiB
714 lines
15 KiB
/************************************************************ |
|
* * |
|
* Sniper, par Julien * |
|
* * |
|
************************************************************/ |
|
|
|
|
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "monsters.h" |
|
#include "schedule.h" |
|
#include "weapons.h" |
|
#include "soundent.h" |
|
#include "animation.h" |
|
#include "effects.h" |
|
|
|
|
|
|
|
#define SNIPER_MAX_CLIP 5 |
|
|
|
// d |
|
|
|
#define HEAD_GROUP 1 |
|
#define ARM_L_GROUP 3 |
|
#define ARM_R_GROUP 4 |
|
#define LEG_L_GROUP 5 |
|
#define LEG_R_GROUP 6 |
|
|
|
#define NO_MEMBRE 1 |
|
|
|
#define HEAD1 0 |
|
#define HEAD2 1 |
|
#define HEAD3 2 |
|
#define NO_HEAD 3 |
|
|
|
//===================================================== |
|
// Monster's anim events |
|
// Constantes associ |
|
//===================================================== |
|
|
|
#define SNIPER_AE_RELOAD ( 1 ) |
|
#define SNIPER_AE_MELEE_ATTACK1 ( 2 ) |
|
#define SNIPER_AE_SHOOT ( 3 ) |
|
#define SNIPER_AE_DROPGUN ( 4 ) |
|
|
|
//===================================================== |
|
// Schedule types : |
|
// Attitudes |
|
//===================================================== |
|
enum |
|
{ |
|
SCHED_SNIPER_RECHARGEMENT = LAST_COMMON_SCHEDULE + 1, |
|
SCHED_SNIPER_COVER, |
|
}; |
|
|
|
//===================================================== |
|
//D |
|
//===================================================== |
|
|
|
|
|
class CSniper : public CBaseMonster |
|
{ |
|
public: |
|
void Spawn ( void ); // initialisation |
|
void Precache ( void ); // on pr |
|
void SetYawSpeed ( void ); //vitesse de rotation |
|
int Classify ( void ); // "camp" du monstre : alien ou humain |
|
int IRelationship ( CBaseEntity *pTarget ); |
|
|
|
void Shoot (void); // le tir ! |
|
CBaseEntity *Kick ( void ); // trace un vecteur et voit s'il touche qqn |
|
|
|
BOOL CheckRangeAttack1 ( float flDot, float flDist ); |
|
BOOL CheckMeleeAttack1 ( float flDot, float flDist ); |
|
|
|
void CheckAmmo ( void ); // v |
|
|
|
void HandleAnimEvent( MonsterEvent_t *pEvent ); |
|
void RunTask ( Task_t *pTask ); |
|
|
|
void GibMonster ( void ); |
|
|
|
Schedule_t *GetSchedule( void ); // analyse des bit_COND_ ... |
|
Schedule_t *GetScheduleOfType ( int Type ); // ... et en retourne un comportement |
|
|
|
int m_cAmmoLoaded; // nbre de balles dans le chargeur du sniper |
|
int m_iShell; |
|
|
|
CUSTOM_SCHEDULES; // d |
|
|
|
static TYPEDESCRIPTION m_SaveData[]; |
|
int Save( CSave &save ); |
|
int Restore( CRestore &restore ); |
|
|
|
void MakeGib ( int body, entvars_t *pevAttacker ); |
|
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); |
|
|
|
virtual Activity GetDeathActivity ( void ); |
|
}; |
|
LINK_ENTITY_TO_CLASS( monster_sniper, CSniper ); |
|
|
|
|
|
TYPEDESCRIPTION CSniper::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CSniper, m_cAmmoLoaded, FIELD_INTEGER ), |
|
}; |
|
IMPLEMENT_SAVERESTORE( CSniper, CBaseMonster ); |
|
|
|
|
|
int CSniper :: Classify ( void ) |
|
{ |
|
return CLASS_HUMAN_MILITARY; // copain avec les grunts |
|
} |
|
|
|
//==================== |
|
|
|
void CSniper :: SetYawSpeed ( void ) |
|
{ |
|
/*int ys; |
|
|
|
switch ( m_Activity ) |
|
{ |
|
case ACT_IDLE: |
|
default: |
|
ys = 50; |
|
break; |
|
} |
|
|
|
pev->yaw_speed = ys;*/ |
|
pev->yaw_speed = 360; // rotation en degr |
|
} |
|
|
|
//----------------------------------------- |
|
// |
|
// Spawn / Precache |
|
// |
|
//------------------------------------------ |
|
|
|
void CSniper :: Spawn() |
|
{ |
|
Precache( ); |
|
|
|
SET_MODEL(ENT(pev), "models/sniper.mdl"); // model |
|
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); // taille de la "boite" : constante pour taille standart |
|
|
|
pev->solid = SOLID_SLIDEBOX; // la "boite" est solide |
|
pev->movetype = MOVETYPE_STEP; // il ne vole pas |
|
m_bloodColor = BLOOD_COLOR_RED; // couleur du sang |
|
pev->health = 75; // vie (plus tard avec le skill.cfg) |
|
pev->view_ofs = Vector ( 0, 0, 73 ); // position of the eyes relative to monster's origin. |
|
m_flFieldOfView = VIEW_FIELD_FULL; // c |
|
m_MonsterState = MONSTERSTATE_NONE; // ? |
|
|
|
m_HackedGunPos = Vector( 0, 24, 48 ); // position du gun |
|
m_cAmmoLoaded = SNIPER_MAX_CLIP; // nbre de balles dans le chargeur (provisoire) |
|
ClearConditions(bits_COND_NO_AMMO_LOADED); |
|
|
|
m_afCapability = bits_CAP_HEAR | |
|
bits_CAP_RANGE_ATTACK1 | // capable d'entendre, et deux attaques |
|
bits_CAP_MELEE_ATTACK1 ; |
|
pev->effects = 0; // pour le muzzleflash ? |
|
|
|
MonsterInit(); // ? |
|
|
|
m_flDistTooFar = 4096.0; |
|
m_flDistLook = 4096.0; |
|
|
|
SetBodygroup ( HEAD_GROUP, RANDOM_LONG(0,2) ); // t |
|
|
|
} |
|
|
|
|
|
void CSniper :: Precache() |
|
{ |
|
PRECACHE_MODEL("models/sniper.mdl"); |
|
PRECACHE_MODEL("models/sniper_gibs.mdl"); |
|
|
|
PRECACHE_SOUND("weapons/fsniper_shoot1.wav"); |
|
|
|
m_iShell = PRECACHE_MODEL ("models/sniper_shell.mdl"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------- |
|
// |
|
// Shoot / Kick |
|
// |
|
//------------------------------------------ |
|
|
|
|
|
|
|
void CSniper :: Shoot ( void ) |
|
{ |
|
if (m_hEnemy == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
Vector vecShootOrigin = GetGunPosition(); |
|
Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); |
|
|
|
float m_flDiviation = 0; // perfect shot |
|
UTIL_MakeVectors ( pev->angles ); |
|
|
|
Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); |
|
EjectBrass ( pev->origin + gpGlobals->v_up * 32 + gpGlobals->v_forward * 12, vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL); |
|
|
|
FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_3DEGREES, 4096, 0, 0, 45 ); |
|
|
|
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/fsniper_shoot1.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); |
|
|
|
m_cAmmoLoaded -- ; |
|
} |
|
|
|
|
|
|
|
CBaseEntity *CSniper :: Kick( void ) |
|
{ |
|
TraceResult tr; |
|
|
|
UTIL_MakeVectors( pev->angles ); // == d |
|
Vector vecStart = pev->origin; // le vecteur part de l'origine |
|
vecStart.z += pev->size.z * 0.5; // du monstre et de la moiti |
|
// de la hauteur de la "boite" |
|
Vector vecEnd = vecStart + (gpGlobals->v_forward * 70); // jusqu' |
|
|
|
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); // on trace ce vecteur |
|
|
|
if ( tr.pHit ) // |
|
{ |
|
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); |
|
return pEntity; |
|
} |
|
|
|
return NULL; // si c'est un coup |
|
} |
|
|
|
|
|
//----------------------------------------- |
|
// IRelationship |
|
// |
|
//----------------------------------------- |
|
|
|
int CSniper::IRelationship ( CBaseEntity *pTarget ) |
|
{ |
|
if ( FClassnameIs( pTarget->pev, "monster_gargantua" ) ) |
|
{ |
|
return R_NM; |
|
} |
|
|
|
if ( FClassnameIs( pTarget->pev, "vehicle_tank" ) ) |
|
{ |
|
return R_NO; |
|
} |
|
|
|
return CBaseMonster::IRelationship( pTarget ); |
|
} |
|
|
|
|
|
//----------------------------------------- |
|
// |
|
// CheckAttacks - |
|
//------------------------------------------. |
|
|
|
void CSniper :: GibMonster ( void ) |
|
{ |
|
if ( GetBodygroup( 2 ) != 0 ) |
|
return; |
|
|
|
Vector vecGunPos = GetGunPosition(); |
|
Vector vecGunAngles; |
|
|
|
Vector zeroVector(0,0,0); |
|
GetAttachment( 0, zeroVector, vecGunAngles ); |
|
DropItem( "weapon_fsniper", vecGunPos, vecGunAngles ); |
|
|
|
CBaseMonster :: GibMonster(); |
|
} |
|
|
|
|
|
//----------------------------------------- |
|
// |
|
// D |
|
//------------------------------------------. |
|
|
|
|
|
void CSniper :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) |
|
{ |
|
CBaseMonster :: TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType); |
|
|
|
if ( gMultiDamage.pEntity != this ) |
|
return; |
|
|
|
if ( ( pev->health - ( gMultiDamage.amount ) <= 0 ) && IsAlive() && m_iHasGibbed == 0 ) |
|
{ |
|
if ( ptr->iHitgroup == HITGROUP_CHEST ) |
|
{ |
|
ptr->iHitgroup = RANDOM_LONG ( 1,3 ); |
|
ptr->iHitgroup = ptr->iHitgroup == 2 ? HITGROUP_LEFTARM : ptr->iHitgroup; |
|
ptr->iHitgroup = ptr->iHitgroup == 3 ? HITGROUP_RIGHTARM : ptr->iHitgroup; |
|
} |
|
|
|
switch ( ptr->iHitgroup ) |
|
{ |
|
case HITGROUP_RIGHTARM: |
|
SetBodygroup( ARM_R_GROUP, NO_MEMBRE); |
|
MakeGib ( 2, pevAttacker ); |
|
break; |
|
case HITGROUP_LEFTARM: |
|
SetBodygroup( ARM_L_GROUP, NO_MEMBRE); |
|
MakeGib ( 2, pevAttacker ); |
|
break; |
|
case HITGROUP_RIGHTLEG: |
|
SetBodygroup( LEG_R_GROUP, NO_MEMBRE); |
|
MakeGib ( 1, pevAttacker ); |
|
break; |
|
case HITGROUP_LEFTLEG: |
|
SetBodygroup( LEG_L_GROUP, NO_MEMBRE); |
|
MakeGib ( 1, pevAttacker ); |
|
break; |
|
case HITGROUP_HEAD: |
|
SetBodygroup( HEAD_GROUP, NO_HEAD); |
|
MakeGib ( 0, pevAttacker ); |
|
break; |
|
} |
|
} |
|
|
|
} |
|
|
|
void CSniper :: MakeGib ( int body, entvars_t *pevAttacker ) |
|
{ |
|
|
|
if ( m_iHasGibbed == 1 ) |
|
return; |
|
m_iHasGibbed = 1; |
|
|
|
CGib *pGib = GetClassPtr( (CGib *)NULL ); |
|
pGib->Spawn( "models/sniper_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 ); |
|
|
|
} |
|
|
|
Activity CSniper :: GetDeathActivity ( void ) |
|
{ |
|
|
|
Activity deathActivity = ACT_DIESIMPLE; |
|
|
|
if ( pev->deadflag != DEAD_NO ) |
|
return m_IdealActivity; |
|
|
|
|
|
switch ( m_LastHitGroup ) |
|
{ |
|
case HITGROUP_HEAD: |
|
deathActivity = ACT_DIE_HEADSHOT; |
|
break; |
|
|
|
case HITGROUP_STOMACH: |
|
case HITGROUP_CHEST: |
|
case HITGROUP_LEFTLEG: |
|
case HITGROUP_RIGHTLEG: |
|
deathActivity = ACT_DIE_GUTSHOT; |
|
break; |
|
|
|
case HITGROUP_LEFTARM: |
|
case HITGROUP_RIGHTARM: |
|
deathActivity = ACT_DIE_BACKSHOT ; |
|
break; |
|
} |
|
|
|
if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) |
|
deathActivity = ACT_DIESIMPLE; |
|
|
|
return deathActivity; |
|
} |
|
|
|
//----------------------------------------- |
|
// |
|
// CheckAttacks |
|
//------------------------------------------ |
|
|
|
|
|
BOOL CSniper :: CheckRangeAttack1 ( float flDot, float flDist ) |
|
{ |
|
if ( flDist <= 4096 ) |
|
{ |
|
if ( flDist <= 64 ) |
|
return FALSE; |
|
|
|
TraceResult tr; |
|
Vector vecSrc = GetGunPosition(); |
|
Vector vecTarget = m_hEnemy->BodyTarget(vecSrc); |
|
|
|
UTIL_TraceLine( vecSrc, vecTarget, ignore_monsters, ignore_glass, ENT(pev), &tr); |
|
|
|
if ( tr.flFraction == 1.0 ) |
|
return TRUE; |
|
|
|
else if ( !FNullEnt ( tr.pHit ) && VARS( tr.pHit )->takedamage == DAMAGE_YES ) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
BOOL CSniper :: CheckMeleeAttack1 ( float flDot, float flDist ) |
|
{ |
|
CBaseMonster *pEnemy; |
|
|
|
if ( m_hEnemy != NULL ) |
|
{ |
|
pEnemy = m_hEnemy->MyMonsterPointer(); |
|
|
|
if ( !pEnemy ) |
|
{ |
|
return FALSE; |
|
} |
|
} |
|
|
|
if ( flDist <= 64 ) |
|
return TRUE; |
|
|
|
return FALSE; |
|
} |
|
|
|
|
|
|
|
void CSniper :: CheckAmmo ( void ) |
|
{ |
|
if ( m_cAmmoLoaded <= 0 ) |
|
{ |
|
SetConditions(bits_COND_NO_AMMO_LOADED); |
|
} |
|
} |
|
|
|
//=============== |
|
|
|
|
|
void CSniper :: HandleAnimEvent( MonsterEvent_t *pEvent ) |
|
{ |
|
switch (pEvent->event) |
|
{ |
|
case SNIPER_AE_SHOOT: |
|
|
|
Shoot (); break; |
|
|
|
case SNIPER_AE_RELOAD: |
|
|
|
m_cAmmoLoaded = SNIPER_MAX_CLIP; |
|
ClearConditions(bits_COND_NO_AMMO_LOADED); |
|
break; |
|
|
|
case SNIPER_AE_MELEE_ATTACK1: |
|
{ |
|
CBaseEntity *pHurt = Kick(); |
|
if ( pHurt ) |
|
{ |
|
UTIL_MakeVectors( pev->angles ); |
|
pHurt->pev->punchangle.z = 25; |
|
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * 200 + gpGlobals->v_up * 100; |
|
pHurt->TakeDamage( pev, pev,25, DMG_CLUB ); |
|
} |
|
break; |
|
} |
|
case SNIPER_AE_DROPGUN: |
|
{ |
|
SetBodygroup( 2, 1); |
|
|
|
Vector vecGunPos = GetGunPosition(); |
|
Vector vecGunAngles; |
|
|
|
Vector zeroVector(0,0,0); |
|
GetAttachment( 0, zeroVector, vecGunAngles ); |
|
DropItem( "weapon_fsniper", vecGunPos, vecGunAngles ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void CSniper :: RunTask ( Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_RANGE_ATTACK1: |
|
{ |
|
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 ); |
|
|
|
CBaseMonster :: RunTask( pTask ); |
|
break; |
|
} |
|
|
|
default: |
|
{ |
|
CBaseMonster :: RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------- |
|
// |
|
// AI Schedules |
|
//---------------------------------------------------- |
|
|
|
|
|
//------------------- |
|
// Rechargement |
|
|
|
Task_t tlSniperRechargement[] = |
|
{ |
|
{ TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, |
|
}; |
|
|
|
Schedule_t slSniperRechargement[] = |
|
{ |
|
{ |
|
tlSniperRechargement, |
|
ARRAYSIZE ( tlSniperRechargement ), |
|
bits_COND_HEAVY_DAMAGE | |
|
bits_COND_HEAR_SOUND, |
|
|
|
0, |
|
"SniperRechargement" |
|
} |
|
}; |
|
|
|
//------------------- |
|
// abri des grenades |
|
|
|
Task_t tlSniperCover[] = |
|
{ |
|
{ TASK_PLAY_SEQUENCE, (float)ACT_COWER }, |
|
}; |
|
|
|
Schedule_t slSniperCover[] = |
|
{ |
|
{ |
|
tlSniperCover, |
|
ARRAYSIZE ( tlSniperRechargement ), |
|
bits_COND_HEAVY_DAMAGE, |
|
|
|
0, |
|
"SniperCover" |
|
} |
|
}; |
|
|
|
|
|
//------------------- |
|
// tir |
|
// red |
|
// ne tire pas sans voir l'ennemi |
|
|
|
|
|
|
|
Task_t tlSniperShoot[] = |
|
{ |
|
{ TASK_STOP_MOVING, 0 }, |
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
{ TASK_RANGE_ATTACK1, (float)0 }, |
|
}; |
|
|
|
Schedule_t slSniperShoot[] = |
|
{ |
|
{ |
|
tlSniperShoot, |
|
ARRAYSIZE ( tlSniperShoot ), |
|
bits_COND_NEW_ENEMY | |
|
bits_COND_ENEMY_DEAD | |
|
bits_COND_LIGHT_DAMAGE | |
|
bits_COND_HEAVY_DAMAGE | |
|
bits_COND_NO_AMMO_LOADED | |
|
bits_COND_HEAR_SOUND, |
|
|
|
bits_SOUND_DANGER, |
|
"SniperShoot" |
|
} |
|
}; |
|
|
|
|
|
//------------------- |
|
// tableau des AI |
|
|
|
DEFINE_CUSTOM_SCHEDULES( CSniper ) |
|
{ |
|
slSniperRechargement, |
|
slSniperCover, |
|
slSniperShoot, |
|
}; |
|
|
|
IMPLEMENT_CUSTOM_SCHEDULES( CSniper, CBaseMonster ); |
|
|
|
|
|
|
|
//---------------------------------------------------- |
|
// |
|
// Gestion des comportements |
|
//---------------------------------------------------- |
|
|
|
|
|
Schedule_t *CSniper :: GetSchedule( void ) |
|
{ |
|
switch ( m_MonsterState ) |
|
{ |
|
|
|
if ( HasConditions(bits_COND_HEAR_SOUND) ) |
|
{ |
|
CSound *pSound = PBestSound(); |
|
|
|
ASSERT( pSound != NULL ); |
|
if ( pSound) |
|
{ |
|
if (pSound->m_iType & bits_SOUND_DANGER) |
|
{ |
|
return GetScheduleOfType( SCHED_SNIPER_COVER ); |
|
} |
|
} |
|
} |
|
|
|
|
|
if ( HasConditions( bits_COND_LIGHT_DAMAGE ) ) |
|
{ |
|
if ( RANDOM_LONG(0,99) <= 75 ) |
|
{ |
|
ClearConditions( bits_COND_LIGHT_DAMAGE ); |
|
return GetScheduleOfType( SCHED_SMALL_FLINCH ); |
|
} |
|
} |
|
|
|
case MONSTERSTATE_COMBAT: |
|
{ |
|
if ( HasConditions ( bits_COND_NO_AMMO_LOADED )) |
|
{ |
|
return GetScheduleOfType ( SCHED_SNIPER_RECHARGEMENT ); |
|
} |
|
|
|
if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) |
|
{ |
|
return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); |
|
} |
|
|
|
|
|
UTIL_MakeVectors ( pev->angles ); |
|
Vector2D vec2LOS = ( m_hEnemy->pev->origin - pev->origin ).Make2D(); |
|
vec2LOS = vec2LOS.Normalize(); |
|
float flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); |
|
|
|
if ( CheckRangeAttack1 ( DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ), (m_hEnemy->pev->origin - pev->origin).Length() ) ) |
|
{ |
|
return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); |
|
} |
|
|
|
} // fin du MS_COMBAT |
|
} // fin du switch |
|
|
|
return CBaseMonster :: GetSchedule(); |
|
} |
|
|
|
//---------------------------------------------------- |
|
// |
|
// Direction des comportements vers un tableau d'AI |
|
//---------------------------------------------------- |
|
|
|
|
|
Schedule_t* CSniper :: GetScheduleOfType ( int Type ) |
|
{ |
|
switch ( Type ) |
|
{ |
|
case SCHED_SNIPER_RECHARGEMENT: |
|
{ |
|
return &slSniperRechargement[ 0 ]; |
|
} |
|
case SCHED_SNIPER_COVER: |
|
{ |
|
return &slSniperCover[ 0 ]; |
|
} |
|
case SCHED_RANGE_ATTACK1: |
|
{ |
|
return &slSniperShoot[ 0 ]; |
|
} |
|
|
|
default: |
|
{ |
|
return CBaseMonster :: GetScheduleOfType ( Type ); |
|
} |
|
} |
|
} |
|
|
|
|