#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 "explodingattack.h"
#include "../common/particletypes.h"
#include "beamattack.h"

extern int gmsgCharge;
extern int gmsgAddEffect;
extern int gmsgRemoveEffect;

#define EARTHSB_RESIZE 0.25
/**
* Destructo disc attack
* @version 30-3-2002
* @author Herwin 'harSens' van Welbergen
*/
class CSpiritBombAttack:public CBeamAttack
{	
public:
	/**
	* Create magical attack spirit bomb
	* @param entvars_t *pevOwner: monster/player who shoots it
	* @param Vector vecStart: Origin vector of the spirit bomb
	* @param sprite the sprite used for the attack
	* @return *CMagicAttack: The created MagicAttack 
	*/
	static CSpiritBombAttack *CreateSpiritBomb( entvars_t *pevOwner, Vector vecStart, int sprite);

	/**
	* Shoots the spiritbomb
	* @param Vector vecVelocity: velocity of the spiritbomb
	* @param int damage: damage of the spiritbomb
	*/
	void ShootSpiritBomb(Vector vecVelocity, int damage);

	/**
	 * Creates a spiritbomb, and shoots it
	 * @param entvars_t *pevOwner: owner of the sb
	 * @param Vector vecStart: start position
	 * @param Vector vecVelocity: velocity
	 * @param int damage: damage
	 * @param float size: size of the sb
	 * @param int sprite the sb sprite
	 */
	static CSpiritBombAttack *ShootSpiritBomb(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity,int damage,float size,int sprite);

	/**
	* Grows a spirit bomb 
	*/
	void Grow (void);
};

void CSpiritBombAttack::ShootSpiritBomb(Vector vecVelocity, int damage)
{	pev->solid = SOLID_TRIGGER;	//touches everything	
	SetDamage(damage); 
	pev->velocity = vecVelocity;
	m_flSpeed = vecVelocity.Length();
	pev->angles = UTIL_VecToAngles(pev->velocity);

	int iAttackSize = (pev->scale * SPIRITBOMB_SIZE)/2;
	UTIL_SetSize(pev, Vector(-iAttackSize, -iAttackSize, -iAttackSize), Vector(iAttackSize, iAttackSize, iAttackSize));
	SetTouch( &CSpiritBombAttack::ExplodeTouch );

	SetThink( &CSpiritBombAttack::NormalThink );
	
	/*DEBUG by harSens: enable bbox
	extern int gmsgBoundingBox;
	MESSAGE_BEGIN( MSG_ALL, gmsgBoundingBox);
		WRITE_LONG(entindex());
	MESSAGE_END();		
	*/
	
	pev->nextthink = gpGlobals->time;	
}

void CSpiritBombAttack::Grow()
{	pev->scale += 0.02;
}

CSpiritBombAttack* CSpiritBombAttack::CreateSpiritBomb( entvars_t *pevOwner, Vector vecStart,int sprite)
{	CSpiritBombAttack *pMagicAttack=GetClassPtr( (CSpiritBombAttack *)NULL );
	pMagicAttack->Spawn();	
	
	pMagicAttack->pev->movetype = MOVETYPE_FLY;
	pMagicAttack->pev->solid = SOLID_NOT;//touches nothing
	
	SET_MODEL(ENT(pMagicAttack->pev), "sprites/null.spr");
	//set model
	extern int gmsgBall;
	MESSAGE_BEGIN( MSG_ALL, gmsgBall, NULL );
		WRITE_SHORT(pMagicAttack->entindex());
		WRITE_SHORT(sprite);
		WRITE_SHORT(26);
	MESSAGE_END();
	//SET_MODEL(ENT(pMagicAttack->pev), "sprites/spiritbomb.spr");
	
	
	pMagicAttack->m_iRadius = SPIRITBOMB_RADIUS;

	pMagicAttack->m_iExplosionColor = WHITE_EXPLOSION;
	
	UTIL_SetOrigin(pMagicAttack->pev,vecStart);
	pMagicAttack->pev->velocity = Vector(0,0,0);
	pMagicAttack->pev->owner=ENT(pevOwner);
	pMagicAttack->pev->classname = MAKE_STRING( "Spirit Bomb" );
	pMagicAttack->pev->scale = 0.01;

	pMagicAttack->SetDamage(0);
	
	pMagicAttack->SetTouch( NULL );
	pMagicAttack->SetThink( &CSpiritBombAttack::NormalThink );
	pMagicAttack->pev->nextthink = gpGlobals->time;
		
	return pMagicAttack;
}

