#include "gen_bot_header.h"
#include "magicattack.h"
#include "explodingattack.h"
#include "beamattack.h"
#include "guidedattack.h"
#include "controlledbeam.h"
//harSens: Linux compile fix
extern CLeftOutput lcout;
// ==================================================================
// Code of CBot - Begin
// ==================================================================

void CBot::MakeCombo(EComboType CT)
{
	if (m_pBotCombo)
		delete m_pBotCombo;

	MakeTask(TaskCombo);
	m_pBotCombo = new CBotCombo(this, CT);
	
	return;
}

void CBot::DeleteCombos()
{
	if (m_pBotCombo)
	{
		delete m_pBotCombo;
		m_pBotCombo = NULL;
	}
}

bool CBot::MakeTeleMelee(CBasePlayer *pPlayer)
{
	float flEnemyDist = GetDistance(pPlayer->pev->origin);
	edict_t *pEdict = edict();
	TMultiChoice<EComboType> TeleMeleeChoices;

	// Only check for this if enemy isn't very close
	if (flEnemyDist > 450.0f)
	{
		if (GetCollisionDist(dir_left, 650.0f) >= 650.0f)
			TeleMeleeChoices.Insert(COMBO_TYPE_TELE_LEFT_MELEE, 40);
		if (GetCollisionDist(dir_right, 650.0f) >= 650.0f)
			TeleMeleeChoices.Insert(COMBO_TYPE_TELE_RIGHT_MELEE, 40);
		if (GetCollisionDist(dir_back, 650.0f) >= 650.0f)
			TeleMeleeChoices.Insert(COMBO_TYPE_TELE_BACK_MELEE, 40);
	}

	// More chance for up & down teleport because it confuses the enemy more :)
	if (GetCollisionDist(dir_up, 650.0f) >= 650.0f)
		TeleMeleeChoices.Insert(COMBO_TYPE_TELE_UP_MELEE, 75);
	
	if (GetCollisionDist(dir_down, 650.0f) >= 650.0f)
		TeleMeleeChoices.Insert(COMBO_TYPE_TELE_DOWN_MELEE, 75);

	if (GetCollisionDist((dir_left | dir_back), 650.0f) >= 650.0f)
		TeleMeleeChoices.Insert(COMBO_TYPE_TELE_LEFT_BACK_MELEE, 40);

	if (GetCollisionDist((dir_right | dir_back), 650.0f) >= 650.0f)
		TeleMeleeChoices.Insert(COMBO_TYPE_TELE_RIGHT_BACK_MELEE, 40);

	// Only add these combos on large distances
	// (otherwise the bot probably won't surprise his enemy at all...)
	if (flEnemyDist < 600.0f)
	{
		if (GetCollisionDist((dir_left | dir_forward), 650.0f) >= 650.0f)
			TeleMeleeChoices.Insert(COMBO_TYPE_TELE_FORWARD_LEFT_MELEE, 45);

		if (GetCollisionDist((dir_right | dir_forward), 650.0f) >= 650.0f)
			TeleMeleeChoices.Insert(COMBO_TYPE_TELE_FORWARD_RIGHT_MELEE, 45);
	}

	EComboType ComboChoice;
	if (TeleMeleeChoices.GetSelection(ComboChoice))
	{
		m_pBotEnemy = pPlayer;
		MakeCombo(ComboChoice);
		return true;
	}

	return false;
}

// ==================================================================
// Code of CBot - End
// ==================================================================

// ==================================================================
// Code of BotComboEntries - Begin
// ==================================================================

void CTeleportComboEntry::Think()
{
	if ((!m_pBot->m_pBotEnemy) && (m_bNeedEnemy))
		m_eComboEntryState = DONE;
	else if ((m_fTeleDelay <= gpGlobals->time) ||
			 (m_pBot->m_sDangerLevel >= DANGER_HEAVY_MIN_VAL))
	{
		m_pBot->Teleport();
		m_eComboEntryState = DONE;
	}
	else
	{
		if (m_pBot->m_pBotEnemy)
		{
			if (m_bAimAtEnemy)
				m_pBot->AimAtEnemy(m_pBot->m_pBotEnemy);

			if ((m_bStopWhenEnemySwoops) &&
				(m_pBot->m_pBotEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP))
			{
				m_eComboEntryState = DONE;
				return;
			}
		}

		m_pBot->SetMoveDir(m_iTeleDir);
	}
}

