#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "effects.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "aura.h"
#include "classes.h"
#include "gamerules.h"
#include "chargedattack.h"
#include "player.h"
#include "shield.h"
#include "blob.h"

extern int gmsgBlobStart;
extern int gmsgBlobUpdate;
extern int gmsgBlobKill;
extern int gmsgCharge;

#define SHIELDATTACK_SIZE			 32
/**
* Gohans Shield Attack
* @version 10-14-2002
* @author Joseph 'Pcjoe' Florencio
*/
class CShieldAtk:public CExplodingAttack
{	
public:
	/**
	* Create a shield 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 damage: the amount of damage to be done
	* @param size: the size of the shieldfire sprite
	*/
	static CShieldAtk *ShootShield( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage);	
};

CShieldAtk* CShieldAtk::ShootShield( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage)
{	CShieldAtk *pMagicAttack = GetClassPtr( (CShieldAtk *)NULL );
	pMagicAttack->Spawn();	
	
	pMagicAttack->pev->movetype = MOVETYPE_FLY;
	pMagicAttack->pev->solid = SOLID_TRIGGER;	//touches everything

	//set model
	SET_MODEL(ENT(pMagicAttack->pev), "sprites/shieldattack.spr");
	
	pMagicAttack->m_iExplosionColor = YELLOW_EXPLOSION;
	pMagicAttack->m_iRadius = SHIELDFIRINGRADIUS;

	pMagicAttack->SetDamage(damage); 
	pMagicAttack->pev->velocity=vecVelocity;
	pMagicAttack->m_flSpeed=vecVelocity.Length();
	pMagicAttack->pev->angles=UTIL_VecToAngles(pMagicAttack->pev->velocity);
	pMagicAttack->pev->owner=ENT(pevOwner);
	pMagicAttack->pev->classname = MAKE_STRING( "shieldattack" );
	pMagicAttack->pev->scale = 1;

	int iAttackSize = SHIELDATTACK_SIZE/2;
	UTIL_SetOrigin(pMagicAttack->pev,vecStart + iAttackSize * vecVelocity.Normalize());
	UTIL_SetSize(pMagicAttack->pev, Vector(-iAttackSize, -iAttackSize, -iAttackSize), Vector(iAttackSize, iAttackSize, iAttackSize));
	pMagicAttack->SetTouch( &CShieldAtk::ExplodeTouch );

	// make monsters/players afraid of it while in the air
	pMagicAttack->SetThink( &CShieldAtk::NormalThink );
	pMagicAttack->pev->nextthink = gpGlobals->time;

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

/**
* Gohans Shield
* @version 9-7-2002
* @author Joseph 'Pcjoe' Florencio
*/
void CShield::Precache()
{
}

void CShield::Spawn( )
{	Precache();
	SET_MODEL(ENT(pev), "sprites/null.spr");
	pev->rendermode = kRenderTransAdd;
	pev->movetype = MOVETYPE_NONE;
	pev->solid = SOLID_TRIGGER;
}

CShield *CShield::CreateShield(CBasePlayer *pl)
{	CShield *pBlob = GetClassPtr( (CShield *)NULL );
	pBlob->Spawn( );

	// Setting info...
	pBlob->pev->velocity = Vector(0, 0, 0);
	pBlob->pev->origin = pl->pev->origin;
	pBlob->pev->classname = MAKE_STRING( "kishield" );
	pBlob->pev->dmg = 0;
	pBlob->m_iSize = SHIELDATTACK_START_RADIUS;
	pBlob->flAddDamage = 0;
	pBlob->m_fRemove = false;
	pBlob->SetThink(&CShield::ExpandShieldThink);
	// Setting times...
	pBlob->pev->nextthink = gpGlobals->time;
	pBlob->pev->owner = ENT(pl->pev);
	pBlob->SetTouch(&CShield::ShieldTouch);
	// Setting size
	UTIL_SetSize(pBlob->pev, Vector(-1*SHIELDATTACK_START_RADIUS, -1*SHIELDATTACK_START_RADIUS, -1*SHIELDATTACK_START_RADIUS),
				Vector(1*SHIELDATTACK_START_RADIUS, 1*SHIELDATTACK_START_RADIUS, 1*SHIELDATTACK_START_RADIUS));
	// Setting origin
	UTIL_SetOrigin(pBlob->pev,pBlob->pev->origin);
	// Create clientside effect
	pBlob->CreateExplosion();

	return pBlob;
}

CBasePlayer *CShield::GetOwner( entvars_t *pevOwner )
{	return (CBasePlayer*)CBaseEntity::Instance(pevOwner);
}

void CShield::ExpandShieldThink( void )
{	// Pointer to owner
	CBasePlayer *pPlayer = GetOwner(VARS(pev->owner));

	// Set the origin
	if((pPlayer->pev->origin - pev->origin).Length() >= 3)
	{	pev->origin = pPlayer->pev->origin;
		UTIL_SetOrigin(pev,pev->origin);
	}


	// Increase size
	m_iSize += 20;
	if(m_iSize >= SHIELDATTACK_RADIUS)
	{	m_iSize = SHIELDATTACK_RADIUS;
		SetThink(&CShield::ShieldThink);
	}
	// Make it grow server side
	UTIL_SetSize(pev, Vector(-1*m_iSize, -1*m_iSize, -1*m_iSize), 
				Vector(1*m_iSize, 1*m_iSize, 1*m_iSize));
	// Send a message to the client to increase shieldsize
	UpdateShieldClient();

	// If the shield is disabled on the player, kill the entity
	
	/* removed by harSens
	if(!pPlayer->m_fShield)
	{	Remove();
		return;
	}
	*/

	pev->nextthink = gpGlobals->time + 0.2;
}

void CShield::ShieldThink( void )
{	CBasePlayer *pPlayer = GetOwner(VARS(pev->owner));

	// Set the origin
	if((pPlayer->pev->origin - pev->origin).Length() >= 3)
	{	pev->origin = pPlayer->pev->origin;

		UTIL_SetOrigin(pev,pev->origin);
	}

	// Small damage adds to shield
	if(flAddDamage < gpGlobals->time)
	{
		flAddDamage = gpGlobals->time + 1.75;
		pev->dmg += 3;
	}

	/* removed by harSens
	if(!pPlayer->m_fShield)
	{	Remove();
		return;
	}
	*/

	pev->nextthink = gpGlobals->time;
}

void CShield::CreateExplosion( void )
{	
	MESSAGE_BEGIN( MSG_ALL, gmsgBlobStart, pev->origin );
		WRITE_BYTE (0);			// Is it a blob or a shield? ( 1 = Blob, 0 = Shield )
		WRITE_COORD(pev->origin.x);	// Origin
		WRITE_COORD(pev->origin.y);
		WRITE_COORD(pev->origin.z);
		WRITE_SHORT (entindex());	// Entity Index
		WRITE_SHORT (m_iSize);	// Size of Explosion		
	MESSAGE_END();
}

void CShield::UpdateShieldClient( void )
{
	MESSAGE_BEGIN( MSG_ALL, gmsgBlobUpdate, pev->origin );
		WRITE_SHORT (m_iSize);	// Size of Explosion
		WRITE_SHORT (entindex());	// Entity Index
	MESSAGE_END();
}

void CShield::Remove( void )
{	
	MESSAGE_BEGIN( MSG_ALL, gmsgBlobKill);
		WRITE_SHORT (entindex());	// Entity Index
		WRITE_SHORT (m_iSize);		// Size
	MESSAGE_END();

	UTIL_Remove(this);
}

void CShield::ShieldTouch(CBaseEntity *pOther)
{	//Push people away that try to enter
	if (pOther->IsPlayer())
	{	
		// Player ptr...
		CBasePlayer *pPlayer = GetOwner(VARS(pev->owner));
		CBasePlayer *pOther2 = (CBasePlayer*)pOther;

		// Dont mess with the owner
		if(pPlayer->entindex() == pOther2->entindex())
		{	return;
		}

		// Dont mess with powerstrugglers or blockstrugglers
		if(pOther2->m_fPowerStruggling || pOther2->m_fBlockStruggling || pOther2->m_fBlockStruggleAttacker || pOther2->m_Melee.InAdvMeleeBattle())
		{	return;
		}

		// Let team mates pass through
		if( g_pGameRules->IsTeamplay() &&( pPlayer->pev->team == pOther->pev->team))
		{	return;
		}

		Vector vecDir = (pev->origin-pOther->pev->origin)*-1;

		// Push enemys away
		pOther->pev->velocity = vecDir.Normalize() * 300;
	}
	// Added by Rick : Push buddies away aswell
	else if (FStrEq(STRING(pOther->pev->classname), "player_buddy"))
	{	
		CBasePlayer *pPlayer = GetOwner(VARS(pev->owner));
		// Dont mess with powerstrugglers or blockstrugglers
//		if(pOther2->m_fPowerStruggling || pOther2->m_fBlockStruggling || pOther2->m_fBlockStruggleAttacker || pOther2->m_Melee.InAdvMeleeBattle())
//		{	return;
//		}

		// Let team mates pass through
		if( g_pGameRules->IsTeamplay() &&( pPlayer->pev->team == pOther->pev->team))
		{	return;
		}

		Vector vecDir = (pev->origin-pOther->pev->origin)*-1;

		// Push enemies away
		pOther->pev->velocity = vecDir.Normalize() * 300;
	}
	else if(pOther->Classify() == CLASS_EXPLODINGATTACK)
	{
		CBasePlayer *pPlayer = GetOwner( VARS(pev->owner) );
		CExplodingAttack *pExp = (CExplodingAttack*)pOther;

		// If attacks are blockstruggling or powerstruggling, leave them alone
		if(pExp->m_fBlockStruggle || pExp->m_fPowerStruggle || pExp->m_fStopped)
		{	return;
		}

		// Take damage strait away from KI
		if(!pPlayer->UseKi(pExp->pev->dmg*3.5))
		{	pPlayer->pev->fuser4 = 0;	// Reset KI
			m_fRemove = true;
			//Remove();			
			//pPlayer->m_fShield = FALSE;
			return;
		}
		else
		{
			// Add damage
			// Ki Blast
			if(FClassnameIs(pExp->pev, "kiblast"))
			{	
				pev->dmg += pExp->pev->dmg;
			}
			// Any other Exploding projectile
			else
			{
				pev->dmg += pOther->pev->dmg/4;
			}

			pExp->Vaporize();
		}
	}
}

//--------------------------------------------------------------
/**
* Shield Weapon
* @version 9-7-2002
* @author Joseph 'Pcjoe' Florencio
*/
class CShieldAttack:public CBasePlayerWeapon
{	
public:
	/**
	* Init function
	*/
	void Spawn( void );

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

	/**
	* Get fingerlaser slot
	* return int: the slot, is 5 for Shield Attack
	*/
	int iItemSlot( void ) { return 5; }

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

	/**
	 * Deploys the Shield Attack
	 * @return BOOL: true if succesfully deployed
	 */
	BOOL Deploy( void );

	/**
	 * shoots a piercing fingerlaser attack
	 */	
	void PrimaryAttack(void);

	/**
	 * shoots an exploding fingerlaser attack
	 */
	void SecondaryAttack(void);

	/**
	* Weapons Idle
	*/
	void WeaponIdle( void );

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

private:
	/**
	* creates the shield attack
	*/
	void CreateShield( void );

	/*
	* shoots the shield attack
	* return: if it shot or not ( if the shield absorbed enough damage to shoot )
	*/
	void ShootShield( void );

	/*
	* Removes Shield
	*/
	void RemoveShield( void );

	// Pointer to attack you created
	CShield *pShield;

	// Timers...
	float m_flShootAttack;
	float m_flStopControl;
	// Dmg for shooting shield
	int iShootShieldDmg;
	// Enum for shield management
	enum { SHIELD_NONE, SHIELD_IDLE, SHIELD_CHARGE, SHIELD_FIRE } m_eShieldStatus;
};

LINK_ENTITY_TO_CLASS( weapon_shieldattack, CShieldAttack );

void CShieldAttack::Spawn( void )
{	Precache();
	m_iId = WEAPON_SHIELDATTACK;
	m_iDefaultAmmo = 0;
	m_iClip = -1;
	pShield = NULL;
	m_flShootAttack = 0;
	m_flStopControl = 0;
	iShootShieldDmg = 0;
	m_eShieldStatus = SHIELD_NONE;

	FallInit();// get ready to fall down.
}

void CShieldAttack::RemoveShield( void )
{	// Disabling shield
	if(pShield)
	{	pShield->Remove();
		pShield = NULL;
	}
	m_pPlayer->m_fShield = FALSE;
}

void CShieldAttack::Holster( int skiplocal = 0)
{	// Destroy the shield if one is active
	if(m_eShieldStatus > SHIELD_NONE)
	{	RemoveShield();
		m_eShieldStatus = SHIELD_NONE;
		m_pPlayer->m_fControl = FALSE;
		m_pPlayer->m_fCharging = FALSE;
		m_pPlayer->m_flSlowDown = 1.0;
		m_flShootAttack = 0;
		m_flStopControl = 0;
		iShootShieldDmg = 0;
	}
}

void CShieldAttack::Precache( void )
{	PRECACHE_MODEL("sprites/shieldattack.spr");
	PRECACHE_SOUND("weapons/shield.wav");
	PRECACHE_SOUND("weapons/shieldha.wav");
	PRECACHE_SOUND("gohan/shield.wav");
	PRECACHE_SOUND("gohan/shield_charge.wav");
}

int CShieldAttack::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 = 7;
	p->iId = m_iId = WEAPON_SHIELDATTACK;
	p->iWeight = SHIELDATTACK_WEIGHT;
	p->iFlags = 0;
	return 1;
}

