//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHarpoon::HarpoonTouch( CBaseEntity *pOther )
{
	// If we've stuck something, freeze. Make sure we hit it along our velocity.
	if ( pOther->GetCollisionGroup() != TFCOLLISION_GROUP_SHIELD )
	{
		// Perform the collision response...
		const trace_t &tr = CBaseEntity::GetTouchTrace( );

		Vector vecNewVelocity;
		PhysicsClipVelocity (GetAbsVelocity(), tr.plane.normal, vecNewVelocity, 2.0 - GetFriction());
		SetAbsVelocity( vecNewVelocity );
	}
	else
	{
		// Move away from the shield...
		// Fling it out a little extra along the plane normal
		Vector vecCenter;
		AngleVectors( pOther->GetAbsAngles(), &vecCenter );

		Vector vecNewVelocity;
		VectorMultiply( vecCenter, 400.0f, vecNewVelocity );
		SetAbsVelocity( vecNewVelocity );
	}

	if ( !pOther->IsBSPModel() && !pOther->GetBaseAnimating() )
		return;

	// At this point, it shouldn't affect player movement
	SetCollisionGroup( COLLISION_GROUP_DEBRIS );

	// Remove myself soon
	SetThink( SUB_Remove );
	SetNextThink( gpGlobals->curtime + 30.0 );

	m_hImpaledTarget = pOther;

	// Should I impale something?
	if ( pOther->GetBaseAnimating() )
	{
		CheckLinkedHarpoon();

		if ( pOther->GetMoveType() != MOVETYPE_NONE )
		{
			ImpaleTarget( pOther );
			return;
		}
	}

	CheckLinkedHarpoon();

	EmitSound( "Harpoon.Impact" );

	// Stop moving
	SetMoveType( MOVETYPE_NONE );
}
void CStickyBomb::Touch( CBaseEntity *pOther )
{
	// Don't stick if already stuck
	if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
	{
		trace_t tr = GetTouchTrace();
		// stickies don't stick to each other or sky
		if ( FClassnameIs(pOther, "grenade_stickybomb") || (tr.surface.flags & SURF_SKY) )
		{
			// bounce
			Vector vecNewVelocity;
			PhysicsClipVelocity( GetAbsVelocity(), tr.plane.normal, vecNewVelocity, 1.0 );
			SetAbsVelocity( vecNewVelocity );
		}
		else 
		{
			SetAbsVelocity( vec3_origin );
			SetMoveType( MOVETYPE_NONE );
			if ( pOther->entindex() != 0 )
			{
				// set up notification if the parent is deleted before we explode
				g_pNotify->AddEntity( this, pOther );

				if ( (tr.surface.flags & SURF_HITBOX) && modelinfo->GetModelType( pOther->GetModel() ) == mod_studio )
				{
					CBaseAnimating *pOtherAnim = dynamic_cast<CBaseAnimating *>(pOther);
					if ( pOtherAnim )
					{
						matrix3x4_t bombWorldSpace;
						MatrixCopy( EntityToWorldTransform(), bombWorldSpace );

						// get the bone info so we can follow the bone
						FollowEntity( pOther );
						SetOwnerEntity( pOther );
						m_boneIndexAttached = pOtherAnim->GetHitboxBone( tr.hitbox );
						matrix3x4_t boneToWorld;
						pOtherAnim->GetBoneTransform( m_boneIndexAttached, boneToWorld );

						// transform my current position/orientation into the hit bone's space
						// UNDONE: Eventually we need to intersect with the mesh here
						// REVISIT: maybe do something like the decal code to find a spot on
						//			the mesh.
						matrix3x4_t worldToBone, localMatrix;
						MatrixInvert( boneToWorld, worldToBone );
						ConcatTransforms( worldToBone, bombWorldSpace, localMatrix );
						MatrixAngles( localMatrix, m_boneAngles.GetForModify(), m_bonePosition.GetForModify() );
						return;
					}
				}
				SetParent( pOther );
			}
		}
	}
}
	void CBaseGrenadeProjectile::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
	{
		//Assume all surfaces have the same elasticity
		float flSurfaceElasticity = 1.0;

		//Don't bounce off of players with perfect elasticity
		if( trace.m_pEnt && trace.m_pEnt->IsPlayer() )
		{
			flSurfaceElasticity = 0.3;
		}

		// if its breakable glass and we kill it, don't bounce.
		// give some damage to the glass, and if it breaks, pass 
		// through it.
		bool breakthrough = false;

		if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable" ) )
		{
			breakthrough = true;
		}

		if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable_surf" ) )
		{
			breakthrough = true;
		}

		if (breakthrough)
		{
			CTakeDamageInfo info( this, this, 10, DMG_CLUB );
			trace.m_pEnt->DispatchTraceAttack( info, GetAbsVelocity(), &trace );

			ApplyMultiDamage();

			if( trace.m_pEnt->m_iHealth <= 0 )
			{
				// slow our flight a little bit
				Vector vel = GetAbsVelocity();

				vel *= 0.4;

				SetAbsVelocity( vel );
				return;
			}
		}
		
		float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
		flTotalElasticity = clamp( flTotalElasticity, 0.0f, 0.9f );

		// NOTE: A backoff of 2.0f is a reflection
		Vector vecAbsVelocity;
		PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
		vecAbsVelocity *= flTotalElasticity;

		// Get the total velocity (player + conveyors, etc.)
		VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
		float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );

		// Stop if on ground.
		if ( trace.plane.normal.z > 0.7f )			// Floor
		{
			// Verify that we have an entity.
			CBaseEntity *pEntity = trace.m_pEnt;
			Assert( pEntity );

			SetAbsVelocity( vecAbsVelocity );

			if ( flSpeedSqr < ( 30 * 30 ) )
			{
				if ( pEntity->IsStandable() )
				{
					SetGroundEntity( pEntity );
				}

				// Reset velocities.
				SetAbsVelocity( vec3_origin );
				SetLocalAngularVelocity( vec3_angle );

				//align to the ground so we're not standing on end
				QAngle angle;
				VectorAngles( trace.plane.normal, angle );

				// rotate randomly in yaw
				angle[1] = random->RandomFloat( 0, 360 );

				// TODO: rotate around trace.plane.normal
				
				SetAbsAngles( angle );			
			}
			else
			{
				Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;	
				Vector vecBaseDir = GetBaseVelocity();
				VectorNormalize( vecBaseDir );
				float flScale = vecDelta.Dot( vecBaseDir );

				VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity ); 
				VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
				PhysicsPushEntity( vecVelocity, &trace );
			}
		}
		else
		{
			// If we get *too* slow, we'll stick without ever coming to rest because
			// we'll get pushed down by gravity faster than we can escape from the wall.
			if ( flSpeedSqr < ( 30 * 30 ) )
			{
				// Reset velocities.
				SetAbsVelocity( vec3_origin );
				SetLocalAngularVelocity( vec3_angle );
			}
			else
			{
				SetAbsVelocity( vecAbsVelocity );
			}
		}
		
		BounceSound();
	}