summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/states/cs_bot_idle.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/cstrike/bot/states/cs_bot_idle.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/cstrike/bot/states/cs_bot_idle.cpp')
-rw-r--r--game/server/cstrike/bot/states/cs_bot_idle.cpp887
1 files changed, 887 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/states/cs_bot_idle.cpp b/game/server/cstrike/bot/states/cs_bot_idle.cpp
new file mode 100644
index 0000000..64e14d2
--- /dev/null
+++ b/game/server/cstrike/bot/states/cs_bot_idle.cpp
@@ -0,0 +1,887 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 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();
+}
+