#include "gen_bot_header.h"

extern cvar_t nomeleehittime;
//harSens: Linux compile fix
extern CLeftOutput lcout;

weapon_info_s BotWeaponInfoTable[] =
{
	{WEAPON_MELEE, "weapon_melee", false, false, false, false, 0.0f, 80.0f, 4800.0f},
	{WEAPON_SWORD, "weapon_sword", false, false, false, false, 0.0f, 80.0f, 4800.0f},
	// Big ki blast
    {WEAPON_KIBLAST, "weapon_kiblast", false, false, false, false, 2.5f, 150.0f, 2500.0f},
	// Small ki blast
    {WEAPON_KIBLAST, "weapon_kiblast", true, false, false, false, 2.5f, 150.0f, 2500.0f},
    {WEAPON_GENERICBEAM, "weapon_genericbeam", false, true, true, true, 0.0f, 300.0f, 4500.0f},
	// Generic ball
	{WEAPON_GENERICBEAM, "weapon_genericbeam", true, true, false, false, 0.0f, 300.0f, 4500.0f},
    {WEAPON_GALLITGUN, "weapon_gallitgun", false, true, true, true, 0.0f, 800.0f, 5200.0f},
    {WEAPON_KAMEHAMEHA, "weapon_kamehameha", false, true, true, true, 0.0f, 800.0f, 5200.0f},
    {WEAPON_DESTRUCTODISC, "weapon_destructodisc", false, true, false, false, 0.0f, 200.0f, 3500.0f},
    {WEAPON_EYELASER, "weapon_eyelaser", false, false, false, false, 1.0f, 200.0f, 2500.0f},
    {WEAPON_FRIEZADISC, "weapon_friezadisc", false, true, false, true, 0.0f, 200.0f, 3500.0f},
    {WEAPON_SPECIALBEAMCANNON, "weapon_specialbeamcannon", false, true, false, false, 0.0f, 800.0f,
	 5500.0f},
	 // Big SB
    {WEAPON_SPIRITBOMB, "weapon_spiritbomb", false, true, false, false, 0.0f, 800.0f, 5500.0f},
	 // Small SB
    {WEAPON_SPIRITBOMB, "weapon_spiritbomb", true, true, false, false, 0.0f, 800.0f, 5500.0f},
    {WEAPON_BIGBANG, "weapon_bigbang", false, true, false, false, 0.0f, 800.0f, 5500.0f},
    {WEAPON_FINGERLASER, "weapon_fingerlaser", false, false, false, false, 1.0f, 200.0f, 2500.0f},
    {WEAPON_FINALFLASH, "weapon_finalflash", false, true, false, true, 0.0f, 800.0f, 5200.0f},
    {WEAPON_MASENKO, "weapon_masenko", false, true, true, true, 0.0f, 400.0f, 4800.0f},
    {WEAPON_DEATHBALL, "weapon_deathball", false, true, false, false, 0.0f, 800.0f, 5500.0f},
    {WEAPON_BURNINGATTACK, "weapon_burningattack", false, true, true, true, 0.0f, 300.0f, 4500.0f},
    {WEAPON_SCATTERBEAM, "weapon_scatterbeam", false, true, false, false, 0.0f, 800.0f, 5500.0f},
    {WEAPON_POWERBEAM, "weapon_powerbeam", false, true, true, true, 0.0f, 400.0f, 4800.0f},
    {WEAPON_MOUTHBLAST, "weapon_mouthblast", false, true, true, true, 0.0f, 800.0f, 5200.0f},
    {WEAPON_FINISHINGBUSTER, "weapon_finishingbuster", false, true, false, false, 0.0f, 800.0f,
	 5500.0f},
    {WEAPON_RENZOKU, "weapon_renzoku", false, true, false, false, 0.0f, 150.0f, 2500.0f},
	// Secondary renzoku
	{WEAPON_RENZOKU, "weapon_renzoku", true, false, false, false, 2.5f, 150.0f, 2500.0f},
	// Used?
    {WEAPON_SOLARFLARE, "weapon_solarflare", false, false, false, false, 0.0f, 150.0f, 2500.0f},
    {WEAPON_KAMETORPEDO, "weapon_kametorpedo", false, true, false, false, 0.0f, 1500.0f, 5500.0f},
    {WEAPON_TELEKINESIS, "weapon_telekinesis", false, true, false, false, 0.0f, 600.0f, 4400.0f},
    {WEAPON_CANDY, "weapon_candy", false, false, false, false, 1.0f, 200.0f, 900.0f},
    {WEAPON_SCATTERSHOT, "weapon_scattershot", false, false, false, false, 1.0f, 150.0f, 2500.0f},
    {SBOT_WEAPON_LASER1, "weapon_dummy", false, false, false, false, 1.0f, 1500.0f, 5500.0f},

    // terminator 
    {0, "", false, false, false, false, 0.0f, 0.0f, 0.0f}
};

// ==================================================================
// TaskCode - Begin
// ==================================================================

// Base task
void CBaseTask::ManageEnemies()
{
	CBasePlayer *pTemp = pBot->m_pBotEnemy; // Store current enemy
	pBot->m_pBotEnemy = pEnemy = pBot->GetBestEnemyFromList();

	// Did the bot lost his enemy ?
	if ((pBot->m_pBotEnemy == NULL) && (UTIL_IsValidPlayer(pTemp, false)))
	{
		// Add enemy searchy delay
		float MinDelay = pBot->m_pBotSkill->flMinEnemySearchDelay;
		float MaxDelay = pBot->m_pBotSkill->flMaxEnemySearchDelay;

		pBot->m_fSearchEnemyDelay = gpGlobals->time + RANDOM_FLOAT(MinDelay, MaxDelay);
	}
}

// Finish melee task
CFinishMeleeTask::CFinishMeleeTask(CBot *pB) : CBaseTask(pB)
{
	short MeleeThrowPercent = pBot->m_pPersonality->meleethrow_percent;
	bool MakeAMeleeCombo = false;

	if (pBot->GetKi() < 400) // Is the bot low on ki?
	{
		float KiFactor = (pBot->GetKi() * 0.001f) * 2.0f;
		MeleeThrowPercent *= KiFactor;
	}

	if (RANDOM_LONG(1, 100) <= MeleeThrowPercent)
	{
		FinishMeleeType = 2;
		FinishMeleeTime = Delay + RANDOM_FLOAT(3.8, 4.6);
	}
	else
	{
		FinishMeleeType = 1;
		FinishMeleeTime = Delay + RANDOM_FLOAT(4.3, 6.1);

		// Make a melee combo?
		if ((pBot->m_pBotSkill->bUsesMeleeCombos) &&
		    (RANDOM_LONG(1, 100) <= pBot->m_pPersonality->meleecombo_percent))
			MakeAMeleeCombo = true;
	}

	PunchKickTime = Delay + RANDOM_FLOAT(1.5, 2.4);
	ComboChargeButton = IN_ATTACK;
	NewKickPunchButtonDelay = 0.0;

	byte MaxStunpunches = static_cast<byte>(CVAR_GET_FLOAT("am_prepunch_maxstun"));
	byte MinPrepunches = static_cast<byte>(CVAR_GET_FLOAT("am_prepunch_mincombo"));
	byte MaxPrepunches = static_cast<byte>(CVAR_GET_FLOAT("am_prepunch_max")) - 1;

	byte MinPunch = MaxStunpunches - (RANDOM_LONG(0, ((pBot->m_bSkillNr+1)/2)));
	byte MaxPunch;

	if (MakeAMeleeCombo)
	{
		// if bot should make a melee combo he should try to cause as much stun time as possible
		MaxPunch = MaxStunpunches + (RANDOM_LONG(0, ((pBot->m_bSkillNr+1)/2)));
	}
	else
		MaxPunch = MaxPrepunches - (RANDOM_LONG(0, pBot->m_bSkillNr)) - byte(pBot->m_pPersonality->aggression_level);

	if (MinPunch <= MinPrepunches)
		MinPunch = MinPrepunches + 1;

	if (MaxPunch <= MinPunch)
		MaxPunch = MinPrepunches + 1;

	// If bot wants to throw, he shouldn't make more then 6 prepunches
	if ((FinishMeleeType == 2) && (MaxPunch > 6))
		MaxPunch = 6;

	PrepunchAmountGoal = RANDOM_LONG(MinPunch, MaxPunch);

	pPlayerToThrowAt = NULL;
	ObstacleToThrowAt = g_vecZero;
	CheckedObstacle = false;

	if (MakeAMeleeCombo)
		pBot->MakeMeleeComboList();
}

CFinishMeleeTask::~CFinishMeleeTask()
{
	pBot->AddShootDelay();
	pBot->ClearMeleeComboList();
}

void CFinishMeleeTask::TaskThink()
{
	if (!pBot->m_fMelee)
	{
		TaskState = DONE;
		return;
	}

	if ((pBot->m_Melee.m_eAdvMeleeStatus != PREPUNCH_ATTACK) &&
		(pBot->m_Melee.m_eAdvMeleeStatus != CHARGE_ATTACK) &&
		(pBot->m_Melee.m_eAdvMeleeStatus != GRAB_ATTACK))
	{
		TaskState = DONE;
		return;
	}

	pBot->ResetMoveSpeed();

	if (pBot->m_Melee.m_eAdvMeleeStatus == PREPUNCH_ATTACK)
	{
		if ((pBot->m_Melee.m_iNumPrepunches < PrepunchAmountGoal) && (pBot->GetKi() > 150))
			return; // Not enough prepunches yet, continue prepunching
	}

	// Check if the bot is an attacker aswell; since if your enemy blocked the prepunches
	// you can't throw him anymore but you will charge a combo started with kick.
	if ((FinishMeleeType == 1) || (pBot->m_Melee.m_eAdvMeleeStatus == CHARGE_ATTACK))
	{
		pBot->ChargeMeleeCombo();

		if (pBot->m_pMeleeComboInfoList && pBot->m_pMeleeComboInfoList->Empty())
		{
			if (pBot->m_fNextComboChargeButtonTime < gpGlobals->time)
				TaskState = DONE;  // Done with the combo
			return;
		}
	}
	else if ((FinishMeleeType == 2) || (pBot->m_Melee.m_eAdvMeleeStatus == GRAB_ATTACK))
	{
		// Time to start looking for a throw place/victim?
		float flChargePercent = pBot->m_Melee.m_flChargeGrab * 100;
		if (flChargePercent >= 30)
		{
			// Is the choosen victim not valid anymore?
			if ((pBot->IsValidEnemy(pPlayerToThrowAt) == false) ||
				(IsValidThrowVictim(pPlayerToThrowAt) == false))
				pPlayerToThrowAt = NULL; // Reset

			// Didnt found a player yet?
			if (!pPlayerToThrowAt)
				CheckPlayerToThrowAt();

			// Din't checked for an obstacle and have no player to throw at?
			if ((CheckedObstacle == false) && (!pPlayerToThrowAt))
			{
				CheckObstacleToThrowAt();
			}

			if (pPlayerToThrowAt)
			{
				pBot->AimAtEnemy(pPlayerToThrowAt);
			}
			else // else aim to the obstacle to throw at
			{
				pBot->AimToVec(ObstacleToThrowAt);
			}
		}
/*
		if (pBot->ChargeBeamVal == 100)
		{
			TaskState = DONE;
			return;
		}

		if (((FinishMeleeTime - gpGlobals->time) < 1.7) && (pBot->ChargeBeamVal == 0))
		{
			TaskState = DONE;
			return;
		}
*/
		pEdict->v.button = IN_ATTACK2; // Keep charging the throw
	}
}

void CFinishMeleeTask::CheckPlayerToThrowAt()
{
	float distance = 0.0f;
	float nearestdistance = 9999.0f;
	byte i;

	Vector v_src;
	Vector v_dest, v_dest2;
	Vector vecEnd;
	Vector IdealThrowVec = g_vecZero;

	CBasePlayer *pPlayer = NULL;
	CBasePlayer *pThrowPlayer = NULL;
	
	TraceResult tr, tr2;

	bool OnScouter = false, InViewCone = false;

	if (!pBot->m_bMadePlayerList)
		pBot->MakePlayerList();

	if (pBot->m_bPlayerCount)
	{
		for (i = 0;i<pBot->m_bPlayerCount;i++)
		{
			pPlayer = pBot->m_pPlayerList[i];

			distance = pBot->GetDistance(pPlayer->pev->origin);

			if ((distance > 1500.0) || (distance >= nearestdistance))
				continue;

		   	// is team play enabled?
			if ((g_teamplay) && (pBot->pev->team == pPlayer->pev->team))
				continue; // don't target your teammates...

			OnScouter = ((distance <= 2300) || (pBot->IsSuperBot()));
		
			if ((!pBot->EntIsInFOV(pPlayer)) && (!OnScouter))
				continue;

			if (IsValidThrowVictim(pPlayer) == false)
				continue;

			nearestdistance = distance;
			pThrowPlayer = pPlayer;
		}
		if (pThrowPlayer)
		{
			pPlayerToThrowAt = pThrowPlayer;
			return;
		}
	}
}

void CFinishMeleeTask::CheckObstacleToThrowAt()
{
	float x=0.0,y=0.0;
	float distance = 0.0, distance2 = 0.0;
	float nearestdistance = 9999.0;
	float dist = 0.0;
	byte i;

	Vector v_src;
	Vector v_dest, v_dest2;
	Vector vecEnd;
	Vector IdealThrowVec = g_vecZero;

	TraceResult tr, tr2;

	for (i=0;i<5;i++)
	{ 
		switch (i)
		{
		case 0:
			x=0.0;
			break;
		case 1:
			x=-40.0;
			break;
		case 2:
			x=40.0;
			break;
		case 3:
			x=-89.0;
			break;
		case 4:
			x=89.0;
			break;
		default:
			break;
		}

		for (int j=0;j<5;j++)
		{
			switch (j)
			{
			case 0:
				y=-90.0;
				break;
			case 1:
				y=-45.0;
				break;
			case 2:
				y=0.0;
				break;
			case 3:
				y=45.0;
				break;
			case 4:
				y=90.0;
				break;
			default:
				break;
			}

			v_src = WrapAngles(pEdict->v.v_angle); // restore bot's current v_angle
			v_src.y = UTIL_WrapAngle (v_src.y + y);
			v_src.x = x;

			UTIL_MakeVectors(v_src);

			v_dest = pEdict->v.origin + gpGlobals->v_forward * 3000;

			v_dest2 = pEdict->v.origin - gpGlobals->v_forward * 3000;

	        UTIL_TraceHull (pEdict->v.origin, v_dest,
		                    ignore_monsters, human_hull, pEdict, &tr);

	        UTIL_TraceHull (pEdict->v.origin, v_dest2,
		                    ignore_monsters, human_hull, pEdict, &tr2);

			distance = (tr.vecEndPos - pEdict->v.origin).Length();
			distance2 = (tr2.vecEndPos - pEdict->v.origin).Length();

			if ((distance < distance2) && (tr.flFraction > 0.0f) && (distance > 125.0f))
			{
				dist = distance;
			}
			else if ((tr2.flFraction > 0.0f) && (distance2 > 125.0f))
			{
				dist = distance2;
				v_dest = v_dest2;
			}
			else
				continue;

			if (dist < nearestdistance)
			{
				nearestdistance = dist;
				IdealThrowVec = v_dest;
			}
		}
	}

	if (IdealThrowVec != g_vecZero) // Found a spot to throw to?
	{
		ObstacleToThrowAt = IdealThrowVec;
	}

	CheckedObstacle = true;
}

bool CFinishMeleeTask::IsValidThrowVictim(CBasePlayer *pPlayer)
{
	if (pPlayer->pev->velocity.Length() > 600.0f)
		return false;

	if (pBot->GetDistance(pPlayer->pev->origin) > 1500.0f)
		return false;

	return (pBot->CanMelee(pPlayer) == TRUE);
}