CSpiritBombAttack* CSpiritBombAttack::ShootSpiritBomb(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity,int damage,float size,int sprite)
{
	CSpiritBombAttack *pMagicAttack=GetClassPtr( (CSpiritBombAttack *)NULL );
	pMagicAttack->Spawn();	
	
	pMagicAttack->pev->movetype = MOVETYPE_FLY;
	pMagicAttack->pev->solid = SOLID_TRIGGER;
	//set model
	SET_MODEL(ENT(pMagicAttack->pev), "sprites/null.spr");
	//set model
	extern int gmsgBall;
	MESSAGE_BEGIN( MSG_ALL, gmsgBall, NULL );
		WRITE_SHORT(pMagicAttack->entindex());
		WRITE_SHORT(sprite);
		WRITE_SHORT(26);
	MESSAGE_END();
	//SET_MODEL(ENT(pMagicAttack->pev), "sprites/spiritbomb.spr");
	
	pMagicAttack->m_iRadius = SPIRITBOMB_RADIUS;

	pMagicAttack->m_iExplosionColor = WHITE_EXPLOSION;
	
	pMagicAttack->pev->velocity = vecVelocity;
	pMagicAttack->m_flSpeed = vecVelocity.Length();
	pMagicAttack->pev->angles = UTIL_VecToAngles(vecVelocity);
	pMagicAttack->pev->owner=ENT(pevOwner);
	pMagicAttack->pev->classname = MAKE_STRING( "Spirit Bomb" );
	pMagicAttack->pev->scale = size;

	float flAttackSize = (size * SPIRITBOMB_SIZE)/2;
	UTIL_SetOrigin(pMagicAttack->pev,vecStart + flAttackSize * vecVelocity.Normalize());	
	UTIL_SetSize(pMagicAttack->pev, Vector(-flAttackSize, -flAttackSize, -flAttackSize), Vector(flAttackSize, flAttackSize, flAttackSize));
	
	pMagicAttack->SetDamage(damage);
	
	pMagicAttack->SetTouch( &CSpiritBombAttack::ExplodeTouch );
	pMagicAttack->SetThink( &CSpiritBombAttack::NormalThink );
	pMagicAttack->pev->nextthink = gpGlobals->time;


	/*DEBUG by harSens: enable bbox
	extern int gmsgBoundingBox;
	MESSAGE_BEGIN( MSG_ALL, gmsgBoundingBox);
		WRITE_LONG(entindex());
	MESSAGE_END();
	*/	
	
	pMagicAttack->pev->nextthink = gpGlobals->time;	
		
	return pMagicAttack;
}

//------------------------------------------------------------

/**
* Spirit Bomb Weapon
* @version 23-7-2001
* @author Herwin 'harSens' van Welbergen
*/
class CSpiritBomb:public CBasePlayerWeapon
{	
public:
	/**
	* Init function
	*/
	void Spawn( void );

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

	/**
	* Get  spiritbomb slot
	* return int: the slot, is 5 for spirit bomb
	*/
	int iItemSlot( void ) { return 5; }

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

	/**
	* shoot beam if charging
	*/
	void WeaponIdle( void );

	/**
	 * charges a namek-style spirit bomb attack
	 */
	void PrimaryAttack( void );

	/**
	 * Charges an earth-style spirit bomb
	 */
	void SecondaryAttack(void);

	/**
	* Called when spirit bomb is selected as weapon
	* @return BOOL: true, if deploy succeeded
	*/
	BOOL Deploy( void );

	/**
	* Holsters the sbc
	* @param int skiplocal:???
	*/
	void Holster(int skiplocal);

	/**
	 * Shoots a Namek-style spirit bomb
	 */
	void Shoot1( void );

	/**
	 * Shoots a Earth-style spirit bomb
	 */
	void Shoot2( void );

private:
	/**
	 * Inits charge sprite
	 */
	virtual void InitChargeSprite();

	/**
	 * Enable charge sprite
	 */
	virtual void EnableChargeSprite(){m_pChargeSprite->SetScale((float)m_iCharge*0.0007);m_pChargeSprite->TurnOn();};

	/**
	 * Disable charge sprite
	 */
	virtual void DisableChargeSprite(){m_pChargeSprite->TurnOff();m_pPlayer->pev->renderfx = kRenderFxNone;};

	/**
	 * Grow charge sprite
	 */
	virtual void GrowChargeSprite(){m_pChargeSprite->SetScale((float)m_iCharge * 0.0007);};

