#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "soundent.h"
#include "decals.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 "kiattack.h"
#include "blob.h"

// Added by Rick
#include "../bot/bot_util.h"
#include "talkmonster.h"
#include "buddy_aura.h"
#include "buddy.h"
// End add by Rick


extern int gmsgExplosion;
extern int gmsgFlare;
extern int gmsgBlobStart;
extern int gmsgBlobUpdate;
extern int gmsgBlobKill;
#define MAX_KIBLOB_DAMAGE 20

void CBlob::Precache()
{
}

void CBlob::Spawn()
{	Precache();
	SET_MODEL(ENT(pev), "sprites/null.spr");
	pev->rendermode = kRenderTransAdd;
	m_pDelayedDamage=NULL;
	pev->movetype = MOVETYPE_NONE;
	pev->solid = SOLID_TRIGGER;
	m_iMaxSize = MAX_KIBLOB_SIZE;
	m_iMaxDamage = MAX_KIBLOB_DAMAGE;
	m_iDamageCounter = 0;
	UTIL_SetSize(pev, Vector(-1*BLOBATTACK_START_RADIUS, -1*BLOBATTACK_START_RADIUS, -1*BLOBATTACK_START_RADIUS), 
				Vector(BLOBATTACK_START_RADIUS, BLOBATTACK_START_RADIUS, BLOBATTACK_START_RADIUS));
}

void CBlob::ApplyDelayedDamage()
{	
	DelayedDamage *pDelayedDamage = m_pDelayedDamage;
	DelayedDamage *pPrevDelayedDamage = NULL;
	DelayedDamage *temp;
	
	int iSizeInc = 0;
	int iTotalDmg=0;
	while(pDelayedDamage)
	{
		double flDist = (pDelayedDamage->pOwner->pev->origin - pev->origin).Length();
		int iDmg = pDelayedDamage->damage;
		
		if(flDist < (gpGlobals->time - pDelayedDamage->flAddTime)*pDelayedDamage->speed)
		{	
			
			iTotalDmg+=iDmg;			
			//DelayedSizeInc(25);
			iSizeInc+=25;

			temp = pDelayedDamage;
			pDelayedDamage = pDelayedDamage->next;
			if(pPrevDelayedDamage)
			{
				pPrevDelayedDamage->next=pDelayedDamage;
			}
			else
			{
				m_pDelayedDamage=pDelayedDamage;
			}
			delete temp;			
		}
		else
		{
			pPrevDelayedDamage=pDelayedDamage;
			pDelayedDamage = pDelayedDamage->next;
		}		
	}
	
	if(iSizeInc>0)
	{	if(m_iSize == 0 && !m_fShown)
		{
			iSizeInc+=BLOBATTACK_START_RADIUS;
		}
		SizeInc(iSizeInc,iTotalDmg);
	}
}
void CBlob::BlobThink( void )
{	ApplyDelayedDamage();
	// Was it around too long? Kill it!
	if(m_flLastUpdateTime < gpGlobals->time - 7)
	{
		Remove();
		return;
	}

	// Cause damage in the sphere
	if(m_iDamageCounter>=10)
	{
		::RadiusDamage( pev->origin, pev, VARS(pev->owner), pev->dmg, m_iSize, CLASS_NONE, DMG_BLOB );
		m_iDamageCounter=0;
	}
	else
	{
		m_iDamageCounter++;
	}
	pev->nextthink = gpGlobals->time + 0.05;
}

