//========= Copyright Valve Corporation, All rights reserved. ============// // halloween_boss_base.cpp // Shared code for the Halloween Bosses // Michael Booth, October 2011 #include "cbase.h" #include "tf_player.h" #include "tf_gamerules.h" #include "halloween_base_boss.h" #include "tf_gamestats.h" //----------------------------------------------------------------------------------------------------- CHalloweenBaseBoss::CHalloweenBaseBoss() { m_wasSpawnedByCheats = false; } //----------------------------------------------------------------------------------------------------- CHalloweenBaseBoss::~CHalloweenBaseBoss() { } //----------------------------------------------------------------------------------------------------- void CHalloweenBaseBoss::Spawn( void ) { BaseClass::Spawn(); ConVarRef sv_cheats( "sv_cheats" ); if ( sv_cheats.IsValid() && sv_cheats.GetBool() ) { // remember we spawned with a cheat command m_wasSpawnedByCheats = true; } m_damagePerSecond = 0.0f; m_maxDamagePerSecond = 0.0f; if ( TFGameRules() ) { TFGameRules()->AddActiveBoss( this ); } // track how many players were playing when boss spawned CUtlVector< CTFPlayer * > playerVector; CollectPlayers( &playerVector, TF_TEAM_RED ); CollectPlayers( &playerVector, TF_TEAM_BLUE, false, APPEND_PLAYERS ); // status CTF_GameStats.Event_HalloweenBossEvent( GetBossType(), GetLevel(), HALLOWEEN_EVENT_BOSS_SPAWN, playerVector.Count(), 0.0f ); } //----------------------------------------------------------------------------------------------------- void CHalloweenBaseBoss::Update( void ) { BaseClass::Update(); UpdateDamagePerSecond(); } //----------------------------------------------------------------------------------------------------- void CHalloweenBaseBoss::UpdateOnRemove() { if ( TFGameRules() ) { TFGameRules()->RemoveActiveBoss( this ); } BaseClass::UpdateOnRemove(); } //----------------------------------------------------------------------------------------------------- int CHalloweenBaseBoss::OnTakeDamage( const CTakeDamageInfo &info ) { if ( info.GetDamage() && ( info.GetAttacker() != this ) ) { CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() ); if ( pAttacker && info.GetWeapon() ) { CTFWeaponBase *pWeapon = dynamic_cast( info.GetWeapon() ); if ( pWeapon ) { pWeapon->ApplyOnHitAttributes( this, pAttacker, info ); } } } return BaseClass::OnTakeDamage( info ); } //----------------------------------------------------------------------------------------------------- int CHalloweenBaseBoss::OnTakeDamage_Alive( const CTakeDamageInfo &rawInfo ) { CTakeDamageInfo info = rawInfo; if ( info.GetAttacker() && info.GetAttacker()->GetTeamNumber() == GetTeamNumber() ) { return 0; } if ( TFGameRules()->RoundHasBeenWon() ) { info.SetDamage( 0.0f ); } if ( info.GetDamageType() & DMG_CRITICAL ) { // do the critical damage increase info.SetDamage( info.GetDamage() * GetCritInjuryMultiplier() ); } // keep a list of everyone who hurt me, and when CTFPlayer *playerAttacker = ToTFPlayer( info.GetAttacker() ); if ( playerAttacker ) { CTFWeaponBase *attackerWeapon = assert_cast< CTFWeaponBase * >( info.GetWeapon() ); bool isMeleeAttack = attackerWeapon && attackerWeapon->IsMeleeWeapon(); RememberAttacker( playerAttacker, isMeleeAttack, info.GetDamage() ); for( int i=0; im_Shared.GetNumHealers(); ++i ) { CTFPlayer *medic = ToTFPlayer( playerAttacker->m_Shared.GetHealerByIndex( i ) ); if ( medic ) { // medics healing my attacker are also considered attackers // they do zero damage to keep DPS calculations sane RememberAttacker( medic, isMeleeAttack, 0.0f ); } } } // fire event for client combat text, beep, etc. IGameEvent *event = gameeventmanager->CreateEvent( "npc_hurt" ); if ( event ) { event->SetInt( "entindex", entindex() ); event->SetInt( "health", MAX( 0, GetHealth() ) ); event->SetInt( "damageamount", info.GetDamage() ); event->SetBool( "crit", ( info.GetDamageType() & DMG_CRITICAL ) ? true : false ); event->SetInt( "boss", GetBossType() ); CTFPlayer *attackerPlayer = ToTFPlayer( info.GetAttacker() ); if ( attackerPlayer ) { event->SetInt( "attacker_player", attackerPlayer->GetUserID() ); if ( attackerPlayer->GetActiveTFWeapon() ) { event->SetInt( "weaponid", attackerPlayer->GetActiveTFWeapon()->GetWeaponID() ); } else { event->SetInt( "weaponid", 0 ); } } else { // hurt by world event->SetInt( "attacker_player", 0 ); event->SetInt( "weaponid", 0 ); } gameeventmanager->FireEvent( event ); } return BaseClass::OnTakeDamage_Alive( info ); } void CHalloweenBaseBoss::Event_Killed( const CTakeDamageInfo &info ) { const CUtlVector< AttackerInfo > &attackerVector = GetAttackerVector(); for( int i=0; iCreateEvent( "halloween_boss_killed" ); if ( pEvent ) { pEvent->SetInt( "boss", GetBossType() ); pEvent->SetInt( "killer", attackerVector[i].m_attacker->GetUserID() ); gameeventmanager->FireEvent( pEvent, true ); } // Shoot a huge soul at players that have damaged the boss TFGameRules()->DropHalloweenSoulPack( 25, EyePosition(), attackerVector[i].m_attacker, TEAM_SPECTATOR ); } } BaseClass::Event_Killed( info ); } //--------------------------------------------------------------------------------------------- void CHalloweenBaseBoss::RememberAttacker( CTFPlayer *playerAttacker, bool wasMeleeHit, float damage ) { // record the damage for dps calculations DamageRateInfo info; info.m_damage = damage; info.m_timestamp = gpGlobals->curtime; m_damageVector.AddToTail( info ); int i; // has this player hurt me before for( i=0; ientindex() == playerAttacker->entindex() ) { // this player is hurting me again m_attackerVector[i].m_timestamp = gpGlobals->curtime; m_attackerVector[i].m_wasLastHitFromMeleeWeapon = wasMeleeHit; return; } } // new attacker AttackerInfo attackerInfo; attackerInfo.m_attacker = playerAttacker; attackerInfo.m_timestamp = gpGlobals->curtime; attackerInfo.m_wasLastHitFromMeleeWeapon = wasMeleeHit; m_attackerVector.AddToTail( attackerInfo ); } //--------------------------------------------------------------------------------------------- void CHalloweenBaseBoss::UpdateDamagePerSecond( void ) { const float windowDuration = 5.0f; int i; m_damagePerSecond = 0.0f; for( i=0; icurtime - m_damageVector[i].m_timestamp; if ( age > windowDuration ) { // too old continue; } m_damagePerSecond += m_damageVector[i].m_damage; } m_damagePerSecond /= windowDuration; if ( m_damagePerSecond > m_maxDamagePerSecond ) { m_maxDamagePerSecond = m_damagePerSecond; } // if ( m_damagePerSecond > 0.0001f ) // { // DevMsg( "%3.2f: dps = %3.2f\n", gpGlobals->curtime, m_damagePerSecond ); // } } //--------------------------------------------------------------------------------------------- void CHalloweenBaseBoss::Break( void ) { CPVSFilter filter( GetAbsOrigin() ); UserMessageBegin( filter, "BreakModel" ); WRITE_SHORT( GetModelIndex() ); WRITE_VEC3COORD( GetAbsOrigin() ); WRITE_ANGLES( GetAbsAngles() ); WRITE_SHORT( m_nSkin ); MessageEnd(); } //--------------------------------------------------------------------------------------------- /*static*/ CHalloweenBaseBoss* CHalloweenBaseBoss::SpawnBossAtPos( HalloweenBossType bossType, const Vector& vSpawnPos, int nTeam /*= TF_TEAM_HALLOWEEN*/, CBaseEntity* pOwner /*= NULL*/ ) { const char* pszBossType = NULL; switch ( bossType ) { case HALLOWEEN_BOSS_HHH: pszBossType = "headless_hatman"; break; case HALLOWEEN_BOSS_MONOCULUS: pszBossType = "eyeball_boss"; break; case HALLOWEEN_BOSS_MERASMUS: pszBossType = "merasmus"; break; default: AssertMsg( 0, "Invalid Halloween Boss Type" ); } CHalloweenBaseBoss *pBoss = NULL; if ( pszBossType ) { pBoss = dynamic_cast< CHalloweenBaseBoss * >( CreateEntityByName( pszBossType ) ); if ( pBoss ) { pBoss->SetAbsOrigin( vSpawnPos + Vector( 0, 0, 10.0f ) ); pBoss->ChangeTeam( nTeam ); pBoss->SetOwnerEntity( pOwner ); DispatchSpawn( pBoss ); } } return pBoss; }