#include "gen_bot_header.h"
#include "monsters.h"
#include "teamplay_gamerules.h"

//harSens: Linux compile fix
extern CLeftOutput lcout;

extern weapon_info_s BotWeaponInfoTable[];

Vector GatherDBPosForTeamGood = g_vecZero;
Vector GatherDBPosForTeamEvil = g_vecZero;

// Custom made gather spots
db_spot_s GatherSpotsForTeamGood[MAX_CUSTOM_DB_GATHER_SPOTS];
db_spot_s GatherSpotsForTeamEvil[MAX_CUSTOM_DB_GATHER_SPOTS];

CBotDBManager BotDBManager;

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

Vector *CBot::GetDBGatherSpot()
{
	return (pev->team == GOOD_TEAM) ? &GatherDBPosForTeamGood : &GatherDBPosForTeamEvil;
}

// returns true if the dragonball task should be made
bool CBot::CheckDragonBalls()
{
	switch(m_eBotDBType)
	{
	case DB_DEFENDER:
		if (UTIL_DBsAreGathered()) // Are the dragonballs gathered?
		{
			if (SearchGatheredSpot())
			{
				// Look for them and make a wish
				m_eCurrentBotDBState = STATE_GOTOGATHEREDSPOT;
				return true;
			}
		}
		else if	(SearchDBDefendSpot())
		{
			m_eCurrentBotDBState = STATE_GOTODEFENDSPOT;
			return true;
		}
		break;
	case DB_COLLECTER:
		if (UTIL_DBsAreGathered()) // Are the dragonballs gathered?
		{
			if (SearchGatheredSpot())
			{
				// Look for them and make a wish
				m_eCurrentBotDBState = STATE_GOTOGATHEREDSPOT;
				return true;
			}
		}
		else if (m_iDragonball) // Bot has a dragonball?
		{
			if (SearchDBGatherSpot())
			{
				// Search for a gather spot
				m_eCurrentBotDBState = STATE_GOTOGATHERSPOT;
				return true;
			}
		}
		else if (SearchDragonBall())
		{
			// Search for a DB
			m_eCurrentBotDBState = STATE_GOTODB;
			return true;
		}
		break;
	case DB_ATTACKER:
		if (UTIL_DBsAreGathered()) // Are the dragonballs gathered?
		{
			if (SearchGatheredSpot())
			{
				// Look for them and make a wish
				m_eCurrentBotDBState = STATE_GOTOGATHEREDSPOT;
				return true;
			}
		}
		else if (m_iDragonball) // Bot has a dragonball?
		{
			if (SearchDBGatherSpot())
			{
				// Search for a gather spot
				m_eCurrentBotDBState = STATE_GOTOGATHERSPOT;
				return true;
			}
		}
		else
		{
			if (m_pBotEnemy)
			{
				// Does this enemy has a dragonball?
				if (m_pBotEnemy->m_iDragonball)
					return false; // Attack him!

				float flMinDistForDB = 750.0f;

				if (m_pPersonality->aggression_level == Aggressive)
					flMinDistForDB = 1350.0f;

				if (GetDistance(m_pBotEnemy) < flMinDistForDB)
					return false;
			}
			
			if (SearchDragonBall())
			{
				// Search for a DB
				m_eCurrentBotDBState = STATE_GOTODB;
				return true;
			}
			break;
		}
	}

	return false;
}

bool CBot::SearchGatheredSpot()
{
	if (g_pGameRules->CanWish() == FALSE)
		return false;

	CBaseEntity *pDB = NULL;

	pDB = UTIL_FindEntityByClassname(pDB, "dragonball");

	if (IsNull(pDB))
		return false;

	Vector v_dest = pDB->pev->origin;

	if (FVisible(v_dest))
	{
		// Spot is visible, no need to look for waypoints \o/
		m_pGoalDB = pDB; // Store the db
		return true;
	}

	int GoalWPIndex = -1;

	// Any waypoints?
	if (WaypointClass.m_iWaypointCount >= 1)
		GoalWPIndex = WaypointClass.GetNearestWaypoint(v_dest, this, 1750.0f, pev->team);

	if (GoalWPIndex != -1)
	{
		// Check if there is a valid waypoint near the bot
		int WPNearBotIndex = WaypointClass.GetNearestWaypoint(this, 1750.0f, pev->team);

		// if so check if there is a route from that WP to the WP near the gatherspot
		if ((WPNearBotIndex != -1) && 
			(WaypointClass.RouteFromTo(WPNearBotIndex, GoalWPIndex, pev->team) != WAYPOINT_UNREACHABLE))
		{
			SetGoalWaypoint(GoalWPIndex);
			m_pGoalDB = pDB;
			return true;
		}
	}

	flood_wp_s *pGoalFloodWP = NULL;

	// Any flood waypoints?
	if ((WaypointFlooder.m_iFloodWPCount >= 1) && (!m_bDeletingAStarLists))
		pGoalFloodWP = WaypointFlooder.GetNearestFloodWP(v_dest, 750.0f);

	if ((pGoalFloodWP) && (!InvalidGoalFloodWP(pGoalFloodWP)))
	{
		flood_wp_s *pFloodWPNearBot = WaypointFlooder.GetNearestFloodWP(pev->origin, 750.0f);

		// Is there a flood WP near the bot?
		if (pFloodWPNearBot)
		{
			SetGoalFloodWP(pGoalFloodWP);
			m_pGoalDB = pDB;
			return true;
		}
	}

	return false;
}

// Returns true when found a DB gather spot
bool CBot::SearchDBGatherSpot()
{
	if ((GatherDBPosForTeamGood == g_vecZero) || (GatherDBPosForTeamEvil == g_vecZero))
		return false;

	if (m_iDragonball == 0)
		return false; // Why search a db spot when we don't have a db...

	if (pev->team == GOOD_TEAM)
	{
		if (FVisible(GatherDBPosForTeamGood))
		{
			return true;
		}
	}
	else
	{
		if (FVisible(GatherDBPosForTeamEvil))
		{
			return true;
		}
	}

	int GoalWPIndex = -1;

	// Any waypoints?
	if (WaypointClass.m_iWaypointCount >= 1)
		GoalWPIndex = SearchNearestWPForDBSpot();

	if (GoalWPIndex != -1)
	{
		// Check if there is a valid waypoint near the bot
		int WPNearBotIndex = WaypointClass.GetNearestWaypoint(this, 1750.0f, pev->team);

		// if so check if there is a route from that WP to the WP near the gatherspot
		if ((WPNearBotIndex != -1) && 
			(WaypointClass.RouteFromTo(WPNearBotIndex, GoalWPIndex, pev->team) != WAYPOINT_UNREACHABLE))
		{
			SetGoalWaypoint(GoalWPIndex);
			return true;
		}
	}

	flood_wp_s *pGoalFloodWP = NULL;

	// Any flood waypoints?
	if ((WaypointFlooder.m_iFloodWPCount >= 1) && (!m_bDeletingAStarLists))
		pGoalFloodWP = WaypointFlooder.GetNearestFloodWP(*GetDBGatherSpot(), 750.0f);

	if ((pGoalFloodWP) && (!InvalidGoalFloodWP(pGoalFloodWP)))
	{
		flood_wp_s *pFloodWPNearBot = WaypointFlooder.GetNearestFloodWP(pev->origin, 750.0f);

		// Is there a flood WP near the bot?
		if (pFloodWPNearBot)
		{
			SetGoalFloodWP(pGoalFloodWP);
			return true;
		}
	}

	return false;
}

// Returns true when found a DB defend spot
bool CBot::SearchDBDefendSpot()
{
	if ((GatherDBPosForTeamGood == g_vecZero) || (GatherDBPosForTeamEvil == g_vecZero))
		return false;

	if (pev->team == GOOD_TEAM)
	{
		if (FVisible(GatherDBPosForTeamGood))
		{
			return true;
		}
	}
	else
	{
		if (FVisible(GatherDBPosForTeamEvil))
		{
			return true;
		}
	}

	int GoalWPIndex = -1;

	// Any waypoints?
	if (WaypointClass.m_iWaypointCount >= 1)
		GoalWPIndex = SearchNearestWPForDBSpot();

	if (GoalWPIndex != -1)
	{
		// Check if there is a valid waypoint near the bot
		int WPNearBotIndex = WaypointClass.GetNearestWaypoint(this, 1750.0f, pev->team);

		// if so check if there is a route from that WP to the WP near the gatherspot
		if ((WPNearBotIndex != -1) && 
			(WaypointClass.RouteFromTo(WPNearBotIndex, GoalWPIndex, pev->team) != WAYPOINT_UNREACHABLE))
		{
			SetGoalWaypoint(GoalWPIndex);
			return true;
		}
	}

	// Any flood waypoints?
	flood_wp_s *pGoalFloodWP = NULL;

	if ((WaypointFlooder.m_iFloodWPCount >= 1) && (!m_bDeletingAStarLists))
		pGoalFloodWP = WaypointFlooder.GetNearestFloodWP(*GetDBGatherSpot(), 750.0f);

	if ((pGoalFloodWP) && (!InvalidGoalFloodWP(pGoalFloodWP)))
	{
		flood_wp_s *pFloodWPNearBot = WaypointFlooder.GetNearestFloodWP(pev->origin, 750.0f);

		// Is there a flood WP near the bot?
		if (pFloodWPNearBot)
		{
			SetGoalFloodWP(pGoalFloodWP);
			return true;
		}
	}

	return false;
}

