#include "hud.h"
#include "parsemsg.h"
#include "const.h"
#include "cl_util.h"
#include "r_efx.h"
#include "../common/com_model.h"
#include "r_studioint.h"
#include "studio_util.h"
#include <math.h>
#include "triangleapi.h"
#include "StudioModelRenderer.h"
#include "GameStudioModelRenderer.h"


//TEST
#include "r_efx.h"
#include "event_api.h"
#include "event_args.h"
#include "in_defs.h"
#include "../common/pmtrace.h"
#include "pm_defs.h"

#define FOOTSKID_WIDTH 1.0

DECLARE_MESSAGE(m_Trail, SBCTrail);
DECLARE_MESSAGE(m_Trail, EETrail);
DECLARE_MESSAGE(m_Trail, KillTrail);
DECLARE_MESSAGE(m_Trail, DetachTrail);
DECLARE_MESSAGE(m_Trail, Flare);
DECLARE_MESSAGE(m_Trail, Ball);
DECLARE_MESSAGE(m_Trail, BlockStrug);
DECLARE_MESSAGE(m_Trail, PowStrug);

#define BEAM_TRAIL_RATIO 0.2
#define SBC_BEAM_TRAIL_RATIO 0.4
#define FLARE_BEAM_TRAIL_RATIO 0.6
#define SBC_RADIUS 10.0
#define FL_ANIMATETIME 0.05 //20 fps
#define BEAM_CONVERGE_SPEED 750

void VectorAngles( const float *forward, float *angles );
vec3_t GetFlareStart(const beam &b, int &succes);
void Draw_WorldSpriteEnt( HSPRITE sprite, vec3_t pos, float size, int RenderMode, TRICULLSTYLE tricullstyle,int r,int g, int b, int a, int frame);
		
//only 4 values, the circle isn't drawn in lowest quality mode
const int SBC_CIRCLE_LENGTH[] = {35,50,75,100};
const int SBC_POINTS_PER_CIRCLE[] = {13,9,7,5};

const int BEAMPART_SIZE[] = {5,50,100,150,300};
using namespace std;
list <sbc_trail> g_SBCTrails;
list <ent_ent_trail> g_EntEntTrails;
list <flare> g_Flares;
extern engine_studio_api_t IEngineStudio;
extern cvar_t *cl_beamquality;
extern CGameStudioModelRenderer g_StudioRenderer;
//VRMLData gVRMLBeamhead;

ent_ent_trail::ent_ent_trail(const ent_ent_trail &copy)
{
	head.bones = copy.head.bones;        
	head.bs = copy.head.bs;
	head.ps = copy.head.ps;
	head.bsent = copy.head.bsent;
	head.ent = copy.head.ent;
	head.flarea = copy.head.flarea;
	head.flareb = copy.head.flareb;
	head.skid = copy.head.skid;
	head.width = copy.head.width;
	int i=0;
	for(i=0;i<9;i++)
		head.flareframe[i] = copy.head.flareframe[i];

	starta = copy.starta;
	startb = copy.startb;
		
	//points = copy.points;
	list<vec3_t>::const_iterator iter = copy.points.begin();
	list<vec3_t>::const_iterator end = copy.points.end();
	while(iter!=end)
	{	points.push_back(*iter);
		iter++;
	}

	startent = copy.startent;
	attachment = copy.attachment;
	trail = copy.trail;
	width = copy.width;
	for (i=0;i<4;i++)
		startframe[i] = copy.startframe[i];
	trailframe = copy.trailframe;
	traillength = copy.traillength;
	detail = copy.detail;
}

flare::flare(const flare &copy)
{
	head.bones = copy.head.bones;        
	head.bs = copy.head.bs;
	head.ps = copy.head.ps;
	head.bsent = copy.head.bsent;
	head.ent = copy.head.ent;
	head.flarea = copy.head.flarea;
	head.flareb = copy.head.flareb;
	head.skid = copy.head.skid;
	head.width = copy.head.width;	
	for(int i=0;i<9;i++)
		head.flareframe[i] = copy.head.flareframe[i];
	
	maxlength = copy.maxlength;
	trail = copy.trail;	
	width = copy.width;
	
	trailframe = copy.trailframe;
	traillength = copy.traillength;
	
	//points = copy.points;
	list<vec3_t>::const_iterator iter = copy.points.begin();
	list<vec3_t>::const_iterator end = copy.points.end();
	while(iter!=end)
	{	points.push_back(*iter);
		iter++;
	}		
}

flare::~flare()
{
	/* TEST
	points.clear();
	*/
}

sbc_trail::sbc_trail(const sbc_trail &copy)
{
	startent = copy.startent;
	attachment = copy.attachment;
	startpos = copy.startpos;
	orbitpos = copy.orbitpos;
	pos = copy.pos;
	angle = copy.angle;

	//head = copy.head;
	head.bones = copy.head.bones;        
	head.bs = copy.head.bs;
	head.ps = copy.head.ps;
	head.bsent = copy.head.bsent;
	head.ent = copy.head.ent;
	head.flarea = copy.head.flarea;
	head.flareb = copy.head.flareb;
	head.skid = copy.head.skid;
	head.width = copy.head.width;	
	int i=0;
	for(i=0;i<9;i++)
		head.flareframe[i] = copy.head.flareframe[i];

	trail = copy.trail;	
	width = copy.width;
	trailframe = copy.trailframe;
	
	//orbithead = copy.orbithead;
	orbithead.bones = copy.orbithead.bones;        
	orbithead.bs = copy.orbithead.bs;
	orbithead.ps = copy.orbithead.ps;
	orbithead.bsent = copy.orbithead.bsent;
	orbithead.ent = copy.orbithead.ent;
	orbithead.flarea = copy.orbithead.flarea;
	orbithead.flareb = copy.orbithead.flareb;
	orbithead.skid = copy.orbithead.skid;
	orbithead.width = copy.orbithead.width;	
	for(i=0;i<9;i++)
		orbithead.flareframe[i] = copy.orbithead.flareframe[i];
	
	orbittrail = copy.orbittrail;
	orbitwidth = copy.orbitwidth;
	orbittrailframe = copy.orbittrailframe;

	list<vec3_t>::const_iterator iter = copy.points.begin();
	list<vec3_t>::const_iterator end = copy.points.end();
	while(iter!=end)
	{	points.push_back(*iter);
		iter++;
	}

	iter = copy.mainpoints.begin();
	end = copy.mainpoints.end();
	while(iter!=end)
	{	mainpoints.push_back(*iter);
		iter++;
	}	
}