// General combat task
void CCombatTask::TaskThink()
{
	if (pBot->m_pBotEnemy == NULL)
	{
		TaskState = DONE;
		return;
	}

	pBot->BotCombatMovement();

	float EnemyDist = pBot->GetDistance(pEnemy->pev->origin);
	bool EnemyIsToFar = true;

	Vector v_end = pEdict->v.origin;

	// Check if the bot isn't facing the enemy and the enemy isn't facing the bot
	if ((EntIsInFieldOfView(pBot, pEnemy) == false) &&
		(EntIsInFieldOfView(pEnemy, pBot) == false))
	{
		pBot->ResetMoveSpeed(); // Give the bot some time to aim at his enemy.
		return;
	}

	// Turn turbo on when the enemys is close
	if ((pBot->m_pBotSkill->bUsesTurbo) && (EnemyDist < 800.0f))
		pBot->ToggleTurbo(true);

	if (pBot->m_fShootDelay > gpGlobals->time)
	{
		//cout << "shoot time\n";
		return; // Bot has to wait a bit
	}

	if ((BotManager.BotsShoot() == false) || (CVAR_GET_FLOAT("mp_ceasefire") == 1.0f))
	{
		//cout << "bots may not attack\n";
		return; // Bots may not attack
	}

	if (!pBot->HasWeapons())
	{
//		cout << "No weapons";
		return; // Bot has just respawned and don't has his weapons yet.
	}

	if ((pBot->IsSuperBot()) && (pBot->GetHealth() < 70) && (pBot->GetKi() > 150) &&
		(pBot->GetMaxSpeed() > 0.0f) && (pBot->m_fMelee == FALSE) &&
		(RANDOM_LONG(1, 100) <= pBot->m_pSBotSettings->laser_percent))
	{
		pBot->m_eTaskAfterTask = TaskAbsorbHealth;
		TaskState = DONE;
		return;
	}

	int MeleeId;
	if (pEdict->v.playerclass == 7)
		MeleeId = WEAPON_SWORD;
	else
		MeleeId = WEAPON_MELEE;

	bool AbleToMelee = ((EnemyDist >= GetMinDistForAttackById(MeleeId)) &&  
		                (EnemyDist <= GetMaxDistForAttackById(MeleeId)) &&
						(pBot->CanMelee(pEnemy)));

	// Bot ONLY uses melee?
	if (pBot->m_pPersonality->melee_percent == 100)
	{
		if (AbleToMelee)
			AttackWithMelee();
		return;
	}

	// Bot is goku?
	if (pEdict->v.playerclass == 2)
	{
		// Check if this bot has kame torp 'left overs'
		CBaseEntity *pTorp = NULL;

		while((pTorp = UTIL_FindEntityByClassname(pTorp, "Kame Torpedo")) != NULL)
		{
			if (IsNull(pTorp))
				continue;

			if (pTorp->pev->owner != pEdict) // Attack isn't owned by the bot
				continue;

			if (pTorp->pev->velocity.Length() > 0.0f) // Attack is moving so is fired already
				continue;

			// Okay...throw one torp at the enemy(assume that the enemy 'caused danger' so
			// lets 'cause danger' aswell...)
			pEdict->v.button |= IN_ATTACK2;
					
			// Make the kametorp task
			TaskState = DONE;
			pBot->m_eTaskAfterTask = TaskKameTorp;
			return;
		}
	}

	float MeleePercent = float(pBot->m_pPersonality->melee_percent);

	bool EnemySwoops = pEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP;
	bool EnemyIsBlinded = (pEnemy->m_fBlinded == TRUE);

	if ((EnemySwoops) && (EnemyDist < 2500.0))
	{
		MeleePercent *= 1.5;
	}

	// Is the bot a defensive bot and low on health?
	if ((pBot->m_pPersonality->aggression_level == Defensive) && (pEdict->v.health < 20))
		MeleePercent *= 0.75; // Lower melee percent

	if (AbleToMelee == false)
		MeleePercent = 0.0;

	// Check if the bot wants to melee
	if (RANDOM_FLOAT(1.0, 100.0) <= MeleePercent)
	{
		AttackWithMelee();
		return;
	}

/*  needed?
	Vector v_src = pEdict->v.origin + pEdict->v.view_ofs;
	Vector v_enemy = pEnemy->pev->origin;
	TraceResult tr;

	UTIL_TraceHull(v_src, v_enemy, ignore_monsters, human_hull, pEdict, &tr);
	if (tr.flFraction < 1.0)
	{
		// If the bot can see the bot the enemy his head(view_ofs)
		// (called in UTIL_IsValidEnemy, see BotPerformTask()) but NOT his body, press jump and 
		// don't attack
		pEdict->v.button |= IN_JUMP;
		pBot->f_updown_speed = pBot->f_max_speed;
		return;
	}
*/
	TMultiChoice<weapon_info_s *> AttackChoice;

	int SelectIndex = 0;
	weapon_info_s *pSelect = &BotWeaponInfoTable[0];

	bool BotMayUseACombo = (RANDOM_LONG(1, 100) <= pBot->m_pPersonality->teleport_percent);

	while (pSelect[SelectIndex].iId != 0)
	{
		// is the bot NOT carrying this weapon?
		if (pBot->HasWeapon(pSelect[SelectIndex].iId) == false)
   		{
            SelectIndex++; // skip to next weapon
       		continue;
		}

		if ((pSelect[SelectIndex].iId == WEAPON_SOLARFLARE) && (pEnemy->m_fBlinded))
		{
			SelectIndex++;
			continue;
		}

		short AttackPercent = GetAttackPercent(pEnemy, &pSelect[SelectIndex]);
		if (AttackPercent == 0)
		{
			SelectIndex++;
			continue;
		}

		float MinDistToAttack = GetMinDistForAttackByTableIndex(SelectIndex);

		// Is the enemy to close ?
		if (EnemyDist < MinDistToAttack)
		{
			// Skip this attack if the bot may not use a teleport combo.
			// Otherwise just lower the use percent
			if (BotMayUseACombo)
				AttackPercent *= 0.75;
			else
			{
				SelectIndex++;
				continue;
			}
		}

		float MaxDistToAttack = GetMaxDistForAttackByTableIndex(SelectIndex);

		// Or to far?
		if (EnemyDist > MaxDistToAttack)
		{
			SelectIndex++;
			continue;
		}

		if (EnemyIsToFar == true)
			EnemyIsToFar = false;

		if ((pSelect[SelectIndex].iId == WEAPON_SPIRITBOMB) &&
            (pBot->GetCollisionDist(dir_up, 250.0f) < 250.0f))
		{
			SelectIndex++;  // skip to next weapon
            continue; // don't make a spirit bomb if theres a roof near the bot
		}
		else if (((pSelect[SelectIndex].iId == WEAPON_KIBLAST) ||
				  (pSelect[SelectIndex].iId == WEAPON_SCATTERSHOT) ||
				  (pSelect[SelectIndex].iId == WEAPON_RENZOKU)) &&
				 ((pBot->GetCollisionDist(dir_left, 225.0f) < 225.0f) ||
				  (pBot->GetCollisionDist(dir_right, 225.0f) < 225.0f)))
		{
			SelectIndex++; // Skip to next weapon
			continue; // Don't fire a kiblast if there is a near wall(or bot might hurt hisself)
		}

		AttackChoice.Insert(&pSelect[SelectIndex], AttackPercent);

		SelectIndex++;
	}
	
	weapon_info_s *pAttackSelection = NULL;
	AttackChoice.GetSelection(pAttackSelection);

	if (EnemyIsToFar)
	{
		/* TODO : Make the bot either swoop or teleport a bit to the enemy
		const short GoSwoop = 1;
		const short GoTeleport = 2;

		TMultiChoice<short> GetCloserChoice;

		GetCloserChoice.Insert(GoSwoop, pBot->m_pPersonality->swoop_percent);
		GetCloserChoice.Insert(GoTeleport, pBot->m_pPersonality->teleport_percent);

		short Choice = 0;
		GetCloserChoice.GetSelection(Choice);

		switch (Choice)
		{
		case GoSwoop:
			MakeCombo(COMBO_TYPE_FORWARD_SWOOP);
			return;
		case GoTeleport: 
			// UNDONE MakeCombo(teleforward);
			return;
		}*/

		// For now just activate turbo
		pBot->ToggleTurbo(true);
		return;
	}

	if (pAttackSelection == NULL)
		return; // No attack found :(

	float MinDistToAttack = GetMinDistForAttackById(pAttackSelection->iId);

	if (EnemyDist < MinDistToAttack)
	{
		if (BotMayUseACombo)
			PrepareAttack(pAttackSelection, USE_COMBO);
	}
	else
		PrepareAttack(pAttackSelection, RANDOM_COMBO_USE);

	return;
}

void CCombatTask::PrepareAttack(weapon_info_s *pAttackInfo, EComboUse ComboUse)
{
	bool MakeACombo = false;

	if (ComboUse == USE_COMBO)
		MakeACombo = true;
	else if ((ComboUse == RANDOM_COMBO_USE) && (pBot->m_bSkillNr < 2) &&
			 (pBot->m_pPersonality->melee_percent > 0))
	{
		float ComboPercent = float(pBot->m_pPersonality->teleport_percent);

		ComboPercent *= (1 + (gpGlobals->time - pBot->m_fTeleComboDelay));

		if (pBot->GetKi() < 200)
			ComboPercent = 0.0;
		else if (pBot->GetKi() < 400)
			ComboPercent *= 0.6;
		else if (pBot->GetKi() < 600)
			ComboPercent *= 0.8;
		else if (pBot->GetKi() < 700)
			ComboPercent *= 0.9;

		float EnemyDist = UTIL_Distance(pEdict->v.origin, pEnemy->pev->origin);

		if (EnemyDist < 500.0f)
			ComboPercent *= 1.25f;
		else if (EnemyDist < 1000.0f)
			ComboPercent *= 1.05f;
		else if (EnemyDist < 1750.0f)
			ComboPercent *= 0.85f;
		else
			ComboPercent = 0.0f;

		if (RANDOM_FLOAT(1.0, 100.0) <= ComboPercent)
			MakeACombo = true;
	}

	// select this weapon if it isn't already selected
	if ((pBot->m_pActiveItem) && (pBot->m_pActiveItem->m_iId != pAttackInfo->iId))
		pBot->SelectItem(pAttackInfo->szName);

    if (pAttackInfo->iId == WEAPON_KAMETORPEDO)
	{
		pBot->m_eTaskAfterTask = TaskKameTorp;
		TaskState = DONE;
		return;
	}

	if (pAttackInfo->iId == SBOT_WEAPON_LASER1)
	{
		pBot->m_eTaskAfterTask = TaskShootLaser1;
		TaskState = DONE;
		return;
	}

	if (pAttackInfo->iId == WEAPON_CANDY)
	{
		pBot->m_eTaskAfterTask = TaskShootCandy;
		TaskState = DONE;
		return;
	}

	if (pAttackInfo->iId == WEAPON_SCATTERSHOT)
	{
		pBot->m_eTaskAfterTask = TaskShootScatterShots;
		TaskState = DONE;
		return;
	}

    short primary_percent = RANDOM_LONG(1, 100);

	bool UsePrimaryFire, UseSecondaryFire;
	UsePrimaryFire = UseSecondaryFire = false;

	if (pAttackInfo->bIsChargeble)
	{
		pBot->m_pCurrentWeaponInfo = pAttackInfo;
	
		// Bots want to make a generic ball?
		if ((pAttackInfo->iId == WEAPON_GENERICBEAM) && (pAttackInfo->bUseSecondary))
		{
			// if the bot NOT defending a db spot?
			if (((pBot->m_eBotDBType != DB_DEFENDER) || (!BotManager.IsDBMode())) &&
				(RANDOM_LONG(1, 100) <= pBot->m_pPersonality->melee_percent))
			{
				MakeCombo(COMBO_TYPE_CHARGE_GBALL_SWOOP);
				PrintAI("Charge GBall Swoop\n");
				return;
			}
		}

		if (MakeACombo == false)
		{
			if (pBot->GetKi() < 450)
			{
				MakeCombo(COMBO_TYPE_POWERUP_ATTACK);
			}
			else
			{
				pBot->m_eTaskAfterTask = TaskChargeAttack;
				TaskState = DONE;
			}
			return;
		}
		else
		{
			MakeCombo(COMBO_TYPE_TELE_ATTACK);
			pBot->m_fTeleComboDelay = gpGlobals->time + RANDOM_FLOAT(10, 20);
		}
	}
	else if (pAttackInfo->flHoldFireTime > 0.0f)
	{
		pBot->m_pCurrentWeaponInfo = pAttackInfo;
		pBot->m_fHoldAttackTime = pAttackInfo->flHoldFireTime;
		
		if (MakeACombo == false)
		{
			if (pBot->GetKi() < 450)
			{
				MakeCombo(COMBO_TYPE_POWERUP_ATTACK2);
			}
			else
			{
				pBot->m_eTaskAfterTask = TaskShootNonChargeble;
				TaskState = DONE;
			}
			return;
		}
		else
		{
			MakeCombo(COMBO_TYPE_TELE_ATTACK2);
			pBot->m_fTeleComboDelay = gpGlobals->time + RANDOM_FLOAT(10, 20);
		}
	}
    else 
	{
		if (!pAttackInfo->bUseSecondary)
		   	pEdict->v.button |= IN_ATTACK;  // use primary attack
		else
			pEdict->v.button |= IN_ATTACK2;  // use secondary attack

		// set next time to shoot
		pBot->AddShootDelay();
	}
}

void CCombatTask::AttackWithMelee()
{
	if ((pBot->m_fYawSpeed >= 25.0) && (pBot->m_bSkillNr > 2))
	{
		return; // Lower skilled bots won't attack when they turn
	}

	if (pBot->GetMaxSpeed() == 0.0f)
		return; // The bot can't move
 
	int iId = 0;

	if (pBot->m_pActiveItem)
	{
		if (pEdict->v.playerclass == 7)
		{
			iId = WEAPON_SWORD;

			if (pBot->m_pActiveItem->m_iId != iId)
				pBot->SelectItem("weapon_sword");
		}
		else
		{
			iId = WEAPON_MELEE;

			if (pBot->m_pActiveItem->m_iId != iId)
				pBot->SelectItem("weapon_melee");
		}
	}

	if ((pBot->m_pPersonality->melee_percent < 100) &&
	    (RANDOM_LONG(1, 100) <= pBot->m_pPersonality->lightbeam_percent))
	{
		MakeCombo(COMBO_TYPE_CHARGE_GBALL_SWOOP);
		PrintAI("Charge GBall Swoop\n");
		return;
	}

	float ComboPercent = float(pBot->m_pPersonality->teleport_percent);

	ComboPercent *= (1 + (gpGlobals->time - pBot->m_fTeleComboDelay));

	if (pBot->GetKi() < 200)
		ComboPercent = 0.0;
	else if (pBot->GetKi() < 400)
		ComboPercent *= 0.6;
	else if (pBot->GetKi() < 600)
		ComboPercent *= 0.8;
	else if (pBot->GetKi() < 700)
		ComboPercent *= 0.9;

	float dist = pBot->GetDistance(pBot->m_pBotEnemy->pev->origin);

	if ((dist > 2100) || (pBot->m_bSkillNr > 0))
		ComboPercent = 0.0;

	if (EntIsInFieldOfView(pEnemy, pBot) == false)
		ComboPercent *= 1.5;

	if (RANDOM_FLOAT(1.0f, 100.0f) > ComboPercent)
	{
		// Bot DOESN'T want to use a tele combo?
		if (pBot->GetKi() < 150)
			MakeCombo(COMBO_TYPE_POWERUP_SWOOP);
		else
			MakeCombo(COMBO_TYPE_FORWARD_SWOOP);

		return;
	}

	short TeleKickChance = 25;
	short TeleBehindSwoopChance = 50;
	short TeleSwoopTeleChance = 50;
	short TeleSwoopChance = 25;
	short TeleSwoopAimChance = 50;

	if (dist > 1200.0)
		TeleKickChance = 0;
	else if (pBot->EntHeadsToBot(pBot->m_pBotEnemy))
		TeleKickChance *= 0.25f;

	if (pBot->m_pBotEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP)
		TeleKickChance = TeleSwoopChance = 0;
	
	if (pBot->m_pBotEnemy->m_Melee.m_eThrowStatus == THROW_TOSS)
		TeleKickChance *= 1.5;

	int TeleCount = (int(dist) / 400);

	if (TeleCount > ((pBot->GetKi() / 200)-1))
		TeleKickChance = 0;

	if ((3 * 160) < pBot->GetKi())
		TeleSwoopAimChance = 0;

	if (dist < 1250.0f)
		TeleBehindSwoopChance = 0;
	
	if ((dist < 850.0f) || (dist > 1750.0f))
		TeleBehindSwoopChance = 0;

	TMultiChoice<EComboType> ComboChoice;

	ComboChoice.Insert(COMBO_TYPE_TELE_BEHIND_SWOOP, TeleBehindSwoopChance);
	ComboChoice.Insert(COMBO_TYPE_SWOOP_TELE_SWOOP, TeleBehindSwoopChance);
	ComboChoice.Insert(COMBO_TYPE_TELE_FORWARD_SWOOP, TeleSwoopChance);
	ComboChoice.Insert(COMBO_TYPE_TELE_FORWARD_SWOOP_AIM, TeleSwoopAimChance);
	ComboChoice.Insert(COMBO_TYPE_TELE_KICK, TeleKickChance);

	EComboType ComboSelection;
	if (ComboChoice.GetSelection(ComboSelection))
	{
		MakeCombo(ComboSelection);
	}
	else
		MakeCombo(COMBO_TYPE_FORWARD_SWOOP);

	pBot->m_fTeleComboDelay = gpGlobals->time + RANDOM_FLOAT(10, 20);
}

float CCombatTask::GetMinDistForAttackById(int AttackId)
{
	int TableInd = GetAttackIndex(AttackId);
	float BaseDist = BotWeaponInfoTable[TableInd].flMinDist;

	switch(pBot->m_pPersonality->aggression_level)
	{
	case Aggressive:
		BaseDist *= 0.8;
		break;
	case Normal:
		break;
	case Defensive:
		BaseDist *= 1.2;
	}

	return BaseDist;
}

float CCombatTask::GetMaxDistForAttackById(int AttackId)
{
	int TableInd = GetAttackIndex(AttackId);
	return BotWeaponInfoTable[TableInd].flMaxDist;
}

float CCombatTask::GetMinDistForAttackByTableIndex(int TableInd)
{
	float BaseDist = BotWeaponInfoTable[TableInd].flMinDist;

	switch(pBot->m_pPersonality->aggression_level)
	{
	case Aggressive:
		BaseDist *= 0.8;
		break;
	case Normal:
		break;
	case Defensive:
		BaseDist *= 1.2;
	}

	return BaseDist;
}

float CCombatTask::GetMaxDistForAttackByTableIndex(int TableInd)
{
	return BotWeaponInfoTable[TableInd].flMaxDist;
}

int CCombatTask::GetAttackIndex(int AttackId)
{
	weapon_info_s *pSelect = &BotWeaponInfoTable[0];
	int SelectIndex = 0;

    while (pSelect[SelectIndex].iId)
	{
		if (pSelect[SelectIndex].iId == AttackId)
			return SelectIndex;

		SelectIndex++;
	}

#ifdef _DEBUG
	ALERT(at_console, "Attack not found in GetAttackIndex()!\n");
#endif

	return 0; // Not found
}