BOOL CShieldAttack::Deploy( void )
{	return DefaultDeploy("shieldattack");
}

void CShieldAttack::WeaponIdle( void )
{	// If the shield is disable within the player, disable it here
	//modified by harSens
	//if(!m_pPlayer->m_fShield && m_eShieldStatus > SHIELD_NONE)
	if(m_eShieldStatus > SHIELD_NONE && pShield)
	{
		if(pShield->m_fRemove)
		{	RemoveShield();
			m_pPlayer->m_fShield = FALSE;
			m_eShieldStatus = SHIELD_NONE;
			m_pPlayer->m_fControl = FALSE;
			m_pPlayer->m_fCharging = FALSE;
			m_pPlayer->m_flSlowDown = 1.0;
			m_flShootAttack = 0;
			m_flStopControl = 0;
			iShootShieldDmg = 0;
			m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 1;
			return;
		}
	}
	// If the guys not attacking, disable teh sheild
	if(m_eShieldStatus == SHIELD_IDLE)
	{	// Disabling Shield
		RemoveShield();

		// Disabling player varibles
		m_pPlayer->m_flSlowDown = 1.0;
		m_pPlayer->m_fCharging = FALSE;
		m_eShieldStatus = SHIELD_NONE;

		// Setting next attack time
		m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 2.5;
	}
	else if(m_eShieldStatus == SHIELD_CHARGE)
	{
		// If player is in melee, completly stop fire
		if(m_pPlayer->m_fMelee)
		{
			m_flShootAttack = 0;
			m_flStopControl = 0;
			iShootShieldDmg = 0;
			m_pPlayer->m_fControl = FALSE;
			m_pPlayer->m_fCharging = FALSE;
			m_pPlayer->m_fShield = FALSE;
			m_eShieldStatus = SHIELD_NONE;
			return;
		}

		// Time to shoot? If so, fire it!
		if(m_flShootAttack <= UTIL_WeaponTimeBase())
		{
			// Shoot Shield
			ShootShield();
			// Setting the control animation to true, and stop charging
			m_pPlayer->m_fControl = TRUE;
			m_pPlayer->m_fCharging = FALSE;
			m_eShieldStatus = SHIELD_FIRE;
			// Setting stop control time
			m_flStopControl = UTIL_WeaponTimeBase() + 1.25;
		}
	}
	else if(m_eShieldStatus == SHIELD_FIRE)
	{
		if(m_flStopControl < UTIL_WeaponTimeBase())
		{
			// Resetting everything
			m_flStopControl = 0;
			m_flShootAttack = 0;
			m_pPlayer->m_fControl = FALSE;
			m_pPlayer->m_fCharging = FALSE;
			m_pPlayer->m_fShield = FALSE;
			m_pPlayer->m_flSlowDown = 1.0;
			m_eShieldStatus = SHIELD_NONE;
		}
	}
}