int CHudTrail::Init()
{	
	HOOK_MESSAGE(SBCTrail);
	HOOK_MESSAGE(EETrail);
	HOOK_MESSAGE(Flare);
	HOOK_MESSAGE(Ball);
	HOOK_MESSAGE(KillTrail);
	HOOK_MESSAGE(DetachTrail);
	HOOK_MESSAGE(BlockStrug);
	HOOK_MESSAGE(PowStrug);
	
	gHUD.AddHudElem(this);
	m_iFlags |= HUD_ACTIVE;
	m_flLastDraw = 0;

	return 1;
}

int CHudTrail::VidInit()
{	m_hsprFootskid = LoadSprite("sprites/footskid.spr");
	//ParseVRML("models/beamhead.wrl", gVRMLBeamhead);
	return 1;
}

void CHudTrail::InitHUDMap()
{
	g_SBCTrails.clear();
	g_EntEntTrails.clear();
	g_Flares.clear();
	m_footskids.clear();
	m_balls.clear();
	m_flPreviousDraw = gHUD.m_flTime;
}

void CHudTrail::DrawTrails()
{	
	//TODO: should do all triapi drawing
	
	cl_entity_t *pClient;				
	//draw balls
	list <ball>::iterator biter = m_balls.begin();
	list <ball>::iterator blast = m_balls.end();
	while (biter!=blast)
	{	pClient = gEngfuncs.GetEntityByIndex(biter->ent);
		if (!pClient)
		{	continue;
		}
		vec3_t vecOrigin = pClient->curstate.origin;
		float flWidth = biter->width * pClient->curstate.scale;
		
		if(biter->bs)
		{	pClient = gEngfuncs.GetEntityByIndex( biter->bsent );
			if(!pClient)
			{	continue;
			}
			if (biter->bones->blCanUsePlayerPos) 
			{	vec3_t vecLeftShoulder = g_StudioRenderer.GetBoneOrigin(pClient,biter->bones->playerpos,"Bip01 L Arm");
				vec3_t vecLeftHand = g_StudioRenderer.GetBoneOrigin(pClient,biter->bones->playerpos,"Bip01 L Hand");
				
				vec3_t vecRightShoulder = g_StudioRenderer.GetBoneOrigin(pClient,biter->bones->playerpos,"Bip01 R Arm");
				vec3_t vecRightHand = g_StudioRenderer.GetBoneOrigin(pClient,biter->bones->playerpos,"Bip01 R Hand");
				
				vec3_t vecForward = (vecLeftHand-vecLeftShoulder+vecRightHand-vecRightShoulder);
				VectorNormalize(vecForward);

				vecOrigin = (vecLeftHand + vecRightHand)*0.5+vecForward * flWidth * 0.5;
			}
			else
			{	vecOrigin = pClient->curstate.origin;
			}
		}		
		
		Draw_WorldSpriteEnt(biter->sprite, vecOrigin,flWidth, kRenderTransAdd, TRI_FRONT, 255,255,255,255,biter->frame);		
		biter++;
	}

	//Update skid positions
	list <ent_ent_trail>::iterator efirst = g_EntEntTrails.begin();
	list <ent_ent_trail>::iterator elast = g_EntEntTrails.end();	
	while (efirst!=elast)
	{	--elast;
		if(elast->head.bs)
		{
			footskid *skid = elast->head.skid;
			if(skid)
			{	
				pClient = gEngfuncs.GetEntityByIndex( elast->head.bsent );
				
				pmtrace_t *tr;				
				tr = gEngfuncs.PM_TraceLine( pClient->curstate.origin, pClient->curstate.origin-vec3_t(0,0,6), PM_WORLD_ONLY, 2,  elast->head.ent);
				
				char buffer[255];
				sprintf(buffer,"Hit entity: %d\nFraction: %f",tr->ent, tr->fraction);
				ConsolePrint(buffer);
				//no skid if no longer on ground
				//if(pClient->curstate.onground == -1) <= doesn't work in MOVETYPE_RIDEBEAM. Fix this. For now hacked
				if (tr->fraction == 1.0)
				{	elast->head.skid = NULL;
				}
				else 
				{
					if (!skid->active)
					{	if(elast->head.bones->blCanUsePlayerPos)
						{	skid->footskid_start_left = g_StudioRenderer.GetBoneOrigin(pClient, elast->head.bones->playerpos, "Bip01 L Foot");
							skid->footskid_start_right = g_StudioRenderer.GetBoneOrigin(pClient, elast->head.bones->playerpos, "Bip01 R Foot");
							skid->footskid_end_left = skid->footskid_start_left;
							skid->footskid_end_right = skid->footskid_start_right;							
							
							AngleVectors(pClient->angles,NULL,skid->right,NULL);
							skid->active = true;
						}	
					}
					else
					{	skid->footskid_end_left = g_StudioRenderer.GetBoneOrigin(pClient, elast->head.bones->playerpos, "Bip01 L Foot");
						skid->footskid_end_right = g_StudioRenderer.GetBoneOrigin(pClient, elast->head.bones->playerpos, "Bip01 R Foot");						
					}
				}
			}
		}
	}		
	
	vector <footskid>::iterator iskid = m_footskids.begin();
	vector <footskid>::iterator iskidlast = m_footskids.end();	
	while (iskid!=iskidlast)
	{	DrawFootskids(*iskid);
		iskid++;
	}		
}

