//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: static_prop - don't move, don't animate, don't do anything. // physics_prop - move, take damage, but don't animate // //===========================================================================// #include "cbase.h" #include "tf_gamerules.h" #include "tf_props.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar tf_soccer_ball_up_max( "tf_soccer_ball_up_max", "350", FCVAR_CHEAT ); ConVar tf_soccer_ball_multiplier( "tf_soccer_ball_multiplier", "4", FCVAR_CHEAT ); ConVar tf_soccer_ball_min_speed( "tf_soccer_ball_min_speed", "30", FCVAR_CHEAT ); // Ranges from ~ .3 (side swipe) to 1 (full head on hit). When it's >= this value, // treat the hit as a full frontal 1.0 collision. ConVar tf_soccer_front_hit_range( "tf_soccer_front_hit_range", ".95", FCVAR_CHEAT ); extern ConVar tf_halloween_kart_dash_speed; extern ConVar tf_halloween_kart_normal_speed; LINK_ENTITY_TO_CLASS( prop_soccer_ball, CPropSoccerBall ); BEGIN_DATADESC( CPropSoccerBall ) DEFINE_KEYFIELD( m_iszTriggers, FIELD_STRING, "trigger_name" ), END_DATADESC() void CPropSoccerBall::Precache() { PrecacheScriptSound( "BumperCar.HitBall" ); } void CPropSoccerBall::Spawn() { BaseClass::Spawn(); SetSolid( SOLID_VPHYSICS ); // Use our vphys model for collision SetSolidFlags( FSOLID_TRIGGER ); // Generate Touch functions, but dont collide SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS ); // Need this one too so players dont get stopped SetTouch( &CPropSoccerBall::BallTouch ); SetContextThink( &CPropSoccerBall::TriggerTouchThink, gpGlobals->curtime + 0.2f, "TriggerTouchThink" ); } // Here's the deal. The ball is a trigger, but triggers are not allowed to touch other triggers. To get around this, // we're going to specify the names of the triggers we actually want to touch and then we're going to manually try to // touch them. Our collision system is a vortex of insanity. void CPropSoccerBall::TriggerTouchThink() { FOR_EACH_VEC( m_vecTriggers, i ) { if ( m_vecTriggers[i]->PointIsWithin( GetAbsOrigin() ) ) { m_vecTriggers[i]->StartTouch( this ); m_vecTriggers[i]->EndTouch( this ); } } SetContextThink( &CPropSoccerBall::TriggerTouchThink, gpGlobals->curtime + 0.2f, "TriggerTouchThink" ); } void CPropSoccerBall::Activate() { CBaseTrigger* pTrigger = NULL; do { pTrigger = dynamic_cast ( gEntList.FindEntityByName( pTrigger, m_iszTriggers.ToCStr() ) ); if ( pTrigger ) { m_vecTriggers.AddToTail( pTrigger ); } } while ( pTrigger ); BaseClass::Activate(); } bool CPropSoccerBall::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace ) { TestHitboxes( ray, 0xFFFFFFFF, trace ); if ( trace.DidHit() ) { IPhysicsObject *pObj = VPhysicsGetObject(); if ( pObj ) pObj->Wake(); } return false; } void CPropSoccerBall::BallTouch( CBaseEntity *pOther ) { if ( gpGlobals->curtime < m_flNextAllowedImpactTime ) return; CTFPlayer *pTFPlayer = ToTFPlayer( pOther ); if ( pTFPlayer ) { const float flSoccerBallMultiplier = tf_soccer_ball_multiplier.GetFloat(); // Get player direction and speed. Vector vPlayer( pOther->GetAbsVelocity().x, pOther->GetAbsVelocity().y, 0.0f ); float flSpeed = vPlayer.Length2D(); vPlayer.NormalizeInPlace(); // Linearly scale up based on kart speed. float fUp = tf_soccer_ball_up_max.GetFloat(); float fUpScale = flSpeed / tf_halloween_kart_dash_speed.GetFloat(); fUp = Clamp( fUp * fUpScale, 5.0f, fUp ) * flSoccerBallMultiplier; // Get vector to ball from player. Vector vToBall = GetAbsOrigin() - pOther->GetAbsOrigin(); vToBall.z = 0.0f; vToBall.NormalizeInPlace(); // cosTheta ranges from about .3 (side swipe) to 1 (full head on hit). float cosTheta = Max( 0.1f, vToBall.Dot( vPlayer ) ); // Scale speed based on incident angle and soccer ball multiplier hack. flSpeed = Max( flSpeed * cosTheta, tf_soccer_ball_min_speed.GetFloat() ); flSpeed *= flSoccerBallMultiplier; Vector vecVelocity; if ( cosTheta >= tf_soccer_front_hit_range.GetFloat() ) { // Front hit - snag player velocity and direction and use that. //DevMsg( "%s cosTheta: %.2f front hit\n", __FUNCTION__, cosTheta ); vecVelocity = vPlayer; } else { // Side swipe. Scale vector by player speed and hit angle. //DevMsg( "%s cosTheta: %.2f side hit\n", __FUNCTION__, cosTheta ); vecVelocity = vToBall; } vecVelocity *= flSpeed; vecVelocity.z = fUp; IPhysicsObject *pObj = VPhysicsGetObject(); pObj->Wake(); pObj->AddVelocity( &vecVelocity, NULL ); m_flNextAllowedImpactTime = gpGlobals->curtime + 0.1f; EmitSound( "BumperCar.HitBall" ); ChangeTeam( pTFPlayer->GetTeamNumber() ); m_hLastToucher = pTFPlayer; } }