//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: TF version of the stickybolt code.
//			I broke off our own version because I didn't want to accidentally break HL2.
// $Workfile:     $
// $Date:         $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "c_basetempentity.h"
#include "fx.h"
#include "decals.h"
#include "iefx.h"
#include "engine/IEngineSound.h"
#include "materialsystem/imaterialvar.h"
#include "IEffects.h"
#include "engine/IEngineTrace.h"
#include "vphysics/constraints.h"
#include "engine/ivmodelinfo.h"
#include "tempent.h"
#include "c_te_legacytempents.h"
#include "engine/ivdebugoverlay.h"
#include "c_te_effect_dispatch.h"
#include "c_tf_player.h"
#include "GameEventListener.h"
#include "tf_shareddefs.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

extern IPhysicsSurfaceProps *physprops;
IPhysicsObject *GetWorldPhysObject( void );

//-----------------------------------------------------------------------------
// Purpose: Creates a Bolt in the world and Ragdolls
// For Attached Bolts on players look at hud_bowcharge "arrow_impact" which should be moved here
//-----------------------------------------------------------------------------
void CreateCrossbowBoltTF( const Vector &vecOrigin, const Vector &vecDirection, const int iFlags, unsigned char nColor )
{
	const char* pszModelName = NULL;
	float flDirOffset = 5.0f;
	float flScale = 1.0f;
	float flLifeTime = 30.0f;
	switch ( iFlags )
	{
	case TF_PROJECTILE_STICKY_BALL:
		pszModelName = g_pszArrowModels[MODEL_SNOWBALL];
		break;
	case TF_PROJECTILE_ARROW:
		pszModelName = g_pszArrowModels[MODEL_ARROW_REGULAR];
		break;
	case TF_PROJECTILE_BUILDING_REPAIR_BOLT:
		pszModelName = g_pszArrowModels[MODEL_ARROW_BUILDING_REPAIR];
		flDirOffset = -2.0f;
		break;
	case TF_PROJECTILE_FESTIVE_ARROW:
		pszModelName = g_pszArrowModels[MODEL_FESTIVE_ARROW_REGULAR];
		break;
	case TF_PROJECTILE_HEALING_BOLT:
#ifdef STAGING_ONLY
	case TF_PROJECTILE_MILK_BOLT:
#endif
		pszModelName = g_pszArrowModels[MODEL_SYRINGE];
		flDirOffset = 0.0f;
		flScale = 3.0f;
		break;
	case TF_PROJECTILE_FESTIVE_HEALING_BOLT:
		pszModelName = g_pszArrowModels[MODEL_FESTIVE_HEALING_BOLT];
		flScale = 2.5f;
		break;
	case TF_PROJECTILE_BREAD_MONSTER:
	case TF_PROJECTILE_BREADMONSTER_JARATE:
	case TF_PROJECTILE_BREADMONSTER_MADMILK:
		pszModelName = g_pszArrowModels[MODEL_BREAD_MONSTER];
		flLifeTime = 8.0f;
		flScale = 2.5f;
		break;
	case TF_PROJECTILE_GRAPPLINGHOOK:
		pszModelName = g_pszArrowModels[MODEL_GRAPPLINGHOOK];
		flDirOffset = 0.0f;
		flLifeTime = 0.1f;
		break;
#ifdef STAGING_ONLY
	case TF_PROJECTILE_THROWING_KNIFE:
		pszModelName = g_pszArrowModels[MODEL_THROWING_KNIFE];
		break;
	case TF_PROJECTILE_SNIPERBULLET:
		pszModelName = g_pszArrowModels[MODEL_SYRINGE];
		break;
#endif // STAGING_ONLY
	default:
		// Unsupported Model
		Assert( 0 );
		pszModelName = g_pszArrowModels[MODEL_ARROW_REGULAR];
		return;
	}
	model_t *pModel = (model_t *)engine->LoadModel( pszModelName );

	QAngle vAngles;
	VectorAngles( vecDirection, vAngles );	
	C_LocalTempEntity *arrow = tempents->SpawnTempModel( pModel, vecOrigin - vecDirection * flDirOffset, vAngles, Vector(0, 0, 0 ), flLifeTime, FTENT_NONE );

	if ( arrow )
	{
		arrow->SetModelScale( flScale );
		arrow->m_nSkin = nColor;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void StickRagdollNowTF( 
	const Vector &vecOrigin, 
	const Vector &vecDirection, 
	const ClientEntityHandle_t &entHandle, 
	const int boneIndexAttached, 
	const int physicsBoneIndex, 
	const int iShooterIndex, 
	const int iHitGroup, 
	const int iVictim,
	const int iFlags,
	unsigned char nColor
) {
	Ray_t	shotRay;
	trace_t tr;
	
	UTIL_TraceLine( vecOrigin - vecDirection * 16, vecOrigin + vecDirection * 64, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
	if ( tr.surface.flags & SURF_SKY )
		return;

	C_BaseAnimating *pModel = dynamic_cast< C_BaseAnimating * >( entHandle.Get() );
	if ( pModel  )
	{
		IPhysicsObject	*pPhysicsObject = NULL;
		ragdoll_t *pRagdollT = NULL;
		if ( pModel->m_pRagdoll )
		{
			CRagdoll *pCRagdoll = dynamic_cast < CRagdoll * > ( pModel->m_pRagdoll );
			if ( pCRagdoll )
			{
				pRagdollT = pCRagdoll->GetRagdoll();
				if ( physicsBoneIndex < pRagdollT->listCount )
				{
					pPhysicsObject = pRagdollT->list[physicsBoneIndex].pObject;
				}
			}
		}

		IPhysicsObject *pReference = GetWorldPhysObject();

		if ( pReference == NULL || pPhysicsObject == NULL )
			return;

		float frand = (float) rand() / VALVE_RAND_MAX;
		Vector adjust = vecDirection*7 + vecDirection * frand * 7;

		Vector vecBonePos;
		QAngle boneAngles;
		pPhysicsObject->GetPosition( &vecBonePos, &boneAngles );

		QAngle angles;
		pPhysicsObject->SetPosition( vecOrigin-adjust, boneAngles, true );

		pPhysicsObject->EnableMotion( false );

		int nNodeIndex = pRagdollT->list[physicsBoneIndex].parentIndex;

		// find largest mass bone
		float flTargetMass = 0;
		for ( int i = 0; i < pRagdollT->listCount; i++ )
		{
			flTargetMass = MAX(flTargetMass, pRagdollT->list[i].pObject->GetMass() );
		}

		// walk the chain of bones from the pinned bone to the root and set each to the max mass
		// This helps transmit the impulses required to stabilize the constraint -- it keeps the body from
		// leaving the constraint because of some high mass bone hanging at the other end of the chain
		while ( nNodeIndex >= 0 )
		{
			if ( pRagdollT->list[nNodeIndex].pConstraint )
			{
				float flCurrentMass = pRagdollT->list[nNodeIndex].pObject->GetMass();
				flCurrentMass = MAX(flCurrentMass, flTargetMass);
				pRagdollT->list[nNodeIndex].pObject->SetMass( flCurrentMass );
			}
			nNodeIndex = pRagdollT->list[nNodeIndex].parentIndex;
		}
	}

	UTIL_ImpactTrace( &tr, 0 );

	CreateCrossbowBoltTF( vecOrigin, vecDirection, iFlags, nColor );

	//Achievement stuff.
	if ( iHitGroup == HITGROUP_HEAD )
	{
		CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
	
		if ( pLocalPlayer && pLocalPlayer->entindex() == iShooterIndex )
		{
			CTFPlayer *pVictim = ToTFPlayer( UTIL_PlayerByIndex( iVictim ) );

			if ( pVictim && pVictim->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
			{
				IGameEvent *event = gameeventmanager->CreateEvent( "player_pinned" );

				if ( event )
				{
					gameeventmanager->FireEventClientSide( event );
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void StickyBoltCallbackTF( const CEffectData &data )
{
	StickRagdollNowTF( 
		data.m_vOrigin, 
		data.m_vNormal, 
		data.m_hEntity, 
		data.m_nAttachmentIndex, 
		data.m_nMaterial, 
		data.m_nHitBox, 
		data.m_nDamageType, 
		data.m_nSurfaceProp, 
		data.m_fFlags, 
		data.m_nColor
	);
}

DECLARE_CLIENT_EFFECT( "TFBoltImpact", StickyBoltCallbackTF );