void CHudTrail::DrawFootskids(footskid skid)
{	
	if (!skid.active)
	{
		return;
	}
	
	if ( !gEngfuncs.pTriAPI->SpriteTexture( (struct model_s *)gEngfuncs.GetSpritePointer( m_hsprFootskid ), 0 ))
	{
		return;
	}
	
	gEngfuncs.pTriAPI->RenderMode( kRenderNormal );
	gEngfuncs.pTriAPI->CullFace( TRI_NONE ); //TODO: single face	
	gEngfuncs.pTriAPI->Begin( TRI_QUADS );
	gEngfuncs.pTriAPI->Color4ub(255,255,255,255);
	
	gEngfuncs.pTriAPI->Begin( TRI_QUADS );
	
	//LEFT
	gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );	
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_start_left.x - skid.right.x * FOOTSKID_WIDTH, skid.footskid_start_left.y - skid.right.y * FOOTSKID_WIDTH, skid.footskid_start_left.z);

	gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );	
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_end_left.x - skid.right.x * FOOTSKID_WIDTH, skid.footskid_end_left.y - skid.right.y * FOOTSKID_WIDTH, skid.footskid_end_left.z);
	
	gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_end_left.x + skid.right.x * FOOTSKID_WIDTH, skid.footskid_end_left.y + skid.right.y * FOOTSKID_WIDTH, skid.footskid_end_left.z);

	gEngfuncs.pTriAPI->TexCoord2f( 1, 0);
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_start_left.x + skid.right.x * FOOTSKID_WIDTH, skid.footskid_start_left.y + skid.right.y * FOOTSKID_WIDTH, skid.footskid_start_left.z);

	//RIGHT
	gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );	
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_start_right.x - skid.right.x * FOOTSKID_WIDTH, skid.footskid_start_right.y - skid.right.y * FOOTSKID_WIDTH, skid.footskid_start_right.z);

	gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );	
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_end_right.x - skid.right.x * FOOTSKID_WIDTH, skid.footskid_end_right.y - skid.right.y * FOOTSKID_WIDTH, skid.footskid_end_right.z);
	
	gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_end_right.x + skid.right.x * FOOTSKID_WIDTH, skid.footskid_end_right.y + skid.right.y * FOOTSKID_WIDTH, skid.footskid_end_right.z);

	gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
	gEngfuncs.pTriAPI->Vertex3f( skid.footskid_start_right.x + skid.right.x * FOOTSKID_WIDTH, skid.footskid_start_right.y + skid.right.y * FOOTSKID_WIDTH, skid.footskid_start_right.z);
	
	gEngfuncs.pTriAPI->End();	
}

int CHudTrail::getTrailFrame(int trail, int oldframe)
{	//update trail frames
	model_s *pTrailModel = IEngineStudio.GetModelByIndex(trail);
	int iFrame = oldframe+1;
	if (iFrame >= pTrailModel->numframes)
	{	iFrame = 0;
	}
	return iFrame;
}

void CHudTrail::AnimateStart(ent_ent_trail &trTrail)
{	model_s *paStartModel = IEngineStudio.GetModelByIndex(trTrail.starta);
	model_s *pbStartModel = IEngineStudio.GetModelByIndex(trTrail.startb);
	
	//so the progression is (from front to back) b,b,b,a
	trTrail.startframe[0]++;
	if (trTrail.startframe[0] >= pbStartModel->numframes)
	{	trTrail.startframe[0] = 0;
	}
	trTrail.startframe[1]++;
	if (trTrail.startframe[1] >= pbStartModel->numframes)
	{	trTrail.startframe[1] = 0;
	}
	trTrail.startframe[2]++;
	if (trTrail.startframe[2] >= pbStartModel->numframes)
	{	trTrail.startframe[2] = 0;
	}
	trTrail.startframe[3]++;
	if (trTrail.startframe[3] >= paStartModel->numframes)
	{	trTrail.startframe[3] = 0;
	}
}

void CHudTrail::AnimateBall(ball &b)
{
	model_s *pModel = IEngineStudio.GetModelByIndex(b.sprite);
	
	//so the progression is (from front to back) a,b,b,b,b,b,b,a,a
	b.frame++;
	if (b.frame >= pModel->numframes)
	{	b.frame=0;
	}
}

void CHudTrail::AnimateFlare(beam &head)
{
	model_s *paFlareModel = IEngineStudio.GetModelByIndex(head.flarea);
	model_s *pbFlareModel = IEngineStudio.GetModelByIndex(head.flareb);
	
	//so the progression is (from front to back) a,b,b,b,b,b,b,a,a
	head.flareframe[0]++;
	if (head.flareframe[0] >= paFlareModel->numframes)
	{	head.flareframe[0] = 0;
	}
	head.flareframe[1]++;
	if (head.flareframe[1] >= pbFlareModel->numframes)
	{	head.flareframe[1] = 0;
	}
	head.flareframe[2]++;
	if (head.flareframe[2] >= pbFlareModel->numframes)
	{	head.flareframe[2] = 0;
	}
	head.flareframe[3]++;
	if (head.flareframe[3] >= pbFlareModel->numframes)
	{	head.flareframe[3] = 0;
	}
	head.flareframe[4]++;
	if (head.flareframe[4] >= pbFlareModel->numframes)
	{	head.flareframe[4] = 0;
	}
	head.flareframe[5]++;
	if (head.flareframe[5] >= pbFlareModel->numframes)
	{	head.flareframe[5] = 0;
	}	
	head.flareframe[6]++;
	if (head.flareframe[6] >= pbFlareModel->numframes)
	{	head.flareframe[6] = 0;
	}
	head.flareframe[7]++;
	if (head.flareframe[7] >= paFlareModel->numframes)
	{	head.flareframe[7] = 0;
	}
	head.flareframe[8]++;
	if (head.flareframe[8] >= paFlareModel->numframes)
	{	head.flareframe[8] = 0;
	}
}

