#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "effects.h"
#include "monsters.h"
#include "weapons.h"
#include "magicattack.h"
#include "explodingattack.h"
#include "beamattack.h"
#include "nodes.h"
#include "aura.h"
#include "classes.h"
#include "magicattack.h"
#include "explodingattack.h"
#include "player.h"
#include "gamerules.h"
#include "chargedattack.h"
#define MAX_SPLIT 25

#define SCATTERBEAM_SIZE 80
#define SCATTERSPLIT_SIZE 50
#define SCATTERBEAM_TRAILLENGTH  300
#define SCATTERSPLIT_TRAILLENGTH 150 

extern int gmsgFlare;
/**
* Scattering Attack
* @version 1-3-2002
* @author Herwin 'harSens' van Welbergen
*/
class CScatterAttack:public CBeamAttack
{	
public:
	/**
	* Create a followbeam attack 
	* @param entvars_t *pevOwner: Monster/Player who shoots it
	* @param Vector vecStart: Origin vector of the attack
	* @param Vector vecVelocity: Velocity of the attack
	* @param int damage: the amount of damage the attack does
	* @param short trail: the trail sprite nr
	* @param short flarea: 
	* @param short flareb: the flare sprites
	* @param float size: relative size of the attack (0..1)
	* @param int radius: the radius of the attack
	* @return *CMagicAttack: The created MagicAttack 
	*/	
	static CScatterAttack* ShootScatterBeam( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail,float size);

	/**
	* Splits and autofollows enemies in the fov
	*/
	void EXPORT SplitThink( void );

	/**
	* Checks wether a certain entity is in the view. 3d version of the
	* standard CBaseMonster FInViewCone
	* @param pEntity: the entity to check for
	* @return BOOL: in view
	*/
	BOOL FInViewCone ( CBaseEntity *pEntity );	
private:
	short m_sTrail;				//trail sprite
	short m_sFlareA;			//flare sprites
	short m_sFlareB;
	float m_flSize;
	EHANDLE m_pFollowList[MAX_SPLIT];
	int m_iFollowers;
};

LINK_ENTITY_TO_CLASS( scatterattack, CScatterAttack );

/**
* Scattering, splitting part
* @version 27-11-2002
* @author Herwin 'harSens' van Welbergen
*/
class CScatterSplit:public CBeamAttack
{	
public:
	/**
	* Create a followbeam attack 
	* @param entvars_t *pevOwner: Monster/Player who shoots it
	* @param Vector vecStart: Origin vector of the attack
	* @param Vector vecVelocity: Velocity of the attack
	* @param int damage: the amount of damage the attack does
	* @param short trail: the trail sprite nr
	* @param short flarea: 
	* @param short flareb: flare sprites
	* @param float size: relative size of the attack (0..1)
	* @param int radius: the radius of the attack
	* @return *CScatterSplit: The created scattersplit 
	*/	
	static CScatterSplit* ShootScatterSplit( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail,float size, CBasePlayer *follower);

	/**
	* Splits and autofollows enemies in the fov
	*/
	void EXPORT ChaseThink( void );

	/**
	* Checks wether a certain entity is in the view. 3d version of the
	* standard CBaseMonster FInViewCone
	* @param pEntity: the entity to check for
	* @return BOOL: in view
	*/
	BOOL FInViewCone ( CBaseEntity *pEntity );
	
private:
	EHANDLE m_pFollower;
};
LINK_ENTITY_TO_CLASS( scattersplit, CScatterSplit );