CStartSwoopComboEntry::CStartSwoopComboEntry(CBot *pB, bool Tapped, int SwoopDir)
											: CBaseBotComboEntry(pB), m_bTappedOnce(Tapped),
											  m_iSwoopDir(SwoopDir)
{
}

void CStartSwoopComboEntry::Think()
{
	if ((!m_pBot->m_pBotEnemy) && (m_pBot->m_sSwoopFlags != SWOOP_GOTODBGOAL))
	{
		m_eComboEntryState = DONE;
		return;
	}

	if (!m_pBot->m_sSwoopFlags)
	{
		if (m_pBot->m_Swoop.m_iSwoopDirection == FRONT)
			m_pBot->m_sSwoopFlags = SWOOP_ATTACK;
		else
			m_pBot->m_sSwoopFlags = SWOOP_AVOIDDANGER;
	}

	m_pBot->ResetMoveSpeed();
	m_pBot->SetMoveDir(m_iSwoopDir);

	// Bot swoops already?
	if (m_pBot->pev->movetype == MOVETYPE_PLAYERSWOOP)
	{
		MELEE_DIRECTION iDesiredSwoopDir;

		if (m_iSwoopDir == dir_forward)
			iDesiredSwoopDir = FRONT;
		else if (m_iSwoopDir == dir_back)
			iDesiredSwoopDir = BACK;
		else if (m_iSwoopDir == dir_up)
			iDesiredSwoopDir = UP;
		else if (m_iSwoopDir == dir_down)
			iDesiredSwoopDir = DOWN;
		else if (m_iSwoopDir == dir_left)
			iDesiredSwoopDir = LEFT;
		else if (m_iSwoopDir == dir_right)
			iDesiredSwoopDir = RIGHT;

		if ((m_pBot->m_Swoop.m_iSwoopDirection == iDesiredSwoopDir) &&
			(m_pBot->m_Swoop.GetSwoopTime() > 0.2f))
		{
			// Bot swoops at the desired dir so done
			m_eComboEntryState = DONE;
			return;
		}
		else // the bot is probably trying to make a chain swoop
		{
			// So just set the swoop button of the current swoop dir if the bot isnt done tapping
			//if ((m_fLifeTime /*- 0.15f*/) > gpGlobals->time)
			{
		//		m_pBot->SetSwoopButton();
			}
		}
	}

	if (m_pBot->m_pBotEnemy)
		m_pBot->AimAtEnemy(m_pBot->m_pBotEnemy);

	if (((m_fLifeTime <= gpGlobals->time) && ((m_pBot->m_Swoop.GetSwoopTime() > 0.2f) || (m_pBot->pev->movetype != MOVETYPE_PLAYERSWOOP))) ||
		((m_pBot->m_sDangerLevel >= DANGER_HEAVY_MIN_VAL) && 
		 (m_pBot->pev->movetype != MOVETYPE_PLAYERSWOOP)))
		m_eComboEntryState = DONE;
}

float CStartSwoopComboEntry::GetDelay()
{/*
	if (m_pBot->pev->movetype == MOVETYPE_PLAYERSWOOP)
	{
		m_pBot->ResetMoveSpeed();

		// Bot is already swooping, so he probably tries to chain swoop. Set the right swoop
		// button so he won't stop the swoop

		m_pBot->SetSwoopButton();
		//return 0.0f; // No delay
	}*/
	
	if (m_bTappedOnce)
		return TAP_DELAY;
	else
		return SWOOP_BEGIN_DELAY;
}

void CStartSwoopComboEntry::Activate()
{
	CBaseBotComboEntry::Activate();
	if (m_bTappedOnce)
		m_fLifeTime = gpGlobals->time + 0.6f;
	else
		m_fLifeTime = gpGlobals->time + 0.07f;
}

void CFallDownComboEntry::Think()
{
	if (m_pBot->m_fFlying)
		m_pBot->StopFly();

	m_pBot->m_bShouldFly = false;
	m_pBot->m_fCheckFlyDelay = gpGlobals->time + 1.2;

	if ((m_fLifeTime < gpGlobals->time) || (m_pBot->pev->flags & FL_ONGROUND))
		m_eComboEntryState = DONE;
}

