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.

1745 lines
42 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 "Sprite.h"
  30. #include "hl1_ai_basenpc.h"
  31. #include "ai_senses.h"
  32. #include "Sprite.h"
  33. #include "beam_shared.h"
  34. #include "logicrelay.h"
  35. #include "ai_navigator.h"
  36. #define N_SCALE 15
  37. #define N_SPHERES 20
  38. ConVar sk_nihilanth_health( "sk_nihilanth_health", "800" );
  39. ConVar sk_nihilanth_zap( "sk_nihilanth_zap", "30" );
  40. class CNPC_Nihilanth : public CHL1BaseNPC
  41. {
  42. DECLARE_CLASS( CNPC_Nihilanth, CHL1BaseNPC );
  43. public:
  44. void Spawn( void );
  45. void Precache( void );
  46. Class_T Classify( void ) { return CLASS_ALIEN_MILITARY; };
  47. /* void Killed( entvars_t *pevAttacker, int iGib );
  48. void GibMonster( void );
  49. void TargetSphere( USE_TYPE useType, float value );
  50. CBaseEntity *RandomTargetname( const char *szName );
  51. void MakeFriend( Vector vecPos );
  52. int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
  53. void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
  54. */
  55. int OnTakeDamage_Alive( const CTakeDamageInfo &info );
  56. void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  57. bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
  58. void PainSound( const CTakeDamageInfo &info );
  59. void DeathSound( const CTakeDamageInfo &info );
  60. void StartupThink( void );
  61. void NullThink( void );
  62. void HuntThink( void );
  63. void DyingThink( void );
  64. void Flight( void );
  65. void NextActivity( void );
  66. void FloatSequence( void );
  67. void HandleAnimEvent( animevent_t *pEvent );
  68. bool EmitSphere( void );
  69. void ShootBalls( void );
  70. bool AbsorbSphere( void );
  71. void MakeFriend( Vector vecStart );
  72. void InputTurnBabyOn( inputdata_t &inputdata );
  73. void InputTurnBabyOff( inputdata_t &inputdata );
  74. float m_flForce;
  75. float m_flNextPainSound;
  76. Vector m_velocity;
  77. Vector m_avelocity;
  78. Vector m_vecTarget;
  79. Vector m_posTarget;
  80. Vector m_vecDesired;
  81. Vector m_posDesired;
  82. float m_flMinZ;
  83. float m_flMaxZ;
  84. Vector m_vecGoal;
  85. float m_flLastSeen;
  86. float m_flPrevSeen;
  87. int m_irritation;
  88. int m_iLevel;
  89. int m_iTeleport;
  90. EHANDLE m_hRecharger;
  91. EHANDLE m_hSphere[N_SPHERES];
  92. int m_iActiveSpheres;
  93. float m_flAdj;
  94. CSprite *m_pBall;
  95. char m_szRechargerTarget[64];
  96. char m_szDrawUse[64];
  97. char m_szTeleportUse[64];
  98. char m_szTeleportTouch[64];
  99. char m_szDeadUse[64];
  100. char m_szDeadTouch[64];
  101. float m_flShootEnd;
  102. float m_flShootTime;
  103. EHANDLE m_hFriend[3];
  104. bool m_bDead;
  105. DECLARE_DATADESC();
  106. };
  107. LINK_ENTITY_TO_CLASS( monster_nihilanth, CNPC_Nihilanth );
  108. BEGIN_DATADESC( CNPC_Nihilanth )
  109. DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
  110. DEFINE_FIELD( m_flNextPainSound, FIELD_TIME ),
  111. DEFINE_FIELD( m_velocity, FIELD_VECTOR ),
  112. DEFINE_FIELD( m_avelocity, FIELD_VECTOR ),
  113. DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
  114. DEFINE_FIELD( m_posTarget, FIELD_POSITION_VECTOR ),
  115. DEFINE_FIELD( m_vecDesired, FIELD_VECTOR ),
  116. DEFINE_FIELD( m_posDesired, FIELD_POSITION_VECTOR ),
  117. DEFINE_FIELD( m_flMinZ, FIELD_FLOAT ),
  118. DEFINE_FIELD( m_flMaxZ, FIELD_FLOAT ),
  119. DEFINE_FIELD( m_vecGoal, FIELD_VECTOR ),
  120. DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
  121. DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
  122. DEFINE_FIELD( m_irritation, FIELD_INTEGER ),
  123. DEFINE_FIELD( m_iLevel, FIELD_INTEGER ),
  124. DEFINE_FIELD( m_iTeleport, FIELD_INTEGER ),
  125. DEFINE_FIELD( m_hRecharger, FIELD_EHANDLE ),
  126. DEFINE_ARRAY( m_hSphere, FIELD_EHANDLE, N_SPHERES ),
  127. DEFINE_FIELD( m_iActiveSpheres, FIELD_INTEGER ),
  128. DEFINE_FIELD( m_flAdj, FIELD_FLOAT ),
  129. DEFINE_FIELD( m_pBall, FIELD_CLASSPTR ),
  130. DEFINE_ARRAY( m_szRechargerTarget, FIELD_CHARACTER, 64 ),
  131. DEFINE_ARRAY( m_szDrawUse, FIELD_CHARACTER, 64 ),
  132. DEFINE_ARRAY( m_szTeleportUse, FIELD_CHARACTER, 64 ),
  133. DEFINE_ARRAY( m_szTeleportTouch, FIELD_CHARACTER, 64 ),
  134. DEFINE_ARRAY( m_szDeadUse, FIELD_CHARACTER, 64 ),
  135. DEFINE_ARRAY( m_szDeadTouch, FIELD_CHARACTER, 64 ),
  136. DEFINE_FIELD( m_flShootEnd, FIELD_TIME ),
  137. DEFINE_FIELD( m_flShootTime, FIELD_TIME ),
  138. DEFINE_ARRAY( m_hFriend, FIELD_EHANDLE, 3 ),
  139. DEFINE_FIELD( m_bDead, FIELD_BOOLEAN ),
  140. DEFINE_THINKFUNC( NullThink ),
  141. DEFINE_THINKFUNC( StartupThink ),
  142. DEFINE_THINKFUNC( HuntThink ),
  143. DEFINE_THINKFUNC( DyingThink ),
  144. DEFINE_INPUTFUNC( FIELD_VOID, "TurnBabyOn", InputTurnBabyOn ),
  145. DEFINE_INPUTFUNC( FIELD_VOID, "TurnBabyOff", InputTurnBabyOff ),
  146. END_DATADESC()
  147. class CNihilanthHVR : public CAI_BaseNPC
  148. {
  149. DECLARE_CLASS( CNihilanthHVR, CAI_BaseNPC );
  150. public:
  151. void Spawn( void );
  152. void Precache( void );
  153. void CircleInit( CBaseEntity *pTarget );
  154. void AbsorbInit( void );
  155. void GreenBallInit( void );
  156. void RemoveTouch( CBaseEntity *pOther );
  157. /*void Zap( void );
  158. void Teleport( void );*/
  159. void HoverThink( void );
  160. bool CircleTarget( Vector vecTarget );
  161. void BounceTouch( CBaseEntity *pOther );
  162. void ZapThink( void );
  163. void ZapInit( CBaseEntity *pEnemy );
  164. void ZapTouch( CBaseEntity *pOther );
  165. void TeleportThink( void );
  166. void TeleportTouch( CBaseEntity *pOther );
  167. void MovetoTarget( Vector vecTarget );
  168. void DissipateThink( void );
  169. CSprite *SpriteInit( const char *pSpriteName, CNihilanthHVR *pOwner );
  170. void TeleportInit( CNPC_Nihilanth *pOwner, CBaseEntity *pEnemy, CBaseEntity *pTarget, CBaseEntity *pTouch );
  171. float m_flIdealVel;
  172. Vector m_vecIdeal;
  173. CNPC_Nihilanth *m_pNihilanth;
  174. EHANDLE m_hTouch;
  175. float m_flBallScale;
  176. void SetSprite( CBaseEntity *pSprite )
  177. {
  178. m_hSprite = pSprite;
  179. }
  180. CBaseEntity *GetSprite( void )
  181. {
  182. return m_hSprite.Get();
  183. }
  184. void SetBeam( CBaseEntity *pBeam )
  185. {
  186. m_hBeam = pBeam;
  187. }
  188. CBaseEntity *GetBeam( void )
  189. {
  190. return m_hBeam.Get();
  191. }
  192. private:
  193. EHANDLE m_hSprite;
  194. EHANDLE m_hBeam;
  195. DECLARE_DATADESC();
  196. };
  197. LINK_ENTITY_TO_CLASS( nihilanth_energy_ball, CNihilanthHVR );
  198. BEGIN_DATADESC( CNihilanthHVR )
  199. DEFINE_FIELD( m_flIdealVel, FIELD_FLOAT ),
  200. DEFINE_FIELD( m_flBallScale, FIELD_FLOAT ),
  201. DEFINE_FIELD( m_vecIdeal, FIELD_VECTOR ),
  202. DEFINE_FIELD( m_pNihilanth, FIELD_CLASSPTR ),
  203. DEFINE_FIELD( m_hTouch, FIELD_EHANDLE ),
  204. DEFINE_FIELD( m_hSprite, FIELD_EHANDLE ),
  205. DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
  206. DEFINE_THINKFUNC( HoverThink ),
  207. DEFINE_ENTITYFUNC( BounceTouch ),
  208. DEFINE_THINKFUNC( ZapThink ),
  209. DEFINE_ENTITYFUNC( ZapTouch ),
  210. DEFINE_THINKFUNC( DissipateThink ),
  211. DEFINE_THINKFUNC( TeleportThink ),
  212. DEFINE_ENTITYFUNC( TeleportTouch ),
  213. DEFINE_ENTITYFUNC( RemoveTouch ),
  214. END_DATADESC()
  215. //=========================================================
  216. // Nihilanth, final Boss monster
  217. //=========================================================
  218. void CNPC_Nihilanth::Spawn( void )
  219. {
  220. Precache( );
  221. // motor
  222. SetMoveType( MOVETYPE_FLY );
  223. SetSolid( SOLID_BBOX );
  224. SetModel( "models/nihilanth.mdl" );
  225. //UTIL_SetSize( this, Vector( -300, -300, 0), Vector(300, 300, 512));
  226. //UTIL_SetSize(this, Vector( -32, -32, 0), Vector(32, 32, 64 ));
  227. UTIL_SetSize(this, Vector( -16 * N_SCALE, -16 * N_SCALE, -48 * N_SCALE ), Vector( 16 * N_SCALE, 16 * N_SCALE, 28 * N_SCALE ) );
  228. Vector vecSurroundingMins( -16 * N_SCALE, -16 * N_SCALE, -48 * N_SCALE );
  229. Vector vecSurroundingMaxs( 16 * N_SCALE, 16 * N_SCALE, 28 * N_SCALE );
  230. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
  231. UTIL_SetOrigin( this, GetAbsOrigin() - Vector( 0, 0, 64 ) );
  232. AddFlag( FL_NPC );
  233. m_takedamage = DAMAGE_AIM;
  234. m_iHealth = sk_nihilanth_health.GetFloat();
  235. SetViewOffset ( Vector( 0, 0, 300 ) );
  236. m_flFieldOfView = -1; // 360 degrees
  237. SetSequence( 0 );
  238. ResetSequenceInfo( );
  239. InitBoneControllers();
  240. SetThink( &CNPC_Nihilanth::StartupThink );
  241. SetNextThink( gpGlobals->curtime + 0.1 );
  242. m_vecDesired = Vector( 1, 0, 0 );
  243. m_posDesired = Vector( GetAbsOrigin().x, GetAbsOrigin().y, 512 );
  244. m_iLevel = 1;
  245. m_iTeleport = 1;
  246. if (m_szRechargerTarget[0] == '\0') Q_strncpy( m_szRechargerTarget, "n_recharger", sizeof( m_szRechargerTarget ) );
  247. if (m_szDrawUse[0] == '\0') Q_strncpy( m_szDrawUse, "n_draw", sizeof( m_szDrawUse ) );
  248. if (m_szTeleportUse[0] == '\0') Q_strncpy( m_szTeleportUse, "n_leaving", sizeof( m_szTeleportUse ) );
  249. if (m_szTeleportTouch[0] == '\0') Q_strncpy( m_szTeleportTouch, "n_teleport", sizeof( m_szTeleportTouch ) );
  250. if (m_szDeadUse[0] == '\0') Q_strncpy( m_szDeadUse, "n_dead", sizeof( m_szDeadUse ) );
  251. if (m_szDeadTouch[0] == '\0') Q_strncpy( m_szDeadTouch, "n_ending", sizeof( m_szDeadTouch ) );
  252. SetBloodColor( BLOOD_COLOR_YELLOW );
  253. }
  254. void CNPC_Nihilanth::Precache( void )
  255. {
  256. PrecacheModel("models/nihilanth.mdl");
  257. PrecacheModel("sprites/lgtning.vmt");
  258. UTIL_PrecacheOther( "nihilanth_energy_ball" );
  259. UTIL_PrecacheOther( "monster_alien_controller" );
  260. UTIL_PrecacheOther( "monster_alien_slave" );
  261. PrecacheScriptSound( "Nihilanth.PainLaugh" );
  262. PrecacheScriptSound( "Nihilanth.Pain" );
  263. PrecacheScriptSound( "Nihilanth.Die" );
  264. PrecacheScriptSound( "Nihilanth.FriendBeam" );
  265. PrecacheScriptSound( "Nihilanth.Attack" );
  266. PrecacheScriptSound( "Nihilanth.BallAttack" );
  267. PrecacheScriptSound( "Nihilanth.Recharge" );
  268. }
  269. void CNPC_Nihilanth::PainSound( const CTakeDamageInfo &info )
  270. {
  271. if (m_flNextPainSound > gpGlobals->curtime)
  272. return;
  273. m_flNextPainSound = gpGlobals->curtime + random->RandomFloat( 2, 5 );
  274. if ( m_iHealth > sk_nihilanth_health.GetFloat() / 2 )
  275. {
  276. CPASAttenuationFilter filter( this );
  277. EmitSound( filter, entindex(), "Nihilanth.PainLaugh" );
  278. }
  279. else if (m_irritation >= 2)
  280. {
  281. CPASAttenuationFilter filter( this );
  282. EmitSound( filter, entindex(), "Nihilanth.Pain" );
  283. }
  284. }
  285. void CNPC_Nihilanth::DeathSound( const CTakeDamageInfo &info )
  286. {
  287. CPASAttenuationFilter filter( this );
  288. EmitSound( filter, entindex(), "Nihilanth.Die" );
  289. }
  290. int CNPC_Nihilanth::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  291. {
  292. if ( info.GetInflictor() == this )
  293. return 0;
  294. if ( m_bDead )
  295. return 0;
  296. if ( info.GetDamage() >= m_iHealth )
  297. {
  298. m_iHealth = 1;
  299. if ( m_irritation != 3 )
  300. return 0;
  301. }
  302. PainSound( info );
  303. m_iHealth -= info.GetDamage();
  304. if( m_iHealth < 0 )
  305. {
  306. m_iHealth = 1;
  307. m_bDead = true;
  308. m_takedamage = DAMAGE_NO;
  309. }
  310. return 0;
  311. }
  312. void CNPC_Nihilanth::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  313. {
  314. if (m_irritation == 3)
  315. m_irritation = 2;
  316. if (m_irritation == 2 && ptr->hitgroup == 0 && info.GetDamage() > 2)
  317. m_irritation = 3;
  318. if (m_irritation != 3)
  319. {
  320. Vector vecBlood = (ptr->endpos - GetAbsOrigin() );
  321. VectorNormalize( vecBlood );
  322. UTIL_BloodStream( ptr->endpos, vecBlood, BloodColor(), info.GetDamage() + (100 - 100 * (m_iHealth / sk_nihilanth_health.GetFloat() )));
  323. }
  324. // SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage * 5.0);// a little surface blood.
  325. AddMultiDamage( info, this );
  326. }
  327. bool CNPC_Nihilanth::EmitSphere( void )
  328. {
  329. m_iActiveSpheres = 0;
  330. int empty = 0;
  331. for (int i = 0; i < N_SPHERES; i++)
  332. {
  333. if (m_hSphere[i] != NULL)
  334. {
  335. m_iActiveSpheres++;
  336. }
  337. else
  338. {
  339. empty = i;
  340. }
  341. }
  342. if (m_iActiveSpheres >= N_SPHERES)
  343. return false;
  344. Vector vecSrc = m_hRecharger->GetAbsOrigin();
  345. CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" );
  346. pEntity->SetAbsOrigin( vecSrc );
  347. pEntity->SetAbsAngles( GetAbsAngles() );
  348. pEntity->SetOwnerEntity( this );
  349. pEntity->Spawn();
  350. pEntity->SetAbsVelocity( GetAbsOrigin() - vecSrc );
  351. pEntity->CircleInit( this );
  352. m_hSphere[empty] = pEntity;
  353. return true;
  354. }
  355. void CNPC_Nihilanth::NullThink( void )
  356. {
  357. StudioFrameAdvance( );
  358. SetNextThink( gpGlobals->curtime + 0.5 );
  359. }
  360. void CNPC_Nihilanth::StartupThink( void )
  361. {
  362. m_irritation = 0;
  363. m_flAdj = 512;
  364. CBaseEntity *pEntity;
  365. pEntity = gEntList.FindEntityByName( NULL, "n_min" );
  366. if (pEntity)
  367. m_flMinZ = pEntity->GetAbsOrigin().z;
  368. else
  369. m_flMinZ = -4096;
  370. pEntity = gEntList.FindEntityByName( NULL, "n_max" );
  371. if (pEntity)
  372. m_flMaxZ = pEntity->GetAbsOrigin().z;
  373. else
  374. m_flMaxZ = 4096;
  375. m_hRecharger = this;
  376. //TODO
  377. for (int i = 0; i < N_SPHERES; i++)
  378. {
  379. EmitSphere();
  380. }
  381. m_hRecharger = NULL;
  382. SetUse( NULL );
  383. SetThink( &CNPC_Nihilanth::HuntThink);
  384. SetNextThink( gpGlobals->curtime + 0.1 );
  385. }
  386. void CNPC_Nihilanth::InputTurnBabyOn( inputdata_t &inputdata )
  387. {
  388. if ( m_irritation == 0 )
  389. {
  390. m_irritation = 1;
  391. }
  392. }
  393. void CNPC_Nihilanth::InputTurnBabyOff( inputdata_t &inputdata )
  394. {
  395. CBaseEntity *pTouch = gEntList.FindEntityByName( NULL, m_szDeadTouch );
  396. if ( pTouch && GetEnemy() != NULL )
  397. pTouch->Touch( GetEnemy() );
  398. }
  399. bool CNPC_Nihilanth::AbsorbSphere( void )
  400. {
  401. for (int i = 0; i < N_SPHERES; i++)
  402. {
  403. if (m_hSphere[i] != NULL)
  404. {
  405. CNihilanthHVR *pSphere = (CNihilanthHVR *)m_hSphere[i].Get();
  406. pSphere->AbsorbInit();
  407. m_hSphere[i] = NULL;
  408. m_iActiveSpheres--;
  409. return TRUE;
  410. }
  411. }
  412. return FALSE;
  413. }
  414. void CNPC_Nihilanth::HuntThink( void )
  415. {
  416. SetNextThink( gpGlobals->curtime + 0.1 );
  417. DispatchAnimEvents( this );
  418. StudioFrameAdvance( );
  419. ShootBalls();
  420. // if dead, force cancelation of current animation
  421. if ( m_bDead )
  422. {
  423. SetThink( &CNPC_Nihilanth::DyingThink );
  424. SetCycle( 1.0f );
  425. StudioFrameAdvance();
  426. return;
  427. }
  428. // ALERT( at_console, "health %.0f\n", pev->health );
  429. // if damaged, try to abosorb some spheres
  430. if ( m_iHealth < sk_nihilanth_health.GetFloat() && AbsorbSphere() )
  431. {
  432. m_iHealth += sk_nihilanth_health.GetFloat() / N_SPHERES;
  433. }
  434. // get new sequence
  435. if ( IsSequenceFinished() )
  436. {
  437. SetCycle( 0 );
  438. NextActivity( );
  439. ResetSequenceInfo( );
  440. m_flPlaybackRate = 2.0 - 1.0 * ( m_iHealth / sk_nihilanth_health.GetFloat() );
  441. }
  442. // look for current enemy
  443. if ( GetEnemy() != NULL && m_hRecharger == NULL)
  444. {
  445. if (FVisible( GetEnemy() ))
  446. {
  447. if (m_flLastSeen < gpGlobals->curtime - 5)
  448. m_flPrevSeen = gpGlobals->curtime;
  449. m_flLastSeen = gpGlobals->curtime;
  450. m_posTarget = GetEnemy()->GetAbsOrigin();
  451. m_vecTarget = m_posTarget - GetAbsOrigin();
  452. VectorNormalize( m_vecTarget );
  453. m_vecDesired = m_vecTarget;
  454. m_posDesired = Vector( GetAbsOrigin().x, GetAbsOrigin().y, m_posTarget.z + m_flAdj );
  455. }
  456. else
  457. {
  458. m_flAdj = MIN( m_flAdj + 10, 1000 );
  459. }
  460. }
  461. // don't go too high
  462. if (m_posDesired.z > m_flMaxZ)
  463. m_posDesired.z = m_flMaxZ;
  464. // don't go too low
  465. if (m_posDesired.z < m_flMinZ)
  466. m_posDesired.z = m_flMinZ;
  467. Flight( );
  468. }
  469. void CNPC_Nihilanth::Flight( void )
  470. {
  471. Vector vForward, vRight, vUp;
  472. QAngle vAngle = QAngle( GetAbsAngles().x + m_avelocity.x, GetAbsAngles().y + m_avelocity.y, GetAbsAngles().z + m_avelocity.z );
  473. AngleVectors( vAngle, &vForward, &vRight, &vUp );
  474. float flSide = DotProduct( m_vecDesired, vRight );
  475. if (flSide < 0)
  476. {
  477. if (m_avelocity.y < 180)
  478. {
  479. m_avelocity.y += 6; // 9 * (3.0/2.0);
  480. }
  481. }
  482. else
  483. {
  484. if (m_avelocity.y > -180)
  485. {
  486. m_avelocity.y -= 6; // 9 * (3.0/2.0);
  487. }
  488. }
  489. m_avelocity.y *= 0.98;
  490. // estimate where I'll be in two seconds
  491. Vector vecEst = GetAbsOrigin() + m_velocity * 2.0 + vUp * m_flForce * 20;
  492. // add immediate force
  493. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  494. m_velocity.x += vUp.x * m_flForce;
  495. m_velocity.y += vUp.y * m_flForce;
  496. m_velocity.z += vUp.z * m_flForce;
  497. float flSpeed = m_velocity.Length();
  498. float flDir = DotProduct( Vector( vForward.x, vForward.y, 0 ), Vector( m_velocity.x, m_velocity.y, 0 ) );
  499. if (flDir < 0)
  500. flSpeed = -flSpeed;
  501. // sideways drag
  502. m_velocity.x = m_velocity.x * (1.0 - fabs( vRight.x ) * 0.05);
  503. m_velocity.y = m_velocity.y * (1.0 - fabs( vRight.y ) * 0.05);
  504. m_velocity.z = m_velocity.z * (1.0 - fabs( vRight.z ) * 0.05);
  505. // general drag
  506. m_velocity = m_velocity * 0.995;
  507. // apply power to stay correct height
  508. if (m_flForce < 100 && vecEst.z < m_posDesired.z)
  509. {
  510. m_flForce += 10;
  511. }
  512. else if (m_flForce > -100 && vecEst.z > m_posDesired.z)
  513. {
  514. if (vecEst.z > m_posDesired.z)
  515. m_flForce -= 10;
  516. }
  517. SetAbsVelocity( m_velocity );
  518. vAngle = QAngle( GetAbsAngles().x + m_avelocity.x * 0.1, GetAbsAngles().y + m_avelocity.y * 0.1, GetAbsAngles().z + m_avelocity.z * 0.1 );
  519. SetAbsAngles( vAngle );
  520. // ALERT( at_console, "%5.0f %5.0f : %4.0f : %3.0f : %2.0f\n", m_posDesired.z, pev->origin.z, m_velocity.z, m_avelocity.y, m_flForce );
  521. }
  522. void CNPC_Nihilanth::NextActivity( )
  523. {
  524. Vector vForward, vRight, vUp;
  525. SetIdealActivity( ACT_DO_NOT_DISTURB );
  526. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  527. if (m_irritation >= 2)
  528. {
  529. if (m_pBall == NULL)
  530. {
  531. m_pBall = CSprite::SpriteCreate( "sprites/tele1.vmt", GetAbsOrigin(), true );
  532. if (m_pBall)
  533. {
  534. m_pBall->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
  535. m_pBall->SetAttachment( this, 1 );
  536. m_pBall->SetScale( 4.0 );
  537. m_pBall->m_flSpriteFramerate = 10.0f;
  538. m_pBall->TurnOn( );
  539. }
  540. }
  541. if (m_pBall)
  542. {
  543. CBroadcastRecipientFilter filterlight;
  544. Vector vOrigin;
  545. QAngle vAngle;
  546. GetAttachment( 2, vOrigin, vAngle );
  547. te->DynamicLight( filterlight, 0.0, &vOrigin, 255, 192, 64, 0, 256, 20, 0 );
  548. }
  549. }
  550. if (( m_iHealth < sk_nihilanth_health.GetFloat() / 2 || m_iActiveSpheres < N_SPHERES / 2) && m_hRecharger == NULL && m_iLevel <= 9)
  551. {
  552. char szName[64];
  553. CBaseEntity *pEnt = NULL;
  554. CBaseEntity *pRecharger = NULL;
  555. float flDist = 8192;
  556. Q_snprintf(szName, sizeof( szName ), "%s%d", m_szRechargerTarget, m_iLevel );
  557. while ((pEnt = gEntList.FindEntityByName( pEnt, szName )) != NULL )
  558. {
  559. float flLocal = (pEnt->GetAbsOrigin() - GetAbsOrigin() ).Length();
  560. if ( flLocal < flDist )
  561. {
  562. flDist = flLocal;
  563. pRecharger = pEnt;
  564. }
  565. }
  566. if (pRecharger)
  567. {
  568. m_hRecharger = pRecharger;
  569. m_posDesired = Vector( GetAbsOrigin().x, GetAbsOrigin().y, pRecharger->GetAbsOrigin().z );
  570. m_vecDesired = pRecharger->GetAbsOrigin() - m_posDesired;
  571. VectorNormalize( m_vecDesired );
  572. m_vecDesired.z = 0;
  573. VectorNormalize( m_vecDesired );
  574. }
  575. else
  576. {
  577. m_hRecharger = NULL;
  578. Msg( "nihilanth can't find %s\n", szName );
  579. m_iLevel++;
  580. if ( m_iLevel > 9 )
  581. m_irritation = 2;
  582. }
  583. }
  584. float flDist = ( m_posDesired - GetAbsOrigin() ).Length();
  585. float flDot = DotProduct( m_vecDesired, vForward );
  586. if (m_hRecharger != NULL)
  587. {
  588. // at we at power up yet?
  589. if (flDist < 128.0)
  590. {
  591. int iseq = LookupSequence( "recharge" );
  592. if (iseq != GetSequence())
  593. {
  594. char szText[64];
  595. Q_snprintf( szText, sizeof( szText ), "%s%d", m_szDrawUse, m_iLevel );
  596. FireTargets( szText, this, this, USE_ON, 1.0 );
  597. Msg( "fireing %s\n", szText );
  598. }
  599. SetSequence ( LookupSequence( "recharge" ) );
  600. }
  601. else
  602. {
  603. FloatSequence( );
  604. }
  605. return;
  606. }
  607. if (GetEnemy() != NULL && !GetEnemy()->IsAlive())
  608. {
  609. SetEnemy( NULL );
  610. }
  611. if (m_flLastSeen + 15 < gpGlobals->curtime)
  612. {
  613. SetEnemy( NULL );
  614. }
  615. if ( GetEnemy() == NULL)
  616. {
  617. GetSenses()->Look( 4096 );
  618. SetEnemy( BestEnemy() );
  619. }
  620. if ( GetEnemy() != NULL && m_irritation != 0)
  621. {
  622. if (m_flLastSeen + 5 > gpGlobals->curtime && flDist < 256 && flDot > 0)
  623. {
  624. if (m_irritation >= 2 && m_iHealth < sk_nihilanth_health.GetFloat() / 2.0)
  625. {
  626. SetSequence( LookupSequence( "attack1_open" ) );
  627. }
  628. else
  629. {
  630. if ( random->RandomInt(0, 1 ) == 0)
  631. {
  632. SetSequence( LookupSequence( "attack1" ) ); // zap
  633. }
  634. else
  635. {
  636. char szText[64];
  637. Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportTouch, m_iTeleport );
  638. CBaseEntity *pTouch = gEntList.FindEntityByName( NULL, szText );
  639. Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportUse, m_iTeleport );
  640. CBaseEntity *pTrigger = gEntList.FindEntityByName( NULL, szText );
  641. if (pTrigger != NULL || pTouch != NULL)
  642. {
  643. SetSequence( LookupSequence( "attack2" ) ); // teleport
  644. }
  645. else
  646. {
  647. m_iTeleport++;
  648. SetSequence( LookupSequence( "attack1" ) ); // zap
  649. }
  650. }
  651. }
  652. return;
  653. }
  654. }
  655. FloatSequence( );
  656. }
  657. void CNPC_Nihilanth::MakeFriend( Vector vecStart )
  658. {
  659. int i;
  660. for (i = 0; i < 3; i++)
  661. {
  662. if (m_hFriend[i] != NULL && !m_hFriend[i]->IsAlive())
  663. {
  664. if ( m_nRenderMode == kRenderNormal) // don't do it if they are already fading
  665. m_hFriend[i]->MyNPCPointer()->CorpseFade();
  666. m_hFriend[i] = NULL;
  667. }
  668. if (m_hFriend[i] == NULL)
  669. {
  670. if ( random->RandomInt( 0, 1 ) == 0)
  671. {
  672. int iNode = GetNavigator()->GetNetwork()->NearestNodeToPoint( vecStart );
  673. CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNode );
  674. if ( pNode && pNode->GetType() == NODE_AIR )
  675. {
  676. trace_t tr;
  677. Vector vNodeOrigin = pNode->GetOrigin();
  678. UTIL_TraceHull( vNodeOrigin + Vector( 0, 0, 32 ), vNodeOrigin + Vector( 0, 0, 32 ), Vector(-40,-40, 0), Vector(40, 40, 100), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  679. if ( tr.startsolid == 0 )
  680. m_hFriend[i] = Create("monster_alien_controller", vNodeOrigin, GetAbsAngles() );
  681. }
  682. }
  683. else
  684. {
  685. int iNode = GetNavigator()->GetNetwork()->NearestNodeToPoint( vecStart );
  686. CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNode );
  687. if ( pNode && ( pNode->GetType() == NODE_GROUND || pNode->GetType() == NODE_WATER ) )
  688. {
  689. trace_t tr;
  690. Vector vNodeOrigin = pNode->GetOrigin();
  691. UTIL_TraceHull( vNodeOrigin + Vector( 0, 0, 36 ), vNodeOrigin + Vector( 0, 0, 36 ), Vector( -15, -15, 0), Vector( 20, 15, 72 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  692. if (tr.startsolid == 0)
  693. m_hFriend[i] = Create("monster_alien_slave", vNodeOrigin, GetAbsAngles() );
  694. }
  695. }
  696. if (m_hFriend[i] != NULL)
  697. {
  698. CPASAttenuationFilter filter( this );
  699. EmitSound( filter, m_hFriend[i]->entindex(), "Nihilanth.FriendBeam" );
  700. }
  701. return;
  702. }
  703. }
  704. }
  705. void CNPC_Nihilanth::ShootBalls( void )
  706. {
  707. if (m_flShootEnd > gpGlobals->curtime)
  708. {
  709. Vector vecHand;
  710. QAngle vecAngle;
  711. while (m_flShootTime < m_flShootEnd && m_flShootTime < gpGlobals->curtime)
  712. {
  713. if ( GetEnemy() != NULL)
  714. {
  715. Vector vecSrc, vecDir;
  716. CNihilanthHVR *pEntity = NULL;
  717. GetAttachment( 3, vecHand, vecAngle );
  718. vecSrc = vecHand + GetAbsVelocity() * (m_flShootTime - gpGlobals->curtime);
  719. vecDir = m_posTarget - GetAbsOrigin();
  720. VectorNormalize( vecDir );
  721. vecSrc = vecSrc + vecDir * (gpGlobals->curtime - m_flShootTime);
  722. pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" );
  723. pEntity->SetAbsOrigin( vecSrc );
  724. pEntity->SetAbsAngles( vecAngle );
  725. pEntity->SetOwnerEntity( this );
  726. pEntity->Spawn();
  727. pEntity->SetAbsVelocity( vecDir * 200.0 );
  728. pEntity->ZapInit( GetEnemy() );
  729. GetAttachment( 4, vecHand, vecAngle );
  730. vecSrc = vecHand + GetAbsVelocity() * (m_flShootTime - gpGlobals->curtime);
  731. vecDir = m_posTarget - GetAbsOrigin();
  732. VectorNormalize( vecDir );
  733. vecSrc = vecSrc + vecDir * (gpGlobals->curtime - m_flShootTime);
  734. pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" );
  735. pEntity->SetAbsOrigin( vecSrc );
  736. pEntity->SetAbsAngles( vecAngle );
  737. pEntity->SetOwnerEntity( this );
  738. pEntity->Spawn();
  739. pEntity->SetAbsVelocity( vecDir * 200.0 );
  740. pEntity->ZapInit( GetEnemy() );
  741. }
  742. m_flShootTime += 0.2;
  743. }
  744. }
  745. }
  746. void CNPC_Nihilanth::FloatSequence( void )
  747. {
  748. if (m_irritation >= 2)
  749. {
  750. SetSequence( LookupSequence( "float_open" ) );
  751. }
  752. else if (m_avelocity.y > 30)
  753. {
  754. SetSequence( LookupSequence( "walk_r" ) );
  755. }
  756. else if (m_avelocity.y < -30)
  757. {
  758. SetSequence( LookupSequence( "walk_l" ) );
  759. }
  760. else if (m_velocity.z > 30)
  761. {
  762. SetSequence( LookupSequence( "walk_u" ) );
  763. }
  764. else if (m_velocity.z < -30)
  765. {
  766. SetSequence( LookupSequence( "walk_d" ) );
  767. }
  768. else
  769. {
  770. SetSequence( LookupSequence( "float" ) );
  771. }
  772. }
  773. void CNPC_Nihilanth::DyingThink( void )
  774. {
  775. SetNextThink( gpGlobals->curtime + 0.1 );
  776. DispatchAnimEvents( this );
  777. StudioFrameAdvance( );
  778. if ( m_lifeState == LIFE_ALIVE )
  779. {
  780. CTakeDamageInfo info;
  781. DeathSound( info );
  782. m_lifeState = LIFE_DYING;
  783. m_posDesired.z = m_flMaxZ;
  784. }
  785. if ( GetAbsOrigin().z < m_flMaxZ && m_lifeState == LIFE_DEAD )
  786. {
  787. SetAbsOrigin( Vector( GetAbsOrigin().x, GetAbsOrigin().y, m_flMaxZ ) );
  788. SetAbsVelocity( Vector( 0, 0, 0 ) );
  789. }
  790. if ( m_lifeState == LIFE_DYING )
  791. {
  792. Flight( );
  793. if (fabs( GetAbsOrigin().z - m_flMaxZ ) < 16)
  794. {
  795. CBaseEntity *pTrigger = NULL;
  796. SetAbsVelocity( Vector( 0, 0, 0 ) );
  797. SetGravity( 0 );
  798. while( ( pTrigger = gEntList.FindEntityByName( pTrigger, m_szDeadUse ) ) != NULL )
  799. {
  800. CLogicRelay *pRelay = (CLogicRelay*)pTrigger;
  801. pRelay->m_OnTrigger.FireOutput( this, this );
  802. }
  803. m_lifeState = LIFE_DEAD;
  804. }
  805. }
  806. if ( IsSequenceFinished() )
  807. {
  808. QAngle qAngularVel = GetLocalAngularVelocity();
  809. qAngularVel.y += random->RandomFloat( -100, 100 );
  810. if ( qAngularVel.y < -100)
  811. qAngularVel.y = -100;
  812. if ( qAngularVel.y > 100)
  813. qAngularVel.y = 100;
  814. SetLocalAngularVelocity( qAngularVel );
  815. SetSequence( LookupSequence( "die1" ) );
  816. }
  817. if ( m_pBall )
  818. {
  819. if (m_pBall->GetBrightness() > 0)
  820. {
  821. m_pBall->SetBrightness( MAX( 0, m_pBall->GetBrightness() - 7 ), 0 );
  822. }
  823. else
  824. {
  825. UTIL_Remove( m_pBall );
  826. m_pBall = NULL;
  827. }
  828. }
  829. Vector vecDir, vecSrc;
  830. QAngle vecAngles;
  831. Vector vForward, vRight, vUp;
  832. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  833. int iAttachment = random->RandomInt( 1, 4 );
  834. do {
  835. vecDir = Vector( random->RandomFloat( -1, 1 ), random->RandomFloat( -1, 1 ), random->RandomFloat( -1, 1 ) );
  836. } while (DotProduct( vecDir, vecDir) > 1.0);
  837. switch( random->RandomInt( 1, 4 ))
  838. {
  839. case 1: // head
  840. vecDir.z = fabs( vecDir.z ) * 0.5;
  841. vecDir = vecDir + 2 * vUp;
  842. break;
  843. case 2: // eyes
  844. if (DotProduct( vecDir, vForward ) < 0)
  845. vecDir = vecDir * -1;
  846. vecDir = vecDir + 2 * vForward;
  847. break;
  848. case 3: // left hand
  849. if (DotProduct( vecDir, vRight ) > 0)
  850. vecDir = vecDir * -1;
  851. vecDir = vecDir - 2 * vRight;
  852. break;
  853. case 4: // right hand
  854. if (DotProduct( vecDir, vRight ) < 0)
  855. vecDir = vecDir * -1;
  856. vecDir = vecDir + 2 * vRight;
  857. break;
  858. }
  859. GetAttachment( iAttachment, vecSrc, vecAngles );
  860. trace_t tr;
  861. UTIL_TraceLine( vecSrc, vecSrc + vecDir * 4096, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  862. CBeam *pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 16 );
  863. if ( pBeam == NULL )
  864. return;
  865. pBeam->PointEntInit( tr.endpos, this );
  866. pBeam->SetEndAttachment( iAttachment );
  867. pBeam->SetColor( 64, 128, 255 );
  868. pBeam->SetFadeLength( 50 );
  869. pBeam->SetBrightness( 255 );
  870. pBeam->SetNoise( 12 );
  871. pBeam->SetScrollRate( 1.0 );
  872. pBeam->LiveForTime( 0.5 );
  873. GetAttachment( 2, vecSrc, vecAngles );
  874. CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" );
  875. pEntity->SetAbsOrigin( vecSrc );
  876. pEntity->SetAbsAngles( GetAbsAngles() );
  877. pEntity->SetOwnerEntity( this );
  878. pEntity->SetAbsVelocity( Vector ( random->RandomFloat( -0.7, 0.7 ), random->RandomFloat( -0.7, 0.7 ), 1.0 ) * 600.0 );
  879. pEntity->Spawn();
  880. pEntity->GreenBallInit();
  881. return;
  882. }
  883. void CNPC_Nihilanth::HandleAnimEvent( animevent_t *pEvent )
  884. {
  885. switch( pEvent->event )
  886. {
  887. case 1: // shoot
  888. break;
  889. case 2: // zen
  890. if ( GetEnemy() != NULL)
  891. {
  892. Vector vOrigin;
  893. QAngle vAngle;
  894. CPASAttenuationFilter filter( this );
  895. if ( random->RandomInt(0,4) == 0)
  896. EmitSound( filter, entindex(), "Nihilanth.Attack" );
  897. EmitSound( filter, entindex(), "Nihilanth.BallAttack" );
  898. GetAttachment( 2, vOrigin, vAngle);
  899. CBroadcastRecipientFilter filterlight;
  900. te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 );
  901. GetAttachment( 3, vOrigin, vAngle);
  902. te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 );
  903. m_flShootTime = gpGlobals->curtime;
  904. m_flShootEnd = gpGlobals->curtime + 1.0;
  905. }
  906. break;
  907. case 3: // prayer
  908. if (GetEnemy() != NULL)
  909. {
  910. char szText[32];
  911. Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportTouch, m_iTeleport );
  912. CBaseEntity *pTouch = gEntList.FindEntityByName( NULL, szText );
  913. Q_snprintf( szText, sizeof( szText ), "%s%d", m_szTeleportUse, m_iTeleport );
  914. CBaseEntity *pTrigger = gEntList.FindEntityByName( NULL, szText );
  915. if (pTrigger != NULL || pTouch != NULL)
  916. {
  917. CPASAttenuationFilter filter( this );
  918. EmitSound( filter, entindex(), "Nihilanth.Attack" );
  919. Vector vecSrc;
  920. QAngle vecAngles;
  921. GetAttachment( 2, vecSrc, vecAngles );
  922. CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" );
  923. pEntity->SetAbsOrigin( vecSrc );
  924. pEntity->SetAbsAngles( vecAngles );
  925. pEntity->SetOwnerEntity( this );
  926. pEntity->Spawn();
  927. pEntity->TeleportInit( this, GetEnemy(), pTrigger, pTouch );
  928. pEntity->SetAbsVelocity( GetAbsOrigin() - vecSrc );
  929. pEntity->SetAbsVelocity( Vector( GetAbsVelocity().x, GetAbsVelocity().y, GetAbsVelocity().z * 0.2 ) );
  930. }
  931. else
  932. {
  933. Vector vOrigin;
  934. QAngle vAngle;
  935. m_iTeleport++; // unexpected failure
  936. CPASAttenuationFilter filter( this );
  937. EmitSound( filter, entindex(), "Nihilanth.BallAttack" );
  938. Msg( "nihilanth can't target %s\n", szText );
  939. GetAttachment( 2, vOrigin, vAngle);
  940. CBroadcastRecipientFilter filterlight;
  941. te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 );
  942. GetAttachment( 3, vOrigin, vAngle);
  943. te->DynamicLight( filterlight, 0.0, &vOrigin, 128, 128, 255, 0, 256, 1.0f, 128 );
  944. m_flShootTime = gpGlobals->curtime;
  945. m_flShootEnd = gpGlobals->curtime + 1.0;
  946. }
  947. }
  948. break;
  949. case 4: // get a sphere
  950. {
  951. if (m_hRecharger != NULL)
  952. {
  953. if (!EmitSphere( ))
  954. {
  955. m_hRecharger = NULL;
  956. }
  957. }
  958. }
  959. break;
  960. case 5: // start up sphere machine
  961. {
  962. CPASAttenuationFilter filter( this );
  963. EmitSound( filter, entindex(), "Nihilanth.Recharge" );
  964. }
  965. break;
  966. case 6:
  967. if ( GetEnemy() != NULL)
  968. {
  969. Vector vecSrc;
  970. QAngle vecAngles;
  971. GetAttachment( 3, vecSrc, vecAngles );
  972. CNihilanthHVR *pEntity = (CNihilanthHVR *)CREATE_ENTITY( CNihilanthHVR, "nihilanth_energy_ball" );
  973. pEntity->SetAbsOrigin( vecSrc );
  974. pEntity->SetAbsAngles( vecAngles );
  975. pEntity->SetOwnerEntity( this );
  976. pEntity->Spawn();
  977. pEntity->SetAbsVelocity( GetAbsOrigin() - vecSrc );
  978. pEntity->ZapInit( GetEnemy() );
  979. }
  980. break;
  981. case 7:
  982. /*
  983. Vector vecSrc, vecAngles;
  984. GetAttachment( 0, vecSrc, vecAngles );
  985. CNihilanthHVR *pEntity = (CNihilanthHVR *)Create( "nihilanth_energy_ball", vecSrc, pev->angles, edict() );
  986. pEntity->pev->velocity = Vector ( RANDOM_FLOAT( -0.7, 0.7 ), RANDOM_FLOAT( -0.7, 0.7 ), 1.0 ) * 600.0;
  987. pEntity->GreenBallInit( );
  988. */
  989. break;
  990. }
  991. }
  992. //=========================================================
  993. // Controller bouncy ball attack
  994. //=========================================================
  995. void CNihilanthHVR::Spawn( void )
  996. {
  997. Precache( );
  998. }
  999. void CNihilanthHVR::Precache( void )
  1000. {
  1001. PrecacheModel("sprites/flare6.vmt");
  1002. PrecacheModel("sprites/nhth1.vmt");
  1003. PrecacheModel("sprites/exit1.vmt");
  1004. PrecacheModel("sprites/tele1.vmt");
  1005. PrecacheModel("sprites/animglow01.vmt");
  1006. PrecacheModel("sprites/xspark4.vmt");
  1007. PrecacheModel("sprites/muzzleflash3.vmt");
  1008. PrecacheModel("sprites/laserbeam.vmt");
  1009. PrecacheScriptSound( "NihilanthHVR.Zap" );
  1010. PrecacheScriptSound( "NihilanthHVR.TeleAttack" );
  1011. }
  1012. void CNihilanthHVR::CircleInit( CBaseEntity *pTarget )
  1013. {
  1014. SetMoveType( MOVETYPE_FLY );
  1015. SetSolid( SOLID_NONE );
  1016. UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0));
  1017. UTIL_SetOrigin( this, GetAbsOrigin() );
  1018. SetThink( &CNihilanthHVR::HoverThink );
  1019. SetTouch( &CNihilanthHVR::BounceTouch );
  1020. SetNextThink( gpGlobals->curtime + 0.1 );
  1021. CSprite *pSprite = SpriteInit( "sprites/muzzleflash3.vmt", this );
  1022. if ( pSprite )
  1023. {
  1024. m_flBallScale = 2.0f;
  1025. pSprite->SetScale( 2.0 );
  1026. pSprite->SetTransparency( kRenderTransAdd, 255, 224, 192, 255, kRenderFxNone );
  1027. }
  1028. SetTarget( pTarget );
  1029. }
  1030. CSprite *CNihilanthHVR::SpriteInit( const char *pSpriteName, CNihilanthHVR *pOwner )
  1031. {
  1032. pOwner->SetSprite( CSprite::SpriteCreate( pSpriteName, pOwner->GetAbsOrigin(), true ) );
  1033. CSprite *pSprite = (CSprite*)pOwner->GetSprite();
  1034. if ( pSprite )
  1035. {
  1036. pSprite->SetAttachment( pOwner, 0 );
  1037. pSprite->SetOwnerEntity( pOwner );
  1038. pSprite->AnimateForTime( 5, 9999 );
  1039. }
  1040. return pSprite;
  1041. }
  1042. void CNihilanthHVR::HoverThink( void )
  1043. {
  1044. SetNextThink( gpGlobals->curtime + 0.1 );
  1045. if ( GetTarget() != NULL )
  1046. {
  1047. CircleTarget( GetTarget()->GetAbsOrigin() + Vector( 0, 0, 16 * N_SCALE ) );
  1048. }
  1049. else
  1050. {
  1051. UTIL_Remove( GetSprite() );
  1052. UTIL_Remove( this );
  1053. }
  1054. }
  1055. void CNihilanthHVR::BounceTouch( CBaseEntity *pOther )
  1056. {
  1057. Vector vecDir = m_vecIdeal;
  1058. VectorNormalize( vecDir );
  1059. const trace_t &tr = GetTouchTrace();
  1060. float n = -DotProduct(tr.plane.normal, vecDir);
  1061. vecDir = 2.0 * tr.plane.normal * n + vecDir;
  1062. m_vecIdeal = vecDir * m_vecIdeal.Length();
  1063. }
  1064. bool CNihilanthHVR::CircleTarget( Vector vecTarget )
  1065. {
  1066. bool fClose = false;
  1067. vecTarget = vecTarget + Vector( 0, 0, 64 );
  1068. Vector vecDest = vecTarget;
  1069. Vector vecEst = GetAbsOrigin() + GetAbsVelocity() * 0.5;
  1070. Vector vecSrc = GetAbsOrigin();
  1071. vecDest.z = 0;
  1072. vecEst.z = 0;
  1073. vecSrc.z = 0;
  1074. float d1 = (vecDest - vecSrc).Length() - 24 * N_SCALE;
  1075. float d2 = (vecDest - vecEst).Length() - 24 * N_SCALE;
  1076. if ( m_vecIdeal == vec3_origin )
  1077. {
  1078. m_vecIdeal = GetAbsVelocity();
  1079. }
  1080. if (d1 < 0 && d2 <= d1)
  1081. {
  1082. // ALERT( at_console, "too close\n");
  1083. Vector vTemp = (vecDest - vecSrc);
  1084. VectorNormalize( vTemp );
  1085. m_vecIdeal = m_vecIdeal - vTemp * 50;
  1086. }
  1087. else if (d1 > 0 && d2 >= d1)
  1088. {
  1089. Vector vTemp = (vecDest - vecSrc);
  1090. VectorNormalize( vTemp );
  1091. m_vecIdeal = m_vecIdeal + vTemp * 50;
  1092. }
  1093. SetLocalAngularVelocity( QAngle( GetLocalAngularVelocity().x, GetLocalAngularVelocity().y, d1 * 20 ) );
  1094. if (d1 < 32)
  1095. {
  1096. fClose = true;
  1097. }
  1098. m_vecIdeal = m_vecIdeal + Vector( random->RandomFloat( -2, 2 ), random->RandomFloat( -2, 2 ), random->RandomFloat( -2, 2 ));
  1099. float flIdealZ = m_vecIdeal.z;
  1100. m_vecIdeal = Vector( m_vecIdeal.x, m_vecIdeal.y, 0 );
  1101. VectorNormalize( m_vecIdeal );
  1102. m_vecIdeal = (m_vecIdeal * 200) + Vector( 0, 0, flIdealZ );
  1103. // move up/down
  1104. d1 = vecTarget.z - GetAbsOrigin().z;
  1105. if (d1 > 0 && m_vecIdeal.z < 200)
  1106. m_vecIdeal.z += 200;
  1107. else if (d1 < 0 && m_vecIdeal.z > -200)
  1108. m_vecIdeal.z -= 200;
  1109. SetAbsVelocity( m_vecIdeal );
  1110. // ALERT( at_console, "%.0f %.0f %.0f\n", m_vecIdeal.x, m_vecIdeal.y, m_vecIdeal.z );
  1111. return fClose;
  1112. }
  1113. void CNihilanthHVR::ZapInit( CBaseEntity *pEnemy )
  1114. {
  1115. SetMoveType( MOVETYPE_FLY );
  1116. SetSolid( SOLID_BBOX );
  1117. CSprite *pSprite = SpriteInit( "sprites/nhth1.vmt", this );
  1118. if ( pSprite )
  1119. {
  1120. m_flBallScale = 2.0f;
  1121. pSprite->SetScale( 2.0 );
  1122. pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
  1123. }
  1124. Vector vVelocity = pEnemy->GetAbsOrigin() - GetAbsOrigin();
  1125. VectorNormalize( vVelocity );
  1126. SetAbsVelocity ( vVelocity * 300 );
  1127. SetEnemy( pEnemy );
  1128. SetThink( &CNihilanthHVR::ZapThink );
  1129. SetTouch( &CNihilanthHVR::ZapTouch );
  1130. SetNextThink( gpGlobals->curtime + 0.1 );
  1131. CPASAttenuationFilter filter( this );
  1132. EmitSound( filter, entindex(), "NihilanthHVR.Zap" );
  1133. }
  1134. void CNihilanthHVR::ZapThink( void )
  1135. {
  1136. SetNextThink( gpGlobals->curtime + 0.05 );
  1137. // check world boundaries
  1138. if ( GetEnemy() == NULL || GetAbsOrigin().x < -4096 || GetAbsOrigin().x > 4096 || GetAbsOrigin().y < -4096 || GetAbsOrigin().y > 4096 || GetAbsOrigin().z < -4096 || GetAbsOrigin().z > 4096)
  1139. {
  1140. SetTouch( NULL );
  1141. UTIL_Remove( GetSprite() );
  1142. UTIL_Remove( this );
  1143. return;
  1144. }
  1145. if ( GetAbsVelocity().Length() < 2000)
  1146. {
  1147. SetAbsVelocity( GetAbsVelocity() * 1.2 );
  1148. }
  1149. if (( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin()).Length() < 256)
  1150. {
  1151. trace_t tr;
  1152. UTIL_TraceLine( GetAbsOrigin(), GetEnemy()->WorldSpaceCenter(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1153. CBaseEntity *pEntity = tr.m_pEnt;
  1154. if (pEntity != NULL && pEntity->m_takedamage )
  1155. {
  1156. ClearMultiDamage( );
  1157. CTakeDamageInfo info( this, this, sk_nihilanth_zap.GetFloat(), DMG_SHOCK );
  1158. CalculateMeleeDamageForce( &info, (tr.endpos - tr.startpos), tr.endpos );
  1159. pEntity->DispatchTraceAttack( info, GetAbsVelocity(), &tr );
  1160. ApplyMultiDamage();
  1161. }
  1162. CBeam *pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 2.0f );
  1163. if ( pBeam == NULL )
  1164. return;
  1165. pBeam->PointEntInit( tr.endpos, this );
  1166. pBeam->SetColor( 64, 196, 255 );
  1167. pBeam->SetBrightness( 255 );
  1168. pBeam->SetNoise( 7.2 );
  1169. pBeam->SetScrollRate( 10 );
  1170. pBeam->LiveForTime( 0.1 );
  1171. UTIL_EmitAmbientSound( GetSoundSourceIndex(), tr.endpos, "Controller.ElectroSound", 0.5, SNDLVL_NORM, 0, random->RandomInt( 140, 160 ) );
  1172. SetTouch( NULL );
  1173. GetSprite()->SetThink( &CBaseEntity::SUB_Remove );
  1174. SetThink( &CBaseEntity::SUB_Remove );
  1175. SetNextThink( gpGlobals->curtime + 0.2 );
  1176. GetSprite()->SetNextThink( gpGlobals->curtime + 0.2 );
  1177. return;
  1178. }
  1179. CBroadcastRecipientFilter filterlight;
  1180. te->DynamicLight( filterlight, 0.0, &GetAbsOrigin(), 128, 128, 255, 0, 128, 10, 128 );
  1181. }
  1182. void CNihilanthHVR::ZapTouch( CBaseEntity *pOther )
  1183. {
  1184. UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin(), "Controller.ElectroSound", 1.0, SNDLVL_NORM, 0, random->RandomInt( 90, 95 ) );
  1185. RadiusDamage( CTakeDamageInfo( this, this, 50, DMG_SHOCK ), GetAbsOrigin(), 125, CLASS_NONE, NULL );
  1186. SetAbsVelocity( GetAbsVelocity() * 0 );
  1187. SetTouch( NULL );
  1188. UTIL_Remove( GetSprite() );
  1189. UTIL_Remove( this );
  1190. SetNextThink( gpGlobals->curtime + 0.2 );
  1191. }
  1192. void CNihilanthHVR::AbsorbInit( void )
  1193. {
  1194. CBroadcastRecipientFilter filter;
  1195. SetThink( &CNihilanthHVR::DissipateThink );
  1196. SetRenderColorA( 255 );
  1197. SetBeam( CBeam::BeamCreate( "sprites/laserbeam.vmt", 8.0f ) );
  1198. CBeam *pBeam = (CBeam*)GetBeam();
  1199. if ( pBeam == NULL )
  1200. return;
  1201. pBeam->EntsInit( this, GetTarget() );
  1202. pBeam->SetEndAttachment( 1 );
  1203. pBeam->SetColor( 255, 128, 64 );
  1204. pBeam->SetBrightness( 255 );
  1205. pBeam->SetNoise( 18 );
  1206. }
  1207. void CNihilanthHVR::DissipateThink( void )
  1208. {
  1209. CSprite *pSprite = (CSprite*)GetSprite();
  1210. SetNextThink ( gpGlobals->curtime + 0.1 );
  1211. if ( m_flBallScale > 5.0)
  1212. {
  1213. UTIL_Remove( this );
  1214. UTIL_Remove( GetSprite() );
  1215. UTIL_Remove( GetBeam() );
  1216. }
  1217. pSprite->SetBrightness( pSprite->GetBrightness() - 7, 0 );
  1218. m_flBallScale += 0.1;
  1219. pSprite->SetScale( m_flBallScale );
  1220. if ( GetTarget() != NULL)
  1221. {
  1222. CircleTarget( GetTarget()->GetAbsOrigin() + Vector( 0, 0, 4096 ) );
  1223. }
  1224. else
  1225. {
  1226. UTIL_Remove( this );
  1227. UTIL_Remove( GetSprite() );
  1228. UTIL_Remove( GetBeam() );
  1229. }
  1230. /* MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  1231. WRITE_BYTE( TE_ELIGHT );
  1232. WRITE_SHORT( entindex( ) ); // entity, attachment
  1233. WRITE_COORD( pev->origin.x ); // origin
  1234. WRITE_COORD( pev->origin.y );
  1235. WRITE_COORD( pev->origin.z );
  1236. WRITE_COORD( pev->renderamt ); // radius
  1237. WRITE_BYTE( 255 ); // R
  1238. WRITE_BYTE( 192 ); // G
  1239. WRITE_BYTE( 64 ); // B
  1240. WRITE_BYTE( 2 ); // life * 10
  1241. WRITE_COORD( 0 ); // decay
  1242. MESSAGE_END();*/
  1243. }
  1244. void CNihilanthHVR::TeleportInit( CNPC_Nihilanth *pOwner, CBaseEntity *pEnemy, CBaseEntity *pTarget, CBaseEntity *pTouch )
  1245. {
  1246. SetMoveType( MOVETYPE_FLY );
  1247. SetSolid( SOLID_BBOX );
  1248. SetModel( "" );
  1249. CSprite *pSprite = SpriteInit( "sprites/exit1.vmt", this );
  1250. if ( pSprite )
  1251. {
  1252. m_flBallScale = 2.0f;
  1253. pSprite->SetScale( 2.0 );
  1254. pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
  1255. }
  1256. m_pNihilanth = pOwner;
  1257. SetEnemy( pEnemy );
  1258. SetTarget( pTarget );
  1259. m_hTouch = pTouch;
  1260. SetThink( &CNihilanthHVR::TeleportThink );
  1261. SetTouch( &CNihilanthHVR::TeleportTouch );
  1262. SetNextThink( gpGlobals->curtime + 0.1 );
  1263. CPASAttenuationFilter filter( this );
  1264. EmitSound( filter, entindex(), "NihilanthHVR.TeleAttack" );
  1265. }
  1266. void CNihilanthHVR::MovetoTarget( Vector vecTarget )
  1267. {
  1268. if ( m_vecIdeal == vec3_origin )
  1269. {
  1270. m_vecIdeal = GetAbsVelocity();
  1271. }
  1272. // accelerate
  1273. float flSpeed = m_vecIdeal.Length();
  1274. if (flSpeed > 300)
  1275. {
  1276. VectorNormalize( m_vecIdeal );
  1277. m_vecIdeal= m_vecIdeal * 300;
  1278. }
  1279. Vector vTemp = vecTarget - GetAbsOrigin();
  1280. VectorNormalize( vTemp );
  1281. m_vecIdeal = m_vecIdeal + vTemp * 300;
  1282. SetAbsVelocity( m_vecIdeal );
  1283. }
  1284. void CNihilanthHVR::TeleportThink( void )
  1285. {
  1286. SetNextThink( gpGlobals->curtime + 0.1 );
  1287. // check world boundaries
  1288. if ( GetEnemy() == NULL || !GetEnemy()->IsAlive() || GetAbsOrigin().x < -4096 || GetAbsOrigin().x > 4096 || GetAbsOrigin().y < -4096 || GetAbsOrigin().y > 4096 || GetAbsOrigin().z < -4096 || GetAbsOrigin().z > 4096)
  1289. {
  1290. StopSound( entindex(), "NihilanthHVR.TeleAttack" );
  1291. UTIL_Remove( this );
  1292. UTIL_Remove( GetSprite() );
  1293. return;
  1294. }
  1295. if (( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin() ).Length() < 128)
  1296. {
  1297. StopSound( entindex(), "NihilanthHVR.TeleAttack" );
  1298. UTIL_Remove( this );
  1299. UTIL_Remove( GetSprite() );
  1300. if ( GetTarget() != NULL)
  1301. {
  1302. CLogicRelay *pRelay = (CLogicRelay*)GetTarget();
  1303. pRelay->m_OnTrigger.FireOutput( this, this );
  1304. }
  1305. if ( m_hTouch != NULL && GetEnemy() != NULL )
  1306. m_hTouch->Touch( GetEnemy() );
  1307. }
  1308. else
  1309. {
  1310. MovetoTarget( GetEnemy()->WorldSpaceCenter( ) );
  1311. }
  1312. CBroadcastRecipientFilter filterlight;
  1313. te->DynamicLight( filterlight, 0.0, &GetAbsOrigin(), 0, 255, 0, 0, 256, 1.0, 256 );
  1314. }
  1315. void CNihilanthHVR::TeleportTouch( CBaseEntity *pOther )
  1316. {
  1317. CBaseEntity *pEnemy = GetEnemy();
  1318. if (pOther == pEnemy)
  1319. {
  1320. if (GetTarget() != NULL)
  1321. {
  1322. if ( GetTarget() != NULL)
  1323. {
  1324. CLogicRelay *pRelay = (CLogicRelay*)GetTarget();
  1325. pRelay->m_OnTrigger.FireOutput( this, this );
  1326. }
  1327. }
  1328. if (m_hTouch != NULL && pEnemy != NULL )
  1329. m_hTouch->Touch( pEnemy );
  1330. }
  1331. else
  1332. {
  1333. m_pNihilanth->MakeFriend( GetAbsOrigin() );
  1334. }
  1335. SetTouch( NULL );
  1336. StopSound( entindex(), "NihilanthHVR.TeleAttack" );
  1337. UTIL_Remove( this );
  1338. UTIL_Remove( GetSprite() );
  1339. }
  1340. void CNihilanthHVR::GreenBallInit( )
  1341. {
  1342. SetMoveType( MOVETYPE_FLY );
  1343. SetSolid( SOLID_BBOX );
  1344. SetModel( "" );
  1345. CSprite *pSprite = SpriteInit( "sprites/exit1.spr", this );
  1346. if ( pSprite )
  1347. {
  1348. pSprite->SetScale( 1.0 );
  1349. pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
  1350. }
  1351. SetTouch( &CNihilanthHVR::RemoveTouch );
  1352. }
  1353. void CNihilanthHVR::RemoveTouch( CBaseEntity *pOther )
  1354. {
  1355. StopSound( entindex(), "NihilanthHVR.TeleAttack" );
  1356. UTIL_Remove( this );
  1357. UTIL_Remove( GetSprite() );
  1358. }