//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003

#include "cbase.h"
#include "cs_simple_hostage.h"
#include "cs_bot.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

// range for snipers to select a hiding spot
const float sniperHideRange = 2000.0f;

//--------------------------------------------------------------------------------------------------------------
/**
 * The Idle state.
 * We never stay in the Idle state - it is a "home base" for the state machine that
 * does various checks to determine what we should do next.
 */
void IdleState::OnEnter( CCSBot *me )
{
	me->DestroyPath();
	me->SetBotEnemy( NULL );

	// lurking death
	if (me->IsUsingKnife() && me->IsWellPastSafe() && !me->IsHurrying())
		me->Walk();

	//
	// Since Idle assigns tasks, we assume that coming back to Idle means our task is complete
	//
	me->SetTask( CCSBot::SEEK_AND_DESTROY );
	me->SetDisposition( CCSBot::ENGAGE_AND_INVESTIGATE );
}

//--------------------------------------------------------------------------------------------------------------
/**
 * Determine what we should do next
 */
void IdleState::OnUpdate( CCSBot *me )
{
	// all other states assume GetLastKnownArea() is valid, ensure that it is
	if (me->GetLastKnownArea() == NULL && me->StayOnNavMesh() == false)
		return;

	// zombies never leave the Idle state
	if (cv_bot_zombie.GetBool())
	{
		me->ResetStuckMonitor();
		return;
	}

	// if we are in the early "safe" time, grab a knife or grenade
	if (me->IsSafe())
	{
		// if we have a grenade, use it
		if (!me->EquipGrenade())
		{
			// high-skill bots run with the knife, unless using the Scout (which moves faster)
			if (me->GetProfile()->GetSkill() > 0.33f && !me->IsUsing( WEAPON_SCOUT ))
			{
				me->EquipKnife();
			}
		}
	}

	// if round is over, hunt
	if (me->GetGameState()->IsRoundOver())
	{
		// if we are escorting hostages, try to get to the rescue zone
		if (me->GetHostageEscortCount())
		{
			const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( me->GetLastKnownArea(), PathCost( me, FASTEST_ROUTE ) );
			const Vector *zonePos = TheCSBots()->GetRandomPositionInZone( zone );

			if (zonePos)
			{
				me->SetTask( CCSBot::RESCUE_HOSTAGES );
				me->Run();
				me->SetDisposition( CCSBot::SELF_DEFENSE );
				me->MoveTo( *zonePos, FASTEST_ROUTE );
				me->PrintIfWatched( "Trying to rescue hostages at the end of the round\n" );
				return;
			}
		}

		me->Hunt();
		return;
	}

	const float defenseSniperCampChance = 75.0f;
	const float offenseSniperCampChance = 10.0f;

	// if we were following someone, continue following them
	if (me->IsFollowing())
	{
		me->ContinueFollowing();
		return;
	}

	//
	// Scenario logic
	//
	switch (TheCSBots()->GetScenario())
	{
		//======================================================================================================
		case CCSBotManager::SCENARIO_DEFUSE_BOMB:
		{
			// if this is a bomb game and we have the bomb, go plant it
			if (me->GetTeamNumber() == TEAM_TERRORIST)
			{
				if (me->GetGameState()->IsBombPlanted())
				{
					if (me->GetGameState()->GetPlantedBombsite() != CSGameState::UNKNOWN)
					{
						// T's always know where the bomb is - go defend it
						const CCSBotManager::Zone *zone = TheCSBots()->GetZone( me->GetGameState()->GetPlantedBombsite() );
						if (zone)
						{
							me->SetTask( CCSBot::GUARD_TICKING_BOMB );

							Place place = TheNavMesh->GetPlace( zone->m_center );
							if (place != UNDEFINED_PLACE)
							{
								// pick a random hiding spot in this place
								const Vector *spot = FindRandomHidingSpot( me, place, me->IsSniper() );
								if (spot)
								{
									me->Hide( *spot );
									return;
								}
							}

							// hide nearby
							me->Hide( TheNavMesh->GetNearestNavArea( zone->m_center ) );
							return;
						}
					}
					else
					{
						// ask our teammates where the bomb is
						me->GetChatter()->RequestBombLocation();

						// we dont know where the bomb is - we must search the bombsites
						int zoneIndex = me->GetGameState()->GetNextBombsiteToSearch();

						// move to bombsite - if we reach it, we'll update its cleared status, causing us to select another
						const Vector *pos = TheCSBots()->GetRandomPositionInZone( TheCSBots()->GetZone( zoneIndex ) );
						if (pos)
						{
							me->SetTask( CCSBot::FIND_TICKING_BOMB );
							me->MoveTo( *pos );
							return;
						}
					}
				}
				else if (me->HasC4())
				{
					// if we're at a bomb site, plant the bomb
					if (me->IsAtBombsite())
					{
						// plant it
						me->SetTask( CCSBot::PLANT_BOMB );
						me->PlantBomb();

						// radio to the team
						me->GetChatter()->PlantingTheBomb( me->GetPlace() );

						return;
					}
					else if (TheCSBots()->IsTimeToPlantBomb())
					{
						// move to the closest bomb site
						const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( me->GetLastKnownArea(), PathCost( me ) );
						if (zone)
						{
							// pick a random spot within the bomb zone
							const Vector *pos = TheCSBots()->GetRandomPositionInZone( zone );
							if (pos)
							{
								// move to bombsite
								me->SetTask( CCSBot::PLANT_BOMB );
								me->Run();
								me->MoveTo( *pos );

								return;
							}
						}
					}
				}
				else
				{
					// at the start of the round, we may decide to defend "initial encounter" areas
					// where we will first meet the enemy rush
					if (me->IsSafe())
					{
						float defendRushChance = -17.0f * (me->GetMorale() - 2);

						if (me->IsSniper() || RandomFloat( 0.0f, 100.0f ) < defendRushChance)
						{
							if (me->MoveToInitialEncounter())
							{
								me->PrintIfWatched( "I'm guarding an initial encounter area\n" );
								me->SetTask( CCSBot::GUARD_INITIAL_ENCOUNTER );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								return;
							}
						}
					}

					// small chance of sniper camping on offense, if we aren't carrying the bomb
					if (me->GetFriendsRemaining() && me->IsSniper() && RandomFloat( 0, 100.0f ) < offenseSniperCampChance)
					{
						me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
						me->Hide( me->GetLastKnownArea(), RandomFloat( 10.0f, 30.0f ), sniperHideRange );
						me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
						me->PrintIfWatched( "Sniping!\n" );
						return;
					}

					// if the bomb is loose (on the ground), go get it
					if (me->NoticeLooseBomb())
					{
						me->FetchBomb();
						return;
					}

					// if bomb has been planted, and we hear it, move to a hiding spot near the bomb and guard it
					if (!me->IsRogue() && me->GetGameState()->IsBombPlanted() && me->GetGameState()->GetBombPosition())
					{
						const Vector *bombPos = me->GetGameState()->GetBombPosition();

						if (bombPos)
						{
							me->SetTask( CCSBot::GUARD_TICKING_BOMB );
							me->Hide( TheNavMesh->GetNavArea( *bombPos ) );
							return;
						}
					}
				}
			}
			else	// CT ------------------------------------------------------------------------------------------
			{
				if (me->GetGameState()->IsBombPlanted())
				{
					// if the bomb has been planted, attempt to defuse it
					const Vector *bombPos = me->GetGameState()->GetBombPosition();
					if (bombPos)
					{
						// if someone is defusing the bomb, guard them
						if (TheCSBots()->GetBombDefuser())
						{
							if (!me->IsRogue())
							{
								me->SetTask( CCSBot::GUARD_BOMB_DEFUSER );
								me->Hide( TheNavMesh->GetNavArea( *bombPos ) );
								return;
							}
						}
						else if (me->IsDoingScenario())
						{
							// move to the bomb and defuse it
							me->SetTask( CCSBot::DEFUSE_BOMB );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							me->MoveTo( *bombPos );
							return;
						}
						else
						{
							// we're not allowed to defuse, guard the bomb zone
							me->SetTask( CCSBot::GUARD_BOMB_ZONE );
							me->Hide( TheNavMesh->GetNavArea( *bombPos ) );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
					}
					else if (me->GetGameState()->GetPlantedBombsite() != CSGameState::UNKNOWN)
					{
						// we know which bombsite, but not exactly where the bomb is, go there
						const CCSBotManager::Zone *zone = TheCSBots()->GetZone( me->GetGameState()->GetPlantedBombsite() );
						if (zone)
						{
							if (me->IsDoingScenario())
							{
								me->SetTask( CCSBot::DEFUSE_BOMB );
								me->MoveTo( zone->m_center );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								return;
							}
							else
							{
								// we're not allowed to defuse, guard the bomb zone
								me->SetTask( CCSBot::GUARD_BOMB_ZONE );
								me->Hide( TheNavMesh->GetNavArea( zone->m_center ) );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								return;
							}
						}
					}
					else
					{
						// we dont know where the bomb is - we must search the bombsites

						// find closest un-cleared bombsite
						const CCSBotManager::Zone *zone = NULL;
						float travelDistance = 9999999.9f;

						for( int z=0; z<TheCSBots()->GetZoneCount(); ++z )
						{
							if (TheCSBots()->GetZone(z)->m_areaCount == 0)
								continue;

							// don't check bombsites that have been cleared
							if (me->GetGameState()->IsBombsiteClear( z ))
								continue;

							// just use the first overlapping nav area as a reasonable approximation
							ShortestPathCost cost = ShortestPathCost();
							float dist = NavAreaTravelDistance( me->GetLastKnownArea(), 
																TheNavMesh->GetNearestNavArea( TheCSBots()->GetZone(z)->m_center ), 
																cost );

							if (dist >= 0.0f && dist < travelDistance)
							{
								zone = TheCSBots()->GetZone(z);
								travelDistance = dist;
							}
						}			
						
						
						if (zone)
						{
							const float farAwayRange = 2000.0f;
							if (travelDistance > farAwayRange)
							{
								zone = NULL;
							}
						}

						// if closest bombsite is "far away", pick one at random
						if (zone == NULL)
						{
							int zoneIndex = me->GetGameState()->GetNextBombsiteToSearch();
							zone = TheCSBots()->GetZone( zoneIndex );
						}

						// move to bombsite - if we reach it, we'll update its cleared status, causing us to select another
						if (zone)
						{
							const Vector *pos = TheCSBots()->GetRandomPositionInZone( zone );
							if (pos)
							{
								me->SetTask( CCSBot::FIND_TICKING_BOMB );
								me->MoveTo( *pos );
								return;
							}
						}
					}
					AssertMsg( 0, "A CT bot doesn't know what to do while the bomb is planted!\n" );
				}


				// if we have a sniper rifle, we like to camp, whether rogue or not
				if (me->IsSniper() && !me->IsSafe())
				{
					if (RandomFloat( 0, 100 ) <= defenseSniperCampChance)
					{
						CNavArea *snipingArea = NULL;

						// if the bomb is loose, snipe near it
						const Vector *bombPos = me->GetGameState()->GetBombPosition();
						if (me->GetGameState()->IsLooseBombLocationKnown() && bombPos)
						{
							snipingArea = TheNavMesh->GetNearestNavArea( *bombPos );
							me->PrintIfWatched( "Sniping near loose bomb\n" );
						}
						else
						{
							// snipe bomb zone(s)
							const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
							if (zone)
							{
								snipingArea = TheCSBots()->GetRandomAreaInZone( zone );
								me->PrintIfWatched( "Sniping near bombsite\n" );
							}
						}

						if (snipingArea)
						{
							me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
							me->Hide( snipingArea, -1.0, sniperHideRange );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
					}
				}

				// rogues just hunt, unless they want to snipe
				// if the whole team has decided to rush, hunt
				// if we know the bomb is dropped, hunt for enemies and the loose bomb
				if (me->IsRogue() || TheCSBots()->IsDefenseRushing() || me->GetGameState()->IsLooseBombLocationKnown())
				{
					me->Hunt();
					return;
				}

				// the lower our morale gets, the more we want to camp the bomb zone(s)
				// only decide to camp at the start of the round, or if we haven't seen anything for a long time
				if (me->IsSafe() || me->HasNotSeenEnemyForLongTime())
				{
					float guardBombsiteChance = -34.0f * me->GetMorale();

					if (RandomFloat( 0.0f, 100.0f ) < guardBombsiteChance)
					{
						float guardRange = 500.0f + 100.0f * (me->GetMorale() + 3);

						// guard bomb zone(s)
						const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
						if (zone)
						{
							CNavArea *area = TheCSBots()->GetRandomAreaInZone( zone );
							if (area)
							{
								me->PrintIfWatched( "I'm guarding a bombsite\n" );
								me->GetChatter()->GuardingBombsite( area->GetPlace() );
								me->SetTask( CCSBot::GUARD_BOMB_ZONE );
								me->Hide( area, -1.0, guardRange );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								return;
							}
						}
					}

					// at the start of the round, we may decide to defend "initial encounter" areas
					// where we will first meet the enemy rush
					if (me->IsSafe())
					{
						float defendRushChance = -17.0f * (me->GetMorale() - 2);

						if (me->IsSniper() || RandomFloat( 0.0f, 100.0f ) < defendRushChance)
						{
							if (me->MoveToInitialEncounter())
							{
								me->PrintIfWatched( "I'm guarding an initial encounter area\n" );
								me->SetTask( CCSBot::GUARD_INITIAL_ENCOUNTER );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								return;
							}
						}
					}
				}
			}

			break;
		}

		//======================================================================================================
		case CCSBotManager::SCENARIO_ESCORT_VIP:
		{
			if (me->GetTeamNumber() == TEAM_TERRORIST)
			{
				// if we have a sniper rifle, we like to camp, whether rogue or not
				if (me->IsSniper())
				{
					if (RandomFloat( 0, 100 ) <= defenseSniperCampChance)
					{
						// snipe escape zone(s)
						const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
						if (zone)
						{
							CNavArea *area = TheCSBots()->GetRandomAreaInZone( zone );
							if (area)
							{
								me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
								me->Hide( area, -1.0, sniperHideRange );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								me->PrintIfWatched( "Sniping near escape zone\n" );
								return;
							}
						}
					}
				}

				// rogues just hunt, unless they want to snipe
				// if the whole team has decided to rush, hunt
				if (me->IsRogue() || TheCSBots()->IsDefenseRushing())
					break;

				// the lower our morale gets, the more we want to camp the escape zone(s)
				float guardEscapeZoneChance = -34.0f * me->GetMorale();

				if (RandomFloat( 0.0f, 100.0f ) < guardEscapeZoneChance)
				{
					// guard escape zone(s)
					const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone();
					if (zone)
					{
						CNavArea *area = TheCSBots()->GetRandomAreaInZone( zone );
						if (area)
						{
							// guard the escape zone - stay closer if our morale is low
							me->SetTask( CCSBot::GUARD_VIP_ESCAPE_ZONE );
							me->PrintIfWatched( "I'm guarding an escape zone\n" );

							float escapeGuardRange = 750.0f + 250.0f * (me->GetMorale() + 3);
							me->Hide( area, -1.0, escapeGuardRange );

							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
					}
				}
			}
			else	// CT
			{
				if (me->m_bIsVIP)
				{
					// if early in round, pick a random zone, otherwise pick closest zone
					const float earlyTime = 20.0f;
					const CCSBotManager::Zone *zone = NULL;

					if (TheCSBots()->GetElapsedRoundTime() < earlyTime)
					{
						// pick random zone
						zone = TheCSBots()->GetRandomZone();
					}
					else
					{
						// pick closest zone
						zone = TheCSBots()->GetClosestZone( me->GetLastKnownArea(), PathCost( me ) );
					}

					if (zone)
					{
						// pick a random spot within the escape zone
						const Vector *pos = TheCSBots()->GetRandomPositionInZone( zone );
						if (pos)
						{
							// move to escape zone
							me->SetTask( CCSBot::VIP_ESCAPE );
							me->Run();
							me->MoveTo( *pos );

							// tell team to follow
							const float repeatTime = 30.0f;
							if (me->GetFriendsRemaining() && 
									TheCSBots()->GetRadioMessageInterval( RADIO_FOLLOW_ME, me->GetTeamNumber() ) > repeatTime)
								me->SendRadioMessage( RADIO_FOLLOW_ME );
							return;
						}
					}
				}
				else
				{
					// small chance of sniper camping on offense, if we aren't VIP
					if (me->GetFriendsRemaining() && me->IsSniper() && RandomFloat( 0, 100.0f ) < offenseSniperCampChance)
					{
						me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
						me->Hide( me->GetLastKnownArea(), RandomFloat( 10.0f, 30.0f ), sniperHideRange );
						me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
						me->PrintIfWatched( "Sniping!\n" );
						return;
					}
				}
			}
			break;
		}

		//======================================================================================================
		case CCSBotManager::SCENARIO_RESCUE_HOSTAGES:
		{
			if (me->GetTeamNumber() == TEAM_TERRORIST)
			{
				bool campHostages;

				// if we are in early game, camp the hostages
				if (me->IsSafe())
				{
					campHostages = true;
				}
				else if (me->GetGameState()->HaveSomeHostagesBeenTaken() || me->GetGameState()->AreAllHostagesBeingRescued())
				{
					campHostages = false;
				}
				else
				{
					// later in the game, camp either hostages or escape zone
					const float campZoneChance = 100.0f * (TheCSBots()->GetElapsedRoundTime() - me->GetSafeTime())/120.0f;

					campHostages = (RandomFloat( 0, 100 ) > campZoneChance) ? true : false; 
				}


				// if we have a sniper rifle, we like to camp, whether rogue or not
				if (me->IsSniper())
				{
					// the at start of the round, snipe the initial rush
					if (me->IsSafe())
					{
						if (me->MoveToInitialEncounter())
						{
							me->PrintIfWatched( "I'm sniping an initial encounter area\n" );
							me->SetTask( CCSBot::GUARD_INITIAL_ENCOUNTER );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
					}

					if (RandomFloat( 0, 100 ) <= defenseSniperCampChance)
					{
						const Vector *hostagePos = me->GetGameState()->GetRandomFreeHostagePosition();
						if (hostagePos && campHostages)
						{
							me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
							me->PrintIfWatched( "Sniping near hostages\n" );
							me->Hide( TheNavMesh->GetNearestNavArea( *hostagePos ), -1.0, sniperHideRange );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
						else
						{
							// camp the escape zone(s)
							if (me->GuardRandomZone( sniperHideRange ))
							{
								me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
								me->PrintIfWatched( "Sniping near a rescue zone\n" );
								me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
								return;
							}
						}
					}
				}

				// if safe time is up, and we stumble across a hostage, guard it
				if (!me->IsSafe() && !me->IsRogue())
				{
					CBaseEntity *hostage = me->GetGameState()->GetNearestVisibleFreeHostage();
					if (hostage)
					{
						// we see a free hostage, guard it
						CNavArea *area = TheNavMesh->GetNearestNavArea( GetCentroid( hostage ) );
						if (area)
						{
							me->SetTask( CCSBot::GUARD_HOSTAGES );
							me->Hide( area );
							me->PrintIfWatched( "I'm guarding hostages I found\n" );
							// don't chatter here - he'll tell us when he's in his hiding spot
							return;
						}
					}
				}


				// decide if we want to hunt, or guard
				const float huntChance = 70.0f + 25.0f * me->GetMorale();

				// rogues just hunt, unless they want to snipe
				// if the whole team has decided to rush, hunt
				if (me->GetFriendsRemaining())
				{
					if (me->IsRogue() || TheCSBots()->IsDefenseRushing() || RandomFloat( 0, 100 ) < huntChance)
					{
						me->Hunt();
						return;
					}
				}

				// at the start of the round, we may decide to defend "initial encounter" areas
				// where we will first meet the enemy rush
				if (me->IsSafe())
				{
					float defendRushChance = -17.0f * (me->GetMorale() - 2);

					if (me->IsSniper() || RandomFloat( 0.0f, 100.0f ) < defendRushChance)
					{
						if (me->MoveToInitialEncounter())
						{
							me->PrintIfWatched( "I'm guarding an initial encounter area\n" );
							me->SetTask( CCSBot::GUARD_INITIAL_ENCOUNTER );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
					}
				}


				// decide whether to camp the hostages or the escape zones
				const Vector *hostagePos = me->GetGameState()->GetRandomFreeHostagePosition();
				if (hostagePos && campHostages)
				{
					CNavArea *area = TheNavMesh->GetNearestNavArea( *hostagePos );
					if (area)
					{
						// guard the hostages - stay closer to hostages if our morale is low
						me->SetTask( CCSBot::GUARD_HOSTAGES );
						me->PrintIfWatched( "I'm guarding hostages\n" );

						float hostageGuardRange = 750.0f + 250.0f * (me->GetMorale() + 3);			// 2000
						me->Hide( area, -1.0, hostageGuardRange );
						me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );

						if (RandomFloat( 0, 100 ) < 50)
							me->GetChatter()->GuardingHostages( area->GetPlace(), IS_PLAN );

						return;
					}
				}

				// guard rescue zone(s)
				if (me->GuardRandomZone())
				{
					me->SetTask( CCSBot::GUARD_HOSTAGE_RESCUE_ZONE );
					me->PrintIfWatched( "I'm guarding a rescue zone\n" );
					me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
					me->GetChatter()->GuardingHostageEscapeZone( IS_PLAN );
					return;
				}
			}
			else	// CT ---------------------------------------------------------------------------------
			{
				// only decide to do something else if we aren't already rescuing hostages
				if (!me->GetHostageEscortCount())
				{
					// small chance of sniper camping on offense
					if (me->GetFriendsRemaining() && me->IsSniper() && RandomFloat( 0, 100.0f ) < offenseSniperCampChance)
					{
						me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
						me->Hide( me->GetLastKnownArea(), RandomFloat( 10.0f, 30.0f ), sniperHideRange );
						me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
						me->PrintIfWatched( "Sniping!\n" );
						return;
					}

					if (me->GetFriendsRemaining() && !me->GetHostageEscortCount())
					{
						// rogues just hunt, unless all friends are dead
						// if we have friends left, we might go hunting instead of hostage rescuing
						const float huntChance = 33.3f;
						if (me->IsRogue() || RandomFloat( 0.0f, 100.0f ) < huntChance)
						{
							me->Hunt();
							return;
						}
					}
				}

				// at the start of the round, we may decide to defend "initial encounter" areas
				// where we will first meet the enemy rush
				if (me->IsSafe())
				{
					float defendRushChance = -17.0f * (me->GetMorale() - 2);

					if (me->IsSniper() || RandomFloat( 0.0f, 100.0f ) < defendRushChance)
					{
						if (me->MoveToInitialEncounter())
						{
							me->PrintIfWatched( "I'm guarding an initial encounter area\n" );
							me->SetTask( CCSBot::GUARD_INITIAL_ENCOUNTER );
							me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
							return;
						}
					}
				}

				// look for free hostages - CT's have radar so they know where hostages are at all times
				CHostage *hostage = me->GetGameState()->GetNearestFreeHostage();

				// if we are not allowed to do the scenario, guard the hostages to clear the area for the human(s)
				if (!me->IsDoingScenario())
				{
					if (hostage)
					{		
						CNavArea *area = TheNavMesh->GetNearestNavArea( GetCentroid( hostage ) );
						if (area)
						{
							me->SetTask( CCSBot::GUARD_HOSTAGES );
							me->Hide( area );
							me->PrintIfWatched( "I'm securing the hostages for a human to rescue\n" );
							return;
						}
					}

					me->Hunt();
					return;
				}


				bool fetchHostages = false;
				bool rescueHostages = false;
				const CCSBotManager::Zone *zone = NULL;
				me->SetGoalEntity( NULL );

				// if we are escorting hostages, determine where to take them
				if (me->GetHostageEscortCount())
					zone = TheCSBots()->GetClosestZone( me->GetLastKnownArea(), PathCost( me, FASTEST_ROUTE ) );

				// if we are escorting hostages and there are more hostages to rescue, 
				// determine whether it's faster to rescue the ones we have, or go get the remaining ones
				if (hostage)
				{
					Vector hostageOrigin = GetCentroid( hostage );

					if (zone)
					{
						PathCost cost( me, FASTEST_ROUTE );
						float toZone = NavAreaTravelDistance( me->GetLastKnownArea(), zone->m_area[0], cost  );
						float toHostage = NavAreaTravelDistance( me->GetLastKnownArea(), TheNavMesh->GetNearestNavArea( GetCentroid( hostage ) ), cost );

						if (toHostage < 0.0f)
						{
							rescueHostages = true;
						}
						else
						{
							if (toZone < toHostage)
								rescueHostages = true;
							else
								fetchHostages = true;
						}
					}
					else
					{
						fetchHostages = true;
					}
				}
				else if (zone)
				{
					rescueHostages = true;
				}


				if (fetchHostages)
				{
					// go get hostages
					me->SetTask( CCSBot::COLLECT_HOSTAGES );
					me->Run();
					me->SetGoalEntity( hostage );
					me->ResetWaitForHostagePatience();

					// if we already have some hostages, move to the others by the quickest route
					RouteType route = (me->GetHostageEscortCount()) ? FASTEST_ROUTE : SAFEST_ROUTE;
					me->MoveTo( GetCentroid( hostage ), route );

					me->PrintIfWatched( "I'm collecting hostages\n" );
					return;
				}

				const Vector *zonePos = TheCSBots()->GetRandomPositionInZone( zone );
				if (rescueHostages && zonePos)
				{
					me->SetTask( CCSBot::RESCUE_HOSTAGES );
					me->Run();
					me->SetDisposition( CCSBot::SELF_DEFENSE );
					me->MoveTo( *zonePos, FASTEST_ROUTE );
					me->PrintIfWatched( "I'm rescuing hostages\n" );
					me->GetChatter()->EscortingHostages();
					return;
				}
			}
			break;
		}

		default:	// deathmatch
		{
			// sniping check
			if (me->GetFriendsRemaining() && me->IsSniper() && RandomFloat( 0, 100.0f ) < offenseSniperCampChance)
			{
				me->SetTask( CCSBot::MOVE_TO_SNIPER_SPOT );
				me->Hide( me->GetLastKnownArea(), RandomFloat( 10.0f, 30.0f ), sniperHideRange );
				me->SetDisposition( CCSBot::OPPORTUNITY_FIRE );
				me->PrintIfWatched( "Sniping!\n" );
				return;
			}
			break;
		}
	}

	// if we have nothing special to do, go hunting for enemies
	me->Hunt();
}