// returns true if the bot has found a db
bool CBot::SearchDragonBall()
{
	if (!BotManager.m_bDBMode)
		return false; // No DB's when no DB mode...

	if (m_iDragonball)
		return true; // Don't search a db when we already got one :)

	if ((UTIL_DBsAreGathered()) || (UTIL_DBsAreActive() == false))
		return false; // Can't collect dragonballs

	CBaseEntity *pent = NULL;
	CBaseEntity *pDragonBall = NULL;
	float Dist = 0.0f;
	float NearestDBDist = 9999.9f;
	int WptInd = -1;
	int NearestWptInd = -1;
	int WptIndNearBot = -1;
	flood_wp_s *pNearestFloodWP = NULL;
	flood_wp_s *pFloodWPNearBot = NULL;
	flood_wp_s *pFloodWP = NULL;
	Vector v_GatherVec;

	if (pev->team == GOOD_TEAM)
		v_GatherVec = GatherDBPosForTeamGood;
	else
		v_GatherVec = GatherDBPosForTeamEvil;

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

	if ((WaypointFlooder.m_iFloodWPCount >= 1) && (!m_bDeletingAStarLists))
	{
		// Check if there is a valid flood waypoint near the bot
		pFloodWPNearBot = WaypointFlooder.GetNearestFloodWP(pev->origin, 750.0f);
	}

	// Search a dragonball
   	while ((pent = UTIL_FindEntityByClassname(pent, "dragonball")) != NULL)
	{
		if (IsNull(pent))
			continue;

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

		if (Dist >= NearestDBDist)
			continue;

		if (UTIL_Distance(v_GatherVec, pent->pev->origin) < 100.0f)
			continue;

		if (!UTIL_EntDoesntMove(pent))
			continue; // Don't search for falling db's

		// If the dragonball is not visible look the bot can use a waypoint to reach it
		if (!FVisible(pent->pev->origin))
		{
			// Is there a waypoint near the bot?
			if (WptIndNearBot != -1)
			{
				// Is there is a valid waypoint near the db?
				WptInd = WaypointClass.GetNearestWaypoint(pent->pev->origin, pent, 750.0f, pev->team);
				if (WptInd == -1)
					continue; // Nope, skip the DB(Can't reach it)

				// See if there is a route to the waypoint near the DB waypoint
				if (WaypointClass.RouteFromTo(WptIndNearBot, WptInd, pev->team) == WAYPOINT_UNREACHABLE)
					continue; // Nope, skip the DB(Can't reach it)

				NearestWptInd = WptInd;
			}
			// Is there a near flood wp instead?
			else if (pFloodWPNearBot)
			{
				// Is there a valid flood wp near the db?
				pFloodWP = WaypointFlooder.GetNearestFloodWP(pent->pev->origin, 750.0f);

				if (pFloodWP == NULL)
					continue;

				if (InvalidGoalFloodWP(pFloodWP))
					continue;

				/* UNDONE
				// Bot can reach the goal from here?
				if (GetNextBestFloodWP(pFloodWPNearBot, WptInd) == -1)
					continue; // Nope...*/

				pNearestFloodWP = pFloodWP;
			}
			else
				continue;
		}
		else
		{
			NearestWptInd = -1;
			pNearestFloodWP = NULL;
		}

		// If we get here the DB is reachable and is nearer then the nearest found DB
		NearestDBDist = Dist;
		pDragonBall = pent;
	}

	if (pDragonBall)
	{
		m_pGoalDB = pDragonBall;

		if (NearestWptInd != -1) // Should the bot use a waypoint?
			SetGoalWaypoint(NearestWptInd);
		else if (pNearestFloodWP) // Should the bot use a flood wp instead?
			SetGoalFloodWP(pNearestFloodWP);
		else // else just aim at the DB(its visible already)
			AimToVec(pDragonBall->pev->origin);

		return true;
	}

	return false; // No (valid) db's found
}

int CBot::SearchNearestWPForDBSpot()
{
	if ((GatherDBPosForTeamGood == g_vecZero) || (GatherDBPosForTeamEvil == g_vecZero))
		return -1;

	Vector v_GatherVec;

	if (pev->team == GOOD_TEAM)
		v_GatherVec = GatherDBPosForTeamGood;
	else
		v_GatherVec = GatherDBPosForTeamEvil;

	return WaypointClass.GetNearestWaypoint(v_GatherVec, this, 1000, pev->team);
}

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

// ==================================================================
// Code of CBotDBManager - Begin
// ==================================================================

CBotDBManager::CBotDBManager() : m_bDrawDBSpots(false),	m_fLastDBSpotDrawTime(0.0f), 
								 m_bAssignDBSpots(false), m_bCheckedForValidDBs(false),
								 m_bCanPlayCPTDB(false)
{
}

void CBotDBManager::Think()
{
	// Should dragon ball gather spots be drawed
	if (m_bDrawDBSpots)
		DrawDBGatherSpots();

	// Should bots decide a new spot to collect the dragonballs?
	if (m_bAssignDBSpots)
		AssignDBGatherSpots();
}

bool CBotDBManager::CanPlayCPTDB()
{
	if (!m_bCheckedForValidDBs)
	{
		short iDBs = 0;
		CBaseEntity *pEnt = NULL;

		while((pEnt = UTIL_FindEntityByClassname(pEnt, "item_dragonball")) != NULL)
			iDBs++;

		m_bCanPlayCPTDB = (iDBs >= 7);
		m_bCheckedForValidDBs = true;
	}

	return m_bCanPlayCPTDB;
}

void CBotDBManager::AssignBotRole(CBot *pBot)
{
	short NeededAttackers = 0, NeededDefenders = 0, NeededCollecters = 0;
	bot_role_info_s *pBotRoleInfo;

	if (pBot->pev->team == GOOD_TEAM)
		pBotRoleInfo = &m_TeamGoodBotDBRoleInfo;
	else
		pBotRoleInfo = &m_TeamEvilBotDBRoleInfo;

	short Total = (pBotRoleInfo->sAttackers + pBotRoleInfo->sDefenders + pBotRoleInfo->sCollecters);

	if (pBotRoleInfo->sAttackers)
		NeededAttackers = float(float(Total) / 3.0f);
	if (pBotRoleInfo->sAttackers)
		NeededDefenders = float(float(Total) / 2.0f);
	if (pBotRoleInfo->sAttackers)
		NeededCollecters = float(float(Total) / 2.0f);

	short AttackerDiff = NeededAttackers - pBotRoleInfo->sAttackers;
	short DefenderDiff = NeededDefenders - pBotRoleInfo->sDefenders;
	short CollecterDiff = NeededCollecters - pBotRoleInfo->sCollecters;

	if ((AttackerDiff > DefenderDiff) && (AttackerDiff > CollecterDiff))
	{
		// Bot should be a attacker
		pBot->m_eBotDBType = DB_ATTACKER;
		pBotRoleInfo->sAttackers++;
	}
	else if ((DefenderDiff > AttackerDiff) && (DefenderDiff > CollecterDiff))
	{
		// Bot should be a defender
		pBot->m_eBotDBType = DB_DEFENDER;
		pBotRoleInfo->sDefenders++;
	}
	else if ((CollecterDiff > DefenderDiff) && (CollecterDiff > AttackerDiff))
	{
		// Bot should be a collecter
		pBot->m_eBotDBType = DB_COLLECTER;
		pBotRoleInfo->sCollecters++;
	}
	else
	{
		// Assign bot to a random role
		switch(RANDOM_LONG(1, 3))
		{
		case 1:
			// Bot should be a attacker
			pBot->m_eBotDBType = DB_ATTACKER;
			pBotRoleInfo->sAttackers++;
			break;
		case 2:
			// Bot should be a defender
			pBot->m_eBotDBType = DB_DEFENDER;
			pBotRoleInfo->sDefenders++;
			break;
		case 3:
			// Bot should be a collecter
			pBot->m_eBotDBType = DB_COLLECTER;
			pBotRoleInfo->sCollecters++;
			break;
		}
	}
}

