Team Fortress 2 Source Code as on 22/4/2020
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.

951 lines
23 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "ai_default.h"
  10. #include "ai_task.h"
  11. #include "ai_schedule.h"
  12. #include "ai_node.h"
  13. #include "ai_hull.h"
  14. #include "ai_hint.h"
  15. #include "ai_memory.h"
  16. #include "ai_route.h"
  17. #include "ai_motor.h"
  18. #include "soundent.h"
  19. #include "game.h"
  20. #include "npcevent.h"
  21. #include "entitylist.h"
  22. #include "activitylist.h"
  23. #include "animation.h"
  24. #include "basecombatweapon.h"
  25. #include "IEffects.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "ammodef.h"
  29. #include "util.h"
  30. #include "hl1_ai_basenpc.h"
  31. #include "hl1_basegrenade.h"
  32. #include "movevars_shared.h"
  33. #include "ai_basenpc.h"
  34. ConVar sk_hassassin_health( "sk_hassassin_health", "50" );
  35. //=========================================================
  36. // monster-specific schedule types
  37. //=========================================================
  38. enum
  39. {
  40. SCHED_ASSASSIN_EXPOSED = LAST_SHARED_SCHEDULE,// cover was blown.
  41. SCHED_ASSASSIN_JUMP, // fly through the air
  42. SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot
  43. SCHED_ASSASSIN_JUMP_LAND, // hit and run away
  44. SCHED_ASSASSIN_FAIL,
  45. SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1,
  46. SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2,
  47. SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND,
  48. SCHED_ASSASSIN_HIDE,
  49. SCHED_ASSASSIN_HUNT,
  50. };
  51. Activity ACT_ASSASSIN_FLY_UP;
  52. Activity ACT_ASSASSIN_FLY_ATTACK;
  53. Activity ACT_ASSASSIN_FLY_DOWN;
  54. //=========================================================
  55. // monster-specific tasks
  56. //=========================================================
  57. enum
  58. {
  59. TASK_ASSASSIN_FALL_TO_GROUND = LAST_SHARED_TASK + 1, // falling and waiting to hit ground
  60. };
  61. //=========================================================
  62. // Monster's Anim Events Go Here
  63. //=========================================================
  64. #define ASSASSIN_AE_SHOOT1 1
  65. #define ASSASSIN_AE_TOSS1 2
  66. #define ASSASSIN_AE_JUMP 3
  67. #define MEMORY_BADJUMP bits_MEMORY_CUSTOM1
  68. class CNPC_HAssassin : public CHL1BaseNPC
  69. {
  70. DECLARE_CLASS( CNPC_HAssassin, CHL1BaseNPC );
  71. public:
  72. void Spawn( void );
  73. void Precache( void );
  74. int TranslateSchedule( int scheduleType );
  75. void HandleAnimEvent( animevent_t *pEvent );
  76. float MaxYawSpeed() { return 360.0f; }
  77. void Shoot ( void );
  78. int MeleeAttack1Conditions ( float flDot, float flDist );
  79. int RangeAttack1Conditions ( float flDot, float flDist );
  80. int RangeAttack2Conditions ( float flDot, float flDist );
  81. int SelectSchedule ( void );
  82. void RunTask ( const Task_t *pTask );
  83. void StartTask ( const Task_t *pTask );
  84. Class_T Classify ( void );
  85. int GetSoundInterests( void );
  86. void RunAI( void );
  87. float m_flLastShot;
  88. float m_flDiviation;
  89. float m_flNextJump;
  90. Vector m_vecJumpVelocity;
  91. float m_flNextGrenadeCheck;
  92. Vector m_vecTossVelocity;
  93. bool m_fThrowGrenade;
  94. int m_iTargetRanderamt;
  95. int m_iFrustration;
  96. int m_iAmmoType;
  97. public:
  98. DECLARE_DATADESC();
  99. DEFINE_CUSTOM_AI;
  100. };
  101. LINK_ENTITY_TO_CLASS( monster_human_assassin, CNPC_HAssassin );
  102. BEGIN_DATADESC( CNPC_HAssassin )
  103. DEFINE_FIELD( m_flLastShot, FIELD_TIME ),
  104. DEFINE_FIELD( m_flDiviation, FIELD_FLOAT ),
  105. DEFINE_FIELD( m_flNextJump, FIELD_TIME ),
  106. DEFINE_FIELD( m_vecJumpVelocity, FIELD_VECTOR ),
  107. DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
  108. DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
  109. DEFINE_FIELD( m_fThrowGrenade, FIELD_BOOLEAN ),
  110. DEFINE_FIELD( m_iTargetRanderamt, FIELD_INTEGER ),
  111. DEFINE_FIELD( m_iFrustration, FIELD_INTEGER ),
  112. //DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
  113. END_DATADESC()
  114. //=========================================================
  115. // Spawn
  116. //=========================================================
  117. void CNPC_HAssassin::Spawn()
  118. {
  119. Precache( );
  120. SetModel( "models/hassassin.mdl");
  121. SetHullType(HULL_HUMAN);
  122. SetHullSizeNormal();
  123. SetNavType ( NAV_GROUND );
  124. SetSolid( SOLID_BBOX );
  125. AddSolidFlags( FSOLID_NOT_STANDABLE );
  126. SetMoveType( MOVETYPE_STEP );
  127. m_bloodColor = BLOOD_COLOR_RED;
  128. ClearEffects();
  129. m_iHealth = sk_hassassin_health.GetFloat();
  130. m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
  131. m_NPCState = NPC_STATE_NONE;
  132. m_HackedGunPos = Vector( 0, 24, 48 );
  133. m_iTargetRanderamt = 20;
  134. SetRenderColor( 255, 255, 255, 20 );
  135. m_nRenderMode = kRenderTransTexture;
  136. CapabilitiesClear();
  137. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  138. CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 | bits_CAP_INNATE_MELEE_ATTACK1 );
  139. NPCInit();
  140. }
  141. //=========================================================
  142. // Precache - precaches all resources this monster needs
  143. //=========================================================
  144. void CNPC_HAssassin::Precache()
  145. {
  146. m_iAmmoType = GetAmmoDef()->Index("9mmRound");
  147. PrecacheModel("models/hassassin.mdl");
  148. UTIL_PrecacheOther( "npc_handgrenade" );
  149. PrecacheScriptSound( "HAssassin.Shot" );
  150. PrecacheScriptSound( "HAssassin.Beamsound" );
  151. PrecacheScriptSound( "HAssassin.Footstep" );
  152. }
  153. int CNPC_HAssassin::GetSoundInterests( void )
  154. {
  155. return SOUND_WORLD |
  156. SOUND_COMBAT |
  157. SOUND_PLAYER |
  158. SOUND_DANGER;
  159. }
  160. Class_T CNPC_HAssassin::Classify ( void )
  161. {
  162. return CLASS_HUMAN_MILITARY;
  163. }
  164. //=========================================================
  165. // CheckMeleeAttack1 - jump like crazy if the enemy gets too close.
  166. //=========================================================
  167. int CNPC_HAssassin::MeleeAttack1Conditions ( float flDot, float flDist )
  168. {
  169. if ( m_flNextJump < gpGlobals->curtime && ( flDist <= 128 || HasMemory( MEMORY_BADJUMP )) && GetEnemy() != NULL )
  170. {
  171. trace_t tr;
  172. Vector vecMin = Vector( random->RandomFloat( 0, -64), random->RandomFloat( 0, -64 ), 0 );
  173. Vector vecMax = Vector( random->RandomFloat( 0, 64), random->RandomFloat( 0, 64 ), 160 );
  174. Vector vecDest = GetAbsOrigin() + Vector( random->RandomFloat( -64, 64), random->RandomFloat( -64, 64 ), 160 );
  175. UTIL_TraceHull( GetAbsOrigin() + Vector( 0, 0, 36 ), GetAbsOrigin() + Vector( 0, 0, 36 ), vecMin, vecMax, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  176. //NDebugOverlay::Box( GetAbsOrigin() + Vector( 0, 0, 36 ), vecMin, vecMax, 0,0, 255, 0, 2.0 );
  177. if ( tr.startsolid || tr.fraction < 1.0)
  178. {
  179. return COND_TOO_CLOSE_TO_ATTACK;
  180. }
  181. float flGravity = GetCurrentGravity();
  182. float time = sqrt( 160 / (0.5 * flGravity));
  183. float speed = flGravity * time / 160;
  184. m_vecJumpVelocity = ( vecDest - GetAbsOrigin() ) * speed;
  185. return COND_CAN_MELEE_ATTACK1;
  186. }
  187. if ( flDist > 128 )
  188. return COND_TOO_FAR_TO_ATTACK;
  189. return COND_NONE;
  190. }
  191. //=========================================================
  192. // CheckRangeAttack1 - drop a cap in their ass
  193. //
  194. //=========================================================
  195. int CNPC_HAssassin::RangeAttack1Conditions ( float flDot, float flDist )
  196. {
  197. if ( !HasCondition( COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 )
  198. {
  199. trace_t tr;
  200. Vector vecSrc = GetAbsOrigin() + m_HackedGunPos;
  201. // verify that a bullet fired from the gun will hit the enemy before the world.
  202. UTIL_TraceLine( vecSrc, GetEnemy()->BodyTarget(vecSrc), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  203. if ( tr.fraction == 1.0 || tr.m_pEnt == GetEnemy() )
  204. {
  205. return COND_CAN_RANGE_ATTACK1;
  206. }
  207. }
  208. return COND_NONE;
  209. }
  210. //=========================================================
  211. // CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close.
  212. //=========================================================
  213. int CNPC_HAssassin::RangeAttack2Conditions ( float flDot, float flDist )
  214. {
  215. m_fThrowGrenade = false;
  216. if ( !FBitSet ( GetEnemy()->GetFlags(), FL_ONGROUND ) )
  217. {
  218. // don't throw grenades at anything that isn't on the ground!
  219. return COND_NONE;
  220. }
  221. // don't get grenade happy unless the player starts to piss you off
  222. if ( m_iFrustration <= 2)
  223. return COND_NONE;
  224. if ( m_flNextGrenadeCheck < gpGlobals->curtime && !HasCondition( COND_ENEMY_OCCLUDED ) && flDist <= 512 )
  225. {
  226. Vector vTossPos;
  227. QAngle vAngles;
  228. GetAttachment( "grenadehand", vTossPos, vAngles );
  229. Vector vecToss = VecCheckThrow( this, vTossPos, GetEnemy()->WorldSpaceCenter(), flDist, 0.5 ); // use dist as speed to get there in 1 second
  230. if ( vecToss != vec3_origin )
  231. {
  232. m_vecTossVelocity = vecToss;
  233. // throw a hand grenade
  234. m_fThrowGrenade = TRUE;
  235. return COND_CAN_RANGE_ATTACK2;
  236. }
  237. }
  238. return COND_NONE;
  239. }
  240. //=========================================================
  241. // StartTask
  242. //=========================================================
  243. void CNPC_HAssassin::StartTask ( const Task_t *pTask )
  244. {
  245. switch ( pTask->iTask )
  246. {
  247. case TASK_RANGE_ATTACK2:
  248. if (!m_fThrowGrenade)
  249. {
  250. TaskComplete( );
  251. }
  252. else
  253. {
  254. BaseClass::StartTask ( pTask );
  255. }
  256. break;
  257. case TASK_ASSASSIN_FALL_TO_GROUND:
  258. m_flWaitFinished = gpGlobals->curtime + 2.0f;
  259. break;
  260. default:
  261. BaseClass::StartTask ( pTask );
  262. break;
  263. }
  264. }
  265. //=========================================================
  266. // RunTask
  267. //=========================================================
  268. void CNPC_HAssassin::RunTask ( const Task_t *pTask )
  269. {
  270. switch ( pTask->iTask )
  271. {
  272. case TASK_ASSASSIN_FALL_TO_GROUND:
  273. GetMotor()->SetIdealYawAndUpdate( GetEnemyLKP() );
  274. if ( IsSequenceFinished() )
  275. {
  276. if ( GetAbsVelocity().z > 0)
  277. {
  278. SetActivity( ACT_ASSASSIN_FLY_UP );
  279. }
  280. else if ( HasCondition ( COND_SEE_ENEMY ))
  281. {
  282. SetActivity( ACT_ASSASSIN_FLY_ATTACK );
  283. SetCycle( 0 );
  284. }
  285. else
  286. {
  287. SetActivity( ACT_ASSASSIN_FLY_DOWN );
  288. SetCycle( 0 );
  289. }
  290. ResetSequenceInfo( );
  291. }
  292. if ( GetFlags() & FL_ONGROUND)
  293. {
  294. TaskComplete( );
  295. }
  296. else if( gpGlobals->curtime > m_flWaitFinished || GetAbsVelocity().z == 0.0 )
  297. {
  298. // I've waited two seconds and haven't hit the ground. Try to force it.
  299. trace_t trace;
  300. UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &trace );
  301. if( trace.DidHitWorld() )
  302. {
  303. SetGroundEntity( trace.m_pEnt );
  304. }
  305. else
  306. {
  307. // Try again in a couple of seconds.
  308. m_flWaitFinished = gpGlobals->curtime + 2.0f;
  309. }
  310. }
  311. break;
  312. default:
  313. BaseClass::RunTask ( pTask );
  314. break;
  315. }
  316. }
  317. //=========================================================
  318. // GetSchedule - Decides which type of schedule best suits
  319. // the monster's current state and conditions. Then calls
  320. // monster's member function to get a pointer to a schedule
  321. // of the proper type.
  322. //=========================================================
  323. int CNPC_HAssassin::SelectSchedule ( void )
  324. {
  325. switch ( m_NPCState )
  326. {
  327. case NPC_STATE_IDLE:
  328. case NPC_STATE_ALERT:
  329. {
  330. if ( HasCondition ( COND_HEAR_DANGER ) || HasCondition ( COND_HEAR_COMBAT ) )
  331. {
  332. if ( HasCondition ( COND_HEAR_DANGER ) )
  333. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  334. else
  335. return SCHED_INVESTIGATE_SOUND;
  336. }
  337. }
  338. break;
  339. case NPC_STATE_COMBAT:
  340. {
  341. // dead enemy
  342. if ( HasCondition( COND_ENEMY_DEAD ) )
  343. {
  344. // call base class, all code to handle dead enemies is centralized there.
  345. return BaseClass::SelectSchedule();
  346. }
  347. // flying?
  348. if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
  349. {
  350. if ( GetFlags() & FL_ONGROUND )
  351. {
  352. //Msg( "landed\n" );
  353. // just landed
  354. SetMoveType( MOVETYPE_STEP );
  355. return SCHED_ASSASSIN_JUMP_LAND;
  356. }
  357. else
  358. {
  359. //Msg("jump\n");
  360. // jump or jump/shoot
  361. if ( m_NPCState == NPC_STATE_COMBAT )
  362. return SCHED_ASSASSIN_JUMP;
  363. else
  364. return SCHED_ASSASSIN_JUMP_ATTACK;
  365. }
  366. }
  367. if ( HasCondition ( COND_HEAR_DANGER ) )
  368. {
  369. return SCHED_TAKE_COVER_FROM_BEST_SOUND;
  370. }
  371. if ( HasCondition ( COND_LIGHT_DAMAGE ) )
  372. {
  373. m_iFrustration++;
  374. }
  375. if ( HasCondition ( COND_HEAVY_DAMAGE ) )
  376. {
  377. m_iFrustration++;
  378. }
  379. // jump player!
  380. if ( HasCondition ( COND_CAN_MELEE_ATTACK1 ) )
  381. {
  382. //Msg( "melee attack 1\n");
  383. return SCHED_MELEE_ATTACK1;
  384. }
  385. // throw grenade
  386. if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) )
  387. {
  388. //Msg( "range attack 2\n");
  389. return SCHED_RANGE_ATTACK2;
  390. }
  391. // spotted
  392. if ( HasCondition ( COND_SEE_ENEMY ) && HasCondition ( COND_ENEMY_FACING_ME ) )
  393. {
  394. //Msg("exposed\n");
  395. m_iFrustration++;
  396. return SCHED_ASSASSIN_EXPOSED;
  397. }
  398. // can attack
  399. if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
  400. {
  401. //Msg( "range attack 1\n" );
  402. m_iFrustration = 0;
  403. return SCHED_RANGE_ATTACK1;
  404. }
  405. if ( HasCondition ( COND_SEE_ENEMY ) )
  406. {
  407. //Msg( "face\n");
  408. return SCHED_COMBAT_FACE;
  409. }
  410. // new enemy
  411. if ( HasCondition ( COND_NEW_ENEMY ) )
  412. {
  413. //Msg( "take cover\n");
  414. return SCHED_TAKE_COVER_FROM_ENEMY;
  415. }
  416. // ALERT( at_console, "stand\n");
  417. return SCHED_ALERT_STAND;
  418. }
  419. break;
  420. }
  421. return BaseClass::SelectSchedule();
  422. }
  423. //=========================================================
  424. // HandleAnimEvent - catches the monster-specific messages
  425. // that occur when tagged animation frames are played.
  426. //
  427. // Returns number of events handled, 0 if none.
  428. //=========================================================
  429. void CNPC_HAssassin::HandleAnimEvent( animevent_t *pEvent )
  430. {
  431. switch( pEvent->event )
  432. {
  433. case ASSASSIN_AE_SHOOT1:
  434. Shoot( );
  435. break;
  436. case ASSASSIN_AE_TOSS1:
  437. {
  438. Vector vTossPos;
  439. QAngle vAngles;
  440. GetAttachment( "grenadehand", vTossPos, vAngles );
  441. CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", vTossPos, vec3_angle );
  442. if ( pGrenade )
  443. {
  444. pGrenade->ShootTimed( this, m_vecTossVelocity, 2.0 );
  445. }
  446. m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
  447. m_fThrowGrenade = FALSE;
  448. // !!!LATER - when in a group, only try to throw grenade if ordered.
  449. }
  450. break;
  451. case ASSASSIN_AE_JUMP:
  452. {
  453. SetMoveType( MOVETYPE_FLYGRAVITY );
  454. SetGroundEntity( NULL );
  455. SetAbsVelocity( m_vecJumpVelocity );
  456. m_flNextJump = gpGlobals->curtime + 3.0;
  457. }
  458. return;
  459. default:
  460. BaseClass::HandleAnimEvent( pEvent );
  461. break;
  462. }
  463. }
  464. //=========================================================
  465. // Shoot
  466. //=========================================================
  467. void CNPC_HAssassin::Shoot ( void )
  468. {
  469. Vector vForward, vRight, vUp;
  470. Vector vecShootOrigin;
  471. QAngle vAngles;
  472. if ( GetEnemy() == NULL)
  473. {
  474. return;
  475. }
  476. GetAttachment( "guntip", vecShootOrigin, vAngles );
  477. Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
  478. if (m_flLastShot + 2 < gpGlobals->curtime)
  479. {
  480. m_flDiviation = 0.10;
  481. }
  482. else
  483. {
  484. m_flDiviation -= 0.01;
  485. if (m_flDiviation < 0.02)
  486. m_flDiviation = 0.02;
  487. }
  488. m_flLastShot = gpGlobals->curtime;
  489. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  490. Vector vecShellVelocity = vRight * random->RandomFloat(40,90) + vUp * random->RandomFloat(75,200) + vForward * random->RandomFloat(-40, 40);
  491. EjectShell( GetAbsOrigin() + vUp * 32 + vForward * 12, vecShellVelocity, GetAbsAngles().y, 0 );
  492. FireBullets( 1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, m_iAmmoType ); // shoot +-8 degrees
  493. //NDebugOverlay::Line( vecShootOrigin, vecShootOrigin + vecShootDir * 2048, 255, 0, 0, true, 2.0 );
  494. CPASAttenuationFilter filter( this );
  495. EmitSound( filter, entindex(), "HAssassin.Shot" );
  496. DoMuzzleFlash();
  497. VectorAngles( vecShootDir, vAngles );
  498. SetPoseParameter( "shoot", vecShootDir.x );
  499. m_cAmmoLoaded--;
  500. }
  501. //=========================================================
  502. //=========================================================
  503. int CNPC_HAssassin::TranslateSchedule ( int scheduleType )
  504. {
  505. // Msg( "%d\n", m_iFrustration );
  506. switch ( scheduleType )
  507. {
  508. case SCHED_TAKE_COVER_FROM_ENEMY:
  509. if ( m_iHealth > 30 )
  510. return SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1;
  511. else
  512. return SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2;
  513. case SCHED_TAKE_COVER_FROM_BEST_SOUND:
  514. return SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND;
  515. case SCHED_FAIL:
  516. if ( m_NPCState == NPC_STATE_COMBAT )
  517. return SCHED_ASSASSIN_FAIL;
  518. break;
  519. case SCHED_ALERT_STAND:
  520. if ( m_NPCState == NPC_STATE_COMBAT )
  521. return SCHED_ASSASSIN_HIDE;
  522. break;
  523. //case SCHED_CHASE_ENEMY:
  524. // return SCHED_ASSASSIN_HUNT;
  525. case SCHED_MELEE_ATTACK1:
  526. if ( GetFlags() & FL_ONGROUND)
  527. {
  528. if (m_flNextJump > gpGlobals->curtime)
  529. {
  530. // can't jump yet, go ahead and fail
  531. return SCHED_ASSASSIN_FAIL;
  532. }
  533. else
  534. {
  535. return SCHED_ASSASSIN_JUMP;
  536. }
  537. }
  538. else
  539. {
  540. return SCHED_ASSASSIN_JUMP_ATTACK;
  541. }
  542. }
  543. return BaseClass::TranslateSchedule( scheduleType );
  544. }
  545. //=========================================================
  546. // RunAI
  547. //=========================================================
  548. void CNPC_HAssassin::RunAI( void )
  549. {
  550. BaseClass::RunAI();
  551. // always visible if moving
  552. // always visible is not on hard
  553. if (g_iSkillLevel != SKILL_HARD || GetEnemy() == NULL || m_lifeState == LIFE_DEAD || GetActivity() == ACT_RUN || GetActivity() == ACT_WALK || !(GetFlags() & FL_ONGROUND))
  554. m_iTargetRanderamt = 255;
  555. else
  556. m_iTargetRanderamt = 20;
  557. CPASAttenuationFilter filter( this );
  558. if ( GetRenderColor().a > m_iTargetRanderamt)
  559. {
  560. if ( GetRenderColor().a == 255)
  561. {
  562. EmitSound( filter, entindex(), "HAssassin.Beamsound" );
  563. }
  564. SetRenderColorA( MAX( GetRenderColor().a - 50, m_iTargetRanderamt ) );
  565. m_nRenderMode = kRenderTransTexture;
  566. }
  567. else if ( GetRenderColor().a < m_iTargetRanderamt)
  568. {
  569. SetRenderColorA ( MIN( GetRenderColor().a + 50, m_iTargetRanderamt ) );
  570. if (GetRenderColor().a == 255)
  571. m_nRenderMode = kRenderNormal;
  572. }
  573. if ( GetActivity() == ACT_RUN || GetActivity() == ACT_WALK)
  574. {
  575. static int iStep = 0;
  576. iStep = ! iStep;
  577. if (iStep)
  578. {
  579. EmitSound( filter, entindex(), "HAssassin.Footstep" );
  580. }
  581. }
  582. }
  583. AI_BEGIN_CUSTOM_NPC( monster_human_assassin, CNPC_HAssassin )
  584. DECLARE_TASK( TASK_ASSASSIN_FALL_TO_GROUND )
  585. DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_UP )
  586. DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_ATTACK )
  587. DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_DOWN )
  588. //=========================================================
  589. // AI Schedules Specific to this monster
  590. //=========================================================
  591. //=========================================================
  592. // Enemy exposed assasin's cover
  593. //=========================================================
  594. //=========================================================
  595. // > SCHED_ASSASSIN_EXPOSED
  596. //=========================================================
  597. DEFINE_SCHEDULE
  598. (
  599. SCHED_ASSASSIN_EXPOSED,
  600. " Tasks"
  601. " TASK_STOP_MOVING 0"
  602. " TASK_RANGE_ATTACK1 0"
  603. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP"
  604. " TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
  605. " "
  606. " Interrupts"
  607. " COND_CAN_MELEE_ATTACK1"
  608. )
  609. //=========================================================
  610. // > SCHED_ASSASSIN_JUMP
  611. //=========================================================
  612. DEFINE_SCHEDULE
  613. (
  614. SCHED_ASSASSIN_JUMP,
  615. " Tasks"
  616. " TASK_STOP_MOVING 0"
  617. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_HOP"
  618. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP_ATTACK"
  619. " "
  620. " Interrupts"
  621. )
  622. //=========================================================
  623. // > SCHED_ASSASSIN_JUMP_ATTACK
  624. //=========================================================
  625. DEFINE_SCHEDULE
  626. (
  627. SCHED_ASSASSIN_JUMP_ATTACK,
  628. " Tasks"
  629. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP_LAND"
  630. " TASK_ASSASSIN_FALL_TO_GROUND 0"
  631. " "
  632. " Interrupts"
  633. )
  634. //=========================================================
  635. // > SCHED_ASSASSIN_JUMP_LAND
  636. //=========================================================
  637. DEFINE_SCHEDULE
  638. (
  639. SCHED_ASSASSIN_JUMP_LAND,
  640. " Tasks"
  641. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_EXPOSED"
  642. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  643. " TASK_REMEMBER MEMORY:CUSTOM1"
  644. " TASK_FIND_NODE_COVER_FROM_ENEMY 0"
  645. " TASK_RUN_PATH 0"
  646. " TASK_FORGET MEMORY:CUSTOM1"
  647. " TASK_WAIT_FOR_MOVEMENT 0"
  648. " TASK_REMEMBER MEMORY:INCOVER"
  649. " TASK_FACE_ENEMY 0"
  650. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
  651. " "
  652. " Interrupts"
  653. )
  654. //=========================================================
  655. // Fail Schedule
  656. //=========================================================
  657. //=========================================================
  658. // > SCHED_ASSASSIN_FAIL
  659. //=========================================================
  660. DEFINE_SCHEDULE
  661. (
  662. SCHED_ASSASSIN_FAIL,
  663. " Tasks"
  664. " TASK_STOP_MOVING 0"
  665. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  666. " TASK_WAIT_FACE_ENEMY 2"
  667. " TASK_SET_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  668. " "
  669. " Interrupts"
  670. " COND_LIGHT_DAMAGE"
  671. " COND_HEAVY_DAMAGE"
  672. " COND_CAN_RANGE_ATTACK1"
  673. " COND_CAN_RANGE_ATTACK2"
  674. " COND_CAN_MELEE_ATTACK1"
  675. " COND_HEAR_DANGER"
  676. " COND_HEAR_PLAYER"
  677. )
  678. //=========================================================
  679. // > SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1
  680. //=========================================================
  681. DEFINE_SCHEDULE
  682. (
  683. SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1,
  684. " Tasks"
  685. " TASK_STOP_MOVING 0"
  686. " TASK_WAIT 0.2"
  687. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
  688. " TASK_FIND_COVER_FROM_ENEMY 0"
  689. " TASK_RUN_PATH 0"
  690. " TASK_WAIT_FOR_MOVEMENT 0"
  691. " TASK_REMEMBER MEMORY:INCOVER"
  692. " TASK_FACE_ENEMY 0"
  693. " "
  694. " Interrupts"
  695. " COND_CAN_MELEE_ATTACK1"
  696. " COND_NEW_ENEMY"
  697. " COND_HEAR_DANGER"
  698. )
  699. //=========================================================
  700. // > SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2
  701. //=========================================================
  702. DEFINE_SCHEDULE
  703. (
  704. SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2,
  705. " Tasks"
  706. " TASK_STOP_MOVING 0"
  707. " TASK_WAIT 0.2"
  708. " TASK_FACE_ENEMY 0"
  709. " TASK_RANGE_ATTACK1 0"
  710. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
  711. " TASK_FIND_COVER_FROM_ENEMY 0"
  712. " TASK_RUN_PATH 0"
  713. " TASK_WAIT_FOR_MOVEMENT 0"
  714. " TASK_REMEMBER MEMORY:INCOVER"
  715. " TASK_FACE_ENEMY 0"
  716. " "
  717. " Interrupts"
  718. " COND_CAN_MELEE_ATTACK1"
  719. " COND_NEW_ENEMY"
  720. " COND_HEAR_DANGER"
  721. )
  722. //=========================================================
  723. // hide from the loudest sound source
  724. //=========================================================
  725. //=========================================================
  726. // > SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND
  727. //=========================================================
  728. DEFINE_SCHEDULE
  729. (
  730. SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND,
  731. " Tasks"
  732. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_MELEE_ATTACK1"
  733. " TASK_STOP_MOVING 0"
  734. " TASK_FIND_COVER_FROM_BEST_SOUND 0"
  735. " TASK_RUN_PATH 0"
  736. " TASK_WAIT_FOR_MOVEMENT 0"
  737. " TASK_REMEMBER MEMORY:INCOVER"
  738. " TASK_TURN_LEFT 179"
  739. " "
  740. " Interrupts"
  741. " COND_NEW_ENEMY"
  742. )
  743. //=========================================================
  744. // > SCHED_ASSASSIN_HIDE
  745. //=========================================================
  746. DEFINE_SCHEDULE
  747. (
  748. SCHED_ASSASSIN_HIDE,
  749. " Tasks"
  750. " TASK_STOP_MOVING 0"
  751. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  752. " TASK_WAIT 2.0"
  753. " TASK_SET_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  754. " Interrupts"
  755. " COND_NEW_ENEMY"
  756. " COND_SEE_ENEMY"
  757. " COND_SEE_FEAR"
  758. " COND_LIGHT_DAMAGE"
  759. " COND_HEAVY_DAMAGE"
  760. " COND_PROVOKED"
  761. " COND_HEAR_DANGER"
  762. )
  763. //=========================================================
  764. // > SCHED_ASSASSIN_HUNT
  765. //=========================================================
  766. DEFINE_SCHEDULE
  767. (
  768. SCHED_ASSASSIN_HUNT,
  769. " Tasks"
  770. " TASK_STOP_MOVING 0"
  771. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2"
  772. " TASK_GET_PATH_TO_ENEMY 0"
  773. " TASK_RUN_PATH 0"
  774. " TASK_WAIT_FOR_MOVEMENT 0"
  775. " Interrupts"
  776. " COND_NEW_ENEMY"
  777. " COND_CAN_RANGE_ATTACK1"
  778. " COND_HEAR_DANGER"
  779. )
  780. AI_END_CUSTOM_NPC()