void CPowerupComboEntry::Think()
{
	if (!m_pBot->BotCanPowerUp())
	{
		// Can't powerup so nu use to try it
		m_eComboEntryState = DONE;
		return;
	}

	if (!m_pBot->m_fPowerUp && RANDOM_LONG(0, 1))
		m_pBot->StartPowerUp();

	short MinKiLevel = 990;

	if (m_pBot->m_pBotEnemy)
	{/*
		float flDist = m_pBot->GetDistance(m_pBot->m_pBotEnemy->pev->origin);
		if (flDist < 1250.0f)
		{
			switch (m_pBot->m_pPersonality->aggression_level)
			{
			case Aggressive:
				MinKiLevel = 700;
				break;
			case Normal:
				MinKiLevel = 750;
				break;
			case Defensive:
				MinKiLevel = 800;
				break;
			}
		}*/

		m_pBot->AimAtEnemy(m_pBot->m_pBotEnemy);
	}

	if (m_pBot->m_sDangerLevel >= DANGER_MEDIUM_MIN_VAL)
	{
		if (m_pBot->m_sDangerLevel <= DANGER_MEDIUM_MAX_VAL)
			MinKiLevel = 100; // hurry up
		else
			MinKiLevel = 20;
	}

	m_pBot->ResetMoveSpeed();

	if (m_pBot->GetKi() >= MinKiLevel)
	{
		m_pBot->StopPowerUp();
	    m_eComboEntryState = DONE;
	}
}	

void CAscendComboEntry::Think()
{
	if (m_pBot->m_fTurbo)
		m_pBot->StopTurbo();

	// if ascending or tried to ascend for a while
	if ((m_pBot->m_fAscending) || (m_fLifeTime < gpGlobals->time))
	{
		// then we're done here
		m_eComboEntryState = DONE;
		return;
	}

	// Bot cannot ascend?
	if (!m_pBot->BotCanAscend())
	{
		// No use to try it then
		m_eComboEntryState = DONE;
		return;
	}

	m_pBot->StartAscend();
}

void CCheckFOVForAimComboEntry::Think()
{
	if ((!m_pBot->m_pBotEnemy) || (m_pBot->m_sDangerLevel >= DANGER_HARD_MIN_VAL) ||
		(m_fLifeTime < gpGlobals->time))
	{
		m_eComboEntryState = DONE;
		return;
	}

	if ((m_pBot->EntIsInFOV(m_pBot->m_pBotEnemy)) ||
		(EntIsInFieldOfView(m_pBot->m_pBotEnemy, m_pBot)))
	{
		m_eComboEntryState = DONE;
		return;
	}

	// Give the bot time to aim @ his enemy
	m_pBot->ResetMoveSpeed();
	m_pBot->AimAtEnemy(m_pBot->m_pBotEnemy);
}

void CSwoopCloseToEnemyComboEntry::Think()
{
	if ((!m_pBot->m_pBotEnemy) || (m_pBot->pev->movetype != MOVETYPE_PLAYERSWOOP))
	{
		m_eComboEntryState = DONE;
		return;
	}

	float MinDist = (m_pBot->m_pBotEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP) ? 600.0f : 450.0f;
	if (m_pBot->GetDistance(m_pBot->m_pBotEnemy->pev->origin) <= MinDist)
	{
		m_eComboEntryState = DONE;
		if (m_pBot->m_fFlying)
		{
			m_pBot->StopFly();
			m_pBot->m_bShouldFly = false;
			m_pBot->m_fCheckFlyDelay = gpGlobals->time + 0.5f;
		}
		return;
	}

	m_pBot->AimAtEnemy(m_pBot->m_pBotEnemy);

	// if enemy swoops, swoop with offset
	if (m_pBot->m_pBotEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP)
	{
		if (m_pBot->m_eWanderDir == MOVE_LEFT)
			m_pBot->m_vAimVec.y = UTIL_WrapAngle(m_pBot->m_vAimVec.y + 30);
		else
			m_pBot->m_vAimVec.y = UTIL_WrapAngle(m_pBot->m_vAimVec.y - 30);
	}

	m_pBot->ResetMoveSpeed();
	m_pBot->SetMoveDir(dir_forward);

	/* UNDONE ?
	if (m_pBot->m_bSkillNr == 0)
	{
		ChainSwoopType = m_pBot->AvoidAttacksWhileSwoop();
		if (ChainSwoopType != COMBO_TYPE_NONE)
			MakeCombo(ChainSwoopType);
	}*/
}

