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.

313 lines
8.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. // halloween_boss_base.cpp
  3. // Shared code for the Halloween Bosses
  4. // Michael Booth, October 2011
  5. #include "cbase.h"
  6. #include "tf_player.h"
  7. #include "tf_gamerules.h"
  8. #include "halloween_base_boss.h"
  9. #include "tf_gamestats.h"
  10. //-----------------------------------------------------------------------------------------------------
  11. CHalloweenBaseBoss::CHalloweenBaseBoss()
  12. {
  13. m_wasSpawnedByCheats = false;
  14. }
  15. //-----------------------------------------------------------------------------------------------------
  16. CHalloweenBaseBoss::~CHalloweenBaseBoss()
  17. {
  18. }
  19. //-----------------------------------------------------------------------------------------------------
  20. void CHalloweenBaseBoss::Spawn( void )
  21. {
  22. BaseClass::Spawn();
  23. ConVarRef sv_cheats( "sv_cheats" );
  24. if ( sv_cheats.IsValid() && sv_cheats.GetBool() )
  25. {
  26. // remember we spawned with a cheat command
  27. m_wasSpawnedByCheats = true;
  28. }
  29. m_damagePerSecond = 0.0f;
  30. m_maxDamagePerSecond = 0.0f;
  31. if ( TFGameRules() )
  32. {
  33. TFGameRules()->AddActiveBoss( this );
  34. }
  35. // track how many players were playing when boss spawned
  36. CUtlVector< CTFPlayer * > playerVector;
  37. CollectPlayers( &playerVector, TF_TEAM_RED );
  38. CollectPlayers( &playerVector, TF_TEAM_BLUE, false, APPEND_PLAYERS );
  39. // status
  40. CTF_GameStats.Event_HalloweenBossEvent( GetBossType(), GetLevel(), HALLOWEEN_EVENT_BOSS_SPAWN, playerVector.Count(), 0.0f );
  41. }
  42. //-----------------------------------------------------------------------------------------------------
  43. void CHalloweenBaseBoss::Update( void )
  44. {
  45. BaseClass::Update();
  46. UpdateDamagePerSecond();
  47. }
  48. //-----------------------------------------------------------------------------------------------------
  49. void CHalloweenBaseBoss::UpdateOnRemove()
  50. {
  51. if ( TFGameRules() )
  52. {
  53. TFGameRules()->RemoveActiveBoss( this );
  54. }
  55. BaseClass::UpdateOnRemove();
  56. }
  57. //-----------------------------------------------------------------------------------------------------
  58. int CHalloweenBaseBoss::OnTakeDamage( const CTakeDamageInfo &info )
  59. {
  60. if ( info.GetDamage() && ( info.GetAttacker() != this ) )
  61. {
  62. CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() );
  63. if ( pAttacker && info.GetWeapon() )
  64. {
  65. CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>( info.GetWeapon() );
  66. if ( pWeapon )
  67. {
  68. pWeapon->ApplyOnHitAttributes( this, pAttacker, info );
  69. }
  70. }
  71. }
  72. return BaseClass::OnTakeDamage( info );
  73. }
  74. //-----------------------------------------------------------------------------------------------------
  75. int CHalloweenBaseBoss::OnTakeDamage_Alive( const CTakeDamageInfo &rawInfo )
  76. {
  77. CTakeDamageInfo info = rawInfo;
  78. if ( info.GetAttacker() && info.GetAttacker()->GetTeamNumber() == GetTeamNumber() )
  79. {
  80. return 0;
  81. }
  82. if ( TFGameRules()->RoundHasBeenWon() )
  83. {
  84. info.SetDamage( 0.0f );
  85. }
  86. if ( info.GetDamageType() & DMG_CRITICAL )
  87. {
  88. // do the critical damage increase
  89. info.SetDamage( info.GetDamage() * GetCritInjuryMultiplier() );
  90. }
  91. // keep a list of everyone who hurt me, and when
  92. CTFPlayer *playerAttacker = ToTFPlayer( info.GetAttacker() );
  93. if ( playerAttacker )
  94. {
  95. CTFWeaponBase *attackerWeapon = assert_cast< CTFWeaponBase * >( info.GetWeapon() );
  96. bool isMeleeAttack = attackerWeapon && attackerWeapon->IsMeleeWeapon();
  97. RememberAttacker( playerAttacker, isMeleeAttack, info.GetDamage() );
  98. for( int i=0; i<playerAttacker->m_Shared.GetNumHealers(); ++i )
  99. {
  100. CTFPlayer *medic = ToTFPlayer( playerAttacker->m_Shared.GetHealerByIndex( i ) );
  101. if ( medic )
  102. {
  103. // medics healing my attacker are also considered attackers
  104. // they do zero damage to keep DPS calculations sane
  105. RememberAttacker( medic, isMeleeAttack, 0.0f );
  106. }
  107. }
  108. }
  109. // fire event for client combat text, beep, etc.
  110. IGameEvent *event = gameeventmanager->CreateEvent( "npc_hurt" );
  111. if ( event )
  112. {
  113. event->SetInt( "entindex", entindex() );
  114. event->SetInt( "health", MAX( 0, GetHealth() ) );
  115. event->SetInt( "damageamount", info.GetDamage() );
  116. event->SetBool( "crit", ( info.GetDamageType() & DMG_CRITICAL ) ? true : false );
  117. event->SetInt( "boss", GetBossType() );
  118. CTFPlayer *attackerPlayer = ToTFPlayer( info.GetAttacker() );
  119. if ( attackerPlayer )
  120. {
  121. event->SetInt( "attacker_player", attackerPlayer->GetUserID() );
  122. if ( attackerPlayer->GetActiveTFWeapon() )
  123. {
  124. event->SetInt( "weaponid", attackerPlayer->GetActiveTFWeapon()->GetWeaponID() );
  125. }
  126. else
  127. {
  128. event->SetInt( "weaponid", 0 );
  129. }
  130. }
  131. else
  132. {
  133. // hurt by world
  134. event->SetInt( "attacker_player", 0 );
  135. event->SetInt( "weaponid", 0 );
  136. }
  137. gameeventmanager->FireEvent( event );
  138. }
  139. return BaseClass::OnTakeDamage_Alive( info );
  140. }
  141. void CHalloweenBaseBoss::Event_Killed( const CTakeDamageInfo &info )
  142. {
  143. const CUtlVector< AttackerInfo > &attackerVector = GetAttackerVector();
  144. for( int i=0; i<attackerVector.Count(); ++i )
  145. {
  146. if ( attackerVector[i].m_attacker != NULL )
  147. {
  148. IGameEvent *pEvent = gameeventmanager->CreateEvent( "halloween_boss_killed" );
  149. if ( pEvent )
  150. {
  151. pEvent->SetInt( "boss", GetBossType() );
  152. pEvent->SetInt( "killer", attackerVector[i].m_attacker->GetUserID() );
  153. gameeventmanager->FireEvent( pEvent, true );
  154. }
  155. // Shoot a huge soul at players that have damaged the boss
  156. TFGameRules()->DropHalloweenSoulPack( 25, EyePosition(), attackerVector[i].m_attacker, TEAM_SPECTATOR );
  157. }
  158. }
  159. BaseClass::Event_Killed( info );
  160. }
  161. //---------------------------------------------------------------------------------------------
  162. void CHalloweenBaseBoss::RememberAttacker( CTFPlayer *playerAttacker, bool wasMeleeHit, float damage )
  163. {
  164. // record the damage for dps calculations
  165. DamageRateInfo info;
  166. info.m_damage = damage;
  167. info.m_timestamp = gpGlobals->curtime;
  168. m_damageVector.AddToTail( info );
  169. int i;
  170. // has this player hurt me before
  171. for( i=0; i<m_attackerVector.Count(); ++i )
  172. {
  173. if ( m_attackerVector[i].m_attacker && m_attackerVector[i].m_attacker->entindex() == playerAttacker->entindex() )
  174. {
  175. // this player is hurting me again
  176. m_attackerVector[i].m_timestamp = gpGlobals->curtime;
  177. m_attackerVector[i].m_wasLastHitFromMeleeWeapon = wasMeleeHit;
  178. return;
  179. }
  180. }
  181. // new attacker
  182. AttackerInfo attackerInfo;
  183. attackerInfo.m_attacker = playerAttacker;
  184. attackerInfo.m_timestamp = gpGlobals->curtime;
  185. attackerInfo.m_wasLastHitFromMeleeWeapon = wasMeleeHit;
  186. m_attackerVector.AddToTail( attackerInfo );
  187. }
  188. //---------------------------------------------------------------------------------------------
  189. void CHalloweenBaseBoss::UpdateDamagePerSecond( void )
  190. {
  191. const float windowDuration = 5.0f;
  192. int i;
  193. m_damagePerSecond = 0.0f;
  194. for( i=0; i<m_damageVector.Count(); ++i )
  195. {
  196. float age = gpGlobals->curtime - m_damageVector[i].m_timestamp;
  197. if ( age > windowDuration )
  198. {
  199. // too old
  200. continue;
  201. }
  202. m_damagePerSecond += m_damageVector[i].m_damage;
  203. }
  204. m_damagePerSecond /= windowDuration;
  205. if ( m_damagePerSecond > m_maxDamagePerSecond )
  206. {
  207. m_maxDamagePerSecond = m_damagePerSecond;
  208. }
  209. // if ( m_damagePerSecond > 0.0001f )
  210. // {
  211. // DevMsg( "%3.2f: dps = %3.2f\n", gpGlobals->curtime, m_damagePerSecond );
  212. // }
  213. }
  214. //---------------------------------------------------------------------------------------------
  215. void CHalloweenBaseBoss::Break( void )
  216. {
  217. CPVSFilter filter( GetAbsOrigin() );
  218. UserMessageBegin( filter, "BreakModel" );
  219. WRITE_SHORT( GetModelIndex() );
  220. WRITE_VEC3COORD( GetAbsOrigin() );
  221. WRITE_ANGLES( GetAbsAngles() );
  222. WRITE_SHORT( m_nSkin );
  223. MessageEnd();
  224. }
  225. //---------------------------------------------------------------------------------------------
  226. /*static*/ CHalloweenBaseBoss* CHalloweenBaseBoss::SpawnBossAtPos( HalloweenBossType bossType, const Vector& vSpawnPos, int nTeam /*= TF_TEAM_HALLOWEEN*/, CBaseEntity* pOwner /*= NULL*/ )
  227. {
  228. const char* pszBossType = NULL;
  229. switch ( bossType )
  230. {
  231. case HALLOWEEN_BOSS_HHH:
  232. pszBossType = "headless_hatman";
  233. break;
  234. case HALLOWEEN_BOSS_MONOCULUS:
  235. pszBossType = "eyeball_boss";
  236. break;
  237. case HALLOWEEN_BOSS_MERASMUS:
  238. pszBossType = "merasmus";
  239. break;
  240. default:
  241. AssertMsg( 0, "Invalid Halloween Boss Type" );
  242. }
  243. CHalloweenBaseBoss *pBoss = NULL;
  244. if ( pszBossType )
  245. {
  246. pBoss = dynamic_cast< CHalloweenBaseBoss * >( CreateEntityByName( pszBossType ) );
  247. if ( pBoss )
  248. {
  249. pBoss->SetAbsOrigin( vSpawnPos + Vector( 0, 0, 10.0f ) );
  250. pBoss->ChangeTeam( nTeam );
  251. pBoss->SetOwnerEntity( pOwner );
  252. DispatchSpawn( pBoss );
  253. }
  254. }
  255. return pBoss;
  256. }