void CBotDBManager::BotDisconnect(CBot *pBot)
{
	bot_role_info_s *pBotRoleInfo;

	if (pBot->pev->team == GOOD_TEAM)
		pBotRoleInfo = &m_TeamGoodBotDBRoleInfo;
	else
		pBotRoleInfo = &m_TeamEvilBotDBRoleInfo;

	switch(pBot->m_eBotDBType)
	{
	case DB_ATTACKER:
		pBotRoleInfo->sAttackers--;
		break;
	case DB_DEFENDER:
		pBotRoleInfo->sDefenders--;
		break;
	case DB_COLLECTER:
		pBotRoleInfo->sCollecters--;
		break;
	}
}

void CBotDBManager::AssignDBGatherSpots()
{
	m_bAssignDBSpots = false;

	if (!BotManager.IsDBMode())
		return; // No need to check if no db mode

	short GoodIndex = GetRandomDBSpotIndex(GOOD_TEAM);
	short EvilIndex = GetRandomDBSpotIndex(EVIL_TEAM);

	if ((GoodIndex > -1) && (EvilIndex > -1))
	{
		GatherDBPosForTeamGood = GatherSpotsForTeamGood[GoodIndex].origin;
		GatherDBPosForTeamEvil = GatherSpotsForTeamEvil[EvilIndex].origin;
		return; // No need to go further
	}

	if ((GoodIndex == -1) && (EvilIndex != -1))
		cout << "Error: missing DB gather spot for team good!\n";
	else if ((EvilIndex == -1) && (GoodIndex != -1))
		cout << "Error: missing DB gather spot for team evil!\n";
	
	CBaseEntity *pDragonBall = NULL;
	TMultiChoice<CBaseEntity *> DBChoiceForTeamGood;
	CBaseEntity *pTeamGoodGatherSpot = NULL;

   	while ((pDragonBall = UTIL_FindEntityByClassname(pDragonBall, "item_dragonball")) != NULL)
	{
		if (IsNull(pDragonBall))
			continue;

		DBChoiceForTeamGood.Insert(pDragonBall, 50);
	}

	pTeamGoodGatherSpot = NULL;
	DBChoiceForTeamGood.GetSelection(pTeamGoodGatherSpot);

	if (pTeamGoodGatherSpot)
		GatherDBPosForTeamGood = pTeamGoodGatherSpot->pev->origin;

	TMultiChoice<CBaseEntity *> DBChoiceForTeamEvil;
	pDragonBall = NULL;

   	while ((pDragonBall = UTIL_FindEntityByClassname(pDragonBall, "item_dragonball")) != NULL)
	{
		if (IsNull(pDragonBall))
			continue;

		if (pDragonBall == pTeamGoodGatherSpot)
			continue;

		DBChoiceForTeamEvil.Insert(pDragonBall, 50);
	}

	pDragonBall = NULL;
	DBChoiceForTeamEvil.GetSelection(pDragonBall);

	if (pDragonBall)
		GatherDBPosForTeamEvil = pDragonBall->pev->origin;
}

short CBotDBManager::GetRandomDBSpotIndex(short Team)
{
	short s;
	TMultiChoice<short> ValidIndexChoices;

	for (s=0;s<MAX_CUSTOM_DB_GATHER_SPOTS;s++)
	{
		if (Team == GOOD_TEAM)
		{
			if (GatherSpotsForTeamGood[s].Deleted)
				continue;
		}
		else
		{
			if (GatherSpotsForTeamEvil[s].Deleted)
				continue;
		}
		ValidIndexChoices.Insert(s, 50);
	}

	if(ValidIndexChoices.GetSelection(s))
		return s;

	return -1;
}

void CBotDBManager::LoadCustomGatherDBSpots()
{
	int i;
	for(i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		GatherSpotsForTeamGood[i].origin = g_vecZero;
		GatherSpotsForTeamGood[i].Deleted = true;
		GatherSpotsForTeamEvil[i].origin = g_vecZero;
		GatherSpotsForTeamEvil[i].Deleted = true;
	}

	char szMapName[32] = "";
	char FileName[256] = "";
	FILE *pDBFile = NULL;
	
   	strcpy(szMapName, STRING(gpGlobals->mapname));
	strcat(szMapName, ".dbg");
	
	BuildBotFileName(FileName, "dragonball", szMapName);

	cout << "Reading dragonball info file...";
	pDBFile = fopen(FileName, "rb");

	if (!pDBFile)
	{
		cout << " file not found, db spawnpoints will be used as gather spots.\n";
		return;
	}

	db_file_header_s FileHeader;
	fread(&FileHeader, sizeof(FileHeader), 1, pDBFile);

	if ((FileHeader.flVersion != DB_SPOT_VERSION) || 
        (FStrEq(STRING(gpGlobals->mapname), FileHeader.szMapName) == false))
	{
		fclose(pDBFile);
		return;
	}

	for (i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		if (!pDBFile)
		{
			cout << "Error reading dragonball file!\n";
			return;
		}
		fread(&GatherSpotsForTeamGood[i], sizeof(GatherSpotsForTeamGood[0]), 1, pDBFile);
	}

	for (i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		if (!pDBFile)
		{
			cout << "Error reading dragonball file!\n";
			return;
		}

		fread(&GatherSpotsForTeamEvil[i], sizeof(GatherSpotsForTeamEvil[0]), 1, pDBFile);
	}

	fclose(pDBFile);
	cout << " Done\n";
}

void CBotDBManager::SaveCustomGatherDBSpots()
{
	if (!BotManager.IsDBMode())
		return;

	char szMapName[32] = "";
	char FileName[256] = "";
	FILE *pDBFile = NULL;
	byte i;
	
   	strcpy(szMapName, STRING(gpGlobals->mapname));
	strcat(szMapName, ".dbg");
	
	BuildBotFileName(FileName, "dragonball", szMapName);

	cout << "Saving dragonball info file...";
	pDBFile = fopen(FileName, "wb");

	if (!pDBFile)
	{
		cout << " couldn't create file!\n";
		return;
	}

	db_file_header_s FileHeader;

	// Set file header
	FileHeader.flVersion = DB_SPOT_VERSION;
	strcpy(FileHeader.szMapName, STRING(gpGlobals->mapname));

	fwrite(&FileHeader, sizeof(FileHeader), 1, pDBFile);

	for (i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		fwrite(&GatherSpotsForTeamGood[i], sizeof(GatherSpotsForTeamGood[0]), 1, pDBFile);
	}

	for (i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		fwrite(&GatherSpotsForTeamEvil[i], sizeof(GatherSpotsForTeamEvil[0]), 1, pDBFile);
	}

	fclose(pDBFile);
	cout << " Done\n";
}

void CBotDBManager::PlaceNewDBGatherSpot()
{
	if (!BotManager.IsDBMode())
		return;

	CBasePlayer *pHost = BotManager.GetHost();

	if (UTIL_IsValidPlayer(pHost, false, false) == false)
		return;

	short Team = pHost->pev->team;
	byte b = 0;
	TraceResult tr;

	if (Team == GOOD_TEAM)
	{
		while (b < MAX_CUSTOM_DB_GATHER_SPOTS)
		{
			if (GatherSpotsForTeamGood[b].Deleted)
			{
				UTIL_TraceLine(pHost->pev->origin, pHost->pev->origin + Vector(0, 0, -9999),
							   ignore_monsters, pHost->pev->pContainingEntity, &tr);
				if (tr.flFraction < 1.0) // Should alway be true
				{
					 // Put it slightly above the ground....
					GatherSpotsForTeamGood[b].origin = tr.vecEndPos + Vector(0, 0, DBSPOT_HEIGHTOFFSET);
				}
				else
					GatherSpotsForTeamGood[b].origin = pHost->pev->origin;

				GatherSpotsForTeamGood[b].Deleted = false;
				return;
			}
			b++;
		}
		
		cout << "Cannot create new DragonBall spot for team good, max reached.\n";
		return;
	}
	else
	{
		while (b < MAX_CUSTOM_DB_GATHER_SPOTS)
		{
			if (GatherSpotsForTeamEvil[b].Deleted)
			{
				UTIL_TraceLine(pHost->pev->origin, pHost->pev->origin + Vector(0, 0, -9999),
							   ignore_monsters, pHost->pev->pContainingEntity, &tr);
				if (tr.flFraction < 1.0) // Should alway be true
				{
					 // Put it slightly above the ground....
					GatherSpotsForTeamEvil[b].origin = tr.vecEndPos + Vector(0, 0, DBSPOT_HEIGHTOFFSET);
				}
				else
					GatherSpotsForTeamEvil[b].origin = pHost->pev->origin;

				GatherSpotsForTeamEvil[b].Deleted = false;
				return;
			}
			b++;
		}
		
		cout << "Cannot create new DragonBall spot for team evil, max reached.\n";
		return;
	}
}

