// ==============================================================================
// Code based on following tutorial :
// http://planethalflife.com/botman/balfourroad/scientist.shtml
// ==============================================================================

// silly reminder:
// remember to search the whole project for "IAN" to remember what else i changed
//
#include	"../bot/gen_bot_header.h"
#include	"monsters.h"
#include	"talkmonster.h"
#include	"schedule.h"
#include	"defaultai.h"
#include	"scripted.h"
#include	"weapons.h"
#include	"nodes.h"
#include    "animation.h"
#include	"../engine/studio.h"
#include	"classes.h"
#include	"playermelee.h"
#include	"buddy_aura.h"
#include	"buddy.h"
#include	"buddy_classes.h"
#include	"buddy_weapons.h"
#include	"hltv.h"
#include	"candy.h"
#include	"decals.h"
#include	"buddy_tasks.h"

//#define DEBUG_BUDDY

extern CBotManager BotManager;
extern int gmsgAfterFX;
extern Vector g_vecAuraColor[];
extern int gmsgPowerupFX;
extern int gmsgStopPowerupFX;
extern int gmsgVGUIMenu;
extern cvar_t nomeleehittime;
extern cvar_t ceasefire;
extern cvar_t friendlyfire;
extern int gmsgScoreInfo;
extern short g_sRockGibs;

// ADDED BY IAN:: msgs in relation to your buddy
//
// this will include
//  BuddyHealth
//  BuddyKI
//
// if the values are negative then your buddy died (this way we only use 1 msg)
extern int gmsgBuddyUpdate;
extern int gmsgBuddyStatus;
// ADDED BY IAN:: msgs in relation to your buddy

#define	BUDDY_POWERUP_KI_INCREASE_RATE		 70
#define	KI_INCREASE_RATE				  3.5
#define KI_UPDATE_TIME					  0.20

LINK_ENTITY_TO_CLASS(player_buddy, CBuddy);

// ==================================================================
// Code of CBuddy - Begin
// ==================================================================

void CBuddy::Spawn(void)
{
	if (FNullEnt(pev->owner))
	{
		UTIL_Remove(this);
		return;
	}

	Precache();

	pev->movetype = MOVETYPE_STEP;
	pev->solid = SOLID_BBOX;
	pev->takedamage = DAMAGE_YES;
	pev->flags |= FL_MONSTER;
	pev->gravity = 1.0;
    
	m_pOwner = CBaseEntity::Instance(pev->owner);
	if (m_pOwner == NULL)
	{
		UTIL_Remove(this);
		return;
	}

	// UNDONE
	// IAN:: this should come from the owner
	//
	// do it now while we have access to pev->owner -- later it is set to NULL
	pev->playerclass = BC_GULDO;

	// Give the bud the right class
	switch(pev->playerclass)
	{
	case 1:
		m_pClass = new CGuldo(this);
		break;
	default:
		ALERT(at_console, "Error : Wrong buddy class!\n");
		break;
	}

	CBasePlayer *pBuddyOwner = (CBasePlayer *)CBaseEntity::Instance(pev->owner);

	// Set the right model for the buddy
	m_pClass->SetModel();

	SetCollisionBox();
	
	m_iMaxSpeed = m_pClass->GetStartSpeed();
	pev->health = m_pClass->GetStartHealth();
	m_fKi = m_pClass->GetMaxKi();
	m_fDesiredKi = (float(m_fKi) * 0.75f);
	m_iMaxPowerLevel = m_iPowerLevel = m_pClass->GetStartPowerLevel();

	m_fLastKi = m_fKi;
	m_fLastHealth = pev->health;

	m_Owner_Ent_Index = ENTINDEX(pev->owner);
	m_Buddy_Ent_Index = this->entindex();

	// send the max info so we can draw the bar
	MESSAGE_BEGIN( MSG_ONE, gmsgBuddyStatus, NULL, pev->owner); 
		WRITE_SHORT(1);

		WRITE_LONG(m_Owner_Ent_Index);
		WRITE_LONG(m_Buddy_Ent_Index);

		WRITE_LONG(pev->health);
		WRITE_LONG(m_fKi);
	MESSAGE_END();

	// let our owner know our current values
	SendUpdateMsg();

	// IAN:: this really should come from owner as well seeing that the buddy will only do what his master does
	//
	// update:: they may now be autonomous fighting monsters -- this may need to jsut come from the buddy
	m_pClass->GiveAttacks();

	m_iShouldTurbo = 0;

	// Set fields common to all monsters
	pev->effects		= 0;
	pev->takedamage		= DAMAGE_AIM;
	pev->ideal_yaw		= pev->angles.y;
	pev->max_health		= pev->health;
	pev->deadflag		= DEAD_NO;
	m_IdealMonsterState	= MONSTERSTATE_IDLE;// Assume monster will be idle, until proven otherwise

	m_IdealActivity = ACT_IDLE;
	m_eAnimationTrigger = PLAYER_NULL;

	SetBits (pev->flags, FL_MONSTER);
	
	ClearSchedule();
	RouteClear();
	InitBoneControllers( );

	m_iHintNode			= NO_NODE;
	m_afMemory			= MEMORY_CLEAR;
	m_hEnemy			= NULL;

	m_flDistTooFar		= 1024.0;
	m_flDistLook		= 2048.0;
	m_HackedGunPos		= Vector(0, 0, 18);

	// teleport in
	EMIT_SOUND(ENT(pev->owner), CHAN_AUTO, "weapons/teleport.wav", 1, ATTN_NORM);

	pev->owner = NULL; // Reset, so the owner collides with his buddies

	// set eye position
	SetEyePosition();

    SetActivity(ACT_IDLE);
	SetSequenceBox();
	SetThink(&CBuddy::BuddyThink);
	pev->nextthink = gpGlobals->time + 0.1f;

	// Init all vars...
	m_fMoveSpeed = m_fSideSpeed = 0.0f;
	m_bFlying = false;
	m_bBlock = false;
	m_bSwat = false;
	m_bStrangled = false;
	m_bPowerUp = false;
	m_bChargingPrimary = m_bChargingSecondary = false;
	m_bTurbo = false;
	m_bBecomeCandy = false;
	m_bThrown = false;
	m_eAnimationTrigger = PLAYER_NULL;
	m_fIdleTime = 0.0f;
	m_eAnimationFlags = ANIM_NONE;
	m_pAura = NULL;
	m_vIdealLookAngles = WrapAngles(pev->v_angle);
	m_fCheckUpDownTime = 0.0f;
	m_fDontAvoidWallTime = 0.0f;
	m_fLookDelay = 0.0f;
	m_fAvoidFriendsDelay = 0.0f;
	m_fTurboTime = 0.0f;
	m_fSlowDown = 1.0f;
	m_fKiUpdateTime = 0.0f;
	m_fShootDelay = 0.0f;
	m_szAnimExtention[0] = 0;
	for(int i = 0; i < 2; i++)
	{	m_vecBlob[i] = Vector(0, 0, 0);
		m_flBlobTime[i] = -1;
	}
	m_Swoop.bActive = m_Swoop.bChainSwoop = false;
	m_Swoop.iSwoopDir = FRONT; // Assign a default value...
	m_Swoop.flStartTime = m_Swoop.flStartSwoopDelay = 0.0f;
	m_vVelocityDir = g_vecZero;
	m_fVelocityTime = m_fVelocitySpeed = 0.0f;
	m_fCheckNameDelay = 0.0f;
	m_pCurrentWeapon = NULL;
	m_fDeathTime = 0.0f;

	m_vlastOrigin.x = pev->origin.x;
	m_vlastOrigin.y = pev->origin.y;
	m_vlastOrigin.z = pev->origin.z;

	m_Melee.SetOwner(this);
	m_Melee.Init();
	m_Toss.SetOwner(this);
	m_Toss.Init();

	MESSAGE_BEGIN( MSG_PVS, gmsgAfterFX, GetGunPosition());
				WRITE_BYTE(entindex());
				WRITE_COORD(GetGunPosition().x);
				WRITE_COORD(GetGunPosition().y);
				WRITE_COORD(GetGunPosition().z);
	MESSAGE_END();

}

void CBuddy::SendUpdateMsg()
{
	// send the initial values
	MESSAGE_BEGIN( MSG_ONE, gmsgBuddyUpdate, NULL, m_pOwner->pev); 
		WRITE_LONG(pev->health);
		WRITE_LONG(m_fKi);
	MESSAGE_END();
}

void CBuddy::Destroy()
{
	// let our owner know we're dead and that if he wants, he can call a new buddy later!
	CBasePlayer *owner;
	owner = (CBasePlayer*) CBasePlayer::Instance(m_pOwner->pev);
	if (owner)
	{
		owner->m_hBuddy = NULL;
	}

	StopTurbo();
	StopPowerUp();

	// Delete all weapons
	while(m_WeaponList.Empty() == false)
		delete m_WeaponList.Pop();

	SetTouch(NULL);

	// send the initial values
	MESSAGE_BEGIN( MSG_ONE, gmsgBuddyUpdate, NULL, m_pOwner->pev); 
		WRITE_LONG(-1);
		WRITE_LONG(-1);
	MESSAGE_END();

	UTIL_Remove(this);
}
void CBuddy::Precache(void)
{
	PRECACHE_MODEL("models/player/guldo/guldo.mdl");

    // every new scientist must call this, otherwise
	// when a level is loaded, nobody will talk (time is reset to 0)
    TalkInit();
}

int CBuddy::Classify(void)
{
	return  CLASS_BUDDY;
}

void CBuddy::SetActivity(int act)
{
	int sequence = LookupActivity( act ); 
    if ( sequence != ACTIVITY_NOT_AVAILABLE )
    {
		pev->sequence = sequence;
		m_Activity = act; 
		pev->frame = 0;
		ResetSequenceInfo( );
		//m_flFrameRate = 1.0;
	}
}

void CBuddy::SetCollisionBox()
{
	//UTIL_SetSize(pev,Vector(-16,-16,-18),Vector(16,16,18));
	//UTIL_SetSize(pev,Vector(-8,-8,-9),Vector(8,8,9));
	//UTIL_SetSize(pev,Vector(-8,-8,-2),Vector(8,8,34));
	//UTIL_SetSize(pev,Vector(-8,-8,16),Vector(8,8,52));
	//UTIL_SetSize(pev,Vector(-8,-8,-22),Vector(8,8,14));
	UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
	
	//UTIL_SetSize(pev,Vector(-8,-8,-18),Vector(8,8,18));
}

// makes sure buddy isn't doing something which would stop them from following or fighting
//
// (renamed from checknecessarystuff for readability)
bool CBuddy::IsBusy()
{
	bool DoSecondaryChecks = true;

	// Primary checks(most important)

//	if (m_bThrown)
//		m_Toss.HandleToss();

	if (m_Toss.Think())
		DoSecondaryChecks = false;

	// Bud is becoming a candy?
	if (m_bBecomeCandy)
	{
		// Don't do a shit
		SetMoveSpeed(0.0f);
		//return true;
		DoSecondaryChecks = false;
	}
	// Velocity Applied to us?
	if (m_fVelocityTime >= gpGlobals->time)
	{
		SetMoveSpeed(0.0f);
		//return true;
		DoSecondaryChecks = false;
	}

	// Secondary Checks
	if (DoSecondaryChecks)
	{
		/*if (m_bPowerUp)
		{
			if (m_fKi >= m_fDesiredKi)
				StopPowerUp();
			else
			{
				SetMoveSpeed(0.0f);
				return true;
			}
		}*/
		if (m_Melee.m_fLastPowerHitTime >= (gpGlobals->time - 1.0f))
		{
			// So the buddy won't move right after he kicked someone(looks unnatural otherwise)
			SetMoveSpeed(0.0f);
			return true;
		}
		// Do we need to powerup?
		
		// do we need to maintain ki for our buddy? makes more sense that if we have enough ki then our buddy should have enough
		/*else if ((m_fKi < m_fDesiredKi) && CanPowerUp())
		{
			StartPowerUp();
			SetMoveSpeed(0.0f);
			return true;
		}*/
	}
	else
		return true;

	return false;
}

void CBuddy::CheckName()
{
	char sz[32];

	strncpy(sz, STRING(m_pOwner->pev->netname), 16);
	sz[16] = 0;
	strcat(sz, "'s Buddy");

	if (!FStrEq(m_szBuddyName, sz))
	{
		strcpy(m_szBuddyName, sz);
		pev->netname = MAKE_STRING(m_szBuddyName);
	}

	m_fCheckNameDelay = gpGlobals->time + 0.75f;
}

void CBuddy::PreThink()
{
	if (m_fCheckNameDelay < gpGlobals->time)
		CheckName();

	pev->team = m_pOwner->pev->team; // Keep the same

	// Turbo time over?
	if ((m_fTurboTime < gpGlobals->time) && (m_fTurboTime > 0.0f))
		StopTurbo();

	//PrintAI("m_fControl == " << m_fControl << "\n");

	/* removed by harSens: doesn't compile
	PrintAI("Gait anim : " << UTIL_GetAnimName(this, pev->gaitsequence) << "\n");
	PrintAI("Anim : " << UTIL_GetAnimName(this, pev->sequence) << "\n");
	*/
	//PrintAI("Health : " << pev->health << "\n");

	//HACK HACK: find exploding attacks to block, fix for HL's crappy collision detection
	//if (!m_bShield)
	{	CBaseEntity *pEntity = UTIL_FindEntityInSphere( NULL, pev->origin, 30 );
		while (pEntity!=NULL)
		{
			if (pEntity->Classify()==CLASS_EXPLODINGATTACK)
			{
				CExplodingAttack *pExp = ((CExplodingAttack*)pEntity);
				if (pExp->pev->solid!=SOLID_NOT)
				{
					pExp->Touch(this);
				}
			}
			pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, 30 );
		}	
	}

	// if you want to see the hitbox for the buddy uncomment this
	/*
	CBasePlayer *pHost = BotManager.GetHost();

    UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y, pev->absmin.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y, pev->absmin.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y, pev->absmin.z), Vector (pev->absmin.x, pev->absmin.y + pev->size.y, pev->absmin.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y, pev->absmin.z), Vector (pev->absmin.x, pev->absmin.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x + pev->size.x, pev->absmin.y + pev->size.y, pev->absmin.z + pev->size.z), Vector (pev->absmin.x, pev->absmin.y + pev->size.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x + pev->size.x, pev->absmin.y + pev->size.y, pev->absmin.z + pev->size.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x + pev->size.x, pev->absmin.y + pev->size.y, pev->absmin.z + pev->size.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y + pev->size.y, pev->absmin.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x + pev->size.x, pev->absmin.y, pev->absmin.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y + pev->size.y, pev->absmin.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x + pev->size.x, pev->absmin.y, pev->absmin.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y + pev->size.y, pev->absmin.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y + pev->size.y, pev->absmin.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y + pev->size.y, pev->absmin.z), Vector (pev->absmin.x, pev->absmin.y + pev->size.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y, pev->absmin.z + pev->size.z), Vector (pev->absmin.x + pev->size.x, pev->absmin.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);
	UTIL_DrawBeam (pHost, Vector (pev->absmin.x, pev->absmin.y, pev->absmin.z + pev->size.z), Vector (pev->absmin.x, pev->absmin.y + pev->size.y, pev->absmin.z + pev->size.z), 10, 1, 255, 0, 0, 250, 10, 1);*/
}

void CBuddy::DeathThink()
{

	if (m_pCurrentTask)
	{
		m_pCurrentTask->CleanUp(this);

		delete m_pCurrentTask;
	}
	//	if (m_fDeathTime < (gpGlobals->time - 15.0f))
//	{
		Destroy();
		return;
//	}

	pev->effects |= EF_NODRAW;
	pev->solid = SOLID_NOT;
}

void CBuddy::AimWithOwner()
{
	//calculate acceleration
	TraceResult tr;
	
	UTIL_MakeVectors(m_pOwner->pev->v_angle);
	Vector vecEnd = m_pOwner->pev->origin + gpGlobals->v_forward * m_flDistLook;

	// make sure we are aiming at the same pace as our owner!
	// so if his traceline hits something we need to aim at that too
	UTIL_TraceLine(m_pOwner->pev->origin,vecEnd,dont_ignore_monsters,edict(),&tr);	


	// make sure we are rotated to look with our owner
	pev->angles = m_pOwner->pev->angles;

	if (tr.flFraction)
	{			
		this->AimToVec(tr.vecEndPos);
		m_vAimVec.x = m_vAimVec.x * -1;
		pev->v_angle = m_vAimVec;

		// check to see if its an enemy, if it is we are going to store it
		CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
		if (pEntity)
		{
			if (pEntity->IsPlayer())
//				PrintAI(pEntity->pev->team);
				
			if (((pEntity->IsPlayer()) || (pEntity->Classify() == CLASS_BUDDY)) &&
				((!g_pGameRules->IsTeamplay()) || (pev->team == pEntity->pev->team)))
			{
				
				if ((m_pCurrentWeapon) && (m_pCurrentWeapon->CanSetEnemy(pEntity)))
					m_pEnemy = CBaseEntity::Instance(pEntity->pev->owner);
				else
					m_pEnemy = NULL;
			}
		}		
	}	
	else
	{
		pev->v_angle = m_pOwner->pev->v_angle;	
	}
}

