/*** * * Copyright (c) 1996-2001, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * This source code contains proprietary and confidential information of * Valve LLC and its suppliers. Access to this code is restricted to * persons who have executed a written SDK license with Valve. Any access, * use or distribution of this code by or to any unlicensed person is illegal. * ****/ #if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) //========================================================= // hassassin - Human assassin, fast and stealthy //========================================================= #include "extdll.h" #include "util.h" #include "cbase.h" #include "monsters.h" #include "schedule.h" #include "squadmonster.h" #include "weapons.h" #include "soundent.h" #include "game.h" extern DLL_GLOBAL int g_iSkillLevel; //========================================================= // monster-specific schedule types //========================================================= enum { SCHED_ASSASSIN_EXPOSED = LAST_COMMON_SCHEDULE + 1,// cover was blown. SCHED_ASSASSIN_JUMP, // fly through the air SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot SCHED_ASSASSIN_JUMP_LAND, // hit and run away }; //========================================================= // monster-specific tasks //========================================================= enum { TASK_ASSASSIN_FALL_TO_GROUND = LAST_COMMON_TASK + 1, // falling and waiting to hit ground }; //========================================================= // Monster's Anim Events Go Here //========================================================= #define ASSASSIN_AE_SHOOT1 1 #define ASSASSIN_AE_TOSS1 2 #define ASSASSIN_AE_JUMP 3 #define bits_MEMORY_BADJUMP (bits_MEMORY_CUSTOM1) class CHAssassin : public CBaseMonster { public: void Spawn( void ); void Precache( void ); void SetYawSpeed ( void ); int Classify ( void ); int ISoundMask ( void); void Shoot( void ); void HandleAnimEvent( MonsterEvent_t *pEvent ); Schedule_t* GetSchedule ( void ); Schedule_t* GetScheduleOfType ( int Type ); BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // jump // BOOL CheckMeleeAttack2 ( float flDot, float flDist ); BOOL CheckRangeAttack1 ( float flDot, float flDist ); // shoot BOOL CheckRangeAttack2 ( float flDot, float flDist ); // throw grenade void StartTask ( Task_t *pTask ); void RunAI( void ); void RunTask ( Task_t *pTask ); void DeathSound ( void ); void IdleSound ( void ); CUSTOM_SCHEDULES; int Save( CSave &save ); int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; float m_flLastShot; float m_flDiviation; float m_flNextJump; Vector m_vecJumpVelocity; float m_flNextGrenadeCheck; Vector m_vecTossVelocity; BOOL m_fThrowGrenade; int m_iTargetRanderamt; int m_iFrustration; int m_iShell; }; LINK_ENTITY_TO_CLASS( monster_human_assassin, CHAssassin ); TYPEDESCRIPTION CHAssassin::m_SaveData[] = { DEFINE_FIELD( CHAssassin, m_flLastShot, FIELD_TIME ), DEFINE_FIELD( CHAssassin, m_flDiviation, FIELD_FLOAT ), DEFINE_FIELD( CHAssassin, m_flNextJump, FIELD_TIME ), DEFINE_FIELD( CHAssassin, m_vecJumpVelocity, FIELD_VECTOR ), DEFINE_FIELD( CHAssassin, m_flNextGrenadeCheck, FIELD_TIME ), DEFINE_FIELD( CHAssassin, m_vecTossVelocity, FIELD_VECTOR ), DEFINE_FIELD( CHAssassin, m_fThrowGrenade, FIELD_BOOLEAN ), DEFINE_FIELD( CHAssassin, m_iTargetRanderamt, FIELD_INTEGER ), DEFINE_FIELD( CHAssassin, m_iFrustration, FIELD_INTEGER ), }; IMPLEMENT_SAVERESTORE( CHAssassin, CBaseMonster ); //========================================================= // DieSound //========================================================= void CHAssassin :: DeathSound ( void ) { } //========================================================= // IdleSound //========================================================= void CHAssassin :: IdleSound ( void ) { } //========================================================= // ISoundMask - returns a bit mask indicating which types // of sounds this monster regards. //========================================================= int CHAssassin :: ISoundMask ( void) { return bits_SOUND_WORLD | bits_SOUND_COMBAT | bits_SOUND_DANGER | bits_SOUND_PLAYER; } //========================================================= // Classify - indicates this monster's place in the // relationship table. //========================================================= int CHAssassin :: Classify ( void ) { return CLASS_HUMAN_MILITARY; } //========================================================= // SetYawSpeed - allows each sequence to have a different // turn rate associated with it. //========================================================= void CHAssassin :: SetYawSpeed ( void ) { int ys; switch ( m_Activity ) { case ACT_TURN_LEFT: case ACT_TURN_RIGHT: ys = 360; break; default: ys = 360; break; } pev->yaw_speed = ys; } //========================================================= // Shoot //========================================================= void CHAssassin :: Shoot ( void ) { if (m_hEnemy == NULL) { return; } Vector vecShootOrigin = GetGunPosition(); Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); if (m_flLastShot + 2 < gpGlobals->time) { m_flDiviation = 0.10; } else { m_flDiviation -= 0.01; if (m_flDiviation < 0.02) m_flDiviation = 0.02; } m_flLastShot = gpGlobals->time; 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( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, BULLET_MONSTER_9MM ); // shoot +-8 degrees switch(RANDOM_LONG(0,1)) { case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); break; case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); break; } pev->effects |= EF_MUZZLEFLASH; Vector angDir = UTIL_VecToAngles( vecShootDir ); SetBlending( 0, angDir.x ); m_cAmmoLoaded--; } //========================================================= // HandleAnimEvent - catches the monster-specific messages // that occur when tagged animation frames are played. // // Returns number of events handled, 0 if none. //========================================================= void CHAssassin :: HandleAnimEvent( MonsterEvent_t *pEvent ) { switch( pEvent->event ) { case ASSASSIN_AE_SHOOT1: Shoot( ); break; case ASSASSIN_AE_TOSS1: { UTIL_MakeVectors( pev->angles ); CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 2.0 ); m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. m_fThrowGrenade = FALSE; // !!!LATER - when in a group, only try to throw grenade if ordered. } break; case ASSASSIN_AE_JUMP: { // ALERT( at_console, "jumping"); UTIL_MakeAimVectors( pev->angles ); pev->movetype = MOVETYPE_TOSS; pev->flags &= ~FL_ONGROUND; pev->velocity = m_vecJumpVelocity; m_flNextJump = gpGlobals->time + 3.0; } return; default: CBaseMonster::HandleAnimEvent( pEvent ); break; } } //========================================================= // Spawn //========================================================= void CHAssassin :: Spawn() { Precache( ); SET_MODEL(ENT(pev), "models/hassassin.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->effects = 0; pev->health = gSkillData.hassassinHealth; m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; m_afCapability = bits_CAP_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP; pev->friction = 1; m_HackedGunPos = Vector( 0, 24, 48 ); m_iTargetRanderamt = 20; pev->renderamt = 20; pev->rendermode = kRenderTransTexture; MonsterInit(); } //========================================================= // Precache - precaches all resources this monster needs //========================================================= void CHAssassin :: Precache() { PRECACHE_MODEL("models/hassassin.mdl"); PRECACHE_SOUND("weapons/pl_gun1.wav"); PRECACHE_SOUND("weapons/pl_gun2.wav"); PRECACHE_SOUND("debris/beamstart1.wav"); m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell } //========================================================= // AI Schedules Specific to this monster //========================================================= //========================================================= // Fail Schedule //========================================================= Task_t tlAssassinFail[] = { { TASK_STOP_MOVING, 0 }, { TASK_SET_ACTIVITY, (float)ACT_IDLE }, { TASK_WAIT_FACE_ENEMY, (float)2 }, // { TASK_WAIT_PVS, (float)0 }, { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, }; Schedule_t slAssassinFail[] = { { tlAssassinFail, ARRAYSIZE ( tlAssassinFail ), bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE | bits_COND_PROVOKED | bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_HEAR_SOUND, bits_SOUND_DANGER | bits_SOUND_PLAYER, "AssassinFail" }, }; //========================================================= // Enemy exposed Agrunt's cover //========================================================= Task_t tlAssassinExposed[] = { { TASK_STOP_MOVING, (float)0 }, { TASK_RANGE_ATTACK1, (float)0 }, { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP }, { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, }; Schedule_t slAssassinExposed[] = { { tlAssassinExposed, ARRAYSIZE ( tlAssassinExposed ), bits_COND_CAN_MELEE_ATTACK1, 0, "AssassinExposed", }, }; //========================================================= // Take cover from enemy! Tries lateral cover before node // cover! //========================================================= Task_t tlAssassinTakeCoverFromEnemy[] = { { TASK_STOP_MOVING, (float)0 }, { TASK_WAIT, (float)0.2 }, { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, { TASK_RUN_PATH, (float)0 }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, { TASK_FACE_ENEMY, (float)0 }, }; Schedule_t slAssassinTakeCoverFromEnemy[] = { { tlAssassinTakeCoverFromEnemy, ARRAYSIZE ( tlAssassinTakeCoverFromEnemy ), bits_COND_NEW_ENEMY | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_HEAR_SOUND, bits_SOUND_DANGER, "AssassinTakeCoverFromEnemy" }, }; //========================================================= // Take cover from enemy! Tries lateral cover before node // cover! //========================================================= Task_t tlAssassinTakeCoverFromEnemy2[] = { { TASK_STOP_MOVING, (float)0 }, { TASK_WAIT, (float)0.2 }, { TASK_FACE_ENEMY, (float)0 }, { TASK_RANGE_ATTACK1, (float)0 }, { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK2 }, { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, { TASK_RUN_PATH, (float)0 }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, { TASK_FACE_ENEMY, (float)0 }, }; Schedule_t slAssassinTakeCoverFromEnemy2[] = { { tlAssassinTakeCoverFromEnemy2, ARRAYSIZE ( tlAssassinTakeCoverFromEnemy2 ), bits_COND_NEW_ENEMY | bits_COND_CAN_MELEE_ATTACK2 | bits_COND_HEAR_SOUND, bits_SOUND_DANGER, "AssassinTakeCoverFromEnemy2" }, }; //========================================================= // hide from the loudest sound source //========================================================= Task_t tlAssassinTakeCoverFromBestSound[] = { { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, { TASK_STOP_MOVING, (float)0 }, { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, { TASK_RUN_PATH, (float)0 }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, { TASK_TURN_LEFT, (float)179 }, }; Schedule_t slAssassinTakeCoverFromBestSound[] = { { tlAssassinTakeCoverFromBestSound, ARRAYSIZE ( tlAssassinTakeCoverFromBestSound ), bits_COND_NEW_ENEMY, 0, "AssassinTakeCoverFromBestSound" }, }; //========================================================= // AlertIdle Schedules //========================================================= Task_t tlAssassinHide[] = { { TASK_STOP_MOVING, 0 }, { TASK_SET_ACTIVITY, (float)ACT_IDLE }, { TASK_WAIT, (float)2 }, { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, }; Schedule_t slAssassinHide[] = { { tlAssassinHide, ARRAYSIZE ( tlAssassinHide ), bits_COND_NEW_ENEMY | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE | bits_COND_PROVOKED | bits_COND_HEAR_SOUND, bits_SOUND_DANGER, "AssassinHide" }, }; //========================================================= // HUNT Schedules //========================================================= Task_t tlAssassinHunt[] = { { TASK_GET_PATH_TO_ENEMY, (float)0 }, { TASK_RUN_PATH, (float)0 }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, }; Schedule_t slAssassinHunt[] = { { tlAssassinHunt, ARRAYSIZE ( tlAssassinHunt ), bits_COND_NEW_ENEMY | // bits_COND_SEE_ENEMY | bits_COND_CAN_RANGE_ATTACK1 | bits_COND_HEAR_SOUND, bits_SOUND_DANGER, "AssassinHunt" }, }; //========================================================= // Jumping Schedules //========================================================= Task_t tlAssassinJump[] = { { TASK_STOP_MOVING, (float)0 }, { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, { TASK_SET_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_ATTACK }, }; Schedule_t slAssassinJump[] = { { tlAssassinJump, ARRAYSIZE ( tlAssassinJump ), 0, 0, "AssassinJump" }, }; //========================================================= // repel //========================================================= Task_t tlAssassinJumpAttack[] = { { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_LAND }, // { TASK_SET_ACTIVITY, (float)ACT_FLY }, { TASK_ASSASSIN_FALL_TO_GROUND, (float)0 }, }; Schedule_t slAssassinJumpAttack[] = { { tlAssassinJumpAttack, ARRAYSIZE ( tlAssassinJumpAttack ), 0, 0, "AssassinJumpAttack" }, }; //========================================================= // repel //========================================================= Task_t tlAssassinJumpLand[] = { { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_EXPOSED }, // { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, { TASK_SET_ACTIVITY, (float)ACT_IDLE }, { TASK_REMEMBER, (float)bits_MEMORY_BADJUMP }, { TASK_FIND_NODE_COVER_FROM_ENEMY, (float)0 }, { TASK_RUN_PATH, (float)0 }, { TASK_FORGET, (float)bits_MEMORY_BADJUMP }, { TASK_WAIT_FOR_MOVEMENT, (float)0 }, { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, { TASK_FACE_ENEMY, (float)0 }, { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, }; Schedule_t slAssassinJumpLand[] = { { tlAssassinJumpLand, ARRAYSIZE ( tlAssassinJumpLand ), 0, 0, "AssassinJumpLand" }, }; DEFINE_CUSTOM_SCHEDULES( CHAssassin ) { slAssassinFail, slAssassinExposed, slAssassinTakeCoverFromEnemy, slAssassinTakeCoverFromEnemy2, slAssassinTakeCoverFromBestSound, slAssassinHide, slAssassinHunt, slAssassinJump, slAssassinJumpAttack, slAssassinJumpLand, }; IMPLEMENT_CUSTOM_SCHEDULES( CHAssassin, CBaseMonster ); //========================================================= // CheckMeleeAttack1 - jump like crazy if the enemy gets too close. //========================================================= BOOL CHAssassin :: CheckMeleeAttack1 ( float flDot, float flDist ) { if ( m_flNextJump < gpGlobals->time && (flDist <= 128 || HasMemory( bits_MEMORY_BADJUMP )) && m_hEnemy != NULL ) { TraceResult tr; Vector vecDest = pev->origin + Vector( RANDOM_FLOAT( -64, 64), RANDOM_FLOAT( -64, 64 ), 160 ); UTIL_TraceHull( pev->origin + Vector( 0, 0, 36 ), vecDest + Vector( 0, 0, 36 ), dont_ignore_monsters, human_hull, ENT(pev), &tr); if ( tr.fStartSolid || tr.flFraction < 1.0) { return FALSE; } float flGravity = g_psv_gravity->value; float time = sqrt( 160 / (0.5 * flGravity)); float speed = flGravity * time / 160; m_vecJumpVelocity = (vecDest - pev->origin) * speed; return TRUE; } return FALSE; } //========================================================= // CheckRangeAttack1 - drop a cap in their ass // //========================================================= BOOL CHAssassin :: CheckRangeAttack1 ( float flDot, float flDist ) { if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) { TraceResult tr; Vector vecSrc = GetGunPosition(); // verify that a bullet fired from the gun will hit the enemy before the world. UTIL_TraceLine( vecSrc, m_hEnemy->BodyTarget(vecSrc), dont_ignore_monsters, ENT(pev), &tr); if ( tr.flFraction == 1 || tr.pHit == m_hEnemy->edict() ) { return TRUE; } } return FALSE; } //========================================================= // CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close. //========================================================= BOOL CHAssassin :: CheckRangeAttack2 ( float flDot, float flDist ) { m_fThrowGrenade = FALSE; if ( !FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) ) { // don't throw grenades at anything that isn't on the ground! return FALSE; } // don't get grenade happy unless the player starts to piss you off if ( m_iFrustration <= 2) return FALSE; if ( m_flNextGrenadeCheck < gpGlobals->time && !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 512 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) { Vector vecToss = VecCheckThrow( pev, GetGunPosition( ), m_hEnemy->Center(), flDist, 0.5 ); // use dist as speed to get there in 1 second if ( vecToss != g_vecZero ) { m_vecTossVelocity = vecToss; // throw a hand grenade m_fThrowGrenade = TRUE; return TRUE; } } return FALSE; } //========================================================= // RunAI //========================================================= void CHAssassin :: RunAI( void ) { CBaseMonster :: RunAI(); // always visible if moving // always visible is not on hard if (g_iSkillLevel != SKILL_HARD || m_hEnemy == NULL || pev->deadflag != DEAD_NO || m_Activity == ACT_RUN || m_Activity == ACT_WALK || !(pev->flags & FL_ONGROUND)) m_iTargetRanderamt = 255; else m_iTargetRanderamt = 20; if (pev->renderamt > m_iTargetRanderamt) { if (pev->renderamt == 255) { EMIT_SOUND (ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 0.2, ATTN_NORM ); } pev->renderamt = max( pev->renderamt - 50, m_iTargetRanderamt ); pev->rendermode = kRenderTransTexture; } else if (pev->renderamt < m_iTargetRanderamt) { pev->renderamt = min( pev->renderamt + 50, m_iTargetRanderamt ); if (pev->renderamt == 255) pev->rendermode = kRenderNormal; } if (m_Activity == ACT_RUN || m_Activity == ACT_WALK) { static int iStep = 0; iStep = ! iStep; if (iStep) { switch( RANDOM_LONG( 0, 3 ) ) { case 0: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step1.wav", 0.5, ATTN_NORM); break; case 1: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step3.wav", 0.5, ATTN_NORM); break; case 2: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step2.wav", 0.5, ATTN_NORM); break; case 3: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step4.wav", 0.5, ATTN_NORM); break; } } } } //========================================================= // StartTask //========================================================= void CHAssassin :: StartTask ( Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_RANGE_ATTACK2: if (!m_fThrowGrenade) { TaskComplete( ); } else { CBaseMonster :: StartTask ( pTask ); } break; case TASK_ASSASSIN_FALL_TO_GROUND: break; default: CBaseMonster :: StartTask ( pTask ); break; } } //========================================================= // RunTask //========================================================= void CHAssassin :: RunTask ( Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_ASSASSIN_FALL_TO_GROUND: MakeIdealYaw( m_vecEnemyLKP ); ChangeYaw( pev->yaw_speed ); if (m_fSequenceFinished) { if (pev->velocity.z > 0) { pev->sequence = LookupSequence( "fly_up" ); } else if (HasConditions ( bits_COND_SEE_ENEMY )) { pev->sequence = LookupSequence( "fly_attack" ); pev->frame = 0; } else { pev->sequence = LookupSequence( "fly_down" ); pev->frame = 0; } ResetSequenceInfo( ); SetYawSpeed(); } if (pev->flags & FL_ONGROUND) { // ALERT( at_console, "on ground\n"); TaskComplete( ); } break; default: CBaseMonster :: RunTask ( pTask ); break; } } //========================================================= // GetSchedule - Decides which type of schedule best suits // the monster's current state and conditions. Then calls // monster's member function to get a pointer to a schedule // of the proper type. //========================================================= Schedule_t *CHAssassin :: GetSchedule ( void ) { switch ( m_MonsterState ) { case MONSTERSTATE_IDLE: case MONSTERSTATE_ALERT: { if ( HasConditions ( bits_COND_HEAR_SOUND )) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) { return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); } if ( pSound && (pSound->m_iType & bits_SOUND_COMBAT) ) { return GetScheduleOfType( SCHED_INVESTIGATE_SOUND ); } } } break; case MONSTERSTATE_COMBAT: { // dead enemy if ( HasConditions( bits_COND_ENEMY_DEAD ) ) { // call base class, all code to handle dead enemies is centralized there. return CBaseMonster :: GetSchedule(); } // flying? if ( pev->movetype == MOVETYPE_TOSS) { if (pev->flags & FL_ONGROUND) { // ALERT( at_console, "landed\n"); // just landed pev->movetype = MOVETYPE_STEP; return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_LAND ); } else { // ALERT( at_console, "jump\n"); // jump or jump/shoot if ( m_MonsterState == MONSTERSTATE_COMBAT ) return GetScheduleOfType ( SCHED_ASSASSIN_JUMP ); else return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_ATTACK ); } } if ( HasConditions ( bits_COND_HEAR_SOUND )) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) { return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); } } if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) { m_iFrustration++; } if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) { m_iFrustration++; } // jump player! if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) { // ALERT( at_console, "melee attack 1\n"); return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); } // throw grenade if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) { // ALERT( at_console, "range attack 2\n"); return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ); } // spotted if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) { // ALERT( at_console, "exposed\n"); m_iFrustration++; return GetScheduleOfType ( SCHED_ASSASSIN_EXPOSED ); } // can attack if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) { // ALERT( at_console, "range attack 1\n"); m_iFrustration = 0; return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); } if ( HasConditions ( bits_COND_SEE_ENEMY ) ) { // ALERT( at_console, "face\n"); return GetScheduleOfType ( SCHED_COMBAT_FACE ); } // new enemy if ( HasConditions ( bits_COND_NEW_ENEMY ) ) { // ALERT( at_console, "take cover\n"); return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); } // ALERT( at_console, "stand\n"); return GetScheduleOfType ( SCHED_ALERT_STAND ); } break; } return CBaseMonster :: GetSchedule(); } //========================================================= //========================================================= Schedule_t* CHAssassin :: GetScheduleOfType ( int Type ) { // ALERT( at_console, "%d\n", m_iFrustration ); switch ( Type ) { case SCHED_TAKE_COVER_FROM_ENEMY: if (pev->health > 30) return slAssassinTakeCoverFromEnemy; else return slAssassinTakeCoverFromEnemy2; case SCHED_TAKE_COVER_FROM_BEST_SOUND: return slAssassinTakeCoverFromBestSound; case SCHED_ASSASSIN_EXPOSED: return slAssassinExposed; case SCHED_FAIL: if (m_MonsterState == MONSTERSTATE_COMBAT) return slAssassinFail; break; case SCHED_ALERT_STAND: if (m_MonsterState == MONSTERSTATE_COMBAT) return slAssassinHide; break; case SCHED_CHASE_ENEMY: return slAssassinHunt; case SCHED_MELEE_ATTACK1: if (pev->flags & FL_ONGROUND) { if (m_flNextJump > gpGlobals->time) { // can't jump yet, go ahead and fail return slAssassinFail; } else { return slAssassinJump; } } else { return slAssassinJumpAttack; } case SCHED_ASSASSIN_JUMP: case SCHED_ASSASSIN_JUMP_ATTACK: return slAssassinJumpAttack; case SCHED_ASSASSIN_JUMP_LAND: return slAssassinJumpLand; } return CBaseMonster :: GetScheduleOfType( Type ); } #endif