short CCombatTask::GetAttackPercent(CBasePlayer *pEnemy, weapon_info_s *pAttackInfo)
{
	if (BotManager.BotsShoot() == false)
		return 0;

	float LightBeamPercent = pBot->m_pPersonality->lightbeam_percent;
	float MediumBeamPercent = pBot->m_pPersonality->mediumbeam_percent;
	float HeavyBeamPercent = pBot->m_pPersonality->heavybeam_percent;
	float HeavyProjectilePercent = pBot->m_pPersonality->heavyprojectile_percent;
	float KiblastPercent = pBot->m_pPersonality->kiblast_percent;
	float DiscPercent = pBot->m_pPersonality->disc_percent;
	float LaserPercent = pBot->m_pPersonality->laser_percent;
	float KameTorpPercent = pBot->m_pPersonality->kametorp_percent;

	int iId = pAttackInfo->iId;

	// First check if we should alter the percents...

	// Bot is supsecting an enemy(doesn't have one yet)
	if (!pEnemy)
	{
		KiblastPercent = 0.0f;
		HeavyBeamPercent *= 0.75f;
		HeavyProjectilePercent *= 0.85f;
		LaserPercent = 0.0f;
		KameTorpPercent = 0.0f;
	}
	else
	{
		if (pEnemy->m_fCharging || pEnemy->m_fCharging2)
		{
			short PredictedChargeVal = pEnemy->m_iChargePercent;

			switch(pBot->m_bSkillNr)
			{
			case 0:
				PredictedChargeVal += RANDOM_LONG(-10,10);
				break;
			case 1:
				PredictedChargeVal += RANDOM_LONG(-15,15);
				break;
			case 2:
				PredictedChargeVal += RANDOM_LONG(-20,20);
				break;
			case 3:
				PredictedChargeVal += RANDOM_LONG(-25,25);
				break;
			case 4:
				PredictedChargeVal += RANDOM_LONG(-30,30);
				break;
			}

			if (PredictedChargeVal > 0 && pEnemy->m_pActiveItem)
			{
				switch (pEnemy->m_pActiveItem->m_iId)
				{
				case WEAPON_GENERICBEAM:
				case WEAPON_BURNINGATTACK:
					LightBeamPercent *= 1.75;
					break;
				case WEAPON_MASENKO:
				case WEAPON_POWERBEAM:
					MediumBeamPercent *= 1.75;
					break;
				case WEAPON_GALLITGUN:
				case WEAPON_KAMEHAMEHA:
				case WEAPON_FINALFLASH:
				case WEAPON_MOUTHBLAST:
					HeavyBeamPercent *= 1.75;
					break;
				case WEAPON_SPECIALBEAMCANNON:
				case WEAPON_SPIRITBOMB:
				case WEAPON_BIGBANG:
				case WEAPON_DEATHBALL:
				case WEAPON_SCATTERBEAM:
				case WEAPON_FINISHINGBUSTER:
					HeavyProjectilePercent *= 1.75;
					break;
				}
			}
		}
		else if ((pEnemy->m_fMelee) &&
			     ((pEnemy->m_Melee.m_eThrowStatus == THROW_WALLSTICK) ||
				  (pEnemy->m_Melee.m_eThrowStatus == THROW_LAY)))
		{
			// muhahaha
			KiblastPercent *= 1.75;
			LaserPercent *= 1.75;
		}
		else if (pEnemy->pev->maxspeed == 0.0f)
		{
			KiblastPercent *= 1.25f;
			LaserPercent *= 1.25f;
		}
	
		// Bot is defending a db spot?
		if ((pBot->m_eBotDBType == DB_DEFENDER) && (BotManager.IsDBMode()))
		{
			KiblastPercent *= 1.35f;
			MediumBeamPercent *= 0.6f;
			HeavyBeamPercent *= 0.25f;
			HeavyProjectilePercent *= 0.5f;
			DiscPercent *= 0.85f;
			LaserPercent *= 1.35f;
			KameTorpPercent = 0.0f;
		}
	}

	if ((iId == WEAPON_MELEE) || (iId == WEAPON_SWORD))
		return 0; // This is handled differently

	if ((iId == WEAPON_KIBLAST) || (iId == WEAPON_RENZOKU) ||
		(iId == WEAPON_SCATTERSHOT))
	    return KiblastPercent;

	if ((iId == WEAPON_GENERICBEAM) || (iId == WEAPON_BURNINGATTACK))
	    return LightBeamPercent;

	if ((iId == WEAPON_POWERBEAM) || (iId == WEAPON_MASENKO))
	    return MediumBeamPercent;

	if ((iId == WEAPON_GALLITGUN) || (iId == WEAPON_KAMEHAMEHA) ||
	    (iId == WEAPON_FINALFLASH) || (iId == WEAPON_MOUTHBLAST))
	    return HeavyBeamPercent;

	if ((iId == WEAPON_DESTRUCTODISC) || (iId == WEAPON_FRIEZADISC))
	    return DiscPercent;

	if ((iId == WEAPON_EYELASER) || (iId == WEAPON_FINGERLASER) ||
		(iId == WEAPON_CANDY))
	    return LaserPercent;

	if ((iId == WEAPON_SPECIALBEAMCANNON) || (iId == WEAPON_SPIRITBOMB) ||
	    (iId == WEAPON_BIGBANG) || (iId == WEAPON_DEATHBALL) ||
	    (iId == WEAPON_SCATTERBEAM) || (iId == WEAPON_FINISHINGBUSTER) ||
		(iId == WEAPON_TELEKINESIS))
	    return HeavyProjectilePercent;

	if (iId == WEAPON_KAMETORPEDO)
	    return KameTorpPercent;

	if (iId == SBOT_WEAPON_LASER1)
	{
		if (pBot->IsSuperBot())
			return pBot->m_pSBotSettings->laser_percent;
		else
			return 0;
	}

	if (iId == WEAPON_SOLARFLARE)
		return pBot->m_pPersonality->solarflare_percent;

	return 0; // Attack not found :S
}

// Shoot non-chargeble attack(like lasers) task
void CShootNonChargebleTask::TaskThink()
{
	if (!pEnemy)
	{
		if (pBot->m_fHasAnEnemyTime < (gpGlobals->time - 2.0))
			TaskState = DONE;
		else
			pBot->GoToAnPreviousLocation();

		return;
	}

	pBot->BotCombatMovement();

	float EnemyDist = pBot->GetDistance(pEnemy->pev->origin);

	bool PressFire = true, EndTask = false;

	if (pBot->m_fHoldAttackTime <= gpGlobals->time)
		EndTask = true;

	if (pBot->GetKi() < 15)
		EndTask = true;
	else if (pBot->CanAttackPlayer(pEnemy) == false)
		PressFire = false;

	// is it time to stop firing?
	if (EndTask)
	{
		// set next time to shoot
		pBot->AddShootDelay();
		TaskState = DONE;
	}
	else if (PressFire)
		pEdict->v.button |= (pBot->m_pCurrentWeaponInfo->bUseSecondary) ? IN_ATTACK2 : IN_ATTACK;
}

// Swoop task
CSwoopTask::CSwoopTask(CBot *pB) : CBaseTask(pB)
{
	Delay = gpGlobals->time; // No delay otherwise bot might swoop not at all sometimes :|
	ChainSwoopCheckDelay = gpGlobals->time + 0.75f; // First swoop a while

	CheckedSecondary = false;
	HoldSecondary = false;
	SwoopFlags = 0;

	if (IsAvoidDangerSwoop())
		LifeTime = GetSwoopLifeTime();
	else
		SelectSwoopType();

	TurnLessTime = Delay;
}

CSwoopTask::~CSwoopTask()
{
	pBot->m_vSwoopOffset = g_vecZero;
	pBot->m_eSwoopType = SWOOP_NORMAL;
	pBot->m_pSwoopAvoidEnt = NULL;
	pBot->m_sSwoopFlags = SwoopFlags;

	if ((pBot->GetKi() < 450) && (!SwoopFlags))
	{
		float MinPowerUpDist = 650.0f;

		if (pBot->m_pPersonality->aggression_level == Aggressive)
			MinPowerUpDist *= 0.8f;
		else if (pBot->m_pPersonality->aggression_level == Defensive)
			MinPowerUpDist *= 1.2f;

		if ((pBot->m_pBotEnemy) && (pBot->GetDistance(pBot->m_pBotEnemy) >= MinPowerUpDist))
		{
			// Powerup for a short time
			pBot->m_sKiGainTarget = pBot->GetKi() + RANDOM_LONG(100, 200);
			pBot->m_eTaskAfterTask = TaskPowerup;
			PrintAI("Powerup after swoop");
		}
	}
}

void CSwoopTask::TaskThink()
{
	pBot->ResetMoveSpeed();

	if (!pBot->m_sSwoopFlags)
	{
		TaskState = DONE;
		return;
	}

	if ((pEdict->v.movetype != MOVETYPE_PLAYERSWOOP) || (!pEnemy))
	{
		TaskState = DONE;
		return;
	}
	
	EComboType ChainSwoopType;

	// Swoop time is almost over?
	if ((pBot->m_Swoop.GetSwoopTime() > 2.25f) && (pBot->m_pBotSkill->bUsesChainSwoop) &&
		(pBot->GetKi() > 250))
	{
		if (pEnemy)
		{
			if (IsAvoidDangerSwoop())
			{
				// Check if the bot should counter attack...
				short CounterPercent = pBot->m_pPersonality->melee_percent * 0.75;

				if (pBot->m_pPersonality->aggression_level == Defensive)
					CounterPercent *= 0.75;
				else if (pBot->m_pPersonality->aggression_level == Aggressive)
					CounterPercent *= 1.25;

				if ((RANDOM_LONG(1, 100) <= CounterPercent) && (pBot->EntIsInFOV(pEnemy)) &&
					(pBot->GetDistance(pEnemy->pev->origin) < 1500.0f))
				{
					PrintAI("Counter swoop\n");
					MakeCombo(COMBO_TYPE_FORWARD_SWOOP);
					SwoopFlags = SWOOP_ATTACK; // Set this so the bot won't stop with the swoop
					return;
				}
			}
			else
			{
				// Check if the bot should make chain swoop attack combo...

				short ChainSwoopPercent = pBot->m_pPersonality->melee_percent * 0.75;

				if (pBot->m_pPersonality->aggression_level == Defensive)
					ChainSwoopPercent *= 0.75;
				else if (pBot->m_pPersonality->aggression_level == Aggressive)
					ChainSwoopPercent *= 1.25;

				if ((RANDOM_LONG(1, 100) <= ChainSwoopPercent) && (pBot->GetKi() > 250) &&
					(pBot->EntIsInFOV(pEnemy)) &&
					(pBot->GetDistance(pEnemy->pev->origin) > 1250.0f))
				{ 
					ChainSwoopType = pBot->MakeAttackChainSwoop();
					if (ChainSwoopType != COMBO_TYPE_NONE)
					{
						PrintAI("Attack chainswoop\n");
						MakeCombo(ChainSwoopType);
						SwoopFlags = SWOOP_ATTACK; // Set this so the bot won't stop with the swoop
						return;
					}
				}
			}
		}
	}

	// Does the bot swoops to avoid danger?
	if (IsAvoidDangerSwoop())
	{
		if (LifeTime < gpGlobals->time)
		{
			TaskState = DONE;
			return;
		}

		// Check if bot should chain swoop
		if ((ChainSwoopCheckDelay < gpGlobals->time) && (pBot->m_pBotSkill->bUsesChainSwoop) &&
			(pBot->GetKi() > 250))
		{
			ChainSwoopCheckDelay = gpGlobals->time + RANDOM_FLOAT(0.2f, 0.5f);
			float EnemyDist = 9999.9f;
			if (pEnemy)
				EnemyDist = pBot->GetDistance(pEnemy->pev->origin);

			// Does the bot want to extra swoop and is there a valid enemy
			// and is he/she in FOV and is the enemy fairly close?
			if (pEnemy && (RANDOM_LONG(1, 100) <= pBot->m_pPersonality->swoop_percent) &&
				pBot->EntIsInFOV(pEnemy) &&	(EnemyDist < 750.0f))
			{
				// Check if the bot should avoid or attack with the chain swoop
				byte bAttackPercent = 50;

				if (pBot->m_pPersonality->aggression_level == Defensive)
					bAttackPercent = 25;
				else if (pBot->m_pPersonality->aggression_level == Aggressive)
					bAttackPercent = 75;

				bool CanCounterAttack = ((pBot->m_pPersonality->melee_percent > 0) &&
										 (BotManager.BotsShoot()) &&
										 (BotManager.BotsSearchEnemies()) &&
										 (pBot->CanAttackPlayer(pEnemy)));
				if ((RANDOM_LONG(1, 100) <= bAttackPercent) && (CanCounterAttack))
				{
					// counter attack
					PrintAI("Counter swoop(2)\n");
					MakeCombo(COMBO_TYPE_FORWARD_SWOOP);
					SwoopFlags = SWOOP_ATTACK; // Set this so the bot won't stop with the swoop
					return;
				}
				else
				{
					PrintAI("Normal chainswoop(2)\n");
					ChainSwoopType = pBot->MakeNormalChainSwoop();
					if (ChainSwoopType != COMBO_TYPE_NONE)
					{
						MakeCombo(ChainSwoopType);
						SwoopFlags = SWOOP_AVOIDDANGER; // Set this so the bot won't stop with the swoop
						return;
					}
				}
			}
			else // else check if the bot is about to hit something
			{
				TraceResult tr;
				UTIL_TraceHull(pEdict->v.origin, pBot->m_Swoop.GetCurrentDirection() * 50,
							  ignore_monsters, human_hull, pEdict, &tr);

				if (tr.flFraction < 1.0f) // Trace hit something?
				{
					if (EnemyDist < 750.0f) // Enemy is close?
					{
						if (RANDOM_LONG(1, 100) <= pBot->m_pPersonality->swoop_percent)
						{
							ChainSwoopType = pBot->MakeNormalChainSwoop();
							if (ChainSwoopType != COMBO_TYPE_NONE)
							{
								PrintAI("ChainSwoop : Bot is about to hit something\n");
								MakeCombo(ChainSwoopType);
								SwoopFlags = SWOOP_AVOIDDANGER; // Set this so the bot won't stop with the swoop
								return;
							}
						}
					}
					// else look if the bot is real close to the obstacle
					if (tr.flFraction < 0.2f)
					{
						PrintAI("Stop swoop : Close wall\n");
						TaskState = DONE;
						return; // Just stop with the swoop
					}
				}
			}
		}
	}
	else
	{
		if ((pBot->m_pPersonality->melee_percent == 0) || (BotManager.BotsShoot() == false) ||
			(BotManager.BotsSearchEnemies() == false))
		{
			TaskState = DONE;

			if ((pBot->m_pActiveItem) && ((pBot->m_pActiveItem->m_iId == WEAPON_MELEE) ||
				(pBot->m_pActiveItem->m_iId == WEAPON_SWORD)))
			{
				// Select to another weapon incase the bot hits his enemy so that the bot 
				// won't melee his enemy 'by accident'(suggested by eekhoorn)
				pBot->SelectItem("weapon_genericbeam");
			}
			return;
		}

		// Can the enemy not be hit?
		if (!pBot->CanMelee(pEnemy))
		{
			PrintAI("Stop swoop : Can't hit enemy\n");
			TaskState = DONE;
			return;
		}

		if ((pBot->m_fYawSpeed < 20.0f) && (pBot->m_fPitchSpeed < 20.0f))
			TurnLessTime = gpGlobals->time;
		else if ((gpGlobals->time - TurnLessTime) > 1.0f) // Bot is turning for a while?
		{
			// Check if the bot can chainswoop away
			if ((pBot->m_pBotSkill->bUsesChainSwoop) && (pBot->GetKi() > 250) &&
				((ChainSwoopType = pBot->MakeAwayChainSwoop(pEnemy)) != COMBO_TYPE_NONE))
			{
				PrintAI("Away chain swoop\n");
				MakeCombo(ChainSwoopType);
				SwoopFlags = SWOOP_AVOIDDANGER; // Keep swooping
				TaskState = DONE;
				return;
			}
		}

		float EnemyDist = pBot->GetDistance(pEnemy->pev->origin);

		if ((pBot->m_fYawSpeed > 100.0f) && (EnemyDist > 700.0f))
		{
			PrintAI("Stop swoop : To big yaw\n");
			TaskState = DONE; // Don't swoop when the bot has to turn much & enemy isn't close
			return;
		}

		if (pBot->m_pActiveItem)
		{
			if ((pBot->m_pActiveItem->m_iId != WEAPON_SWORD) && (pEdict->v.playerclass == 7))
				pBot->SelectItem("weapon_sword");
			else if ((pBot->m_pActiveItem->m_iId != WEAPON_MELEE) && (pEdict->v.playerclass != 7))
				pBot->SelectItem("weapon_melee");
		}

		if (pBot->m_eSwoopType != SWOOP_NORMAL)
		{
			float MinDist, MaxDist;

			if (pBot->m_eSwoopType == SWOOP_RANDOM_OFFSET)
			{
				MinDist = 200.0f;
				MaxDist = 1700.0f;
			}
			else
			{
				MinDist = 750.0f;
				MaxDist = 2200.0f;
			}

			if ((EnemyDist >= MinDist) && (EnemyDist <= MaxDist))
			{
				pBot->AimToVec(pEnemy->pev->origin); // Set AimVec

				if (pBot->m_eSwoopType == SWOOP_RANDOM_OFFSET)
				{
					if (SwoopOffsetXFactor > 0)
					{
						if (pBot->m_vSwoopOffset.x >= SwoopOffsetXTarget)
						{
							SwoopOffsetXTarget = RANDOM_LONG(-18, -16);
							SwoopOffsetXFactor = RANDOM_LONG(-2, -3);
						}
					}
					else
					{
						if (pBot->m_vSwoopOffset.x <= SwoopOffsetXTarget)
						{
							SwoopOffsetXTarget = RANDOM_LONG(16, 18);
							SwoopOffsetXFactor = RANDOM_LONG(2, 3);
						}
					}

					if (SwoopOffsetYFactor > 0)
					{
						if (pBot->m_vSwoopOffset.y >= SwoopOffsetYTarget)
						{
							SwoopOffsetYTarget = RANDOM_LONG(-18, -16);
							SwoopOffsetYFactor = RANDOM_LONG(-2, -3);
						}
					}
					else
					{
						if (pBot->m_vSwoopOffset.y <= SwoopOffsetYTarget)
						{
							SwoopOffsetYTarget = RANDOM_LONG(16, 18);
							SwoopOffsetYFactor = RANDOM_LONG(2, 3);
						}
					}

					pBot->m_vSwoopOffset.x += SwoopOffsetXFactor;
					pBot->m_vSwoopOffset.y += SwoopOffsetYFactor;

					pBot->SetIdealAngles(WrapAngles(pBot->m_vAimVec + pBot->m_vSwoopOffset));
				}
				else
				{
					pBot->SetIdealAngles(WrapAngles(pBot->m_vAimVec + pBot->m_vSwoopOffset));
				}
			}
		}
	}

	pBot->SetSwoopButton();

	if (CheckedSecondary == false)
		CheckSecondaryFire();

	if (HoldSecondary)
		pEdict->v.button |= IN_ATTACK2;

	ChainSwoopType = pBot->AvoidAttacksWhileSwoop();
	if (ChainSwoopType != COMBO_TYPE_NONE)
		MakeCombo(ChainSwoopType);
}