CScatterSplit* CScatterSplit::ShootScatterSplit( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail,float size, CBasePlayer *follower)
{	ALERT(at_console,"split\n");
	CScatterSplit *pScatterAttack = GetClassPtr( (CScatterSplit *)NULL );
	pScatterAttack->Spawn();	
	
	pScatterAttack->pev->movetype = MOVETYPE_FLY;
	pScatterAttack->pev->solid = SOLID_NOT;	//touches everything

	//set model
	SET_MODEL(ENT(pScatterAttack->pev), "sprites/null.spr");
	
	pScatterAttack->m_iExplosionColor = YELLOW_EXPLOSION;
		
	pScatterAttack->m_iRadius = SCATTERBEAM_RADIUS;
	
	pScatterAttack->SetDamage(damage);
	pScatterAttack->pev->velocity=vecVelocity;
	pScatterAttack->m_flSpeed=vecVelocity.Length();
	pScatterAttack->pev->angles=UTIL_VecToAngles(pScatterAttack->pev->velocity);
	pScatterAttack->pev->owner=ENT(pevOwner);
	pScatterAttack->pev->classname = MAKE_STRING( "Scattering Beam" );
	
	float flSize = SCATTERSPLIT_SIZE * size;
	float flAttackSize = flSize * 0.25;	//compensate for empty space in sprite
	
	vecStart = vecStart + flAttackSize * vecVelocity.Normalize();
	UTIL_SetOrigin(pScatterAttack->pev, vecStart);
	UTIL_SetSize(pScatterAttack->pev, Vector(-flAttackSize, -flAttackSize, -flAttackSize), Vector(flAttackSize, flAttackSize, flAttackSize));
	
	//create flare
	MESSAGE_BEGIN( MSG_ALL, gmsgFlare, NULL );
		WRITE_LONG(pScatterAttack->entindex());			//end entity
		WRITE_SHORT(flarea);							//start attachment
		WRITE_SHORT(flareb);							//start sprites
		WRITE_SHORT( flSize * 0.1);						//size in .1's
		WRITE_SHORT(trail);								//trail sprite
		WRITE_SHORT(SCATTERSPLIT_TRAILLENGTH * size);	//trail length
		WRITE_COORD(vecStart.x);						//start pos
		WRITE_COORD(vecStart.y);
		WRITE_COORD(vecStart.z);
	MESSAGE_END();
	
	pScatterAttack->SetThink( &CScatterSplit::ChaseThink );
	pScatterAttack->SetTouch( &CScatterSplit::ExplodeTouch );
	pScatterAttack->m_pFollower = follower;
	pScatterAttack->m_flFieldOfView = VIEW_FIELD_INTERMIDIATE;
	pScatterAttack->pev->solid = SOLID_TRIGGER;	//touches everything

	pScatterAttack->pev->nextthink = gpGlobals->time;	
	return pScatterAttack;
}