void CAvoidAttackWhileSwoopComboEntry::Think()
{
	if (m_pBot->pev->movetype != MOVETYPE_PLAYERSWOOP)
	{
		m_eComboEntryState = DONE;
		return;
	}

	m_pBot->SetSwoopButton();

	CBaseEntity *pAttack = m_pBot->GetNearestHostileAttack(50.0f, 2250.0f);
	if (!pAttack)
	{
		m_eComboEntryState = DONE;
		return;
	}

	if (UTIL_GetHeadingPercentage(m_pBot, pAttack) < 65)
		m_eComboEntryState = DONE; // Attack isn't dangerous anymore
}

void CSwoopAWhileComboEntry::Think()
{
	if (m_pBot->pev->movetype != MOVETYPE_PLAYERSWOOP)
	{
		m_eComboEntryState = DONE;
		return;
	}

	m_pBot->SetSwoopButton();

	if (m_fLifeTime < gpGlobals->time)
		m_eComboEntryState = DONE;
}
	
void CChargeGenBallComboEntry::Think()
{
	if (!m_pBot->m_pBotEnemy)
	{
		PrintAI("Stop Charge GBall : No enemy\n");
		m_eComboEntryState = DONE;
		return;
	}

	if (m_pBot->m_sDangerLevel >= DANGER_HARD_MIN_VAL)
	{
		PrintAI("Stop Charge GBall : Danger to high\n");
		m_eComboEntryState = DONE;
		return;
	}

	if (m_pBot->GetKi() < 100)
	{
		PrintAI("Stop Charge GBall : Ki to low\n");
		m_eComboEntryState = DONE;
		return;
	}

	if ((m_pBot->m_pActiveItem) && (m_pBot->m_pActiveItem->m_iId != WEAPON_GENERICBEAM))
		m_pBot->SelectItem("weapon_genericbeam");
	else
	{
		if (m_pBot->m_iChargePercent >= 60)
		{
			PrintAI("Stop Charge GBall : Charge limit reached\n");
			m_eComboEntryState = DONE;
			return;
		}
	}

	m_pBot->AimAtEnemy(m_pBot->m_pBotEnemy);
	m_pBot->pev->button |= IN_ATTACK2;
	m_pBot->SetMoveDir(dir_back);
}

// ==================================================================
// Code of BotComboEntries - End
// ==================================================================

// ==================================================================
// Code of CBotCombo - Begin
// ==================================================================