void CBuddy::LookAndAim()
{

	//calculate acceleration
	TraceResult tr;
	
	UTIL_MakeVectors(m_pOwner->pev->v_angle);
	Vector vecEnd = m_pOwner->pev->origin + gpGlobals->v_forward * m_flDistLook;

	// make sure we are aiming at the same pace as our owner!
	// so if his traceline hits something we need to aim at that too
	UTIL_TraceLine(m_pOwner->pev->origin,vecEnd,dont_ignore_monsters,edict(),&tr);	


	// make sure we are rotated to look with our owner
	pev->angles = m_pOwner->pev->angles;

	if (tr.flFraction)
	{			
		this->AimToVec(tr.vecEndPos);
		m_vAimVec.x = m_vAimVec.x * -1;
		pev->v_angle = m_vAimVec;

		// check to see if its an enemy, if it is we are going to store it
		CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
		if (pEntity)
		{
			if (pEntity->IsPlayer())
//				PrintAI(pEntity->pev->team);
				
			if (((pEntity->IsPlayer()) || (pEntity->Classify() == CLASS_BUDDY)) &&
				((!g_pGameRules->IsTeamplay()) || (pev->team == pEntity->pev->team)))
			{
				
				if ((m_pCurrentWeapon) && (m_pCurrentWeapon->CanSetEnemy(pEntity)))
					m_pEnemy = CBaseEntity::Instance(pEntity->pev->owner);
				else
					m_pEnemy = NULL;
			}
		}		
	}	
	else
	{
		pev->v_angle = m_pOwner->pev->v_angle;	
	}

	//UTIL_MakeVectors(pev->v_angle);
	//UTIL_DrawBeam (BotManager.GetHost(), pev->origin, pev->origin + gpGlobals->v_forward * 4096, 10, 1, 0, 255, 0, 250, 10, 1);
	//pev->v_angle = m_pOwner->pev->v_angle;	

}

void CBuddy::BuddyThink()
{
	if (m_pOwner == NULL)
	{
		//Destroy();
		Die(NULL, NULL);
		return;
	}

    if(!IsInWorld())
    {
		if (!IsInWorld())
			ALERT(at_console, "Buddy is out of level!\n");
		// Remove buddy if he's dead or out of the level
		//Destroy();
		Die(NULL,NULL);
		return;
	}
	
	if (!IsAlive())
	{
		Die(NULL, NULL);
		return;
	}

	PreThink();

	pev->nextthink = gpGlobals->time + 0.016;
	float flInterval = StudioFrameAdvance();
    
	DispatchAnimEvents(flInterval);

	// m_pOwner is a EHANDLE, thats why we have to use this code to 'get the CBasePlayer *
	// from it'
	CBaseEntity *pTemp = m_pOwner;
	CBasePlayer *pBuddyOwner = (CBasePlayer *)pTemp;
	
	// handle external forces
	VelocityThink();

	if ((pBuddyOwner->IsAlive() == false) || pBuddyOwner->IsSpectator() ||
		pBuddyOwner->m_fObserverFlag)
    {
		// Remove buddy if owner is dead or spectating
		//Destroy();
		Die(NULL,NULL);
		return;
	}
	
	// deal with ki
	if (gpGlobals->time - m_fKiUpdateTime > KI_UPDATE_TIME)
	{
		//m_fKi += KI_INCREASE_RATE;
			
		if (m_bPowerUp)
		{			
			m_fKi += BUDDY_POWERUP_KI_INCREASE_RATE;
		}			
		
		if  (m_fKi > m_pClass->GetMaxKi())
		{
			m_fKi = m_pClass->GetMaxKi();
		}

		m_fKiUpdateTime = gpGlobals->time;
	}
	
	// Check for necessary stuff(ie powerup)
	if (IsBusy())
		; // Needed stuff was executed in IsBusy()
	// look for an enemy...
	else
	{				
		if (this->m_pCurrentTask == NULL)
			this->m_pCurrentTask = new CBuddyTask_Follow();
		else
		{
			CBuddyTask *newTask;
			if (m_pCurrentTask->GetTaskState() == tsComplete)
			{
				delete m_pCurrentTask;
				m_pCurrentTask = new CBuddyTask_Follow();
			}
			
			m_pCurrentTask->SenseThreat(this);

			newTask = m_pCurrentTask->ManageThreat(this);

			if ((newTask) && (m_pCurrentTask->GetTaskState() != tsCantSwitch))
			{
				delete m_pCurrentTask;
				m_pCurrentTask = newTask;
			}
			else
			{
				newTask = m_pCurrentTask->TaskThink(this);

				if (newTask)
				{
					delete m_pCurrentTask;
					m_pCurrentTask = newTask;

					if (newTask->GetTaskState() == tsRunNow)
					{
						m_pCurrentTask->TaskThink(this);
					}
				}
			}


		}
		// handle defending our owner
		//DefendOwner(pBuddyOwner);
		
		// if we're not defending should we be attacking?
		//AttackThink(pBuddyOwner);

		// set our velocity to follow our owner
		//
		// eventually maybe we should fly around our owner instead of just following him?
		//if (CanMove(pBuddyOwner))
		//{
		//	FollowOwner(); // -- removed -- do this after we set our speed		
		//	MoveBuddy(flInterval);
		//}

	}

	SetCollisionBox();	
//	MoveBuddy(flInterval);
//	LookAndAim();
	PlayAnimation(pBuddyOwner);
//	pev->angles = m_pOwner->pev->angles;

#ifdef DEBUG_BUDDY
	if (m_pCurrentTask) m_pCurrentTask->DebugPrint(this);
#endif

	if ((m_fLastKi !=  m_fKi) ||	(m_fLastHealth != pev->health))
	{
		m_fLastKi = m_fKi;
		m_fLastHealth = pev->health;
		SendUpdateMsg();
	}

	//PrintAI("Velocity : " << pev->velocity.Length() << "\n");
	//PrintAI("X : " << pev->velocity.x << " : Y : " << pev->velocity.y << " : Z : " << pev->velocity.z << "\n");
}

bool CBuddy::CanAttack(CBasePlayer* pBuddyOwner)
{	
	return true;
}

bool CBuddy::CanDefend(CBasePlayer* pBuddyOwner)
{
	return true;
}

bool CBuddy::CanMove(CBasePlayer* pBuddyOwner)
{
	// cant move if:
	//	1) we are controlling a beam
	//	2) we have an external force placed on us
	//	3) our owner is "ascending" -- buddy has just been created
	return ((!m_fControl) && (m_fVelocityTime == 0.0f) && (!pBuddyOwner->m_fAscending) && (!m_bPowerUp));
}

bool CBuddy::DefendOwner(CBasePlayer *pBuddyOwner)
{	
	// if we're controlling a beam or charing something we are not going to defend anyone, thats the way it has to be
	if ((!m_fCharging) && (!m_fControl)) return false;
	
	CBaseEntity *pAttack = NULL;
	CBasePlayer *pPlayer = NULL;

	//if (flSearchForAttackTime > bleh) and (!enemy)
	{
		while ((pAttack = UTIL_FindEntityByString(pAttack, "globalname", "magic_attack")) != NULL)
		{
			if (IsNull(pAttack->pev->owner))
				continue; // Since we want some info about the owner aswell...

			if (pAttack->pev->owner == pBuddyOwner->edict())
				continue;

			if (g_teamplay)
			{
				if (pAttack->pev->owner->v.team == pev->team)
				{
					continue;
				}
			}

			if (pAttack->pev->velocity.Length() < 10.0f)
				continue; // Attack is not scary
		}
	}

	return false;
}

bool CBuddy::AttackThink(CBasePlayer *pBuddyOwner)
{
	CBaseBuddyWeapon *pAttack;
	
	// our attack is based on what our owner is doing -- this should come from the owner class or
	// maybe the buddy class?
	//
	//  ** NEED TO MAKE SURE THEY CAN CHANGE WEAPON **
	//
	//  buddies do not do melee!!! (from team meeting)
	if ((!m_fCharging) && (!m_fControl)) // can't switch weapons if we're charging silly!
	{
		if (pBuddyOwner)
		{
			if ( (pBuddyOwner->m_pActiveItem) )
			{
				switch (pBuddyOwner->m_pActiveItem->m_iId)
				{
				case WEAPON_GENERICBEAM:
					pAttack = GetWeapon("genericbeam");
					break;
				default:
					pAttack = GetWeapon("kiblast");
				}
			}
			else
				pAttack = GetWeapon("kiblast");
		}
		// no weapon no fighting!	
		if (!pAttack) return false;

		SetCurrentWeapon(pAttack);
	}

	// now we need to aim -- should the weapon aim and control how the aiming is done?
	LookAndAim();
	
	if (m_pCurrentWeapon)
		m_pCurrentWeapon->WeaponThink(pBuddyOwner);

	// Fly when attacking
	if (!m_bFlying)
		StartFly();

	return true;
}

void CBuddy::MoveBuddy(float flInterval)
{
	if ((m_fMoveSpeed == 0.0f) && (m_fSideSpeed == 0.0f))
	{
		if (m_fVelocityTime < gpGlobals->time)
			pev->velocity = g_vecZero;
		return;
	}

	//UTIL_MakeVectors(GetMoveDirection());
	//pev->velocity = gpGlobals->v_forward * fabs(m_fMoveSpeed);
	pev->velocity = GetMoveDirection().Normalize() * fabs(m_fMoveSpeed);

	//Vector v_goal = pev->origin + gpGlobals->v_forward * fabs(m_fMoveSpeed);
    //UTIL_DrawBeam(BotManager.GetHost(),pev->origin,pev->origin+pev->velocity,10,1,255,0,0,250,10);
	//UTIL_DrawBeam(BotManager.GetHost(), pev->origin, v_goal, 10, 1, 255, 0, 0, 250, 10);
	//WALK_MOVE( ENT(pev), pev->angles.y, m_fMoveSpeed * flInterval, WALKMOVE_NORMAL );
	//UTIL_MoveToOrigin( edict(), v_goal, fabs(m_fMoveSpeed) * flInterval, MOVE_STRAFE );
}

CBaseEntity *CBuddy::GetEnemy()
{
	// UNDONE

	CBasePlayer *pPlayer;
	CBaseEntity *pEnt = NULL;
	CBaseEntity *pBestEnemy = NULL;
	float flDist, flNearestDist = 9999.0f;

	// First look for a normal player enemy
	for(byte b=1;b<=gpGlobals->maxClients;b++)
	{
		pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(b);

		if (!UTIL_IsValidPlayer(pPlayer, true, false))
			continue;

		// Skip friends and owner
		if (((pPlayer->pev->team == pev->team) && g_teamplay) || (pPlayer == m_pOwner))
			continue;

		flDist = UTIL_Distance(pev->origin, pPlayer->pev->origin);
		if (flDist >= flNearestDist)
			continue;

		if (!FInViewCone(pPlayer) || !FVisible(pPlayer))
			continue;

		pBestEnemy = pPlayer;
	}

	// Now search for enemy buddies
	while((pEnt = UTIL_FindEntityByClassname(pEnt, "player_buddy")) != NULL)
	{
		if (pEnt == this)
			continue;

		if (IsNull(pEnt))
			continue;

		if (!pEnt->IsAlive())
			continue;

		// Skip friends
		if ((pEnt->pev->team == pev->team) && g_teamplay)
			continue;

		flDist = UTIL_Distance(pev->origin, pEnt->pev->origin);
		if (flDist >= flNearestDist)
			continue;

		if (!FInViewCone(pEnt) || !FVisible(pEnt))
			continue;

		pBestEnemy = pEnt;
	}

	return pBestEnemy;
}

void CBuddy::WeaponIdle()
{
	if (m_pCurrentWeapon)
		m_pCurrentWeapon->WeaponIdle();
}

void CBuddy::ShootEnemy(CBasePlayer *pBuddyOwner)
{
	//if (m_hEnemy == NULL)
	//	return;

	// Aim at our enemy
	/*if (m_fControl) // if we're shooting a beam just aim and get out of here!
	{
		// here we shoudl consider if we also want to explode the current beam
		
		LookAndAim();
		return;
	}*/

	// Take a desired attack and shoot at the enemy
	// UNDONE
	//CBasePlayer *pBuddyOwner = (CBasePlayer *)CBaseEntity::Instance(pev->owner);
	//CBasePlayerWeapon *pWeapon = (CBasePlayerWeapon*)pBuddyOwner->m_pActiveItem;
	CBaseBuddyWeapon *pAttack;
	
	// our attack is based on what our owner is doing -- this should come from the owner class or
	// maybe the buddy class?
	//
	//  ** NEED TO MAKE SURE THEY CAN CHANGE WEAPON **
	//
	//  buddies do not do melee!!! (from team meeting)
	if ((!m_fCharging) && (!m_fControl)) // can't switch weapons if we're charging silly!
	{
		if (pBuddyOwner)
		{
			if ( (pBuddyOwner->m_pActiveItem) )
			{
				switch (pBuddyOwner->m_pActiveItem->m_iId)
				{
				case WEAPON_GENERICBEAM:
					pAttack = GetWeapon("genericbeam");
					break;
				default:
					pAttack = GetWeapon("kiblast");
				}
			}
			else
				pAttack = GetWeapon("kiblast");
		}
		// no weapon no fighting!	
		if (!pAttack) return;

		SetCurrentWeapon(pAttack);
	}

	// now we need to aim -- should the weapon aim and control how the aiming is done?
	LookAndAim();
	
	m_pCurrentWeapon->WeaponThink(pBuddyOwner);

	// Fly when attacking
	if (!m_bFlying)
		StartFly();
}

void CBuddy::StartFly()
{
	m_bFlying = true;
	pev->gravity = 0.0000001;
	pev->movetype = MOVETYPE_FLY;
	pev->flags |= FL_FLY;
}

void CBuddy::StopFly()
{
	m_bFlying = false;
	pev->gravity = 1;
	pev->movetype = MOVETYPE_STEP;
	pev->flags &= ~FL_FLY;
}

