You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
887 lines
26 KiB
887 lines
26 KiB
//========= 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(); |
|
} |
|
|
|
|