void CSwoopTask::CheckSecondaryFire()
{
	CheckedSecondary = true;

	if (CVAR_GET_FLOAT("mp_simplemelee") == 1.0f)
	{
		HoldSecondary = true; // Always hold secondary fire
		return;
	}

	short SecondaryPercent = pBot->m_pPersonality->simplemelee_percent;

	// Is the enemy low on health and the bot smart?
	if ((pEnemy->pev->health < 5) && (pBot->m_bSkillNr <= 2))
		SecondaryPercent *= 1.5f;

	if (pBot->GetKi() < 300)
		SecondaryPercent *= 1.2f; // Prepunching takes more ki...

	if (RANDOM_LONG(1, 100) <= SecondaryPercent)
		HoldSecondary = true;
}

void CSwoopTask::SelectSwoopType()
{
	if (!pEnemy)
		return;

	float dist = pBot->GetDistance(pEnemy->pev->origin);

	pBot->m_eSwoopType = SWOOP_NORMAL;

	if ((RANDOM_LONG(1, 100) <= 70) && (pBot->m_bSkillNr == 0) && (dist > 800.0))
	{
		if (RANDOM_LONG(0, 1))
		{
			pBot->m_eSwoopType = SWOOP_RANDOM_OFFSET;
			pBot->m_vSwoopOffset = g_vecZero;

			if (RANDOM_LONG(0, 1))
			{
				SwoopOffsetXTarget = RANDOM_LONG(16, 18);
				SwoopOffsetXFactor = RANDOM_LONG(2, 3);
			}
			else
			{
				SwoopOffsetXTarget = RANDOM_LONG(-18, -16);
				SwoopOffsetXFactor = RANDOM_LONG(-3, -2);
			}

			if (RANDOM_LONG(0, 1))
			{
				SwoopOffsetYTarget = RANDOM_LONG(16, 18);
				SwoopOffsetYFactor = RANDOM_LONG(2, 3);
			}
			else
			{
				SwoopOffsetYTarget = RANDOM_LONG(-18, -16);
				SwoopOffsetYFactor = RANDOM_LONG(-3, -2);
			}
		}
		else
		{
			TMultiChoice<Vector> SwoopOffsetChoice;
			Vector OffsetVec = g_vecZero;

			if (pBot->GetCollisionDist(dir_left, 200.0f) >= 200.0f)
				SwoopOffsetChoice.Insert(Vector(0, 20, 0), 50);
			if (pBot->GetCollisionDist(dir_right, 200.0f) >= 200.0f)
				SwoopOffsetChoice.Insert(Vector(0, -20, 0), 50);
			if (pBot->GetCollisionDist(dir_up, 200.0f) >= 200.0f)
				SwoopOffsetChoice.Insert(Vector(20, 0, 0), 50);
			if (pBot->GetCollisionDist(dir_down, 200.0f) >= 200.0f)
				SwoopOffsetChoice.Insert(Vector(-20, 0, 0), 50);

			if (SwoopOffsetChoice.GetSelection(OffsetVec))
			{
				pBot->m_vSwoopOffset = OffsetVec;
				pBot->m_eSwoopType = SWOOP_OFFSET;
			}
		}
	}
}

float CSwoopTask::GetSwoopLifeTime()
{
	float TraceDist = static_cast<float>(pBot->m_sDangerLevel) * SWOOP_TRACE_DIST_FACTOR;
	if (TraceDist > MAX_SWOOP_TRACE_DIST)
		TraceDist = MAX_SWOOP_TRACE_DIST;

	Vector v_src = pEdict->v.origin;
	Vector v_end;

	TraceResult tr;

	UTIL_MakeVectors(pEdict->v.v_angle);

	switch(pBot->m_Swoop.m_iSwoopDirection)
	{
	case BACK:
		v_end = v_src - gpGlobals->v_forward * TraceDist;
		break;
	case LEFT:
		v_end = v_src - gpGlobals->v_right * TraceDist;
		break;
	case RIGHT:
		v_end = v_src + gpGlobals->v_right * TraceDist;
		break;
	case UP:
		v_end = v_src + gpGlobals->v_up * TraceDist;
		break;
	case DOWN:
		v_end = v_src - gpGlobals->v_up * TraceDist;
		break;
	}

	UTIL_TraceHull(v_src, v_end, dont_ignore_monsters, human_hull, pEdict, &tr);

	if (tr.flFraction <= 0.0f)
		return 0.1f; // Just return something otherwise we get a zero devide

	return (((tr.flFraction * TraceDist) / MAX_SWOOP_TRACE_DIST ) * 2.0f) + gpGlobals->time;
}

void CCounterMeleeTask::TaskThink()
{
	if (!pBot->m_fMelee)
	{
		TaskState = DONE;
		return;
	}

	pBot->ResetMoveSpeed(); // Othwerwise bots won't perform combo's properly...

	if (!pBot->m_Melee.m_fPreKnockStrug)
		pEdict->v.button |= IN_ATTACK; // Set this so bots can reteliate when possible

	switch (pBot->m_Melee.m_eAdvMeleeStatus)
	{
	case CHARGE_ATTACK:
		pBot->ChargeMeleeCombo();
		break;
	case EXE_DEFEND:
		if (pBot->m_Melee.CanRetaliate())
		{
			pBot->ChargeMeleeCombo();
			break;
		}

		// else don't break so the bot will defend
	case CHARGE_DEFEND:
	case STRUGGLE_DEFEND:
	case STRUGGLE_ATTACK:
	case STRUGGLE_BURST_ATTACK:
	case EXE_ATTACK:
		if (pBot->m_MeleeDefendButtons.Empty() == false)
		{
			int ButtonToReactOn = pBot->m_MeleeDefendButtons.Pop();

			bool TakeARandomButton = (ButtonToReactOn == 0);
/*
			// If bot suspects a combo and is smart enough to suspect a combo anyway?
			if ((pBot->SuspectACombo()) && (pBot->m_bSkillNr <= 1))
			{
				pBot->HandleSuspectedCombo();
				break;
			}
*/
			if (!TakeARandomButton)
			{
				if (RANDOM_LONG(1, 100) <= pBot->m_pBotSkill->RandomComboButtonPercent)
					TakeARandomButton = true;
			}

			if (TakeARandomButton)
			{
				// Set a random combo button
				switch (RANDOM_LONG(1, 4))
				{
				case 1:
					ButtonToReactOn = IN_FORWARD;
					break;
				case 2:
					ButtonToReactOn = IN_BACK;
					break;
				case 3:
					ButtonToReactOn = IN_MOVELEFT;
					break;
				case 4:
					ButtonToReactOn = IN_MOVERIGHT;
					break;
				}
			}

			pEdict->v.button |= ButtonToReactOn;
		}
	
		break;
	default:
		TaskState = DONE;
	}
}

void CHuntEnemyTask::TaskThink()
{
	if (UTIL_IsValidPlayer(pBot->m_pHuntPlayer) == false)
	{
		TaskState = DONE;
		return;
	}

	// Hunting with use of waypoints?
	if ((pBot->m_pCurrentGoalWaypoint) && (WaypointClass.m_iWaypointCount))
	{
		if (pBot->ReachedGoalWP())
		{
			// Something went wrong; reached the goal waypoint but still have no enemy
			TaskState = DONE;
			return;
		}

		if (pBot->CurrentWPIsValid() == false)
		{
			// find the nearest reachable waypoint
			int WP = WaypointClass.GetNearestWaypoint(pBot, 1250.0f, pEdict->v.team);

			if (WP != -1)
			{
				pBot->SetCurrentWaypoint(WP);
			}
			else
			{
				pBot->ResetWaypointVars();
				TaskState = DONE;
				return;
			}
		}

		// Time to update enemy's position?
		if (m_fHuntPlayerUpdateTime < gpGlobals->time)
		{
			m_fHuntPlayerUpdateTime = gpGlobals->time + 1.5f; // Don't do this every frame

			int Wpt = WaypointClass.GetNearestWaypoint(pBot->m_pHuntPlayer, 750.0f,
													   (g_teamplay) ? pEdict->v.team : -1);
			// Is there is a valid waypoint near this enemy?
			if (Wpt != -1)
			{
				// See if there is a route to the waypoint near the enemy waypoint
				if (WaypointClass.RouteFromTo(pBot->m_sCurrentWPIndex, Wpt, (g_teamplay) ? pEdict->v.team : -1))
				{
					// Update goal waypoint
					pBot->SetGoalWaypoint(Wpt);
				}
			}
		}

		if (!pBot->HeadToWaypoint())
		{
			TaskState = DONE;
			return;
		}
	}
	else
	{
		float Dist = pBot->GetDistance(pBot->m_HuntPos.Pos);
		bool Done = false;

		if (Dist < 60.0)
			Done = true;

		if ((pBot->m_fPrevHuntDist > 0.0) && (Dist > pBot->m_fPrevHuntDist))
			Done = true;

		pBot->m_fPrevHuntDist = Dist;

		// Did we reached the point ?
		if (Done == false)
		{
			// Guess not

			// Time to look for another one ?
			if (m_fHuntPlayerUpdateTime < gpGlobals->time)
			{
				m_fHuntPlayerUpdateTime = gpGlobals->time + 0.8;
				short PosVal, BestPosVal = 0, BestPosIndex = -1;
				float NearestDistToEnemy = 9999.9f;
				float NearestDistToBot = 9999.9f;

				for (short j=(MAX_OLD_POSITIONS-1);j>=0;j--)
				{
					PosVal = 1;
					Vector OldPos = pBot->m_pHuntPlayer->m_OldPositions[j].Pos;

					if (pBot->m_pHuntPlayer->m_OldPositions[j].flCreateTime < 
						(gpGlobals->time-MAX_OLD_POSITION_LIFE_TIME))
						continue;

					Dist = pBot->GetDistance(OldPos);
					if ((Dist > 6000.0) || (Dist < 300.0))
						continue;

					if (pBot->m_LastHuntPos.Pos == pBot->m_pHuntPlayer->m_OldPositions[j].Pos)
						continue;

					if (pBot->FVisible(OldPos) == false)
						continue;

					if (Dist < NearestDistToEnemy)
					{
						NearestDistToEnemy = Dist;
						PosVal++;
					}

					if (Dist < NearestDistToBot)
					{
						NearestDistToBot = Dist;
						PosVal++;
					}

					if (PosVal >= BestPosVal)
					{
						BestPosVal = PosVal;
						BestPosIndex = j;
					}
				}
				if (BestPosIndex > -1)
				{
					pBot->m_LastHuntPos = pBot->m_HuntPos;
					pBot->m_HuntPos = pBot->m_pHuntPlayer->m_OldPositions[BestPosIndex];
				}
			}
		}
		else
		{
			if (!pBot->CheckForHuntEnemy())
				TaskState = DONE; // Couldn't hunt any further, kill the task
			return;
		}

		// Aim to it
		pBot->AimToVec(pBot->m_HuntPos.Pos);

		// Turn turbo on
		pBot->ToggleTurbo(true);
	}
}

// Superbot task, absorb health from multiple players
bool CAbsorbHealthTask::ShootAbsorbHealthBeams()
{
	CBasePlayer *pPlayer = NULL;

	if (!pBot->m_bMadePlayerList)
		pBot->MakePlayerList();

	if (pBot->m_bPlayerCount == 0)
		return false;

	bool ShotABeam = false;

	for (int i=0;i<pBot->m_bPlayerCount;i++)
	{
		pPlayer = pBot->m_pPlayerList[i];

	   	// is team play enabled?
		if (g_teamplay)
		{
			// don't target your teammates...
			if (pEdict->v.team == pPlayer->pev->team)
			    continue;
		}

		if (!pBot->CanAttackPlayer(pPlayer))
			continue;

		float VelocityFactor = 0.95f;
		long PlDiff = pBot->m_iPowerLevel - pPlayer->m_iPowerLevel;

		if (PlDiff > 0)
			VelocityFactor = float(PlDiff) / 10000000.0f;

		if (VelocityFactor < 0.05)
			VelocityFactor = 0.05; // Player should be atleast 5% slowed down
		else if (VelocityFactor > 0.95f)
			VelocityFactor = 0.95f; // But he should be able to move aswell...

		pPlayer->pev->velocity = pPlayer->pev->velocity - pPlayer->pev->velocity.Normalize() * (pPlayer->pev->velocity.Length() * VelocityFactor);

		pBot->ShootHealthBeam(pPlayer);

		if (!ShotABeam)
			ShotABeam = true;

		if (pEdict->v.health >= pBot->m_iMaxHealth) // we drained enough :-)
			return false;
	}

	if (pBot->m_fLastBeamDrawTime <= gpGlobals->time)
		pBot->m_fLastBeamDrawTime = gpGlobals->time + 0.19;

	return ShotABeam;
}

void CShootCandyTask::TaskThink()
{
	if (pBot->GetKi() < 50)
	{
		TaskState = DONE;
		return;
	}

	if (pBot->m_pBotEnemy == NULL)
	{
		if (MoveToCookieTime == 0.0)
			MoveToCookieTime = gpGlobals->time + 1.0f;
		else if ((MoveToCookieTime < gpGlobals->time) || (!pBot->m_pLastEnemy))
		{
			TaskState = DONE;
			return;
		}

		if (CookieCheckDelay < gpGlobals->time)
		{
			CookieCheckDelay = gpGlobals->time + 0.15;

			CBaseEntity *pCookie = NULL;
			bool FoundOne = false;

			while ((pCookie = UTIL_FindEntityByClassname(pCookie, "candy")) != NULL)
			{
				if (IsNull(pCookie))
					continue;

				/* Needed? I think you can all cookies even if it was your team mate :-\
				if (pCookie->pev->owner != pBot->m_pLastEnemy->edict())
				{
//					lcout << "Wrong owner\n";
					continue;
				}*/

				if (pBot->GetDistance(pCookie->pev->origin) > 750.0)
					continue;

				FoundOne = true;
				break;
			}

			if ((FoundOne) || ((MoveToCookieTime - gpGlobals->time) > 0.75))
			{
				if (pCookie)
					pBot->AimToVec(pCookie->pev->origin);
			}
			else
			{
				//lcout << "No Cookie\n";
				TaskState = DONE;
			}
		}
		return;
	}
				
	if (pBot->CanAttackPlayer(pBot->m_pBotEnemy) == false)
	{
//		lcout << "Invalid Enemy\n";
		TaskState = DONE;
		return;
	}

	float EnemyDist = pBot->GetDistance(pBot->m_pBotEnemy->pev->origin);

	if (EnemyDist > 950.0)
	{
//		lcout << "Dist to high\n";
		TaskState = DONE;
		return;
	}
	else if (EnemyDist > 500.0)
	{
		if (TeleCheckTime <= gpGlobals->time)
		{
			TeleCheckTime = gpGlobals->time + 0.2;

			if (RANDOM_LONG(1, 100) <= pBot->m_pPersonality->teleport_percent)
				pBot->Teleport();
		}
		else if (pBot->GetKi() > 250)
			pBot->ToggleTurbo(true); // Just toggle turbo on
	}
	else if (EnemyDist < 75.0)
	{
		EnemyIsInRangeTime = gpGlobals->time;
		MoveBackTime = gpGlobals->time + 0.35;
//		lcout << "Go back\n";
	}
	else
		EnemyIsInRangeTime = gpGlobals->time;

	// Is the enemy to far for a while?
	if (EnemyIsInRangeTime < (gpGlobals->time - 3.5f))
	{
		TaskState = DONE;
		return;
	}

	if (MoveBackTime > gpGlobals->time)
		pBot->SetMoveDir(dir_back);

	if ((pBot->m_pActiveItem) && (pBot->m_pActiveItem->m_iId != WEAPON_CANDY))
		pBot->SelectItem("weapon_candy");

	pEdict->v.button |= IN_ATTACK;
}

// Shoot scattershots task
CShootScatterShotsTask::CShootScatterShotsTask(CBot *pB) : CBaseTask(pB)
{
	ShootTime = Delay + RANDOM_FLOAT(2.75, 3.45);
	StartTime = Delay;

	if ((pBot->m_bSkillNr < 2) && (RANDOM_LONG(1, 100) < 20))
		UseScatterCheat = true;
	else
		UseScatterCheat = false;
}

CShootScatterShotsTask::~CShootScatterShotsTask()
{
	pBot->AddShootDelay();
	pEdict->v.button |= IN_ATTACK2;
}

