#include "gen_bot_header.h"

//harSens: Linux compile fix
extern CLeftOutput lcout;

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

void CBot::ResetMoveSpeed()
{
	m_fMoveSpeed = m_fStrafeSpeed = m_fUpDownSpeed = 0.0f;
	pev->button &= ~IN_FORWARD;
	pev->button &= ~IN_BACK;
	pev->button &= ~IN_MOVELEFT;
	pev->button &= ~IN_MOVERIGHT;
	pev->button &= ~IN_JUMP;
	pev->button &= ~IN_DUCK;
}

void CBot::SetMoveDir(int MoveDir)
{
	if (MoveDir & dir_left)
	{
		m_fStrafeSpeed = -GetMaxSpeed();
		pev->button |= IN_MOVELEFT;
	}
	if (MoveDir & dir_right)
	{
		m_fStrafeSpeed = GetMaxSpeed();
		pev->button |= IN_MOVERIGHT;
	}
	if (MoveDir & dir_forward)
	{
		m_fMoveSpeed = GetMaxSpeed();
		pev->button |= IN_FORWARD;
	}
	if (MoveDir & dir_back)
	{
		m_fMoveSpeed = -GetMaxSpeed();
		pev->button |= IN_BACK;
	}
	if (MoveDir & dir_up)
	{
		m_fUpDownSpeed = GetMaxSpeed();
		pev->button |= IN_JUMP;
	}
	if (MoveDir & dir_down)
	{
		m_fUpDownSpeed = -GetMaxSpeed();
		pev->button |= IN_DUCK;
	}
}

void CBot::BotAim()
{
    if (m_fAimDelay > gpGlobals->time)
	   return;

	Vector v_src = pev->origin;
	Vector v_aim = WrapAngles(m_vAimVec);

    // Paulo-La-Frite - START bot aiming bug fix
    if (v_aim.y > 180)
      v_aim.y -=360;

    if (v_aim.x > 180)
      v_aim.x -=360;

	// Paulo-La-Frite - END

	if (m_sSBotType > 1)
	{
		pev->v_angle = WrapAngles(v_aim); // Mega and Ultimate bots have instant aim :)
	}
	else
	{
		float MaxXSpeed = RANDOM_FLOAT(m_pBotSkill->flMinAimXSpeed, m_pBotSkill->flMaxAimXSpeed);
		float MaxYSpeed = RANDOM_FLOAT(m_pBotSkill->flMinAimYSpeed, m_pBotSkill->flMaxAimYSpeed);
		float XOffset = RANDOM_FLOAT(m_pBotSkill->flMinAimXOffset, m_pBotSkill->flMaxAimXOffset);
		float YOffset = RANDOM_FLOAT(m_pBotSkill->flMinAimYOffset, m_pBotSkill->flMaxAimYOffset);
		float RealXOffset, RealYOffset;
		float AimXSpeed = MaxXSpeed, AimYSpeed = MaxYSpeed;
		float XDiff = fabs(v_aim.x - -pev->v_angle.x);
		float YDiff = fabs(v_aim.y - pev->v_angle.y);

		// How higher the diff, how higher the offsets and aim speed

		if (XOffset)
		{
			if (RANDOM_LONG(0, 1))
				RealXOffset = XDiff * (XOffset / 100.0f);
			else
				RealXOffset = -(XDiff * (XOffset / 100.0f));
		}
		else
			RealXOffset = 0.0f;

		if (YOffset)
		{
			if (RANDOM_LONG(0, 1))
				RealYOffset = YDiff * (YOffset / 100.0f);
			else
				RealYOffset = -(YDiff * (YOffset / 100.0f));
		}
		else
			RealYOffset = 0.0f;

		if (XDiff >= 1.0f)
			AimXSpeed *= (XDiff / 70.0f);
		else
			AimXSpeed *= 0.01f;

		if (YDiff >= 1.0f)
			AimYSpeed *= (YDiff / 60.0f);
		else
			AimYSpeed *= 0.015f;

		v_aim.x = UTIL_WrapAngle(v_aim.x + RealXOffset);
		v_aim.y = UTIL_WrapAngle(v_aim.y + RealYOffset);

		m_fPrevPitchSpeed = m_fPitchSpeed;
		m_fPrevYawSpeed = m_fYawSpeed;
		m_fPitchSpeed = BotChangePitch(AimXSpeed, v_aim.x);
		m_fYawSpeed = BotChangeYaw(AimYSpeed, v_aim.y);
	}

	// set the body angles to aim correctly
	pev->angles.x = pev->v_angle.x / 3;
	pev->angles.y = pev->v_angle.y;
	pev->angles.z = 0;

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

// returns the number of degrees left to turn toward ideal pitch...
float CBot::BotChangePitch(float speed, float ideal )
{
    float current;
    float current_180;  // current +/- 180 degrees
    float diff;

    // turn from the current v_angle pitch to the idealpitch by selecting
    // the quickest way to turn to face that direction
   
    current = -pev->v_angle.x;

    // find the difference in the current and ideal angle
    diff = fabs(current - ideal);

    speed = speed * BotManager.m_fFrameTime;  // angles per second

    // check if difference is less than the max degrees per turn
    if (diff < speed)
		speed = diff;  // just need to turn a little bit (less than max)

    // here we have four cases, both angle positive, one positive and
    // the other negative, one negative and the other positive, or
    // both negative.  handle each case separately...

    if ((current >= 0) && (ideal >= 0))  // both positive
    {
		if (current > ideal)
			current -= speed;
		else
			current += speed;
    }
    else if ((current >= 0) && (ideal < 0))
    {
		current_180 = current - 180;

        if (current_180 > ideal)
			current += speed;
		else
			current -= speed;
    }
    else if ((current < 0) && (ideal >= 0))
    {
		current_180 = current + 180;
		if (current_180 > ideal)
			current += speed;
		else
			current -= speed;
    }
    else  // (current < 0) && (ideal < 0)  both negative
    {
		if (current > ideal)
			current -= speed;
		else
			current += speed;
    }

    // check for wrap around of angle...
    if (current > 180)
		current -= 360;
    if (current < -180)
		current += 360;

    pev->v_angle.x = current;

    return diff;  // return number of degrees left to turn
}

// returns the number of degrees left to turn toward ideal yaw...
float CBot::BotChangeYaw(float speed, float ideal)
{
	float current;
	float current_180;  // current +/- 180 degrees
	float diff;

	// turn from the current v_angle yaw to the ideal_yaw by selecting
	// the quickest way to turn to face that direction

	current = pev->v_angle.y;

	// find the difference in the current and ideal angle
	diff = fabs(current - ideal);

	// speed that we can turn during this frame...
	speed = speed * BotManager.m_fFrameTime;

	// check if difference is less than the max degrees per turn
	if (diff < speed)
		speed = diff;  // just need to turn a little bit (less than max)

	// here we have four cases, both angle positive, one positive and
	// the other negative, one negative and the other positive, or
	// both negative.  handle each case separately...

	if ((current >= 0) && (ideal >= 0))  // both positive
	{
		if (current > ideal)
			current -= speed;
		else
			current += speed;
    }
    else if ((current >= 0) && (ideal < 0))
    {
		current_180 = current - 180;

		if (current_180 > ideal)
			current += speed;
		else
			current -= speed;
    }
    else if ((current < 0) && (ideal >= 0))
    {
		current_180 = current + 180;
		
		if (current_180 > ideal)
			current += speed;
		else
			current -= speed;
	}
    else  // (current < 0) && (ideal < 0)  both negative
    {
		if (current > ideal)
			current -= speed;
		else
			current += speed;
	}

    // check for wrap around of angle...
    if (current > 180)
		current -= 360;
	if (current < -180)
		current += 360;

    pev->v_angle.y = current;

    return diff;  // return number of degrees left to turn
}

void CBot::CheckStrafe(bool CheckWall)
{
	if (GetMaxSpeed() == 0.0)
		return;

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

	if (m_fStrafeTime > gpGlobals->time)
		return;

	m_eStrafeDir = MOVE_NONE;

	m_fStrafeTime = gpGlobals->time + 0.8;

	if (CheckWall)
	{
		bool wall_left = false;
		bool wall_right = false;

		wall_left = (GetCollisionDist(dir_left, 70.0) < 70.0f);
		wall_right = (GetCollisionDist(dir_right, 70.0) < 70.0f);

		if ((wall_left) && (!wall_right))
		{
			// Strafe right if there is a wall on the left side but not on the right side.
			m_eStrafeDir = MOVE_RIGHT;
			m_fStrafeTime = gpGlobals->time + RANDOM_FLOAT(0.3, 0.7);
			return;
		}
		else if ((!wall_left) && (wall_right))
		{
			// Strafe left if there is a wall on the right side but not on the left side.
			m_eStrafeDir = MOVE_LEFT;
			m_fStrafeTime = gpGlobals->time + RANDOM_FLOAT(0.3, 0.7);
			return;
		}
	}

	if (!g_teamplay)
		return; // Dont continue if no teamplay

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

	float dist, nearestdistance = 300.0;
	Vector vecEnd;

	if (!m_bMadePlayerList)
		MakePlayerList();

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

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

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

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

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

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

		if (PlayerDir & dir_left)
			m_eStrafeDir = MOVE_RIGHT;
		else if (PlayerDir & dir_right)
			m_eStrafeDir = MOVE_LEFT;
		else
		{
			// choose a random dir
			if (RANDOM_LONG(0, 1))
				m_eStrafeDir = MOVE_LEFT; // Strafe left
			else
				m_eStrafeDir = MOVE_RIGHT; // Strafe right
		}

		m_fStrafeTime = gpGlobals->time + RANDOM_FLOAT(0.3, 0.7);
	}
}

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