void CShieldAttack::PrimaryAttack( void )
{	if(m_eShieldStatus > SHIELD_IDLE)
	{	// Ignore and run WeaponIdle
		WeaponIdle();
		m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase();
		return;
	}

	// Dont attempt to make a shield if you already have one out
	if(m_eShieldStatus == SHIELD_IDLE)
	{	// Take away KI
		if(!m_pPlayer->UseKi(SHIELDATTACK_KI_COST))
		{	
			// Disable shield if your out of KI
			RemoveShield();

			m_pPlayer->m_fCharging = FALSE;
			m_pPlayer->m_flSlowDown = 1.0;
			m_eShieldStatus = SHIELD_NONE;
			m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 2;
			return;
		}
	}
	else if(m_eShieldStatus == SHIELD_NONE)
	{	if(m_pPlayer->UseKi(SHIELDATTACK_KI_COST))	
		{	
			// Start it up!
			CreateShield();

			// If flying is disabled and we are in the air, enable flying
			if(!FBitSet(m_pPlayer->pev->flags, FL_ONGROUND))
			{
				m_pPlayer->StartFly();
			}
			// Enable bools + set varibles
			m_pPlayer->m_fShield = TRUE;
			m_pPlayer->m_fCharging = TRUE;
			m_pPlayer->m_flSlowDown = 0.0;
			iShootShieldDmg = 0;
			m_eShieldStatus = SHIELD_IDLE;
		}
	}
	
	m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + CHARGETIME;
}