BOOL CScatterSplit :: FInViewCone ( CBaseEntity *pEntity )
{	Vector	vecLOS;
	float	flDot;

	UTIL_MakeVectors ( pev->angles );
	
	vecLOS = pEntity->pev->origin - pev->origin;
	vecLOS = vecLOS.Normalize();

	flDot = DotProduct (vecLOS, gpGlobals->v_forward);

	if ( flDot > m_flFieldOfView )
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

void CScatterSplit::ChaseThink()
{	if (m_pFollower)
	{	
		//don't follow player if we can't find it	
		if (FInViewCone(m_pFollower))
		{	Vector vecTarget = (m_pFollower->pev->origin - pev->origin);
			vecTarget = vecTarget.Normalize() * m_flSpeed;
			pev->velocity = pev->velocity * (1-SCATTERBEAM_ROT) + vecTarget * SCATTERBEAM_ROT;
			pev->velocity = pev->velocity.Normalize() * m_flSpeed;
			pev->angles = UTIL_VecToAngles(pev->velocity);
		}
	}	
	DrawBubbles();
	pev->nextthink = gpGlobals->time + 0.2;
}

CScatterAttack* CScatterAttack::ShootScatterBeam( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail,float size)
{	CScatterAttack *pScatterAttack = GetClassPtr( (CScatterAttack *)NULL );
	pScatterAttack->Spawn();	
	pScatterAttack->m_iFollowers = 0;
	
	pScatterAttack->m_sTrail = trail;
	pScatterAttack->m_sFlareA = flarea;
	pScatterAttack->m_sFlareB = flareb;

	pScatterAttack->pev->movetype = MOVETYPE_FLY;
	pScatterAttack->pev->solid = SOLID_TRIGGER;	//touches everything

	//set model
	SET_MODEL(ENT(pScatterAttack->pev), "sprites/null.spr");
	
	pScatterAttack->m_iExplosionColor = YELLOW_EXPLOSION;
		
	pScatterAttack->m_iRadius = SCATTERBEAM_RADIUS;
	
	pScatterAttack->SetDamage(damage);
	pScatterAttack->pev->velocity=vecVelocity;
	pScatterAttack->m_flSpeed=vecVelocity.Length();
	pScatterAttack->pev->angles=UTIL_VecToAngles(pScatterAttack->pev->velocity);
	pScatterAttack->pev->owner=ENT(pevOwner);
	pScatterAttack->pev->classname = MAKE_STRING( "Scatter Beam" );
	
	pScatterAttack->m_flSize = size;
	float flSize = size * SCATTERBEAM_SIZE;
	float flAttackSize = flSize * 0.25;
	
	vecStart = vecStart + flAttackSize * vecVelocity.Normalize();
	UTIL_SetOrigin(pScatterAttack->pev,vecStart);
	UTIL_SetSize(pScatterAttack->pev, Vector(-flAttackSize, -flAttackSize, -flAttackSize), Vector(flAttackSize, flAttackSize, flAttackSize));
	pScatterAttack->SetTouch( &CScatterAttack::ExplodeTouch );

	//create flare
	MESSAGE_BEGIN( MSG_ALL, gmsgFlare, NULL );
		WRITE_LONG(pScatterAttack->entindex());			//end entity
		WRITE_SHORT(flarea);							//start attachment
		WRITE_SHORT(flareb);							//start sprites
		WRITE_SHORT( flSize * 0.1);						//size in .1's
		WRITE_SHORT(trail);								//trail sprite
		WRITE_SHORT(SCATTERBEAM_TRAILLENGTH * size);	//trail length
		WRITE_COORD(vecStart.x);						//start pos
		WRITE_COORD(vecStart.y);
		WRITE_COORD(vecStart.z);		
	MESSAGE_END();

	/*DEBUG by harSens: enable bbox	
	extern int gmsgBoundingBox;
	MESSAGE_BEGIN( MSG_ALL, gmsgBoundingBox);
		WRITE_LONG(pScatterAttack->entindex());
	MESSAGE_END();
	*/
		
	// make monsters/players afraid of it while in the air
	pScatterAttack->SetThink( &CScatterAttack::SplitThink );
	pScatterAttack->pev->nextthink = gpGlobals->time+0.3;
	pScatterAttack->m_flFieldOfView = VIEW_FIELD_INTERMIDIATE;
	
	return pScatterAttack;
}

void CScatterAttack::SplitThink()
{	entvars_t *pevOwner;
	
	if(pev->owner)
	{	pevOwner = VARS(pev->owner);
		if (!pevOwner)
		{	SetThink(&CScatterAttack::NormalThink);
			pev->nextthink = gpGlobals->time;
			return;			
		}
		
		if (m_iFollowers<MAX_SPLIT)
		{	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
			{	CBasePlayer *pPlayer = (CBasePlayer *)UTIL_PlayerByIndex( i );
				if (pPlayer)
				{	if (pPlayer->IsAlive() && pPlayer->pev!=pevOwner)
					{	if (pPlayer->pev->team!=pevOwner->team || !g_pGameRules->IsTeamplay())
						{	if (FInViewCone ( pPlayer ))
							{	//are we following it already?
								int bFollowed = false;
								for(int j=0;j<m_iFollowers;j++)
								{	if(m_pFollowList[j]==pPlayer)
									{	bFollowed = true;
										break;
									}
								}

								//no, so split
								if (!bFollowed)
								{	CScatterSplit::ShootScatterSplit( pevOwner, pev->origin, pev->velocity, pev->dmg/2, m_sFlareA, m_sFlareB, m_sTrail, m_flSize, pPlayer);
									m_pFollowList[m_iFollowers] = pPlayer;
									m_iFollowers++;
									break;
								}
							}
						}
					}	
				}
			}
		}
	}
	DrawBubbles();
	pev->nextthink = gpGlobals->time + 0.2;
}

BOOL CScatterAttack :: FInViewCone ( CBaseEntity *pEntity )
{	Vector	vecLOS;
	float	flDot;

	UTIL_MakeVectors ( pev->angles );
	
	vecLOS = pEntity->pev->origin - pev->origin;
	vecLOS = vecLOS.Normalize();

	flDot = DotProduct (vecLOS, gpGlobals->v_forward);

	if ( flDot > m_flFieldOfView )
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------
/**
* scatter beam
* @version 16-2-2002
* @author Herwin 'harSens' van Welbergen
*/
class CScatterBeam:public CChargedAttack
{	
public:
	/**
	* Init function
	*/
	void Spawn( void );

	/**
	* Precaches sprites and sounds
	*/
	void Precache( void );

	/**
	* Get  scatterbeam slot
	* return int: the slot, is 7 scatterbeam
	*/
	int iItemSlot( void ) { return 7; }

	/**
	* Get scatterbeam item info
	* @param ItemInfo *p: The ItemInfo gets stored here
	* @return int: Always returns 1 (why???)
	*/
	int GetItemInfo(ItemInfo *p);

	/**
	 * Emits shoot sound
	 */
	void EmitShootSound();

	/**
	 * Emits charge sound
	 */
	void EmitChargeSound();
private:
	/**
	* shoots the scatterbeam
	*/
	void ShootAttack();
	short m_sTrail;
	short m_sFlareA;
	short m_sFlareB;
};

LINK_ENTITY_TO_CLASS( weapon_scatterbeam, CScatterBeam );

void CScatterBeam::Spawn( void )
{	Precache();
	m_iId = WEAPON_SCATTERBEAM;
	m_iDefaultAmmo = 0;
	m_iClip = -1;

	m_iMaxCharge = SCATTERBEAM_MAXCHARGETIME;
	m_iKiCost = SCATTERBEAM_KI_COST;
	m_flPlayerSlowDown = 0.0;
	strcpy(m_pAttackName,"scatterbeam");
	FallInit();// get ready to fall down.
}

void CScatterBeam::Precache( void )
{	m_sFlareA = PRECACHE_MODEL("sprites/scatterbeama.spr");
	m_sFlareB = PRECACHE_MODEL("sprites/scatterbeamb.spr");
	m_sTrail = PRECACHE_MODEL("sprites/scatterbeamtrail.spr");
	
	PRECACHE_SOUND("krillin/scatterbeamha.wav");
	PRECACHE_SOUND("krillin/scatterbeamha_charge.wav");
	PRECACHE_SOUND("weapons/scatterbeam.wav");
}

int CScatterBeam::GetItemInfo(ItemInfo *p)
{	p->pszName = STRING(pev->classname);
	p->pszAmmo1 = "ki";
	p->iMaxAmmo1 = -1;	//max ammo is weapon independent
	p->pszAmmo2 = NULL;
	p->iMaxAmmo2 = -1;
	p->iMaxClip = WEAPON_NOCLIP;
	p->iSlot = 6;
	p->iPosition = 0;
	p->iId = m_iId = WEAPON_SCATTERBEAM;
	p->iWeight = SCATTERBEAM_WEIGHT;
	p->iFlags = 0;
	return 1;
}

void CScatterBeam::ShootAttack()	
{	//shoot the attack
	UTIL_MakeVectors( m_pPlayer->pev->v_angle );
	Vector vecSrc = m_pPlayer->GetGunPosition() + gpGlobals->v_forward * 10;
	Vector vecThrow = gpGlobals->v_forward * SCATTERBEAM_SPEED;
	CScatterAttack::ShootScatterBeam( m_pPlayer->pev, vecSrc, vecThrow, m_iCharge * SCATTERBEAM_DAMAGE * GetPowerRatio() , m_sFlareA, m_sFlareB, m_sTrail,(float)m_iCharge/(float)SCATTERBEAM_MAXCHARGETIME);
}

void CScatterBeam::EmitShootSound()
{
	m_pPlayer->EmitClassSound("scatterbeamha.wav");
	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/scatterbeam.wav", 1.0, ATTN_NORM);
}

void CScatterBeam::EmitChargeSound()
{
	m_pPlayer->EmitClassSound("scatterbeamha_charge.wav");	
}