1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
//========= 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<CBaseTrigger *> ( 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;
}
}
|