// copied from botman's source (HPB_bot 2.11)
bool CBot::FindWaypoint()
{
	int index, select_index;
    short Team = (g_teamplay) ? pev->team : -1;
    waypoint_path_s *pPath = NULL;
    int path_index;
    float distance, min_distance[3];
    int min_index[3];

    for (index=0; index < 3; index++)
    {
		min_distance[index] = 9999.0;
        min_index[index] = -1;
    }

    index = WaypointClass.FindPath(&pPath, &path_index, m_sCurrentWPIndex, Team);

    while (index != -1)
    {
		// if index is not a current or recent previous waypoint...
		if ((index != m_sCurrentWPIndex) &&
            (index != m_iPrevWaypointIndexes[0]) &&
			(index != m_iPrevWaypointIndexes[1]) &&
			(index != m_iPrevWaypointIndexes[2]) &&
			(index != m_iPrevWaypointIndexes[3]) &&
			(index != m_iPrevWaypointIndexes[4]))
		{
			// find the distance from the bot to this waypoint
			distance = GetDistance(WaypointClass.m_Waypoints[index].Waypoint.v_origin);

			if (distance < min_distance[0])
			{
				min_distance[2] = min_distance[1];
				min_index[2] = min_index[1];

				min_distance[1] = min_distance[0];
				min_index[1] = min_index[0];

				min_distance[0] = distance;
				min_index[0] = index;
			}
			else if (distance < min_distance [1])
			{
				min_distance[2] = min_distance[1];
				min_index[2] = min_index[1];

				min_distance[1] = distance;
				min_index[1] = index;
			}
			else if (distance < min_distance[2])
			{
				min_distance[2] = distance;
				min_index[2] = index;
			}
		}

		// find the next path to a waypoint
		index = WaypointClass.FindPath(&pPath, &path_index, m_sCurrentWPIndex, Team);
    }

	select_index = -1;

    // about 20% of the time choose a waypoint at random
    // (don't do this any more often than every 10 seconds)

    if ((RANDOM_LONG(1, 100) <= 20) &&
		(m_fRandomWaypointTime <= gpGlobals->time))
    {
		m_fRandomWaypointTime = gpGlobals->time + 10.0;

		if (min_index[2] != -1)
			index = RANDOM_LONG(0, 2);
		else if (min_index[1] != -1)
			index = RANDOM_LONG(0, 1);
		else if (min_index[0] != -1)
			index = 0;
		else
			return false;  // no waypoints found!

		select_index = min_index[index];
    }
    else
    {
		// use the closest waypoint that has been recently used
		select_index = min_index[0];
    }

    if (select_index != -1)  // was a waypoint found?
    {
		m_iPrevWaypointIndexes[4] = m_iPrevWaypointIndexes[3];
		m_iPrevWaypointIndexes[3] = m_iPrevWaypointIndexes[2];
		m_iPrevWaypointIndexes[2] = m_iPrevWaypointIndexes[1];
		m_iPrevWaypointIndexes[1] = m_iPrevWaypointIndexes[0];
		m_iPrevWaypointIndexes[0] = m_sCurrentWPIndex;

		SetCurrentWaypoint(select_index);
		return true;
    }

    return false;  // couldn't find a waypoint
}