int CHudTrail::GetBeamQuality()
{
	int iQuality = cl_beamquality->value;
	if (iQuality<0)
		iQuality = 0;
	if (iQuality>4)
		iQuality = 4;
	return iQuality;
}

int CHudTrail::Draw(float flTime)
{	//get beam quality
	int iQuality = GetBeamQuality();
	
	float flTimeDiff = flTime - m_flPreviousDraw;
	m_flPreviousDraw = flTime;

	int fAnimate = false;
	if (flTime-m_flLastDraw>FL_ANIMATETIME||flTime<m_flLastDraw)
	{	m_flLastDraw = flTime;
		fAnimate = true;
	}
	
	
	{
	for (list<ball>::iterator iter=m_balls.begin();iter!=m_balls.end();iter++)
	{	if(fAnimate)
		{	AnimateBall(*iter);
		}
	}
	}{
	for (list<sbc_trail>::iterator iter=g_SBCTrails.begin(); iter != g_SBCTrails.end(); iter++)
	{	if(fAnimate)
		{	AnimateFlare(iter->head);
			iter->trailframe=getTrailFrame(iter->trail,iter->trailframe);
		}
		
		//only draw the circle if quality<4
		if (iQuality<4)
		{	if(fAnimate)
			{	AnimateFlare(iter->orbithead);
				iter->orbittrailframe=getTrailFrame(iter->orbittrail,iter->orbittrailframe);
			}		
			
			int succes;
			vec3_t vecOrigin = GetFlareStart(iter->head,succes);
			if (vecOrigin == vec3_t(0,0,0))
				continue;

			//create directional vectors
			vec3_t vecOldOrigin = iter->pos;		
			//vec3_t vecOrigin = pClient->curstate.origin;
			vec3_t vecDir = vecOrigin - vecOldOrigin;
		
			//only 0/1 points drawn yet, skip
			if (Length(vecDir)==0)
			{	continue;
			}
		

			vec3_t vecForward = vecDir;
			VectorNormalize(vecForward);
			vec3_t vecRight;
			vecRight.x = vecForward.y*vecForward.z;
			vecRight.y = -0.5 * vecForward.x * vecForward.z;
			vecRight.z = -0.5 * vecForward.x * vecForward.y;
			VectorNormalize(vecRight);		
			vec3_t vecUp = CrossProduct(vecForward,vecRight);
			VectorNormalize(vecUp);
		
			vec3_t vecOrbit = iter->orbitpos;
			float flLength = Length(vecDir);
			float flLengthStep = SBC_CIRCLE_LENGTH[iQuality]/SBC_POINTS_PER_CIRCLE[iQuality];
			float flAngleStep = 360.0/SBC_POINTS_PER_CIRCLE[iQuality];
			float flAngle = iter->angle;
		
			float flRadAngle;
			float flAngle2;
			vec3_t vecRight2,vecUp2,vecDst;
			while (flLength>flLengthStep)
			{
				flAngle+=flAngleStep;

				flAngle2=flAngle;			
			
				//set angle inbetween 0..360
				while (flAngle2>360)
					flAngle2-=360;
				if (flAngle2<0)
					flAngle2+=360;

				//select proper angle&axis
				if(flAngle2>90 && flAngle2<=180)
				{	vecRight2=vecUp;
					vecUp2=-vecRight;
					flAngle2-=90;
				}
				else if(flAngle2>180 && flAngle2<=270)
				{	vecRight2=-vecRight;
					vecUp2=-vecUp;
					flAngle2-=180;
				}
				else if(flAngle2>270)
				{	vecRight2=-vecUp;
					vecUp2=vecRight;
					flAngle2-=270;
				}			
				else
				{	vecRight2=vecRight;
					vecUp2=vecUp;
				}
			
				flRadAngle = flAngle2*2.0*M_PI/360.0;
				vecDst = cos(flRadAngle)*vecRight2 + sin(flRadAngle)*vecUp2;
				vecDst = vecDst * SBC_RADIUS;
		
				vecOldOrigin = vecOldOrigin + vecForward * flLengthStep;			
				vecOrbit = vecOldOrigin + vecDst;
			
				iter->points.push_front(vecOrbit);
	
				flLength -= flLengthStep;
			}
			
			//add points to middle trail (so it won't deform in a ps)
			float flDist = Length(vecOrigin-*iter->mainpoints.begin());
			if(flDist>BEAMPART_SIZE[iQuality])
			{	
				iter->mainpoints.push_front(vecOrigin);
			}
			iter->pos = vecOldOrigin;
			iter->orbitpos = vecOrbit;
			iter->angle = flAngle;
		}
	}	
	}{	
	//draw ent ent trails
	for (list<ent_ent_trail>::iterator iter= g_EntEntTrails.begin();iter!=g_EntEntTrails.end();iter++)
	{	if(fAnimate)
		{	AnimateFlare(iter->head);
			iter->trailframe=getTrailFrame(iter->trail,iter->trailframe);
			AnimateStart(*iter);
		}		
		cl_entity_t *pClient;
		
		pClient = gEngfuncs.GetEntityByIndex( iter->head.ent );
						
		if (!pClient)
		{	continue;
		}

		//client not in the fov, don't update
		//if (!InPVS(pClient))
		//	continue;		
		

		//prediction is evil! only use the coordinates the beam really passes
		vec3_t vecOrigin = pClient->curstate.origin;

		if (vecOrigin == vec3_t(0,0,0))
			continue;

		if (iter->head.bs || iter->head.ps)
		{
			//beam converge
			list<vec3_t>::iterator iter2 = iter->points.begin();
			list<vec3_t>::iterator iter2_end = iter->points.end();
			list<vec3_t>::iterator last = iter->points.end();
			last--;
			vec3_t vecBegin = *(iter2);
			vec3_t vecEnd = *(last);
			vec3_t vecIdeal = vecEnd - vecBegin;
			vec3_t vecCurrent = vecBegin;
			//int iSize = iter->points.size()-1;		
			//int i=0;
			float flLength = 0;
			float flMovement = flTimeDiff * BEAM_CONVERGE_SPEED;
			float flMovementModifier = 1.0;
			vec3_t vecPrev;
			vec3_t vecDir;
			float flTrailLength = 0;
			vec3_t vecOld = vecBegin;
			while (iter2 != iter2_end)
			{
				vecDir = vecCurrent - *iter2;	
				vecPrev = *iter2;
				if (vecDir.Length()<flMovement*flMovementModifier)
				{
					*iter2 = vecCurrent;
				}
				else
				{
					*iter2 = *iter2 + vecDir.Normalize()*flMovement*flMovementModifier;
					flTrailLength+=(*iter2-vecOld).Length();
					vecOld = *iter2;
				}
				//i++;			
				//vecCurrent = vecBegin + ((float)i/(float)iSize) * vecIdeal;						
				iter2++;
				if(iter2!=iter2_end)
				{	flLength += (vecPrev-*iter2).Length();
					vecCurrent = vecBegin + (flLength/iter->traillength) * vecIdeal;
					flMovementModifier = 1.0 - 0.9 * (flLength/iter->traillength);
				}
			}
			iter->traillength=flTrailLength;
			//don't add parts when ps/bs'ing, the beam will go straight anyways		
			continue;
		}

		float flDist = Length(vecOrigin-*iter->points.begin());			
		//Handle high detail beams, redraw whenever the server updates them
		if (iter->detail == HIGH_DETAIL && flDist>0)
		{	
			iter->traillength += flDist;
			iter->points.push_front(vecOrigin);
			continue;
		}
			
		//Handle medium detail beams
		if (iter->detail == MEDIUM_DETAIL)
		{	if(flDist>BEAMPART_SIZE[iQuality])
			{	
				iter->traillength += flDist;
				iter->points.push_front(vecOrigin);
			}
		}
		//low detail beams don't get points added
	}
	}{
	//draw flares
	for (list<flare>::iterator iter = g_Flares.begin();iter!=g_Flares.end();iter++)
	{	
		if(fAnimate)
		{	AnimateFlare(iter->head);
			iter->trailframe=getTrailFrame(iter->trail,iter->trailframe);			
		}
		
		//cl_entity_t *pClient;
		//pClient = gEngfuncs.GetEntityByIndex( iter->head.ent );
		int succes;
		vec3_t vecOrigin = GetFlareStart(iter->head,succes);
		if (vecOrigin == vec3_t(0,0,0))
			continue;
		
		//get traillength
		iter->traillength = LimitTrailLength(iter->points, iter->maxlength, vecOrigin);
		
		float flDist = Length(vecOrigin-*iter->points.begin());
		if(flDist>BEAMPART_SIZE[iQuality]*0.1)
		{	iter->traillength += flDist;
			iter->points.push_front(vecOrigin);						
		}				
	}
	}
	return 1;	
}