void CShootScatterShotsTask::TaskThink()
{
	// Time to stop or not much ki left?
	if ((ShootTime < gpGlobals->time) || (pBot->GetKi() < 20))
	{
		TaskState = DONE;
		return;
	}

	if ((pBot->m_pBotEnemy) || (pBot->m_fHasAnEnemyTime > (gpGlobals->time - 2.0)))
	{
//		lcout << "Firing scattershot\n";

		if (pBot->m_pBotEnemy == NULL)
		{
			pBot->GoToAnPreviousLocation();
			return;
		}
		else
			pBot->BotCombatMovement();

		if (pBot->CanAttackPlayer(pBot->m_pBotEnemy) == false)
			return; // Wait untill the enemy can be hit again.

		pEdict->v.button |= IN_ATTACK;   // Fire another blast

		if (UseScatterCheat)
			pEdict->v.button |= IN_ATTACK2; // And home it immediately :D
		else if ((StartTime + 0.55f) <= gpGlobals->time)
		{
			CBaseEntity *pScatterShot = GetFurthestScatterShot();
			if (pScatterShot)
			{
				float FurthestScatterShotDist = pBot->GetDistance(pScatterShot->pev->origin);
				float EnemyDist = pBot->GetDistance(pBot->m_pBotEnemy->pev->origin);

				// Time to let all shots come together?
				if (EnemyDist < (FurthestScatterShotDist - (225.0 + (25 * (pBot->m_bSkillNr + 1)))))
				{
//					lcout << "Binding scattershots\n";
					pEdict->v.button |= IN_ATTACK2;
					TaskState = DONE;
					return;
				}
			}
		}

		if (pBot->m_bSkillNr < 2)
		{
			pBot->ToggleTurbo(true);
			pBot->m_fTurboTime = gpGlobals->time + 1.5;
		}
	}
	else
	{
		TaskState = DONE;
	}
}

CBaseEntity *CShootScatterShotsTask::GetFurthestScatterShot()
{
	CBaseEntity *pScatterShot = NULL;
	CBaseEntity *pFurthestScatterShot = NULL;
	float FurthestDist = 0.0f;
	float Dist;

	while ((pScatterShot = UTIL_FindEntityByClassname(pScatterShot, "scattershot")) != NULL)
	{
		if (IsNull(pScatterShot))
			continue;

		if (pScatterShot->pev->owner != pEdict)
			continue;

		Dist = pBot->GetDistance(pScatterShot->pev->origin);
		if (Dist > FurthestDist)
		{
			FurthestDist = Dist;
			pFurthestScatterShot = pScatterShot;
		}
	}

	return pFurthestScatterShot;
}

// Charge attack task
void CChargeAttackTask::TaskThink()
{
	if (!pBot->m_pCurrentWeaponInfo)
	{
		TaskState = DONE;
		return;
	}

	if ((pBot->m_pBotEnemy) || (pBot->m_fHasAnEnemyTime > (gpGlobals->time - 2.0)))
	{
		int iId = pBot->m_pCurrentWeaponInfo->iId;

		if ((iId == WEAPON_MASENKO) || (iId == WEAPON_POWERBEAM) ||
			(iId == WEAPON_SPIRITBOMB) || (iId == WEAPON_FINALFLASH) ||
			(iId == WEAPON_BIGBANG) || (iId == WEAPON_FINISHINGBUSTER) ||
			(iId == WEAPON_SCATTERBEAM))	
		{
			pBot->ResetMoveSpeed(); // Don't try to move when the bot can't
		}
		else if (pBot->m_pBotEnemy == NULL)
			pBot->GoToAnPreviousLocation();
		else
			pBot->BotCombatMovement();

		float EnemyDist = 9999.9;

		if (pBot->m_pBotEnemy)
		{
			EnemyDist = pBot->GetDistance(pBot->m_pBotEnemy->pev->origin);
		
			if (pBot->m_pBotEnemy->pev->velocity.Length() > 550.0f)
				EnemyMovingFastTime = gpGlobals->time;
			else
				EnemyMovingSlowTime = gpGlobals->time;
		}

		bool ReleaseFire = false;

		if (pBot->m_iChargePercent == 100) // TODO: make a target value
			ReleaseFire = true;

		if (pBot->GetKi() < 15)
			ReleaseFire = true;
		else if (pBot->m_pBotEnemy)
		{
			// Is the enemy moving fast?(but not for a very long while(bots don't want to wait to long...))
			if ((EnemyMovingFastTime > (gpGlobals->time - 0.5f)) &&
				(EnemyMovingSlowTime > (gpGlobals->time - 4.0f)))
			{
				// Also the bot doesn't head 'very much' to the bot?
				if (UTIL_GetHeadingPercentage(pBot, pBot->m_pBotEnemy) < 75)
					ReleaseFire = false; // Then wait with fire
			}
			else if (pBot->CanAttackPlayer(pBot->m_pBotEnemy) == false)
				ReleaseFire = false; // Wait till the bot can attack again
			// Is the bot defending a db gather spot?
			else if ((pBot->m_eBotDBType == DB_DEFENDER) && (BotManager.IsDBMode()))
			{
				// Enemy close?
				if ((EnemyDist < 750.0f) && (pBot->m_iChargePercent >= 33))
					ReleaseFire = true;
			}
		}


		// is it time to fire the charged weapon?
		if (ReleaseFire)
		{
			// set next time to shoot
			pBot->AddShootDelay();

			// Check first if the enemy is ascending and if the bot is NOT a transkiller?
			if ((pBot->m_pBotEnemy) && (pBot->m_pBotEnemy->m_fAscending) &&
				(!pBot->m_pPersonality->transkiller))
			{
				// Don't fire but just switch to another weapon(melee) so the attack will
				// 'disappear'
				if (pEdict->v.playerclass == 7) // Class == trunks?
					pBot->SelectItem("weapon_sword"); // Trunks doesn't have melee, but a sword instead
				else
					pBot->SelectItem("weapon_melee");

				TaskState = DONE;
				return;
			}

			if (pBot->m_pCurrentWeaponInfo->bControllable && (pBot->m_iChargePercent > 0))
			{
				if (iId == WEAPON_FRIEZADISC)
				{
					pBot->m_eTaskAfterTask = TaskControlFriezaDisc;
				}
				else
				{
					// Is the bot a beam lamer?
					if (RANDOM_LONG(1, 100) <= 	pBot->m_pPersonality->beamexplode_percent)
						pBot->m_bLetBeamExplode = true;
					else
						pBot->m_bLetBeamExplode = false;

					if ((!pBot->m_pBotEnemy) && (pBot->BotCanBeamJump(false)) &&
						(pBot->m_pCurrentWeaponInfo->bCanBeamJump))
						pBot->m_eTaskAfterTask = TaskBeamJump;
					else
						pBot->m_eTaskAfterTask = TaskControlBeam;
				}
			}

			TaskState = DONE;
		}
		else
		{
			pEdict->v.button |= (pBot->m_pCurrentWeaponInfo->bUseSecondary) ? IN_ATTACK2 : IN_ATTACK;

			// If the bot has not much ki left and turbo is on...
			if ((pBot->GetKi() < 125) && (pBot->m_fTurbo) && (pBot->m_iChargePercent < 85))
				pBot->ToggleTurbo(false); // ...toggle turbo off
			// Else if the bot is charging a beam, has a enough ki, is skilled enough
			// and just begun charging, turn turbo on.
			else if ((iId != WEAPON_DESTRUCTODISC) && (iId != WEAPON_FRIEZADISC) &&
					 (iId != WEAPON_TELEKINESIS) &&	(pBot->m_pBotSkill->bUsesTurbo) &&
					 (pBot->GetKi() > 250))
			{
				if (pBot->m_iChargePercent <= 10)
   					pBot->ToggleTurbo(true);
			}

			pBot->m_fTurboTime = gpGlobals->time + 1.5; // Keep turbo on(if on...)
		}
	}
	else
	{
		TaskState = DONE;
	}
}