bool CBot::HeadToWaypoint()
{
	if ((m_sCurrentWPIndex == -1) || (!m_pCurrentWaypoint))
		return false; // Can't head to waypoint

	short Team = (g_teamplay) ? pev->team : -1;
	bool Touching = false;
	float WPDist = GetDistance(m_pCurrentWaypoint->v_origin);

    // did the bot run past the waypoint? (prevent the loop-the-loop problem)
    if ((m_fPrevWaypointDistance > 1.0) && (WPDist > m_fPrevWaypointDistance))
		Touching = true;

	// are we close enough to a target waypoint...
    if (WPDist < 75.0f)
		Touching = true;

    // save current distance as previous
    m_fPrevWaypointDistance = WPDist;

	// Reached the waypoint?
    if (Touching)
    {
		m_fPrevWaypointDistance = 0.0;

		// Does the bot have a goal?
		if (m_pCurrentGoalWaypoint)
		{
			// get the next waypoint to reach goal...
			int i = WaypointClass.RouteFromTo(m_sCurrentWPIndex, m_sCurrentGoalWPIndex, Team);

			if (i != WAYPOINT_UNREACHABLE)  // can we get to the goal from here?
			{
				SetCurrentWaypoint(i);
			}
			else
				return false;
		}
		else // else just find a random near wp
        {
			byte index = 4;
			bool status;

			// try to find the next waypoint
			while (((status = FindWaypoint()) == false) && (index > 0))
			{
				// if waypoint not found, clear oldest previous index and try again

				m_iPrevWaypointIndexes[index] = -1;
				index--;
			}

			if (status == false)
			{
				ResetWaypointVars();
				return false;
			}
		}
	}

    // keep turning towards the waypoint...
	AimToVec(m_pCurrentWaypoint->v_origin);

	if (m_fYawSpeed <= 25.0f)
		m_fWaypointHeadLastTurnLessTime = gpGlobals->time;

	// Bot had to turn much for a while?
	if ((m_fWaypointHeadLastTurnLessTime > 0.0f) &&
		(m_fWaypointHeadLastTurnLessTime < (gpGlobals->time - 1.0f)))
	{
		m_fWaypointHeadPauseTime = gpGlobals->time + 0.2f;
		m_fWaypointTime += 0.2f;
	}

	if (m_fWaypointHeadPauseTime >= gpGlobals->time)
	{
		ResetMoveSpeed();
		PrintAI("Pause in HeadToWaypoint()\n");
	}

	return true;
}

bool CBot::ReachedGoalWP()
{
	if ((m_sCurrentWPIndex == -1) || (m_sCurrentGoalWPIndex == -1) ||
		(!m_pCurrentWaypoint) || (!m_pCurrentGoalWaypoint))
		return false;

	if (m_sCurrentWPIndex != m_sCurrentGoalWPIndex)
		return false;

	return ((GetDistance(m_pCurrentGoalWaypoint->v_origin) < 50.0f) &&
			(FVisible(m_pCurrentGoalWaypoint->v_origin)));
}

bool CBot::ReachedGoalFloodWP()
{
	if (!m_pCurrentGoalFloodWP || !m_pCurrentFloodWP)
		return false;

	if (m_pCurrentFloodWP != m_pCurrentGoalFloodWP)
		return false;

	return ((GetDistance(m_pCurrentGoalFloodWP->v_origin) < 70.0f) &&
			(FVisible(m_pCurrentGoalFloodWP->v_origin)));
}

bool CBot::CurrentWPIsValid()
{
	if ((m_sCurrentWPIndex == -1) || (!m_pCurrentWaypoint))
		return false;

    // check if the bot has been trying to get to this waypoint for a while...
    if ((m_fWaypointTime + 5.0) < gpGlobals->time)
    {
		return false;
    }

	return (FVisible(m_pCurrentWaypoint->v_origin) == TRUE);
}

bool CBot::CurrentFloodWPIsValid()
{
	if (!m_pCurrentFloodWP)
		return false;

    // check if the bot has been trying to get to this waypoint for a while...
    if (((m_fWaypointTime + 7.5f) < gpGlobals->time) && BotManager.BotsMove())
    {
		PrintAI("Flood WP is out of time\n");
		return false;
    }

	return (FVisible(m_pCurrentFloodWP->v_origin) == TRUE);
}

void CBot::ResetWaypointVars()
{
	m_fWaypointTime = 0.0;
	m_pCurrentWaypoint = NULL;
	m_pCurrentGoalWaypoint = NULL;
	m_sCurrentWPIndex = m_sCurrentGoalWPIndex = -1;
	m_iPrevWaypointIndexes[0] = -1;
	m_iPrevWaypointIndexes[1] = -1;
	m_iPrevWaypointIndexes[2] = -1;
	m_iPrevWaypointIndexes[3] = -1;
	m_iPrevWaypointIndexes[4] = -1;
	m_fPrevWaypointDistance = 0.0;
	m_fWaypointHeadLastTurnLessTime = 0.0f;
	m_fWaypointHeadPauseTime = 0.0f;
	// Flood stuff
	m_pCurrentFloodWP = NULL;
	m_pCurrentGoalFloodWP = NULL;
	m_AStarNodeList.DeleteAllNodes();
	m_bDeletingAStarLists = true;
	m_bCalculatingAStarPath = false;
}