int CBuddy::PlayFullBodyAnimation(CBasePlayer* pBuddyOwner)
{	m_IdealActivity = ACT_RESET;
	ANIM_FLAGS eIdealAnimFlags = ANIM_NONE;
	int animDesired = 0;
	char szAnim[64];
	//choose full body animation

	//wrapped in a bodypart
	if(m_eAnimationTrigger == PLAYER_WRAP)
	{	m_IdealActivity = ACT_WRAP;
		animDesired = LookupSequence("wrap");
	}
	else if (m_eAnimationTrigger != PLAYER_NULL)
	{	//all other ani triggers go on the upper body
		return false;
	}
	/*
	// Added by Pcjoe
	// Advanced Melee Animations
	else if ( m_Melee.CanPlayAnimation() )
	{	int iAnim;
		// Get animation
		plrmelee_anim_s *pAnim = m_Melee.GetCurrentAnim();
		iAnim = LookupSequence( pAnim->szAnim[pAnim->iCurrentAnim] );
		if(iAnim == -1)
			iAnim = 0;

		// Init animation ( Reset frames )
		if(!pAnim->fInit )
		{	pev->frame = 0.001;	// HACK HACK: Setting to a incredibly low non-zero value so we wont 'skip' animations accidently
			m_fSequenceFinished = false;
			pAnim->fStartOnGround = FBitSet(pev->flags,FL_ONGROUND);
			pAnim->fInit = true;
		}

		// Set animation
		m_IdealActivity = pAnim->IdealActivity[pAnim->iCurrentAnim];
		animDesired = iAnim;	
		eIdealAnimFlags = ANIM_REF;

		if((iAnim == pev->sequence && m_fSequenceFinished) || pev->frame == 255)
		{	// First check if it's combo execution
			if(pAnim->fExecution)
			{	// switch to next combo in list
				m_Melee.NextExeMove( );
			}
			// Go to next anim
			else if((pAnim->iCurrentAnim+1) < pAnim->iNumAnims)
			{	pAnim->iCurrentAnim++;
			}
			else
			{	// Sequence is finished, we arn't looping, delete it
				if(!pAnim->fLooping && !m_Melee.InAdvMeleeBattle())
				{	// Unset fixed angles
					m_Melee.RemoveAnim(pAnim);
				}
			}
		}
	}
	*/
	// If Ascending + not controlling a beam, play the FX
	else if ( pBuddyOwner->m_fAscending && !pBuddyOwner->m_fControl )
	{
		eIdealAnimFlags = ANIM_REF;

		//float flAscendTime = m_flAscendTime - gpGlobals->time;

		switch(pBuddyOwner->m_iTransformStatus)
		{
		case TRANS_SHORT:
			{
				strcpy( szAnim, "change_finish" );
				m_IdealActivity = ACT_TRANS_FINISH;
				break;
			}
		case TRANS_NORM:
			{
				if(pBuddyOwner->m_flAscendTime <= 1)
				{
					strcpy( szAnim, "change_finish" );
					m_IdealActivity = ACT_TRANS_FINISH;
				}
				else
				{
					strcpy( szAnim, "change_normal" );
					m_IdealActivity = ACT_TRANS_NORMAL;
				}
				break;
			}
		case TRANS_LONG:
			{
				if(pBuddyOwner->m_flAscendTime <= 1)
				{
					strcpy( szAnim, "change_finish" );
					m_IdealActivity = ACT_TRANS_FINISH;
				}
				else
				{
					strcpy( szAnim, "change_long" );
					m_IdealActivity = ACT_TRANS_LONG;
				}
				break;
			}
		default:
			{
				strcpy( szAnim, "change_normal" );
				m_IdealActivity = ACT_TRANS_NORMAL;
				break;
			}
		}
		animDesired = LookupSequence( szAnim );
	}
	/*
	// Added by Pcjoe
	else if(m_fMelee)
	{	// Swoop Animation
		if(m_Swoop.m_fSwoop)
		{	m_Swoop.SetSwoopAnim( szAnim, m_IdealActivity );
		}
		// Throw animation
		else if( m_Melee.m_fThrown )
		{	m_Melee.SetThrowAnim( szAnim, m_IdealActivity );
		}
		
		// Set animation
		eIdealAnimFlags = ANIM_REF;
		animDesired = LookupSequence( szAnim );
	}
	// end of add by Pcjoe
	//Control attacks*/
	else if(m_fControl)
	{//	m_IdealActivity = ACT_CONTROL;
		// Edited by Pcjoe, add in support for beam jump anims
		if(pev->movetype == MOVETYPE_RIDEBEAM)
		{	m_IdealActivity = ACT_BEAMJUMP;
			strcpy( szAnim, "ref_beam_push" );
		}
		else
		{	m_IdealActivity = ACT_CONTROL;
			strcpy( szAnim, "ref_control_" );
			strcat( szAnim, m_szAnimExtention );
		}
		eIdealAnimFlags = ANIM_REF;
		animDesired = LookupSequence( szAnim );		
	}
	
	//charge attacks secondary mode (when not able to walk with them
	else if(m_bChargingSecondary && m_flSlowDown == 0.0)
	{	m_IdealActivity = ACT_CHARGE;
		strcpy( szAnim, "ref_charge2_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;
		animDesired = LookupSequence( szAnim );
	}
	//charge attacks (when not able to walk with them
	else if(m_bChargingPrimary && m_flSlowDown == 0.0)
	{	m_IdealActivity = ACT_CHARGE;
		strcpy( szAnim, "ref_charge_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;
		animDesired = LookupSequence( szAnim );
	}
	
	// Swoop Animation
	/*else if(m_Swoop.bActive)
	{
		SetSwoopAnim( szAnim, m_IdealActivity );
		animDesired = LookupSequence(szAnim);
	}
	// Power hit animation
	else if (m_Melee.m_fLastPowerHitTime >= (gpGlobals->time - 1.0f))
	{
		int iAnim;
		// Get animation
		iAnim = LookupSequence( m_Melee.m_szPowerHitAnim );
		if(iAnim == -1)
			iAnim = 0;

		// Init animation ( Reset frames )
		if(m_Melee.m_bInitPowerHitAnim)
		{	pev->frame = 0.001;	// HACK HACK: Setting to a incredibly low non-zero value so we wont 'skip' animations accidently
//			m_fSequenceFinished = false;
//			pAnim->fStartOnGround = FBitSet(pev->flags,FL_ONGROUND);
			m_Melee.m_bInitPowerHitAnim = false;
		}

		// Set animation
		//m_IdealActivity = pAnim->IdealActivity[pAnim->iCurrentAnim];
		m_IdealActivity = ACT_MELEE_ONE;
		animDesired = iAnim;	
		eIdealAnimFlags = ANIM_REF;

		if((iAnim == pev->sequence && m_fSequenceFinished) || pev->frame == 255)
		{	// First check if it's combo execution
			//if(pAnim->fExecution)
			//{	// switch to next combo in list
			//	m_Melee.NextExeMove( );
			//}
			// Go to next anim
			//else if((pAnim->iCurrentAnim+1) < pAnim->iNumAnims)
			//{	pAnim->iCurrentAnim++;
			//}
			//else
			//{	// Sequence is finished, we arn't looping, delete it
			//	if(!pAnim->fLooping && !m_Melee.InAdvMeleeBattle())
			//	{	// Unset fixed angles
			//		m_Melee.RemoveAnim(pAnim);
			//	}
			//}

			m_Melee.m_bInitPowerHitAnim = false;
			m_Melee.m_fLastPowerHitTime = 0.0f;
		}
	}*/
	// Toss animations
	else if (m_bThrown)
	{
		//m_Melee.SetThrowAnim(m_IdealActivity, animDesired);
		m_Toss.SetTossAnim(szAnim, m_IdealActivity);
		animDesired = LookupSequence( szAnim );
	}
	//blocking
	else if(m_bBlock && !m_bSwat)
	{	m_IdealActivity = ACT_BLOCK;
		if (m_bFlying)
		{	strcpy( szAnim, "fly_block" );
			eIdealAnimFlags = ANIM_FLY;
		}
		else
		{	strcpy( szAnim, "ref_block" );
			eIdealAnimFlags = ANIM_REF;
		}
		animDesired = LookupSequence( szAnim );
	}
	//strangled
	else if (m_bStrangled)
	{	/*
		if (m_fPowerUp)
		{	
		}
		else
		*/
		{	animDesired = LookupSequence("struggle");
			m_IdealActivity = ACT_STRUGGLE;
		}
	}
	
	//powering up
	else if (m_bPowerUp)
	{	if ( (m_Activity == ACT_POWERUP && m_fSequenceFinished) || m_Activity == ACT_POWERUPCHARGE)
		{	m_IdealActivity = ACT_POWERUPCHARGE;		
			//select right ani
			if (m_bFlying)
			{	animDesired = LookupSequence ("fly_powerup_charge");
				eIdealAnimFlags = ANIM_FLY;
			}
			else
			{	animDesired = LookupSequence ("ref_powerup_charge");
				eIdealAnimFlags = ANIM_REF;
			}
		}
		else
		{	m_IdealActivity = ACT_POWERUP;
			//select right ani
			if (m_bFlying)
			{	animDesired = LookupSequence ("fly_powerup");
				eIdealAnimFlags = ANIM_FLY;
			}
			else
			{	animDesired = LookupSequence ("ref_powerup");
				eIdealAnimFlags = ANIM_REF;
			}
		}		
	}

	//play the animation
	if (m_IdealActivity )
	{	// Pcjoe: Reset animation trigger
		if(m_eAnimationTrigger != PLAYER_NULL)
			m_eAnimationTrigger = PLAYER_NULL;
		int iFrame = 0;
		if ( m_Activity == m_IdealActivity )
		{	if (eIdealAnimFlags && eIdealAnimFlags==m_eAnimationFlags)
			{	return true;
			}
			// Pcjoe: Force interpolation on death
			else if(IsAlive())
			{	iFrame = pev->frame;
				pev->effects |= EF_NOINTERP;
			}
		}
		m_Activity = m_IdealActivity;
		m_eAnimationFlags = eIdealAnimFlags;
		
		if (animDesired == -1)
		{	animDesired = 0;			
		}		
		
		// Already using the desired animation?
		if (pev->sequence == animDesired)
		{	return true;
		}

		pev->gaitsequence = 0;
		pev->sequence		= animDesired;
		pev->frame			= iFrame;
		ResetSequenceInfo( );
		return true;
	}
	return false;
}

int CBuddy::GetUpperBodyAnimation(int &iFrame)
{	int animDesired = 0;
	ANIM_FLAGS eIdealAnimFlags = ANIM_NONE;
	char szAnim[64];
	float flSpeed = fabs(m_fMoveSpeed);
	
	//select an animation
	//attack 1
	if (m_eAnimationTrigger == PLAYER_ATTACK1)
	{	m_IdealActivity = ACT_RANGE_ATTACK1;
		strcpy( szAnim, "ref_shoot_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;		
		animDesired = LookupSequence( szAnim );
	}
	
	//attack 2
	else if (m_eAnimationTrigger == PLAYER_ATTACK2)
	{	m_IdealActivity = ACT_RANGE_ATTACK2;
		strcpy( szAnim, "ref_shoot2_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;		
		animDesired = LookupSequence( szAnim );
	}
	
	//charging secondary
	else if (m_bChargingSecondary)
	{	m_IdealActivity = ACT_CHARGE;
		strcpy( szAnim, "ref2_charge_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;		
		animDesired = LookupSequence( szAnim );
	}
	//charging
	else if (m_bChargingPrimary)
	{	m_IdealActivity = ACT_CHARGE;
		strcpy( szAnim, "ref_charge_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;		
		animDesired = LookupSequence( szAnim );
	}

	//swat an attack away to the left
	else if (m_eAnimationTrigger == PLAYER_SWATAWAY_LEFT)
	{	m_bSwat = true;
		m_IdealActivity = ACT_SWATAWAY_LEFT;
		strcpy( szAnim, "ref_left_swataway" );
		animDesired = LookupSequence( szAnim );
		eIdealAnimFlags = ANIM_REF;		
	}

	//swat an attack away to the right
	else if (m_eAnimationTrigger == PLAYER_SWATAWAY_RIGHT)
	{	m_bSwat = true;
		m_IdealActivity = ACT_SWATAWAY_RIGHT;
		strcpy( szAnim, "ref_right_swataway" );
		animDesired = LookupSequence( szAnim );
		eIdealAnimFlags = ANIM_REF;		
	}

	//push a heavy beam forward
	else if (m_eAnimationTrigger == PLAYER_SWATAWAY_FORWARD)
	{	m_bSwat = true;
		m_IdealActivity = ACT_SWATAWAY_FORWARD;
		strcpy( szAnim, "ref_forward_swataway" );
		animDesired = LookupSequence( szAnim );
		eIdealAnimFlags = ANIM_REF;		
	}

	//aim animation?
	else 
	{	m_IdealActivity = ACT_WALK;
		strcpy( szAnim, "ref_aim_" );
		strcat( szAnim, m_szAnimExtention );
		eIdealAnimFlags = ANIM_REF;		
				
		animDesired = LookupSequence( szAnim );
		if (animDesired == -1)
		{	animDesired = 0;
		}
		
		//no aim animation: generic walk/fly/jump/swim animation
		if (!animDesired)
		{	//Flying
			if (m_bFlying)
			{	// Edited by Pcjoe, Make the animations blend
				if(m_fMoveSpeed > 0.0f)
				{	animDesired = LookupSequence( "fly_forward" );
					m_IdealActivity = ACT_FLY_FRONT;
				}
				else if(m_fMoveSpeed < 0.0f)
				{	animDesired = LookupSequence( "fly_backward" );
					m_IdealActivity = ACT_FLY_BACK;
				}
				else
				{	animDesired = LookupSequence( "fly_idle" );
					m_IdealActivity = ACT_FLY;
				}
			}

			//Swimming
			else if ( pev->waterlevel > 1 )
			{// Edited by Pcjoe
				//	if ( flSpeed == 0 )
				if (flSpeed > GetMaxSpeed()/1.6)
				{	m_IdealActivity = ACT_SWIM;	

					// Back/front swimming animations
					if(m_fMoveSpeed < 0.0f)
						animDesired = LookupSequence("swim_backwards");
					else
						animDesired = LookupActivity(ACT_SWIM);
				}
				else
				{	m_IdealActivity = ACT_HOVER;
					animDesired = LookupActivity(ACT_HOVER);
				}
			}

			else if (!FBitSet(pev->flags,FL_ONGROUND))
			{	//falling
				if(pev->velocity.z < 0 )
				{	m_IdealActivity = ACT_FALL;
					animDesired = LookupSequence("fly_fall");
				}

				//jumping
				else if(m_eAnimationTrigger == PLAYER_JUMP)
				{	m_IdealActivity = ACT_HOP;
					animDesired = LookupActivity(ACT_HOP);
				}
				
				//backflip
				else if(m_eAnimationTrigger == PLAYER_BACKFLIP)
				{	m_IdealActivity = ACT_BACKFLIP;
					animDesired = LookupSequence("back_flip");
				}

				//super jumping
				else if(m_eAnimationTrigger == PLAYER_SUPERJUMP)
				{	m_IdealActivity = ACT_LEAP;
					animDesired = LookupActivity(ACT_LEAP);
				}
				// Pcjoe: Must be moving upwards, play jump anim
				else
				{	m_IdealActivity = ACT_HOP;
					animDesired = LookupActivity(ACT_HOP);
				}

			}

			//walk/crouch
			else 
			{	// Edited by Pcjoe - Making it possible to walk :-)
				//	if ( flSpeed > GetMaxSpeed()/3 )
				if ( flSpeed > GetMaxSpeed()/1.3 || (m_bTurbo && flSpeed > 0))
				{	// Added by Pcjoe - Run Backwards
					if(m_fMoveSpeed < 0.0f)
					{	animDesired = LookupSequence( "run_backward" );
						m_IdealActivity = ACT_RUN_BACK;
					}
					else
					{	// Turbo run anim
						if(m_bTurbo && ((m_fMoveSpeed > 0.0f) || (m_fSideSpeed != 0.0f)))
						{	animDesired = LookupSequence( "turbo_run" );
						}
						else
						{	// Added by Pcjoe
							if(CanPlayForwardWalk())
							{	animDesired = LookupSequence( "run" );
							}
							else
							{	animDesired = LookupSequence( "run_backward" );
							}
						}
						m_IdealActivity = ACT_RUN_FRONT;
					}
				}
				else if (flSpeed > 0)
				{	// Added by Pcjoe - walk Backwards
					if(m_fMoveSpeed < 0.0f)
					{	animDesired = LookupSequence( "walk_backward" );
						m_IdealActivity = ACT_WALK;
					}
					else
					{	// Added by Pcjoe
						if(CanPlayForwardWalk())
						{	animDesired  = LookupSequence( "walk2handed" );
						}
						else
						{	animDesired = LookupSequence( "walk_backward" );
						}
						m_IdealActivity = ACT_WALK;
					}
				}
				else
				{	// Edited by Pcjoe, implention of deep idle
					if(pev->sequence != LookupSequence( "idle" ) && pev->sequence != LookupSequence( "deep_idle" ))
					{	m_fIdleTime = gpGlobals->time;
					}
					if(m_fIdleTime < gpGlobals->time - 5)
					{	animDesired  = LookupSequence( "deep_idle" );
					}
					else
					{	animDesired  = LookupSequence( "idle" );
					}
				}
			}
		}
	}	

	//play the animation
	iFrame = 0;
	if ( m_Activity == m_IdealActivity )
	{	if (eIdealAnimFlags && eIdealAnimFlags!=m_eAnimationFlags)
		{	iFrame = pev->frame;				
		}		
	}
	m_Activity = m_IdealActivity;
	m_eAnimationFlags = eIdealAnimFlags;	

	if (animDesired == -1)
	{	animDesired = 0;
	}
	return animDesired;
}

int CBuddy::GetLowerBodyAnimation()
{	int animDesired = 0;
	float flSpeed = fabs(m_fMoveSpeed);

	//Flying IAN:: flipped to play correct animation
	if (m_bFlying)
	{	if (m_fMoveSpeed > 0.0f)
		{	animDesired = LookupSequence("fly_backward");			
		}
		else if (m_fMoveSpeed < 0.0f)
		{	animDesired = LookupSequence("fly_forward");			
		}
		else
		{	animDesired = LookupSequence("fly_idle");			
		}
	}
	
	//Swimming
	else if ( pev->waterlevel > 1 )
	{// Edited by Pcjoe
		//	if ( flSpeed == 0 )
		if (flSpeed > GetMaxSpeed()/1.6)
		{
			// Back/front swimming animations
			if(m_fMoveSpeed < 0.0f)
				animDesired = LookupSequence("swim_backwards");
			else
				animDesired = LookupActivity(ACT_SWIM);
		}
		else
		{	animDesired = LookupActivity(ACT_HOVER);
		}
	}

	else if(!FBitSet(pev->flags,FL_ONGROUND))
	{	//Falling
		if(pev->velocity.z < 0)
		{	animDesired = LookupSequence("fly_fall");
		}
	
		//Long Jumping
		else if(m_eAnimationTrigger == PLAYER_SUPERJUMP)
		{	animDesired = LookupActivity(ACT_LEAP);		
		}

		//Back Flip
		else if (m_eAnimationTrigger == PLAYER_BACKFLIP)
		{	animDesired = LookupSequence("back_flip");
		}
		
		//Jump
		else if(m_eAnimationTrigger == PLAYER_JUMP )
		{	animDesired = LookupActivity(ACT_HOP);		
		}

		// Pcjoe: Must be moving upwards, play jump anim
		else
		{	m_IdealActivity = ACT_HOP;
			animDesired = LookupActivity(ACT_HOP);
		}
	}	
	else 
	{	//Crouching
		// Edited by Pcjoe - Making it possible to walk :-)
		//	if ( flSpeed > GetMaxSpeed()/3 )
		if ( flSpeed > GetMaxSpeed()/1.3  || (m_bTurbo && flSpeed > 0))
		{	// Added by Pcjoe - Run Backwards
			if(m_fMoveSpeed < 0.0f)
			{	animDesired = LookupSequence( "run_backward" );
			}
			else
			{	// Turbo run anim
				if(m_bTurbo && ((m_fMoveSpeed > 0.0f) || (m_fSideSpeed != 0.0f)))
				{	animDesired = LookupSequence( "turbo_run" );
				}
				else
				{	// Added by Pcjoe
					if(CanPlayForwardWalk())
					{	animDesired = LookupSequence( "run" );
					}
					else
					{	animDesired = LookupSequence( "run_backward" );
					}
				}
			}
		}
		else if ( flSpeed != 0 )
		{	// Added by Pcjoe - walk Backwards
			if(m_fMoveSpeed < 0.0f)
			{	animDesired = LookupSequence( "walk_backward" );
			}
			else
			{	// Added by Pcjoe
				if(CanPlayForwardWalk())
				{	animDesired  = LookupSequence( "walk2handed" );
				}
				else
				{	animDesired = LookupSequence( "walk_backward" );
				}
			}		
		}
		else
		{	// Edited by Pcjoe, implention of deep idle
			if(pev->sequence != LookupSequence( "idle" ) && pev->sequence != LookupSequence( "deep_idle" ))
			{	m_fIdleTime = gpGlobals->time;
			}
			if(m_fIdleTime < gpGlobals->time - 5)
			{	animDesired = LookupSequence( "deep_idle" );		
			}
			else
			{	animDesired  = LookupSequence( "idle" );
			}
		}		
	}

	//animation error, don't play ani
	if (animDesired == -1)
	{	animDesired = 0;
	}
	return animDesired;
}

//Pcjoe: Can play forward walk/run animation
bool CBuddy::CanPlayForwardWalk( void )
{	UTIL_MakeVectors ( pev->v_angle );
	float flDot = DotProduct (GetVelocity(), gpGlobals->v_forward);

	// Use Forward anims
	if (flDot > VIEW_FIELD_FULL)
	{	return true;
	}
	// Use backward anims
	return false;
}

void CBuddy::SetAnimation( PLAYER_ANIM playerAnim )
{	// Added by Pcjoe, reset sequence first
	ResetSequenceInfo( );
	if (m_eAnimationTrigger != PLAYER_SWATAWAY_FORWARD && m_eAnimationTrigger != PLAYER_SWATAWAY_LEFT && m_eAnimationTrigger != PLAYER_SWATAWAY_RIGHT)
	{	m_bSwat = false;
	}
	m_eAnimationTrigger = playerAnim;
	//PlayAnimation(NULL);
}

void CBuddy::PlayAnimation(CBasePlayer* pBuddyOwner)
{	
	//set aura direction, 1 is up, 0 is down
	if (m_pAura)
	{	// Added by Pcjoe, better way to determine aura direction
		m_pAura->pev->body = GetAuraDirection();
	}
/*
	// Added by Pcjoe, manage animations
	m_Melee.ManageList();*/
	//select an animation
	if (!PlayFullBodyAnimation(pBuddyOwner))
	{	int iFrame;
		int animDesired = GetUpperBodyAnimation(iFrame);
		int animDesiredGait = GetLowerBodyAnimation();
		if (animDesiredGait)
		{	pev->gaitsequence = animDesiredGait;
		}
		
		// Already using the desired animation?
		if (pev->sequence != animDesired)
		{
			pev->frame = iFrame;
			pev->sequence = animDesired;
			ResetSequenceInfo( );
		}
	}
	RemoveAnimationTriggers();
}

void CBuddy::RemoveAnimationTriggers()
{
	//remove triggers on running animations if they're finished
	if(m_eAnimationTrigger == PLAYER_ATTACK1)
	{	if (m_Activity != ACT_RANGE_ATTACK1 || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_ATTACK2)
	{	if (m_Activity != ACT_RANGE_ATTACK2 || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_SWATAWAY_LEFT)
	{	if (m_Activity != ACT_SWATAWAY_LEFT || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
			m_bSwat = false;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_SWATAWAY_RIGHT)
	{	if (m_Activity != ACT_SWATAWAY_RIGHT || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
			m_bSwat = false;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_SWATAWAY_FORWARD)
	{	if (m_Activity != ACT_SWATAWAY_FORWARD || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
			m_bSwat = false;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_WRAP)
	{	if (m_Activity != ACT_WRAP || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_JUMP)
	{	if (m_Activity != ACT_HOP || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_BACKFLIP)
	{	if (m_Activity != ACT_BACKFLIP || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
		}
	}
	else if(m_eAnimationTrigger == PLAYER_SUPERJUMP)
	{	if (m_Activity != ACT_LEAP || m_fSequenceFinished)
		{	m_eAnimationTrigger = PLAYER_NULL;
		}
	}
}

void CBuddy::AimAt(Vector vOrigin)
{
	//m_vIdealLookAngles = WrapAngles(UTIL_VecToAngles(vOrigin - GetGunPosition()));
	m_vIdealLookAngles = WrapAngles(UTIL_VecToAngles(vOrigin - pev->origin));
}

void CBuddy::AimToIdeal()
{
	// UNDONE : Don't use perfect aim
	Vector Angles = WrapAngles(m_vIdealLookAngles);

	pev->v_angle = Angles;
	pev->v_angle.z = 0.0f;
	pev->v_angle.x = -pev->v_angle.x;

	if (Angles.x > 180)
		Angles.x = Angles.x - 360;
	pev->angles.x = UTIL_Approach(Angles.x, pev->angles.x, 50 * 0.1 );
	if (pev->angles.x < -80) pev->angles.x = -80;
	if (pev->angles.x >  80) pev->angles.x =  80;
	pev->angles.y = pev->v_angle.y;
	pev->angles.z = 0.0f;


/*
	pev->v_angle = WrapAngles(m_vIdealLookAngles);
	pev->v_angle.z = 0.0f;

	pev->angles.x = pev->v_angle.x / 3;
	pev->angles.y = pev->v_angle.y;
	pev->angles.z = 0.0f;

	pev->v_angle.x = -pev->v_angle.x; // Aim bug-fix*/
}

void CBuddy::HomeThroughMap()
{
	if (m_fCheckUpDownTime <= gpGlobals->time)
		CheckUpDown();

	if (m_fAvoidFriendsDelay < gpGlobals->time)
		AvoidNearFriends();

	// check if we should be avoiding walls
	if (m_fDontAvoidWallTime <= gpGlobals->time)
	{
		if (m_fLookDelay < gpGlobals->time)
		{
			CheckFOV(); // Check FOV
			m_fLookDelay = gpGlobals->time + RANDOM_FLOAT(0.8, 1.2);
		}

        CheckSkybox(); // and after that look for and avoid skyboxes...
    }
}

// Function: fire (to) many tracelines to determine what the best move direction is;
// The best direction is the spot whith the highest distance
void CBuddy::CheckFOV()
{
    if (m_fDontAvoidWallTime > gpGlobals->time)
 	   return;

	TraceResult tr;

	Vector v_src = GetGunPosition();

	float angle;

    float distance = 0;
    float nearestdistance = 0;

    Vector DesiredVec = g_vecZero;
	float flMostLeftEndDist, flMostStraightEndDist, flMostRightEndDist;
	Vector vecForwardPlaneNormal;

    // scan 90 degrees of buddy's field of view...
	for (angle =-45; angle <=45; angle+=5) //-70 -- +70
	{
		v_src = WrapAngles(pev->v_angle); // restore bot's current v_angle
        v_src.y = UTIL_WrapAngle (v_src.y + angle); // pan it from left to right
		UTIL_MakeVectors(v_src);

        // trace line slightly under eyes level
        UTIL_TraceHull (pev->origin + pev->view_ofs,
                       (pev->origin + pev->view_ofs) + (gpGlobals->v_forward * 10000),
                        ignore_monsters, human_hull, edict(), &tr);

        distance = tr.flFraction * 10000; // store distance to obstacle

        // now take the most logical direction:
        // take the endVec with the largest distance
        if (distance > nearestdistance)
        {
            DesiredVec = tr.vecEndPos;
            nearestdistance = distance;
		}

		if (angle == -45)
			flMostLeftEndDist = distance;
		else if (angle == 0)
			flMostStraightEndDist = distance;
		else if (angle == 45)
			flMostRightEndDist = distance;
   }

   if (DesiredVec != g_vecZero)
   { 
	   AimAt(DesiredVec);
   }

   // Now check for walls and other collisions...

   // first check if bot is in a corner
   if ((flMostLeftEndDist < 70) && (flMostRightEndDist < 70) &&
	   (flMostStraightEndDist < 70)) // he is..
   {
	   TraceResult tr;
	   Vector v_src, v_end;
/*
	   // check if we can jump up if when we're not flying and not under water...
	   if ((pev->gravity > 0.1) && (pev->waterlevel != 3) &&
		   (flMostStraightEndDist < 45.0) && (!(pev->flags & FL_ONGROUND)))
	   {
		   UTIL_MakeVectors(pev->v_angle);

		   v_src = pev->origin + Vector(0,0,200);
		   v_end = v_src + gpGlobals->v_forward * 80;

		   UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull, 
					  pev->pContainingEntity, &tr);

	       if (tr.flFraction >= 1.0)
		   {
			   pev->button |= IN_JUMP;
			   return;
		   }
	   }
*/
       TurnAround();
	   
	   m_fDontAvoidWallTime = gpGlobals->time + 1.0;

       m_fDontAvoidWallTime = gpGlobals->time + 1.0;
	   return;
   }

   // then check if he can't move forward...
   if (flMostStraightEndDist < 70) // something is blocking on eye height
   {
	   TraceResult tr;
	   Vector v_src, v_end;
/*
	   // check if the bot can jump when he's not flying and the bot is not in water
	   if ((pev->gravity > 0.1) && (pev->waterlevel != 3) &&
		   (!(pev->flags & FL_ONGROUND)))
	   {
		   UTIL_MakeVectors(pev->v_angle);

		   v_src = pev->origin + Vector(0,0,200);
		   v_end = v_src + gpGlobals->v_forward * 40;

		   UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull, 
					  pev->pContainingEntity, &tr);

	       if (tr.flFraction >= 1.0)
		   {
			   pev->button |= IN_JUMP;
			   return;
		   }
/* Disabled, maybe i work on it later
		   else // Otherwise try to walljump on it
		   {
			   MakeTask(pBot, TaskWallJump);
			   pev->button |= IN_JUMP;
			   return;
		   }
*//*
	   }
*/
	   TurnAtWall(&vecForwardPlaneNormal);

       m_fDontAvoidWallTime = gpGlobals->time + 1.0;
	   return;
   }

   return;
}

void CBuddy::CheckSkybox()
{
   if (m_fDontAvoidWallTime > gpGlobals->time)
	   return;

   Vector v_src = WrapAngles(pev->v_angle); // store bot's current v_angle;
   UTIL_MakeVectors(v_src);
   Vector vecEnd = pev->origin + pev->view_ofs + (gpGlobals->v_forward * 400);

   // Skybox infront?
   if (UTIL_PointContents(vecEnd) == CONTENTS_SKY)
   {
       TurnAround();
	   m_fDontAvoidWallTime = gpGlobals->time + 1.0;
   }

   return;
}

void CBuddy::CheckUpDown()
{
	if (!m_bFlying)
	{
		// Look up so the buddy will walk on slopes aswell(otherwise the buddy thinks the slope
		// is a wall
		m_vIdealLookAngles.x = 40.0;
		return;
	}

	bool direction_up = (GetCollisionDist(this, (dir_up | dir_forward), 100.0) < 100.0f);
	bool direction_down = (GetCollisionDist(this, (dir_down | dir_forward), 100.0) < 100.0f);

	m_fCheckUpDownTime = gpGlobals->time + 3.0;

	if ((direction_up) && (!direction_down))
	{
        m_vIdealLookAngles.x = RANDOM_FLOAT(-25.0, -35.0);  // look downwards
	}
	else if ((!direction_up) && (direction_down))
	{
        m_vIdealLookAngles.x = RANDOM_FLOAT(25.0, 35.0);  // look upwards
	}
    else if ((direction_up) && (direction_down))
	{
		m_vIdealLookAngles.x = 0; // look straight
	}
	else
	{
		int i = RANDOM_LONG(1, 3);
		if (i == 1)
            m_vIdealLookAngles.x = RANDOM_FLOAT(-25.0, -35.0);  // look downwards
		else if (i == 2)
            m_vIdealLookAngles.x = RANDOM_FLOAT(25.0, 35.0);  // look upwards
		else
            m_vIdealLookAngles.x = 0;  // look straight
	}
    return;
}

void CBuddy::TurnAtWall(Vector *vecPlaneNormal)
{
	Vector Normal;
    float Y, Y1, Y2, D1, D2, Z;
 
    // Find the normal vector from the trace result.  The normal vector will
    // be a vector that is perpendicular to the surface from the TraceResult.

    Normal = UTIL_VecToAngles(*vecPlaneNormal);

    // Since the bot keeps it's view angle in -180 < x < 180 degrees format,
    // and since TraceResults are 0 < x < 360, we convert the bot's view
    // angle (yaw) to the same format at TraceResult.

    Y = pev->v_angle.y;
    Y = Y + 180;
    if (Y > 359) Y -= 360;

    // Turn the normal vector around 180 degrees (i.e. make it point towards
    // the wall not away from it.  That makes finding the angles that the
    // bot needs to turn a little easier.

    Normal.y = Normal.y - 180;
    if (Normal.y < 0)
		Normal.y += 360;

    // Here we compare the bots view angle (Y) to the Normal - 90 degrees (Y1)
    // and the Normal + 90 degrees (Y2).  These two angles (Y1 & Y2) represent
    // angles that are parallel to the wall surface, but heading in opposite
    // directions.  We want the bot to choose the one that will require the
    // least amount of turning (saves time) and have the bot head off in that
    // direction.

    Y1 = Normal.y - 90;
    if (RANDOM_LONG(1, 100) <= 50)
    {
		Y1 = Y1 - RANDOM_FLOAT(5.0, 20.0);
    }
    if (Y1 < 0) Y1 += 360;

    Y2 = Normal.y + 90;
    if (RANDOM_LONG(1, 100) <= 50)
	{
		Y2 = Y2 + RANDOM_FLOAT(5.0, 20.0);
    }
    if (Y2 > 359) Y2 -= 360;

    // D1 and D2 are the difference (in degrees) between the bot's current
    // angle and Y1 or Y2 (respectively).

    D1 = abs(Y - Y1);
    if (D1 > 179) D1 = abs(D1 - 360);
    D2 = abs(Y - Y2);
    if (D2 > 179) D2 = abs(D2 - 360);

    // If difference 1 (D1) is more than difference 2 (D2) then the bot will
    // have to turn LESS if it heads in direction Y1 otherwise, head in
    // direction Y2.  I know this seems backwards, but try some sample angles
    // out on some graph paper and go through these equations using a
    // calculator, you'll see what I mean.

    if (D1 > D2)
		Z = Y1;
    else
		Z = Y2;

    // convert from TraceResult 0 to 360 degree format back to bot's
    // -180 to 180 degree format.

    if (Z > 180)
		Z -= 360;

    // set the direction to head off into...
    m_vIdealLookAngles.y = UTIL_WrapAngle(Z);
}

void CBuddy::TurnAround()
{
	Vector LookVec = pev->v_angle;
    LookVec.y = UTIL_WrapAngle(LookVec.y + 180.0f);
    m_vIdealLookAngles = LookVec;
}

void CBuddy::AvoidNearFriends()
{
	m_fAvoidFriendsDelay = gpGlobals->time + 1.0f;
	bool Avoid = false;
	CBaseMonster *pFriend = NULL, *pEnt = NULL, *pNearestFriend = NULL;
	float flDist, flNearestDist = 9999.0f;

	while ((pEnt = (CBaseMonster *)UTIL_FindEntityInSphere(pEnt, pev->origin, 250.0f)) != NULL)
	{
		if (IsNull(pEnt))
			continue;

		// Skip our owner
		if (pEnt == m_pOwner)
			continue;

		pFriend = (CBaseMonster *)pEnt;

		// Is it a friend?
		if (pFriend->IsPlayer() || FStrEq(STRING(pFriend->pev->classname), "player_buddy"))
		{
			// Skip if other team
			if (g_teamplay && (pFriend->pev->team != pev->team))
				continue;
		}
		else
			continue;

		if (!FInViewCone(pFriend) || !FVisible(pFriend))
			continue;

		flDist = UTIL_Distance(pev->origin, pFriend->pev->origin);
		if (flDist < flNearestDist)
		{
			flNearestDist = flDist;
			pNearestFriend = pFriend;
		}
	}

	if (pNearestFriend)
	{
		// Randomly turn around from him
		Vector v_Face = WrapAngles(UTIL_VecToAngles(pNearestFriend->GetGunPosition() - GetGunPosition()));
		
		switch(RANDOM_LONG(0, 1))
		{
		case 0:
			// Turn randomly left
			v_Face.y = UTIL_WrapAngle(v_Face.y + RANDOM_LONG(40, 180));
			break;
		case 1:
			// Turn randomly right
			v_Face.y = UTIL_WrapAngle(v_Face.y - RANDOM_LONG(40, 180));
			break;
		}
		m_fDontAvoidWallTime = gpGlobals->time + 0.25f;
	}
}

int CBuddy::GetAuraDirection()
{/*	float flSpeed = pev->velocity.Length2D();
	if (flSpeed>GetMaxSpeed()*0.1)
	{	m_pAura->pev->body = 1;
	}
	else
	{	m_pAura->pev->body = 0;
	}*/

	// Speed of the player
	float flSpeed = fabs(m_fMoveSpeed);

	// Saved here to make code shorter + easier to read...
	int iLeft = (m_fSideSpeed < 0.0f), iRight = (m_fSideSpeed > 0.0f),
		iFront = (m_fMoveSpeed > 0.0f), iBack = (m_fMoveSpeed > 0.0f);

	// If flying...
	if(pev->flDuckTime)
	{	return UP;
	}
	else if(m_bFlying)
	{	// If your moving side to side in flight, dont tip the aura
		if( (iLeft || iRight) && !(iFront || iBack) )
		{	return UP;
		}
		else if((iFront || iBack) && m_fSlowDown)
		{	return DOWN;
		}
		else
		{	return UP;
		}
	}
	// Swooping
	else if(m_Swoop.bActive)
	{	switch(m_Swoop.iSwoopDir)
		{	
		default:
		case UP:
		case LEFT:
		case RIGHT:
		case FRONT:
		case BACK:
		case DOWN:
			{	return DOWN;
			}
		}
	}
	// On ground
	else if ( pev->flags & FL_ONGROUND )
	{	// If your ducking, automatically point it down
		if(pev->button & IN_DUCK)
		{	return UP;
		}
		// If your moving side to side, dont tip the aura
		else if( (iLeft || iRight) && !(iFront || iBack) )
		{	return UP;
		}
		// Moving? Point it down, if not, point it up
	/*	else if(flSpeed > GetMaxSpeed()*0.45)
		{	return DOWN;
		}*/
		// Moving front/back
		else if((iFront || iBack) && m_fSlowDown)
		{	return DOWN;
		}
		else
		{	return UP;
		}
	}
	// If the player is in water
	else if(pev->waterlevel > 1)
	{
		if (flSpeed > GetMaxSpeed()/1.6)
		{	return DOWN;
		}
		else
		{	return UP;
		}

	}
	// In the air, falling up/down
	else
	{	if(pev->velocity.z >= 0)
		{	return DOWN;
		}
		else
		{	return UP;
		}
	}

	return UP;
}

void CBuddy::StartTurbo ( void )
{	
	//if (!m_fBecomeCandy && !m_fTurbo && !m_fObserverFlag && IsAlive() && !m_fCharging && !m_fHoldDisc &&!m_fAscending&&!m_fMelee)
	//harSens TODO: handle strangling
	if (!m_bBecomeCandy && !m_bTurbo && !m_bPowerUp && (m_iShouldTurbo==10))
	{	m_bTurbo = true;
		m_pAura = CBuddyAura::Create(this);
		m_fTurboTime = gpGlobals->time + 10.25f;
	}
	else
	{
		if (m_iShouldTurbo < 10)
			m_iShouldTurbo++;
	}
}

void CBuddy::StopTurbo ( void )
{	// Added by Pcjoe, dont mess with turbo When in Melee is active
	if (!m_Swoop.bActive /*&&!m_fMelee*/)
    {	if (m_bTurbo)
	    {	m_bTurbo = false;
			if(m_pAura)
		    {	m_pAura->Destroy();
		 	    m_pAura = NULL;
		    }
	    }
		m_fTurboTime = 0.0f;
    }

	m_iShouldTurbo = 0;
}

void CBuddy::DeathSound()
{
	// UNDONE	
}

void CBuddy::PainSound()
{
	// UNDONE
}

void CBuddy::EmitClassSound(const char *sound)
{
	EMIT_SOUND(ENT(pev), CHAN_VOICE, UTIL_VarArgs("%s/%s",m_pClass->GetSoundDir(),sound), 1.0, ATTN_NORM);
}

void CBuddy::Teleport()
{
	if(!m_bBecomeCandy && IsAlive() && /*!m_fControl &&
		!(m_fCharging && !m_pClass->CanTeleportWhileCharging()) && !m_fHoldDisc && !m_fMelee &&*/
		!m_bBlock && /*!m_fShield &&*/ !m_bPowerUp/* && !m_fAscending*/ && !m_Swoop.bActive)
	{	if (UseKi(150))
		{				
			EMIT_SOUND(ENT(pev), CHAN_AUTO, "weapons/teleport.wav", 1, ATTN_NORM);

			MESSAGE_BEGIN( MSG_PVS, gmsgAfterFX, GetGunPosition());
				WRITE_BYTE(entindex());
				WRITE_COORD(GetGunPosition().x);
				WRITE_COORD(GetGunPosition().y);
				WRITE_COORD(GetGunPosition().z);
			MESSAGE_END();

			TraceResult tr;
			UTIL_MakeVectors(GetMoveDirection());
			Vector v_end = GetGunPosition() + gpGlobals->v_forward * 267;

			// Use MoveToOrigin, instead of a tracehull + setorigin. For some reason
			// TraceHull doesn't work right for monsters :(
			UTIL_MoveToOrigin( edict(), v_end, 267.0f, MOVE_STRAFE );

			// Added by Pcjoe
//			m_flLastTeleport = gpGlobals->time;
		}		
	}
}

void CBuddy::StartPowerUp ( void )
{
	if (!m_bPowerUp && CanPowerUp())
	{
		if (m_bBlock)
		{//	StopBlock();
		}
		if (m_bTurbo)
		{	StopTurbo();
		}
		//stay in the air, if falling down
		if (!(pev->flags&FL_ONGROUND))
		{	if (!m_bFlying)
			{	StartFly();
			}
		}
		m_fSlowDown = 0.0;
		m_bPowerUp = true;
		this->pev->velocity = Vector(0,0,0);

		Vector col = g_vecAuraColor[m_pClass->GetAuraSkin()];
		MESSAGE_BEGIN( MSG_ALL, gmsgPowerupFX, GetGunPosition());
			WRITE_BYTE(entindex());
			WRITE_BYTE(col.x);
			WRITE_BYTE(col.y);
			WRITE_BYTE(col.z);
		MESSAGE_END();
	}
}

void CBuddy::StopPowerUp ( void )
{	if (m_bPowerUp)
	{	m_fSlowDown = 1.0;
		m_bPowerUp = false;
		MESSAGE_BEGIN( MSG_ALL, gmsgStopPowerupFX, GetGunPosition());
			WRITE_BYTE(entindex());
		MESSAGE_END();
	}
}

bool CBuddy::CanPowerUp()
{
	return (!m_bBecomeCandy && !m_bPowerUp && !m_Swoop.bActive && !m_fCharging && /*!m_bControl &&*/
			IsAlive() && /*!m_fHoldDisc && !m_fAscending && !m_fMelee &&*/
			(m_Melee.m_fLastPowerHitTime < (gpGlobals->time-1.0)) /*&& !m_fShield*/);
}

Vector CBuddy::GetMoveDirection()
{
	Vector movedir;

	//movedir = WrapAngles(m_pOwner->pev->origin - pev->origin);
	movedir = m_pOwner->pev->origin - pev->origin;
	movedir.Normalize();

	// IAN:: we always move towards our owner unless we are shooting
	return  movedir;
	
	UTIL_MakeVectors(m_pOwner->pev->origin - pev->origin);

	//UTIL_MakeVectors(pev->angles);
	//UTIL_MakeAimVectors(pev->angles);
	Vector v_end;

	if (m_fMoveSpeed != 0.0f)
		v_end = pev->origin + gpGlobals->v_forward * 200;
	else if (m_fSideSpeed < 0.0f)
		v_end = pev->origin - gpGlobals->v_right * 200;
	else if (m_fSideSpeed > 0.0f)
		v_end = pev->origin + gpGlobals->v_right * 200;
	else
		v_end = pev->origin - gpGlobals->v_up * 200;

	Vector v_dir = WrapAngles(UTIL_VecToAngles(v_end - pev->origin));

	if (m_fMoveSpeed < 0.0f)
	{
		v_dir.y = UTIL_WrapAngle(v_dir.y + 180.0f);
		v_dir.x = -v_dir.x;

		if (m_fSideSpeed < 0.0f)
			v_dir.y = UTIL_WrapAngle(v_dir.y - 45.0f);
		else if (m_fSideSpeed > 0.0f)
			v_dir.y = UTIL_WrapAngle(v_dir.y + 45.0f);
	}
	else if (m_fMoveSpeed > 0.0f)
	{
		if (m_fSideSpeed < 0.0f)
			v_dir.y = UTIL_WrapAngle(v_dir.y + 45.0f);
		else if (m_fSideSpeed > 0.0f)
			v_dir.y = UTIL_WrapAngle(v_dir.y - 45.0f);
	}

	return v_dir;
}

Vector CBuddy::GetVelocity()
{
	UTIL_MakeVectors(GetMoveDirection());
	return (gpGlobals->v_forward * fabs(m_fMoveSpeed));
}

float CBuddy::GetMaxSpeed()
{
	//no slowing down/speeding up, when riding someone's beam
	if (pev->movetype == MOVETYPE_RIDEBEAM && m_bBlock)
	{	return m_iMaxSpeed;
	}
	else
	{	if(!m_Swoop.bActive)
			return ( m_bTurbo ? m_iMaxSpeed * 2 : m_iMaxSpeed) * m_fSlowDown;
		else
			return float(m_iMaxSpeed) * 3.7f;
	}
}

void CBuddy::FollowOwner()
{
	// if we're firing a beam then we cant move!
	if (m_fControl)
	{	StopTurbo();
		SetMoveSpeed(0.0f);
		return;
	}
	
	float flDist = UTIL_Distance(pev->origin, m_pOwner->pev->origin);

	if (!m_bFlying)
		StartFly();

	// Aim at owner
	// AimAt(m_pOwner->pev->origin);

	if (flDist < 30.0f)
	{
		// Wait
		StopTurbo();
		SetMoveSpeed(0.0f);
	}
	else if (flDist < 150.0f)
	{
		// Walk to owner
		StopTurbo();
		SetMoveSpeed(GetMaxSpeed()/2);
	}
	else
	{
		// Run to owner
		StartTurbo();
		SetMoveSpeed(GetMaxSpeed());
	}
}

// when the buddy shoots he uses his owners ki
BOOL CBuddy::UseOwnerKi(int amount)
{
	CBaseEntity *pTemp = m_pOwner;
	CBasePlayer *pBuddyOwner = (CBasePlayer *)pTemp;

	return pBuddyOwner->UseKi(amount);
}

bool CBuddy::UseKi(float flKi)
{	flKi *= m_pClass->GetKiEfficiency();
	//make sure nothing is ki-free
	if(flKi<1)
	{	flKi=1;
	}

	if(m_fKi<flKi)
	{	return false;
	}
	else
	{	m_fKi-=flKi;
		return true;
	}
}

int CBuddy :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
	// have suit diagnose the problem - ie: report damage type
	int bitsDamage = bitsDamageType;
	int ffound = true;
	int fTookDamage;
	float flHealthPrev = pev->health;

	// Already dead
	if ( !IsAlive() )
		return 0;
	// go take the damage first

	//added by harSens:
	CBaseEntity *pInflictor = CBaseEntity::Instance(pevInflictor);
	
	CBaseEntity *pAttacker = CBaseEntity::Instance(pevAttacker);

/*	// Added by Pcjoe, if we're in advanced melee, dont take external damage
	if ( m_Melee.InAdvMeleeBattle() )
	{	// Only melee attacks can harm you in a melee battle
		if( !((bitsDamageType & DMG_MELEE) || (bitsDamageType & DMG_KICK)) )
		{	return 0;
		}
		// Check damage sources
		if(m_Melee.IsAttacker())
		{	if(pevAttacker != m_Melee.GetDefender()->pev)
			{	return 0;
			}
		}
		if(m_Melee.IsDefender())
		{	if(pevAttacker != m_Melee.GetAttackerOther()->pev)
			{	return 0;
			}
		}
	}

	// Added by Pcjoe, adv melee invulnerability
	if(!m_Melee.CanInflictDamage((CBasePlayer*)pInflictor, bitsDamageType))
	{	return 0;
	}
*/
	//added by harSens: attacked with a disc?
	BOOL fDisc = false;
	if (pInflictor->Classify()==CLASS_DISCATTACK)
	{	fDisc = true;		
	}
	
/*	//reduced damage while blockstruggling
	if (m_fBlockStruggleAttacker)
	{	//flDamage *= 0.02;
		return 0;
	}

	if (m_fBlockStruggling && m_pBlockStruggler != pInflictor)
	{	//flDamage *= 0.02;
		return 0;
	}

	//reduce damage while powerstruggling, if the attack that hits us is not in the struggle
	if(m_fPowerStruggling && m_pPowerStruggle)
	{	if(!m_pPowerStruggle->InStruggle(pevInflictor))
		{	//flDamage *= 0.02;
			return 0;
		}
	}*/
	// keep track of amount of damage last sustained
	m_lastDamageAmount = flDamage;

	// turbo reduces damage
	if (m_bTurbo)
	{	flDamage *= 0.75;	
	}
/*
	// Added by Pcjoe, 1/2 damage when transforming
	if (m_fAscending)
	{	flDamage *= 0.5;
	}
*/
	int iMyYaw = ( (int)pev->v_angle.y + 360)%360;
	int iInflictorYaw = ( (int)pevInflictor->angles.y + 540)%360;	

	//block reduces damage, if performed to the right dir
	BOOL fBlocked=false;
	if (m_bBlock)
	{	if(iInflictorYaw < iMyYaw + PLAYER_BLOCKANGLE)
		{	if (iInflictorYaw > iMyYaw - PLAYER_BLOCKANGLE)
			{	flDamage *= 0.25;
				fBlocked = true;
			}
		}				
	}

	if (bitsDamageType&DMG_CANDY)
	{	//killed by a candy attack
		if (pAttacker->IsPlayer() && flDamage >= pev->health && pAttacker != this)
		{	WaitToBecomeCandy((CBasePlayer *)pAttacker);
			return 0;
		}
	}
	
	// give the attacker some higher stats
	if (pAttacker->IsPlayer())
	{	if (fDisc)
		{	((CBasePlayer *)pAttacker)->IncreaseStrength(this,10);
		}
		else if((bitsDamageType & DMG_MELEE) || (bitsDamageType & DMG_KICK))
		{	if (flDamage<pev->health)
			{	((CBasePlayer *)pAttacker)->IncreaseStrength(this,flDamage*2);
			}
			else
			{	((CBasePlayer *)pAttacker)->IncreaseStrength(this,pev->health*2);
			}
		}
		else
		{	if (flDamage<pev->health)
			{	((CBasePlayer *)pAttacker)->IncreaseStrength(this,flDamage);
			}
			else
			{	((CBasePlayer *)pAttacker)->IncreaseStrength(this,pev->health);
			}
		}
	}
	else if (FStrEq(STRING(pAttacker->pev->classname), "player_buddy"))
	{	if (fDisc)
		{	((CBuddy *)pAttacker)->IncreaseStrength(this,10);
		}
		else if((bitsDamageType & DMG_MELEE) || (bitsDamageType & DMG_KICK))
		{	if (flDamage<pev->health)
			{	((CBuddy *)pAttacker)->IncreaseStrength(this,flDamage*2);
			}
			else
			{	((CBuddy *)pAttacker)->IncreaseStrength(this,pev->health*2);
			}
		}
		else
		{	if (flDamage<pev->health)
			{	((CBuddy *)pAttacker)->IncreaseStrength(this,flDamage);
			}
			else
			{	((CBuddy *)pAttacker)->IncreaseStrength(this,pev->health);
			}
		}
	}

/*
	//non directional blind
	if (!m_fBlinded && bitsDamageType & DMG_BLIND)
	{	UTIL_ScreenFade	( this, Vector (255,255,255),  BURNINGATTACK_BLIND_TIME/3, BURNINGATTACK_BLIND_TIME*2/3  , 255, FFADE_IN );	
		m_fBlinded = true;
		m_flBlindedTime = gpGlobals->time + BURNINGATTACK_BLIND_TIME; 
	}
*/
	//can't flash yourself
	if (pAttacker == this && bitsDamageType & DMG_FLASH)
	{	return 0;
	}
	
/*	//flashed by solarflare?
	int iAttackerYaw = ( (int)pevAttacker->v_angle.y + 540)%360;
	if (!m_fBlinded)
	{	if(bitsDamageType & DMG_FLASH)
		{	//not blinded if we turn our back to the attacker
			if(iAttackerYaw < iMyYaw + 75)
			{	if (iAttackerYaw > iMyYaw - 75)
				{	UTIL_ScreenFade	( this, Vector (255,255,255),  SOLARFLARE_BLIND_TIME/3, SOLARFLARE_BLIND_TIME*2/3  , 255, FFADE_IN );	
					m_fBlinded = true;
					m_flBlindedTime = gpGlobals->time + SOLARFLARE_BLIND_TIME;
				}
			}
		}
	}
*/
	//hit animations
	if(bitsDamageType&DMG_KICK||bitsDamageType&DMG_PUNCH)
	{	if(fBlocked)
		{	//move attacker backward
			UTIL_MakeVectors(pevInflictor->angles);
			if (bitsDamageType&DMG_PUNCH)
			{
				// Is it a buddy?
				if (FStrEq(STRING(pInflictor->pev->classname), "player_buddy"))
				{
					CBuddy *pB = (CBuddy *)pInflictor;
					pB->ApplyVelocity(pB->pev->velocity - gpGlobals->v_forward, 75, 1.0f);
				}
				else
					pevInflictor->velocity=pevInflictor->velocity-gpGlobals->v_forward * 75;
			}
			else
			{
				// Is it a buddy?
				if (!FStrEq(STRING(pInflictor->pev->classname), "player_buddy"))
					pevInflictor->velocity=pevInflictor->velocity-gpGlobals->v_forward * 250;
			}
		}
		else
		{	UTIL_MakeVectors(pevInflictor->angles);
			//pev->velocity=pev->velocity+gpGlobals->v_forward * 50;
			ApplyVelocity(pev->velocity + gpGlobals->v_forward, 50.0f, 1.0f);
		}
	}
	//end harSens add

	// this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that
	// as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc)
	fTookDamage = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, (int)flDamage, bitsDamageType);

	// tell director about it
	
	MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
		/* disabled by harSens
		 */	
		WRITE_BYTE ( 9 );	// command length in bytes
		WRITE_BYTE ( DRC_CMD_EVENT );	// take damage event
		WRITE_SHORT( ENTINDEX(this->edict()) );	// index number of primary entity
		WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) );	// index number of secondary entity
		WRITE_LONG( 5 );   // eventflags (priority and flags)
	MESSAGE_END();
	

	// how bad is it, doc?

	pev->punchangle.x = -2;

	if (!IsAlive()) // Buddy died?
		Die(pAttacker, pInflictor);

	return fTookDamage;
}

void CBuddy::WaitToBecomeCandy(CBasePlayer *Attacker)
{
	if (!Attacker->m_pCandyVictim && !Attacker->m_pBuddyCandyVictim && !m_bBecomeCandy)
	{	//send candy menu to attacker
		MESSAGE_BEGIN(MSG_ONE, gmsgVGUIMenu, NULL, Attacker->edict());
			WRITE_BYTE( 32 );			//show candy menu
		MESSAGE_END();
	
		//give the attacker his info
		Attacker->m_pBuddyCandyVictim = this;
		Attacker->m_flCandyCounter = gpGlobals->time;

		m_fSlowDown = 0.0f;
		m_bBecomeCandy = true;
	}
}

void CBuddy::ReleaseCandy()
{	//unfreeze controls
	m_fSlowDown = 1.0f;
	m_bBecomeCandy = false;
}

bool CBuddy::BecomeCandy(const char *candy, CBaseEntity *attacker)
{	if (!strcmp(candy,"cookie"))
	{	CCookie::CreateCookie(GetGunPosition(), pev);
	}
	else if (!strcmp(candy,"chocolate"))
	{	CChocolate::CreateChocolate(GetGunPosition(), pev);
	}
	else if (!strcmp(candy,"donut"))
	{	CDonut::CreateDonut(GetGunPosition(), pev);
	}
	else
	{	return false;
	}
	EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/candypop.wav", 1.0, ATTN_NORM);
	pev->effects |= EF_NODRAW;
	int damageFlags = DMG_GENERIC;
	damageFlags |= DMG_NEVERGIB;
	
	TakeDamage( attacker->pev, attacker->pev, 900, damageFlags );

	pev->flags &= ~FL_FROZEN;
	m_bBecomeCandy = false;
	return true;
}

void CBuddy :: IncreaseStrength(CBasePlayer *pVictim, int damage)
{	// don't gain exp for hurting your owner :)
	if (pVictim == m_pOwner)
		return;

	float flPlIncrease = (float)pVictim->m_iMaxPowerLevel / (float)m_iMaxPowerLevel;
	
	
	if (flPlIncrease < 1.0)
	{	//You hit someone weak, you meanie! We gonna give you a cap
		flPlIncrease *= flPlIncrease;
	}

	flPlIncrease *= 600 * damage;
	m_iPowerLevel += flPlIncrease;
	m_iMaxPowerLevel += flPlIncrease;
}

void CBuddy :: IncreaseStrength(CBuddy *pVictim, int damage)
{
	// don't gain exp for hurting yourself :)
	if (pVictim == this)
		return;

	float flPlIncrease = (float)pVictim->m_iMaxPowerLevel / (float)m_iMaxPowerLevel;
	
	
	if (flPlIncrease < 1.0)
	{	//You hit someone weak, you meanie! We gonna give you a cap
		flPlIncrease *= flPlIncrease;
	}

	flPlIncrease *= 600 * damage;
	m_iPowerLevel += flPlIncrease;
	m_iMaxPowerLevel += flPlIncrease;
}

bool CBuddy::HasWeapon(const char *szName)
{
	/*modified by harSens
	TLinkedList<CBaseBuddyWeapon *>::node_s *pNode = m_WeaponList.GetFirst();
	*/
	node_s<CBaseBuddyWeapon *> *pNode = m_WeaponList.GetFirst();

	while(pNode)
	{
		if (FStrEq(pNode->Entry->GetWeaponName(), szName))
			return true;

		pNode = pNode->next;
	}

	return false;
}

CBaseBuddyWeapon *CBuddy::GetWeapon(const char *szName)
{
	/*modified by harSens
	TLinkedList<CBaseBuddyWeapon *>::node_s *pNode = m_WeaponList.GetFirst();
	*/
	node_s<CBaseBuddyWeapon *> *pNode = m_WeaponList.GetFirst();
	while(pNode)
	{
		if (FStrEq(pNode->Entry->GetWeaponName(), szName))
			return pNode->Entry;

		pNode = pNode->next;
	}

	return NULL;
}

bool CBuddy::CanSwoop()
{
	// UNDONE?
	return ((m_fSlowDown == 1.0f) && (m_Swoop.flStartSwoopDelay < gpGlobals->time));
}

void CBuddy::StartSwoop(int iDirection)
{
	if (!CanSwoop())
		return;

	// UNDONE : No chain swoop
	if (m_Swoop.bActive)
		return;

	m_Swoop.bActive = true;
	m_Swoop.flStartTime = gpGlobals->time;
	m_Swoop.iSwoopDir = iDirection;

	// Play sound if Aura exist
	if(m_pAura)
	{	if(m_Swoop.bActive)
		{	EMIT_SOUND(ENT(pev), CHAN_ITEM, "weapons/chainswoop.wav", 1, ATTN_NORM);
		}
		else
		{	EMIT_SOUND(ENT(pev), CHAN_ITEM, "weapons/swoop.wav", 1, ATTN_NORM);
		}
	}
	// Create aura
	else
	{	m_pAura = CBuddyAura::Create(this);
	}
}

void CBuddy::StopSwoop()
{
	m_Swoop.bActive = false;
	m_Swoop.flStartSwoopDelay = gpGlobals->time + 1.0f; // So the buddy can't swoop forever :)
	
	if(m_pAura)
	{	// Just reset the sound if the aura already exist
		if(m_bTurbo)
		{	EMIT_SOUND(ENT(pev), CHAN_ITEM, "weapons/aura.wav", 1, ATTN_NORM);
		}
		// remove it, no turbo was active
		else
		{	m_pAura->Destroy();
			m_pAura = NULL;
		}
	}
}

void CBuddy::SwoopThink()
{
	if (!m_Swoop.bActive)
		return;

	if ((m_Swoop.flStartTime < (gpGlobals->time - 2.85)) ||
		(m_Melee.m_fLastPowerHitTime > (gpGlobals->time - 1.0f)))
	{
		StopSwoop();
		SetMoveSpeed(0.0f);
	}
	else
	{
		// Get Boost Speed
		float flRatio = 0;
		if((m_Swoop.flStartTime + 0.75f) > gpGlobals->time)
		{ 
			flRatio = float(1)-(gpGlobals->time - m_Swoop.flStartTime)/0.75f;
		}

		// Calculate and apply
		float flSlowdown = (m_Swoop.iSwoopDir == BACK) ? 0.85f : 1.0f;
		float flSwoopSpeed = (GetMaxSpeed() + flRatio * (m_pClass->GetStartSpeed() * 4.6f)) * flSlowdown;
		SetMoveSpeed(flSwoopSpeed);
	}
}

void CBuddy::SetSwoopAnim( char *pAnim, Activity &IdealActivity )
{	strcpy( pAnim, "swoop_" );
	switch(m_Swoop.iSwoopDir)
	{
	case UP:
		{	strcat(pAnim, "up");
			IdealActivity = ACT_SWOOP_UP;
			break;
		}
	case DOWN:
		{	strcat(pAnim, "down");
			IdealActivity = ACT_SWOOP_DOWN;
			break;
		}
	case LEFT:
		{	strcat(pAnim, "left");
			IdealActivity = ACT_SWOOP_LEFT;
			break;
		}
	case RIGHT:
		{	strcat(pAnim, "right");
			IdealActivity = ACT_SWOOP_RIGHT;
			break;
		}
	default:
	case FRONT:
		{	strcat(pAnim, "forward");
			IdealActivity = ACT_SWOOP_FORWARD;
			break;
		}
	case BACK:
		{	strcat(pAnim, "backward");
			IdealActivity = ACT_SWOOP_BACK;
			break;
		}
	}
}

Vector CBuddy::GetSwoopDirection()
{	UTIL_MakeVectors( pev->v_angle );

	if(!m_Swoop.bActive)
	{	return gpGlobals->v_forward;
	}

	switch(m_Swoop.iSwoopDir)
	{
	case UP:
		{	return gpGlobals->v_up;
		}
	case DOWN:
		{	return -gpGlobals->v_up;
		}
	case LEFT:
		{	return -gpGlobals->v_right;
		}
	case RIGHT:
		{	return gpGlobals->v_right;
		}
	default:
	case FRONT:
		{	return gpGlobals->v_forward;
		}
	case BACK:
		{	return -gpGlobals->v_forward;
		}
	}

	return Vector(0,0,0);
}

bool CBuddy::CanTakeDamage(CBaseEntity *pEntity)
{
	if (ceasefire.value)
		return false;

	if (g_teamplay)
	{
		if ((pEntity != this) && (pEntity->pev->team == pev->team) && !friendlyfire.value)
			return false;
	}

	return true;
}

Vector CBuddy::GetGunPosition()
{
	Vector v_forward, v_right, v_up;
	UTIL_MakeVectorsPrivate(pev->angles, v_forward, v_right, v_up);

	Vector vecSrc = pev->origin 
					+ v_forward * m_HackedGunPos.y 
					+ v_right * m_HackedGunPos.x 
					+ v_up * m_HackedGunPos.z;

	return vecSrc;
}

void CBuddy::ApplyVelocity(Vector v_velocitydir, float flSpeed, float flDuration)
{
	m_fVelocitySpeed = flSpeed;
	m_vVelocityDir = v_velocitydir.Normalize();
	pev->velocity = v_velocitydir * flSpeed;
	m_fVelocityTime = gpGlobals->time + flDuration;
}

// this handles any forces which have been applied to us
void CBuddy::VelocityThink()
{
	if (m_fVelocityTime == 0.0f)
		return;

	if (m_fVelocityTime <= gpGlobals->time)
	{
		ResetVelocity();

		// Bud was thrown?
		if (m_bThrown)
			m_Toss.StopThrow();
	}
	else
	{
		if (!m_bFlying)	
			StartFly();
		m_fVelocitySpeed *= (1.0f-(BotManager.m_fFrameTime/1.5)); //(m_fVelocitySpeed - (BotManager.m_fFrameTime/(m_fVelocityTime-gpGlobals->time)));
		if (m_fVelocitySpeed >= 2000.0f)
		{
			ALERT(at_console, "Warning : m_fVelocitySpeed >= 2000\n");
			m_fVelocitySpeed = 1999.0f;
		}
		pev->velocity = m_vVelocityDir * m_fVelocitySpeed;
		if (pev->velocity.Length() >= 2000.0f)
		{
			char sz[128];
			sprintf(sz, "Warning : you kvelocity >= 2000 (%f, %f)\n", pev->velocity.Length(), m_fVelocitySpeed);
			ALERT(at_console, sz);
			m_vVelocityDir = m_vVelocityDir.Normalize();
			pev->velocity = pev->velocity.Normalize();
			sprintf(sz, "Fixed vel: %f, %f\n", pev->velocity.Length(), (m_vVelocityDir * m_fVelocitySpeed).Length());
			ALERT(at_console, sz);
		}
	}
}

void CBuddy::ResetVelocity()
{
	pev->velocity = m_vVelocityDir = g_vecZero;
	m_fVelocityTime = m_fVelocitySpeed = 0.0f;
}

char *CBuddy::GetCurrentWeaponName()
{
	if (m_pCurrentWeapon)
		return m_pCurrentWeapon->GetWeaponName();

	return NULL;
}

void CBuddy::SetCurrentWeapon(CBaseBuddyWeapon *pWeapon)
{
	if (m_pCurrentWeapon != pWeapon)
	{
		if (m_pCurrentWeapon)
			m_pCurrentWeapon->m_bActive = false;

		m_pCurrentWeapon = pWeapon;

		m_pCurrentWeapon->Activate();
		m_pCurrentWeapon->Deploy();
	}
}

void CBuddy::Die(CBaseEntity *pKiller, CBaseEntity *pInflictor)
{
	StopTurbo();
	StopPowerUp();

	SetThink(&CBuddy::DeathThink);
	m_fDeathTime = gpGlobals->time;
	pev->nextthink = gpGlobals->time;

	// send the max info so we can draw the bar
	MESSAGE_BEGIN( MSG_ONE, gmsgBuddyStatus, NULL, m_pOwner->pev); 
		WRITE_SHORT(0);

		WRITE_LONG(m_Owner_Ent_Index);
		WRITE_LONG(m_Buddy_Ent_Index);

		WRITE_LONG(pev->health);
		WRITE_LONG(m_fKi);
	MESSAGE_END();
	
	
	if (pKiller && pInflictor)
		g_pGameRules->BuddyDeathNotice( this, pKiller->pev, pInflictor->pev );

	if (pKiller)
	{
		CBasePlayer *pPlayer = NULL;
		if (pKiller->IsPlayer())
			pPlayer = (CBasePlayer *)pKiller;

		if ( pev == pKiller->pev )
		{  // killed self
			pKiller->pev->frags -= 1;
		}
		else if ( pPlayer )
		{
			// if a player dies in a deathmatch game and the killer is a client, award the killer some points
			if (g_teamplay && (pev->team == pKiller->pev->team))
				pPlayer->pev->frags -= 1;
			else
				pPlayer->pev->frags += 1;
		}
		else
		{  // killed by the world
			pKiller->pev->frags -= 1;
		}

		// killers score, if it's a player
		if ( pKiller->Classify() == CLASS_PLAYER )
		{
			CBasePlayer *PK = (CBasePlayer*)pKiller;

			MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
				WRITE_BYTE( ENTINDEX(PK->edict()) );
				WRITE_SHORT( PK->pev->frags );
				WRITE_SHORT( PK->m_iDeaths );
				//added by harSens
				WRITE_SHORT( PK->m_iWishes);
				/*modified by harSens
				WRITE_SHORT( 0 );
				*/		
				WRITE_SHORT( PK->pev->playerclass );
				WRITE_SHORT( PK->pev->team );
				WRITE_SHORT(PK->m_pClass->ScoreboardName());
			MESSAGE_END();

			// let the killer paint another decal as soon as he'd like.
			PK->m_flNextDecalTime = gpGlobals->time;
		}
	}
}

bool CBuddy::Follow()
{
	CBaseEntity *pTemp = m_pOwner;
	CBasePlayer *pBuddyOwner = (CBasePlayer *)pTemp;
	if (!pBuddyOwner) return 0;

	if (CanMove(pBuddyOwner))
	{		
		float flDist = UTIL_Distance(pev->origin, m_pOwner->pev->origin);

		if (!m_bFlying)
			StartFly();
		
		if (flDist < 75.0f)
		{
			// Wait
			SetMoveSpeed(0.0f);
		}
		else if (flDist < 100.0f)
		{
			// Walk to owner
			SetMoveSpeed(GetMaxSpeed()/2);
		}
		else
		{
			// Run to owner
			SetMoveSpeed(GetMaxSpeed());
		}

		if ((m_fMoveSpeed == GetMaxSpeed()) || (pBuddyOwner->m_fTurbo))
			StartTurbo();
		else
			StopTurbo();

		pev->velocity = GetMoveDirection().Normalize() * fabs(m_fMoveSpeed);

		pev->angles = m_pOwner->pev->angles;
		pev->v_angle = m_pOwner->pev->v_angle;
	}
	else
	{
        pev->velocity = Vector(0,0,0);
	}

	return true;
}

// ==================================================================
// Code of CBuddy - End
// ==================================================================

// ==================================================================
// Code of CBuddyToss - Begin
// ==================================================================

// Returns true if bud should be idle
bool CBuddyToss::Think()
{
	// Make sure we're being thrown
	if(!m_pOwner->m_bThrown)
	{
//		PrintAI("Toss state : none\n");
		return false;
	}

	switch(m_pOwner->m_Toss.m_eThrowStatus)
	{
	case THROW_TOSS:
		{	
			HandleToss();
//			PrintAI("Toss state : HandleToss\n");
			break;
		}
	case THROW_ROLL:
		{	
			HandleRoll();
//			PrintAI("Toss state : HandleRoll\n");
			break;
		}
	case THROW_WALLSTICK:
		{
			HandleWallStick();
//			PrintAI("Toss state : HandleWallStick\n");
			break;
		}
	case THROW_LAY:
		{	
			HandleLay();
//			PrintAI("Toss state : HandleLay\n");
			break;
		}
	case THROW_LANDRECOVER:
		{	
			HandleLandRecover();
//			PrintAI("Toss state : HandleLandRecover\n");
			break;
		}
	case THROW_RECOVER:
		{	
			SlowlyRecover();
//			PrintAI("Toss state : HandleRecover\n");
			break;
		}
	}

	return true;
}

void CBuddyToss::Init()
{
	m_vTossDir = g_vecZero;
	m_fCurSpeed = 0.0f;
	m_fMinSpeed = 0.0f;
	m_fDeacceleration = 0.0f;
	m_fMinRecoverTime = 0.0f;
	m_bFlyRecover = false;
	m_fLandRecoverStart = 0.0f;
	m_vLandRecoverAngles = g_vecZero;
	m_fLayStart = 0.0f;
	m_fStartWallStick = 0.0f;
	m_vWallStickNormal = g_vecZero;
	m_bOnGround = false;
	m_fLastRecoverTime = 0.0f;
	m_eThrowStatus = THROW_DISABLE;
}

void CBuddyToss::SetTossAnim(char *pAnim, Activity &IdealActivity)
{
	BOOL fStopThrow = FALSE;
	char szThrowAnim[256];

	switch(m_eThrowStatus)
	{
	case THROW_TOSS:
	case THROW_WALLSTICK:
		{
			IdealActivity = GetThrowActivity();
			//GetThrowAnimDirection( TRUE, szThrowAnim );
			GetThrowAnimDirection(0, GetThrowDirection(true), szThrowAnim);
			sprintf(pAnim, "hit_blow_%s", szThrowAnim);
			break;
		}
	case THROW_ROLL:
		{
			if(FBitSet(m_pOwner->pev->flags, FL_ONGROUND))
			{	IdealActivity = GetThrowActivity();
				//GetThrowAnimDirection( FALSE, szThrowAnim );
				GetThrowAnimDirection(0, GetThrowDirection(false), szThrowAnim);
				sprintf(pAnim, "tumble_%s", szThrowAnim);
			}
			else
			{	IdealActivity = GetThrowActivity();
				//GetThrowAnimDirection( TRUE, szThrowAnim );
				GetThrowAnimDirection(0, GetThrowDirection(false), szThrowAnim);
				sprintf(pAnim, "hit_blow_%s", szThrowAnim);
			}
			break;
		}
	case THROW_LAY:
		{
			// Lay
			if(m_fLayStart)
			{	if(m_pOwner->m_Activity == ACT_ROLL_BACK ||
					m_pOwner->LookupSequence( "lay_ground" ) == m_pOwner->pev->sequence)
				{	strcpy( pAnim, "lay_ground" );
				}
				else
				{	strcpy( pAnim, "lay_ground2" );
				}

				IdealActivity = GetThrowActivity();
			}
			// Recover
			else
			{	if(m_pOwner->m_Activity == GetThrowActivity() && m_pOwner->m_fSequenceFinished)
				{	fStopThrow = TRUE;
				}

				if(m_pOwner->LookupSequence( "lay_ground" ) == m_pOwner->pev->sequence ||
					m_pOwner->LookupSequence( "lay_ground_recover" ) == m_pOwner->pev->sequence)
				{	strcpy( pAnim, "lay_ground_recover" );
				}
				else
				{	strcpy( pAnim, "lay_ground_recover2" );
				}

				IdealActivity = GetThrowActivity();
			}
			break;
		}
	case THROW_LANDRECOVER:
		{	
			strcpy( pAnim, "throw_land" );
			IdealActivity = GetThrowActivity();
			break;
		}
	case THROW_RECOVER:
		{	
			if(m_pOwner->m_Activity == GetThrowActivity() && m_pOwner->m_fSequenceFinished)
			{	fStopThrow = TRUE;
			}

			// Recovering from roll
			if(!m_bFlyRecover)
			{	IdealActivity = GetThrowActivity();
				//GetThrowAnimDirection( FALSE, szThrowAnim );
				GetThrowAnimDirection(0, GetThrowDirection(false), szThrowAnim);
				sprintf(pAnim, "tumble_%s_recover", szThrowAnim);
			}
			// Recovering from blow
			else
			{	IdealActivity = GetThrowActivity();
				//GetThrowAnimDirection( TRUE, szThrowAnim );
				GetThrowAnimDirection(0, GetThrowDirection(true), szThrowAnim);
				sprintf( pAnim, "hit_blow_%s_recover", szThrowAnim );
			}
			break;
		}
	}

	if(fStopThrow)
	{	StopThrow();
	}
}

Activity CBuddyToss::GetThrowActivity( void )
{
	Activity activity;

	if(m_eThrowStatus == THROW_TOSS || m_eThrowStatus == THROW_WALLSTICK || 
		(m_eThrowStatus == THROW_ROLL && !FBitSet(m_pOwner->pev->flags, FL_ONGROUND)))
	{	switch(GetThrowDirection(TRUE))
		{
		case UP:
			{	activity = ACT_BLOWN_UP;
				break;
			}
		case DOWN:
			{	activity = ACT_BLOWN_DOWN;
				break;
			}
		case LEFT:
			{	activity = ACT_BLOWN_LEFT;
				break;
			}
		case RIGHT:
			{	activity = ACT_BLOWN_RIGHT;
				break;
			}
		case FRONT:
			{	activity = ACT_BLOWN_FRONT;
				break;
			}
		case BACK:
			{	activity = ACT_BLOWN_BACK;
				break;
			}
		}
	}
	else if(m_eThrowStatus == THROW_ROLL)
	{	switch(GetThrowDirection(FALSE))
		{
		case LEFT:
			{	activity = ACT_ROLL2_LEFT;
				break;
			}
		case RIGHT:
			{	activity = ACT_ROLL2_RIGHT;
				break;
			}
		case FRONT:
			{	activity = ACT_ROLL_FRONT;
				break;
			}
		case BACK:
			{	activity = ACT_ROLL_BACK;
				break;
			}
		}
	}
	else if(m_eThrowStatus == THROW_LAY)
	{	if(m_fLayStart)
		{	activity = ACT_LAY;
		}
		else
		{	activity = ACT_LAY_RECOVER;
		}
	}
	else if(m_eThrowStatus == THROW_LANDRECOVER)
	{	activity = ACT_LANDRECOVER;
	}
	else if(m_eThrowStatus == THROW_RECOVER)
	{	activity = ACT_ROLL_RECOVER;
	}

	return activity;
}

void CBuddyToss::GetThrowAnimDirection(int null, int iDirection, char *pAnim)
{	strcpy(pAnim,"");
	switch(iDirection)
	{
	case UP:
		{	strcpy(pAnim, "up");
			break;
		}
	case DOWN:
		{	strcpy(pAnim, "down");
			break;
		}
	case LEFT:
		{	strcpy(pAnim, "left");
			break;
		}
	case RIGHT:
		{	strcpy(pAnim, "right");
			break;
		}
	case FRONT:
		{	if((m_eThrowStatus == THROW_ROLL && FBitSet(m_pOwner->pev->flags, FL_ONGROUND)) ||
				(m_eThrowStatus == THROW_RECOVER && !m_bFlyRecover))
			{	strcpy(pAnim, "forward");
			}
			else
			{	strcpy(pAnim, "front");
			}
			break;
		}
	case BACK:
		{	strcpy(pAnim, "back");
			break;
		}
	}
}

int CBuddyToss::GetThrowDirection(bool bUseUpDown )
{	int iDirection;
	int iStart;
	float flLargestDot = 0;
	Vector vecDirections[6];

	UTIL_MakeVectors(m_pOwner->pev->v_angle);
	
	vecDirections[UP] = Vector(0,0,1);
	vecDirections[DOWN] = Vector(0,0,-1);
	vecDirections[LEFT] = -gpGlobals->v_right;
	vecDirections[RIGHT] = gpGlobals->v_right;
	vecDirections[FRONT] = gpGlobals->v_forward;
	vecDirections[BACK] = -gpGlobals->v_forward;

	if(!bUseUpDown)
		iStart = LEFT;
	else
		iStart = 0;

	for(int i=iStart;i<6;i++)
	{	float flDot;
		flDot = DotProduct (vecDirections[i], m_pOwner->pev->velocity);

		if(flDot > flLargestDot)
		{	flLargestDot = flDot;
			iDirection = i;
		}
	}

	return iDirection;
}

bool CBuddyToss::CheckForObsticales()
{
	TraceResult tr;
	Vector vecLOS;

	UTIL_TraceHull(m_pOwner->GetGunPosition(), m_pOwner->pev->origin + m_vTossDir * 3000, dont_ignore_monsters, human_hull, ENT(m_pOwner->pev), &tr);
	cout << "TraceHull at CheckForObsticales stopped after " << tr.flFraction * 3000 << " units\n";
	cout << "fAllSolid : " << tr.fAllSolid << " fStartSolid : " << tr.fStartSolid << "\n";

	// Traceline
	UTIL_TraceHull(m_pOwner->GetGunPosition(), m_pOwner->pev->origin + m_vTossDir * 32, dont_ignore_monsters, human_hull, ENT(m_pOwner->pev), &tr);
	
	// We hit something
	if(tr.flFraction < 1)
	{	
		CBaseEntity	*pHit = CBaseEntity::Instance( tr.pHit );

		// Hit map
		if(pHit->pev->solid == SOLID_BSP)
		{	float flPitch;
			float flDot;
			
			// Get Pitch + Dot
			flPitch = UTIL_VecToAngles(tr.vecPlaneNormal).x;
			flDot = DotProduct (m_vTossDir, -tr.vecPlaneNormal);

			// Limit Pitch
			if(flPitch < -180)
				flPitch += 360;
			else if(flPitch > 180)
				flPitch -= 360;

			// Stop when we hit a wall
			if(!m_bTakeDmgOnCollision)
			{	
				m_pOwner->m_Toss.StopThrow();
				return true;
			}

			// We were thrown directly into the ground, go strait to laying down
			if(flDot > 0.8 && flPitch < 115 && flPitch > 65 ) // +- 35 degrees
			{	MakeCreater();

				// Emit Sound
				EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/wallhit.wav", 1.0, ATTN_NORM);
				
				// Inflict damage
				if(!InflictWallhitDmg( true ))
				{	return true;
				}
				
				// If player is holding onto jump and has enough KI, do a ground recover
				if(/*(pev->button & IN_JUMP) && */m_pOwner->UseKi(MELEE_WALLSTICK_RECOVER_COST))
				{	// Ground Recover
					StartLandRecover();
				}
				else
				{	// Lay Down
					StartLay();
				}
				return true;
			}
			// Wallstick
			else if(flDot > VIEW_FIELD_NARROW && flPitch < 45 && flPitch > -45)	// +- 45 degrees
			{
				MakeCreater();

				// Emit Sound
				EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/wallhit.wav", 1.0, ATTN_NORM);

				// Emit Rocks
				EmitExplosionRocks(tr.vecEndPos, tr.vecPlaneNormal * 125, 50, 20, 20);

				// Inflict damage
				if(!InflictWallhitDmg( true ))
				{	return true;
				}

				// Wallstick
				StartWallStick(tr.vecEndPos, tr.vecPlaneNormal);
				return true;
			}
			// Change to roll
			else
			{	// Inflict damage
				if(!InflictWallhitDmg( false ))
				{	return true;
				}

				StartRoll();
				return true;
			}
		}
		// Hit Player
		// Check if we have a m_pThrower or m_pBuddyThrower
		else if( pHit->IsPlayer() && pHit->IsAlive() && pHit != m_pOwner->m_Melee.m_pThrower )
		{
			CBasePlayer *pPlayHit = (CBasePlayer *)pHit;
			if(!pPlayHit->m_Melee.m_fThrown)
			{
				if (m_pOwner->m_Melee.m_pThrower && m_pOwner->m_Melee.m_pThrower->CanMelee(pPlayHit))
				{
					EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/wallhit.wav", 1.0, ATTN_NORM);
					pPlayHit->m_Melee.StartToss( m_pOwner->m_Melee.m_pThrower, true, m_vTossDir, m_fCurSpeed * 1.15, m_fMinSpeed, m_fDeacceleration * 0.85, 1.5);
				}
				else if (m_pOwner->m_Melee.m_pBuddyThrower && m_pOwner->m_Melee.m_pBuddyThrower->m_Melee.CanMelee(pPlayHit))
				{
					EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/wallhit.wav", 1.0, ATTN_NORM);
					pPlayHit->m_Melee.StartToss( m_pOwner->m_Melee.m_pBuddyThrower, true, m_vTossDir, m_fCurSpeed * 1.15, m_fMinSpeed, m_fDeacceleration * 0.85, 1.5);
				}
			}
		}
		// else check if hit buddy
		else if (tr.pHit && FStrEq(STRING(tr.pHit->v.classname), "player_buddy"))
		{
			CBuddy *pBuddy = (CBuddy *)CBaseEntity::Instance(tr.pHit);
			if (pBuddy && pBuddy->IsAlive() && !pBuddy->m_bThrown)
			{
				if (m_pOwner->m_Melee.m_pThrower && m_pOwner->m_Melee.m_pThrower->CanMelee(pBuddy))
				{
					EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/wallhit.wav", 1.0, ATTN_NORM);
					pBuddy->m_Toss.StartToss( m_pOwner->m_Melee.m_pThrower, true, m_vTossDir, m_fCurSpeed * 1.15, m_fMinSpeed, m_fDeacceleration * 0.85, 1.5);
				}
				else if (m_pOwner->m_Melee.m_pBuddyThrower && m_pOwner->m_Melee.m_pBuddyThrower->m_Melee.CanMelee(pBuddy))
				{	EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/wallhit.wav", 1.0, ATTN_NORM);
					pBuddy->m_Toss.StartToss( m_pOwner->m_Melee.m_pBuddyThrower, true, m_vTossDir, m_fCurSpeed * 1.15, m_fMinSpeed, m_fDeacceleration * 0.85, 1.5);
				}
			}
		}
	}

	return false;
}

bool CBuddyToss::Deaccelerate( void )
{
	float flFrameTime = BotManager.m_fFrameTime;
	m_fCurSpeed -= m_fDeacceleration * flFrameTime;
	
	if(m_fCurSpeed < 0)
		m_fCurSpeed = 0;

	if(m_fCurSpeed >= m_fMinSpeed)
		return false;
	else
		return true;
}

void CBuddyToss::HandleToss( void )
{
	if(Deaccelerate())
	{	m_pOwner->m_Toss.StopThrow();
		return;
	}

	if(SlowlyRecover())
	{	return;
	}

	if(CheckForObsticales())
	{	return;
	}

	m_pOwner->ApplyVelocity(m_vTossDir, m_fCurSpeed, 1.0f);
}

void CBuddyToss::HandleRoll()
{
	CheckCreater();

	// We stopped moving, enable lay
	if(m_pOwner->pev->velocity.Length() < 10 && FBitSet(m_pOwner->pev->flags, FL_ONGROUND))
	{	
		StartLay();
		return;
	}

	SlowlyRecover();
}

void CBuddyToss::HandleWallStick()
{	
	// Keep fixed velocity
	m_pOwner->ResetVelocity();
	//m_pAttacker->pev->movetype = MOVETYPE_NONE;

	// Can't unstick yet
	if(m_fStartWallStick > gpGlobals->time - melee_wallstick_mintime.value)
	{	
		return;
	}

	// We have jump pushed down, lets try to recover
	//if(m_pAttacker->pev->button & IN_JUMP)
	{	// Recover
		if(m_pOwner->UseKi(MELEE_WALLSTICK_RECOVER_COST))
		{	Vector vecWall = m_vWallStickNormal;
			// Stop Throw
			m_pOwner->m_Toss.StopThrow();

			// Set velocity
			UTIL_MakeVectors( vecWall );
			//m_pOwner->pev->velocity = (vecWall*0.65 + gpGlobals->v_up*0.35) * 1200;
			m_pOwner->ApplyVelocity(vecWall*0.65 + gpGlobals->v_up*0.35, 1200, 1.0f);

			// Set animation
			//SetAnimPriority( ANIM_PRIORITY_LOW, AddAnimationAFix(vecWall, false, gpGlobals->time, "wall_recover"));

			// Play sound
			EMIT_SOUND(ENT(m_pOwner->pev), CHAN_BODY, "weapons/jump.wav", 1, ATTN_NORM);

			return;
		}
	}

	// We cant recover, fall from the wall
	if(m_fStartWallStick + melee_wallstick_maxtime.value < gpGlobals->time)
	{	// Start Roll
		StartRoll();

		// Set blowtime
		m_fMinRecoverTime += gpGlobals->time - m_fStartWallStick;

		// Set velocity
		m_pOwner->pev->velocity = m_vWallStickNormal * 100;

		// Reset wallstick vars
		m_vWallStickNormal = Vector(0,0,0);
		m_fStartWallStick = 0;

		// Unset fixed angles
		//UTIL_Model_RemoveAll(m_pAttacker->entindex());

		// Set Falling off animation
		//RemoveAllAnims();

		// HACK HACK: Gives player a little time so he can't be hit as soon as he falls off the wall
		m_fLastRecoverTime = gpGlobals->time;
	}
}

void CBuddyToss::HandleLay()
{	// Time to recover
	if(m_fLayStart + TOSS_LAYTIME < gpGlobals->time)
	{	m_fLayStart = 0;
	}
}

void CBuddyToss::HandleLandRecover()
{	// Time to recover
	if(m_fLandRecoverStart + LANDRECOVER_TIME < gpGlobals->time)
	{	m_fLandRecoverStart = 0;

		// Stop throw
		m_pOwner->m_Toss.StopThrow();

		// Recover
		LandRecoverDash();
	}
}

bool CBuddyToss::FullRecover( void )
{
	bool bRecover = false;

	// We're allowed to recover now
	if(m_pOwner->m_Toss.m_eThrowStatus != THROW_RECOVER)
	{	
		if(m_fMinRecoverTime < gpGlobals->time)
		{	
			// Higher KI cost for off ground
			if(FBitSet(m_pOwner->pev->flags, FL_ONGROUND))
			{
				if(m_pOwner->UseKi(MELEE_RECOVER_COST/2.5))
				{	
					bRecover = true;
					return true;
				}
			}
			else
			{
				if(m_pOwner->UseKi(MELEE_RECOVER_COST))
				{
					bRecover = true;
					return true;
				}
			}
		}
	}
	else
	{
		if(m_bFlyRecover)
		{	Deaccelerate();
			//ApplyVelocity();
		}
	}

	if (bRecover)
	{
		if(m_pOwner->m_Toss.m_eThrowStatus == THROW_TOSS)
		{
			m_bFlyRecover = true;
			m_fCurSpeed *= 0.5;	// 1/2 speed
			m_fDeacceleration *= 2; // Double Deacceleration rate
			//m_flFrameTime = gpGlobals->time; // Set frame time
			EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/airrecover.wav", 1.0, ATTN_NORM);
		}
		else
		{	
			m_bFlyRecover = false;
			EMIT_SOUND(ENT(m_pOwner->pev), CHAN_WEAPON, "weapons/groundrecover.wav", 1.0, ATTN_NORM);
		}

		m_pOwner->m_Toss.m_eThrowStatus = THROW_RECOVER;
	}
	return false;
}

bool CBuddyToss::SlowlyRecover()
{
	// We're allowed to recover now
	if(m_pOwner->m_Toss.m_eThrowStatus != THROW_RECOVER)
	{
		if(m_fMinRecoverTime < gpGlobals->time)
		{
			// Pressing Primary/Secondary or jump
			//if((iButton & IN_ATTACK) || (iButton & IN_ATTACK2) || (iButton & IN_JUMP))
			{	
				// Higher KI cost for off ground
				if(FBitSet(m_pOwner->pev->flags, FL_ONGROUND))
				{	
					if(m_pOwner->UseKi(MELEE_RECOVER_COST/2.5))
					{
						FullRecover();
						return true;
					}
				}
				else
				{
					if(m_pOwner->UseKi(MELEE_RECOVER_COST))
					{	
						FullRecover();
						return true;
					}
				}
			}
		}
	}
	else
	{	
		if(m_bFlyRecover)
		{	
			Deaccelerate();
			//ApplyVelocity();
		}
	}
	return false;
}

void CBuddyToss::StartToss( CBasePlayer *pThrower, bool fDmgOnCollision, Vector vecDirection, float flMaxSpeed, float flMinSpeed, float flDeacceleration, float flMinRecoverTime )
{	if(!pThrower || !m_pOwner->IsAlive())
	{	return;
	}

	if(m_pOwner->m_bThrown)
	{	int iHits = m_pOwner->m_Melee.m_iConsecutiveHits;
		m_pOwner->m_Toss.StopThrow();
		m_pOwner->m_Melee.m_iConsecutiveHits = iHits;
		m_pOwner->m_Melee.m_iConsecutiveHits++;
	}
	else
	{	m_pOwner->m_Melee.m_iConsecutiveHits = 1;
	}

	// Disable Special Modes
	if(m_pOwner->m_Swoop.bActive)
	{	m_pOwner->StopSwoop();
	}/*
	if (m_pAttacker->m_fAscending)
	{	m_pAttacker->Descend( true );
	}*/
	if (m_pOwner->m_bPowerUp)
	{	m_pOwner->StopPowerUp();
	}/*
	if (m_pAttacker->m_fFlying)
	{	m_pAttacker->StopFly();
	}*/
	if(m_pOwner->m_bTurbo)
	{	m_pOwner->StopTurbo();
	}/*
	if(m_pAttacker->m_fWallJump)
	{	m_pAttacker->RemoveWallJump();
	}
	if(m_pAttacker)
	{	CBasePlayerWeapon *pWeapon = (CBasePlayerWeapon*)m_pAttacker->m_pActiveItem;
		if(pWeapon && (pWeapon->HolsterWhenMeleed()))
		{	// HACK HACK: Safely holster enemys weapon
			pWeapon->Holster();
			pWeapon->Deploy();
		}
	}*/
/* UNDONE
	// Set Varibles*/
	m_pOwner->m_Melee.m_pThrower = pThrower;
	m_bTakeDmgOnCollision = fDmgOnCollision;
	m_pOwner->m_bThrown = true;
	m_pOwner->m_Toss.m_eThrowStatus = THROW_TOSS;
	m_vLandRecoverAngles = Vector(0,0,0);/*
	m_flFrameTime = gpGlobals->time;

	m_flMaxSpeed = flMaxSpeed;*/
	m_fCurSpeed = flMaxSpeed;
	m_fMinSpeed = flMinSpeed;
	m_fDeacceleration = flDeacceleration;
	m_fMinRecoverTime = gpGlobals->time + flMinRecoverTime;/*

	// Set movetype
	m_pAttacker->m_fMelee = true;
	m_pAttacker->pev->movetype = MOVETYPE_PLAYERTOSS;
	m_pAttacker->pev->gravity = 1;
*/
	// Set Velocity
	m_vTossDir = vecDirection.Normalize();
	//m_pAttacker->pev->vuser4 = m_vecDirection * m_fCurSpeed;
	m_pOwner->ApplyVelocity(m_vTossDir, flMaxSpeed, 1.0f);

	// Remove Anims
	/*RemoveAllAnims();*/
}

void CBuddyToss::StartToss( CBuddy *pThrower, bool fDmgOnCollision, Vector vecDirection, float flMaxSpeed, float flMinSpeed, float flDeacceleration, float flMinRecoverTime )
{	if(!pThrower || !m_pOwner->IsAlive())
	{	return;
	}

	if(m_pOwner->m_bThrown)
	{	int iHits = m_pOwner->m_Melee.m_iConsecutiveHits;
		m_pOwner->m_Toss.StopThrow();
		m_pOwner->m_Melee.m_iConsecutiveHits = iHits;
		m_pOwner->m_Melee.m_iConsecutiveHits++;
	}
	else
	{	m_pOwner->m_Melee.m_iConsecutiveHits = 1;
	}

	// Disable Special Modes
	if(m_pOwner->m_Swoop.bActive)
	{	m_pOwner->StopSwoop();
	}/*
	if (m_pAttacker->m_fAscending)
	{	m_pAttacker->Descend( true );
	}*/
	if (m_pOwner->m_bPowerUp)
	{	m_pOwner->StopPowerUp();
	}/*
	if (m_pAttacker->m_fFlying)
	{	m_pAttacker->StopFly();
	}*/
	if(m_pOwner->m_bTurbo)
	{	m_pOwner->StopTurbo();
	}/*
	if(m_pAttacker->m_fWallJump)
	{	m_pAttacker->RemoveWallJump();
	}
	if(m_pAttacker)
	{	CBasePlayerWeapon *pWeapon = (CBasePlayerWeapon*)m_pAttacker->m_pActiveItem;
		if(pWeapon && (pWeapon->HolsterWhenMeleed()))
		{	// HACK HACK: Safely holster enemys weapon
			pWeapon->Holster();
			pWeapon->Deploy();
		}
	}*/
/* UNDONE
	// Set Varibles*/
	m_pOwner->m_Melee.m_pBuddyThrower = pThrower;
	m_bTakeDmgOnCollision = fDmgOnCollision;
	m_pOwner->m_bThrown = true;
	m_pOwner->m_Toss.m_eThrowStatus = THROW_TOSS;
	m_vLandRecoverAngles = Vector(0,0,0);/*
	m_flFrameTime = gpGlobals->time;

	m_flMaxSpeed = flMaxSpeed;*/
	m_fCurSpeed = flMaxSpeed;
	m_fMinSpeed = flMinSpeed;
	m_fDeacceleration = flDeacceleration;
	m_fMinRecoverTime = gpGlobals->time + flMinRecoverTime;
/*
	// Set movetype
	m_pAttacker->m_fMelee = true;
	m_pAttacker->pev->movetype = MOVETYPE_PLAYERTOSS;
	m_pAttacker->pev->gravity = 1;
*/
	// Set Velocity
	m_vTossDir = vecDirection.Normalize();
	//m_pAttacker->pev->vuser4 = m_vecDirection * m_fCurSpeed;
	m_pOwner->ApplyVelocity(m_vTossDir, flMaxSpeed, 1.0f);

	// Remove Anims
/*	RemoveAllAnims();*/
}

void CBuddyToss::MakeCreater( void )
{
	Vector vecDirection;

	// Get Direction
	if(m_pOwner->m_Toss.m_eThrowStatus == THROW_TOSS)
		vecDirection = m_vTossDir;
	else
		vecDirection = (m_pOwner->pev->velocity.Normalize() + Vector(0,0,-1)).Normalize();
	
	// Create imprint
	TraceResult tr;
	Vector vecEnd = m_pOwner->pev->origin + vecDirection * 128;
	UTIL_TraceLine (m_pOwner->pev->origin, vecEnd, ignore_monsters, ENT(m_pOwner->pev), &tr );
	
	if(tr.flFraction != 1 && CBaseEntity::Instance(tr.pHit)->pev->solid == SOLID_BSP)
	{	UTIL_DecalTrace( &tr, DECAL_GARGSTOMP1 );
	}
}

bool CBuddyToss::InflictWallhitDmg(bool bObjectStick)
{
	float flVelocityDamage = 1 + m_fCurSpeed / 1000;

	// Check if we have a m_pThrower or m_pBuddyThrower
	if (m_pOwner->m_Melee.m_pThrower)
		m_pOwner->TakeDamage(m_pOwner->m_Melee.m_pThrower->pev, m_pOwner->m_Melee.m_pThrower->pev, ((int)(BLOW_KICK_DAMAGE/2 * flVelocityDamage) * (bObjectStick ? 0.5 : 1)), DMG_KICK);
	else if (m_pOwner->m_Melee.m_pBuddyThrower)
		m_pOwner->TakeDamage(m_pOwner->m_Melee.m_pBuddyThrower->pev, m_pOwner->m_Melee.m_pBuddyThrower->pev, ((int)(BLOW_KICK_DAMAGE/2 * flVelocityDamage) * (bObjectStick ? 0.5 : 1)), DMG_KICK);

	if(m_pOwner->IsAlive())
	{	return TRUE;
	}

	return FALSE;
}

void CBuddyToss::StartLandRecover( void )
{
	// Set movetype
	//m_pAttacker->pev->movetype = MOVETYPE_NONE;
	m_pOwner->m_fSlowDown = 0;
	//m_pAttacker->pev->friction = 1;
	m_pOwner->pev->gravity = 1;
	m_pOwner->ResetVelocity();

	// Set land recover time
	m_fLandRecoverStart = gpGlobals->time;
	m_pOwner->m_Toss.m_eThrowStatus = THROW_LANDRECOVER;

	// Fix Angles
	m_vLandRecoverAngles = m_pOwner->pev->angles;
	m_vLandRecoverAngles.x = 0;
	//UTIL_Model_FixAngles( m_pOwner->entindex(), m_vLandRecoverAngles );
}

void CBuddyToss::StartLay()
{
	// Set movetype
	//m_pAttacker->pev->movetype = MOVETYPE_NONE;
	m_pOwner->m_fSlowDown = 0;
	m_pOwner->pev->friction = 1;
	//m_pOwner->->pev->gravity = 1;
	m_pOwner->StopFly();
	m_pOwner->ResetVelocity();

	// Set Lay
	m_fLayStart = gpGlobals->time;
	m_pOwner->m_Toss.m_eThrowStatus = THROW_LAY;

	// Fix Angles
/*	Vector vecAngles = m_pAttacker->pev->angles;
	vecAngles.x = 0;
	UTIL_Model_FixAngles( m_pAttacker->entindex(), vecAngles );*/
}

void CBuddyToss::StartWallStick(Vector vecOrigin, Vector vecNormal)
{
	// Set Varibles
	//m_pAttacker->pev->movetype = MOVETYPE_NONE;
	//m_pAttacker->pev->gravity = 0.0000001;
	m_pOwner->StartFly();
	m_pOwner->m_fSlowDown = 0;

	m_pOwner->m_Toss.m_eThrowStatus = THROW_WALLSTICK;
	m_vWallStickNormal = vecNormal;
	m_fStartWallStick = gpGlobals->time;

	// Set origin + velocity
	//UTIL_SetOrigin(m_pOwner->pev, vecOrigin);
	UTIL_MoveToOrigin(m_pOwner->edict(), vecOrigin, UTIL_Distance(m_pOwner->pev->origin, vecOrigin), MOVE_STRAFE);
	m_pOwner->ResetVelocity();
	
	// Set animation
	//AddAnimationAFix(vecNormal, true, gpGlobals->time, "wall_collide");
	// HACK HACK: Can't have varible length arguments on afix function, so manually add next anim
	//AddToCurAnim(true, "wall_idle");
}

void CBuddyToss::StartRoll( void )
{	// Set Movetype info
	//m_pAttacker->pev->movetype = MOVETYPE_STEP;
	m_pOwner->pev->friction = 0.3;
	m_pOwner->StopFly();
	m_pOwner->pev->gravity = 0.75;
	m_pOwner->m_fSlowDown = 0.0;

	m_pOwner->m_Toss.m_eThrowStatus = THROW_ROLL;
	m_bOnGround = false;

	// Hardcode consecutive hits to 1, allowing for one more
	m_pOwner->m_Melee.m_iConsecutiveHits = 1;
}

void CBuddyToss::EmitExplosionRocks( Vector vecSrc, Vector vecVelocity, float flSize, int iRandom, int iCount )
{
	MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY);
		WRITE_BYTE(TE_BREAKMODEL);		// box of models or sprites
		WRITE_COORD( vecSrc.x );		// coord, coord, coord (position)
		WRITE_COORD( vecSrc.y );
		WRITE_COORD( vecSrc.z );
		WRITE_COORD( flSize );				// coord, coord, coord (size)
		WRITE_COORD( flSize );
		WRITE_COORD( flSize );
		WRITE_COORD( vecVelocity.x );	// coord, coord, coord (velocity)
		WRITE_COORD( vecVelocity.y );
		WRITE_COORD( vecVelocity.z );
		WRITE_BYTE(iRandom);					// byte (random velocity in 10's)
		WRITE_SHORT(g_sRockGibs);		// short (sprite or model index)
		WRITE_BYTE(iCount);				// byte (count)
		WRITE_BYTE(50);					// byte (life in 0.1 secs)
		WRITE_BYTE(0);					// byte (flags)
	MESSAGE_END();
}

void CBuddyToss::CheckCreater()
{
	if(FBitSet(m_pOwner->pev->flags, FL_ONGROUND) && !m_bOnGround)
	{	m_bOnGround = true;
		MakeCreater();
		return;
	}
	
	if(m_bOnGround && !FBitSet(m_pOwner->pev->flags, FL_ONGROUND))
	{	m_bOnGround = false;
	}
}

void CBuddyToss::LandRecoverDash()
{	
	UTIL_MakeVectors( m_vLandRecoverAngles );
	Vector vecHop = Vector(0,0,0);
	BOOL fJump = FALSE;
	int iDirection = 0;
	//int iButton = m_pAttacker->pev->button;
	// UNDONE
	int iButton = IN_MOVERIGHT;

	if((iButton & IN_MOVELEFT) || (iButton & IN_LEFT))
	{	vecHop = -gpGlobals->v_right;
		iDirection = LEFT;
	}
	else if((iButton & IN_MOVERIGHT) || (iButton & IN_RIGHT))
	{	vecHop = gpGlobals->v_right;
		iDirection = RIGHT;
	}
	else if(iButton & IN_FORWARD)
	{	vecHop = gpGlobals->v_forward;
		iDirection = FRONT;
	}
	else if(iButton & IN_BACK)
	{	vecHop = -gpGlobals->v_forward;
		iDirection = BACK;
	}
	else if(iButton & IN_JUMP)
	{	fJump = TRUE;
		vecHop = Vector(0,0,1);
		iDirection = UP;
	}
	// No directions pressed
	else
	{	return;
	}

	// Slight Z
	if(!fJump)
	{	vecHop.z = 0;
		vecHop = vecHop.Normalize();
	}

	// Set Velocity
	m_pOwner->ApplyVelocity(vecHop, LANDRECOVER_HOP_FORCE, 1.0f);
	/* UNDONE
	vecHop = vecHop * LANDRECOVER_HOP_FORCE;
	if(!fJump)
		vecHop.z += 500;
	m_pAttacker->pev->velocity = vecHop;*/
	m_pOwner->StopFly();

	/* UNDONE
	// Add Animation
	char szAnim[256];
	char szThrow[256];
	if(!fJump)
	{	GetThrowAnimDirection( 0, iDirection, szThrow );
		sprintf(szAnim,"throw_recover_%s", szThrow);
		if(!strcmp(szAnim,"throw_recover_front"))
			strcpy(szAnim,"throw_recover_forward");
		else if(!strcmp(szAnim,"throw_recover_back"))
			strcpy(szAnim,"throw_recover_backward");
	}
	else
	{	strcpy(szAnim,"throw_recover_jump");
	}*/

	UTIL_MakeVectors( m_vLandRecoverAngles );
	//AddAnimationAFix( gpGlobals->v_forward, false, gpGlobals->time, szAnim);
	m_vLandRecoverAngles = Vector(0,0,0);

	// Play sound
	EMIT_SOUND(ENT(m_pOwner->pev), CHAN_BODY, "weapons/recoverjump.wav", 1, ATTN_NORM);
}

void CBuddyToss::StopThrow()
{
	BOOL fStartFly = TRUE;
	if(m_eThrowStatus == THROW_WALLSTICK)
	{	fStartFly = FALSE;
	}
/*
	// Enable invulnerability
	if(m_fEndAdvMeleeToss && !fMultiHit)
	{	m_fInvulnerable = TRUE;
		m_pInvulException = m_pThrower;
		m_flInvulnerableTime = gpGlobals->time + INVUL_RECOVER_TIME;
	}
*/
	// Remove all animations
//	RemoveAllAnims();

	// Reset Varibles
	m_pOwner->m_Melee.m_pThrower = NULL;
	m_pOwner->m_Melee.m_pBuddyThrower = NULL;
	m_bTakeDmgOnCollision = false;
	m_pOwner->m_bThrown = false;
	m_bFlyRecover = false;
//	m_fEndAdvMeleeToss = FALSE;
//	m_pAttacker->m_fMelee = FALSE;
	m_eThrowStatus = THROW_DISABLE;

//	m_flMaxSpeed = 0;
	m_fCurSpeed = 0.0f;
	m_fMinSpeed = 0.0f;
	m_fDeacceleration = 0.0f;
	m_fMinRecoverTime = 0;
	m_fStartWallStick = 0;
	m_pOwner->m_Melee.m_iConsecutiveHits = 0;
	m_fLastRecoverTime = gpGlobals->time;
	m_vWallStickNormal = Vector(0,0,0);

	// Set movetype
	m_pOwner->m_fSlowDown = 1;
	m_pOwner->pev->friction = 1;
	m_pOwner->pev->gravity = 1;
/*	if(!fStartFly || FBitSet(m_pAttacker->pev->flags, FL_ONGROUND))
	{	m_pOwner->pev->movetype = MOVETYPE_WALK;
	}
	else
	{	m_pAttacker->StartFly();
	}*/

	// Set Velocity
	m_pOwner->ResetVelocity();
	//m_pOwner->pev->vuser4 = Vector(0,0,0);
}


// ==================================================================
// Code of CBuddyToss - End
// ==================================================================
