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

#define MAX_TORPEDOES 6
#define KAMETORPEDO_SIZE 64
#define KAMETORPEDO_SIZE_TRAIL_LENGTH 80

extern int gmsgFlare;

/**
 * Kame torpedo attack
 * @version 29-12-2002
 * @author Herwin 'harSens' van Welbergen
 */
class CKameTorpedoAttack:public CBeamAttack
{	
public:
	/**
	* Drops a torpedo
	* @param entvars_t *pevOwner: Monster/Player who shoots it
	* @param Vector vecStart: Origin vector of the attack
	* @param int damage: the amount of damage the attack does
	* @return *CKameTorpedoAttack: The created MagicAttack 
	*/	
	static CKameTorpedoAttack* DropTorpedo( entvars_t *pevOwner, Vector vecStart, int damage, short flarea, short flareb,short trail, int radius, float size);

	/**
	 * Shoots the torpedo upward
	 * @param int chase: homing enabled?
	 * @param Vector vecDir: the direction to shoot to
	 */
	void Fire(Vector vecDir,int chase);

	/**
	 * Try to find an enemy, and chase it
	 */
	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:
	/**
	 * Attack to follow
	 */
	CBasePlayer *m_pFollower;
};

CKameTorpedoAttack* CKameTorpedoAttack::DropTorpedo( entvars_t *pevOwner, Vector vecStart, int damage, short flarea, short flareb,short trail,int radius,float size)
{	CKameTorpedoAttack *pMagicAttack = GetClassPtr( (CKameTorpedoAttack *)NULL );
	pMagicAttack->Spawn();	
	pMagicAttack->m_fRemoveAfterExplode=false;
	
	pMagicAttack->pev->movetype = MOVETYPE_FLY;
	pMagicAttack->pev->solid = SOLID_NOT;	//touches everything

	SET_MODEL(ENT(pMagicAttack->pev), "sprites/null.spr");
	
	pMagicAttack->m_iRadius = radius;
	pMagicAttack->m_iExplosionColor = BLUE_EXPLOSION;
	pMagicAttack->m_flFieldOfView = VIEW_FIELD_INTERMIDIATE;
		
	pMagicAttack->SetDamage(damage);
	pMagicAttack->pev->velocity=Vector(0,0,0);
	pMagicAttack->m_flSpeed=KAMETORPEDO_SPEED;
	pMagicAttack->pev->angles=UTIL_VecToAngles(Vector(0,0,1));
	pMagicAttack->pev->owner=ENT(pevOwner);
	pMagicAttack->pev->classname = MAKE_STRING("Kame Torpedo");

	float flSize = KAMETORPEDO_SIZE * size;
	
	float flAttackSize = (flSize)*0.25; //compensate for empty space in sprites
	UTIL_SetSize(pMagicAttack->pev, Vector(-flAttackSize, -flAttackSize, -flAttackSize), Vector(flAttackSize, flAttackSize, flAttackSize));
	UTIL_MakeVectors ( pevOwner->angles );
	vecStart = vecStart + flAttackSize * gpGlobals->v_forward;
	UTIL_SetOrigin(pMagicAttack->pev,vecStart);	
	
	MESSAGE_BEGIN( MSG_ALL, gmsgFlare, NULL );
		WRITE_LONG(pMagicAttack->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(KAMETORPEDO_SIZE_TRAIL_LENGTH * 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(pMagicAttack->entindex());
	MESSAGE_END();		
	*/
	
	
	pMagicAttack->SetThink( &CKameTorpedoAttack::NormalThink );
	pMagicAttack->pev->nextthink = gpGlobals->time;

	return pMagicAttack;
}

void CKameTorpedoAttack::Fire(Vector vecDir, int chase)
{	m_fRemoveAfterExplode=true;			
	
	pev->velocity = vecDir;
	pev->angles=UTIL_VecToAngles(vecDir);
	if (chase)
	{	SetThink(&CKameTorpedoAttack::ChaseThink);
	}
	pev->solid = SOLID_TRIGGER;
	SetTouch( &CKameTorpedoAttack::ExplodeTouch );
}

BOOL CKameTorpedoAttack::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 CKameTorpedoAttack::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-KAMETORPEDO_ROT) + vecTarget * KAMETORPEDO_ROT;
			pev->velocity = pev->velocity.Normalize() * m_flSpeed;
			pev->angles = UTIL_VecToAngles(pev->velocity);
		}
		else
		{	m_pFollower = NULL;
		}
	}
	else
	{	//get owner
		entvars_t *pevOwner=NULL;
		if(pev->owner)
		{	pevOwner = VARS(pev->owner);			
		}
		if (!pevOwner)
		{	SetThink(&CKameTorpedoAttack::NormalThink);
			pev->nextthink = gpGlobals->time;
			return;			
		}
	
		//try to find someone to chase
		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 ))
						{	m_pFollower = pPlayer;
						}
					}	
				}
			}		
		}	
	}
	DrawBubbles();
	pev->nextthink = gpGlobals->time + 0.1;
}
//--------------------------------------------------------------
/**
* Kametorpedo  Weapon
* @version 30-12-2001
* @author Herwin 'harSens' van Welbergen
*/
class CKameTorpedo:public CChargedAttack
{	
public:
	/**
	* Init function
	*/
	void Spawn( void );

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

	/**
	* Get kametorpedoes slot
	* return int: the slot, is 7 for kame torpedo
	*/
	int iItemSlot( void ) { return 7; }

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

	/**
	 * Kametorpedo idle
	 */
	void WeaponIdle(void);