void CBot::SetCurrentWaypoint(short sIndex)
{
	m_sCurrentWPIndex = sIndex;
	m_pCurrentWaypoint = &WaypointClass.m_Waypoints[sIndex].Waypoint;
	m_fWaypointTime = gpGlobals->time;
}

void CBot::SetGoalWaypoint(short sIndex)
{
	m_sCurrentGoalWPIndex = sIndex;
	m_pCurrentGoalWaypoint = &WaypointClass.m_Waypoints[sIndex].Waypoint;
}
	
bool CBot::TurnMuchForNextWP()
{
	Vector v_nextangle;
	Vector v_current;

	if ((m_sCurrentWPIndex != -1) && (m_sCurrentGoalWPIndex != -1) &&
		(m_pCurrentWaypoint) && (m_pCurrentGoalWaypoint))
	{
		v_current = m_pCurrentWaypoint->v_origin;
		int Team = (g_teamplay) ? pev->team : -1;

		// First figure out what the next waypoint is...
		int iWPInd = WaypointClass.RouteFromTo(m_sCurrentWPIndex, m_sCurrentGoalWPIndex, Team);

		if (iWPInd == WAYPOINT_UNREACHABLE)
			return false; // There is no next waypoint :(

		// Now figure out how much the bot has to turn
		Vector v_next = WaypointClass.m_Waypoints[iWPInd].Waypoint.v_origin;
		v_nextangle = WrapAngles(UTIL_VecToAngles(v_next - v_current));
	}
	else if ((m_pCurrentFloodWP) && (m_pCurrentGoalFloodWP))
	{
		return false;/* // UNDONE
		v_current = m_pCurrentFloodWP->v_origin;
		// First figure out what the next waypoint is...
		int iWPInd = GetNextBestFloodWP(m_iCurrentFloodWPIndex, m_iCurrentGoalFloodWPIndex);

		if (iWPInd == -1)
			return false; // There is no next waypoint :(

		// Now figure out how much the bot has to turn
		Vector v_next = WaypointFlooder.m_pFloodWPs[iWPInd].v_origin;
		v_nextangle = WrapAngles(UTIL_VecToAngles(v_next - v_current));*/
	}
	else
		return false;

	float fCurrentX = pev->v_angle.x;
	float fCurrentY = pev->v_angle.y;
	float fNextX = -v_nextangle.x;
	float fNextY = v_nextangle.y;
	float XDiff = fabs(fCurrentX - fNextX);
	float YDiff = fabs(fCurrentY - fNextY);

	return ((XDiff >= 25) || (YDiff >= 25));
}

void CBot::BotCombatMovement()
{
	if (!m_pBotEnemy)
		return;

	if ((GetMaxSpeed() == 0.0f) || (BotManager.BotsMove() == false))
		return;

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

	if ((m_bSkillNr > 2) && (!EntIsInFOV(m_pBotEnemy)))
	{
		ResetMoveSpeed();
		m_sCombatNavDir = 0;
		return;
	}

	Vector AimVec, v_src, v_dest;
	float TraceDist[3][5];
	float nearestdistance = 0;
	float distance = 0;
	int i, j;
	float x=0,y=0;
	TraceResult tr;
	int ind_i = 0, ind_j = 0;
	bool CheckUp = (!m_fCharging); // Only move up when not charging, otherwise beam 'jumps up' a bit
	
	if (m_fCombatNavDelay < gpGlobals->time)
	{
		bool go_backwards = (dist < RANDOM_FLOAT(1300.0, 1600.0));
		v_dest = EyePosition();

		for (i=0;i<3;i++)
		{
			if (i == 0)
				x = 0; // trace straight forward
			else if (i == 1)
			{
				if (CheckUp)
					x = -30; // trace 30 degrees up
				else
					continue;
			}
			else
				x = 30;

			for (j=0;j<5;j++)
			{
				switch (j)
				{
				case 0:
					y=-90;
					break;
				case 1:
					y=-45;
					break;
				case 2:
					y=0;
					break;
				case 3:
					y=45;
					break;
				case 4:
					y=90;
					break;
				default:
					break;
				}

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

				if (go_backwards)
					v_dest = v_dest - gpGlobals->v_forward * 10000;
				else
					v_dest = v_dest + gpGlobals->v_forward * 10000;

			    // trace line slightly under eyes level
		        UTIL_TraceHull (pev->origin + pev->view_ofs,v_dest,
			                    dont_ignore_monsters, human_hull, pev->pContainingEntity, &tr);

				TraceDist[i][j] = (pev->origin - tr.vecEndPos).Length();
				distance = (pev->origin - tr.vecEndPos).Length();
				if (distance > nearestdistance)
				{
					nearestdistance = distance;
					AimVec = tr.vecEndPos;
					ind_i = i;
					ind_j =	j;
				}
			}
		}

		m_sCombatNavDir = 0;
		m_fCombatNavDelay = gpGlobals->time + RANDOM_FLOAT(0.8, 1.2);

		if (ind_i == 1)
			m_sCombatNavDir |= dir_up;
		else if (ind_i == 2)
			m_sCombatNavDir |= dir_down;

		switch (ind_j)
		{
		case 0:
			m_sCombatNavDir |= dir_left;
			break;
		case 1:
			m_sCombatNavDir |= dir_left;

			if (go_backwards)
				m_sCombatNavDir |= dir_back;
			else
				m_sCombatNavDir |= dir_forward;

			break;
		case 2:
			if (go_backwards)
				m_sCombatNavDir |= dir_back;
			else
				m_sCombatNavDir |= dir_forward;

			break;
		case 3:
			m_sCombatNavDir |= dir_right;

			if (go_backwards)
				m_sCombatNavDir |= dir_back;
			else
				m_sCombatNavDir |= dir_forward;

			break;
		case 4:
			m_sCombatNavDir |= dir_right;
			break;
		}
	}

	ResetMoveSpeed();
	SetMoveDir(m_sCombatNavDir);

	CheckStrafe();
}