float CHudTrail::LimitTrailLength(list <vec3_t> &points, float length, vec3_t vecOrigin)
{	float flLength=0;
	float flCurrentLength=0;
	vec3_t vecPrevious=vecOrigin;
	list <vec3_t>::iterator iter;
	for (iter = points.begin();iter!=points.end();iter++)
	{	flCurrentLength=Length(vecPrevious-*iter);
		flLength+=flCurrentLength;
		if(flLength>length)
			break;
		vecPrevious = *iter;
	}

	if (flCurrentLength>0 && flLength>0)
	{	float flLengthDiff = flLength-length;
		if (flLengthDiff>0)
		{	//too long, shrink
			vec3_t vecDir = *iter-vecPrevious;
			VectorNormalize(vecDir);		
			*iter = vecPrevious + vecDir * (flCurrentLength-flLengthDiff);
			++iter;
			if(iter!=points.end())
			{	points.erase(iter,points.end());
			}
			return length;
		}
	}
	return flLength;
}

list<sbc_trail>::iterator CHudTrail::FindSBCTrail(int target)
{	for (list<sbc_trail>::iterator iter = g_SBCTrails.begin();iter!=g_SBCTrails.end();iter++)
	{	
		if(iter->head.ent==target)
		{	return iter;
		}
	}
	return g_SBCTrails.end();
}

list<ent_ent_trail>::iterator CHudTrail::FindEntEntTrail(int target)
{	//find the trail
	for(list<ent_ent_trail>::iterator iter= g_EntEntTrails.begin();iter!=g_EntEntTrails.end();iter++)
	{	if(iter->head.ent==target)
		{	return iter;
		}
	}
	return g_EntEntTrails.end();
}

list<flare>::iterator CHudTrail::FindFlare(int target)
{	//find the flare
	for(list<flare>::iterator iter = g_Flares.begin();iter!=g_Flares.end();iter++)
	{	if(iter->head.ent==target)
		{	return iter;
		}
	}
	return g_Flares.end();
}

list<ball>::iterator CHudTrail::FindBall(int target)
{	//find the ball
	for(list<ball>::iterator iter = m_balls.begin();iter!=m_balls.end();iter++)
	{	if(iter->ent == target)
		{	return iter;
		}
	}
	return m_balls.end();
}

