/**
* Melee FX
* @version: 7-16-02
* @author Joseph 'Pcjoe' Florencio
*/

#include "hud.h"
#include "parsemsg.h"
#include "const.h"
#include "cl_util.h"
#include "r_efx.h"
#include "pm_defs.h"
#include "pmtrace.h"
#include "event_api.h"
#include "com_model.h"
#include "StudioModelRenderer.h"
#include "GameStudioModelRenderer.h"
#include "triangleapi.h"
#include "vrml.h"
using namespace std;

extern CGameStudioModelRenderer g_StudioRenderer;

DECLARE_MESSAGE(m_MeleeFX, MeleeFX);
DECLARE_MESSAGE(m_MeleeFX, AfterFX);

// Sprite for Shockwave
HSPRITE g_hsprMeleeFlash1, g_hsprMeleeFlash2;

void Draw_WorldSprite( HSPRITE sprite, vec3_t pos, float size, int RenderMode, TRICULLSTYLE tricullstyle,int r,int g, int b, int a, int frame);
extern VRMLData *gVRMLSphere;
extern HSPRITE g_hsprWhite;

#define ANIMATE_TIME	0.1

enum meleefx
{	MELEE_SIMPLE=1,
	MELEE_PREPUNCH,
	MELEE_ENDSHOCKWAVE,
};

int CHudMeleeFX::Init(void)
{	HOOK_MESSAGE(MeleeFX);
	HOOK_MESSAGE(AfterFX);
	InitHUDMap();
	m_iFlags=0;
	gHUD.AddHudElem(this);
	return 1;
}

int CHudMeleeFX::VidInit(void)
{	g_hsprMeleeFlash1 = LoadSprite("sprites/meleeflash1.spr");
	g_hsprMeleeFlash2 = LoadSprite("sprites/meleeflash2.spr");
	return 1;
}

void CHudMeleeFX::InitHUDMap( void )
{	m_Meleefx.clear();
}

/*
* Render Shockwaves
*/
void CHudMeleeFX::RenderShockwaves( void )
{	list <melee_shock_t>::iterator current = m_Meleefx.begin();
	list <melee_shock_t>::iterator last = m_Meleefx.end();	
	while (current!=last)
	{	// Dead, remove
		if((current->flSTime+current->flETime) < gHUD.m_flTime)
		{	current = m_Meleefx.erase(current);
			continue;
		}

		// Animate
		Animate(current);

		// Draw 3d
		if(current->bl3d)
		{	float flAlpha = GetAlpha(current)/255;
			VRMLData vrmlScaledShockwave;
			ScaleVRML(vrmlScaledShockwave, *gVRMLSphere, GetSize(current));
			DrawVRML(vrmlScaledShockwave, current->vecOrigin,
				current->hsprSprite,kRenderTransAdd, TRI_NONE, current->iFrame,
				current->r*flAlpha,current->g*flAlpha,current->b*flAlpha,GetAlpha(current));
		}
		// Draw 2d
		else
		{	Draw_WorldSprite(current->hsprSprite, current->vecOrigin, GetSize(current), kRenderTransAdd, TRI_FRONT, current->r, current->g, current->b, GetAlpha(current), current->iFrame);
		}


		current++;
	}
}

/*
* Get Life
*/
float CHudMeleeFX::GetLife( std::list<melee_shock_t>::iterator shock )
{	float flTime = (shock->flSTime+shock->flETime)-gHUD.m_flTime;
	return (flTime/shock->flETime);
}

/*
* Get Size
*/
float CHudMeleeFX::GetSize( std::list<melee_shock_t>::iterator shock )
{	return (shock->iSSize + (shock->iESize-shock->iSSize)*(1-GetLife(shock)));
}

/*
* Get Alpha
*/
float CHudMeleeFX::GetAlpha( std::list<melee_shock_t>::iterator shock )
{	return (shock->iSAlpha + (shock->iEAlpha-shock->iSAlpha)*(1-GetLife(shock)));
}

/*
* Animate
* @param std::list<melee_shock_t>::iterator iter: STL iterator
*/
void CHudMeleeFX::Animate( std::list<melee_shock_t>::iterator shock )
{	if(shock->flFrameTime < gHUD.m_flTime)
	{	shock->flFrameTime = gHUD.m_flTime + ANIMATE_TIME;
		
		// Change frames
		shock->iFrame++;
		if(shock->iFrame >= SPR_Frames(shock->hsprSprite))
		{	shock->iFrame = 0;
		}
	}
}

/*
* Create shockwave FX
*/
std::list<melee_shock_t>::iterator CHudMeleeFX::CreateShockwave( int iType, vec3_t vecOrigin )
{	melee_shock_t ns;	// new shock
	ns.iType = iType;
	ns.flSTime = gHUD.m_flTime;
	ns.vecOrigin = vecOrigin;
	ns.iFrame = 0;
	ns.flFrameTime = 0;
	switch(iType)
	{	
	case MELEE_SIMPLE:
		{	ns.flETime = 1;
			ns.iSSize = 1;
			ns.iESize = 100;
			ns.iSAlpha = 255;
			ns.iEAlpha = 0;
			ns.r=ns.g=ns.b=255;
			ns.bl3d = false;
			ns.hsprSprite = g_hsprMeleeFlash1;
			break;
		}
	case MELEE_PREPUNCH:
		{	ns.flETime = 0.3;
			ns.iSSize = 1;
			ns.iESize = 6;
			ns.iSAlpha = 90;
			ns.iEAlpha = 0;
			ns.r=ns.g=ns.b=255;
			ns.bl3d = true;
			ns.hsprSprite = g_hsprWhite;
			break;
		}
	case MELEE_ENDSHOCKWAVE:
		{	ns.flETime = 0.5;
			ns.iSSize = 5;
			ns.iESize = 350;
			ns.iSAlpha = 90;
			ns.iEAlpha = 0;
			ns.r=ns.g=ns.b=255;
			ns.bl3d = true;
			ns.hsprSprite = g_hsprWhite;
			break;
		}
	default:
		return NULL;
	}

	m_Meleefx.push_front(ns);
	
	return m_Meleefx.begin();
}