void CBot::SetSwoopButton()
{
	if (pev->movetype != MOVETYPE_PLAYERSWOOP)
		return; // Shouldn't happen

	switch (m_Swoop.m_iSwoopDirection)
	{
	case UP:
		SetMoveDir(dir_up);
		break;
	case DOWN:
		SetMoveDir(dir_down);
		break;
	case LEFT:
		SetMoveDir(dir_left);
		break;
	case RIGHT:
		SetMoveDir(dir_right);
		break;
	case BACK:
		SetMoveDir(dir_back);
		break;
	default:
	case FRONT:
		SetMoveDir(dir_forward);
		break;
	}
}

void CBot::GoToAnPreviousLocation()
{
	for (int i=(MAX_OLD_POSITIONS-1);i>=0;i--)
	{
		if ((m_OldPositions[i].flCreateTime > (gpGlobals->time - MAX_OLD_POSITION_LIFE_TIME)) &&
			(FVisible(m_OldPositions[i].Pos)))
		{
			int vecDir = UTIL_EntDir(this, m_OldPositions[i].Pos);

			ResetMoveSpeed();
			SetMoveDir(vecDir);
		}
	}
}

bool CBot::HeadToGoal()
{
	if (m_bCalculatingAStarPath)
	{
		// Bot is calculating a new path, just stand still for now
		ResetMoveSpeed();
		m_fWaypointTime += 0.2f;

		// Done calculating the path?
		if (AStar())
		{
			m_bCalculatingAStarPath = false;
			if (m_AStarNodeList.Empty())
			{
				m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalFloodWP, gpGlobals->time));
				return false; // Couldn't find a path
			}
			//else
			//	SetCurrentFloodWP(m_AStarNodeList.Pop());
		}
		else
			return true; // else just wait a little bit longer
	}

	if (!m_pCurrentFloodWP)
	{
		m_pCurrentFloodWP = WaypointFlooder.GetNearestFloodWP(pev->origin, 750.0f);
		if (!m_pCurrentFloodWP)
			return false;
		SetCurrentFloodWP(m_pCurrentFloodWP);
	}

	if (!CurrentFloodWPIsValid())
	{
		if (m_pCurrentFloodWP == m_pCurrentGoalFloodWP)
			m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentFloodWP, gpGlobals->time));
		PrintAI("Current WP is InValid\n");
		return false;
	}

	bool Touching = false;
	float WPDist = GetDistance(m_pCurrentFloodWP->v_origin);

    // did the bot run past the waypoint? (prevent the loop-the-loop problem)
    if ((m_fPrevWaypointDistance > 1.0) && (WPDist > m_fPrevWaypointDistance) &&
		(WPDist > 25.0f))
		Touching = true;

	// are we close enough to a target waypoint...
    if (WPDist < 75.0f)
	{
		if (m_pCurrentFloodWP->Flags & FL_TELEPORT)
		{
			// Bot should be very close to a teleport WP, that way he will more lickely trigger it
			if (WPDist <= 5)
				Touching = true;
		}
		else
		{
			// check if the bot already can see his next WP
			flood_wp_s *pNextFloodWP = GetNextFloodWP();
			if (!pNextFloodWP || (FVisible(pNextFloodWP->v_origin)))
				Touching = true;
		}
	}

    // save current distance as previous
    m_fPrevWaypointDistance = WPDist;

	// Reached the waypoint?
    if (Touching)
    {
		m_fPrevWaypointDistance = 0.0;

		if (m_pCurrentFloodWP != m_pCurrentGoalFloodWP)
		{
			if (m_AStarNodeList.Empty())
			{
				if (!AStar())
				{
					// Bot is calculating a new path, just stand still for now
					ResetMoveSpeed();
					m_fWaypointTime += 0.2f;
					return true;
				}
				else
				{
					if (m_AStarNodeList.Empty())
					{
						m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalFloodWP, gpGlobals->time));
						return false; // Couldn't get a new wp to go to
					}
				}
			}

			m_pCurrentFloodWP = m_AStarNodeList.Pop();

			if ((!FVisible(m_pCurrentFloodWP->v_origin)) &&
				(!(m_pCurrentFloodWP->Flags & FL_TELEPORT)))
			{
				m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalFloodWP, gpGlobals->time));
				return false;
			}

			/*
			{
				m_pCurrentFloodWP = WaypointFlooder.GetNearestFloodWP(pev->origin, 750.0f);
				if (!m_pCurrentFloodWP)
					return false;

				if (!AStar())
				{
					if (m_AStarNodeList.Empty())
					{
						m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalFloodWP, gpGlobals->time));
						return false; // Couldn't get a new wp to go to
					}
					else
					{
						// Bot is calculating a new path, just stand still for now
						ResetMoveSpeed();
						m_fWaypointTime += 0.2f;
						return true;
					}
				}

				m_pCurrentFloodWP = m_AStarNodeList.Pop();
			}*/
		
			flood_wp_s *pNextFloodWP = GetNextFloodWP();
			int Counter = 2;
			while(pNextFloodWP && Counter)
			{
				if (FVisible(pNextFloodWP->v_origin))
					m_pCurrentFloodWP = m_AStarNodeList.Pop();
				else
					break;

				Counter--;
				pNextFloodWP = GetNextFloodWP();
			}
		
			SetCurrentFloodWP(m_pCurrentFloodWP);
		}
		else
		{
			// Bot reached the goal waypoint but couldn't reach the goal itself
			// (otherwise we wouldn't be in this function)
			m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalFloodWP, gpGlobals->time));
			return false;
		}