CBotCombo::CBotCombo(CBot *pB, EComboType CT) : m_pBot(pB), m_pCurrentComboEntry(NULL),
												m_eComboState(NOT_STARTED),
												m_eTaskAfterCombo(TaskNone)
{
	switch(CT)
	{
	case COMBO_TYPE_LEFT_SWOOP:
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_left));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_left));
		//m_eTaskAfterCombo = TaskSwoop;
		break;
	case COMBO_TYPE_RIGHT_SWOOP:
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_right));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_right));
		//m_eTaskAfterCombo = TaskSwoop;
		break;
	case COMBO_TYPE_FORWARD_SWOOP:
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
		break;
	case COMBO_TYPE_BACKWARD_SWOOP:
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_back));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_back));
		//m_eTaskAfterCombo = TaskSwoop;
		break;
	case COMBO_TYPE_UP_SWOOP:
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_up));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_up));
		//m_eTaskAfterCombo = TaskSwoop;
		break;
	case COMBO_TYPE_DOWN_SWOOP:
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_down));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_down));
		//m_eTaskAfterCombo = TaskSwoop;
		break;
	case COMBO_TYPE_TELE_ATTACK:
		MakeRandomTeleports(TELE_RANDOM, TELE_BACK, TELE_RANDOM);
		m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_eTaskAfterCombo = TaskChargeAttack;
		break;
	case COMBO_TYPE_TELE_ATTACK2:
		MakeRandomTeleports(TELE_RANDOM, TELE_BACK, TELE_RANDOM);
		m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_eTaskAfterCombo = TaskShootNonChargeble;
		break;
	case COMBO_TYPE_FALL_POWERUP:
		m_ComboList.AddNode(new CFallDownComboEntry(m_pBot));

		//m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_eTaskAfterCombo = TaskPowerup;
		break;
	case COMBO_TYPE_FALL_ASCEND:
		m_ComboList.AddNode(new CFallDownComboEntry(m_pBot));
		m_ComboList.AddNode(new CAscendComboEntry(m_pBot));
		break;
	case COMBO_TYPE_MULTI_TELEPORT:
		{
			if (!m_pBot->m_pPersonality->teleport_percent)
				return; // We don't want a zero divide

			short TeleTimes = (m_pBot->m_sDangerLevel / 150) * (m_pBot->m_pPersonality->teleport_percent / 100);
			while ((TeleTimes > 0) && ((TeleTimes*150) > m_pBot->GetKi()))
				TeleTimes--;

			if (TeleTimes < 1)
				return; // Couldn't teleport because the hasn't got enough ki
			else if (TeleTimes > 4)
				TeleTimes = 4; // Don't teleport to much either

			while(TeleTimes)
			{
				m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, dir_back, true));
				TeleTimes--;
			}
			break;
		}
	case COMBO_TYPE_TELE_LEFT_BACK_MELEE:
	case COMBO_TYPE_TELE_RIGHT_BACK_MELEE:
	case COMBO_TYPE_TELE_BACK_MELEE:
	case COMBO_TYPE_TELE_LEFT_MELEE:
	case COMBO_TYPE_TELE_RIGHT_MELEE:
	case COMBO_TYPE_TELE_UP_MELEE:
	case COMBO_TYPE_TELE_DOWN_MELEE:
	case COMBO_TYPE_TELE_FORWARD_LEFT_MELEE:
	case COMBO_TYPE_TELE_FORWARD_RIGHT_MELEE:
		{
			if (!m_pBot->m_pBotEnemy)
				return;

			short TeleDir;

			if (CT == COMBO_TYPE_TELE_LEFT_BACK_MELEE)
				TeleDir = (dir_back | dir_left);
			else if (CT == COMBO_TYPE_TELE_RIGHT_BACK_MELEE)
				TeleDir = (dir_back | dir_right);
			else if (CT == COMBO_TYPE_TELE_BACK_MELEE)
				TeleDir = dir_back;
			else if (CT == COMBO_TYPE_TELE_LEFT_MELEE)
				TeleDir = dir_left;
			else if (CT == COMBO_TYPE_TELE_RIGHT_MELEE)
				TeleDir = dir_right;
			else if (CT == COMBO_TYPE_TELE_UP_MELEE)
				TeleDir = dir_up;
			else if (CT == COMBO_TYPE_TELE_DOWN_MELEE)
				TeleDir = dir_down;
			else if (CT == COMBO_TYPE_TELE_FORWARD_LEFT_MELEE)
				TeleDir = (dir_forward | dir_left);
			else if (CT == COMBO_TYPE_TELE_FORWARD_RIGHT_MELEE)
				TeleDir = (dir_forward | dir_right);

			m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, TeleDir, true));

			// if enemy is close teleport 2 times instead of 1
			if (m_pBot->GetDistance(m_pBot->m_pBotEnemy->pev->origin) < 350.0f)
				m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, TeleDir, true));

			m_ComboList.AddNode(new CCheckFOVForAimComboEntry(m_pBot));

			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
			break;
		}
	case COMBO_TYPE_TELE_KICK:
		{
			short TeleTimes = (m_pBot->GetDistance(m_pBot->m_pBotEnemy->pev->origin) / 400.0f);

			while ((TeleTimes > 0) && ((TeleTimes*150) > m_pBot->GetKi()))
				TeleTimes--;

			if (TeleTimes < 1)
				return; // Couldn't teleport because the hasn't got enough ki
			else if (TeleTimes > 4)
				TeleTimes = 4; // Don't teleport to much either

			while(TeleTimes)
			{
				m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, dir_forward, true, true));
				TeleTimes--;
			}

			m_eTaskAfterCombo = TaskKickEnemy;
			break;
		}
	case COMBO_TYPE_TELE_BEHIND_SWOOP:
	case COMBO_TYPE_TELE_FORWARD_SWOOP_AIM:
		{
			if (!m_pBot->m_pBotEnemy)
				return;

			short TeleTimes = RANDOM_LONG(3, 4);
			while ((TeleTimes) && ((TeleTimes * 165) > m_pBot->GetKi()))
				TeleTimes--; // Not enough ki

			int TeleDir;
			if (m_pBot->m_eWanderDir == MOVE_LEFT)
				TeleDir = dir_left;
			else
				TeleDir = dir_right;

			TeleDir |= dir_forward;

			bool AimAtEnemy = (CT == COMBO_TYPE_TELE_FORWARD_SWOOP_AIM);

			// First teleport should have more delay
			m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.35f, TeleDir, AimAtEnemy, true));

			while (TeleTimes - 1)
			{
				m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, TeleDir, AimAtEnemy, true));
				TeleTimes--;
			}

			m_ComboList.AddNode(new CCheckFOVForAimComboEntry(m_pBot));

			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));

			break;
		}
	case COMBO_TYPE_SWOOP_TELE_SWOOP:
		if (!m_pBot->m_pBotEnemy)
			return;

		// First charge ki(when needed)
		m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		// Then start a forward swoop
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
		// Then swoop close to enemy
		m_ComboList.AddNode(new CSwoopCloseToEnemyComboEntry(m_pBot));
		// Then teleport behind him/her
		m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.30f, 0, true, true));
		// Then give the bot a bit time to aim
		m_ComboList.AddNode(new CCheckFOVForAimComboEntry(m_pBot));
		// Then swoop to enemy again
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
		
		break;
	case COMBO_TYPE_TELE_FORWARD_SWOOP:
		{
			if (!m_pBot->m_pBotEnemy)
				return;

			// First charge ki(when needed)
			m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));

			int Dist = m_pBot->GetDistance(m_pBot->m_pBotEnemy->pev->origin);

			short TeleTimes = Dist/400;

			if ((Dist % 400) > 100)
				TeleTimes += 1;

			short RandNr = RANDOM_LONG(0, 3);
			int TeleDir = 0;

			if (RandNr == 0)
				TeleDir = dir_left;
			else if (RandNr == 1)
				TeleDir = dir_right;

			if (RandNr <= 1)
				TeleTimes += RANDOM_LONG(1, 2);

			TeleDir |= dir_forward;
		
			// First teleport should have more delay
			m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.35f, TeleDir, true, true, true));

			while (TeleTimes - 1)
			{
				m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, TeleDir, true, true, true));
				TeleTimes--;
			}

			m_ComboList.AddNode(new CCheckFOVForAimComboEntry(m_pBot));
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
			break;
		}
	case COMBO_TYPE_POWERUP_SWOOP:
		m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
		break;
	case COMBO_TYPE_POWERUP_ATTACK:
		m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_eTaskAfterCombo = TaskChargeAttack;
		break;
	case COMBO_TYPE_POWERUP_ATTACK2:
		m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_eTaskAfterCombo = TaskShootNonChargeble;
		break;
	case COMBO_TYPE_CHARGE_GBALL_SWOOP:
		m_ComboList.AddNode(new CChargeGenBallComboEntry(m_pBot));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
		m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));		
		break;
	case COMBO_TYPE_LEFT_AVOID_ATTACK_SWOOP:
	case COMBO_TYPE_RIGHT_AVOID_ATTACK_SWOOP:
	case COMBO_TYPE_UP_AVOID_ATTACK_SWOOP:
	case COMBO_TYPE_DOWN_AVOID_ATTACK_SWOOP:
	case COMBO_TYPE_LEFT_ATTACK_CHAINSWOOP:
	case COMBO_TYPE_RIGHT_ATTACK_CHAINSWOOP:
	case COMBO_TYPE_UP_ATTACK_CHAINSWOOP:
	case COMBO_TYPE_DOWN_ATTACK_CHAINSWOOP:
		{
			int iSwoopDir = 0;
			bool AvoidAttack = false;

			switch (CT)
			{
			case COMBO_TYPE_LEFT_AVOID_ATTACK_SWOOP: iSwoopDir = dir_left; AvoidAttack = true; break;
			case COMBO_TYPE_RIGHT_AVOID_ATTACK_SWOOP: iSwoopDir = dir_right; AvoidAttack = true ;break;
			case COMBO_TYPE_UP_AVOID_ATTACK_SWOOP: iSwoopDir = dir_up; AvoidAttack = true; break;
			case COMBO_TYPE_DOWN_AVOID_ATTACK_SWOOP: iSwoopDir = dir_down; AvoidAttack = true; break;
			}

			// First chain swoop away
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, iSwoopDir));
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, iSwoopDir));

			if (AvoidAttack)
			{
				// If the bot should avoid an attack then do that for a while
				m_ComboList.AddNode(new CAvoidAttackWhileSwoopComboEntry(m_pBot));
			}
			else
			{
				// Otherwise just chainswoop a bit
				m_ComboList.AddNode(new CSwoopAWhileComboEntry(m_pBot, 0.2f));
			}

			// Then counter attack
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, false, dir_forward));
			m_ComboList.AddNode(new CStartSwoopComboEntry(m_pBot, true, dir_forward));
			break;
		}
	case COMBO_TYPE_TELE_POWERUP:
		m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.2f, dir_back));
		//m_ComboList.AddNode(new CPowerupComboEntry(m_pBot));
		m_eTaskAfterCombo = TaskPowerup;
		break;
	default:
		m_eComboState = DONE;
		break;
	};
}