	/**
	 * Remove charge sprite
	 */
	virtual void RemoveChargeSprite(){UTIL_Remove(m_pChargeSprite);};


	int m_iCharge;						//how far is it charged
	CSpiritBombAttack* m_pSpiritBomb;	//the spirit bomb attack
	BOOL m_fCharge1,m_fCharge2;
	CSprite *m_pChargeSprite;
	int m_fGlowing;
	short m_sSprite;
};

LINK_ENTITY_TO_CLASS( weapon_spiritbomb, CSpiritBomb );

void CSpiritBomb::Spawn( void )
{	Precache();
	m_iId = WEAPON_SPIRITBOMB;
	m_pSpiritBomb = NULL;
	m_iDefaultAmmo = 0;
	m_iClip = -1;
	FallInit();// get ready to fall down.
}

void CSpiritBomb::Precache( void )
{	m_sSprite = PRECACHE_MODEL("sprites/spiritbomb.spr");
	PRECACHE_SOUND("goku/spiritbomb_shoot.wav");
	PRECACHE_SOUND("goku/spiritbomb_charge.wav");
	PRECACHE_SOUND("goku/spiritbomb2_charge.wav");
	PRECACHE_SOUND("weapons/spiritbomb_fire.wav");
}

int CSpiritBomb::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 = 4;
	p->iPosition = 3;
	p->iId = m_iId = WEAPON_SPIRITBOMB;
	p->iWeight = SPIRITBOMB_WEIGHT;
	p->iFlags = 0;
	return 1;
}

BOOL CSpiritBomb::Deploy( void )
{	m_fCharge1 = false;
	m_fCharge2 = false;
	m_fGlowing = false;
	m_pSpiritBomb = NULL;
	InitChargeSprite();
	return DefaultDeploy( "spiritbomb");
}


void CSpiritBomb::Holster(int skiplocal)
{	if (m_pPlayer->m_fCharging)
	{	m_pPlayer->m_flSlowDown=1.0;
		m_pPlayer->m_fCharging=false;
		m_pPlayer->m_fCharging2=false;
		m_pPlayer->m_fChargeNamekSB=false;
		
		/* Modified by Rick
		//undo drawing charge bar
		MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
			WRITE_BYTE(0);	//amount charged in %
		MESSAGE_END();*/

		m_pPlayer->DrawChargeBar(0); // remove charge bar
		
		//silence sounds
		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_VOICE, "common/null.wav", 1.0, ATTN_NORM);

		if(m_fCharge1)
		{	//stop the movement of the sprites
			//MESSAGE_BEGIN( MSG_ALL, gmsgStopSBCharge, m_pPlayer->pev->origin );		
			//	WRITE_SHORT( m_pSpiritBomb->entindex() );
			//MESSAGE_END();
			MESSAGE_BEGIN( MSG_ALL, gmsgRemoveEffect);		
				WRITE_BYTE(SPIRITBOMB);
				WRITE_SHORT( m_pSpiritBomb->entindex() );
			MESSAGE_END();
		
			//remove the bomb sprite
			m_pSpiritBomb->Remove();
			m_pSpiritBomb = NULL;
		}
		else if(m_fCharge2)
		{	DisableChargeSprite();
		}
	}
	RemoveChargeSprite();	
}

void CSpiritBomb::Shoot1()	
{	m_pPlayer->m_flSlowDown=1.0;
	m_pPlayer->m_fCharging=false;
	m_pPlayer->m_fChargeNamekSB=false;
	m_fCharge1=false;

	/* Modified by Rick
	//undo drawing charge bar
	MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
		WRITE_BYTE(0);	//disable it
	MESSAGE_END();*/

	m_pPlayer->DrawChargeBar(0); // Remove charge bar

	//stop the movement of the sprites
	//MESSAGE_BEGIN( MSG_ALL, gmsgStopSBCharge, m_pPlayer->pev->origin );		
	//	WRITE_SHORT( m_pPlayer->entindex() );
	//MESSAGE_END();
	MESSAGE_BEGIN( MSG_ALL, gmsgRemoveEffect);	
		WRITE_BYTE(SPIRITBOMB);
		WRITE_SHORT( m_pSpiritBomb->entindex() );
	MESSAGE_END();

	//prevent mass shooting
	if (m_iCharge < SPIRITBOMB_MAXCHARGETIME/3)
	{	m_pSpiritBomb->Remove();
		m_pSpiritBomb = NULL;
		
		//silence sounds
		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_VOICE, "common/null.wav", 1.0, ATTN_NORM);
		return;
	}

	m_pPlayer->EmitClassSound("spiritbomb_shoot.wav");
	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/spiritbomb_fire.wav", 1.0, ATTN_NORM);//set the right animation
	
	//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 );
	
	vecAiming = tr.vecEndPos - m_pSpiritBomb->pev->origin;

	// Added by Pcjoe: Do the animation
	m_pPlayer->SetAnimation( PLAYER_ATTACK1 );

	//m_pSpiritBomb->ShootSpiritBomb(vecAiming.Normalize() * SPIRITBOMB_SPEED, m_iCharge * GetPowerRatio() * SPIRITBOMB_DAMAGE);
	//HARSENS: pl related damage removed
	m_pSpiritBomb->ShootSpiritBomb(vecAiming.Normalize() * SPIRITBOMB_SPEED, m_iCharge * SPIRITBOMB_DAMAGE);
}