void CControlBeamTask::TaskThink()
{
	pBot->m_fTurboTime = gpGlobals->time + 1.5; // Keep turbo on(if on...)

	if ((pBot->m_fControl) || ((ControlBeamTime - gpGlobals->time) > 9.5))
	{
		if (ControlBeamTime >= gpGlobals->time)
		{
			if (pBot->IsValidEnemy(pBot->m_pBotEnemy))
			{
				Vector v_aim;

				CBaseEntity *pAttack = UTIL_GetLastAttackFromPlayer(pBot);

				if (pAttack)
				{
					CBaseEntity *pEnemyAttack = UTIL_GetLastAttackFromPlayer(pBot->m_pBotEnemy);
					Vector v_attack = pAttack->pev->origin;

					if ((pEnemyAttack) && ((IsBeamAttack(pEnemyAttack->edict())) || 
						(IsNonBeamAttack(pEnemyAttack->edict()))))
					{
						// Aim at the enemies attack instead?
						if (EntIsInFieldOfView(pAttack, pEnemyAttack))
						{
							pBot->AimToVec(pEnemyAttack->pev->origin);
							BotShouldAimAtAttack = true;
							return;
						}
					}

					float BotToAttackDist = pBot->GetDistance(v_attack);
					float EnemyToAttackDist = (v_attack - pBot->m_pBotEnemy->pev->origin).Length();

					if ((pBot->m_bLetBeamExplode) && 
						(EnemyToAttackDist <= pBot->m_pBotSkill->flExplodeBeamDistance))
					{
						pEdict->v.button |= IN_ATTACK2;
						TaskState = DONE;
						return;
					}

					float EnemyDist = UTIL_Distance(pEdict->v.origin, v_attack);
					// If the attack is much further away then the enemy...
					if ((BotToAttackDist - 500.0f) > EnemyDist)
					{
						// ... then explode the attack
						pEdict->v.button |= IN_ATTACK2;
						TaskState = DONE;
						return;
					}
						
					if ((BotToAttackDist - 100.0) < EnemyDist)
						pBot->AimAtEnemy(pBot->m_pBotEnemy); // Predict enemy's pos
					else
						pBot->AimToVec(pBot->m_pBotEnemy->pev->origin); // Don't predict enemy's pos
				}
				else if ((ControlBeamTime - gpGlobals->time) < 9.0)
				{
					// Still no attack owned by this bot?
					pEdict->v.button |= IN_ATTACK2;
					TaskState = DONE;
					return;
				}
				else
					pBot->AimAtEnemy(pBot->m_pBotEnemy);
			}
			else
			{
				CBaseEntity *pAttack = UTIL_GetLastAttackFromPlayer(pBot);
				if (pAttack)
				{
					float MinExplodeAttackDist = pAttack->pev->dmg * 12.0;

					float AttackDist = pBot->GetDistance(pAttack->pev->origin);

					if (AttackDist < MinExplodeAttackDist)
					{
						// DON'T explode the attack when its (still) near the bot...

						UTIL_MakeVectors(pAttack->pev->angles);
						Vector v_src = pAttack->pev->origin;
						Vector v_end = v_src + gpGlobals->v_forward * 800.0;
						TraceResult tr;

						UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull,
									   pAttack->pev->pContainingEntity, &tr);
						float TraceDist = tr.flFraction * 800.0;

						// Is the attack about to hit a wall ?
						if (TraceDist < 800.0)
						{
							// Beam jump ?
							if (pBot->BotCanBeamJump(true))
							{
								pBot->m_eTaskAfterTask = TaskBeamJump;
								TaskState = DONE;
								return;
							}

							// Try to move it away
							if (RANDOM_LONG(0, 1) == 0)
							{
								// first check left then right

								if (GetCollisionDist(pAttack, dir_left, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.y = 
										UTIL_WrapAngle(pBot->m_vAimVec.y + RANDOM_FLOAT(40.0, 50.0));
									return;
								}
								else if (GetCollisionDist(pAttack, dir_right, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.y = 
										UTIL_WrapAngle(pBot->m_vAimVec.y - RANDOM_FLOAT(40.0, 50.0));
									return;
								}
							}
							else
							{
								// first check right then left

								if (GetCollisionDist(pAttack, dir_right, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.y = 
										UTIL_WrapAngle(pBot->m_vAimVec.y - RANDOM_FLOAT(40.0, 50.0));
									return;
								}
								else if (GetCollisionDist(pAttack, dir_left, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.y = 
										UTIL_WrapAngle(pBot->m_vAimVec.y + RANDOM_FLOAT(40.0, 50.0));
									return;
								}
							}

							// Still no solution found ? Then check up or down
							if (RANDOM_LONG(0, 1) == 0)
							{
								// first check down then up
								if (GetCollisionDist(pAttack, dir_down, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.x = 
										UTIL_WrapAngle(pBot->m_vAimVec.x + RANDOM_FLOAT(40.0, 50.0));
									return;
								}
								else if (GetCollisionDist(pAttack, dir_up, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.x = 
										UTIL_WrapAngle(pBot->m_vAimVec.x - RANDOM_FLOAT(40.0, 50.0));
									return;
								}
							}
							else
							{
								// first check up then down
								if (GetCollisionDist(pAttack, dir_up, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.x = 
										UTIL_WrapAngle(pBot->m_vAimVec.x - RANDOM_FLOAT(40.0, 50.0));
									return;
								}
								else if (GetCollisionDist(pAttack, dir_down, 800.0) >= 800.0f)
								{
									pBot->m_vAimVec.x = 
										UTIL_WrapAngle(pBot->m_vAimVec.x + RANDOM_FLOAT(40.0, 50.0));
									return;
								}
							}
						}
					}
					return;
				}	
				pEdict->v.button |= IN_ATTACK2;
				TaskState = DONE;
				return;
			}
		}
		else
		{
			pEdict->v.button |= IN_ATTACK2;
			TaskState = DONE;
			return;
		}
	}
	else
	{
		TaskState = DONE;
		return;
	}
}

void CControlFriezaDiscTask::TaskThink()
{
	if (LifeTime < gpGlobals->time)
	{
		TaskState = DONE;
		return;
	}

	if (pBot->m_pBotEnemy == NULL)
	{
		TaskState = DONE;
		return;
	}

	if (pBot->GetMaxSpeed() > 0.0f)
		pBot->BotCombatMovement();

	CBaseEntity *pAttack = UTIL_GetAttackFromPlayerByName(pBot, "Frieza Disc");

	if (!pAttack)
	{
		if ((LifeTime - gpGlobals->time) < (RealDiscTime - 1.0))
		{
			TaskState = DONE;
		}
		return;
	}

	if (pBot->m_pBotEnemy->pev->velocity.Length() > 550.0f)
		EnemyMovingFastTime = gpGlobals->time;
	else
		EnemyMovingSlowTime = gpGlobals->time;

	float DiscDist = pBot->GetDistance(pAttack->pev->origin);
	float EnemyDist = pBot->GetDistance(pBot->m_pBotEnemy->pev->origin);
	float MinCallBackDist = DiscDist - ((float(pBot->m_bSkillNr + 1) * 250.0) / 1.5);

	if ((MinCallBackDist > EnemyDist) && (pBot->GetMaxSpeed() == 0.0))
		pEdict->v.button |= IN_ATTACK2; // call the disc back

	// Is the disc back 'at home'?
	if (pAttack->pev->velocity.Length() == 0.0)
	{
		bool ThrowDisc = true;

		// Is the enemy moving fast?(but not for a very long while(bots don't want to wait to long...))
		if ((EnemyMovingFastTime > (gpGlobals->time - 0.5f)) &&
			(EnemyMovingSlowTime > (gpGlobals->time - 4.0f)))
		{
			// Also the bot doesn't head 'very much' to the bot?
			if (UTIL_GetHeadingPercentage(pBot, pBot->m_pBotEnemy) < 75)
				ThrowDisc = false; // Wait a bit with firing the disc
		}

		if (ThrowDisc)
		{
			if (pBot->CanAttackPlayer(pBot->m_pBotEnemy) == false)
				ThrowDisc = false; // Wait untill the enemy can be hurt again.
		}

		if (ThrowDisc)
			pEdict->v.button |= IN_ATTACK; // throw the disc
	}
}

// Charge/Shoot kame torpedo task
CKameTorpTask::CKameTorpTask(CBot *pB) : CBaseTask(pB)
{
	float BaseLifeTime = RANDOM_FLOAT(35.0f, 43.0f);
	if (pBot->m_pPersonality->aggression_level == Defensive)
		BaseLifeTime *= 1.2f;
	else if (pBot->m_pPersonality->aggression_level == Aggressive)
		BaseLifeTime *= 0.8f;

	LifeTime = Delay + BaseLifeTime;
	ShootLifeTime = 0.0;
	DesiredTorpCount = 6;
	ShootingTorps = false;
	KameTorpState = STATE_CHARGING;
	MoveAwayTime = 0.0;
	MoveAwayDir = 0;
	LookDelay = 0.0;
	MoveAwayDelay = 0.0;
	PickedAnAimSpot = false;
}

CKameTorpTask::~CKameTorpTask()
{
	pEdict->v.button |= IN_ATTACK2;
	pBot->m_bChargeKameTorp = false;
	pBot->m_fCheckKameTorpTime = gpGlobals->time + RANDOM_FLOAT(22.0, 42.0);
}

void CKameTorpTask::TaskThink()
{
	pBot->ResetMoveSpeed();

	if ((LifeTime < gpGlobals->time) && (LifeTime > 0.0))
	{
		KameTorpState = STATE_RELEASING;
		LifeTime = 0.0;
		ShootLifeTime = gpGlobals->time + 3.0;
	}

	if (pBot->GetKi() < 150)
	{
		KameTorpState = STATE_POWERUP;
		pBot->m_bChargeKameTorp = false;
	}

	if (KameTorpState == STATE_POWERUP)
	{
		if (pBot->GetKi() > 700)
		{
			KameTorpState = STATE_CHARGING;
			if (pBot->m_fPowerUp)
				pBot->StopPowerUp();
		}
		else if (!pBot->m_fPowerUp)
			pBot->StartPowerUp();
//		rcout << "Torp state: POWERUP\n";
	}
	else if (KameTorpState == STATE_RELEASING)
	{
		if ((!pBot->m_pBotEnemy) && (LifeTime >= gpGlobals->time))
			KameTorpState = STATE_LOOK_AROUND;
		else
		{
			if (!pBot->m_pBotEnemy)
			{
				if (!PickedAnAimSpot)
				{
					Vector v_furthest = GetFurthestCollision(pBot, 1000.0f);
					if (v_furthest != g_vecZero)
						pBot->AimToVec(v_furthest);
					PickedAnAimSpot = true;
				}
			}
			else if ((LifeTime >= gpGlobals->time) && (pBot->GetKi() > 10))
			{
				if (!pBot->CanAttackPlayer(pEnemy))
					return; // Wait untill the enemy can be hit again.
			}				

			if (RANDOM_LONG(0, 4) == 0)
				pEdict->v.button |= IN_ATTACK2;

			if (ShootLifeTime < gpGlobals->time)
				TaskState = DONE;

			short TorpCount = CountTorps();
			if (TorpCount == 0)
				TaskState = DONE;

//			rcout << "Torp state: RELEASE\n";
		}
	}
	else if (KameTorpState == STATE_CHARGING)
	{	
		ChargeKameTorps();
//		rcout << "Torp state: CHARGE\n";
	}
	else if (KameTorpState == STATE_MOVE)
	{
		pBot->SetMoveDir(MoveAwayDir);

		if (MoveAwayTime <= gpGlobals->time)
		{
			MoveAwayDir = 0;
			KameTorpState = STATE_CHARGING;
		}
//		rcout << "Torp state: MOVE\n";
	}
	else if (KameTorpState == STATE_LOOK_AROUND)
	{
		if (pBot->m_pBotEnemy)
			KameTorpState = STATE_RELEASING;
		else
		{
			if (MoveAwayDelay < gpGlobals->time)
			{
				MoveAwayDelay = gpGlobals->time + RANDOM_FLOAT(1.75, 2.75);

				bool PickedADirection = SetMoveAwayDir();
				if (PickedADirection) // Found a direction to move at?
				{
					KameTorpState = STATE_MOVE;
					MoveAwayTime = gpGlobals->time + RANDOM_FLOAT(0.13f, 0.25f);
					LookDelay = MoveAwayTime + RANDOM_FLOAT(0.2, 0.45);
				}
			}
			else if (LookDelay < gpGlobals->time)
			{
				LookDelay = gpGlobals->time + RANDOM_FLOAT(0.35, 0.75);

				if (pBot->m_eWanderDir == MOVE_LEFT)
					pBot->m_vAimVec.y = UTIL_WrapAngle(pBot->m_vAimVec.y + RANDOM_FLOAT(40.0f, 50.0f));
				else
					pBot->m_vAimVec.y = UTIL_WrapAngle(pBot->m_vAimVec.y - RANDOM_FLOAT(40.0f, 50.0f));
			}
		}
//		rcout << "Torp state: LOOK_AROUND\n";
	}

//	rcout << "Torp lifetime: " << (LifeTime - gpGlobals->time) << "\n";
}

void CKameTorpTask::ChargeKameTorps()
{
	if ((pBot->m_iChargePercent < 100) && (pBot->m_bChargeKameTorp))
	{
		pEdict->v.button |= IN_ATTACK;
		return;
	}

	if (pBot->m_iChargePercent == 100)
	{
		pBot->m_bChargeKameTorp = false;
		bool PickedADirection = SetMoveAwayDir();

		if (PickedADirection) // Found a direction to move at?
		{
			KameTorpState = STATE_MOVE;
			MoveAwayTime = gpGlobals->time + RANDOM_FLOAT(0.13f, 0.25f);
		}
		return;
	}

	short TorpCount = CountTorps();

	if (TorpCount >= DesiredTorpCount)
	{
		KameTorpState = STATE_RELEASING;
		ShootLifeTime = gpGlobals->time + 4.0;
		return;
	}

	// Not enough kame torps yet. Make a new one.
	pBot->m_bChargeKameTorp = true;

	if ((pBot->m_pActiveItem) && (pBot->m_pActiveItem->m_iId != WEAPON_KAMETORPEDO))
		pBot->SelectItem("weapon_kametorpedo");

	pEdict->v.button |= IN_ATTACK;
}

bool CKameTorpTask::SetMoveAwayDir()
{
	MoveAwayDir = 0;
	bool PickedADirection = false;
	byte RandNr = RANDOM_LONG(0, 2);

	// Go back or forward?
	if (RandNr > 0)
	{
		if (RandNr == 1) // First check forward then backward
		{
			if (pBot->GetCollisionDist(dir_forward, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_forward;
				PickedADirection = true;
			}
			else if (pBot->GetCollisionDist(dir_back, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_back;
				PickedADirection = true;
			}
		}
		else // First check backward then forward
		{
			if (pBot->GetCollisionDist(dir_back, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_back;
				PickedADirection = true;
			}
			else if (pBot->GetCollisionDist(dir_forward, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_forward;
				PickedADirection = true;
			}
		}
	}

	if (PickedADirection)
		RandNr = RANDOM_LONG(0, 2);
	else
		RandNr = RANDOM_LONG(1, 2);

	// Go left or right?
	if (RandNr > 0)
	{
		if (RandNr == 1) // First check left then right
		{
			if (pBot->GetCollisionDist(dir_left, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_left;
				PickedADirection = true;
			}
			else if (pBot->GetCollisionDist(dir_right, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_right;
				PickedADirection = true;
			}
		}
		else // First check right then left
		{
			if (pBot->GetCollisionDist(dir_right, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_right;
				PickedADirection = true;
			}
			else if (pBot->GetCollisionDist(dir_left, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_left;
				PickedADirection = true;
			}
		}
	}

	if (PickedADirection)
		RandNr = RANDOM_LONG(0, 2);
	else
		RandNr = RANDOM_LONG(1, 2);

	// Go up or down?
	if (RandNr > 0)
	{
		if (RandNr == 1) // First check up then down
		{
			if (pBot->GetCollisionDist(dir_up, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_up;
				PickedADirection = true;
			}
			else if (pBot->GetCollisionDist(dir_down, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_down;
				PickedADirection = true;
			}
		}
		else // First check backward then forward
		{
			if (pBot->GetCollisionDist(dir_down, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_down;
				PickedADirection = true;
			}
			else if (pBot->GetCollisionDist(dir_up, 35.0f) >= 35.0f)
			{
				MoveAwayDir |= dir_up;
				PickedADirection = true;
			}
		}
	}
	return PickedADirection;
}

short CKameTorpTask::CountTorps()
{
	CBaseEntity *pEnt = NULL;
	short TorpCount = 0;

	while((pEnt = UTIL_FindEntityByClassname(pEnt, "Kame Torpedo")) != NULL)
	{
		if ((IsNull(pEnt)) || (pEnt->pev->owner != pEdict))
			continue;

		if (pEnt->pev->velocity.Length() > 0.0f)
			continue; // Skip fired torps

		TorpCount++;
	}

	return TorpCount;
}

// Drain entities power in range task, for superbots only
void CDrainEntsTask::TaskThink()
{
	if (pBot->GetKi() < 150.0)
	{
		TaskState = DONE;
		return;
	}

	if ((LifeTime < gpGlobals->time) || (pBot->m_fMelee) || (pBot->GetMaxSpeed() == 0.0))
	{
		TaskState = DONE;
		return;
	}

	bool PlaySound = false;

	pBot->ResetMoveSpeed();

	if (pBot->m_fTurbo)
	{
		pBot->ToggleTurbo(false); // turn turbo off when its on, otherwise we get crappy things...
		return;
	}

	// Give the bot a nice color...
	pEdict->v.rendermode = kRenderNormal;
    pEdict->v.renderfx = kRenderFxGlowShell;
  
    pEdict->v.renderamt = 25;  // glow shell distance from entity
    pEdict->v.rendercolor.x = 255;
    pEdict->v.rendercolor.y = 255;
    pEdict->v.rendercolor.z = 255;

	bool BotMayDrawBeams = false;

	if (pBot->m_fLastBeamDrawTime <= gpGlobals->time)
	{
		pBot->m_fLastBeamDrawTime = gpGlobals->time + 0.1;
		BotMayDrawBeams = true;
	}

	float dist = 0.0;
	CBaseEntity *pEnt = NULL;
	CBasePlayer *pPlayer = NULL;

	// Mark all attacks as not founded
	/*modified by harSens
	TLinkedList<attack_info_s *>::node_s *pNode = pAttackInfoList->GetFirst();
	*/
	node_s<attack_info_s *> *pNode = pAttackInfoList->GetFirst();
	while (pNode)
	{
		pNode->Entry->bFounded = false;
		pNode = pNode->next;
	}

	while((pEnt = UTIL_FindEntityInSphere(pEnt, pEdict->v.origin, 800.0f)) != NULL)
	{
		if (pBot->GetKi() < 20)
			break; // Stop if the bot hasn't much ki left

		pPlayer = NULL;

		if (pEnt == pBot)
			continue; // skip self

		if (IsNull(pEnt))
			continue;

		if ((pEnt->IsPlayer() == false) && ((!IsAttack(pEnt->edict())) || (IsOtherAttack(pEnt->edict()))))
			continue;

		if (pEnt->IsPlayer())
		{
			dist = pBot->GetDistance(pEnt->pev->origin);
			// Player is to close, return because it probably is to dangerous now
			if (dist < 350.0)
			{
				TaskState = DONE;
				return;
			}

			pPlayer = (CBasePlayer *)pEnt;
			if (!UTIL_IsValidPlayer(pPlayer))
				continue;
		}
		else if ((FStrEq(STRING(pEnt->pev->classname), "Kame Torpedo")) &&
			   (pEnt->pev->velocity.Length() == 0.0f))
			continue; // Don't drain unfired kame torps

		if (g_teamplay)
		{
			// Is this ent a player and if so is he/she on the same team as the bot?
			if ((pPlayer) && (pPlayer->pev->team == pEdict->v.team))
				continue; // Don't target your team mates
			else if (!IsNull(pEnt->pev->owner)) // else has this ent a valid owner?
			{
				if (pEnt->pev->owner->v.team == pEdict->v.team)
					continue; // Don't target friendly attacks
			}
		}

		// If this ent is a player check if the bot can attack him/her
		if (pPlayer && !pBot->CanAttackPlayer(pPlayer))
			continue;

		if (pBot->FVisible(pEnt->pev->origin) == FALSE)
			continue;

		if (!PlaySound)
			PlaySound = true;

		if (BotMayDrawBeams)
			UTIL_DrawBeamToAll(pBot->EyePosition(), pEnt->pev->origin,
 					       20, 8, 125, 255, 255, 250, 5);

		if (pPlayer)
			DrainPlayer(pPlayer);
		else
			DrainAttack(pEnt);

		// Increase life time
		LifeTime += 0.2f;
	}

	if (PlaySound)
	{
		if (pBot->m_fLastSoundPlayedTime <= gpGlobals->time)
		{
			EMIT_SOUND_DYN2(pEdict, CHAN_WEAPON, "weapons/basicbeam_chargeoriginal.wav", 1.0,
					    ATTN_NORM, 0, 100);

			// Small delay, otherwise the engine will get it to busy...
			pBot->m_fLastSoundPlayedTime = gpGlobals->time + 0.3;
		}
	}
	else // else if no ent found decrease the lifetime
	{
		if (LifeTime > (gpGlobals->time + 1.0f))
			LifeTime = gpGlobals->time + 0.9;
	}

	// Filter out all non founded attacks in the list
	pNode = pAttackInfoList->GetFirst();
	/*modified by harSens
	TLinkedList<attack_info_s *>::node_s *pTempNode;
	*/
	node_s<attack_info_s *> *pTempNode;
	attack_info_s *pAttackInfo;

	while(pNode)
	{
		pAttackInfo = pNode->Entry;
		if (pAttackInfo->bFounded == false)
		{
			pTempNode = pNode;
			pNode = pNode->next;
			pAttackInfoList->DeleteNode(pTempNode); // remove node from list
			delete pAttackInfo; // remove itself
			continue;
		}
		pNode = pNode->next;
	}		
}

CDrainEntsTask::~CDrainEntsTask()
{
	pEdict->v.renderfx = kRenderFxNone;
  	pEdict->v.rendermode = kRenderTransTexture;
	pEdict->v.renderamt = 255;

	if (pAttackInfoList)
	{
		while (pAttackInfoList->Empty() == false)
			delete pAttackInfoList->Pop();

		delete pAttackInfoList;
	}
}

void CDrainEntsTask::DrainAttack(CBaseEntity *pAttack)
{
	// Check if this attack is in the list
	/*modified by harSens
	TLinkedList<attack_info_s *>::node_s *pNode = pAttackInfoList->GetFirst();
	*/
	node_s <attack_info_s *>*pNode = pAttackInfoList->GetFirst();
	attack_info_s *pAttackInfo = NULL;

	while (pNode)
	{
		if (pNode->Entry->pAttack == pAttack)
		{
			// Mark it as found
			pNode->Entry->bFounded = true;
			pAttackInfo = pNode->Entry;
			break; // No need to search further
		}
		pNode = pNode->next;
	}

	// Didn't found the attack?
	if (pAttackInfo == NULL)
	{
		// then make a new node
		pAttackInfo = new attack_info_s(pAttack);
		pAttackInfoList->AddNode(pAttackInfo);
	}

	if (pAttack->pev->velocity.Length() > 0.0f)
	{
		// Slow down the attack
		pAttack->pev->velocity.x = pAttackInfo->OldVelocity.x / 100.0;
		pAttack->pev->velocity.y = pAttackInfo->OldVelocity.y / 100.0;
		pAttack->pev->velocity.z = pAttackInfo->OldVelocity.z / 100.0;
	}

	float DamageDrain = (pBot->m_iPowerLevel / 1000000.0f) * ABSORB_DAMAGE_VALUE;
	pAttack->pev->dmg -= DamageDrain; // Drain power of the attack

	// Increase bot stats
	pBot->UseKi(ABSORB_ATTACK_KI_COST);
	pBot->IncreasePL(ABSORB_ATTACK_PL_GAIN);

	if (pAttack->pev->dmg <= 2)
	{
		pAttack->pev->dmg = 1;
		DispatchTouch(pAttack->edict(), pEdict);
	}	
}

void CDrainEntsTask::DrainPlayer(CBasePlayer *pPlayer)
{
	long NewPlayerPl = pPlayer->m_iPowerLevel;
	long NewBotPl = pBot->m_iPowerLevel;

	if (pPlayer->pev->velocity.Length() > 10.0)
	{
		// Decrease players velocity...

		float VelocityFactor = 0.95f;
		long PlDiff = NewBotPl - NewPlayerPl;

		if (PlDiff > 0)
			VelocityFactor = float(PlDiff) / 10000000.0f;

		if (VelocityFactor < 0.05)
			VelocityFactor = 0.05; // Player should be atleast 5% slowed down
		else if (VelocityFactor > 0.95f)
			VelocityFactor = 0.95f; // But he should be able to move aswell...

		pPlayer->pev->velocity = pPlayer->pev->velocity - pPlayer->pev->velocity.Normalize() * (pPlayer->pev->velocity.Length() * VelocityFactor);
	}

	NewPlayerPl -= ABSORB_PL_GAIN;
	NewBotPl += ABSORB_PL_GAIN;

	if (pPlayer->m_iPowerLevel <= (ABSORB_PL_GAIN * 3))
	{
		// kill the enemy when he doesn't have much pl left
		UTIL_DamagePlayer(pBot, pPlayer, "Super bot laser", pPlayer->pev->health+1);
	}
	else if (NewBotPl < 999999999)
	{
		pPlayer->SetPowerLevel(NewPlayerPl);
		pBot->SetPowerLevel(NewBotPl);
		pBot->SetMaxPowerLevel(NewBotPl);
	}

	pPlayer->UseKi(3); // Decrease ki of the victim
	pBot->UseKi(3); // Decrease ki of the bot				
}

// ==================================================================
// TaskCode - End
// ==================================================================

// ==================================================================
// Code of CBot - Begin
// ==================================================================

bool CBot::CanAttackPlayer(CBasePlayer *pPlayer)
{
	if ((!m_pPersonality->transkiller) && (pPlayer->m_fAscending))
		return false;

	// Can we attack?
	if(pPlayer->m_fPowerStruggling || pPlayer->m_fBlockStruggling || pPlayer->m_fBlockStruggleAttacker ||
		pPlayer->m_fSpawnInvulnerable || !pPlayer->IsAlive() || pPlayer->m_Melee.InAdvMeleeBattle())
		return false;

	// Can the player take dmg?
	if(!g_pGameRules->FPlayerCanTakeDamage(this, pPlayer))
		return false;
	
	return true;
}

bool CBot::HasWeapon(int iId)
{
	if ((!IsSuperBot()) && (iId == SBOT_WEAPON_LASER1))
		return false;

	if ((!BotManager.BotsUseDiscs()) && ((iId == WEAPON_DESTRUCTODISC) ||
		 (iId == WEAPON_FRIEZADISC)))
		return false;

	CBasePlayerItem *pItem = NULL;

	for (int i = 0; i < MAX_ITEM_TYPES; i++)
	{
		if (m_rgpPlayerItems[i])
		{
			pItem = m_rgpPlayerItems[i];
	
			while (pItem)
			{
				if (pItem->m_iId == iId)
					break;
				pItem = pItem->m_pNext;
			}
		}

		if (pItem)
			break;
	}

	return (pItem != NULL);
}

void CBot::AimAtEnemy(CBasePlayer *pEnemy)
{
	float EnemyDist = GetDistance(pEnemy->pev->origin);

	// Bots may only predict if the enemy isn't close otherwise they will 
	// aim behind them self.
	bool Predict = (EnemyDist > (pEnemy->pev->velocity.Length() + 35.0f));

	if (Predict)
	{
		float PredictTime = 0.0f;
		if (m_bSkillNr == 0)
		{
			if (pev->movetype == MOVETYPE_PLAYERSWOOP)
				PredictTime = RANDOM_FLOAT(0.6, 1.1);
			else
				PredictTime = RANDOM_FLOAT(0.2, 0.4);
		}
		else if (m_bSkillNr == 1)
		{
			if (pev->movetype == MOVETYPE_PLAYERSWOOP)
				PredictTime = RANDOM_FLOAT(0.4, 0.8);
			else
				PredictTime = RANDOM_FLOAT(0.1, 0.2);
		}

		if (PredictTime > 0.0f)
		{
			AimToVec(PredictEntityPos(pEnemy, PredictTime));
			return;
		}
	}

	AimToVec(pEnemy->pev->origin);
}

void CBot::AddShootDelay()
{
	float min_delay = m_pBotSkill->flMinAttackDelay;
	float max_delay = m_pBotSkill->flMaxAttackDelay;

	float m_fShootDelay = 0.5 + RANDOM_FLOAT(min_delay, max_delay);

	switch (m_pPersonality->aggression_level)
	{
	case Defensive:
		m_fShootDelay *= 1.2;
		break;
	case Aggressive:
		m_fShootDelay *= 0.8;
		break;
	}

	m_fShootDelay += gpGlobals->time;
}

bool CBot::IsValidEnemy(CBasePlayer *pClient)
{
	if (UTIL_IsValidPlayer(pClient) == false)
		return false;

   	// is team play enabled?
	if (g_teamplay)
	{
		if (pev->team == pClient->pev->team)
			return false; // Don't shoot at your team mates
	}

	Vector vecEnemy = pClient->EyePosition();

	Vector v_dist = vecEnemy - GetGunPosition();
	v_dist.z = 0;
	float distance = v_dist.Length();

	bool OnScouter = ((distance <= 2300.0) || (IsSuperBot()));
	bool JustMadeASound = false;

	/* UNDONE: Need to store player sounds
	if (ClInd > -1)
		JustMadeASound = ((ClientInfo[ClInd].LastSoundTime > (gpGlobals->time - 1.0)) &&
						  (ClientInfo[ClInd].SoundName[0] != 0) && (distance < 3000.0));
	*/

	if ((!EntIsInFOV(pClient)) && (!OnScouter) && (!JustMadeASound))
		return false;

	// Check if there is an player list, saves us a traceline :)
	if (m_bMadePlayerList)
	{
		return ((m_lVisiblePlayers & (1<<(pClient->entindex() - 1))) != 0);
	}
	else
	{
		// We have to use a traceline :(
		return (FVisible(vecEnemy) == TRUE);
	}
}

bool CBot::EnemyIsInList(CBasePlayer *pEnemy)
{
	for (int i=0;i<4;i++)
	{
		if (m_EnemyList[i].pEnemy == pEnemy)
			return true;
	}

	return false;
}

// Find an enemy for the enemy list
CBasePlayer *CBot::BotFindEnemy()
{
	if (BotManager.BotsSearchEnemies() == false)
		return NULL;

    Vector vecEnd;

    CBasePlayer *pNewEnemy = NULL;

    float nearestdistance;
    float distance;
    int i;

    bool OnScouter = false;
	bool InFOV = false;
    bool BotIsInFOV = false;
    bool JustMadeASound = false;

    CBasePlayer *pPlayer = NULL;

    short EnemyVal = 0;
    short BestEnemyVal = -100;

    m_pBotEnemy = NULL;

    if (m_fSearchEnemyDelay <= gpGlobals->time)
    {
		// Add enemy searchy delay
		float MinDelay = m_pBotSkill->flMinEnemySearchDelay;
		float MaxDelay = m_pBotSkill->flMaxEnemySearchDelay;

		m_fSearchEnemyDelay = gpGlobals->time + RANDOM_FLOAT(MinDelay, MaxDelay);

	    if (!m_bMadePlayerList)
			MakePlayerList();

	    if (m_bPlayerCount)
		{
			nearestdistance = 9000.0;

			for (i = 0; i < m_bPlayerCount; i++)
			{
				pPlayer = m_pPlayerList[i];
				EnemyVal = 1;

				// Is this player already in the list ?
				if (EnemyIsInList(pPlayer))
					continue;

			   	// is team play enabled?
				if (g_teamplay)
				{
					// don't target your teammates...
					if (pev->team == pPlayer->pev->team)
					    continue;
				}

				vecEnd = pPlayer->EyePosition();

				Vector v_dist = vecEnd - GetGunPosition();
				v_dist.z = 0;
				distance = v_dist.Length();

				OnScouter = ((distance <= 2300.0) || (IsSuperBot()));
				InFOV = (EntIsInFOV(pPlayer));
				BotIsInFOV = (EntIsInFieldOfView(pPlayer, this));

				distance = GetDistance(pPlayer->pev->origin);

				JustMadeASound = false;
/* NOT DONE: Need to store sounds
				JustMadeASound = ((pClient->LastSoundTime > (gpGlobals->time - 1.0)) &&
								  (pClient->SoundName[0] != 0) && (distance < 3000.0));
*/

				if ((!InFOV) && (!OnScouter) && (!JustMadeASound))
					continue;

				if (nearestdistance > distance)
				{
					nearestdistance = distance;
					EnemyVal+=2;
				}
			
				if (InFOV)
					EnemyVal+=2;

				if (BotIsInFOV)
					EnemyVal+=2;

				if ((m_pLastEnemy) && (pPlayer == m_pLastEnemy))
					EnemyVal += RANDOM_LONG(1, 4);
			
				if (CanAttackPlayer(pPlayer) == false)
				{
					EnemyVal -= 3;
				}

				if (pPlayer->m_fAscending)
					EnemyVal -= RANDOM_LONG(1,2);

				if ((DangerousPlForBot(pPlayer->m_iPowerLevel)) && (GetHealth() < 30))
					EnemyVal--;

				// Check if this player is an enemy for another bot
	            for (int n=1;n<=gpGlobals->maxClients;n++)
				{
					CBasePlayer *pPlay = (CBasePlayer *)UTIL_PlayerByIndex(n);

					if (pPlay == this)
						continue; // Don't examine yourself....

					if ((UTIL_IsValidPlayer(pPlay) == false) || (UTIL_IsBot(pPlay) == false))
						continue; // Only examine valid bots

					CBot *pB = UTIL_GetBotPointer(pPlay);

					if ((pB) && (pB->m_pBotEnemy == pPlayer))
					{
                        if (EnemyVal > 0)
							EnemyVal--;
						else
							break;
					}
				}

				if (EnemyVal > BestEnemyVal)
				{
					pNewEnemy = pPlayer;
					BestEnemyVal = EnemyVal;
				}
			}
		}
   }

   if (pNewEnemy)
   {
      m_pHuntPlayer = NULL;
      m_fHuntDelay = 0.0f;
   }

   return pNewEnemy;
}

// For enemy sorting with qsort
int EnemyPriorityCmp(const void *p, const void *q)
{
	int Pr1 = static_cast<const enemy_priority_s*>(p)->Priority;
	int Pr2 = static_cast<const enemy_priority_s*>(q)->Priority;

	return Pr2 - Pr1;
}

void CBot::ManageEnemyList()
{
	short i = 0;
	short EnemyVal;
	CBasePlayer *pEnemy;
	float NearestDist = 9999.9;

	// Filter out bad enemys and search new ones
	for (i=0;i<4;i++)
	{
		bool SearchNewOne = false;
		CBasePlayer *pNewEnemy = NULL;

		pEnemy = m_EnemyList[i].pEnemy;
		bool ValidEnemy = IsValidEnemy(pEnemy);

		if ((ValidEnemy == false) || (CanAttackPlayer(pEnemy) == false))
			SearchNewOne = true;

		if (SearchNewOne)
		{
			pNewEnemy = BotFindEnemy();

			if (pNewEnemy)
				m_EnemyList[i].pEnemy = pNewEnemy;
			else if (ValidEnemy == false)
			{
				// Previous enemy is still valid but can't be attacked anymore?(ie dead)
				if (UTIL_IsValidPlayer(m_EnemyList[i].pEnemy, false))
					m_pLastEnemy = m_EnemyList[i].pEnemy; // Store last enemy

				m_EnemyList[i].pEnemy = NULL;
			}
		}
	}
	
	// Give every enemy in the list an priority
	for (i=0;i<4;i++)
	{
		EnemyVal = 1;
		pEnemy = m_EnemyList[i].pEnemy;

		if (pEnemy == NULL)
		{
			m_EnemyList[i].Priority = -100; // never interesting...
		}
		else
		{
			if (pEnemy->m_fAscending)
				EnemyVal--;

			// Is this enemy a bot?
			if (UTIL_IsBot(pEnemy))
			{
				CBot *pBotEnemy = UTIL_GetBotPointer(pEnemy);

				if (pBotEnemy)
				{
					// Does the bot have another enemy?
					if ((pBotEnemy->m_pBotEnemy != this) &&
					    (UTIL_IsValidPlayer(pBotEnemy->m_pBotEnemy)))
						EnemyVal-=2;
				}
			}

			// Enemy swoops?
			if (pEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP)
				EnemyVal+=2;

			// Enemy charging an attack?
			if (pEnemy->m_fCharging || pEnemy->m_fCharging2)
				EnemyVal+=2;

			if (EntHeadsToBot(pEnemy))
				EnemyVal++;

			// In FOV of the bot(most lickely...)?
			if (EntIsInFieldOfView(this, pEnemy))
				EnemyVal++;

			float Distance = GetDistance(pEnemy->pev->origin);

			if (NearestDist > Distance)
			{
				float DistDiff = (NearestDist - Distance);

				if (DistDiff > 300.0)
					EnemyVal++;
				else if (DistDiff > 800.0)
					EnemyVal += 2;
				else
					EnemyVal += 3;

				NearestDist = Distance;
			}

			if (Distance < 400.0)
				EnemyVal+=2;
			else if (Distance < 800.0)
				EnemyVal++;

			if (CanAttackPlayer(pEnemy) == false)
				EnemyVal -= 2;

			m_EnemyList[i].Priority = EnemyVal;
		}
	}

	// Sort the list out with the good ol' C func :)
	qsort(m_EnemyList, 4, sizeof(enemy_priority_s), EnemyPriorityCmp);
}

// Return the best enemy from the enemy list
CBasePlayer *CBot::GetBestEnemyFromList()
{
	// Sort out and manage enemy list
	ManageEnemyList();

	if (m_EnemyList[0].pEnemy)
	{
		// Aim at enemy with highest priority
		AimAtEnemy(m_EnemyList[0].pEnemy);
		
		m_fHasAnEnemyTime = gpGlobals->time; // save the time that the bot has an enemy
		return m_EnemyList[0].pEnemy;
	}
	return NULL;
}

// Look for a player to hunt at, returns true if the bot can hunt someone
bool CBot::CheckForHuntEnemy()
{
	if (!m_fFlying)
		return false; // don't hunt when the bot doesn't fly...

	if (BotManager.BotsSearchEnemies() == false)
		return false;

	if (m_fHuntDelay > gpGlobals->time)
		return false;

	m_fHuntDelay = gpGlobals->time + 1.25f; // Dont check every frame

	CBasePlayer *pPlayer = NULL;
	CBasePlayer *pNewHuntPlayer = NULL;
	Vector v_src = pev->origin;

	short PosVal = 0;
	short BestPosVal = 0;

	short BestPosIndex = -1;
	short WptIndNearBot = -1, WptInd = -1, BestWPInd = -1;
	short BestPosOwnerIndex = -1;

	float NearestDistToEnemy = 9999.9;
	float NearestDistToBot = 9999.9;
	float Dist = 0.0;

	int i, j;

	if (WaypointClass.m_iWaypointCount)
	{
		// Check if there is a valid waypoint near the bot
		WptIndNearBot = WaypointClass.GetNearestWaypoint(this, 1750.0f,
														 (g_teamplay) ? pev->team : -1);
	}

    for (i=1;i<=gpGlobals->maxClients;i++)
	{
		pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(i);

		if (pPlayer == this)
			continue;

		if (UTIL_IsValidPlayer(pPlayer) == false)
			continue;

		if (g_teamplay)
		{
			// don't hunt your team mates...
			if (pev->team == pPlayer->pev->team)
				continue;
		}

		Dist = GetDistance(pPlayer->pev->origin);
		if (Dist > NearestDistToEnemy)
			continue;

		// Is there a waypoint near the bot?
		if (WptIndNearBot != -1)
		{
			// Is there is a valid waypoint near this enemy?
			WptInd = WaypointClass.GetNearestWaypoint(pPlayer, 750.0f,
													  (g_teamplay) ? pev->team : -1);

			// See if there is a route to the waypoint near the enemies waypoint
			if ((WptInd != -1) &&
				(WaypointClass.RouteFromTo(WptIndNearBot, WptInd, (g_teamplay) ? pev->team : -1) != WAYPOINT_UNREACHABLE))
			{
				BestWPInd = WptInd;
				NearestDistToEnemy = Dist;
				pNewHuntPlayer = pPlayer;
				continue;
			}
		}

		for (j=(MAX_OLD_POSITIONS-1);j>=0;j--)
		{
			PosVal = 1;
			Vector OldPos = pPlayer->m_OldPositions[j].Pos;

			if (pPlayer->m_OldPositions[j].flCreateTime < 
				(gpGlobals->time-MAX_OLD_POSITION_LIFE_TIME))
				continue;

			Dist = UTIL_Distance(v_src, OldPos);
			if ((Dist > 6000.0) || (Dist < 300.0))
				continue;

			if (Dist < NearestDistToEnemy)
			{
				NearestDistToEnemy = Dist;
				PosVal++;
			}

			if (Dist < NearestDistToBot)
			{
				NearestDistToBot = Dist;
				PosVal++;
			}

			if (m_LastHuntPos.Pos == pPlayer->m_OldPositions[j].Pos)
				continue;

			if ((PosVal >= BestPosVal) && (FVisible(OldPos)))
			{
				BestPosVal = PosVal;
				BestPosIndex = j;
				BestWPInd = -1;
				pNewHuntPlayer = pPlayer;
			}
		}
	}

	// Bot found a player to hund at?
	if (pNewHuntPlayer)
	{
		m_pHuntPlayer = pNewHuntPlayer;

		// Found the player with waypoints?
		if (BestWPInd != -1)
		{	
			SetGoalWaypoint(BestWPInd);
			SetCurrentWaypoint(WptIndNearBot);
			return true;
		}
		else if (BestPosIndex != -1)
		{
			m_LastHuntPos = m_HuntPos;
			m_HuntPos = m_pHuntPlayer->m_OldPositions[BestPosIndex];
			m_fPrevHuntDist = 0.0;

			// Aim at the enemy trace
			AimToVec(m_HuntPos.Pos);

			// Turn turbo on
			ToggleTurbo(true);

			return true;
		}
	}

	return false; // Noone to hunt :(
}

void CBot::ShootHealthBeam(CBasePlayer *pPlayer)
{
	if (GetKi() < LASER1_KICOST)
		return;
	
	if (m_fAbsorbHealthTime < gpGlobals->time)
	{
		m_fAbsorbHealthTime = gpGlobals->time + 0.1;
		UseKi(LASER1_KICOST);
		pev->health += 2;
		UTIL_DamagePlayer(this, pPlayer, "Super bot laser", 2);
	}

	if (m_fLastBeamDrawTime <= gpGlobals->time)
	{
		UTIL_DrawBeamToAll(EyePosition(), pPlayer->pev->origin, 40, 20, 125, 255, 255, 250, 0);
	}
}

bool CBot::EnemyIsInMeleeRange(CBasePlayer *pPlayer)
{
	Vector2D vec2LOS;
	float flDot;

	UTIL_MakeVectors (pev->angles);

	vec2LOS = (pPlayer->pev->origin - pev->origin ).Make2D();
	vec2LOS = vec2LOS.Normalize();

	flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() );

	return (flDot < 0.9);
}

bool CBot::AttackIsBeam(weapon_info_s *pWeaponInfo)
{
	if (pWeaponInfo->iId == WEAPON_FRIEZADISC)
		return false;
	
	return (pWeaponInfo->bControllable);
}

// returns a combo type if a chainswoop should be made
EComboType CBot::AvoidAttacksWhileSwoop()
{
	if (m_fAttacksWhileSwoopCheckDelay > gpGlobals->time)
		return COMBO_TYPE_NONE;

	//m_fAttacksWhileSwoopCheckDelay = gpGlobals->time + 0.15f;

	CBaseEntity *pNearestAttack = GetNearestHostileAttack(50.0f, 2250.0);

	if (pNearestAttack)
	{
		float flDist = GetDistance(pNearestAttack->pev->origin);

		if (flDist > 350.0f)
		{
			if (m_pBotSkill->bUsesChainSwoop)
			{
				// Lets try to chainswoop...
				EComboType ChainSwoopType = MakeAttackAvoidChainSwoop();
				if (ChainSwoopType != COMBO_TYPE_NONE)
					return ChainSwoopType;
			}
		}

		PrintAI("Turn away from attack\n");

		// If couldn't chain swoop then just try to turn around
		if (m_eSwoopType == SWOOP_OFFSET)
		{
			if (m_vSwoopOffset.x < 0)
				m_vAimVec.x = UTIL_WrapAngle(m_vAimVec.x - RANDOM_FLOAT(85.0, 95.0));
			else if (m_vSwoopOffset.x > 0)
				m_vAimVec.x = UTIL_WrapAngle(m_vAimVec.x + RANDOM_FLOAT(85.0, 95.0));
			else if (m_vSwoopOffset.y < 0)
				m_vAimVec.y = UTIL_WrapAngle(m_vAimVec.y - RANDOM_FLOAT(85.0, 95.0));
			else if (m_vSwoopOffset.y > 0)
				m_vAimVec.y = UTIL_WrapAngle(m_vAimVec.y + RANDOM_FLOAT(85.0, 95.0));
		}
		else
		{
			if (m_eWanderDir == MOVE_LEFT)
				m_vAimVec.y = UTIL_WrapAngle(m_vAimVec.y + RANDOM_FLOAT(85.0, 95.0));
			else
				m_vAimVec.y = UTIL_WrapAngle(m_vAimVec.y - RANDOM_FLOAT(85.0, 95.0));
		}
	}

	return COMBO_TYPE_NONE;
}

CBaseEntity *CBot::GetNearestHostileAttack(float flMinDist, float flMaxDist)
{
	CBaseEntity *pAttack = NULL;
	CBaseEntity *pNearestAttack = NULL;
	float NearestDist = 9999.9f;
	float Dist;

	while ((pAttack = UTIL_FindEntityByString(pAttack, "globalname", "magic_attack")) != NULL)
	{
		if (IsNull(pAttack))
			continue;

		// Teamplay enabled?
		if (g_teamplay)
		{
			if ((!IsNull(pAttack->pev->owner)) && (pAttack->pev->owner->v.team == pev->team))
				continue; // Friendly attacks aren't hostile...
		}

		Dist = GetDistance(pAttack->pev->origin);

		if (Dist < flMinDist)
			continue;
		
		if ((Dist > flMaxDist) || (Dist > NearestDist))
			continue; // Attack isn't in range or closer then closest found

		if (EntHeadsToBot(pAttack) == false)
			continue;

		if ((m_bSkillNr > 0) && (EntIsInFOV(pAttack) == false))
			continue;

		if (!FVisible(pAttack->pev->origin))
			continue;

		pNearestAttack = pAttack;
		NearestDist = Dist;
	}

	return pNearestAttack;
}

void CBot::MakeMeleeComboList()
{
	ClearMeleeComboList();

	m_pMeleeComboInfoList = new TLinkedList<meleecombo_info_s *>;

	switch (RANDOM_LONG(1, 2))
	{
	case 1: // Combo Type 1
		// Down Punch (w), down+left punch (w), left punch (w)
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(WEAK_HIT, (IN_ATTACK | IN_BACK)));
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(WEAK_HIT, (IN_ATTACK | IN_BACK | IN_MOVELEFT)));
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(WEAK_HIT, (IN_ATTACK | IN_MOVELEFT)));
		break;
	case 2: // Combo Type 2
		// Down punch (w), down+right punch (w), right punch (w), left kick (m)
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(WEAK_HIT, (IN_ATTACK | IN_BACK)));
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(WEAK_HIT, (IN_ATTACK | IN_BACK | IN_MOVERIGHT)));
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(WEAK_HIT, (IN_ATTACK | IN_MOVERIGHT)));
		m_pMeleeComboInfoList->AddNode(new meleecombo_info_s(MEDIUM_HIT, (IN_ATTACK2 | IN_MOVELEFT)));
		break;
	}
}

void CBot::ClearMeleeComboList()
{
	if (m_pMeleeComboInfoList)
	{
		while (!m_pMeleeComboInfoList->Empty())
			delete m_pMeleeComboInfoList->Pop();

		delete m_pMeleeComboInfoList;
		m_pMeleeComboInfoList = NULL;
	}
}

void CBot::SetComboChargeButton(MELEE_STRENGTH MeleeStrength, int Buttons)
{
	float WeakButtonChargeTime = RANDOM_FLOAT(m_Melee.GetSkillValues()->flMinRange,
											  m_Melee.GetSkillValues()->flWeakRange);
	float MediumButtonChargeTime = RANDOM_FLOAT(m_Melee.GetSkillValues()->flWeakRange,
												m_Melee.GetSkillValues()->flMediumRange);
	float StrongButtonChargeTime = RANDOM_FLOAT(m_Melee.GetSkillValues()->flMediumRange,
												m_Melee.GetSkillValues()->flStrongRange);

	switch (MeleeStrength)
	{
	case WEAK_HIT:
		m_fNextComboChargeButtonTime = WeakButtonChargeTime;
		break;
	case MEDIUM_HIT:
		m_fNextComboChargeButtonTime = MediumButtonChargeTime;
		break;
	case STRONG_HIT:
		m_fNextComboChargeButtonTime = StrongButtonChargeTime;
		break;
	}

	m_fNextComboChargeButtonTime += gpGlobals->time;
	m_iOldComboChargeHitButtons = m_iComboChargeHitButtons;
	m_iComboChargeHitButtons = 0;
	m_iOldComboChargeButton = m_iComboChargeButton; // Store old punch/kick button
	m_fSwitchChargeButtonDelay = gpGlobals->time + 0.15f; // Small delay so that the previous hit stays a punch or kick

	if (Buttons & IN_FORWARD)
	{
		SetMoveDir(dir_forward);
		m_iComboChargeHitButtons |= IN_FORWARD;
	}
	if (Buttons & IN_BACK)
	{
		SetMoveDir(dir_back);
		m_iComboChargeHitButtons |= IN_BACK;
	}
	if (Buttons & IN_MOVELEFT)
	{
		SetMoveDir(dir_left);
		m_iComboChargeHitButtons |= IN_MOVELEFT;
	}
	if (Buttons & IN_MOVERIGHT)
	{
		SetMoveDir(dir_right);
		m_iComboChargeHitButtons |= IN_MOVERIGHT;
	}
	if (Buttons & IN_ATTACK)
	{
		m_iComboChargeButton = IN_ATTACK;
	}
	if (Buttons & IN_ATTACK2)
	{
		m_iComboChargeButton = IN_ATTACK2;
	}
}

void CBot::ChargeMeleeCombo()
{
	// Is the bot making a melee combo?
	if (m_pMeleeComboInfoList)
	{
		// Time to charge the next button?
		if (m_fNextComboChargeButtonTime < gpGlobals->time && !m_pMeleeComboInfoList->Empty())
		{
			meleecombo_info_s *pMeleeComboInfo = m_pMeleeComboInfoList->Pop();

			SetComboChargeButton(pMeleeComboInfo->MeleeStrength, pMeleeComboInfo->Buttons);
			delete pMeleeComboInfo;
		}
	}
	else // the bot is just hitting some random directions
	{
		// Time to charge the next button?
		if (m_fNextComboChargeButtonTime < gpGlobals->time)
		{
			int iButtons = 0;

			// Punch or kick?
			if (RANDOM_LONG(1, 100) <= m_pPersonality->meleekick_percent)
				iButtons = IN_ATTACK2; // Kick
			else
				iButtons = IN_ATTACK; // Punch
	
			TMultiChoice<MELEE_STRENGTH> ButtonStrengthChoice;

			short WeakButtonValue = m_pPersonality->meleeweakbutton_percent;
			short MediumButtonValue = m_pPersonality->meleemediumbutton_percent;
			short StrongButtonValue = m_pPersonality->meleestrongbutton_percent;

			if (m_bSkillNr > 2)
			{
				// if low skill higher chance for weak buttons
				WeakButtonValue *= 1.25;
				// and lower chance for medium buttons
				MediumButtonValue *= 0.75;
			}
			else
			{
				float flStunTime = 0.0f;
				CBasePlayer *pDefender = m_Melee.GetDefender();
				if (pDefender)
					flStunTime = pDefender->m_Melee.GetStunTime();

				// If about ending a combo the strong button chance should be higher
				 if ((flStunTime > 0.0f) && 
				     ((flStunTime - m_Melee.TotalCmbTime()) <= m_Melee.GetSkillValues()->flStrongRange))
					StrongButtonValue *= 2.0f;
			}

			if (m_pPersonality->aggression_level == Defensive)
				StrongButtonValue *= 1.2;
			else if (m_pPersonality->aggression_level == Aggressive)
				WeakButtonValue *= 1.2;

			ButtonStrengthChoice.Insert(WEAK_HIT, WeakButtonValue);
			ButtonStrengthChoice.Insert(MEDIUM_HIT, MediumButtonValue);
			ButtonStrengthChoice.Insert(STRONG_HIT, StrongButtonValue);

			MELEE_STRENGTH MeleeStrength = WEAK_HIT;
			ButtonStrengthChoice.GetSelection(MeleeStrength);

			// Set a random direction
			TMultiChoice<int> MeleeDirChoice;

			bool PrevWasLeft = (m_iOldComboChargeHitButtons == IN_MOVELEFT);
			bool PrevWasRight = (m_iOldComboChargeHitButtons == IN_MOVERIGHT);
			bool PrevWasLeftUp = (m_iOldComboChargeHitButtons == (IN_MOVELEFT | IN_FORWARD));
			bool PrevWasRightUp = (m_iOldComboChargeHitButtons == (IN_MOVERIGHT | IN_FORWARD));
			bool PrevWasLeftDown = (m_iOldComboChargeHitButtons == (IN_MOVELEFT | IN_BACK));
			bool PrevWasRightDown = (m_iOldComboChargeHitButtons == (IN_MOVERIGHT | IN_BACK));
			bool PrevWasUp = (m_iOldComboChargeHitButtons == IN_FORWARD);
			bool PrevWasDown = (m_iOldComboChargeHitButtons == IN_BACK);

			// Make sure bot doesnt try to set the same direction again
			if (!PrevWasLeft)
				MeleeDirChoice.Insert(IN_MOVELEFT);
			if (!PrevWasRight)
				MeleeDirChoice.Insert(IN_MOVERIGHT);
			if (!PrevWasLeftUp)
				MeleeDirChoice.Insert((IN_MOVELEFT | IN_FORWARD));
			if (!PrevWasRightUp)
				MeleeDirChoice.Insert((IN_MOVERIGHT | IN_FORWARD));
			if (!PrevWasLeftDown)
				MeleeDirChoice.Insert((IN_MOVELEFT | IN_BACK));
			if (!PrevWasRightDown)
				MeleeDirChoice.Insert((IN_MOVERIGHT | IN_BACK));
			if (!PrevWasUp)
				MeleeDirChoice.Insert(IN_FORWARD);
			if (!PrevWasDown)
				MeleeDirChoice.Insert(IN_BACK);

			int b = 0;
			MeleeDirChoice.GetSelection(b);
			iButtons |= b;
		
			SetComboChargeButton(MeleeStrength, iButtons);
		}
	}
	// Keep charging the combo...
	pev->button = m_iComboChargeHitButtons;

	if (m_fSwitchChargeButtonDelay > gpGlobals->time)
		// Keep holding the old punch/kick button so that the old combo dir stays a kick or punch
		pev->button |= m_iOldComboChargeButton;
	else if (m_fSwitchChargeButtonDelay <= gpGlobals->time)
		pev->button |= m_iComboChargeButton; // Charge the next punch/kick button
}

// Check if the bot make a few kame torps when he doesn't have an enemy
bool CBot::BotShouldMakeKameTorps()
{
	if ((BotManager.BotsShoot() == false) || (BotManager.BotsSearchEnemies() == false))
		return false;

	if (!HasWeapons())
		return false;

	if (RANDOM_LONG(1, 100) > m_pPersonality->kametorp_percent)
		return false;

	if (m_pClass->Classify() != PC_GOKU)
		return false;

	CBasePlayer *pPlayer = NULL;
	float Dist = 0.0;
	short s;

	for (s=1;s<=gpGlobals->maxClients;s++)
	{
		pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(s);

		if (UTIL_IsValidPlayer(pPlayer) == false)
			continue;

		Dist = GetDistance(pPlayer->pev->origin);
		if (Dist > 5000.0)
			continue;

		if (g_teamplay)
		{
			// skip if the team of the player is equal to the team of the bot and the player is
			// visible on the scouter.
			if ((pev->team == pPlayer->pev->team) && (Dist < 2300.0f))
				continue;
		}

		/* UNDONE
		// Did the player just made a sound ?
		if (ClientInfo[ClInd].LastSoundTime > (gpGlobals->time - 6.0))
			return true; // Ok lets do it*/

		// Is the player visible on the scouter ?
		if (Dist < 2300.0)
			return true; // Ok lets make some torpedo's

	}

	// Look for a bot-team mate, if found one and he has an enemy; return true
	if (g_teamplay)
	{
		if (!m_bMadePlayerList)
			MakePlayerList();

		if (m_bPlayerCount)
		{
			for (s=0;s<m_bPlayerCount;s++)
			{
				pPlayer = m_pPlayerList[s];

				if (UTIL_IsBot(pPlayer))
				{
					if (pev->team == pPlayer->pev->team)
					{
						CBot *pBot = UTIL_GetBotPointer(pPlayer);

						if (pBot)
						{
							if (UTIL_IsValidPlayer(pBot->m_pBotEnemy))
								return true;
						}
					}
				}
			}
		}
	}

	return false;
}

EComboType CBot::MakeAttackChainSwoop()
{
	TMultiChoice<EComboType> SwoopChoices;
	edict_t *pEdict = edict(); // So we don't have to call edict() more then once...
	int CurSwoopDir = m_Swoop.m_iSwoopDirection; // Shortcut

	if ((CurSwoopDir != LEFT) && (GetCollisionDist(dir_left, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_LEFT_AVOID_ATTACK_SWOOP);
	if ((CurSwoopDir != RIGHT) && (GetCollisionDist(dir_right, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_RIGHT_AVOID_ATTACK_SWOOP);
	if ((CurSwoopDir != UP) && (GetCollisionDist(dir_up, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_UP_AVOID_ATTACK_SWOOP);
	if ((CurSwoopDir != DOWN) && (GetCollisionDist(dir_down, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_DOWN_AVOID_ATTACK_SWOOP);

	EComboType SwoopType;
	if (SwoopChoices.GetSelection(SwoopType))
	{
		return SwoopType;
	}

	return COMBO_TYPE_NONE;
}

EComboType CBot::MakeNormalChainSwoop()
{
	TMultiChoice<EComboType> SwoopChoices;
	edict_t *pEdict = edict(); // So we don't have to call edict() more then once...
	int CurSwoopDir = m_Swoop.m_iSwoopDirection; // Shortcut

	if ((CurSwoopDir != LEFT) && (GetCollisionDist(dir_left, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_LEFT_SWOOP);
	if ((CurSwoopDir != RIGHT) && (GetCollisionDist(dir_right, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_RIGHT_SWOOP);
	if ((CurSwoopDir != UP) && (GetCollisionDist(dir_up, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_UP_SWOOP);
	if ((CurSwoopDir != DOWN) && (GetCollisionDist(dir_down, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_DOWN_SWOOP);

	EComboType SwoopType;
	if (SwoopChoices.GetSelection(SwoopType))
	{
		return SwoopType;
	}

	return COMBO_TYPE_NONE;
}

EComboType CBot::MakeAttackAvoidChainSwoop()
{
	TMultiChoice<EComboType> SwoopChoices;
	edict_t *pEdict = edict(); // So we don't have to call edict() more then once...
	int CurSwoopDir = m_Swoop.m_iSwoopDirection; // Shortcut

	if ((CurSwoopDir != LEFT) && (GetCollisionDist(dir_left, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_LEFT_AVOID_ATTACK_SWOOP);
	if ((CurSwoopDir != RIGHT) && (GetCollisionDist(dir_right, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_RIGHT_AVOID_ATTACK_SWOOP);
	if ((CurSwoopDir != UP) && (GetCollisionDist(dir_up, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_UP_AVOID_ATTACK_SWOOP);
	if ((CurSwoopDir != DOWN) && (GetCollisionDist(dir_down, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_DOWN_AVOID_ATTACK_SWOOP);

	EComboType SwoopType;
	if (SwoopChoices.GetSelection(SwoopType))
	{
		return (SwoopType);
	}

	return COMBO_TYPE_NONE;
}

EComboType CBot::MakeAwayChainSwoop(CBaseEntity *pEntity)
{
	TMultiChoice<EComboType> SwoopChoices;
	edict_t *pEdict = edict(); // So we don't have to call edict() more then once...
	int CurSwoopDir = m_Swoop.m_iSwoopDirection; // Shortcut
	int iEntDir = UTIL_EntDir(this, pEntity->pev->origin);
	short LeftVal = 50, RightVal = 50, UpVal = 50, DownVal = 50, BackVal = 50;

	if (iEntDir & dir_left)
		LeftVal = 15;
	if (iEntDir & dir_right)
		RightVal = 15;
	if (iEntDir & dir_up)
		UpVal = 15;
	if (iEntDir & dir_down)
		DownVal = 15;
	if (iEntDir & dir_back)
		BackVal = 15;

	if ((CurSwoopDir != LEFT) && (GetCollisionDist(dir_left, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_LEFT_SWOOP, LeftVal);
	if ((CurSwoopDir != RIGHT) && (GetCollisionDist(dir_right, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_RIGHT_SWOOP, RightVal);
	if ((CurSwoopDir != UP) && (GetCollisionDist(dir_up, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_UP_SWOOP, UpVal);
	if ((CurSwoopDir != DOWN) && (GetCollisionDist(dir_down, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_DOWN_SWOOP, DownVal);
	if ((CurSwoopDir != BACK) && (GetCollisionDist(dir_back, 650.0f) >= 650.0f))
		SwoopChoices.Insert(COMBO_TYPE_BACKWARD_SWOOP, BackVal);

	EComboType SwoopType;
	if (SwoopChoices.GetSelection(SwoopType))
	{
		return SwoopType;
	}

	return COMBO_TYPE_NONE;
}

bool CBot::CanMeleeKickPlayer(CBasePlayer *pPlayer)
{
	return (!EntIsInFieldOfView(pPlayer, this) || !pPlayer->CanMelee(this));
}

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