/*
		float flNearestDist = 9999.9, flDist;
		int iWPIndex = m_iCurrentFloodWPIndex, iBestWPIndex = -1, i;
		flood_wp_s *pFloodWP;

		for (i=0;i<3;i++)
		{
			iWPIndex = GetNextBestFloodWP(iWPIndex, m_iCurrentGoalFloodWPIndex);
			if (iWPIndex == -1)
				break;

			iBestWPIndex = iWPIndex;
			break;

			pFloodWP = &WaypointFlooder.m_pFloodWPs[iWPIndex]; // Shortcut
			flDist = UTIL_Distance(pFloodWP->v_origin, m_pCurrentGoalFloodWP->v_origin);

			if ((flDist < flNearestDist) && (FVisible(pFloodWP->v_origin)))
			{
				flNearestDist = flDist;
				iBestWPIndex = iWPIndex;
			}
		}

		if ((iBestWPIndex != -1) && (iBestWPIndex != m_iCurrentFloodWPIndex))
		{
			m_fWaypointTime = gpGlobals->time;

			for (i=3;i>=1;i--)
				m_iPrevFloodWPIndexes[i] = m_iPrevFloodWPIndexes[i-1];

			m_iPrevFloodWPIndexes[0] = m_iCurrentFloodWPIndex;
			m_iCurrentFloodWPIndex = iBestWPIndex;
			m_pCurrentFloodWP = &WaypointFlooder.m_pFloodWPs[m_iCurrentFloodWPIndex];
		}
		else
			return false;*/
	}

    // keep turning towards the waypoint...
	AimToVec(m_pCurrentFloodWP->v_origin);

	if (m_fYawSpeed <= 25.0f)
		m_fWaypointHeadLastTurnLessTime = gpGlobals->time;

	// Bot had to turn much for a while?
	if ((m_fWaypointHeadLastTurnLessTime > 0.0f) &&
		(m_fWaypointHeadLastTurnLessTime < (gpGlobals->time - 1.0f)))
	{
		m_fWaypointHeadPauseTime = gpGlobals->time + 0.2f;
		m_fWaypointTime += 0.2f;
	}

	if (m_fWaypointHeadPauseTime >= gpGlobals->time)
	{
		ResetMoveSpeed();
		PrintAI("Pause in HeadToGoal()\n");
	}

	return true;
}

void CBot::SetCurrentFloodWP(flood_wp_s *pFloodWP)
{
	m_pCurrentFloodWP = pFloodWP;
	m_fWaypointTime = gpGlobals->time;
}

void CBot::SetGoalFloodWP(flood_wp_s *pFloodWP)
{
	m_pCurrentGoalFloodWP = pFloodWP;
}

// return true when done calculating
bool CBot::AStar()
{
	if (m_bDeletingAStarLists)
	{
		// Busy with deleting A* lists, don't do any A* stuff now
		return true;
	}

	if (!m_pCurrentGoalFloodWP || !m_pCurrentFloodWP)
	{
		m_bCalculatingAStarPath = false;
		m_bDeletingAStarLists = true;
		return true;
	}

	// Ideas by PMB :
	// * Make locals static to speed up a bit
	// * MaxCycles per frame and make it fps dependent

	static int iMaxCycles;
	static int iCurrentCycles;
	static short s;
	static float newg;
	static flood_wp_s *n, *n2;
	static int BotBit;

	BotBit = (1<<(entindex() - 1));

	iMaxCycles = 10 * BotManager.GetMSec();
	iCurrentCycles = 0;

	if (!m_bCalculatingAStarPath)
	{
		m_bDeletingAStarLists = false;

		m_pCurrentFloodWP->g = 0.0f;
		m_pCurrentFloodWP->f = m_pCurrentFloodWP->g + GetDistance(m_pCurrentGoalFloodWP->v_origin);
		m_pCurrentFloodWP->pParent = NULL;
		m_pCurrentFloodWP->iOpenIndexes |= BotBit;
		m_AStarOpenList.AddEntry(m_pCurrentFloodWP, m_pCurrentFloodWP->f);

		m_pCurrentGoalFloodWP->g = 0.0f;
		m_pCurrentGoalFloodWP->f = m_pCurrentGoalFloodWP->g + GetDistance(m_pCurrentFloodWP->v_origin);
		m_pCurrentGoalFloodWP->pGoalParent = NULL;
		m_pCurrentGoalFloodWP->iGoalOpenIndexes |= BotBit;
		m_AStarGoalOpenList.AddEntry(m_pCurrentGoalFloodWP, m_pCurrentGoalFloodWP->f);

		m_AStarNodeList.DeleteAllNodes();
	}

	while(!m_AStarOpenList.Empty() && !m_AStarGoalOpenList.Empty())
	{
		if (iCurrentCycles >= iMaxCycles)
		{
			m_bCalculatingAStarPath = true;
			return false;
		}

		n = m_AStarOpenList.Pop();
		n->iOpenIndexes &= ~BotBit;

		if (n == m_pCurrentGoalFloodWP)
		{
			while(n)
			{
				m_AStarNodeList.PushNode(n);
				n = n->pParent;
			}

			//m_AStarOpenList.Clear();
			//m_AStarClosedList.DeleteAllNodes();

			m_bDeletingAStarLists = true;
			m_bCalculatingAStarPath = false;
			return true;
		}

		for(s=0;s<6;s++)
		{
			if (n->pConnectedWPs[s] == NULL)
				continue;

			n2 = n->pConnectedWPs[s];

			if (n2->iGoalClosedIndexes & BotBit)
			{
				while(n)
				{
					m_AStarNodeList.PushNode(n);
					n = n->pParent;
				}

				while(n2)
				{
					m_AStarNodeList.AddNode(n2);
					n2 = n2->pGoalParent;
				}

				m_bDeletingAStarLists = true;
				m_bCalculatingAStarPath = false;
				return true;
			}

			newg = n->g + 1;//AStarCost(n, n2);

			if ((n2->g <= newg) && ((n2->iOpenIndexes & BotBit) || (n2->iClosedIndexes & BotBit)))
//			if ((n2->g <= newg) && ((m_AStarClosedList.IsInList(n2) || m_AStarOpenList.IsInList(n2, n2->f))))
				continue;

			n2->pParent = n;
			n2->g = newg;
			n2->f = n2->g + UTIL_Distance(n2->v_origin, m_pCurrentGoalFloodWP->v_origin);

			n2->iClosedIndexes &= ~BotBit;
			//m_AStarClosedList.DeleteEntry(n2);

			//if (!m_AStarOpenList.IsInList(n2, n2->f))
			if (!(n2->iOpenIndexes & BotBit))
			{
				m_AStarOpenList.AddEntry(n2, n2->f);
				n2->iOpenIndexes |= BotBit;
			}
		}

		m_AStarClosedList.PushNode(n);
		n->iClosedIndexes |= BotBit;

		n = m_AStarGoalOpenList.Pop();
		n->iGoalOpenIndexes &= ~BotBit;

		if (n == m_pCurrentFloodWP)
		{
			while(n)
			{
				m_AStarNodeList.AddNode(n);
				n = n->pGoalParent;
			}

			//m_AStarOpenList.Clear();
			//m_AStarClosedList.DeleteAllNodes();

			m_bDeletingAStarLists = true;
			m_bCalculatingAStarPath = false;
			return true;
		}

		for(s=0;s<6;s++)
		{
			if (n->pConnectedWPs[s] == NULL)
				continue;

			n2 = n->pConnectedWPs[s];

			newg = n->g + 1;//AStarCost(n, n2);

			if ((n2->g <= newg) && ((n2->iGoalOpenIndexes & BotBit) || (n2->iGoalClosedIndexes & BotBit)))
//			if ((n2->g <= newg) && ((m_AStarClosedList.IsInList(n2) || m_AStarOpenList.IsInList(n2, n2->f))))
				continue;

			n2->pGoalParent = n;
			n2->g = newg;
			n2->f = n2->g + UTIL_Distance(n2->v_origin, m_pCurrentFloodWP->v_origin);

			n2->iGoalClosedIndexes &= ~BotBit;
			//m_AStarClosedList.DeleteEntry(n2);

			//if (!m_AStarOpenList.IsInList(n2, n2->f))
			if (!(n2->iGoalOpenIndexes & BotBit))
			{
				m_AStarGoalOpenList.AddEntry(n2, n2->f);
				n2->iGoalOpenIndexes |= BotBit;
			}
		}

		m_AStarGoalClosedList.PushNode(n);
		n->iGoalClosedIndexes |= BotBit;
		
		iCurrentCycles++;
	}

	m_bDeletingAStarLists = true;
	m_bCalculatingAStarPath = false;
	return true;
}

