diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/halloween/halloween_behavior/crybaby_boss_attack.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/tf/halloween/halloween_behavior/crybaby_boss_attack.cpp')
| -rw-r--r-- | game/server/tf/halloween/halloween_behavior/crybaby_boss_attack.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/game/server/tf/halloween/halloween_behavior/crybaby_boss_attack.cpp b/game/server/tf/halloween/halloween_behavior/crybaby_boss_attack.cpp new file mode 100644 index 0000000..e2decd4 --- /dev/null +++ b/game/server/tf/halloween/halloween_behavior/crybaby_boss_attack.cpp @@ -0,0 +1,269 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// crybaby_boss_attack.cpp +// Halloween Boss 2011 chase and attack behavior +// Michael Booth, October 2011 + +#include "cbase.h" + +#include "tf_player.h" +#include "tf_gamerules.h" +#include "tf_team.h" +#include "nav_mesh/tf_nav_area.h" +#include "particle_parse.h" +#include "team_control_point_master.h" + +#include "../headless_hatman.h" +#include "crybaby_boss_attack.h" + + +//---------------------------------------------------------------------------------- +CCryBabyBossAttack::CCryBabyBossAttack( CTFPlayer *victim ) +{ + m_victim = victim; +} + + +//---------------------------------------------------------------------------------- +ActionResult< CHeadlessHatman > CCryBabyBossAttack::OnStart( CHeadlessHatman *me, Action< CHeadlessHatman > *priorAction ) +{ + // start animation + me->GetBodyInterface()->StartActivity( ACT_MP_RUN_ITEM1 ); + + m_axeSwingTimer.Invalidate(); + m_attackTimer.Invalidate(); + + m_attackTarget = NULL; + m_attackTargetFocusTimer.Invalidate(); + + return Continue(); +} + + +//---------------------------------------------------------------------------------- +bool CCryBabyBossAttack::IsSwingingAxe( void ) const +{ + return !m_axeSwingTimer.IsElapsed(); +} + + +//---------------------------------------------------------------------------------- +void CCryBabyBossAttack::UpdateAxeSwing( CHeadlessHatman *me ) +{ + if ( m_axeSwingTimer.HasStarted() ) + { + // continue axe swing + if ( m_axeSwingTimer.IsElapsed() ) + { + // moment of impact - did axe swing hit? + m_axeSwingTimer.Invalidate(); + + if ( m_attackTarget != NULL ) + { + Vector forward; + me->GetVectors( &forward, NULL, NULL ); + + Vector toVictim = m_attackTarget->WorldSpaceCenter() - me->WorldSpaceCenter(); + toVictim.NormalizeInPlace(); + + // looser tolerance as victim gets closer + const float closeRange = 100.0f; + float range = me->GetRangeTo( m_attackTarget ); + float closeness = ( range < closeRange ) ? 0.0f : ( range - closeRange ) / ( tf_halloween_bot_attack_range.GetFloat() - closeRange ); + float hitAngle = 0.0f + closeness * 0.27f; + + if ( DotProduct( forward, toVictim ) > hitAngle ) + { + if ( me->IsRangeLessThan( m_attackTarget, 0.9f * tf_halloween_bot_attack_range.GetFloat() ) ) + { + if ( me->IsLineOfSightClear( m_attackTarget ) ) + { + // CHOP! + CTakeDamageInfo info( me, me, 2.0f * m_attackTarget->GetHealth(), DMG_CLUB, TF_DMG_CUSTOM_DECAPITATION_BOSS ); + CalculateMeleeDamageForce( &info, toVictim, me->WorldSpaceCenter(), 5.0f ); + m_attackTarget->TakeDamage( info ); + me->EmitSound( "Halloween.HeadlessBossAxeHitFlesh" ); + } + } + } + } + + // always playe the axe-hit-world impact sound, since it carries through the world better + me->EmitSound( "Halloween.HeadlessBossAxeHitWorld" ); + UTIL_ScreenShake( me->GetAbsOrigin(), 15.0f, 5.0f, 1.0f, 1000.0f, SHAKE_START ); + + Vector bladePos; + QAngle bladeAngle; + if ( me->GetAxe() && me->GetAxe()->GetAttachment( "axe_blade", bladePos, bladeAngle ) ) + { + DispatchParticleEffect( "halloween_boss_axe_hit_world", bladePos, bladeAngle ); + } + } + } +} + + +//---------------------------------------------------------------------------------- +// Validate that our victim is still alive and someplace we can actually get to +bool CCryBabyBossAttack::IsVictimChaseable( CHeadlessHatman *me ) +{ + if ( m_victim == NULL ) + { + return false; + } + + if ( !m_victim->IsAlive() ) + { + return false; + } + + CTFNavArea *victimArea = (CTFNavArea *)m_victim->GetLastKnownArea(); + if ( !victimArea || victimArea->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE | TF_NAV_SPAWN_ROOM_RED ) ) + { + // unreachable + return false; + } + + if ( m_victim->GetGroundEntity() != NULL ) + { + Vector victimAreaPos; + victimArea->GetClosestPointOnArea( m_victim->GetAbsOrigin(), &victimAreaPos ); + if ( ( m_victim->GetAbsOrigin() - victimAreaPos ).AsVector2D().IsLengthGreaterThan( 50.0f ) ) + { + // off the mesh and unreachable + return false; + } + } + + return true; +} + + +//---------------------------------------------------------------------------------- +ActionResult< CHeadlessHatman > CCryBabyBossAttack::Update( CHeadlessHatman *me, float interval ) +{ + if ( !me->IsAlive() ) + { + return Done(); + } + + if ( !IsVictimChaseable( me ) ) + { + return Done( "Victim is dead or unreachable" ); + } + + if ( !IsSwingingAxe() && m_laughTimer.IsElapsed() ) + { + me->EmitSound( "Halloween.HeadlessBossLaugh" ); + m_laughTimer.Start( RandomFloat( 3.0f, 5.0f ) ); + } + + // chase after our chase victim + const float standAndSwingRange = 100.0f; + + if ( me->IsRangeGreaterThan( m_victim, standAndSwingRange ) || !me->IsLineOfSightClear( m_victim ) ) + { + CHeadlessHatmanPathCost cost( me ); + m_chasePath.Update( me, m_victim, cost ); + } + + if ( m_attackTargetFocusTimer.IsElapsed() || m_attackTarget == NULL ) + { + m_attackTarget = m_victim.Get(); + } + + // swing our axe at our attack target if they are in range + if ( m_attackTarget != NULL && m_attackTarget->IsAlive() ) + { + if ( me->IsRangeLessThan( m_attackTarget, tf_halloween_bot_attack_range.GetFloat() ) ) + { + if ( m_attackTimer.IsElapsed() ) + { + if ( !IsSwingingAxe() ) + { + me->AddGesture( ACT_MP_ATTACK_STAND_ITEM1 ); + m_axeSwingTimer.Start( 0.58f ); + me->EmitSound( "Halloween.HeadlessBossAttack" ); + + m_attackTimer.Start( 1.0f ); + } + } + + me->GetLocomotionInterface()->FaceTowards( m_attackTarget->WorldSpaceCenter() ); + } + } + + // finish axe swing once it has begun + UpdateAxeSwing( me ); + + if ( me->GetLocomotionInterface()->IsAttemptingToMove() ) + { + // play running animation + int healthRatio = 100 * me->GetHealth() / me->GetMaxHealth(); + + if ( healthRatio > 50 ) + { + if ( !me->GetBodyInterface()->IsActivity( ACT_MP_RUN_ITEM1 ) ) + { + me->GetBodyInterface()->StartActivity( ACT_MP_RUN_ITEM1 ); + } + } + else + { + if ( !me->GetBodyInterface()->IsActivity( ACT_MP_RUN_MELEE ) ) + { + me->GetBodyInterface()->StartActivity( ACT_MP_RUN_MELEE ); + } + } + + if ( m_footfallTimer.IsElapsed() ) + { + m_footfallTimer.Start( 0.25f ); + + // periodically shake nearby players' screens when we're moving + UTIL_ScreenShake( me->GetAbsOrigin(), 15.0f, 5.0f, 1.0f, 1000.0f, SHAKE_START ); + } + } + else + { + // standing still + if ( !me->GetBodyInterface()->IsActivity( ACT_MP_STAND_ITEM1 ) ) + { + me->GetBodyInterface()->StartActivity( ACT_MP_STAND_ITEM1 ); + } + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CHeadlessHatman > CCryBabyBossAttack::OnStuck( CHeadlessHatman *me ) +{ + // we're stuck - just warp to the our next path goal + if ( m_chasePath.GetCurrentGoal() ) + { + me->SetAbsOrigin( m_chasePath.GetCurrentGoal()->pos + Vector( 0, 0, 10.0f ) ); + } + + return TryContinue( RESULT_TRY ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CHeadlessHatman > CCryBabyBossAttack::OnContact( CHeadlessHatman *me, CBaseEntity *other, CGameTrace *result ) +{ + if ( other ) + { + CBaseCombatCharacter *combatChar = dynamic_cast< CBaseCombatCharacter * >( other ); + if ( combatChar && combatChar->IsAlive() ) + { + // force attack the thing we bumped into + // this prevents us from being stuck on dispensers, for example + m_attackTarget = combatChar; + m_attackTargetFocusTimer.Start( 3.0f ); + } + } + + return TryContinue( RESULT_TRY ); +} + |