/*
* Create shockwave on bone
*/
void CHudMeleeFX::CreateShockwaveBone( int iType, int idx, char *pBone )
{	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(idx);
	/*modified by harSens
	std::list<model_manip_s>::iterator pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(idx);
	*/
	model_manip_s *pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(idx);
	if(pManip==NULL)
	{	CreateShockwave(iType, pClient->origin);
		return;
	}

	// Get origin
	vec3_t vecSrc;
	if(idx == pManip->iPlayerIndex)
	{	vecSrc = g_StudioRenderer.GetBoneOrigin( pClient, pManip->playerpos, pBone );
	}
	else
	{	vecSrc = g_StudioRenderer.GetBoneOrigin( pClient, pManip->otherpos, pBone );
	}
	CreateShockwave(iType, vecSrc);
}

/*
* Set Bodypart ( only while in melee )
*/
void CHudMeleeFX::SetBodypart( int iIndex, int iBody )
{	/*modified by harSens
	std::list<model_manip_s>::iterator pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(iIndex);
	*/
	model_manip_s *pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(iIndex);
	if(pManip==NULL)
	{	return;
	}

	cl_entity_t *pEntity = gEngfuncs.GetEntityByIndex( iIndex );
	if(!pEntity)
	{	return;
	}

	if(pManip->iPlayerIndex == iIndex)
	{	pManip->iBodyPlayer = iBody;
		pManip->iBodyPlayerSeq = pEntity->curstate.sequence;
	}
	else
	{	pManip->iBodyOther = iBody;
		pManip->iBodyOtherSeq = pEntity->curstate.sequence;
	}
}

/*
* Create after fx
*/
void CHudMeleeFX::CreateAfterfx( float flFadeSpeed, vec3_t vecOrigin, vec3_t vecAngles, cl_entity_t *pEnt )
{	if(!pEnt->model)
	{	return;
	}

	TEMPENTITY *pTempEntity = gEngfuncs.pEfxAPI->CL_TempEntAlloc(vecOrigin, pEnt->model);
	if(!pTempEntity)
	{	return;
	}
	// Set fade
	pTempEntity->die = gHUD.m_flTime;
	pTempEntity->fadeSpeed = flFadeSpeed;
	pTempEntity->flags = FTENT_FADEOUT;

	// shorter code...
	cl_entity_t *pEntity = &pTempEntity->entity;

	// Copy over data
	pEntity->baseline.renderamt = 255;
	pEntity->curstate = pEnt->curstate;
	pEntity->latched = pEnt->latched;
	pEntity->curstate.rendermode = kRenderTransTexture;
	pEntity->curstate.framerate = 0;
	pEntity->angles = vecAngles;
	pEntity->curstate.origin = vecOrigin;
	pEntity->origin = vecOrigin;

	// Straiten out legs
	pEntity->curstate.controller[0] = 127;
	pEntity->curstate.controller[1] = 127;
	pEntity->curstate.controller[2] = 127;
	pEntity->curstate.controller[3] = 127;
	pEntity->latched.prevcontroller[0] = pEntity->curstate.controller[0];
	pEntity->latched.prevcontroller[1] = pEntity->curstate.controller[1];
	pEntity->latched.prevcontroller[2] = pEntity->curstate.controller[2];
	pEntity->latched.prevcontroller[3] = pEntity->curstate.controller[3];
}

/*
* (Simple) melee fx
*/
int CHudMeleeFX::MsgFunc_MeleeFX(const char *pszName, int iSize, void *pbuf)
{	BEGIN_READ( pbuf, iSize );
	int iFxtype = READ_BYTE();
	int iIndex = READ_BYTE();

	cl_entity_t *pEntity = gEngfuncs.GetEntityByIndex( iIndex );
	if(pEntity)
	{	vec3_t vecForward;
		AngleVectors(pEntity->curstate.angles, vecForward, NULL, NULL);
		CreateShockwave( iFxtype, pEntity->origin+vecForward*20 );
	}

	return 1;
}

/*
* After FX
*/
int CHudMeleeFX::MsgFunc_AfterFX(const char *pszName, int iSize, void *pbuf)
{	BEGIN_READ( pbuf, iSize );
	int idx = READ_BYTE();
	vec3_t vecOrigin;
	vecOrigin.x = READ_COORD();
	vecOrigin.y = READ_COORD();
	vecOrigin.z = READ_COORD();

	cl_entity_t *pClient = gEngfuncs.GetEntityByIndex(idx);
	if(!pClient)
	{	return 1;
	}

/*	vec3_t vecAngles = pClient->angles;
	std::list<model_manip_s>::iterator pManip = gHUD.m_ManipModel.FindByPlayerAndOtherIndex(pClient->index);
	if(pManip!=NULL)
	{	if(pManip->blFixedAngles)
		{	if(pManip->iFlags & MANIP_FLAG_CONNECT)
			{	// Make both players teleport
				pManip->blTeleportPlayer = true;
				pManip->blTeleportOther = true;
				pManip->vecTele = vecOrigin;
				return 1;
			}
			else
			{	vecAngles = pManip->vecAngles;
			}
		}
	}*/
	CreateAfterfx(.75,vecOrigin, pClient->angles, pClient);

	return 1;
}