float CBot::AStarCost(flood_wp_s *pWP1, flood_wp_s *pWP2)
{
	float flCost = 1.0f;

	// Is there a player near this WP and is he near the bot?
	CBasePlayer *pPlayer;
	float flBotToPlayerDist, flNodeToPlayerDist;
	for(byte b=1;b<=gpGlobals->maxClients;b++)
	{
		pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(b);

		if (pPlayer == this)
			continue;

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

		flBotToPlayerDist = GetDistance(pPlayer);
		flNodeToPlayerDist = UTIL_Distance(pPlayer->pev->origin, pWP2->v_origin);

		// Is this player near the new node and is the player fairly close to the bot
		if ((flNodeToPlayerDist < 200.0f) && (flBotToPlayerDist < 1250.0f))
		{
			if (pPlayer->FVisible(pWP2->v_origin))
				flCost += ((1250.0f - flBotToPlayerDist) * 0.01f) + ((200.0f - flNodeToPlayerDist) * 0.01f);
		}
	}

	return flCost;
}

bool CBot::InvalidGoalFloodWP(flood_wp_s *pGoal)
{
	/*modified by harSens
	TLinkedList<unreachable_node_s>::node_s *pNode = m_UnreachableNodes.GetFirst(), *pPrev;
	*/
	node_s<unreachable_node_s> *pNode = m_UnreachableNodes.GetFirst(), *pPrev;

	while(pNode)
	{
		if (pGoal == pNode->Entry.pNode)
		{
			if ((gpGlobals->time - pNode->Entry.flUnreachableTime) <= 15.0f)
				return true;
			else
			{
				m_UnreachableNodes.DeleteNode(pNode);
				return false;
			}
		}

		pPrev = pNode;
		pNode = pNode->next;

		if ((gpGlobals->time - pPrev->Entry.flUnreachableTime) > 15.0f)
			m_UnreachableNodes.DeleteNode(pPrev);
	}

	return false;
}

flood_wp_s *CBot::GetNextFloodWP()
{
	if (m_AStarNodeList.Empty() == false)
		return m_AStarNodeList.GetFirst()->Entry;

	return NULL;
}

//=========================================================
// FVisible - returns true if a line can be traced from
// the caller's eyes to the target
//=========================================================
BOOL CBot :: FVisible ( CBaseEntity *pEntity )
{
	static TraceResult tr;
	static Vector vecLookerOrigin;
	static Vector vecTargetOrigin;
	
	vecLookerOrigin = pev->origin + pev->view_ofs;//look through the caller's 'eyes'
	vecTargetOrigin = pEntity->EyePosition();

	UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ENT(pev)/*pentIgnore*/, &tr);

	return (tr.flFraction >= 1.0f);
}

//=========================================================
// FVisible - returns true if a line can be traced from
// the caller's eyes to the target vector
//=========================================================
BOOL CBot :: FVisible ( const Vector &vecOrigin )
{
	static TraceResult tr;

	UTIL_TraceLine(EyePosition(), vecOrigin, ignore_monsters, ENT(pev)/*pentIgnore*/, &tr);
	
	return (tr.flFraction >= 1.0f);
}

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

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