void CBotDBManager::DeleteDBGatherSpot()
{
	if (!BotManager.IsDBMode())
		return;

	CBasePlayer *pHost = BotManager.GetHost();

	if (UTIL_IsValidPlayer(pHost, false, false) == false)
		return;

	short Team = pHost->pev->team;
	db_spot_s *pSpot = GetNearestDBSpotIndex(pHost, 75.0f);

	if (pSpot)
		pSpot->Deleted = true;
}

db_spot_s *CBotDBManager::GetNearestDBSpotIndex(CBasePlayer *pPlayer, float flRadius)
{
	CBasePlayer *pHost = BotManager.GetHost();

	if (UTIL_IsValidPlayer(pHost, false, false) == false)
		return NULL;

	Vector v_src = pHost->pev->origin;
	float NearestDist = 9999.9f;
	float Dist;
	db_spot_s *pNearestSpot = NULL;
	short s;
	
	for (s=0;s<MAX_CUSTOM_DB_GATHER_SPOTS;s++)
	{
		if (GatherSpotsForTeamGood[s].Deleted)
			continue;

		Dist = UTIL_Distance(v_src, GatherSpotsForTeamGood[s].origin);

		if ((Dist < NearestDist) && (Dist <= flRadius))
		{
			if (pHost->FVisible(GatherSpotsForTeamGood[s].origin))
			{
				NearestDist = Dist;
				pNearestSpot = &GatherSpotsForTeamGood[s];
			}
		}
	}

	for (s=0;s<MAX_CUSTOM_DB_GATHER_SPOTS;s++)
	{
		if (GatherSpotsForTeamEvil[s].Deleted)
			continue;

		Dist = UTIL_Distance(v_src, GatherSpotsForTeamEvil[s].origin);

		if ((Dist < NearestDist) && (Dist <= flRadius))
		{
			if (pHost->FVisible(GatherSpotsForTeamEvil[s].origin))
			{
				NearestDist = Dist;
				pNearestSpot = &GatherSpotsForTeamEvil[s];
			}
		}
	}

	return pNearestSpot;
}

void CBotDBManager::DrawDBGatherSpots()
{
	if (m_fLastDBSpotDrawTime > gpGlobals->time)
		return;

	m_fLastDBSpotDrawTime = gpGlobals->time + 0.25f;
	
	if (!BotManager.IsDBMode())
		return;

	CBasePlayer *pHost = BotManager.GetHost();

	if (!UTIL_IsValidPlayer(pHost, false, false))
		return;

	byte i;
	Vector v_src, v_end;

	// Draw all good DB spots
	for (i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		if (GatherSpotsForTeamGood[i].Deleted)
			continue;

		v_src = GatherSpotsForTeamGood[i].origin - Vector(0, 0, DBSPOT_HEIGHTOFFSET);
		v_end = v_src + Vector(0, 0, 100);
		UTIL_DrawBeam(pHost, v_src, v_end, 30, 0, 0, 255, 0, 250, 5);
	}

	// Draw all evil DB spots
	for (i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		if (GatherSpotsForTeamEvil[i].Deleted)
			continue;

		v_src = GatherSpotsForTeamEvil[i].origin - Vector(0, 0, DBSPOT_HEIGHTOFFSET);
		v_end = v_src + Vector(0, 0, 100);
		UTIL_DrawBeam(pHost, v_src, v_end, 30, 0, 255, 0, 0, 250, 5);
	}
}

void CBotDBManager::ClearAllDBSpots()
{
	int i;
	for(i=0;i<MAX_CUSTOM_DB_GATHER_SPOTS;i++)
	{
		GatherSpotsForTeamGood[i].origin = g_vecZero;
		GatherSpotsForTeamGood[i].Deleted = true;
		GatherSpotsForTeamEvil[i].origin = g_vecZero;
		GatherSpotsForTeamEvil[i].Deleted = true;
	}
	cout << "All dragonball gather spots removed!\n";
}


// ==================================================================
// Code of CBotDBManager - End
// ==================================================================

// ==================================================================
// Task Code - Begin
// ==================================================================

// Dragonball task, handles all the db searching and gathering
CDBTask::~CDBTask()
{
	pBot->m_pGoalDB = NULL;
	pBot->ResetWaypointVars();
	pBot->m_eCurrentBotDBState = STATE_NOCURRENTDBSTATE;
}

void CDBTask::TaskThink()
{
	pBot->m_sSwoopFlags = 0;

	// Check if the bot should go to a spot where the dragonballs are gathered
	if (CheckGatheredSpot())
	{
		m_bMoveAway = false;
		pBot->m_eCurrentBotDBState = STATE_GOTOGATHEREDSPOT;
	}

	// Moving away(from dragonballs)?
	if (m_bMoveAway)
	{
		PrintAI("Moving away from DBs\n");
		pBot->ResetMoveSpeed();
		switch(m_eMoveAwayState)
		{
		case STATE_PAUSE:
			m_fStuckCheckTime += 0.2f;
			if (m_fNextAwayStateUpdate < gpGlobals->time)
			{
				m_fNextAwayStateUpdate = gpGlobals->time + 0.2f;
				m_eMoveAwayState = STATE_TURNAWAY;
				pBot->TurnAround();
			}
			break;
		case STATE_TURNAWAY:
			m_fStuckCheckTime += 0.2f;
			if (pBot->m_fYawSpeed <= 15.0f)
			{
				m_eMoveAwayState = STATE_MOVEAWAY;
				m_fNextAwayStateUpdate = gpGlobals->time + 0.75f;
			}
			break;
		case STATE_MOVEAWAY:
			pBot->SetMoveDir(dir_forward);
			if (m_fNextAwayStateUpdate < gpGlobals->time)
				TaskState = DONE;
			break;
		}

		return;
	}

	if (pBot->m_eBotDBType == DB_DEFENDER)
	{
		// Does the bot has an enemy?
		if (pBot->m_pBotEnemy && pBot->CanAttackPlayer(pEnemy))
		{
			float flGatherDist = pBot->GetDistance(*pBot->GetDBGatherSpot());
			float flEnemyDist = pBot->GetDistance(pBot->m_pBotEnemy);
			float flMinDistToAttack;

			// Is the gather spot visible
			if (pBot->FVisible(*pBot->GetDBGatherSpot()))
				flMinDistToAttack = 750.0f;
			else if (flGatherDist < 1250.0f)
				flMinDistToAttack = 500.0f;
			else
				flMinDistToAttack = 9999.9f; // Don't attack

			if ((flEnemyDist < 450.0f) || (flGatherDist < 1750.0f))
			{
				// Is the enemy close enough?
				if (flEnemyDist < flMinDistToAttack)
				{
					if (AttackEnemy())
					{
						TaskState = DONE;
						return;
					}
				}
			}
		}
	}

	switch (pBot->m_eCurrentBotDBState)
	{
	case STATE_GOTODB:
		if (!GoToDB())
		{
			// Bot couldn't find a way to reach the dragonball
			TaskState = DONE;
			return;
		}

		break;
	case STATE_GOTOGATHERSPOT:
		if (!GoToDBSpot())
		{
			// Bot couldn't find a way to reach a gather spot
			TaskState = DONE;
			return;
		}

		break;
	case STATE_GOTOGATHEREDSPOT:
		if (!GoToGatheredSpot())
		{
			// Bot couldn't find a way to reach the gathered dragonballs
			TaskState = DONE;
			return;
		}

		break;
	case STATE_DEFENDGATHERSPOT:
		if (!DefendGatherSpot())
		{
			// Bot couldn't defend the dbh gather spot
			TaskState = DONE;
			return;
		}

		break;
	case STATE_GOTODEFENDSPOT:
		if (!GoToDefendSpot())
		{
			// Bot couldn't reach the defend spot
			TaskState = DONE;
			return;
		}

		break;
	default:
		TaskState = DONE;

		return;
	}
}