void CBlob::CreateExplosion( void )
{	
	MESSAGE_BEGIN( MSG_ALL, gmsgBlobStart );
		WRITE_BYTE (1);				// 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 CBlob::UpdateExplosion( void )
{
	if(!m_fShown)
	{
		m_fShown=true;
		CreateExplosion();
	}
	MESSAGE_BEGIN( MSG_ALL, gmsgBlobUpdate );
		WRITE_SHORT (m_iSize);	// Size of Explosion
		WRITE_SHORT (entindex());	// Entity Index
	MESSAGE_END();
}

void CBlob::Remove( void )
{	
	// Explosion
	m_iSize += m_iSize * 0.85;
	::RadiusDamage( pev->origin, pev, VARS(pev->owner), pev->dmg*2.5, m_iSize, CLASS_NONE, DMG_MAGIC );

	MESSAGE_BEGIN( MSG_ALL, gmsgBlobKill );
		WRITE_SHORT (entindex());	// Entity Index
		WRITE_SHORT (m_iSize);		// Size
	MESSAGE_END();

	entvars_t *pevOwner;
	if (pev->owner)
	{	pevOwner=VARS(pev->owner);
	}
	else
	{	pevOwner=NULL;
		return;
	}

	// Modified by Rick : Buddies have kiblasts aswell
	
	CBaseEntity *pEntOwner = CBaseEntity::Instance(pevOwner);

	if (!pEntOwner)
	{
		pevOwner = NULL;
		return;
	}
	// Is the owner a player?
	if (pEntOwner->IsPlayer())
	{
		// Player ptr...
		CBasePlayer *pPlayer = (CBasePlayer*)pEntOwner;

		// reset players blob Status
		for(int i = 0; i < 2; i++)
		{	pPlayer->m_vecBlob[i] = Vector(0, 0, 0);
			pPlayer->m_flBlobTime[i] = -1;
		}
	}
	// Otherwise its a buddy
	else
	{
		// Buddy ptr...
		CBuddy *pBuddy = (CBuddy*)pEntOwner;

		// reset players blob Status
		for(int i = 0; i < 2; i++)
		{	pBuddy->m_vecBlob[i] = Vector(0, 0, 0);
			pBuddy->m_flBlobTime[i] = -1;
		}
	}
	RemoveDelayedDamage(m_pDelayedDamage);
	UTIL_Remove(this);
}
/*
void CBlob::DelayedSizeInc(int SizeInc)
{
	m_iSize += SizeInc;	
	m_flLastUpdateTime = gpGlobals->time;

	// Update size clientside
	UpdateExplosion();

	UTIL_SetSize(pev, Vector(-1*m_iSize, -1*m_iSize, -1*m_iSize), 
				Vector(1*m_iSize, 1*m_iSize, 1*m_iSize));
}
*/
void CBlob::SizeInc( int SizeInc , int iDmgInc)
{	
	bool blDecreaseSize = false;
	if(SizeInc < 0)
	{	
		if(m_iSize < 0)
		{
			Remove();
			return;
		}
		else
		{	m_iSize = m_iSize + SizeInc;
			blDecreaseSize = true;
		}
	}
	// Check for max size
	else if(m_iSize != m_iMaxSize && !blDecreaseSize)
	{	m_iSize += SizeInc;
	}
	
	if(m_iSize > m_iMaxSize)
	{	m_iSize = m_iMaxSize;
	}

	// If it has too much energy, blow it up!
	if(pev->dmg > m_iMaxDamage)
	{	Remove();
		return;
	}

	m_flLastUpdateTime = gpGlobals->time;

	if(!blDecreaseSize)
	{	pev->dmg += iDmgInc;
	}

	// Update size clientside
	UpdateExplosion();

	UTIL_SetSize(pev, Vector(-1*m_iSize, -1*m_iSize, -1*m_iSize), 
				Vector(1*m_iSize, 1*m_iSize, 1*m_iSize));
}

void CBlob::AddDelayedDamage(DelayedDamage *dDamage)
{
	dDamage->next = m_pDelayedDamage;	
	m_pDelayedDamage = dDamage;		
	//ApplyDelayedDamage();
}

void CBlob::BlobTouch(CBaseEntity *pOther)
{	if (pOther->Classify() == CLASS_EXPLODINGATTACK)
	{	CExplodingAttack *pExp = (CExplodingAttack*)pOther;
		if(!pExp->m_fPowerStruggle && !pExp->m_fBlockStruggle && !pExp->m_fStopped)
		{	if(FClassnameIs(pExp->pev, "kiblast"))
			{	// Make it bigger
				SizeInc( 25,2 );
			}
			// Disregard Scatter shot, rock
			else if(!FClassnameIs(pOther->pev, "scattershot") && !FClassnameIs(pOther->pev, "rock"))
			{	// Absorb Beams
				pev->dmg += pExp->pev->dmg/1.5;
				m_iSize += pExp->GetExplosionRadius()/2;
				Remove();		
			}
			pExp->Vaporize();
		}
	}
}

CBlob *CBlob::CreateBlob(CBaseEntity *pEnt, Vector vecOrigin,int dmg,int show,int maxsize,int maxdmg)
{
	CBlob *pBlob = GetClassPtr( (CBlob *)NULL );
	pBlob->Spawn( );
	pBlob->m_iMaxSize = maxsize;
	pBlob->m_iMaxDamage = maxdmg;
	pBlob->pev->velocity = Vector(0, 0, 0);
	pBlob->pev->origin = vecOrigin;
	pBlob->pev->classname = MAKE_STRING( "kiblob" );
	pBlob->m_iSize = 0;//BLOBATTACK_START_RADIUS;
	pBlob->pev->dmg = dmg;
	pBlob->pev->owner = ENT(pEnt->pev);
	// Setting thinks...
	pBlob->SetThink(&CBlob::BlobThink);
	pBlob->SetTouch(&CBlob::BlobTouch);
	pBlob->pev->nextthink = gpGlobals->time;
	pBlob->m_flLastUpdateTime = gpGlobals->time;
	// Setting origin
	UTIL_SetOrigin(pBlob->pev,pBlob->pev->origin);
	
	pBlob->m_fShown = show;
	if(show)
	{	// Create clientside effect
		pBlob->CreateExplosion();
	}
	return pBlob;
}

CBlob *CBlob::CreateBlob(CBaseEntity *pEnt, Vector vecOrigin)
{	return CreateBlob(pEnt, vecOrigin,2,1,MAX_KIBLOB_SIZE,MAX_KIBLOB_DAMAGE);
}

void CKiAttack::DrawExplosionFX(int radius, Vector origin)
{	//explosion sphere
	MESSAGE_BEGIN( MSG_ALL, gmsgExplosion);
		WRITE_COORD( origin.x );	
		WRITE_COORD( origin.y );
		WRITE_COORD( origin.z );
		WRITE_LONG (radius);//radius
		WRITE_BYTE (YELLOW_EXPLOSION);		
	MESSAGE_END();
	// Added by Pcjoe, Blob Attack
	// Make sure these are KI blast that make the blobs
	if(!FClassnameIs(pev, "kiblast"))
	{	return;
	}

	//added by harSens: don't create blobs after a ps or bs
	if(m_fPowerStruggle || m_fBlockStruggle)
	{
		return;
	}

	entvars_t *pevOwner;
	if (pev->owner)
	{	pevOwner=VARS(pev->owner);
	}
	else
	{	pevOwner=NULL;
		return;
	}
	
	// Dont create blob on meleeing players
	CBaseEntity *pEntity = NULL;
	while ((pEntity = UTIL_FindEntityInSphere( pEntity, origin, radius )) != NULL)
	{	if(pEntity->IsPlayer())
		{	if(((CBasePlayer*)pEntity)->m_Melee.InAdvMeleeBattle())
			{	return;
			}
		}
	}

	// Added/Modified by Rick : Buddy kiblast support
	CBaseEntity *pEntOwner = CBaseEntity::Instance(pevOwner);

	if (!pEntOwner)
	{
		pevOwner = NULL;
		return;
	}

	// Owner is a player?
	if (pEntOwner->IsPlayer())
	{
	// Player ptr...
		CBasePlayer *pPlayer = (CBasePlayer*)pEntOwner;

		if(pPlayer->m_flBlobTime[0] == -1)
	{	pPlayer->m_flBlobTime[0] = gpGlobals->time;
		pPlayer->m_vecBlob[0] = origin;
	}
	else if(pPlayer->m_flBlobTime[1] == -1)
	{	pPlayer->m_flBlobTime[1] = gpGlobals->time;
		pPlayer->m_vecBlob[1] = origin;
	}
	else
	{	// Reset the times
		pPlayer->m_flBlobTime[0] = gpGlobals->time;
		pPlayer->m_vecBlob[0] = origin;
		pPlayer->m_flBlobTime[1] = -1;
	}
	
	// Make sure they have values stored
	if(pPlayer->m_flBlobTime[0] != -1 && pPlayer->m_flBlobTime[1] != -1)
	{	
		if((pPlayer->m_flBlobTime[1] - pPlayer->m_flBlobTime[0]) < 1)
		{	
			if((pPlayer->m_vecBlob[0] - pPlayer->m_vecBlob[1]).Length() < 150)
			{
				Vector vecAverage;
				vecAverage.x = ( pPlayer->m_vecBlob[0].x + pPlayer->m_vecBlob[1].x ) / 2;
				vecAverage.y = ( pPlayer->m_vecBlob[0].y + pPlayer->m_vecBlob[1].y ) / 2;
				vecAverage.z = ( pPlayer->m_vecBlob[0].z + pPlayer->m_vecBlob[1].z ) / 2;

					CBlob::CreateBlob(pPlayer, vecAverage);

					// Reset Values
					for(int i = 0; i < 2; i++)
						pPlayer->m_flBlobTime[i] = -1;
				}
			}
		}
	}
	// Otherwise the owner is a buddy
	else
	{
		// Buddy ptr...
		CBuddy *pBuddy = (CBuddy*)pEntOwner;

		if(pBuddy->m_flBlobTime[0] == -1)
		{	pBuddy->m_flBlobTime[0] = gpGlobals->time;
			pBuddy->m_vecBlob[0] = origin;
		}
		else if(pBuddy->m_flBlobTime[1] == -1)
		{	pBuddy->m_flBlobTime[1] = gpGlobals->time;
			pBuddy->m_vecBlob[1] = origin;
		}
		else
		{	// Reset the times
			pBuddy->m_flBlobTime[0] = gpGlobals->time;
			pBuddy->m_vecBlob[0] = origin;
			pBuddy->m_flBlobTime[1] = -1;
		}
	
		// Make sure they have values stored
		if(pBuddy->m_flBlobTime[0] != -1 && pBuddy->m_flBlobTime[1] != -1)
		{	
			if((pBuddy->m_flBlobTime[1] - pBuddy->m_flBlobTime[0]) < 1)
			{	
				if((pBuddy->m_vecBlob[0] - pBuddy->m_vecBlob[1]).Length() < 150)
				{
					Vector vecAverage;
					vecAverage.x = ( pBuddy->m_vecBlob[0].x + pBuddy->m_vecBlob[1].x ) / 2;
					vecAverage.y = ( pBuddy->m_vecBlob[0].y + pBuddy->m_vecBlob[1].y ) / 2;
					vecAverage.z = ( pBuddy->m_vecBlob[0].z + pBuddy->m_vecBlob[1].z ) / 2;

					CBlob::CreateBlob(pBuddy, vecAverage);

					// Reset Values
					for(int i = 0; i < 2; i++)
						pBuddy->m_flBlobTime[i] = -1;
				}
			}
		}
	}
}

CKiAttack* CKiAttack::ShootKiAttack( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail)
{	CKiAttack *pKiAttack = GetClassPtr( (CKiAttack *)NULL );
	pKiAttack->Spawn();	
	
	pKiAttack->pev->movetype = MOVETYPE_FLY;
	pKiAttack->pev->solid = SOLID_TRIGGER;	//touches everything

	//set dummy model
	SET_MODEL(ENT(pKiAttack->pev), "sprites/null.spr");

	pKiAttack->SetDamage(damage);

	UTIL_SetOrigin(pKiAttack->pev,vecStart + 8 * vecVelocity.Normalize());
	float flSize = KIATTACK_SIZE;
	float flAttackSize = flSize * 0.25;	//compensate for emptyness in sprites
	UTIL_SetOrigin(pKiAttack->pev,vecStart + flAttackSize * vecVelocity.Normalize());	
	UTIL_SetSize(pKiAttack->pev, Vector(-flAttackSize, -flAttackSize, -flAttackSize), Vector(flAttackSize, flAttackSize, flAttackSize));
	pKiAttack->pev->velocity = vecVelocity;
	pKiAttack->m_flSpeed = vecVelocity.Length();
	pKiAttack->pev->angles = UTIL_VecToAngles(pKiAttack->pev->velocity);
	pKiAttack->pev->owner = ENT(pevOwner);
	pKiAttack->pev->classname = MAKE_STRING( "kiblast" );
	pKiAttack->m_fExploded = false;
	pKiAttack->m_fRemoveAfterExplode = true;
	
	pKiAttack->SetTouch( &CKiAttack::ExplodeTouch );
	
	MESSAGE_BEGIN( MSG_ALL, gmsgFlare, NULL );
		WRITE_LONG(pKiAttack->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(KIATTACK_TRAIL_LENGTH);	//trail length
		WRITE_COORD(vecStart.x);				//start pos
		WRITE_COORD(vecStart.y);
		WRITE_COORD(vecStart.z);
	MESSAGE_END();

	// make monsters/players afraid of it while in the air
	pKiAttack->SetThink( &CKiAttack::NormalThink );
	pKiAttack->pev->nextthink = gpGlobals->time;
	
	/* Debug: draw boundingbox
	extern int gmsgBoundingBox;
	MESSAGE_BEGIN( MSG_ALL, gmsgBoundingBox);
		WRITE_LONG(pKiAttack->entindex());
	MESSAGE_END();
	*/
	
	return pKiAttack;
}

CKiAttack* CKiAttack::ShootCurvedKiAttack( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, int damage, short flarea, short flareb, short trail)
{	CKiAttack *pKiAttack = CKiAttack::ShootKiAttack(pevOwner,vecStart,vecVelocity,damage,flarea,flareb,trail);
	
	//calculate start speed
	float flStartAngle = RANDOM_FLOAT(10,20);
	flStartAngle = 2.0*M_PI*flStartAngle/360.0;
	float flSideSpeed = sin(flStartAngle)*pKiAttack->m_flSpeed;	
	float flForwardSpeed = cos(flStartAngle)*pKiAttack->m_flSpeed;	
	UTIL_MakeVectors(pKiAttack->pev->angles);
	pKiAttack->m_vecSideVelocity = gpGlobals->v_right*RANDOM_FLOAT(-1,1)+gpGlobals->v_up*RANDOM_FLOAT(-1,1);
	pKiAttack->m_vecSideVelocity = pKiAttack->m_vecSideVelocity.Normalize() * flSideSpeed;

	//calculate acceleration
	TraceResult tr;
	UTIL_TraceLine(pKiAttack->pev->origin,vecStart+vecVelocity.Normalize()*4096,dont_ignore_monsters,NULL,&tr);
	Vector vecPath = tr.vecEndPos-pKiAttack->pev->origin;
	float flHalfDuration = vecPath.Length()*0.5/vecVelocity.Length();
	pKiAttack->m_vecAcceleration = (1.0/flHalfDuration) * pKiAttack->m_vecSideVelocity;

	pKiAttack->SetThink(&CKiAttack::CurveThink);
	pKiAttack->pev->nextthink = gpGlobals->time;
	vecVelocity = vecVelocity.Normalize()*flForwardSpeed;
	pKiAttack->pev->velocity = vecVelocity + pKiAttack->m_vecSideVelocity;
	pKiAttack->m_flStartTime = gpGlobals->time;
	pKiAttack->m_vecForwardVelocity = vecVelocity;
	return pKiAttack;
}

void CKiAttack::CurveThink()
{
	float flTime = gpGlobals->time-m_flStartTime;
	Vector vecVelocity = pev->velocity = m_vecForwardVelocity + m_vecSideVelocity - m_vecAcceleration * flTime;
	//pev->velocity = vecVelocity.Normalize() * m_flSpeed;
	NormalThink();
	pev->nextthink = gpGlobals->time + 0.05;
}