void CSpiritBomb::Shoot2()	
{	m_pPlayer->m_flSlowDown=1.0;
	m_pPlayer->m_fCharging=false;
	m_pPlayer->m_fCharging2 = false;
	m_fCharge2=false;
	m_fGlowing=false;

	/* Modified by Rick
	//undo drawing charge bar
	MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
		WRITE_BYTE(0);	//disable it
	MESSAGE_END();*/

	m_pPlayer->DrawChargeBar(0); // Remove charge bar

	DisableChargeSprite();

	//prevent mass shooting
	if (m_iCharge < SPIRITBOMB2_MAXCHARGETIME/3)
	{	//silence sounds
		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_VOICE, "common/null.wav", 1.0, ATTN_NORM);
		return;
	}	
	
	m_pPlayer->EmitClassSound("spiritbomb_shoot.wav");
	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/spiritbomb_fire.wav", 1.0, ATTN_NORM);//set the right animation
	
	//calculate crosshair pos
	UTIL_MakeVectors( m_pPlayer->pev->v_angle );
	Vector vecAiming = gpGlobals->v_forward;
	Vector vecSrc = m_pPlayer->GetGunPosition() + gpGlobals->v_forward * 10;
	
	m_pPlayer->SetAnimation( PLAYER_ATTACK2 );
	
	CSpiritBombAttack::ShootSpiritBomb(m_pPlayer->pev,vecSrc, vecAiming.Normalize() * SPIRITBOMB_SPEED, m_iCharge * SPIRITBOMB_DAMAGE, EARTHSB_RESIZE * m_iCharge/SPIRITBOMB2_MAXCHARGETIME,m_sSprite);
}

void CSpiritBomb::InitChargeSprite( void )
{	m_pChargeSprite = CSprite::SpriteCreate( "sprites/spiritbomb.spr", m_pPlayer->pev->origin, true);
	m_pChargeSprite->SetAttachment(m_pPlayer->edict(),1);
	m_pChargeSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 254, kRenderFxGlowShell );
	m_pChargeSprite->SetScale(0.0105);
	m_pChargeSprite->pev->framerate = 10;
	m_pChargeSprite->TurnOff();	
}

void CSpiritBomb::WeaponIdle()
{	if(m_pPlayer->m_fCharging)
	{	if(m_fCharge1)
		{	Shoot1();
		}
		else if(m_fCharge2)
		{	Shoot2();
		}
	}
}		