bool CDBTask::CheckGatheredSpot()
{
	return ((pBot->m_eCurrentBotDBState != STATE_GOTOGATHEREDSPOT) && (UTIL_DBsAreGathered()) &&
			(pBot->SearchGatheredSpot()));
}

bool CDBTask::DefendGatherSpot()
{
	// Bot has a dragonball?
	if (pBot->m_iDragonball)
	{
		if (pBot->SearchDBGatherSpot())
		{
			pBot->m_eCurrentBotDBState = STATE_GOTOGATHERSPOT;
			return true;
		}
	}

	// Bot is charging an attack?
	if (m_bCharging)
	{
		PrintAI("Charging while defending\n");

		if (pBot->m_pActiveItem->m_iId != pBot->m_pCurrentWeaponInfo->iId)
			pBot->SelectItem(pBot->m_pCurrentWeaponInfo->szName);

		if (pBot->m_pCurrentWeaponInfo->bUseSecondary)
			pEdict->v.button |= IN_ATTACK2;
		else
			pEdict->v.button |= IN_ATTACK;

		if (pBot->GetKi() <= 350)
		{
			if (pBot->GetKi() < 175)
				m_bCharging = false;
			else if ((pBot->GetKi() <= 250) && (pBot->m_iChargePercent >= 50))
				m_bCharging = false;
			else if ((pBot->GetKi() <= 350) && (pBot->m_iChargePercent >= 65))
				m_bCharging = false;
		}

		// Bot has now an enemy?
		if (pEnemy && m_bCharging)
		{
			// Almost done charging?
			if (pBot->m_iChargePercent >= 33)
			{
				float flEnemyDist = pBot->GetDistance(pEnemy);

				if (flEnemyDist < 450.0f)
					m_bCharging = false;
				else if ((flEnemyDist < 750.0f) && (pBot->m_iChargePercent >= 50))
					m_bCharging = false;
				else if ((flEnemyDist < 1000.0f) && (pBot->m_iChargePercent >= 75))
					m_bCharging = false;
				else
				{
					// Switch to charge task(handles more combat stuff)
					pBot->m_eTaskAfterTask = TaskChargeAttack;
					return false;
				}
			}
			else
			{
				// Switch to charge task(handles more combat stuff)
				pBot->m_eTaskAfterTask = TaskChargeAttack;
				return false;
			}
		}

		// Bot isn't charging anymore?
		if (!m_bCharging)
		{
			if (pEnemy)
			{
				// set next time to shoot
				pBot->AddShootDelay();

				// Check first if the enemy is ascending and if the bot is NOT a transkiller?
				if ((pEnemy->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");
				}

				if (pBot->m_pCurrentWeaponInfo->bControllable && (pBot->m_iChargePercent > 0))
				{
					if (pBot->m_pCurrentWeaponInfo->iId == WEAPON_FRIEZADISC)
					{
						pBot->m_eTaskAfterTask = TaskControlFriezaDisc;
						return false;
					}
					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;

						pBot->m_eTaskAfterTask = TaskControlBeam;
						return false;
					}
				}
			}
			else
			{
				// else just let the attack disappear by changing the current attack
				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");
			}
		}
	}

	float flSpotDist = pBot->GetDistance(*pBot->GetDBGatherSpot());
	// Is the gather spot out of range or not visible anymore?
	if ((flSpotDist > 1500.0f) || (!pBot->FVisible(*pBot->GetDBGatherSpot())))
		return false;

	// Not charging an attack?
	if (!m_bCharging)
	{
		// Does the bot need to attack an enemy?
		if (pEnemy && (pBot->m_fShootDelay < gpGlobals->time))
		{
			float flDist = pBot->GetDistance(pEnemy);
			bool TryToAttack;

			if (flDist > 2500.0f) // enemy is far?
			{
				// Enemy is far away, only attack him when he looks dangerious

				TryToAttack = false;
						
				// Enemy should atleast face the bot
				if (EntIsInFieldOfView(pEnemy, pBot))
				{
					// Player is charging an attack?
					if (pEnemy->m_iChargePercent >= 33)
						TryToAttack = true;
					// Player swoops to the bot?
					else if (pEnemy->pev->movetype == MOVETYPE_PLAYERSWOOP)
						TryToAttack = true;
				}
			}
			else
				TryToAttack = true;

			if (TryToAttack && AttackEnemy())
			{
				TaskState = DONE;
				return false; // return false so we end this task
			}
		}
		else if (m_fChargeAttackCheckDelay < gpGlobals->time)
		{
			m_fChargeAttackCheckDelay = gpGlobals->time + 0.5f;

			if (SuspectEnemy())
			{
				// Bot suspects an enemy, check if the bot can charge an attack and surprise him

				// Make some kame torps?
				if ((pEdict->v.playerclass == PC_GOKU) &&
					(RANDOM_LONG(1, 100) <= pBot->m_pPersonality->kametorp_percent))
				{
					pBot->m_eTaskAfterTask = TaskKameTorp;
					return false;
				}
		
				// else check if the bot can charge an chargeble attack(beam etc)
				weapon_info_s *pAttackInfo = CheckForChargeble();
				if (pAttackInfo)
				{
					pBot->m_pCurrentWeaponInfo = pAttackInfo;
					m_bCharging = true; // Set this so the bot will charge the attack
				}
			}	
		}
	}

	pBot->ResetMoveSpeed();

	if (m_fRandomMoveTime >= gpGlobals->time)
	{
		pBot->SetMoveDir(m_iRandomMoveDir);
		if (pBot->GetCollisionDist(m_iRandomMoveDir, 75.0f) < 75.0f)
		{
			m_fRandomMoveTime = 0.0f;
			m_iRandomMoveDir = 0;
			m_fLastRandomMoveTime = gpGlobals->time + RANDOM_FLOAT(4.0f, 9.0f);
		}
	}
	else if (m_fRandomMoveTime > 0.0f)
	{
		m_fRandomMoveTime = 0.0f;
		m_iRandomMoveDir = 0;
		m_fLastRandomMoveTime = gpGlobals->time + RANDOM_FLOAT(4.0f, 9.0f);
	}
	// Time to check if the bot should randomly move a round a bit?
	else if (m_fLastRandomMoveTime < gpGlobals->time)
	{
		m_fLastRandomMoveTime = gpGlobals->time + 1.25f;
		m_iRandomMoveDir = 0;
		bool CanMoveForward = (flSpotDist < 600.0f);
		bool CanMoveBack = (flSpotDist > 500.0f);

		TMultiChoice<int> MoveDirChoices;

		float LeftCollisionDist = pBot->GetCollisionDist(dir_left, 300.0f);
		float RightCollisionDist = pBot->GetCollisionDist(dir_right, 300.0f);
		float UpCollisionDist = pBot->GetCollisionDist(dir_up, 300.0f);
		float DownCollisionDist = pBot->GetCollisionDist(dir_down, 300.0f);
		float LeftUpCollisionDist = pBot->GetCollisionDist((dir_left | dir_up), 300.0f);
		float LeftDownCollisionDist = pBot->GetCollisionDist((dir_left | dir_down), 300.0f);
		float RightUpCollisionDist = pBot->GetCollisionDist((dir_right | dir_up), 300.0f);
		float RightDownCollisionDist = pBot->GetCollisionDist((dir_right | dir_down), 300.0f);

		float BackDist = 0.0f, ForwardDist = 0.0f;

		if (CanMoveForward)
			ForwardDist = pBot->GetCollisionDist(dir_forward, 300.0f);
		if (CanMoveBack)
			BackDist = pBot->GetCollisionDist(dir_back, 300.0f);

		if (LeftCollisionDist > 100.0f)
			MoveDirChoices.Insert(dir_left, LeftCollisionDist);
		if (RightCollisionDist > 100.0f)
			MoveDirChoices.Insert(dir_right, RightCollisionDist);
		if (UpCollisionDist > 100.0f)
			MoveDirChoices.Insert(dir_up, UpCollisionDist);
		if (DownCollisionDist > 100.0f)
			MoveDirChoices.Insert(dir_down, DownCollisionDist);
		if (LeftUpCollisionDist > 100.0f)
			MoveDirChoices.Insert((dir_left | dir_up), LeftUpCollisionDist);
		if (LeftDownCollisionDist > 100.0f)
			MoveDirChoices.Insert((dir_left | dir_down), LeftDownCollisionDist);
		if (RightUpCollisionDist > 100.0f)
			MoveDirChoices.Insert((dir_right | dir_up), RightUpCollisionDist);
		if (RightDownCollisionDist > 100.0f)
			MoveDirChoices.Insert((dir_right | dir_down), RightDownCollisionDist);
		if (ForwardDist > 100.0f)
			MoveDirChoices.Insert(dir_forward, (ForwardDist * 0.8f));
		if (BackDist > 100.0f)
			MoveDirChoices.Insert(dir_back, (BackDist * 0.9f));

		if (MoveDirChoices.GetSelection(m_iRandomMoveDir))
			m_fRandomMoveTime = gpGlobals->time + RANDOM_FLOAT(1.0f, 1.75f);
	}

	// look away from the gather spot
	Vector v_dest;

	if (pEdict->v.team == GOOD_TEAM)
		v_dest = GatherDBPosForTeamGood;
	else
		v_dest = GatherDBPosForTeamEvil;

	pBot->AimToVec(v_dest);
	pBot->m_vAimVec.y = UTIL_WrapAngle(pBot->m_vAimVec.y + 180.0f);

	// To close?
	if (flSpotDist < 75.0f)
		pBot->SetMoveDir(dir_forward);
	else if (m_fRandomMoveTime < gpGlobals->time)
	{
		if (m_fRandomLookAroundTime >= gpGlobals->time)
		{
			m_fStuckCheckTime += 0.2f;
			if (m_fLastLookAroundUpdate < gpGlobals->time)
			{
				m_fLastLookAroundUpdate = gpGlobals->time + RANDOM_FLOAT(0.75f, 1.25f);

				TraceResult tr;
				Vector v_src, v_end;
				float flLongestDist = 0.0f;
				Vector v_traces[EYE_WIDTH];
				float flDistances[EYE_WIDTH];
				short Counter = 0, angle;

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

					v_end = (pEdict->v.origin + pEdict->v.view_ofs) + (gpGlobals->v_forward * 10000);

					// trace line slightly under eyes level
					UTIL_TraceHull(pEdict->v.origin + pEdict->v.view_ofs, v_end, ignore_monsters,
						           human_hull, pEdict, &tr);

					v_traces[Counter] = tr.vecEndPos;
					flDistances[Counter] = tr.flFraction * 10000.0f;

					if (flLongestDist < flDistances[Counter])
						flLongestDist = flDistances[Counter];

					Counter++;
				}

				TMultiChoice<Vector> FocusSpots;

				for(Counter=0;Counter<EYE_WIDTH;Counter++)
				{
					if (flDistances[Counter] >= (flLongestDist - 200.0f))
						FocusSpots.Insert(v_traces[Counter]);
				}

				FocusSpots.GetSelection(m_vRandomLookPos);
			}
			
			if (m_vRandomLookPos != g_vecZero)
				pBot->AimToVec(m_vRandomLookPos);
		}
		else if (m_fRandomLookAroundTime > 0.0f)
		{
			m_fRandomLookAroundTime = 0.0f;
			m_fLastRandomLookAroundTime = gpGlobals->time + RANDOM_FLOAT(0.25f, 0.75f);
		}
		else if (m_fLastRandomLookAroundTime < gpGlobals->time)
		{
			m_fRandomLookAroundTime = gpGlobals->time + RANDOM_FLOAT(2.5f, 5.0f);
		}
	}

	return true;
}