CBotCombo::~CBotCombo()
{
	while (m_ComboList.Empty() == false)
		delete m_ComboList.Pop();
}

void CBotCombo::ExcuteCombo()
{
	if (!m_pCurrentComboEntry)
	{
		m_eComboState = DONE;
		return;
	}

	if (m_pCurrentComboEntry->m_eComboEntryState == NOT_STARTED)
	{
		if ((m_pCurrentComboEntry->m_fCreateTime + m_pCurrentComboEntry->GetDelay()) <= gpGlobals->time)
			m_pCurrentComboEntry->Activate();
		else
		{
			m_pBot->ResetMoveSpeed();
			return;
		}
	}

	m_pCurrentComboEntry->Think();

	if (m_pCurrentComboEntry->m_eComboEntryState == DONE)
	{
		delete m_pCurrentComboEntry;
		m_pCurrentComboEntry = NULL;

		if (!m_ComboList.Empty())
		{
			m_pCurrentComboEntry = m_ComboList.Pop();
			if (m_pCurrentComboEntry)
				m_pCurrentComboEntry->m_fCreateTime = gpGlobals->time;
			else
				m_eComboState = DONE;
		}
		else
		{
			m_eComboState = DONE;
		}
	}
}

// Make random teleport combo entries, returns true if succesful made
bool CBotCombo::MakeRandomTeleports(ETeleDir TeleDirLR, ETeleDir TeleDirBF, ETeleDir TeleDirUD)
{
	if (!m_pBot->m_pBotEnemy)
		return false;

	float dist = m_pBot->GetDistance(m_pBot->m_pBotEnemy->pev->origin);
	int RandNr;

	int TeleCount;

	if (TeleDirBF == TELE_FORWARD)
		TeleCount = int(dist / RANDOM_FLOAT(300.0,500.0));
	else
		TeleCount = RANDOM_LONG(3, 5);

	while ((TeleCount*150) > m_pBot->GetKi())
		TeleCount--;

	if (TeleCount == 0)
		return false;

	Vector PosToTrace = m_pBot->pev->origin;
	Vector v_end, v_aim;

	short TelCount = 0;
	for (int i=0;i<TeleCount;i++)
	{
		bool CheckedLeftForwardUp = false, CheckedRightForwardUp = false;
		bool CheckedLeftBackwardUp = false, CheckedRightBackwardUp = false;
		bool CheckedLeftForwardDown = false, CheckedRightForwardDown = false;
		bool CheckedLeftBackwardDown = false, CheckedRightBackwardDown = false;

		while ((CheckedLeftForwardUp == false) && (CheckedRightForwardUp == false) &&
			   (CheckedLeftBackwardUp == false) && (CheckedRightBackwardUp == false) &&
			   (CheckedLeftForwardDown == false) && (CheckedRightForwardDown == false) &&
			   (CheckedLeftBackwardDown == false) && (CheckedRightBackwardDown == false))
		{
			int IdealDir = 0;
			TraceResult tr;

			RandNr = 0;

			if (TeleDirLR == TELE_RANDOM)
				RandNr = RANDOM_LONG(0, 1);

			if ((TeleDirLR == TELE_LEFT) || (RandNr))
				IdealDir |= dir_left;
			else
				IdealDir |= dir_right;

			RandNr = 0;

			if (TeleDirBF == TELE_RANDOM)
				RandNr = RANDOM_LONG(0, 1);

			if ((TeleDirBF == TELE_BACK) || (RandNr))
				IdealDir |= dir_back;
			else
				IdealDir |= dir_forward;

			RandNr = 0;

			if (TeleDirUD == TELE_RANDOM)
				RandNr = RANDOM_LONG(0, 1);

			if ((TeleDirUD == TELE_DOWN) || (RandNr))
				IdealDir |= dir_down;
			else
				IdealDir |= dir_up;

			if ((IdealDir & dir_left) && (IdealDir & dir_forward) && (IdealDir & dir_up))
			{
				if (CheckedLeftForwardUp)
					continue;

				v_aim = m_pBot->pev->v_angle;
				v_aim.x = -45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + 45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace + gpGlobals->v_forward * 400.0;

				CheckedLeftForwardUp = true;
			}
			else if ((IdealDir & dir_right) && (IdealDir & dir_forward) && (IdealDir & dir_up))
			{
				if (CheckedRightForwardUp)
					continue;

				CheckedRightForwardUp = true;

				v_aim = m_pBot->pev->v_angle;
				v_aim.x = -45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + -45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace + gpGlobals->v_forward * 400.0;
			}
			else if ((IdealDir & dir_left) && (IdealDir & dir_back) && (IdealDir & dir_up))
			{
				if (CheckedLeftBackwardUp)
					continue;

				CheckedLeftBackwardUp = true;
				v_aim = m_pBot->pev->v_angle;
				v_aim.x = 45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + -45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace - gpGlobals->v_forward * 400.0;
			}
			else if ((IdealDir & dir_right) && (IdealDir & dir_back) && (IdealDir & dir_up))
			{
				if (CheckedRightBackwardUp)
					continue;

				CheckedLeftForwardUp = true;
				v_aim = m_pBot->pev->v_angle;
				v_aim.x = 45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + 45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace - gpGlobals->v_forward * 400.0;
			}
			else if ((IdealDir & dir_left) && (IdealDir & dir_forward) && (IdealDir & dir_down))
			{
				if (CheckedLeftForwardDown)
					continue;

				CheckedLeftForwardDown = true;
				v_aim = m_pBot->pev->v_angle;
				v_aim.x = 45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + 45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace + gpGlobals->v_forward * 400.0;
			}
			else if ((IdealDir & dir_right) && (IdealDir & dir_forward) && (IdealDir & dir_down))
			{
				if (CheckedRightForwardDown)
					continue;

				CheckedRightForwardDown = true;
				v_aim = m_pBot->pev->v_angle;
				v_aim.x = 45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + -45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace + gpGlobals->v_forward * 400.0;
			}
			else if ((IdealDir & dir_left) && (IdealDir & dir_back) && (IdealDir & dir_down))
			{
				if (CheckedLeftBackwardDown)
					continue;

				CheckedLeftBackwardDown = true;
				v_aim = m_pBot->pev->v_angle;
				v_aim.x = -45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + -45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace - gpGlobals->v_forward * 400.0;
			}
			else if ((IdealDir & dir_right) && (IdealDir & dir_back) && (IdealDir & dir_down))
			{
				if (CheckedRightBackwardDown)
					continue;

				CheckedRightBackwardDown = true;
				v_aim = m_pBot->pev->v_angle;
				v_aim.x = -45.0;
				v_aim.y = UTIL_WrapAngle(v_aim.y + 45.0);
				UTIL_MakeVectors(v_aim);

				v_end = PosToTrace - gpGlobals->v_forward * 400.0;
			}
			else
				continue; // Shouldn't happen

			UTIL_TraceHull(PosToTrace, v_end, dont_ignore_monsters, human_hull,
						   m_pBot->pev->pContainingEntity, &tr);

			// check if the trace didn't hit something...
			if (tr.flFraction >= 1.0)
			{
				m_ComboList.AddNode(new CTeleportComboEntry(m_pBot, 0.4f, IdealDir, true, true));
				PosToTrace = tr.vecEndPos;
				TelCount++;
				break;
			}
		}
	}

	return (TelCount >= 1);
}

void CBotCombo::Activate()
{
	m_eComboState = BUSY;
	m_pCurrentComboEntry = m_ComboList.Pop();
	if (m_pCurrentComboEntry)
		m_pCurrentComboEntry->m_fCreateTime = gpGlobals->time;
}

// ==================================================================
// Code of CBotCombo - End
// ==================================================================