void CHudTrail::RemoveBall(int target)
{
	//find the trail
	list<ball>::iterator pRemoveBall = FindBall(target);
	
	while (pRemoveBall!=m_balls.end())
	{	if (pRemoveBall->bs)
		{	gHUD.m_ManipModel.Remove( pRemoveBall->bones );
		}
		m_balls.erase(pRemoveBall);				
		pRemoveBall = FindBall(target);
	}
}

void CHudTrail::RemoveSBCTrail(int target)
{	
	//find the trail
	list<sbc_trail>::iterator pRemoveTrail = FindSBCTrail(target);
	
	while (pRemoveTrail!=g_SBCTrails.end())
	{	if (pRemoveTrail->head.bs)
		{	gHUD.m_ManipModel.Remove( pRemoveTrail->head.bones );
		}
		g_SBCTrails.erase(pRemoveTrail);				
		pRemoveTrail = FindSBCTrail(target);
	}
}

void CHudTrail::RemoveEETrail(int target)
{	//find the trail
	list<ent_ent_trail>::iterator pRemoveTrail = FindEntEntTrail(target);
	
	while (pRemoveTrail!=g_EntEntTrails.end())
	{	if (pRemoveTrail->head.bs)
		{	gHUD.m_ManipModel.Remove( pRemoveTrail->head.bones );
		}
		g_EntEntTrails.erase(pRemoveTrail);
		pRemoveTrail = FindEntEntTrail(target);
	}
}

void CHudTrail::RemoveFlare(int target)
{	list<flare>::iterator pRemoveFlare = FindFlare(target);
	while(pRemoveFlare!=g_Flares.end())
	{	if (pRemoveFlare->head.bs)
		{	gHUD.m_ManipModel.Remove( pRemoveFlare->head.bones );
		}
		g_Flares.erase(pRemoveFlare);
		pRemoveFlare = FindFlare(target);
	}
}

int CHudTrail::MsgFunc_KillTrail(const char *pszName,  int iSize, void *pbuf )
{	vec3_t vecStart;
	BEGIN_READ( pbuf, iSize );
	int iTarget = READ_LONG();
	
	RemoveEETrail(iTarget);
	RemoveSBCTrail(iTarget);
	RemoveFlare(iTarget);
	RemoveBall(iTarget);
	return 1;
}

void CHudTrail::AddEntEntTrail(int startent, int endent, int attachment, int starta, int startb, int flarea, int flareb, int trail, int width, int detail, vec3_t vecOrigin)
{	
	ent_ent_trail eet;
	
	eet.points.push_front(vecOrigin);
	
	eet.startent = startent;
	eet.head.ent = endent;
	eet.head.bs = false;
	eet.head.ps = false;
	eet.head.skid = NULL;
	eet.attachment = attachment;
	eet.starta = starta;
	eet.startb = startb;
	eet.head.flarea = flarea;		
	eet.head.flareb = flareb;
	eet.trail = trail;
	eet.traillength = 0;
	eet.width = (float)width * BEAM_TRAIL_RATIO;
	eet.head.width = (float)width;
	eet.trailframe = 0;
	eet.detail = (beam_detail)detail;
		
	model_s *paStartModel = IEngineStudio.GetModelByIndex(starta);
	model_s *pbStartModel = IEngineStudio.GetModelByIndex(startb);
	//so the progression is (from front to back) b,b,b,a
	eet.startframe[0] = gEngfuncs.pfnRandomLong(0,pbStartModel->numframes-1);
	eet.startframe[1] = gEngfuncs.pfnRandomLong(0,pbStartModel->numframes-1);
	eet.startframe[2] = gEngfuncs.pfnRandomLong(0,pbStartModel->numframes-1);
	eet.startframe[3] = gEngfuncs.pfnRandomLong(0,paStartModel->numframes-1);
		
	model_s *paFlareModel = IEngineStudio.GetModelByIndex(flarea);
	model_s *pbFlareModel = IEngineStudio.GetModelByIndex(flareb);
	
	eet.head.flareframe[0] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	eet.head.flareframe[1] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	eet.head.flareframe[2] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	eet.head.flareframe[3] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	eet.head.flareframe[4] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	eet.head.flareframe[5] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	eet.head.flareframe[6] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	eet.head.flareframe[7] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	eet.head.flareframe[8] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	g_EntEntTrails.push_back(eet);
	
}

void CHudTrail::AddSBCTrail(int iStartEnt,int iEndEnt,int iStartAttachment,int iFlareA,int iFlareB,int iBeam,int iSize,int iOrbitFlareA,int iOrbitFlareB,int iOrbitBeam,int iOrbitSize)
{	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(iStartEnt);
	
	vec3_t vecStart;
	if (pClient)
	{	if (iStartAttachment)
		{	
			vecStart = pClient->attachment[iStartAttachment-1];
		}
		else
		{	vecStart = pClient->origin;
		}
	}
	
	sbc_trail s;	
	s.angle = 0.0;
	s.startpos = vecStart;
	s.pos = vecStart;
	s.orbitpos = vecStart;
	
	s.points.push_front(vecStart);
	s.mainpoints.push_front(vecStart);
	s.startent = iStartEnt;
	s.head.ent = iEndEnt;
	s.attachment = iStartAttachment;

	s.head.flarea = iFlareA;		
	s.head.flareb = iFlareB;
	s.head.bs = false;
	s.head.ps = false;
	s.head.skid = NULL;
	s.trail = iBeam;
	s.width = (float)iSize * SBC_BEAM_TRAIL_RATIO;
	s.head.width = (float)iSize;
	s.trailframe = 0;

	s.orbithead.flarea = iOrbitFlareA;		
	s.orbithead.flareb = iOrbitFlareB;
	s.orbittrail = iOrbitBeam;
	s.orbitwidth = (float)iOrbitSize * SBC_BEAM_TRAIL_RATIO;
	s.orbithead.width = iOrbitSize;
	s.orbittrailframe = 0;
		
				
	model_s *paFlareModel = IEngineStudio.GetModelByIndex(iFlareA);
	model_s *pbFlareModel = IEngineStudio.GetModelByIndex(iFlareB);
	s.head.flareframe[0] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	s.head.flareframe[1] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.head.flareframe[2] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.head.flareframe[3] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.head.flareframe[4] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.head.flareframe[5] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.head.flareframe[6] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.head.flareframe[7] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	s.head.flareframe[8] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);

	paFlareModel = IEngineStudio.GetModelByIndex(iOrbitFlareA);
	pbFlareModel = IEngineStudio.GetModelByIndex(iOrbitFlareB);
	s.orbithead.flareframe[0] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	s.orbithead.flareframe[1] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.orbithead.flareframe[2] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.orbithead.flareframe[3] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.orbithead.flareframe[4] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.orbithead.flareframe[5] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.orbithead.flareframe[6] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	s.orbithead.flareframe[7] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	s.orbithead.flareframe[8] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);

	g_SBCTrails.push_back(s);
}