bool CDBTask::GoToDefendSpot()
{
	if ((GatherDBPosForTeamGood == g_vecZero) || (GatherDBPosForTeamEvil == g_vecZero))
		return false;

	Vector v_dest;

	if (pEdict->v.team == GOOD_TEAM)
		v_dest = GatherDBPosForTeamGood;
	else
		v_dest = GatherDBPosForTeamEvil;

	HeadToGoal(v_dest);

	if (IsStuck())
		return false;

	if (pBot->FVisible(v_dest))
	{
		pBot->AimToVec(v_dest);

		if ((pBot->m_pCurrentWaypoint) || (pBot->m_pCurrentGoalWaypoint) ||
			(pBot->m_pCurrentFloodWP) || (pBot->m_pCurrentGoalFloodWP))
			pBot->ResetWaypointVars(); // Reset

		// Close to the spot?
		if (pBot->GetDistance(v_dest) < 750.0f)
		{
			// Bot reached the defend spot
			pBot->m_eCurrentBotDBState = STATE_DEFENDGATHERSPOT;
			pBot->ToggleTurbo(false);
			pBot->m_fTurboTime = 0.0f;
		}

		return true;
	}

	// DB spot is NOT visible, see if we have a goal WP so we can reach it
	if (pBot->m_pCurrentGoalWaypoint)
	{
		return HeadToNextDBWP();
	}
	else if (pBot->m_pCurrentGoalFloodWP)
		return pBot->HeadToGoal();

	return false;
}

bool CDBTask::GoToDBSpot()
{
	if ((GatherDBPosForTeamGood == g_vecZero) || (GatherDBPosForTeamEvil == g_vecZero))
		return false;

	if (!pBot->m_iDragonball)
		return false;

	Vector v_dest;

	if (pEdict->v.team == GOOD_TEAM)
		v_dest = GatherDBPosForTeamGood;
	else
		v_dest = GatherDBPosForTeamEvil;

	if (pBot->FVisible(v_dest))
	{
		float flDist = pBot->GetDistance(v_dest);
		if ((flDist < 1000.0f) && (flDist > 175.0f))
			pBot->AimToVec(v_dest + Vector(0, 0, 65));
		else
			pBot->AimToVec(v_dest);

		if ((pBot->m_pCurrentWaypoint) || (pBot->m_pCurrentGoalWaypoint) ||
			(pBot->m_pCurrentFloodWP) || (pBot->m_pCurrentGoalFloodWP))
			pBot->ResetWaypointVars(); // Reset

		if (CheckThrowToDBSpot())
		{
			m_bMoveAway = true;
			m_fNextAwayStateUpdate = gpGlobals->time + 0.15f;
			m_eMoveAwayState = STATE_PAUSE;

			pBot->m_fDBCheckDelay = gpGlobals->time + 1.25f;
		}
		else
		{
			HeadToGoal(v_dest);

			if (IsStuck())
				return false;
		}
		return true;
	}
	else
	{
		HeadToGoal(v_dest);

		if (IsStuck())
			return false;
	}

	// DB spot is NOT visible, see if we have a goal WP so we can reach it
	if (pBot->m_pCurrentGoalWaypoint)
	{
		return HeadToNextDBWP();
	}
	else if (pBot->m_pCurrentGoalFloodWP)
		return pBot->HeadToGoal();

	return false;
}

bool CDBTask::GoToGatheredSpot()
{
	if (IsNull(pBot->m_pGoalDB))
	{
		pBot->m_pGoalDB = NULL;
		pBot->m_pGoalDB = UTIL_FindEntityByClassname(pBot->m_pGoalDB, "dragonball");
	}

	if (IsNull(pBot->m_pGoalDB))
	{
		pBot->m_pGoalDB = NULL;
		return false;
	}

	if (g_pGameRules->CanWish() == FALSE)
		return false;

	Vector v_dest = pBot->m_pGoalDB->pev->origin;

	HeadToGoal(v_dest);

	if (IsStuck())
		return false;

	if (pBot->FVisible(v_dest))
	{
		if ((pBot->m_pCurrentWaypoint) || (pBot->m_pCurrentGoalWaypoint) ||
			(pBot->m_pCurrentFloodWP) || (pBot->m_pCurrentGoalFloodWP))
			pBot->ResetWaypointVars(); // Reset

		if (UTIL_Distance(pEdict->v.origin, v_dest) < 100.0f)
		{
			if (RANDOM_LONG(1, 100) <= 50)
				pEdict->v.button |= IN_USE;
			pBot->ResetMoveSpeed();
			m_fStuckCheckTime += 0.2f;
		}
		else
			pBot->AimToVec(v_dest);

		return true;
	}

	// DB spot is NOT visible, see if we have a goal WP so we can reach it
	if (pBot->m_pCurrentGoalWaypoint)
	{
		return HeadToNextDBWP();
	}
	else if (pBot->m_pCurrentGoalFloodWP)
		return pBot->HeadToGoal();

	return false;
}