void CShieldAttack::SecondaryAttack( void )
{	// If control is enabled, dont run this function
	if(m_eShieldStatus != SHIELD_IDLE)
	{	WeaponIdle();
		m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase();
		return;
	}

	// Storing Shield damage
	iShootShieldDmg = pShield->pev->dmg;

	// Remove the shield first
	RemoveShield();

	// If the shield has any type of energy in it, allow the player to shoot it
	if(iShootShieldDmg && m_pPlayer->UseKi(SHIELDATTACK_KI_COST*5))
	{
		// Prepare to fire shield
		m_pPlayer->m_fCharging = TRUE;
		m_pPlayer->m_fControl = FALSE;
		m_pPlayer->m_fShield = TRUE;
		m_flShootAttack = UTIL_WeaponTimeBase() + 1.5;
		m_eShieldStatus = SHIELD_CHARGE;
		m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 5.5;
		return;
	}

	m_pPlayer->m_fCharging = FALSE;
	m_pPlayer->m_fShield = FALSE;
	m_pPlayer->m_flSlowDown = 1.0;
	m_eShieldStatus = SHIELD_NONE;
	m_flShootAttack = 0;
	m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 4;
}

void CShieldAttack::CreateShield()
{	// Create Attack
	pShield = CShield::CreateShield(m_pPlayer);
	// Emit sound
	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/shield.wav", 1.0, ATTN_NORM);
	m_pPlayer->EmitClassSound("shield_charge.wav");
}

void CShieldAttack::ShootShield()
{
	//shoot the attack
	UTIL_MakeVectors( m_pPlayer->pev->v_angle );
	Vector vecSrc = m_pPlayer->GetGunPosition();
	Vector vecThrow = gpGlobals->v_forward * SHIELDATTACK_SPEED;
	CShieldAtk::ShootShield( m_pPlayer->pev, vecSrc, vecThrow, iShootShieldDmg * GetPowerRatio());
	// Emit sound
	EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/shieldha.wav", 1.0, ATTN_NORM);
	m_pPlayer->EmitClassSound("shield.wav");
}