void CHudTrail::AddFlare(int iEnt, int iFlareA, int iFlareB, int iTrail, int iLength, int iWidth, vec3_t vecStart)
{	flare f;
	f.maxlength = iLength;
	f.width = (float)iWidth * FLARE_BEAM_TRAIL_RATIO;
	f.trail = iTrail;
	f.trailframe = 0;
	f.traillength = 0;		
	f.points.push_front(vecStart);
		
	f.head.ent = iEnt;
	f.head.flarea = iFlareA;		
	f.head.flareb = iFlareB;
	f.head.width = (float)iWidth;
	f.head.bs = false;
	f.head.ps = false;
	f.head.skid = NULL;
		
	model_s *paFlareModel = IEngineStudio.GetModelByIndex(iFlareA);
	model_s *pbFlareModel = IEngineStudio.GetModelByIndex(iFlareB);
	f.head.flareframe[0] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	f.head.flareframe[1] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	f.head.flareframe[2] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	f.head.flareframe[3] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	f.head.flareframe[4] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	f.head.flareframe[5] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	f.head.flareframe[6] = gEngfuncs.pfnRandomLong(0,pbFlareModel->numframes-1);
	f.head.flareframe[7] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	f.head.flareframe[8] = gEngfuncs.pfnRandomLong(0,paFlareModel->numframes-1);
	g_Flares.push_back(f);
}

void CHudTrail::AddBall(int iEnt,int iSprite,int iWidth)
{
	ball bal;
	bal.bones = NULL;
	bal.bs = false;
	bal.ps = false;
	bal.ent = iEnt;
	bal.skid = NULL;
	bal.sprite = iSprite;
	bal.width = iWidth;
	bal.frame = 0;
	m_balls.push_back(bal);
}

int CHudTrail::MsgFunc_SBCTrail(const char *pszName,  int iSize, void *pbuf )
{	vec3_t vecStart;
	BEGIN_READ( pbuf, iSize );
	int iStartEnt = READ_BYTE();
	int iEndEnt = READ_LONG();
	unsigned char iStartAttachment = READ_BYTE();
	int iFlareA = READ_SHORT();
	int iFlareB = READ_SHORT();
	int iBeam = READ_SHORT();
	int iWidth = READ_BYTE() * 10;

	int iOrbitFlareA = READ_SHORT();
	int iOrbitFlareB = READ_SHORT();
	int iOrbitBeam = READ_SHORT();
	int iOrbitWidth = READ_BYTE() * 10;
	
	AddSBCTrail(iStartEnt, iEndEnt, iStartAttachment, iFlareA, iFlareB, iBeam, iWidth,iOrbitFlareA, iOrbitFlareB, iOrbitBeam, iOrbitWidth);
	return 1;
}

int CHudTrail::MsgFunc_EETrail(const char *pszName,  int iSize, void *pbuf )
{	vec3_t vecStart;
	BEGIN_READ( pbuf, iSize );
	int iStartEnt = READ_LONG();
	int iEndEnt = READ_LONG();
	unsigned char iStartAttachment = READ_BYTE();
	short iStartA = READ_SHORT();
	short iStartB = READ_SHORT();
	short iFlareA = READ_SHORT();
	short iFlareB = READ_SHORT();
	short iTrail = READ_SHORT();
	int iWidth = READ_BYTE() * 10;
	unsigned char iDetail = READ_BYTE();
	vec3_t vecOrigin;
	vecOrigin.x = READ_COORD();
	vecOrigin.y = READ_COORD();
	vecOrigin.z = READ_COORD();
	AddEntEntTrail(iStartEnt, iEndEnt, iStartAttachment, iStartA, iStartB, iFlareA, iFlareB,iTrail, iWidth, iDetail,vecOrigin);

	return 1;
}

int CHudTrail::MsgFunc_Flare(const char *pszName,  int iSize, void *pbuf )
{	
	BEGIN_READ( pbuf, iSize );
	int iEnt = READ_LONG();
	int iFlareA = READ_SHORT();
	int iFlareB = READ_SHORT();	
	int iWidth = READ_SHORT() * 10;
	int iTrail = READ_SHORT();
	int iLength = READ_SHORT();
	vec3_t vecStart;
	for (int i=0 ; i<3 ; i++)
		vecStart[i] = READ_COORD();

	AddFlare(iEnt, iFlareA, iFlareB, iTrail, iLength,iWidth,vecStart);
	return 1;
}

int CHudTrail::MsgFunc_Ball(const char *pszName,  int iSize, void *pbuf )
{	
	BEGIN_READ( pbuf, iSize );
	int iEnt = READ_SHORT();
	int iSprite = READ_SHORT();
	int iWidth = READ_SHORT() * 10;

	AddBall(iEnt, iSprite, iWidth);
	return 1;
}