	/**
	 * Holsters attack
	 */
	void Holster(int skiplocal);

	/**
	 * charges the kametorpedo
	 */
	void PrimaryAttack(void);

	/**
	 * Fires the kametorpedo
	 */
	void SecondaryAttack(void);

	/**
	 * Emits the kametorp charge sound
	 */
	void EmitChargeSound(void);

	/**
	 * Deploy the kametorpedo
	 */
	BOOL Deploy(void);
	
	/*
	* Pcjoe: Holster when meleed
	*/
	BOOL HolsterWhenMeleed( void ) { return TRUE; }
private:
	/**
	* drops a torpedo
	*/
	void ShootAttack();
	
	/**
	 * Updates the torpedo list, removing exploded torpedoes
	 */
	void UpdateTorpedoList();	

	CKameTorpedoAttack *m_pTorpedo[MAX_TORPEDOES];
	short m_sFlareA,m_sFlareB,m_sTrail;
	int m_iTorpedoes;
	int m_fIdle;
};
LINK_ENTITY_TO_CLASS( weapon_kametorpedo, CKameTorpedo);

void CKameTorpedo::Spawn( void )
{	Precache();
	m_iId = WEAPON_KAMETORPEDO;
	m_iDefaultAmmo = 0;
	m_iClip = -1;

	m_iMaxCharge = KAMETORPEDO_MAXCHARGETIME;
	m_iKiCost = KAMETORPEDO_KI_COST;
	m_flPlayerSlowDown = KAMETORPEDO_PLAYERSLOWDOWN;
	strcpy(m_pAttackName,"kametorpedo");
	FallInit();// get ready to fall down.
}

void CKameTorpedo::Precache( void )
{	m_sFlareA = PRECACHE_MODEL("sprites/kamehamehaa.spr");
	m_sFlareB = PRECACHE_MODEL("sprites/kamehamehab.spr");	
	m_sTrail = PRECACHE_MODEL("sprites/kamehamehatrail.spr");
	PRECACHE_SOUND("weapons/kame_charge.wav");
}

int CKameTorpedo::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 = 2;
	p->iId = m_iId = WEAPON_KAMETORPEDO;
	p->iWeight = KAMETORPEDO_WEIGHT;
	p->iFlags = 0;
	return 1;
}

BOOL CKameTorpedo::Deploy( void )
{	m_fIdle = false;
	m_iTorpedoes = 0;
	return CChargedAttack::Deploy();	
}

void CKameTorpedo::ShootAttack()	
{	//play animation
	m_pPlayer->SetAnimation( PLAYER_ATTACK1 );

	//drop it
	UTIL_MakeVectors( m_pPlayer->pev->v_angle );
	Vector vecSrc = m_pPlayer->GetGunPosition()+gpGlobals->v_forward*20;
	m_pTorpedo[m_iTorpedoes] = CKameTorpedoAttack::DropTorpedo( m_pPlayer->pev, vecSrc, m_iCharge * KAMETORPEDO_DAMAGE * GetPowerRatio(), m_sFlareA, m_sFlareB, m_sTrail, KAMETORPEDO_RADIUS, (float)m_iCharge/(float)KAMETORPEDO_MAXCHARGETIME);
	m_iTorpedoes++;
}

void CKameTorpedo::UpdateTorpedoList()
{	//remove exploded torpedoes
	int i=0;
	while(i<m_iTorpedoes)
	{	if(m_pTorpedo[i]->m_fExploded)
		{	m_pTorpedo[i]->Remove();
			m_iTorpedoes--;
			m_pTorpedo[i]=m_pTorpedo[m_iTorpedoes];
		}
		else
		{	i++;
		}
	}
}

void CKameTorpedo::WeaponIdle()
{	m_fIdle = true;
	CChargedAttack::WeaponIdle();
}

void CKameTorpedo::Holster(int skiplocal)
{	UpdateTorpedoList();
	for(int i=0;i<m_iTorpedoes;i++)
	{	m_pTorpedo[i]->Fire(Vector(0,0,KAMETORPEDO_SPEED),false);
	}
	CChargedAttack::Holster(skiplocal);
}

void CKameTorpedo::SecondaryAttack()
{	if(m_fIdle)
	{	m_fIdle = false;
		UpdateTorpedoList();		
		if(m_iTorpedoes>0)
		{	
			m_iTorpedoes--;
			
			//play animation
			m_pPlayer->SetAnimation( PLAYER_ATTACK2 );

			//calculate crosshair pos
			UTIL_MakeVectors( m_pPlayer->pev->v_angle );
			Vector vecAiming = gpGlobals->v_forward;
			Vector vecSrc = m_pPlayer->GetGunPosition();
			TraceResult tr;
			UTIL_TraceLine (vecSrc,vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(pev), &tr );
			Vector vecDir = tr.vecEndPos - m_pTorpedo[m_iTorpedoes]->pev->origin;
			vecDir = vecDir.Normalize()*KAMETORPEDO_SPEED;
			
			m_pTorpedo[m_iTorpedoes]->Fire(vecDir,true);			
		}
	}
	m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.2;
}

void CKameTorpedo::PrimaryAttack()
{	if(!m_pPlayer->m_fCharging)
	{	UpdateTorpedoList();
		if(m_iTorpedoes<MAX_TORPEDOES)
		{	m_fIdle = false;
			CChargedAttack::PrimaryAttack();
		}
	}
	else
	{	CChargedAttack::PrimaryAttack();
	}
}

void CKameTorpedo::EmitChargeSound()
{	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/kame_charge.wav", 1.0, ATTN_NORM);
}