bool CDBTask::GoToDB()
{
	if (pBot->m_iDragonball)
	{
		TaskState = DONE;
		return false;
	}

	if ((UTIL_DBsAreGathered()) || (UTIL_DBsAreActive() == false))
	{
		return false; // Can't collect dragonballs
	}

	if (IsNull(pBot->m_pGoalDB))
	{
		pBot->m_pGoalDB = NULL;
		TaskState = DONE;
		return false;
	}

	Vector v_dest = pBot->m_pGoalDB->pev->origin;

	HeadToGoal(v_dest);

	if (IsStuck())
		return false;

	// Is the DB visible?
	if (pBot->FVisible(v_dest))
	{
		// Just aim at the DB when visible
		pBot->AimToVec(v_dest);

		if ((pBot->m_pCurrentWaypoint) || (pBot->m_pCurrentGoalWaypoint) ||
			(pBot->m_pCurrentFloodWP) || (pBot->m_pCurrentGoalFloodWP))
			pBot->ResetWaypointVars(); // Reset

		return true;
	}

	// DB is NOT visible see if we have a goal WP so we can reach it
	if (pBot->m_pCurrentGoalWaypoint)
	{
		return HeadToNextDBWP();
	}
	else if (pBot->m_pCurrentGoalFloodWP)
		return pBot->HeadToGoal();
	// else look for a new goal waypoint 
	else if (!pBot->SearchDragonBall())
	{
		// Can't reach the DB anymore :(
		PrintAI("DB isn't visible anymore\n");
		return false;
	}

	return true;
}

// returns true if the bot thrown the DB
bool CDBTask::CheckThrowToDBSpot()
{
	Vector v_GatherVec;

	if (pBot->pev->team == GOOD_TEAM)
		v_GatherVec = GatherDBPosForTeamGood;
	else
		v_GatherVec = GatherDBPosForTeamEvil;
	
	Vector v_disttospot = pEdict->v.origin - v_GatherVec;
	v_disttospot.z = 0;
	float DistToSpot = v_disttospot.Length();

	if (DistToSpot < 175.0f) // Is the bot close?
	{
		pBot->ResetMoveSpeed();

		if (!pBot->m_fFlying)
		{
			// Check if bot is close to another db
			CBaseEntity *pEnt = NULL;

			while ((pEnt = UTIL_FindEntityByClassname(pEnt, "dragonball")) != NULL)
			{
				if (pBot->GetDistance(pEnt) > 20.0f)
					continue;

				if (!pBot->EntIsInFOV(pEnt))
					continue;

				// ok, lets just drop the db now so the bot won't pickup this one after he thrown
				// his own db
				PrintAI("Throw db when there is a near other db\n");
				pBot->m_vAimVec.x = 0.0f; // Look straight
				pEdict->v.button |= IN_ATTACK2;
				return true;
			}
		}

		if (DistToSpot < 145.0f) // Is the bot to close?
			pBot->SetMoveDir(dir_back);
		else if ((pEdict->v.gravity > 0.1) && (pEdict->v.flags & FL_ONGROUND))
		{
			if (m_fDBThrowDelay == 0.0f)
				m_fDBThrowDelay = gpGlobals->time + 1.0f;
			else if (m_fDBThrowDelay < gpGlobals->time)
			{
				pBot->m_vAimVec.x = 0.0f; // Look straight
				pEdict->v.button |= IN_ATTACK2;
				return true;
			}
			else if (m_fDBThrowDelay >= gpGlobals->time)
			{
				pBot->m_vAimVec.x = 0.0f; // Look straight
				m_fStuckCheckTime += 0.2f;
			}
		}
		else
		{
			float HeightDiff = pEdict->v.origin.z - v_GatherVec.z;

			// Make sure we are between 40 - 75 units higher then the spot
			if (HeightDiff < 40.0f)
			{
				// go up
				pBot->SetMoveDir(dir_up);
				m_iMoveUpDownDir = dir_up;
			}
			else if (HeightDiff > 75.0f)
			{
				// go down
				pBot->SetMoveDir(dir_down);
				m_iMoveUpDownDir = dir_down;
			}
			else // else we can throw :)
			{
				if (m_fDBThrowDelay == 0.0f)
					m_fDBThrowDelay = gpGlobals->time + 1.0f;
				else if (m_fDBThrowDelay < gpGlobals->time)
				{
					pEdict->v.button |= IN_ATTACK2;
					return true;
				}
				else
					m_fStuckCheckTime += 0.2f;
			}
		}
	}
	return false;
}

bool CDBTask::HeadToNextDBWP()
{
	bool FoundWP = false;

	// Reached the goal WP?
	if (pBot->ReachedGoalWP())
	{
		PrintAI("Reached Goal Error\n");
		// If we are in this function and the bot reached his goal WP something
		// is wrong...(ie gather spot or db is still not visible)
		return false;
	}

	// check if we need to find a waypoint...
	if (pBot->CurrentWPIsValid() == false)
	{
		// find the nearest reachable waypoint
		int i = WaypointClass.GetNearestWaypoint(pBot, 1750.0f, pEdict->v.team);

		if (i != -1)
		{
			pBot->SetCurrentWaypoint(i);
			FoundWP = pBot->HeadToWaypoint();
		}
	}
	else
		FoundWP = pBot->HeadToWaypoint();

	if (!FoundWP)
		PrintAI("False FoundWP\n");
	return FoundWP;
}

void CDBTask::HeadToGoal(Vector v_target)
{
	if (pBot->m_fYawSpeed <= 25.0f)
		m_fLastTurnLessTime = gpGlobals->time;

	// Bot had to turn much for a while?
	if (m_fLastTurnLessTime < (gpGlobals->time - 1.0f))
		m_fStopMoveTime = gpGlobals->time + 0.25f; // Give the bot some time to aim

	if (m_fStopMoveTime > gpGlobals->time)
	{
		pBot->ResetMoveSpeed();
		PrintAI("Pause in HeadToGoal()\n");
		m_fStuckCheckTime = m_fStopMoveTime + 1.5f;
		return;
	}

	float flDist = pBot->GetDistance(v_target);

	// Bot doesn't swoop?
	if (pEdict->v.movetype != MOVETYPE_PLAYERSWOOP)
	{
		// Bot swooped the previous frame?
		if (m_bSwooped)
		{
			m_bSwooped = false;
			// Don't check for a swoop right now
			m_fLastSwoopCheck = max(m_fLastSwoopCheck, gpGlobals->time + 1.25f);
		}

		if (flDist > 220.0f)
		{
			if (pBot->m_pBotSkill->bUsesTurbo)
			{
				// Use turbo when not close to the goal
				pBot->ToggleTurbo(true);
				pBot->m_fTurboTime = gpGlobals->time + 0.1f;
			}
		}
		else
		{
			pBot->ToggleTurbo(false);
			pBot->m_fTurboTime = 0.0f;
		}

		AvoidPlayers(v_target);
/*
		// Time to check for a teleport?
		if (m_fLastTeleCheck < gpGlobals->time)
		{
			float flTeleCheckDelay = 1.0f;
			bool GoTele = false;

			// Has the bot enough ki AND is the bot not very close AND does the bot moves for
			// a while fairly straigh AND does the bot want to teleport at all
			if ((pBot->GetKi() > 350) && (flDist > 600.0f) && (pBot->m_fYawSpeed <= 10) &&
				(m_fLastTurnLessTime >= (gpGlobals->time - 0.2f)) &&
				(RANDOM_LONG(1, 100) <= pBot->m_pPersonality->teleport_percent))
			{
				// Is the bot not really far away from his target?
				if (pBot->GetDistance(v_target) < 2000.0f)
				{
					// Is the target visible, if so the bot doesn't have a goal WP
					if (!pBot->m_pCurrentGoalWaypoint)
						GoTele = true;
					// else check if there is no near obstacle in front of the bot
					else if (pBot->GetCollisionDist(dir_forward, 450.0f) >= 450.0f)
						GoTele = true;
				}

				// If the bot doesn't want to teleport yet...
				if (!GoTele)
				{
					// Is it a bit dangerous?
					if (pBot->m_sDangerLevel >= DANGER_MEDIUM_MIN_VAL)
						GoTele = true;
				}
			}

			m_fLastTeleCheck = gpGlobals->time + flTeleCheckDelay;
			if (GoTele)
			{
				pBot->Teleport();
				return;
			}
		}
		
		// Time to look for a swoop?
		if (m_fLastSwoopCheck < gpGlobals->time)
		{
			m_fLastSwoopCheck = gpGlobals->time + 1.0f;

			if ((pBot->GetKi() > 275) && (flDist >= 450.0f) &&
				(RANDOM_LONG(1, 100) <= pBot->m_pPersonality->swoop_percent))
			{
				bool GoSwoop = true;

				// Bot uses waypoints atm?
				if ((pBot->m_pCurrentWaypoint) && (pBot->m_pCurrentGoalWaypoint))
				{
					if ((pBot->GetDistance(pBot->m_pCurrentWaypoint->v_origin) < 450.0f) ||
						(pBot->TurnMuchForNextWP()))
					{
						// Don't swoop if the bot has to turn much for the next WP
						// Otherwise the navigation gets all messed up
						GoSwoop = false;
					}
				}
				//Bot uses flood waypoints atm?
				else if (pBot->m_pCurrentFloodWP && pBot->m_pCurrentGoalFloodWP)
				{
					if ((pBot->GetDistance(pBot->m_pCurrentFloodWP->v_origin) < 450.0f) ||
						(pBot->TurnMuchForNextWP()))
					{
						// Don't swoop if the bot has to turn much for the next WP
						// Otherwise the navigation gets all messed up
						GoSwoop = false;
					}
				}

				if (GoSwoop)
				{
					pBot->m_sSwoopFlags = SWOOP_GOTODBGOAL; // Set this so we can return to this task
					MakeCombo(COMBO_TYPE_FORWARD_SWOOP);
				}
			}
		}*/
	}
	else
	{
		// check if the bot may swoop
		if (pBot->m_Swoop.m_iSwoopDirection == FRONT)
		{
			PrintAI("Swoop in db task\n");
			// NOT close to the target?
			if (flDist > 250.0f)
			{
				pBot->m_sSwoopFlags = SWOOP_GOTODBGOAL; // Set this so we keep this task
				pBot->SetMoveDir(dir_forward);
			}
			else
			{
				// Bot shouldn't swoop
				m_fLastSwoopCheck = gpGlobals->time + 1.0f;
				//pBot->m_sSwoopFlags = 0;
				m_fStuckCheckTime += 0.2f;
				pBot->ResetMoveSpeed();
			}
		}
		else
		{
			// Bot shouldn't swoop
			m_fLastSwoopCheck = gpGlobals->time + 1.0f;
			//pBot->m_sSwoopFlags = 0;
			pBot->ResetMoveSpeed();
			m_fStuckCheckTime += 0.2f;
			m_fStopMoveTime = gpGlobals->time + 1.0f;
		}
	}
}