int CHudTrail::MsgFunc_DetachTrail(const char *pszName,  int iSize, void *pbuf )
{	BEGIN_READ( pbuf, iSize );
	int iTarget = READ_LONG();
	
	list<ball>::iterator pBall = FindBall(iTarget);
	if (pBall!=m_balls.end())
	{
		if(pBall->bs)
		{	pBall->bs = false;
			pBall->skid = NULL;
			gHUD.m_ManipModel.Remove( pBall->bones );
		}
	}

	list<flare>::iterator pFlare = FindFlare(iTarget);
	if (pFlare != g_Flares.end())
	{
		if (pFlare->head.bs)		
		{	
			pFlare->head.bs = false;
			pFlare->head.skid = NULL;
			gHUD.m_ManipModel.Remove( pFlare->head.bones );
		}		
	}	
	
	list<sbc_trail>::iterator pSBC = FindSBCTrail(iTarget);
	if (pSBC != g_SBCTrails.end())
	{
		if (pSBC->head.bs)		
		{	
			pSBC->head.bs = false;
			pSBC->head.skid = NULL;
			gHUD.m_ManipModel.Remove( pSBC->head.bones );
		}		
	}	

	list<ent_ent_trail>::iterator pBeam = FindEntEntTrail(iTarget);		
	while (pBeam != g_EntEntTrails.end())
	{	flare f;
		f.head.flarea = pBeam->head.flarea;
		f.head.flareb = pBeam->head.flareb;
		f.head.bs = false;
		f.head.skid = NULL;
			
		if (pBeam->head.bs)
		{	gHUD.m_ManipModel.Remove( pBeam->head.bones );
		}

		for(int i=0;i<9;i++)
		{	f.head.flareframe[i] = pBeam->head.flareframe[i];
		}
				
		cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(pBeam->head.ent);
		vec3_t vecStart;
		if (pClient)
		{	vecStart = pClient->origin;			
		}
		else
		{	ConsolePrint("Flare with invalid ent\n");					
		}		

		f.head.ent = pBeam->head.ent;
		f.head.width = pBeam->head.width;
		f.width = pBeam->head.width * 0.75;
		f.maxlength = 250;//send length over?
		f.trail = pBeam->trail;
		f.trailframe = pBeam->trailframe;
		f.traillength = 0;
		f.points.push_front(vecStart);
		g_Flares.push_back(f);
		g_EntEntTrails.erase(pBeam);
		pBeam = FindEntEntTrail(iTarget);
	}

	return 1;
}

void CHudTrail::SetBlockStruggle(beam &head, int player)
{
	head.bs = true;
	head.bsent = player;
	head.bones = gHUD.m_ManipModel.AddRecordBone( player );
		
	cl_entity_t *pClient;
	pClient = gEngfuncs.GetEntityByIndex( player );
	if (pClient)
	{
		/* disabled as long as i don't fix the skids bug, drawing skids over cliffs
		if(pClient->curstate.onground == 0)
		{
			footskid skid;
			skid.active = false;
			m_footskids.push_back(skid);
			vector <footskid>::iterator skiditer = m_footskids.end();
			skiditer--;
			head.skid = skiditer;				
		}
		*/
	}
}

int CHudTrail::MsgFunc_BlockStrug(const char *pszName,  int iSize, void *pbuf )
{	BEGIN_READ( pbuf, iSize );
	int iEnt = READ_SHORT();
	int iPlayerEnt = READ_SHORT();

	list<ent_ent_trail>::iterator pEEBeam = FindEntEntTrail(iEnt);
	if (pEEBeam != g_EntEntTrails.end())
	{
		SetBlockStruggle(pEEBeam->head,iPlayerEnt);		
	}

	list<flare>::iterator pFlare = FindFlare(iEnt);
	if (pFlare != g_Flares.end())
	{
		SetBlockStruggle(pFlare->head,iPlayerEnt);		
	}

	list<sbc_trail>::iterator pPSBeam = FindSBCTrail(iEnt);
	if (pPSBeam != g_SBCTrails.end())
	{
		SetBlockStruggle(pPSBeam->head,iPlayerEnt);
	}

	list <ball>::iterator pBall = FindBall(iEnt);
	if (pBall != m_balls.end())
	{
		pBall->bs = true;
		pBall->bsent = iPlayerEnt;
		pBall->bones = gHUD.m_ManipModel.AddRecordBone( iPlayerEnt );
	}
	return 1;
}

int CHudTrail::MsgFunc_PowStrug(const char *pszName,  int iSize, void *pbuf )
{
	BEGIN_READ( pbuf, iSize );
	int iEnt = READ_SHORT();
	
	list<ent_ent_trail>::iterator pEEBeam = FindEntEntTrail(iEnt);
	if (pEEBeam != g_EntEntTrails.end())
	{
		//SetBlockStruggle(pEEBeam->head,iPlayerEnt);		
	}

	list<flare>::iterator pFlare = FindFlare(iEnt);
	if (pFlare != g_Flares.end())
	{
		//SetBlockStruggle(pFlare->head,iPlayerEnt);		
	}

	list<sbc_trail>::iterator pPSBeam = FindSBCTrail(iEnt);
	if (pPSBeam != g_SBCTrails.end())
	{
		//SetBlockStruggle(pPSBeam->head,iPlayerEnt);
	}

	list <ball>::iterator pBall = FindBall(iEnt);
	if (pBall != m_balls.end())
	{
		//pBall->bs = true;
		//pBall->bsent = iPlayerEnt;
		//pBall->bones = gHUD.m_ManipModel.AddRecordBone( iPlayerEnt );
	}
	return 1;
}