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.

1174 lines
28 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
  4. // events
  5. //
  6. // $Workfile: $
  7. // $Date: $
  8. // $NoKeywords: $
  9. //=============================================================================//
  10. #include "cbase.h"
  11. #include "beam_shared.h"
  12. #include "Sprite.h"
  13. #include "ai_default.h"
  14. #include "ai_task.h"
  15. #include "ai_schedule.h"
  16. #include "ai_node.h"
  17. #include "ai_hull.h"
  18. #include "ai_hint.h"
  19. #include "ai_memory.h"
  20. #include "ai_route.h"
  21. #include "ai_motor.h"
  22. #include "hl1_npc_gargantua.h"
  23. #include "soundent.h"
  24. #include "game.h"
  25. #include "npcevent.h"
  26. #include "entitylist.h"
  27. #include "activitylist.h"
  28. #include "animation.h"
  29. #include "basecombatweapon.h"
  30. #include "IEffects.h"
  31. #include "vstdlib/random.h"
  32. #include "engine/IEngineSound.h"
  33. #include "ammodef.h"
  34. #include "shake.h"
  35. #include "decals.h"
  36. #include "particle_smokegrenade.h"
  37. #include "gib.h"
  38. #include "func_break.h"
  39. #include "hl1_shareddefs.h"
  40. extern short g_sModelIndexFireball;
  41. int gGargGibModel;
  42. //=========================================================
  43. // Gargantua Monster
  44. //=========================================================
  45. #define GARG_ATTACKDIST 120.0f
  46. // Garg animation events
  47. #define GARG_AE_SLASH_LEFT 1
  48. //#define GARG_AE_BEAM_ATTACK_RIGHT 2 // No longer used
  49. #define GARG_AE_LEFT_FOOT 3
  50. #define GARG_AE_RIGHT_FOOT 4
  51. #define GARG_AE_STOMP 5
  52. #define GARG_AE_BREATHE 6
  53. // Gargantua is immune to any damage but this
  54. #define GARG_DAMAGE ( DMG_ENERGYBEAM | DMG_CRUSH | DMG_MISSILEDEFENSE | DMG_BLAST )
  55. #define GARG_EYE_SPRITE_NAME "sprites/gargeye1.vmt"
  56. #define GARG_BEAM_SPRITE_NAME "sprites/xbeam3.vmt"
  57. #define GARG_BEAM_SPRITE2 "sprites/xbeam3.vmt"
  58. #define GARG_STOMP_SPRITE_NAME "sprites/gargeye1.vmt"
  59. #define GARG_FLAME_LENGTH 330
  60. #define GARG_GIB_MODEL "models/metalplategibs.mdl"
  61. #define STOMP_SPRITE_COUNT 10
  62. #define ATTACH_EYE 1
  63. ConVar sk_gargantua_health ( "sk_gargantua_health", "800" );
  64. ConVar sk_gargantua_dmg_slash( "sk_gargantua_dmg_slash", "10" );
  65. ConVar sk_gargantua_dmg_fire ( "sk_gargantua_dmg_fire", "3" );
  66. ConVar sk_gargantua_dmg_stomp( "sk_gargantua_dmg_stomp", "50" );
  67. enum
  68. {
  69. TASK_SOUND_ATTACK = LAST_SHARED_TASK,
  70. TASK_FLAME_SWEEP,
  71. };
  72. enum
  73. {
  74. SCHED_GARG_FLAME = LAST_SHARED_SCHEDULE,
  75. SCHED_GARG_SWIPE,
  76. SCHED_GARG_CHASE_ENEMY,
  77. SCHED_GARG_CHASE_ENEMY_FAILED,
  78. };
  79. LINK_ENTITY_TO_CLASS( monster_gargantua, CNPC_Gargantua );
  80. BEGIN_DATADESC( CNPC_Gargantua )
  81. DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
  82. DEFINE_FIELD( m_eyeBrightness, FIELD_INTEGER ),
  83. DEFINE_FIELD( m_seeTime, FIELD_TIME ),
  84. DEFINE_FIELD( m_flameTime, FIELD_TIME ),
  85. DEFINE_FIELD( m_streakTime, FIELD_TIME ),
  86. DEFINE_ARRAY( m_pFlame, FIELD_CLASSPTR, 4 ),
  87. DEFINE_FIELD( m_flameX, FIELD_FLOAT ),
  88. DEFINE_FIELD( m_flameY, FIELD_FLOAT ),
  89. DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
  90. DEFINE_FIELD( m_painSoundTime, FIELD_TIME ),
  91. END_DATADESC()
  92. static void MoveToGround( Vector *position, CBaseEntity *ignore, const Vector &mins, const Vector &maxs )
  93. {
  94. trace_t tr;
  95. // Find point on floor where enemy would stand at chasePosition
  96. Vector floor = *position;
  97. floor.z -= 1024;
  98. UTIL_TraceHull( *position, floor, mins, maxs, MASK_NPCSOLID, ignore, COLLISION_GROUP_NONE, &tr );
  99. if ( tr.fraction < 1 )
  100. {
  101. position->z = tr.endpos.z;
  102. }
  103. }
  104. class CStomp : public CBaseEntity
  105. {
  106. DECLARE_CLASS( CStomp, CBaseEntity );
  107. public:
  108. DECLARE_DATADESC();
  109. virtual void Precache();
  110. void Spawn( void );
  111. void Think( void );
  112. static CStomp *StompCreate( Vector &origin, Vector &end, float speed, CBaseEntity* pOwner );
  113. private:
  114. Vector m_vecMoveDir;
  115. float m_flScale;
  116. float m_flSpeed;
  117. unsigned int m_uiFramerate;
  118. float m_flDmgTime;
  119. CBaseEntity* m_pOwner;
  120. // UNDONE: re-use this sprite list instead of creating new ones all the time
  121. // CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
  122. };
  123. BEGIN_DATADESC(CStomp)
  124. DEFINE_FIELD( m_vecMoveDir, FIELD_VECTOR ),
  125. DEFINE_FIELD( m_flScale, FIELD_FLOAT ),
  126. DEFINE_FIELD( m_flSpeed, FIELD_FLOAT ),
  127. DEFINE_FIELD( m_uiFramerate, FIELD_INTEGER ),
  128. DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
  129. DEFINE_FIELD( m_pOwner, FIELD_CLASSPTR ),
  130. END_DATADESC()
  131. LINK_ENTITY_TO_CLASS( garg_stomp, CStomp );
  132. CStomp *CStomp::StompCreate( Vector &origin, Vector &end, float speed, CBaseEntity* pOwner )
  133. {
  134. CStomp *pStomp = (CStomp*)CreateEntityByName( "garg_stomp" );
  135. pStomp->SetAbsOrigin( origin );
  136. Vector dir = (end - origin);
  137. // pStomp->m_flScale = dir.Length();
  138. pStomp->m_flScale = 2048;
  139. pStomp->m_vecMoveDir = dir;
  140. VectorNormalize( pStomp->m_vecMoveDir );
  141. pStomp->m_flSpeed = speed;
  142. pStomp->m_pOwner = pOwner;
  143. pStomp->Spawn();
  144. return pStomp;
  145. }
  146. void CStomp::Precache()
  147. {
  148. BaseClass::Precache();
  149. PrecacheScriptSound( "Garg.Stomp" );
  150. PrecacheModel( GARG_STOMP_SPRITE_NAME );
  151. }
  152. void CStomp::Spawn( void )
  153. {
  154. Precache();
  155. SetNextThink( gpGlobals->curtime );
  156. SetClassname( "garg_stomp" );
  157. m_flDmgTime = gpGlobals->curtime;
  158. m_uiFramerate = 30;
  159. // pev->rendermode = kRenderTransTexture;
  160. // SetBrightness( 0 );
  161. CPASAttenuationFilter filter( this );
  162. EmitSound( filter, entindex(), "Garg.Stomp" );
  163. }
  164. #define STOMP_INTERVAL 0.025
  165. void CStomp::Think( void )
  166. {
  167. trace_t tr;
  168. SetNextThink( gpGlobals->curtime + 0.1 );
  169. // Do damage for this frame
  170. Vector vecStart = GetAbsOrigin();
  171. vecStart.z += 30;
  172. Vector vecEnd = vecStart + (m_vecMoveDir * m_flSpeed * gpGlobals->frametime);
  173. UTIL_TraceHull( vecStart, vecEnd, Vector(-32, -32, -32), Vector(32, 32, 32), MASK_SOLID, m_pOwner, COLLISION_GROUP_NONE, &tr );
  174. // NDebugOverlay::Line( vecStart, vecEnd, 0, 255, 0, false, 10.0f );
  175. if ( tr.m_pEnt )
  176. {
  177. CBaseEntity *pEntity = tr.m_pEnt;
  178. CTakeDamageInfo info( this, this, 50, DMG_SONIC );
  179. CalculateMeleeDamageForce( &info, m_vecMoveDir, tr.endpos );
  180. pEntity->TakeDamage( info );
  181. }
  182. // Accelerate the effect
  183. m_flSpeed += (gpGlobals->frametime) * m_uiFramerate;
  184. m_uiFramerate += (gpGlobals->frametime) * 2000;
  185. // Move and spawn trails
  186. if ( gpGlobals->curtime - m_flDmgTime > 0.2f )
  187. {
  188. m_flDmgTime = gpGlobals->curtime - 0.2f;
  189. }
  190. while ( gpGlobals->curtime - m_flDmgTime > STOMP_INTERVAL )
  191. {
  192. SetAbsOrigin( GetAbsOrigin() + m_vecMoveDir * m_flSpeed * STOMP_INTERVAL );
  193. for ( int i = 0; i < 2; i++ )
  194. {
  195. CSprite *pSprite = CSprite::SpriteCreate( GARG_STOMP_SPRITE_NAME, GetAbsOrigin(), TRUE );
  196. if ( pSprite )
  197. {
  198. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,500), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  199. pSprite->SetAbsOrigin( tr.endpos );
  200. // pSprite->pev->velocity = Vector(RandomFloat(-200,200),RandomFloat(-200,200),175);
  201. pSprite->SetNextThink( gpGlobals->curtime + 0.3 );
  202. pSprite->SetThink( &CSprite::SUB_Remove );
  203. pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxFadeFast );
  204. }
  205. g_pEffects->EnergySplash( tr.endpos, tr.plane.normal );
  206. }
  207. m_flDmgTime += STOMP_INTERVAL;
  208. // Scale has the "life" of this effect
  209. m_flScale -= STOMP_INTERVAL * m_flSpeed;
  210. if ( m_flScale <= 0 )
  211. {
  212. // Life has run out
  213. UTIL_Remove(this);
  214. CPASAttenuationFilter filter( this );
  215. StopSound( entindex(), CHAN_STATIC, "Garg.Stomp" );
  216. }
  217. }
  218. }
  219. //=========================================================================
  220. // Gargantua
  221. //=========================================================================
  222. //=========================================================
  223. // Spawn
  224. //=========================================================
  225. void CNPC_Gargantua::Spawn()
  226. {
  227. Precache( );
  228. SetModel( "models/garg.mdl" );
  229. SetNavType(NAV_GROUND);
  230. SetSolid( SOLID_BBOX );
  231. AddSolidFlags( FSOLID_NOT_STANDABLE );
  232. SetMoveType( MOVETYPE_STEP );
  233. Vector vecSurroundingMins( -80, -80, 0 );
  234. Vector vecSurroundingMaxs( 80, 80, 214 );
  235. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
  236. m_bloodColor = BLOOD_COLOR_GREEN;
  237. m_iHealth = sk_gargantua_health.GetFloat();
  238. SetViewOffset( Vector ( 0, 0, 96 ) );// taken from mdl file
  239. m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result )
  240. m_NPCState = NPC_STATE_NONE;
  241. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  242. CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
  243. SetHullType( HULL_LARGE );
  244. SetHullSizeNormal();
  245. m_pEyeGlow = CSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, GetAbsOrigin(), FALSE );
  246. m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
  247. m_pEyeGlow->SetAttachment( this, 1 );
  248. EyeOff();
  249. m_seeTime = gpGlobals->curtime + 5;
  250. m_flameTime = gpGlobals->curtime + 2;
  251. NPCInit();
  252. BaseClass::Spawn();
  253. // Give garg a healthy free knowledge.
  254. GetEnemies()->SetFreeKnowledgeDuration( 59.0f );
  255. }
  256. //=========================================================
  257. // Precache - precaches all resources this monster needs
  258. //=========================================================
  259. void CNPC_Gargantua::Precache()
  260. {
  261. PrecacheModel("models/garg.mdl");
  262. PrecacheModel( GARG_EYE_SPRITE_NAME );
  263. PrecacheModel( GARG_BEAM_SPRITE_NAME );
  264. PrecacheModel( GARG_BEAM_SPRITE2 );
  265. //gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME );
  266. gGargGibModel = PrecacheModel( GARG_GIB_MODEL );
  267. PrecacheScriptSound( "Garg.AttackHit" );
  268. PrecacheScriptSound( "Garg.AttackMiss" );
  269. PrecacheScriptSound( "Garg.Footstep" );
  270. PrecacheScriptSound( "Garg.Breath" );
  271. PrecacheScriptSound( "Garg.Attack" );
  272. PrecacheScriptSound( "Garg.Pain" );
  273. PrecacheScriptSound( "Garg.BeamAttackOn" );
  274. PrecacheScriptSound( "Garg.BeamAttackRun" );
  275. PrecacheScriptSound( "Garg.BeamAttackOff" );
  276. PrecacheScriptSound( "Garg.StompSound" );
  277. }
  278. Class_T CNPC_Gargantua::Classify ( void )
  279. {
  280. return CLASS_ALIEN_MONSTER;
  281. }
  282. void CNPC_Gargantua::PrescheduleThink( void )
  283. {
  284. if ( !HasCondition( COND_SEE_ENEMY ) )
  285. {
  286. m_seeTime = gpGlobals->curtime + 5;
  287. EyeOff();
  288. }
  289. else
  290. {
  291. EyeOn( 200 );
  292. }
  293. EyeUpdate();
  294. }
  295. float CNPC_Gargantua::MaxYawSpeed ( void )
  296. {
  297. float ys = 60;
  298. switch ( GetActivity() )
  299. {
  300. case ACT_IDLE:
  301. ys = 60;
  302. break;
  303. case ACT_TURN_LEFT:
  304. case ACT_TURN_RIGHT:
  305. ys = 180;
  306. break;
  307. case ACT_WALK:
  308. case ACT_RUN:
  309. ys = 60;
  310. break;
  311. default:
  312. ys = 60;
  313. break;
  314. }
  315. return ys;
  316. }
  317. int CNPC_Gargantua::MeleeAttack1Conditions( float flDot, float flDist )
  318. {
  319. if (flDot >= 0.7)
  320. {
  321. if ( flDist <= GARG_ATTACKDIST )
  322. {
  323. return COND_CAN_MELEE_ATTACK1;
  324. }
  325. }
  326. return COND_NONE;
  327. }
  328. // Flame thrower madness!
  329. int CNPC_Gargantua::MeleeAttack2Conditions( float flDot, float flDist )
  330. {
  331. if ( gpGlobals->curtime > m_flameTime )
  332. {
  333. if ( flDot >= 0.8 )
  334. {
  335. if ( flDist > GARG_ATTACKDIST )
  336. {
  337. if ( flDist <= GARG_FLAME_LENGTH )
  338. return COND_CAN_MELEE_ATTACK2;
  339. }
  340. }
  341. }
  342. return COND_NONE;
  343. }
  344. //=========================================================
  345. // CheckRangeAttack1
  346. // flDot is the cos of the angle of the cone within which
  347. // the attack can occur.
  348. //=========================================================
  349. //
  350. // Stomp attack
  351. //
  352. //=========================================================
  353. int CNPC_Gargantua::RangeAttack1Conditions( float flDot, float flDist )
  354. {
  355. if ( gpGlobals->curtime > m_seeTime )
  356. {
  357. if ( flDot >= 0.7 )
  358. {
  359. if ( flDist > GARG_ATTACKDIST )
  360. {
  361. return COND_CAN_RANGE_ATTACK1;
  362. }
  363. }
  364. }
  365. return COND_NONE;
  366. }
  367. //=========================================================
  368. // CheckTraceHullAttack - expects a length to trace, amount
  369. // of damage to do, and damage type. Returns a pointer to
  370. // the damaged entity in case the monster wishes to do
  371. // other stuff to the victim (punchangle, etc)
  372. // Used for many contact-range melee attacks. Bites, claws, etc.
  373. // Overridden for Gargantua because his swing starts lower as
  374. // a percentage of his height (otherwise he swings over the
  375. // players head)
  376. //=========================================================
  377. CBaseEntity* CNPC_Gargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType)
  378. {
  379. trace_t tr;
  380. Vector vForward, vUp;
  381. AngleVectors( GetAbsAngles(), &vForward, NULL, &vUp );
  382. Vector vecStart = GetAbsOrigin();
  383. vecStart.z += 64;
  384. Vector vecEnd = vecStart + ( vForward * flDist) - ( vUp * flDist * 0.3);
  385. //UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
  386. UTIL_TraceEntity( this, GetAbsOrigin(), vecEnd, MASK_SOLID, &tr );
  387. if ( tr.m_pEnt )
  388. {
  389. CBaseEntity *pEntity = tr.m_pEnt;
  390. if ( iDamage > 0 )
  391. {
  392. CTakeDamageInfo info( this, this, iDamage, iDmgType );
  393. CalculateMeleeDamageForce( &info, vForward, tr.endpos );
  394. pEntity->TakeDamage( info );
  395. }
  396. return pEntity;
  397. }
  398. return NULL;
  399. }
  400. void CNPC_Gargantua::HandleAnimEvent( animevent_t *pEvent )
  401. {
  402. CPASAttenuationFilter filter( this );
  403. switch( pEvent->event )
  404. {
  405. case GARG_AE_SLASH_LEFT:
  406. {
  407. // HACKHACK!!!
  408. CBaseEntity *pHurt = GargantuaCheckTraceHullAttack( GARG_ATTACKDIST + 10.0, sk_gargantua_dmg_slash.GetFloat(), DMG_SLASH );
  409. if (pHurt)
  410. {
  411. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  412. {
  413. pHurt->ViewPunch( QAngle( -30, -30, 30 ) );
  414. Vector vRight;
  415. AngleVectors( GetAbsAngles(), NULL, &vRight, NULL );
  416. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - vRight * 100 );
  417. }
  418. EmitSound( filter, entindex(), "Garg.AttackHit" );
  419. }
  420. else // Play a random attack miss sound
  421. {
  422. EmitSound( filter, entindex(),"Garg.AttackMiss" );
  423. }
  424. }
  425. break;
  426. case GARG_AE_RIGHT_FOOT:
  427. case GARG_AE_LEFT_FOOT:
  428. UTIL_ScreenShake( GetAbsOrigin(), 4.0, 3.0, 1.0, 1500, SHAKE_START );
  429. EmitSound( filter, entindex(), "Garg.Footstep" );
  430. break;
  431. case GARG_AE_STOMP:
  432. StompAttack();
  433. m_seeTime = gpGlobals->curtime + 12;
  434. break;
  435. case GARG_AE_BREATHE:
  436. EmitSound( filter, entindex(), "Garg.Breath" );
  437. break;
  438. default:
  439. BaseClass::HandleAnimEvent(pEvent);
  440. break;
  441. }
  442. }
  443. int CNPC_Gargantua::TranslateSchedule( int scheduleType )
  444. {
  445. //TEMP TEMP
  446. if ( FlameIsOn() )
  447. FlameDestroy();
  448. switch( scheduleType )
  449. {
  450. case SCHED_MELEE_ATTACK2:
  451. return SCHED_GARG_FLAME;
  452. case SCHED_MELEE_ATTACK1:
  453. return SCHED_GARG_SWIPE;
  454. case SCHED_CHASE_ENEMY:
  455. return SCHED_GARG_CHASE_ENEMY;
  456. case SCHED_CHASE_ENEMY_FAILED:
  457. return SCHED_GARG_CHASE_ENEMY_FAILED;
  458. case SCHED_ALERT_STAND:
  459. return SCHED_CHASE_ENEMY;
  460. break;
  461. }
  462. return BaseClass::TranslateSchedule( scheduleType );
  463. }
  464. void CNPC_Gargantua::StartTask( const Task_t *pTask )
  465. {
  466. switch ( pTask->iTask )
  467. {
  468. case TASK_FLAME_SWEEP:
  469. //TEMP TEMP
  470. FlameCreate();
  471. m_flWaitFinished = gpGlobals->curtime + pTask->flTaskData;
  472. m_flameTime = gpGlobals->curtime + 6;
  473. m_flameX = 0;
  474. m_flameY = 0;
  475. break;
  476. case TASK_SOUND_ATTACK:
  477. if ( random->RandomInt(0,100) < 30 )
  478. {
  479. CPASAttenuationFilter filter( this );
  480. EmitSound( filter, entindex(), "Garg.Attack" );
  481. }
  482. TaskComplete();
  483. break;
  484. case TASK_DIE:
  485. m_flWaitFinished = gpGlobals->curtime + 1.6;
  486. DeathEffect();
  487. // FALL THROUGH
  488. default:
  489. BaseClass::StartTask( pTask );
  490. break;
  491. }
  492. }
  493. bool CNPC_Gargantua::ShouldGib( const CTakeDamageInfo &info )
  494. {
  495. return false;
  496. }
  497. //=========================================================
  498. // RunTask
  499. //=========================================================
  500. void CNPC_Gargantua::RunTask( const Task_t *pTask )
  501. {
  502. switch ( pTask->iTask )
  503. {
  504. case TASK_DIE:
  505. if ( gpGlobals->curtime > m_flWaitFinished )
  506. {
  507. //TEMP TEMP
  508. m_nRenderFX = kRenderFxExplode;
  509. SetRenderColor( 255, 0, 0 , 255 );
  510. StopAnimation();
  511. SetNextThink( gpGlobals->curtime + 0.15 );
  512. SetThink( &CBaseEntity::SUB_Remove );
  513. int i;
  514. int parts = modelinfo->GetModelFrameCount( modelinfo->GetModel( gGargGibModel ) );
  515. for ( i = 0; i < 10; i++ )
  516. {
  517. CGib *pGib = CREATE_ENTITY( CGib, "gib" );
  518. pGib->Spawn( GARG_GIB_MODEL);
  519. int bodyPart = 0;
  520. if ( parts > 1 )
  521. bodyPart = random->RandomInt( 0, parts-1 );
  522. pGib->SetBodygroup( 0, bodyPart );
  523. pGib->SetBloodColor( BLOOD_COLOR_YELLOW );
  524. pGib->m_material = matNone;
  525. pGib->SetAbsOrigin( GetAbsOrigin() );
  526. pGib->SetAbsVelocity( UTIL_RandomBloodVector() * random->RandomFloat( 300, 500 ) );
  527. pGib->SetNextThink( gpGlobals->curtime + 1.25 );
  528. pGib->SetThink( &CBaseEntity::SUB_FadeOut );
  529. }
  530. Vector vecSize = Vector( 200, 200, 128 );
  531. CPVSFilter filter( GetAbsOrigin() );
  532. te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle, vecSize, vec3_origin,
  533. gGargGibModel, 200, 50, 3.0, BREAK_FLESH );
  534. return;
  535. }
  536. else
  537. BaseClass::RunTask( pTask );
  538. break;
  539. case TASK_FLAME_SWEEP:
  540. if ( gpGlobals->curtime > m_flWaitFinished )
  541. {
  542. //TEMP TEMP
  543. FlameDestroy();
  544. TaskComplete();
  545. FlameControls( 0, 0 );
  546. SetBoneController( 0, 0 );
  547. SetBoneController( 1, 0 );
  548. }
  549. else
  550. {
  551. bool cancel = false;
  552. QAngle angles = QAngle( 0, 0, 0 );
  553. //TEMP TEMP
  554. FlameUpdate();
  555. CBaseEntity *pEnemy = GetEnemy();
  556. if ( pEnemy )
  557. {
  558. Vector org = GetAbsOrigin();
  559. org.z += 64;
  560. Vector dir = pEnemy->BodyTarget(org) - org;
  561. VectorAngles( dir, angles );
  562. angles.x = -angles.x;
  563. angles.y -= GetAbsAngles().y;
  564. if ( dir.Length() > 400 )
  565. cancel = true;
  566. }
  567. if ( fabs(angles.y) > 60 )
  568. cancel = true;
  569. if ( cancel )
  570. {
  571. m_flWaitFinished -= 0.5;
  572. m_flameTime -= 0.5;
  573. }
  574. //TEMP TEMP
  575. //FlameControls( angles.x + 2 * sin(gpGlobals->curtime*8), angles.y + 28 * sin(gpGlobals->curtime*8.5) );
  576. FlameControls( angles.x, angles.y );
  577. }
  578. break;
  579. default:
  580. BaseClass::RunTask( pTask );
  581. break;
  582. }
  583. }
  584. void CNPC_Gargantua::FlameCreate( void )
  585. {
  586. int i;
  587. Vector posGun;
  588. QAngle angleGun;
  589. trace_t trace;
  590. Vector vForward;
  591. AngleVectors( GetAbsAngles(), &vForward );
  592. for ( i = 0; i < 4; i++ )
  593. {
  594. if ( i < 2 )
  595. m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 24.0 );
  596. else
  597. m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE2, 14.0 );
  598. if ( m_pFlame[i] )
  599. {
  600. int attach = i%2;
  601. // attachment is 0 based in GetAttachment
  602. GetAttachment( attach+1, posGun, angleGun );
  603. Vector vecEnd = ( vForward * GARG_FLAME_LENGTH) + posGun;
  604. //UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace );
  605. UTIL_TraceLine ( posGun, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace);
  606. // NDebugOverlay::Line( posGun, vecEnd, 255, 255, 255, false, 10.0f );
  607. m_pFlame[i]->PointEntInit( trace.endpos, this );
  608. if ( i < 2 )
  609. m_pFlame[i]->SetColor( 255, 130, 90 );
  610. else
  611. m_pFlame[i]->SetColor( 0, 120, 255 );
  612. m_pFlame[i]->SetBrightness( 190 );
  613. m_pFlame[i]->SetBeamFlags( FBEAM_SHADEIN );
  614. m_pFlame[i]->SetScrollRate( 20 );
  615. // attachment is 1 based in SetEndAttachment
  616. m_pFlame[i]->SetEndAttachment( attach + 2 );
  617. CSoundEnt::InsertSound( SOUND_COMBAT, posGun, 384, 0.3 );
  618. }
  619. }
  620. CPASAttenuationFilter filter4( this );
  621. EmitSound( filter4, entindex(), "Garg.BeamAttackOn" );
  622. EmitSound( filter4, entindex(), "Garg.BeamAttackRun" );
  623. }
  624. void CNPC_Gargantua::FlameControls( float angleX, float angleY )
  625. {
  626. if ( angleY < -180 )
  627. angleY += 360;
  628. else if ( angleY > 180 )
  629. angleY -= 360;
  630. if ( angleY < -45 )
  631. angleY = -45;
  632. else if ( angleY > 45 )
  633. angleY = 45;
  634. m_flameX = UTIL_ApproachAngle( angleX, m_flameX, 4 );
  635. m_flameY = UTIL_ApproachAngle( angleY, m_flameY, 8 );
  636. SetBoneController( 0, m_flameY );
  637. SetBoneController( 1, m_flameX );
  638. }
  639. void CNPC_Gargantua::FlameUpdate( void )
  640. {
  641. int i;
  642. static float offset[2] = { 60, -60 };
  643. trace_t trace;
  644. Vector vecStart;
  645. QAngle angleGun;
  646. BOOL streaks = FALSE;
  647. Vector vForward;
  648. for ( i = 0; i < 2; i++ )
  649. {
  650. if ( m_pFlame[i] )
  651. {
  652. QAngle vecAim = GetAbsAngles();
  653. vecAim.x += -m_flameX;
  654. vecAim.y += m_flameY;
  655. AngleVectors( vecAim, &vForward );
  656. GetAttachment( i + 2, vecStart, angleGun );
  657. Vector vecEnd = vecStart + ( vForward * GARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right;
  658. UTIL_TraceLine ( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace);
  659. m_pFlame[i]->SetStartPos( trace.endpos );
  660. m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.endpos * 0.4) );
  661. if ( trace.fraction != 1.0 && gpGlobals->curtime > m_streakTime )
  662. {
  663. g_pEffects->Sparks( trace.endpos, 1, 1, &trace.plane.normal );
  664. streaks = TRUE;
  665. UTIL_DecalTrace( &trace, "SmallScorch" );
  666. }
  667. // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN );
  668. FlameDamage( vecStart, trace.endpos, this, this, sk_gargantua_dmg_fire.GetFloat(), CLASS_ALIEN_MONSTER, DMG_BURN );
  669. CBroadcastRecipientFilter filter;
  670. GetAttachment(i + 2, vecStart, angleGun);
  671. te->DynamicLight( filter, 0.0, &vecStart, 255, 0, 0, 0, 48, 0.2, 150 );
  672. }
  673. }
  674. if ( streaks )
  675. m_streakTime = gpGlobals->curtime;
  676. }
  677. void CNPC_Gargantua::FlameDamage( Vector vecStart, Vector vecEnd, CBaseEntity *pevInflictor, CBaseEntity *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType )
  678. {
  679. CBaseEntity *pEntity = NULL;
  680. trace_t tr;
  681. float flAdjustedDamage;
  682. Vector vecSpot;
  683. Vector vecMid = (vecStart + vecEnd) * 0.5;
  684. // float searchRadius = (vecStart - vecMid).Length();
  685. float searchRadius = GARG_FLAME_LENGTH;
  686. float maxDamageRadius = searchRadius / 2.0;
  687. Vector vecAim = (vecEnd - vecStart);
  688. VectorNormalize( vecAim );
  689. // iterate on all entities in the vicinity.
  690. while ((pEntity = gEntList.FindEntityInSphere( pEntity, GetAbsOrigin(), searchRadius )) != NULL)
  691. {
  692. if ( pEntity->m_takedamage != DAMAGE_NO )
  693. {
  694. // UNDONE: this should check a damage mask, not an ignore
  695. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  696. {// houndeyes don't hurt other houndeyes with their attack
  697. continue;
  698. }
  699. vecSpot = pEntity->BodyTarget( vecMid );
  700. float dist = DotProduct( vecAim, vecSpot - vecMid );
  701. if (dist > searchRadius)
  702. dist = searchRadius;
  703. else if (dist < -searchRadius)
  704. dist = searchRadius;
  705. Vector vecSrc = vecMid + dist * vecAim;
  706. UTIL_TraceLine ( vecStart, vecSpot, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  707. // NDebugOverlay::Line( vecStart, vecSpot, 0, 255, 0, false, 10.0f );
  708. if ( tr.fraction == 1.0 || tr.m_pEnt == pEntity )
  709. {// the explosion can 'see' this entity, so hurt them!
  710. // decrease damage for an ent that's farther from the flame.
  711. dist = ( vecSrc - tr.endpos ).Length();
  712. if (dist > maxDamageRadius)
  713. {
  714. flAdjustedDamage = flDamage - (dist - maxDamageRadius) * 0.4;
  715. if (flAdjustedDamage <= 0)
  716. continue;
  717. }
  718. else
  719. {
  720. flAdjustedDamage = flDamage;
  721. }
  722. // ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
  723. if (tr.fraction != 1.0)
  724. {
  725. ClearMultiDamage( );
  726. Vector vDir = (tr.endpos - vecSrc);
  727. VectorNormalize( vDir );
  728. CTakeDamageInfo info( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
  729. CalculateMeleeDamageForce( &info, vDir, tr.endpos );
  730. pEntity->DispatchTraceAttack( info, vDir, &tr );
  731. ApplyMultiDamage();
  732. }
  733. else
  734. {
  735. pEntity->TakeDamage( CTakeDamageInfo( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ) );
  736. }
  737. }
  738. }
  739. }
  740. }
  741. void CNPC_Gargantua::FlameDestroy( void )
  742. {
  743. int i;
  744. CPASAttenuationFilter filter4( this );
  745. EmitSound( filter4, entindex(), "Garg.BeamAttackOff" );
  746. for ( i = 0; i < 4; i++ )
  747. {
  748. if ( m_pFlame[i] )
  749. {
  750. UTIL_Remove( m_pFlame[i] );
  751. m_pFlame[i] = NULL;
  752. }
  753. }
  754. }
  755. void CNPC_Gargantua::EyeOn( int level )
  756. {
  757. m_eyeBrightness = level;
  758. }
  759. void CNPC_Gargantua::EyeOff( void )
  760. {
  761. m_eyeBrightness = 0;
  762. }
  763. void CNPC_Gargantua::EyeUpdate( void )
  764. {
  765. if ( m_pEyeGlow )
  766. {
  767. m_pEyeGlow->SetBrightness( UTIL_Approach( m_eyeBrightness, m_pEyeGlow->GetBrightness(), 26 ), 0.5f );
  768. if ( m_pEyeGlow->GetBrightness() == 0 )
  769. {
  770. m_pEyeGlow->AddEffects( EF_NODRAW );
  771. }
  772. else
  773. {
  774. m_pEyeGlow->RemoveEffects( EF_NODRAW );
  775. }
  776. }
  777. }
  778. void CNPC_Gargantua::StompAttack( void )
  779. {
  780. trace_t trace;
  781. Vector vecForward;
  782. AngleVectors(GetAbsAngles(), &vecForward );
  783. Vector vecStart = GetAbsOrigin() + Vector(0,0,60) + 35 * vecForward;
  784. CBaseEntity* pPlayer = GetEnemy();
  785. if ( !pPlayer )
  786. return;
  787. Vector vecAim = pPlayer->GetAbsOrigin() - GetAbsOrigin();
  788. VectorNormalize( vecAim );
  789. Vector vecEnd = (vecAim * 1024) + vecStart;
  790. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
  791. // NDebugOverlay::Line( vecStart, vecEnd, 255, 0, 0, false, 10.0f );
  792. CStomp::StompCreate( vecStart, trace.endpos, 0, this );
  793. UTIL_ScreenShake( GetAbsOrigin(), 12.0, 100.0, 2.0, 1000, SHAKE_START );
  794. CPASAttenuationFilter filter( this );
  795. EmitSound( filter, entindex(), "Garg.StompSound" );
  796. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,20), MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
  797. if ( trace.fraction < 1.0 )
  798. {
  799. UTIL_DecalTrace( &trace, "SmallScorch" );
  800. }
  801. }
  802. void CNPC_Gargantua::DeathEffect( void )
  803. {
  804. int i;
  805. Vector vForward;
  806. AngleVectors( GetAbsAngles(), &vForward );
  807. Vector deathPos = GetAbsOrigin() + vForward * 100;
  808. Vector position = GetAbsOrigin();
  809. position.z += 32;
  810. CPASFilter filter( GetAbsOrigin() );
  811. for ( i = 0; i < 7; i++)
  812. {
  813. te->Explosion( filter, i * 0.2, &GetAbsOrigin(), g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
  814. position.z += 15;
  815. }
  816. UTIL_Smoke(GetAbsOrigin(),random->RandomInt(10, 15), 10);
  817. UTIL_ScreenShake( GetAbsOrigin(), 25.0, 100.0, 5.0, 1000, SHAKE_START );
  818. }
  819. void CNPC_Gargantua::Event_Killed( const CTakeDamageInfo &info )
  820. {
  821. EyeOff();
  822. UTIL_Remove( m_pEyeGlow );
  823. m_pEyeGlow = NULL;
  824. BaseClass::Event_Killed( info );
  825. m_takedamage = DAMAGE_NO;
  826. }
  827. void CNPC_Gargantua::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  828. {
  829. CTakeDamageInfo subInfo = info;
  830. if ( !IsAlive() )
  831. {
  832. BaseClass::TraceAttack( subInfo, vecDir, ptr, pAccumulator );
  833. return;
  834. }
  835. // UNDONE: Hit group specific damage?
  836. if ( subInfo.GetDamageType() & ( GARG_DAMAGE | DMG_BLAST ) )
  837. {
  838. if ( m_painSoundTime < gpGlobals->curtime )
  839. {
  840. CPASAttenuationFilter filter( this );
  841. EmitSound( filter, entindex(), "Garg.Pain" );
  842. m_painSoundTime = gpGlobals->curtime + random->RandomFloat( 2.5, 4 );
  843. }
  844. }
  845. int bitsDamageType = subInfo.GetDamageType();
  846. bitsDamageType &= GARG_DAMAGE;
  847. subInfo.SetDamageType( bitsDamageType );
  848. if ( subInfo.GetDamageType() == 0 )
  849. {
  850. if ( m_flDmgTime != gpGlobals->curtime || (random->RandomInt( 0, 100 ) < 20) )
  851. {
  852. g_pEffects->Ricochet(ptr->endpos, -vecDir );
  853. m_flDmgTime = gpGlobals->curtime;
  854. }
  855. subInfo.SetDamage( 0 );
  856. }
  857. BaseClass::TraceAttack( subInfo, vecDir, ptr, pAccumulator );
  858. }
  859. int CNPC_Gargantua::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  860. {
  861. if( GetState() == NPC_STATE_SCRIPT )
  862. {
  863. // Invulnerable while scripted. This fixes the problem where garg wouldn't
  864. // explode in C2A1 because for some reason he wouldn't die while scripted, he'd
  865. // only freeze in place. Now he's just immune until he gets to the script and stops.
  866. return 0;
  867. }
  868. CTakeDamageInfo subInfo = info;
  869. float flDamage = subInfo.GetDamage();
  870. if ( IsAlive() )
  871. {
  872. if ( !(subInfo.GetDamageType() & GARG_DAMAGE) )
  873. {
  874. flDamage *= 0.01;
  875. subInfo.SetDamage( flDamage );
  876. }
  877. if ( subInfo.GetDamageType() & DMG_BLAST )
  878. {
  879. SetCondition( COND_LIGHT_DAMAGE );
  880. }
  881. }
  882. return BaseClass::OnTakeDamage_Alive( subInfo );
  883. }
  884. AI_BEGIN_CUSTOM_NPC( monster_gargantua, CNPC_Gargantua )
  885. DECLARE_TASK ( TASK_SOUND_ATTACK )
  886. DECLARE_TASK ( TASK_FLAME_SWEEP )
  887. //=========================================================
  888. // > SCHED_GARG_FLAME
  889. //=========================================================
  890. DEFINE_SCHEDULE
  891. (
  892. SCHED_GARG_FLAME,
  893. " Tasks"
  894. " TASK_STOP_MOVING 0"
  895. " TASK_FACE_ENEMY 0"
  896. " TASK_SOUND_ATTACK 0"
  897. " TASK_SET_ACTIVITY ACTIVITY:ACT_MELEE_ATTACK2"
  898. " TASK_FLAME_SWEEP 4.5"
  899. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  900. )
  901. //=========================================================
  902. // > SCHED_GARG_SWIPE
  903. //=========================================================
  904. DEFINE_SCHEDULE
  905. (
  906. SCHED_GARG_SWIPE,
  907. " Tasks"
  908. " TASK_STOP_MOVING 0"
  909. " TASK_FACE_ENEMY 0"
  910. " TASK_MELEE_ATTACK1 0"
  911. " "
  912. " Interrupts"
  913. " COND_CAN_MELEE_ATTACK2"
  914. )
  915. DEFINE_SCHEDULE
  916. (
  917. SCHED_GARG_CHASE_ENEMY,
  918. " Tasks"
  919. " TASK_STOP_MOVING 0"
  920. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_GARG_CHASE_ENEMY_FAILED"
  921. " TASK_GET_CHASE_PATH_TO_ENEMY 300"
  922. " TASK_RUN_PATH 0"
  923. " TASK_WAIT_FOR_MOVEMENT 0"
  924. " TASK_FACE_ENEMY 0"
  925. ""
  926. " Interrupts"
  927. " COND_NEW_ENEMY"
  928. " COND_ENEMY_DEAD"
  929. " COND_ENEMY_UNREACHABLE"
  930. " COND_CAN_RANGE_ATTACK1"
  931. " COND_CAN_MELEE_ATTACK1"
  932. " COND_CAN_RANGE_ATTACK2"
  933. " COND_CAN_MELEE_ATTACK2"
  934. " COND_TOO_CLOSE_TO_ATTACK"
  935. " COND_LOST_ENEMY"
  936. );
  937. DEFINE_SCHEDULE
  938. (
  939. SCHED_GARG_CHASE_ENEMY_FAILED,
  940. " Tasks"
  941. " TASK_SET_ROUTE_SEARCH_TIME 2" // Spend 2 seconds trying to build a path if stuck
  942. " TASK_GET_PATH_TO_RANDOM_NODE 180"
  943. " TASK_WALK_PATH 0"
  944. " TASK_WAIT_FOR_MOVEMENT 0"
  945. ""
  946. " Interrupts"
  947. " COND_NEW_ENEMY"
  948. " COND_ENEMY_DEAD"
  949. " COND_CAN_RANGE_ATTACK1"
  950. " COND_CAN_MELEE_ATTACK1"
  951. " COND_CAN_RANGE_ATTACK2"
  952. " COND_CAN_MELEE_ATTACK2"
  953. );
  954. AI_END_CUSTOM_NPC()