bool CDBTask::AttackEnemy()
{
	float flDist = pBot->GetDistance(pEnemy);

	// Attack enemy with melee?
	if ((flDist < 1500.0f) && (pBot->CanMelee(pEnemy)) &&
		(RANDOM_LONG(1, 100) <= pBot->m_pPersonality->melee_percent))
	{
		MakeCombo(COMBO_TYPE_FORWARD_SWOOP);
		return true;
	}

	// if the bot only uses melee return
	if (pBot->m_pPersonality->melee_percent == 100)
		return false;

	TMultiChoice<weapon_info_s *> AttackChoice;

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

	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 (flDist < MinDistToAttack)
		{
			SelectIndex++;
			continue;
		}
			
		float MaxDistToAttack = GetMaxDistForAttackByTableIndex(SelectIndex);

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

		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 (AttackChoice.GetSelection(pAttackSelection))
	{
		PrepareAttack(pAttackSelection, DONT_USE_COMBO);
		return true;
	}

	return false;
}

bool CDBTask::SuspectEnemy()
{
	// Bot had recently an enemy
	if (pBot->m_fHasAnEnemyTime >= (gpGlobals->time - 10.0f))
		return true;

	// Does the bot see an enemy on his scouter?
	CBasePlayer *pPlayer;
	for (int i=1;i<=gpGlobals->maxClients;i++)
	{
		pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(i);

		if (!UTIL_IsValidPlayer(pPlayer))
			continue;

		if (pBot->GetDistance(pPlayer) < 2300.0f)
			return true;
	}

	// Does the bot see an attack?
	CBaseEntity *pAttack = NULL;
	while((pAttack = UTIL_FindEntityByString(pAttack, "globalname", "magic_attack")) != NULL)
	{
		if (IsNull(pAttack))
			continue;

		float flDist = pBot->GetDistance(pAttack);
		if (flDist > 3500.0f)
			continue;
		else if ((flDist > 2000.0f) && (!pBot->EntIsInFOV(pAttack)))
			continue;

		if (pBot->FVisible(pAttack->pev->origin))
			return true;
	}

	return false;
}

weapon_info_s *CDBTask::CheckForChargeble()
{
	// if the bot only uses melee return
	if (pBot->m_pPersonality->melee_percent == 100)
		return NULL;

	TMultiChoice<weapon_info_s *> AttackChoice;

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

	while (pSelect[SelectIndex].iId != 0)
	{
		if (pSelect[SelectIndex].bIsChargeble == false)
		{
			SelectIndex++; // skip to next weapon
       		continue;
		}

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

		short AttackPercent = GetAttackPercent(NULL, &pSelect[SelectIndex]);
		if (AttackPercent == 0)
		{
			SelectIndex++;
			continue;
		}
			
		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
		}

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

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

	if (AttackChoice.GetSelection(pAttackSelection))
	{
		return pAttackSelection;
	}

	return NULL;
}

int CDBTask::GetBotStatusChecks(void)
{
	int iStatusChecks = (CheckFly | CheckKi);

	// Only check ki if bot isn't charging, otherwise the bot will powerup to soon
	if (m_bCharging)
		iStatusChecks &= ~CheckKi;
	else if (((pBot->m_iDragonball) || ((pBot->m_eBotDBType == DB_DEFENDER) && (pEnemy))) &&
			 (pBot->GetKi() >= 550))
		iStatusChecks &= ~CheckKi;

	return iStatusChecks;
}

// Avoid nearby team mates by strafing
void CDBTask::AvoidPlayers(Vector v_target)
{
	if (pBot->GetMaxSpeed() == 0.0)
		return;

	if (pBot->m_eStrafeDir == MOVE_LEFT)
		pBot->m_fStrafeSpeed = -pBot->GetMaxSpeed();
	else if (pBot->m_eStrafeDir == MOVE_RIGHT)
		pBot->m_fStrafeSpeed = pBot->GetMaxSpeed();

	if (pBot->m_fStrafeTime > gpGlobals->time)
		return;

	pBot->m_eStrafeDir = MOVE_NONE;

	pBot->m_fStrafeTime = gpGlobals->time + 0.8;

	CBasePlayer *pNearestPlayer = NULL;
	CBasePlayer *pPlayer = NULL;

	float dist, nearestdistance = 300.0, flTargetDist = pBot->GetDistance(v_target);
	Vector vecEnd;

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

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

			if (pEdict->v.team != pPlayer->pev->team)
				continue; // only avoid team mates

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

			if (dist > 300.0)
				continue; // only avoid close peeps

			if (flTargetDist < dist)
				continue;

			if (pBot->EntIsInFOV(pPlayer) == false)
				continue;

			if (dist < nearestdistance)
			{
				nearestdistance = dist;
				pNearestPlayer = pPlayer;
			}
		}
	}

	if (pNearestPlayer)
	{
		int PlayerDir = UTIL_EntDir(pBot, pPlayer->pev->origin);

		if (PlayerDir & dir_left)
			pBot->m_eStrafeDir = MOVE_RIGHT;
		else if (PlayerDir & dir_right)
			pBot->m_eStrafeDir = MOVE_LEFT;
		else
		{
			// choose a random dir
			TMultiChoice<EMoveDir> StrafeDirChoices;
			float flLeftDist = pBot->GetCollisionDist(dir_left, 175.0f);
			float flRightDist = pBot->GetCollisionDist(dir_right, 175.0f);

			if (flLeftDist >= 75.0f)
				StrafeDirChoices.Insert(MOVE_LEFT, flLeftDist);
			if (flRightDist >= 75.0f)
				StrafeDirChoices.Insert(MOVE_RIGHT, flRightDist);

			StrafeDirChoices.GetSelection(pBot->m_eStrafeDir);
		}
	}
}

bool CDBTask::UseShield()
{
	return false; // UNDONE
}

bool CDBTask::IsStuck()
{
	if (!BotManager.BotsMove())
		return false;

	if ((m_fStuckCheckTime < gpGlobals->time) &&
		(pBot->m_fWaypointHeadPauseTime < gpGlobals->time))
	{
		// Did the bot rarely moved for a while?
		if (!pBot->m_bCalculatingAStarPath && (pBot->GetDistance(m_vPrevLocation) < 50.0f))
		{
			PrintAI("Stuck in DBTask()\n");
			return true;
		}

		m_vPrevLocation = pEdict->v.origin;
		m_fStuckCheckTime = gpGlobals->time + 1.0f;
	}

	return false;
}

// ==================================================================
// Task Code - End
// ==================================================================