diff options
Diffstat (limited to 'game/server/cstrike/bot/states/cs_bot_idle.cpp')
| -rw-r--r-- | game/server/cstrike/bot/states/cs_bot_idle.cpp | 887 |
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(); +} + |