void CNormalMovementTask::TaskThink()
{
	pEdict->v.v_angle.z = 0;  // reset roll to 0 (straight up and down)

   //pEdict->v.angles.x = 0;
    pEdict->v.angles.y = pEdict->v.v_angle.y;
    pEdict->v.angles.z = 0;

    bool found_waypoint = FALSE;

    // If the bot can't move.... return
    if ((pBot->GetMaxSpeed() == 0.0f) || (BotManager.BotsMove() == false))
		return;

    // if it is time to look for a waypoint AND the bot is flying AND
    // if there are waypoints in this level...
    if ((pBot->m_fLookForWaypointTime <= gpGlobals->time) && (pBot->m_fFlying) &&
        (WaypointClass.m_iWaypointCount >= 1))
    {
		// check if we need to find a waypoint...
		if (pBot->CurrentWPIsValid() == false)
		{
			// find the nearest reachable waypoint
			int i = WaypointClass.GetNearestWaypoint(pBot, 400.0f, (g_teamplay) ? pEdict->v.team : -1);

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

    // if the bot isn't heading toward a waypoint...
    // then do the unwaypointed navagation :-)
    if (found_waypoint == false)
    {
	    // Time to pause?
		if (pBot->m_fPauseCheckTime < gpGlobals->time)
		{
			pBot->m_eTaskAfterTask = TaskLookAround;
			TaskState = DONE;
			return;
		}

        if (pBot->m_fCheckUpDownTime <= gpGlobals->time)
			BotCheckUpDown();

		pBot->CheckStrafe();

		// check if we should be avoiding walls
		if (pBot->m_fDontAvoidWallTime <= gpGlobals->time)
		{
           if (pBot->m_fLookDelay < gpGlobals->time)
		   {
			   BotLook();    // first do our magical and much cpu intensive look...
			   CheckFOV();    // then look what we do with the results...
			   pBot->m_fLookDelay = gpGlobals->time + RANDOM_FLOAT(0.8, 1.2);
		   }

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

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

	TraceResult tr;

	Vector v_src = pEdict->v.origin;

	float angle;

    float distance = 0;
    float nearestdistance = 0;

    int count = 0;
    int DesiredAngleIndex = -1;

    // scan 90 degrees of bot's field of view...
	for (angle =-45; angle <=45; angle+=5) //-70 -- +70
	{
         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);

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

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

         // save our results...
         pBot->m_eye[count].distance = distance * 10000;
         pBot->m_eye[count].contents = UTIL_PointContents( tr.vecEndPos );
         pBot->m_eye[count].endVec = tr.vecEndPos;
		 pBot->m_eye[count].vecPlaneNormal = tr.vecPlaneNormal;

         // now take the most logical direction:
         // take the endVec with the largest distance
         if (pBot->m_eye[count].distance > nearestdistance)
         {
             DesiredAngleIndex = count;
             nearestdistance = pBot->m_eye[count].distance;
         }

         count++;
   }

   if (DesiredAngleIndex >= 0)
   { 
	   pBot->AimToVec(pBot->m_eye[DesiredAngleIndex].endVec);
   }

   return;
}

void CNormalMovementTask::CheckFOV()
{
   if (pBot->m_fDontAvoidWallTime > gpGlobals->time)
	   return;

   // first check if bot is in a corner....
   if ((pBot->m_eye[0].distance < 70) && (pBot->m_eye[EYE_WIDTH-1].distance < 70) &&
	   (pBot->m_eye[(EYE_WIDTH+1)/2].distance < 70)) // he is..
   {
	   TraceResult tr;
	   Vector v_src, v_end;

	   // check if we can jump up if when we're not flying and not under water...
	   if ((pEdict->v.gravity > 0.1) && (pEdict->v.waterlevel != 3) &&
		   (pBot->m_eye[(EYE_WIDTH+1)/2].distance < 45.0) && (!(pEdict->v.flags & FL_ONGROUND)))
	   {
		   UTIL_MakeVectors(pEdict->v.v_angle);

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

		   UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull, 
					  pEdict->v.pContainingEntity, &tr);

	       if (tr.flFraction >= 1.0)
		   {
			   pEdict->v.button |= IN_JUMP;
			   return;
		   }
	   }

       pBot->TurnAround();
	   
	   pBot->m_fDontAvoidWallTime = gpGlobals->time + 1.0;

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

   // then check if he can't move forward...
   if (pBot->m_eye[(EYE_WIDTH+1)/2].distance < 70) // something is blocking on eye height
   {
	   TraceResult tr;
	   Vector v_src, v_end;

	   // check if the bot can jump when he's not flying and the bot is not in water
	   if ((pEdict->v.gravity > 0.1) && (pEdict->v.waterlevel != 3) &&
		   (!(pEdict->v.flags & FL_ONGROUND)))
	   {
		   UTIL_MakeVectors(pEdict->v.v_angle);

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

		   UTIL_TraceHull(v_src, v_end, ignore_monsters, human_hull, 
					  pEdict->v.pContainingEntity, &tr);

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

	   BotTurnAtWall(&pBot->m_eye[(EYE_WIDTH+1)/2].vecPlaneNormal);

       pBot->m_fDontAvoidWallTime = gpGlobals->time + 1.0;
	   pBot->m_fCheckKameTorpTime += 1.5;
	   return;
   }

   return;
}

void CNormalMovementTask::CheckSkyBox()
{
   if (pBot->m_fDontAvoidWallTime > gpGlobals->time)
	   return;

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

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

   return;
}

void CNormalMovementTask::BotCheckUpDown()
{
	if (!pBot->m_fFlying)
	{
		// Loop up so the bot will walk on slopes aswell(otherwise the bot thinks the slope is a
		// wall
		pBot->m_vAimVec.x = 40.0;
		return;
	}

	bool direction_up = false;
	bool direction_down = false;

	pBot->m_fCheckUpDownTime = gpGlobals->time + 3.0;

	direction_up = (pBot->GetCollisionDist((dir_up | dir_forward), 100.0) < 100.0f);
	direction_down = (pBot->GetCollisionDist((dir_down | dir_forward), 100.0) < 100.0f);

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

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

    Normal = UTIL_VecToAngles(*vecPlaneNormal);

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

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

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

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

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

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

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

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

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

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

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

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

    if (Z > 180)
		Z -= 360;

    // set the direction to head off into...
    pBot->m_vAimVec.y = UTIL_WrapAngle(Z);
}

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