void CSpiritBomb::PrimaryAttack( void )
{	if(m_fCharge2)
	{	return;
	}
	if (!m_pPlayer->m_fCharging)	//start charging
	{	//check if we spirit bomb pos is free
		UTIL_MakeVectors( m_pPlayer->pev->angles );
		Vector vecBombPos = m_pPlayer->pev->origin + Vector(0,0,200);
		TraceResult tr;
		UTIL_TraceLine(m_pPlayer->GetGunPosition(),vecBombPos,ignore_monsters,ENT(m_pPlayer->pev),&tr);
		if (tr.flFraction<1)
			return;

		//make some sounds :-)
		m_pPlayer->EmitClassSound("spiritbomb_charge.wav");
		
		m_pPlayer->m_fCharging = true;
		m_pPlayer->m_fChargeNamekSB = true;
		m_fCharge1 = true;
		m_pPlayer->m_flSlowDown=0.0;
		m_iCharge=0;		

		m_pSpiritBomb = CSpiritBombAttack::CreateSpiritBomb(m_pPlayer->pev, vecBombPos,m_sSprite);

		// Edited by Pcjoe
		//enable charge sprites
		//MESSAGE_BEGIN( MSG_ALL, gmsgChargeSB, m_pPlayer->pev->origin);
		//	WRITE_COORD(m_pSpiritBomb->pev->origin.x);
		//	WRITE_COORD(m_pSpiritBomb->pev->origin.y);
		//	WRITE_COORD(m_pSpiritBomb->pev->origin.z);
		//	WRITE_SHORT( m_pPlayer->entindex() );
		//MESSAGE_END();		
		MESSAGE_BEGIN( MSG_ALL, gmsgAddEffect, m_pPlayer->pev->origin);
			WRITE_BYTE(SPIRITBOMB);
			WRITE_SHORT( m_pSpiritBomb->entindex() );
			WRITE_COORD(m_pSpiritBomb->pev->origin.x);
			WRITE_COORD(m_pSpiritBomb->pev->origin.y);
			WRITE_COORD(m_pSpiritBomb->pev->origin.z);			
		MESSAGE_END();		
	}
	else //increase damage
	{	// Check we have some ammo
		if (!m_pPlayer->UseKi(SPIRITBOMB_KI_COST))
		{	if (m_pPlayer->m_fCharging)
			{	Shoot1();
			}
			return;
		}
	
		if (m_iCharge>SPIRITBOMB_MAXCHARGETIME)
		{	/* no more auto-shoot
			Shoot1();
			*/
		}
		else
		{	if(m_iCharge%2) 
			{	m_pSpiritBomb->Grow(); 
			}

			m_iCharge++;
			
			/* Modified by Rick
			//draw charge bar
			MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
				WRITE_BYTE( m_iCharge*100/SPIRITBOMB_MAXCHARGETIME);	//amount charged in %
			MESSAGE_END();*/
			m_pPlayer->DrawChargeBar(m_iCharge*100/SPIRITBOMB_MAXCHARGETIME); // draw charge bar
		}
	}
	m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + CHARGETIME;
}

void CSpiritBomb::SecondaryAttack( void )
{	if(m_fCharge1)
	{	return;
	}
	
	if (!m_pPlayer->m_fCharging)	//start charging
	{	m_pPlayer->m_fCharging = true;
		m_pPlayer->m_fCharging2 = true;
		m_fCharge2 = true;
		m_pPlayer->m_flSlowDown=0.0;
		m_iCharge=0;

		//make some sounds :-)
		m_pPlayer->EmitClassSound("spiritbomb2_charge.wav");
		
		if (!m_pPlayer->m_fTurbo)
		{
			m_fGlowing = true;

			//glowshell fx
			m_pPlayer->pev->rendercolor = Vector (255,255,255);
			m_pPlayer->pev->renderamt = 0;
			m_pPlayer->pev->renderfx = kRenderFxGlowShell;
		}
	}
	else //increase damage
	{	
		// Check we have some ammo
		if (!m_pPlayer->UseKi(SPIRITBOMB2_KI_COST))
		{	if (m_pPlayer->m_fCharging)
			{	Shoot2();
			}
			return;
		}
	
		
		if (m_iCharge>=SPIRITBOMB2_MAXCHARGETIME)
		{	//Shoot2();
		}
		else
		{	int iChargeLine = SPIRITBOMB2_MAXCHARGETIME/3;
			if (m_iCharge == iChargeLine)
			{	EnableChargeSprite();
			}		
			else if (m_iCharge > iChargeLine)
			{	GrowChargeSprite();
				if(m_fGlowing)
				{	m_pPlayer->pev->renderamt = 35 - 35 * (m_iCharge-iChargeLine)/(SPIRITBOMB2_MAXCHARGETIME-iChargeLine);
				}
			}
			else
			{	if(m_fGlowing)
				{	m_pPlayer->pev->renderamt = 35 * m_iCharge/iChargeLine;
				}
			}
			
			m_iCharge++;			

			/* Modified by Rick
			//draw charge bar
			MESSAGE_BEGIN( MSG_ONE, gmsgCharge, NULL, m_pPlayer->pev ); 
				WRITE_BYTE( m_iCharge*100/SPIRITBOMB2_MAXCHARGETIME);	//amount charged in %
			MESSAGE_END();*/

			m_pPlayer->DrawChargeBar(m_iCharge*100/SPIRITBOMB2_MAXCHARGETIME); // draw charge bar
		}
	}
	m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + CHARGETIME;
}