// Called when enemy seen to adjust safe time for this round void CCSBot::AdjustSafeTime() { // if we spotted an enemy sooner than we thought possible, adjust our notion of "safe" time if (m_safeTime > TheCSBots()->GetElapsedRoundTime()) { // since right now is not safe, adjust safe time to be a few seconds ago m_safeTime = TheCSBots()->GetElapsedRoundTime() - 2.0f; } }
bool CHostageImprov::CanSeeRescueZone() const { if (TheCSBots() == NULL) return false; const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(&GetCentroid()); if (zone != NULL) return IsVisible(zone->m_center); return false; }
// Return true if can see the bomb lying on the ground bool CCSBot::CanSeeLooseBomb() const { if (TheCSBots()->GetScenario() != CCSBotManager::SCENARIO_DEFUSE_BOMB) return false; CBaseEntity *bomb = TheCSBots()->GetLooseBomb(); if (bomb) { if (IsVisible(&bomb->pev->origin, CHECK_FOV)) return true; } return false; }
// Return true if we noticed the bomb on the ground or on the radar (for T's only) bool CCSBot::NoticeLooseBomb() const { if (TheCSBots()->GetScenario() != CCSBotManager::SCENARIO_DEFUSE_BOMB) return false; CBaseEntity *bomb = TheCSBots()->GetLooseBomb(); if (bomb) { // T's can always see bomb on their radar return true; } return false; }
// Pick a random zone and hide near it bool CCSBot::GuardRandomZone(float range) { const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone(); if (zone) { CNavArea *rescueArea = TheCSBots()->GetRandomAreaInZone(zone); if (rescueArea) { Hide(rescueArea, -1.0f, range); return true; } } return false; }
// Return true if it is the early, "safe", part of the round bool CCSBot::IsSafe() const { if (TheCSBots()->GetElapsedRoundTime() < m_safeTime) return true; return false; }
// Return true if it is well past the early, "safe", part of the round bool CCSBot::IsWellPastSafe() const { if (TheCSBots()->GetElapsedRoundTime() > 1.25f * m_safeTime) return true; return false; }
void CC4::Drop( const Vector &vecVelocity ) { #if !defined( CLIENT_DLL ) m_bStartedArming = false; // stop arming sequence if ( !CSGameRules()->m_bBombPlanted ) // its not dropped if its planted { // tell the bots about the dropped bomb TheCSBots()->SetLooseBomb( this ); CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(GetOwnerEntity()); Assert( pPlayer ); if ( pPlayer ) { IGameEvent * event = gameeventmanager->CreateEvent("bomb_dropped" ); if ( event ) { event->SetInt( "userid", pPlayer->GetUserID() ); event->SetInt( "priority", 6 ); gameeventmanager->FireEvent( event ); } } } #endif BaseClass::Drop( vecVelocity ); }
// Return true if we are in a hurry bool CCSBot::IsHurrying() const { if (!m_hurryTimer.IsElapsed()) return true; // if the bomb has been planted, we are in a hurry, CT or T (they could be defusing it!) if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB && TheCSBots()->IsBombPlanted()) return true; // if we are a T and hostages are being rescued, we are in a hurry if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_RESCUE_HOSTAGES && m_iTeam == TERRORIST && GetGameState()->AreAllHostagesBeingRescued()) return true; return false; }
bool CCSBot::IsWellPastSafe() const { CCSBotManager *ctrl = TheCSBots(); if (ctrl->GetElapsedRoundTime() > 1.25f * m_safeTime) return true; return false; }
/** * Defuse the bomb */ void DefuseBombState::OnUpdate( CCSBot *me ) { const Vector *bombPos = me->GetGameState()->GetBombPosition(); if (bombPos == NULL) { me->PrintIfWatched( "In Defuse state, but don't know where the bomb is!\n" ); me->Idle(); return; } // look at the bomb me->SetLookAt( "Defuse bomb", *bombPos, PRIORITY_HIGH ); // defuse... me->UseEnvironment(); if (gpGlobals->curtime - me->GetStateTimestamp() > 1.0f) { // if we missed starting the defuse, give up if (TheCSBots()->GetBombDefuser() == NULL) { me->PrintIfWatched( "Failed to start defuse, giving up\n" ); me->Idle(); return; } else if (TheCSBots()->GetBombDefuser() != me) { // if someone else got the defuse, give up me->PrintIfWatched( "Someone else started defusing, giving up\n" ); me->Idle(); return; } } // if bomb has been defused, give up if (!TheCSBots()->IsBombPlanted()) { me->Idle(); return; } }
CBasePlayer *CCSBot::GetImportantEnemy(bool checkVisibility) const { CCSBotManager *ctrl = TheCSBots(); CBasePlayer *nearEnemy = NULL; float nearDist = 999999999.9f; for (int i = 1; i <= gpGlobals->maxClients; ++i) { CBaseEntity *entity = UTIL_PlayerByIndex(i); if (entity == NULL) continue; if (FNullEnt(entity->pev)) continue; if (FStrEq(STRING(entity->pev->netname), "")) continue; // is it a player? if (!entity->IsPlayer()) continue; CBasePlayer *player = static_cast<CBasePlayer *>(entity); // is it alive? if (!player->IsAlive()) continue; // skip friends if (player->m_iTeam == m_iTeam) continue; // is it "important" if (!ctrl->IsImportantPlayer(player)) continue; // is it closest? Vector d = pev->origin - player->pev->origin; float distSq = d.x * d.x + d.y * d.y + d.z * d.z; if (distSq < nearDist) { if (checkVisibility && !IsVisible(player, CHECK_FOV)) continue; nearEnemy = player; nearDist = distSq; } } return nearEnemy; }
void HostageEscapeState::__MAKE_VHOOK(OnEnter)(CHostageImprov *improv) { const CCSBotManager::Zone *zone = TheCSBots()->GetRandomZone(); if (zone != NULL) { m_toCoverState.SetRescueGoal(zone->m_center); m_behavior.Reset(improv); m_behavior.SetState(&m_toCoverState); } m_canEscape = true; }
// Return true if can see the planted bomb bool CCSBot::CanSeePlantedBomb() const { if (TheCSBots()->GetScenario() != CCSBotManager::SCENARIO_DEFUSE_BOMB) return false; if (!GetGameState()->IsBombPlanted()) return false; const Vector *bombPos = GetGameState()->GetBombPosition(); if (bombPos && IsVisible(bombPos, CHECK_FOV)) return true; return false; }
// Return the closest "important" enemy for the given scenario (bomb carrier, VIP, hostage escorter) CBasePlayer *CCSBot::GetImportantEnemy(bool checkVisibility) const { CBasePlayer *nearEnemy = nullptr; float nearDist = 999999999.9f; for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); if (!pPlayer) continue; if (FNullEnt(pPlayer->pev)) continue; if (FStrEq(STRING(pPlayer->pev->netname), "")) continue; // is it a player? if (!pPlayer->IsPlayer()) continue; // is it alive? if (!pPlayer->IsAlive()) continue; // skip friends if (BotRelationship(pPlayer) == BOT_TEAMMATE) continue; // is it "important" if (!TheCSBots()->IsImportantPlayer(pPlayer)) continue; // is it closest? Vector d = pev->origin - pPlayer->pev->origin; float distSq = d.x * d.x + d.y * d.y + d.z * d.z; if (distSq < nearDist) { if (checkVisibility && !IsVisible(pPlayer, CHECK_FOV)) continue; nearEnemy = pPlayer; nearDist = distSq; } } return nearEnemy; }
/* <3fd284> ../cstrike/dlls/bot/cs_gamestate.cpp:225 */ bool CSGameState::IsAtPlantedBombsite(void) const { if (m_bombState != PLANTED) return false; CCSBotManager *ctrl = TheCSBots(); const CCSBotManager::Zone *zone = ctrl->GetClosestZone(&m_owner->pev->origin); if (zone != NULL) { return (m_plantedBombsite == zone->m_index); } return false; }
void CCSBot::SpawnBot() { CCSBotManager *ctrl = TheCSBots(); ctrl->ValidateMapData(); ResetValues(); Q_strcpy(m_name, STRING(pev->netname)); SetState(&m_buyState); SetTouch(&CCSBot::BotTouch); if (!TheNavAreaList.Count () && !ctrl->IsLearningMap()) { ctrl->SetLearningMapFlag(); StartLearnProcess(); } }
// Return true if we are acting like a rogue (not listening to teammates, not doing scenario goals) // TODO: Account for morale bool CCSBot::IsRogue() const { if (!TheCSBots()->AllowRogues()) return false; // periodically re-evaluate our rogue status if (m_rogueTimer.IsElapsed()) { m_rogueTimer.Start(RANDOM_FLOAT(10, 30)); // our chance of going rogue is inversely proportional to our teamwork attribute const float rogueChance = 100.0f * (1.0f - GetProfile()->GetTeamwork()); m_isRogue = (RANDOM_FLOAT(0, 100) < rogueChance); } return m_isRogue; }
/* <3fd373> ../cstrike/dlls/bot/cs_gamestate.cpp:313 */ void CSGameState::UpdatePlantedBomb(const Vector *pos) { CCSBotManager *ctrl = TheCSBots(); const CCSBotManager::Zone *zone = ctrl->GetClosestZone(pos); if (zone == NULL) { CONSOLE_ECHO("ERROR: Bomb planted outside of a zone!\n"); m_plantedBombsite = UNKNOWN; } else { m_plantedBombsite = zone->m_index; } m_plantedBombPos = *pos; m_isPlantedBombPosKnown = true; SetBombState(PLANTED); }
// Send a radio message void CCSBot::SendRadioMessage(GameEventType event) { // make sure this is a radio event if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) { return; } PrintIfWatched("%3.1f: SendRadioMessage( %s )\n", gpGlobals->time, GameEventName[event]); // note the time the message was sent TheCSBots()->SetRadioMessageTimestamp(event, m_iTeam); m_lastRadioSentTimestamp = gpGlobals->time; char slot[2]; slot[1] = '\0'; if (event > EVENT_START_RADIO_1 && event < EVENT_START_RADIO_2) { slot[0] = event - EVENT_START_RADIO_1; ClientCommand("radio1"); //Radio1(this, event - EVENT_START_RADIO_3); } else if (event > EVENT_START_RADIO_2 && event < EVENT_START_RADIO_3) { slot[0] = event - EVENT_START_RADIO_2; ClientCommand("radio2"); //Radio2(this, event - EVENT_START_RADIO_3); } else { slot[0] = event - EVENT_START_RADIO_3; ClientCommand("radio3"); //Radio3(this, event - EVENT_START_RADIO_3); } ClientCommand("menuselect", slot); ClientCommand("menuselect", "10"); }
// Move to the bomb on the floor and pick it up void FetchBombState::__MAKE_VHOOK(OnUpdate)(CCSBot *me) { if (me->IsCarryingBomb()) { me->PrintIfWatched( "I picked up the bomb\n" ); me->Idle(); return; } CBaseEntity *bomb = TheCSBots()->GetLooseBomb(); if (bomb != NULL) { if (!me->HasPath()) { // build a path to the bomb if (me->ComputePath(TheNavAreaGrid.GetNavArea(&bomb->pev->origin), &bomb->pev->origin, SAFEST_ROUTE) == false) { me->PrintIfWatched("Fetch bomb pathfind failed\n"); // go Hunt instead of Idle to prevent continuous re-pathing to inaccessible bomb me->Hunt(); //return; } } } else { // someone picked up the bomb me->PrintIfWatched("Bomb not loose\n"); me->Idle(); return; } // look around me->UpdateLookAround(); if (me->UpdatePathMovement() != CCSBot::PROGRESSING) me->Idle(); }
BOOL CGameRules::CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) { // only living players can have items if (pPlayer->pev->deadflag != DEAD_NO) { return FALSE; } CCSBotManager *ctrl = TheCSBots(); if (pPlayer->IsBot() && ctrl != NULL && !ctrl->IsWeaponUseable(pWeapon)) { return FALSE; } if (pWeapon->pszAmmo1()) { if (!CanHaveAmmo(pPlayer, pWeapon->pszAmmo1(), pWeapon->iMaxAmmo1())) { // we can't carry anymore ammo for this gun. We can only // have the gun if we aren't already carrying one of this type if (pPlayer->HasPlayerItem(pWeapon)) { return FALSE; } } } else { // weapon doesn't use ammo, don't take another if you already have it. if (pPlayer->HasPlayerItem(pWeapon)) { return FALSE; } } // note: will fall through to here if GetItemInfo doesn't fill the struct! return TRUE; }
/* <3fd4f4> ../cstrike/dlls/bot/cs_gamestate.cpp:55 */ void CSGameState::Reset(void) { int i; CCSBotManager *ctrl = TheCSBots(); m_isRoundOver = false; // bomb m_bombState = MOVING; m_lastSawBomber.Invalidate(); m_lastSawLooseBomb.Invalidate(); m_bombsiteCount = ctrl->GetZoneCount(); m_isPlantedBombPosKnown = false; m_plantedBombsite = UNKNOWN; for (i = 0; i < m_bombsiteCount; ++i) { m_isBombsiteClear[i] = false; m_bombsiteSearchOrder[i] = i; } // shuffle the bombsite search order // allows T's to plant at random site, and TEAM_CT's to search in a random order // NOTE: VS6 std::random_shuffle() doesn't work well with an array of two elements (most maps) for (i = 0; i < m_bombsiteCount; ++i) { int swap = m_bombsiteSearchOrder[i]; int rnd = RANDOM_LONG(i, m_bombsiteCount - 1); m_bombsiteSearchOrder[i] = m_bombsiteSearchOrder[rnd]; m_bombsiteSearchOrder[rnd] = swap; } m_bombsiteSearchIndex = 0; InitializeHostageInfo(); }
// Return true if we can see the point bool CCSBot::__MAKE_VHOOK(IsVisible)(const Vector *pos, bool testFOV) const { // we can't see anything if we're blind if (IsBlind()) return false; // is it in my general viewcone? if (testFOV && !(const_cast<CCSBot *>(this)->FInViewCone(pos))) return false; // check line of sight against smoke if (TheCSBots()->IsLineBlockedBySmoke(&GetEyePosition(), pos)) return false; // check line of sight // Must include CONTENTS_MONSTER to pick up all non-brush objects like barrels TraceResult result; UTIL_TraceLine(GetEyePosition(), *pos, ignore_monsters, ignore_glass, ENT(pev), &result); if (result.flFraction != 1.0f) return false; return true; }
void CCSBot::__MAKE_VHOOK(OnEvent)(GameEventType event, CBaseEntity *entity, CBaseEntity *other) { GetGameState()->OnEvent(event, entity, other); GetChatter()->OnEvent(event, entity, other); // Morale adjustments happen even for dead players switch (event) { case EVENT_TERRORISTS_WIN: if (m_iTeam == CT) { DecreaseMorale(); } else { IncreaseMorale(); } break; case EVENT_CTS_WIN: if (m_iTeam == CT) { IncreaseMorale(); } else { DecreaseMorale(); } break; } if (!IsAlive()) return; CBasePlayer *player = static_cast<CBasePlayer *>(entity); // If we just saw a nearby friend die, and we haven't yet acquired an enemy // automatically acquire our dead friend's killer if (!IsAttacking() && (GetDisposition() == ENGAGE_AND_INVESTIGATE || GetDisposition() == OPPORTUNITY_FIRE)) { if (event == EVENT_PLAYER_DIED) { if (BotRelationship(player) == BOT_TEAMMATE) { CBasePlayer *killer = static_cast<CBasePlayer *>(other); // check that attacker is an enemy (for friendly fire, etc) if (killer != NULL && killer->IsPlayer()) { // check if we saw our friend die - dont check FOV - assume we're aware of our surroundings in combat // snipers stay put if (!IsSniper() && IsVisible(&player->pev->origin)) { // people are dying - we should hurry Hurry(RANDOM_FLOAT(10.0f, 15.0f)); // if we're hiding with only our knife, be a little more cautious const float knifeAmbushChance = 50.0f; if (!IsHiding() || !IsUsingKnife() || RANDOM_FLOAT(0, 100) < knifeAmbushChance) { PrintIfWatched("Attacking our friend's killer!\n"); Attack(killer); return; } } } } } } switch (event) { case EVENT_PLAYER_DIED: { CBasePlayer *victim = player; CBasePlayer *killer = (other != NULL && other->IsPlayer()) ? static_cast<CBasePlayer *>(other) : NULL; // if the human player died in the single player game, tell the team if (CSGameRules()->IsCareer() && !victim->IsBot() && BotRelationship(victim) == BOT_TEAMMATE) { GetChatter()->Say("CommanderDown", 20.0f); } // keep track of the last player we killed if (killer == this) { m_lastVictimID = victim->entindex(); } // react to teammate death if (BotRelationship(victim) == BOT_TEAMMATE) { // chastise friendly fire from humans if (killer != NULL && !killer->IsBot() && BotRelationship(killer) == BOT_TEAMMATE && killer != this) { GetChatter()->KilledFriend(); } if (IsHunting()) { PrintIfWatched("Rethinking hunt due to teammate death\n"); Idle(); return; } if (IsAttacking()) { if (GetTimeSinceLastSawEnemy() > 0.4f) { PrintIfWatched("Rethinking my attack due to teammate death\n"); // allow us to sneak past windows, doors, etc IgnoreEnemies(1.0f); // move to last known position of enemy - this could cause us to flank if // the danger has changed due to our teammate's recent death SetTask(MOVE_TO_LAST_KNOWN_ENEMY_POSITION, GetEnemy()); MoveTo(&GetLastKnownEnemyPosition()); return; } } } // an enemy was killed else { if (killer != NULL && BotRelationship(killer) == BOT_TEAMMATE) { // only chatter about enemy kills if we see them occur, and they were the last one we see if (GetNearbyEnemyCount() <= 1) { // report if number of enemies left is few and we killed the last one we saw locally GetChatter()->EnemiesRemaining(); if (IsVisible(&victim->pev->origin, CHECK_FOV)) { // congratulate teammates on their kills if (killer != this) { float delay = RANDOM_FLOAT(2.0f, 3.0f); if (killer->IsBot()) { if (RANDOM_FLOAT(0.0f, 100.0f) < 40.0f) GetChatter()->Say("NiceShot", 3.0f, delay); } else { // humans get the honorific if (CSGameRules()->IsCareer()) GetChatter()->Say("NiceShotCommander", 3.0f, delay); else GetChatter()->Say("NiceShotSir", 3.0f, delay); } } } } } } return; } case EVENT_TERRORISTS_WIN: if (m_iTeam == TERRORIST) GetChatter()->CelebrateWin(); return; case EVENT_CTS_WIN: if (m_iTeam == CT) GetChatter()->CelebrateWin(); return; case EVENT_BOMB_DEFUSED: if (m_iTeam == CT && TheCSBots()->GetBombTimeLeft() < 2.0) GetChatter()->Say("BarelyDefused"); return; case EVENT_BOMB_PICKED_UP: { if (m_iTeam == CT && player != NULL) { // check if we're close enough to hear it const float bombPickupHearRangeSq = 1000.0f * 1000.0f; if ((pev->origin - player->pev->origin).LengthSquared() < bombPickupHearRangeSq) { GetChatter()->TheyPickedUpTheBomb(); } } return; } case EVENT_BOMB_BEEP: { // if we don't know where the bomb is, but heard it beep, we've discovered it if (GetGameState()->IsPlantedBombLocationKnown() == false) { // check if we're close enough to hear it const float bombBeepHearRangeSq = 1000.0f * 1000.0f; if ((pev->origin - entity->pev->origin).LengthSquared() < bombBeepHearRangeSq) { // radio the news to our team if (m_iTeam == CT && GetGameState()->GetPlantedBombsite() == CSGameState::UNKNOWN) { const CCSBotManager::Zone *zone = TheCSBots()->GetZone(&entity->pev->origin); if (zone != NULL) GetChatter()->FoundPlantedBomb(zone->m_index); } // remember where the bomb is GetGameState()->UpdatePlantedBomb(&entity->pev->origin); } } return; } case EVENT_BOMB_PLANTED: { // if we're a CT, forget what we're doing and go after the bomb if (m_iTeam == CT) { Idle(); } // if we are following someone, stop following if (IsFollowing()) { StopFollowing(); Idle(); } OnEvent(EVENT_BOMB_BEEP, other); return; } case EVENT_BOMB_DEFUSE_ABORTED: PrintIfWatched("BOMB DEFUSE ABORTED\n"); return; case EVENT_WEAPON_FIRED: case EVENT_WEAPON_FIRED_ON_EMPTY: case EVENT_WEAPON_RELOADED: { if (m_enemy == entity && IsUsingKnife()) ForceRun(5.0f); break; } default: break; } // Process radio events from our team if (player != NULL && BotRelationship(player) == BOT_TEAMMATE && event > EVENT_START_RADIO_1 && event < EVENT_END_RADIO) { // TODO: Distinguish between radio commands and responses if (event != EVENT_RADIO_AFFIRMATIVE && event != EVENT_RADIO_NEGATIVE && event != EVENT_RADIO_REPORTING_IN) { m_lastRadioCommand = event; m_lastRadioRecievedTimestamp = gpGlobals->time; m_radioSubject = player; m_radioPosition = player->pev->origin; } } // player_follows needs a player if (player == NULL) return; if (!IsRogue() && event == EVENT_HOSTAGE_CALLED_FOR_HELP && m_iTeam == CT && IsHunting()) { if ((entity->pev->origin - pev->origin).IsLengthGreaterThan(1000.0f)) return; if (IsVisible(&entity->Center())) { m_task = COLLECT_HOSTAGES; m_taskEntity = NULL; Run(); m_goalEntity = entity; MoveTo(&entity->pev->origin, m_hostageEscortCount == 0 ? SAFEST_ROUTE : FASTEST_ROUTE); PrintIfWatched("I'm fetching a hostage that called out to me\n"); return; } } // don't pay attention to noise that friends make if (!IsEnemy(player)) return; float range; PriorityType priority; bool isHostile; if (IsGameEventAudible(event, entity, other, &range, &priority, &isHostile) == false) return; if (event == EVENT_HOSTAGE_USED) { if (m_iTeam == CT) return; if ((entity->pev->origin - pev->origin).IsLengthGreaterThan(range)) return; GetChatter()->HostagesBeingTaken(); if (!GetGameState()->GetNearestVisibleFreeHostage() && m_task != GUARD_HOSTAGE_RESCUE_ZONE && GuardRandomZone()) { m_task = GUARD_HOSTAGE_RESCUE_ZONE; m_taskEntity = NULL; SetDisposition(OPPORTUNITY_FIRE); PrintIfWatched("Trying to beat them to an escape zone!\n"); } } // check if noise is close enough for us to hear const Vector *newNoisePosition = &player->pev->origin; float newNoiseDist = (pev->origin - *newNoisePosition).Length(); if (newNoiseDist < range) { // we heard the sound if ((IsLocalPlayerWatchingMe() && cv_bot_debug.value == 3.0f) || cv_bot_debug.value == 4.0f) { PrintIfWatched("Heard noise (%s from %s, pri %s, time %3.1f)\n", (event == EVENT_WEAPON_FIRED) ? "Weapon fire " : "", STRING(player->pev->netname), (priority == PRIORITY_HIGH) ? "HIGH" : ((priority == PRIORITY_MEDIUM) ? "MEDIUM" : "LOW"), gpGlobals->time); } if (event == EVENT_PLAYER_FOOTSTEP && IsUsingSniperRifle() && newNoiseDist < 300.0) EquipPistol(); // should we pay attention to it // if noise timestamp is zero, there is no prior noise if (m_noiseTimestamp > 0.0f) { // only overwrite recent sound if we are louder (closer), or more important - if old noise was long ago, its faded const float shortTermMemoryTime = 3.0f; if (gpGlobals->time - m_noiseTimestamp < shortTermMemoryTime) { // prior noise is more important - ignore new one if (priority < m_noisePriority) return; float oldNoiseDist = (pev->origin - m_noisePosition).Length(); if (newNoiseDist >= oldNoiseDist) return; } } // find the area in which the noise occured // TODO: Better handle when noise occurs off the nav mesh // TODO: Make sure noise area is not through a wall or ceiling from source of noise // TODO: Change GetNavTravelTime to better deal with NULL destination areas CNavArea *noiseArea = TheNavAreaGrid.GetNavArea(newNoisePosition); if (noiseArea == NULL) noiseArea = TheNavAreaGrid.GetNearestNavArea(newNoisePosition); if (noiseArea == NULL) { PrintIfWatched(" *** Noise occurred off the nav mesh - ignoring!\n"); return; } m_noiseArea = noiseArea; // remember noise priority m_noisePriority = priority; // randomize noise position in the area a bit - hearing isn't very accurate // the closer the noise is, the more accurate our placement // TODO: Make sure not to pick a position on the opposite side of ourselves. const float maxErrorRadius = 400.0f; const float maxHearingRange = 2000.0f; float errorRadius = maxErrorRadius * newNoiseDist / maxHearingRange; m_noisePosition.x = newNoisePosition->x + RANDOM_FLOAT(-errorRadius, errorRadius); m_noisePosition.y = newNoisePosition->y + RANDOM_FLOAT(-errorRadius, errorRadius); // make sure noise position remains in the same area m_noiseArea->GetClosestPointOnArea(&m_noisePosition, &m_noisePosition); m_isNoiseTravelRangeChecked = false; // note when we heard the noise m_noiseTimestamp = gpGlobals->time; } }
/** * Buy weapons, armor, etc. */ void BuyState::OnEnter( CCSBot *me ) { m_retries = 0; m_prefRetries = 0; m_prefIndex = 0; const char *cheatWeaponString = bot_loadout.GetString(); if ( cheatWeaponString && *cheatWeaponString ) { m_doneBuying = false; // we're going to be given weapons - ignore the eco limit } else { // check if we are saving money for the next round if (me->m_iAccount < cv_bot_eco_limit.GetFloat()) { me->PrintIfWatched( "Saving money for next round.\n" ); m_doneBuying = true; } else { m_doneBuying = false; } } m_isInitialDelay = true; // this will force us to stop holding live grenade me->EquipBestWeapon( MUST_EQUIP ); m_buyDefuseKit = false; m_buyShield = false; if (me->GetTeamNumber() == TEAM_CT) { if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB) { // CT's sometimes buy defuse kits in the bomb scenario (except in career mode, where the player should defuse) if (CSGameRules()->IsCareer() == false) { const float buyDefuseKitChance = 100.0f * (me->GetProfile()->GetSkill() + 0.2f); if (RandomFloat( 0.0f, 100.0f ) < buyDefuseKitChance) { m_buyDefuseKit = true; } } } // determine if we want a tactical shield if (!me->HasPrimaryWeapon() && TheCSBots()->AllowTacticalShield()) { if (me->m_iAccount > 2500) { if (me->m_iAccount < 4000) m_buyShield = (RandomFloat( 0, 100.0f ) < 33.3f) ? true : false; else m_buyShield = (RandomFloat( 0, 100.0f ) < 10.0f) ? true : false; } } } if (TheCSBots()->AllowGrenades()) { m_buyGrenade = (RandomFloat( 0.0f, 100.0f ) < 33.3f) ? true : false; } else { m_buyGrenade = false; } m_buyPistol = false; if (TheCSBots()->AllowPistols()) { // check if we have a pistol if (me->Weapon_GetSlot( WEAPON_SLOT_PISTOL )) { // if we have our default pistol, think about buying a different one if (HasDefaultPistol( me )) { // if everything other than pistols is disallowed, buy a pistol if (TheCSBots()->AllowShotguns() == false && TheCSBots()->AllowSubMachineGuns() == false && TheCSBots()->AllowRifles() == false && TheCSBots()->AllowMachineGuns() == false && TheCSBots()->AllowTacticalShield() == false && TheCSBots()->AllowSnipers() == false) { m_buyPistol = (RandomFloat( 0, 100 ) < 75.0f); } else if (me->m_iAccount < 1000) { // if we're low on cash, buy a pistol m_buyPistol = (RandomFloat( 0, 100 ) < 75.0f); } else { m_buyPistol = (RandomFloat( 0, 100 ) < 33.3f); } } } else { // we dont have a pistol - buy one m_buyPistol = true; } } }
//-------------------------------------------------------------------------------------------------------------- void BuyState::OnUpdate( CCSBot *me ) { char cmdBuffer[256]; // wait for a Navigation Mesh if (!TheNavMesh->IsLoaded()) return; // apparently we cant buy things in the first few seconds, so wait a bit if (m_isInitialDelay) { const float waitToBuyTime = 0.25f; if (gpGlobals->curtime - me->GetStateTimestamp() < waitToBuyTime) return; m_isInitialDelay = false; } // if we're done buying and still in the freeze period, wait if (m_doneBuying) { if (CSGameRules()->IsMultiplayer() && CSGameRules()->IsFreezePeriod()) { // make sure we're locked and loaded me->EquipBestWeapon( MUST_EQUIP ); me->Reload(); me->ResetStuckMonitor(); return; } me->Idle(); return; } // If we're supposed to buy a specific weapon for debugging, do so and then bail const char *cheatWeaponString = bot_loadout.GetString(); if ( cheatWeaponString && *cheatWeaponString ) { CUtlVector<char*, CUtlMemory<char*> > loadout; Q_SplitString( cheatWeaponString, " ", loadout ); for ( int i=0; i<loadout.Count(); ++i ) { const char *item = loadout[i]; if ( FStrEq( item, "vest" ) ) { me->GiveNamedItem( "item_kevlar" ); } else if ( FStrEq( item, "vesthelm" ) ) { me->GiveNamedItem( "item_assaultsuit" ); } else if ( FStrEq( item, "defuser" ) ) { if ( me->GetTeamNumber() == TEAM_CT ) { me->GiveDefuser(); } } else if ( FStrEq( item, "nvgs" ) ) { me->m_bHasNightVision = true; } else if ( FStrEq( item, "primammo" ) ) { me->AttemptToBuyAmmo( 0 ); } else if ( FStrEq( item, "secammo" ) ) { me->AttemptToBuyAmmo( 1 ); } else { me->GiveWeapon( item ); } } m_doneBuying = true; return; } if (!me->IsInBuyZone()) { m_doneBuying = true; CONSOLE_ECHO( "%s bot spawned outside of a buy zone (%d, %d, %d)\n", (me->GetTeamNumber() == TEAM_CT) ? "CT" : "Terrorist", (int)me->GetAbsOrigin().x, (int)me->GetAbsOrigin().y, (int)me->GetAbsOrigin().z ); return; } // try to buy some weapons const float buyInterval = 0.02f; if (gpGlobals->curtime - me->GetStateTimestamp() > buyInterval) { me->m_stateTimestamp = gpGlobals->curtime; bool isPreferredAllDisallowed = true; // try to buy our preferred weapons first if (m_prefIndex < me->GetProfile()->GetWeaponPreferenceCount() && bot_randombuy.GetBool() == false ) { // need to retry because sometimes first buy fails?? const int maxPrefRetries = 2; if (m_prefRetries >= maxPrefRetries) { // try to buy next preferred weapon ++m_prefIndex; m_prefRetries = 0; return; } int weaponPreference = me->GetProfile()->GetWeaponPreference( m_prefIndex ); // don't buy it again if we still have one from last round char weaponPreferenceName[32]; Q_snprintf( weaponPreferenceName, sizeof(weaponPreferenceName), "weapon_%s", me->GetProfile()->GetWeaponPreferenceAsString( m_prefIndex ) ); if( me->Weapon_OwnsThisType(weaponPreferenceName) )//Prefs and buyalias use the short version, this uses the long { // done with buying preferred weapon m_prefIndex = 9999; return; } if (me->HasShield() && weaponPreference == WEAPON_SHIELDGUN) { // done with buying preferred weapon m_prefIndex = 9999; return; } const char *buyAlias = NULL; if (weaponPreference == WEAPON_SHIELDGUN) { if (TheCSBots()->AllowTacticalShield()) buyAlias = "shield"; } else { buyAlias = WeaponIDToAlias( weaponPreference ); WeaponType type = GetWeaponType( buyAlias ); switch( type ) { case PISTOL: if (!TheCSBots()->AllowPistols()) buyAlias = NULL; break; case SHOTGUN: if (!TheCSBots()->AllowShotguns()) buyAlias = NULL; break; case SUB_MACHINE_GUN: if (!TheCSBots()->AllowSubMachineGuns()) buyAlias = NULL; break; case RIFLE: if (!TheCSBots()->AllowRifles()) buyAlias = NULL; break; case MACHINE_GUN: if (!TheCSBots()->AllowMachineGuns()) buyAlias = NULL; break; case SNIPER_RIFLE: if (!TheCSBots()->AllowSnipers()) buyAlias = NULL; break; } } if (buyAlias) { Q_snprintf( cmdBuffer, 256, "buy %s\n", buyAlias ); CCommand args; args.Tokenize( cmdBuffer ); me->ClientCommand( args ); me->PrintIfWatched( "Tried to buy preferred weapon %s.\n", buyAlias ); isPreferredAllDisallowed = false; } ++m_prefRetries; // bail out so we dont waste money on other equipment // unless everything we prefer has been disallowed, then buy at random if (isPreferredAllDisallowed == false) return; } // if we have no preferred primary weapon (or everything we want is disallowed), buy at random if (!me->HasPrimaryWeapon() && (isPreferredAllDisallowed || !me->GetProfile()->HasPrimaryPreference())) { if (m_buyShield) { // buy a shield CCommand args; args.Tokenize( "buy shield" ); me->ClientCommand( args ); me->PrintIfWatched( "Tried to buy a shield.\n" ); } else { // build list of allowable weapons to buy BuyInfo *masterPrimary = (me->GetTeamNumber() == TEAM_TERRORIST) ? primaryWeaponBuyInfoT : primaryWeaponBuyInfoCT; BuyInfo *stockPrimary[ PRIMARY_WEAPON_BUY_COUNT ]; int stockPrimaryCount = 0; // dont choose sniper rifles as often const float sniperRifleChance = 50.0f; bool wantSniper = (RandomFloat( 0, 100 ) < sniperRifleChance) ? true : false; if ( bot_randombuy.GetBool() ) { wantSniper = true; } for( int i=0; i<PRIMARY_WEAPON_BUY_COUNT; ++i ) { if ((masterPrimary[i].type == SHOTGUN && TheCSBots()->AllowShotguns()) || (masterPrimary[i].type == SUB_MACHINE_GUN && TheCSBots()->AllowSubMachineGuns()) || (masterPrimary[i].type == RIFLE && TheCSBots()->AllowRifles()) || (masterPrimary[i].type == SNIPER_RIFLE && TheCSBots()->AllowSnipers() && wantSniper) || (masterPrimary[i].type == MACHINE_GUN && TheCSBots()->AllowMachineGuns())) { stockPrimary[ stockPrimaryCount++ ] = &masterPrimary[i]; } } if (stockPrimaryCount) { // buy primary weapon if we don't have one int which; // on hard difficulty levels, bots try to buy preferred weapons on the first pass if (m_retries == 0 && TheCSBots()->GetDifficultyLevel() >= BOT_HARD && bot_randombuy.GetBool() == false ) { // count up available preferred weapons int prefCount = 0; for( which=0; which<stockPrimaryCount; ++which ) if (stockPrimary[which]->preferred) ++prefCount; if (prefCount) { int whichPref = RandomInt( 0, prefCount-1 ); for( which=0; which<stockPrimaryCount; ++which ) if (stockPrimary[which]->preferred && whichPref-- == 0) break; } else { // no preferred weapons available, just pick randomly which = RandomInt( 0, stockPrimaryCount-1 ); } } else { which = RandomInt( 0, stockPrimaryCount-1 ); } Q_snprintf( cmdBuffer, 256, "buy %s\n", stockPrimary[ which ]->buyAlias ); CCommand args; args.Tokenize( cmdBuffer ); me->ClientCommand( args ); me->PrintIfWatched( "Tried to buy %s.\n", stockPrimary[ which ]->buyAlias ); } } } // // If we now have a weapon, or have tried for too long, we're done // if (me->HasPrimaryWeapon() || m_retries++ > 5) { // primary ammo CCommand args; if (me->HasPrimaryWeapon()) { args.Tokenize( "buy primammo" ); me->ClientCommand( args ); } // buy armor last, to make sure we bought a weapon first args.Tokenize( "buy vesthelm" ); me->ClientCommand( args ); args.Tokenize( "buy vest" ); me->ClientCommand( args ); // pistols - if we have no preferred pistol, buy at random if (TheCSBots()->AllowPistols() && !me->GetProfile()->HasPistolPreference()) { if (m_buyPistol) { int which = RandomInt( 0, SECONDARY_WEAPON_BUY_COUNT-1 ); const char *what = NULL; if (me->GetTeamNumber() == TEAM_TERRORIST) what = secondaryWeaponBuyInfoT[ which ].buyAlias; else what = secondaryWeaponBuyInfoCT[ which ].buyAlias; Q_snprintf( cmdBuffer, 256, "buy %s\n", what ); args.Tokenize( cmdBuffer ); me->ClientCommand( args ); // only buy one pistol m_buyPistol = false; } // make sure we have enough pistol ammo args.Tokenize( "buy secammo" ); me->ClientCommand( args ); } // buy a grenade if we wish, and we don't already have one if (m_buyGrenade && !me->HasGrenade()) { if (UTIL_IsTeamAllBots( me->GetTeamNumber() )) { // only allow Flashbangs if everyone on the team is a bot (dont want to blind our friendly humans) float rnd = RandomFloat( 0, 100 ); if (rnd < 10) { args.Tokenize( "buy smokegrenade" ); me->ClientCommand( args ); // smoke grenade } else if (rnd < 35) { args.Tokenize( "buy flashbang" ); me->ClientCommand( args ); // flashbang } else { args.Tokenize( "buy hegrenade" ); me->ClientCommand( args ); // he grenade } } else { if (RandomFloat( 0, 100 ) < 10) { args.Tokenize( "buy smokegrenade" ); // smoke grenade me->ClientCommand( args ); } else { args.Tokenize( "buy hegrenade" ); // he grenade me->ClientCommand( args ); } } } if (m_buyDefuseKit) { args.Tokenize( "buy defuser" ); me->ClientCommand( args ); } m_doneBuying = true; } } }
/* <43c197> ../cstrike/dlls/hostage/states/hostage_idle.cpp:23 */ void HostageIdleState::__MAKE_VHOOK(OnUpdate)(CHostageImprov *improv) { if (!UTIL_ActivePlayersInGame()) return; if (m_mustFlee || (improv->IsScared() && !improv->IsTerroristNearby() && m_moveState != Moving)) { if (!m_mustFlee && improv->GetScareIntensity() == CHostageImprov::TERRIFIED) m_mustFlee = true; if ((improv->GetScareIntensity() == CHostageImprov::TERRIFIED || m_mustFlee) || (m_fleeTimer.IsElapsed() && improv->GetScareIntensity() > CHostageImprov::NERVOUS)) { m_fleeTimer.Start(RANDOM_FLOAT(10, 20)); const float fleeChance = 33.3f; const float terroristRecentTime = 5.0f; if (!m_mustFlee && improv->GetTimeSinceLastSawPlayer(TERRORIST) > terroristRecentTime && RANDOM_FLOAT(0, 100) < fleeChance) m_mustFlee = true; if (m_mustFlee) { m_mustFlee = false; const Vector *spot = FindNearbyRetreatSpot(NULL, &improv->GetFeet(), improv->GetLastKnownArea(), 500.0, TERRORIST, false); if (spot != NULL) { improv->MoveTo(*spot); improv->Run(); m_moveState = Moving; if (improv->GetScareIntensity() == CHostageImprov::TERRIFIED) { improv->Frighten(CHostageImprov::SCARED); } return; } } } } if (m_moveState && improv->IsAtMoveGoal()) { m_moveState = NotMoving; improv->Stop(); improv->FaceOutwards(); const float crouchChance = 33.3f; if (improv->IsScared() && !improv->IsAtHome() && RANDOM_FLOAT(0, 100) <= crouchChance) { improv->Crouch(); } return; } if (m_moveState == Moving) { improv->Run(); return; } if (!improv->IsAtMoveGoal(75.0f)) { improv->Walk(); m_moveState = Moving; return; } CBasePlayer *rescuer = improv->GetClosestVisiblePlayer(CT); CBasePlayer *captor = improv->GetClosestVisiblePlayer(TERRORIST); if (rescuer != NULL) { improv->LookAt(rescuer->EyePosition()); improv->Stop(); if (captor != NULL) { const float attentionRange = 700.0f; float rangeT = (improv->GetCentroid() - captor->pev->origin).Length(); if (rangeT < attentionRange) { const float cosTolerance = 0.95f; if (improv->IsAnyPlayerLookingAtMe(TERRORIST, cosTolerance)) { improv->LookAt(captor->EyePosition()); } else { TraceResult result; UTIL_TraceLine(rescuer->pev->origin, captor->pev->origin, ignore_monsters, ignore_glass, captor->edict(), &result); if (result.flFraction != 1.0f && m_disagreeTimer.IsElapsed()) { improv->Disagree(); m_disagreeTimer.Start(RANDOM_FLOAT(2, 4)); } } return; } } else if (!TheCSBots()->IsRoundOver() && m_askTimer.IsElapsed()) { const float closeRange = 200.0f; if ((rescuer->pev->origin - improv->GetCentroid()).IsLengthLessThan(closeRange)) { if (improv->IsPlayerLookingAtMe(rescuer, 0.99)) { HostageChatterType say; if (improv->IsTerroristNearby()) say = HOSTAGE_CHATTER_WARN_NEARBY; else say = HOSTAGE_CHATTER_PLEASE_RESCUE_ME; improv->Chatter(say, false); m_askTimer.Start(RANDOM_FLOAT(3, 10)); } } } if (m_waveTimer.IsElapsed()) { CHostage *hostage = improv->GetEntity(); const float waveRange = 250.0f; if ((rescuer->pev->origin - hostage->pev->origin).IsLengthGreaterThan(waveRange)) { improv->Stop(); improv->Wave(); improv->LookAt(rescuer->EyePosition()); improv->Chatter(HOSTAGE_CHATTER_CALL_TO_RESCUER, false); m_moveState = NotMoving; m_waveTimer.Start(RANDOM_FLOAT(10, 20)); } } } else if (captor != NULL) { improv->LookAt(captor->EyePosition()); improv->Stop(); const float closeRange = 200.0f; if ((captor->pev->origin - improv->GetCentroid()).IsLengthLessThan(closeRange) && improv->IsPlayerLookingAtMe(captor, 0.99)) { if (!m_intimidatedTimer.HasStarted()) { m_intimidatedTimer.Start(); } if (!improv->IsScared()) { improv->Frighten(CHostageImprov::NERVOUS); } const float minThreatenTime = 1.0f; if ((!m_intimidatedTimer.HasStarted() || m_intimidatedTimer.IsGreaterThen(minThreatenTime)) && m_pleadTimer.IsElapsed()) { improv->Chatter(HOSTAGE_CHATTER_INTIMIDATED, true); m_pleadTimer.Start(RANDOM_FLOAT(10, 20)); } if (!improv->IsAtHome()) { improv->Chatter(HOSTAGE_CHATTER_RETREAT, true); improv->Retreat(); } } else { m_intimidatedTimer.Invalidate(); } } else { improv->ClearLookAt(); const float pushbackRange = 60.0f; if (pushbackRange - improv->GetAggression() * 5.0f < TheCSBots()->GetElapsedRoundTime() && m_escapeTimer.IsElapsed()) { const float stayHomeDuration = 5.0f; m_escapeTimer.Start(stayHomeDuration); float sightTimeT = improv->GetTimeSinceLastSawPlayer(TERRORIST); float sightTimeCT = improv->GetTimeSinceLastSawPlayer(CT); const float waitTime = 15.0f - improv->GetAggression() * 3.0f; if (sightTimeT > waitTime && sightTimeCT > waitTime) { if (improv->IsTerroristNearby()) { if (cv_hostage_debug.value > 0.0f) { CONSOLE_ECHO("Hostage: I want to escape, but a T is nearby\n"); } m_escapeTimer.Start(waitTime); } else { improv->Escape(); if (cv_hostage_debug.value > 0.0f) { CONSOLE_ECHO("Hostage: I'm escaping!\n"); } } } } } }
// Buy weapons, armor, etc. void BuyState::__MAKE_VHOOK(OnEnter)(CCSBot *me) { m_retries = 0; m_prefRetries = 0; m_prefIndex = 0; m_doneBuying = false; m_isInitialDelay = true; // this will force us to stop holding live grenade me->EquipBestWeapon(); m_buyDefuseKit = false; m_buyShield = false; if (me->m_iTeam == CT) { if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB) { // CT's sometimes buy defuse kits in the bomb scenario (except in career mode, where the player should defuse) if (!CSGameRules()->IsCareer()) { const float buyDefuseKitChance = 50.0f; // 100.0f * (me->GetProfile()->GetSkill() + 0.2f); if (RANDOM_FLOAT(0.0f, 100.0f) < buyDefuseKitChance) { m_buyDefuseKit = true; } } } // determine if we want a tactical shield if (!me->m_bHasPrimary && TheCSBots()->AllowTacticalShield()) { if (me->m_iAccount > 2500) { if (me->m_iAccount < 4000) m_buyShield = (RANDOM_FLOAT(0, 100.0f) < 33.3f) ? true : false; else m_buyShield = (RANDOM_FLOAT(0, 100.0f) < 10.0f) ? true : false; } } } if (TheCSBots()->AllowGrenades()) { m_buyGrenade = (RANDOM_FLOAT(0.0f, 100.0f) < 33.3f) ? true : false; } else { m_buyGrenade = false; } m_buyPistol = false; if (TheCSBots()->AllowPistols()) { CBasePlayerWeapon *pistol = static_cast<CBasePlayerWeapon *>(me->m_rgpPlayerItems[ PISTOL_SLOT ]); // check if we have a pistol if (pistol != NULL) { // if we have our default pistol, think about buying a different one if (HasDefaultPistol(me)) { // if everything other than pistols is disallowed, buy a pistol if (TheCSBots()->AllowShotguns() == false && TheCSBots()->AllowSubMachineGuns() == false && TheCSBots()->AllowRifles() == false && TheCSBots()->AllowMachineGuns() == false && TheCSBots()->AllowTacticalShield() == false && TheCSBots()->AllowSnipers() == false) { m_buyPistol = (RANDOM_FLOAT(0, 100) < 75.0f); } else if (me->m_iAccount < 1000) { // if we're low on cash, buy a pistol m_buyPistol = (RANDOM_FLOAT(0, 100) < 75.0f); } else { m_buyPistol = (RANDOM_FLOAT(0, 100) < 33.3f); } } } else { // we dont have a pistol - buy one m_buyPistol = true; } } }
void BuyState::__MAKE_VHOOK(OnUpdate)(CCSBot *me) { // wait for a Navigation Mesh if (!TheNavAreaList.size()) return; // apparently we cant buy things in the first few seconds, so wait a bit if (m_isInitialDelay) { const float waitToBuyTime = 2.0f; // 0.25f; if (gpGlobals->time - me->GetStateTimestamp() < waitToBuyTime) return; m_isInitialDelay = false; } // if we're done buying and still in the freeze period, wait if (m_doneBuying) { if (CSGameRules()->IsMultiplayer() && CSGameRules()->IsFreezePeriod()) { #ifdef REGAMEDLL_FIXES // make sure we're locked and loaded me->EquipBestWeapon(MUST_EQUIP); me->Reload(); me->ResetStuckMonitor(); #endif return; } me->Idle(); #ifdef REGAMEDLL_FIXES return; #endif } // is the bot spawned outside of a buy zone? if (!(me->m_signals.GetState() & SIGNAL_BUY)) { m_doneBuying = true; UTIL_DPrintf("%s bot spawned outside of a buy zone (%d, %d, %d)\n", (me->m_iTeam == CT) ? "CT" : "Terrorist", int(me->pev->origin.x), int(me->pev->origin.y), int(me->pev->origin.z)); return; } // try to buy some weapons const float buyInterval = 0.2f; // 0.02f if (gpGlobals->time - me->GetStateTimestamp() > buyInterval) { me->m_stateTimestamp = gpGlobals->time; bool isPreferredAllDisallowed = true; // try to buy our preferred weapons first if (m_prefIndex < me->GetProfile()->GetWeaponPreferenceCount()) { // need to retry because sometimes first buy fails?? const int maxPrefRetries = 2; if (m_prefRetries >= maxPrefRetries) { // try to buy next preferred weapon ++m_prefIndex; m_prefRetries = 0; return; } int weaponPreference = me->GetProfile()->GetWeaponPreference(m_prefIndex); // don't buy it again if we still have one from last round CBasePlayerWeapon *weapon = me->GetActiveWeapon(); if (weapon != NULL && weapon->m_iId == weaponPreference) { // done with buying preferred weapon m_prefIndex = 9999; return; } if (me->HasShield() && weaponPreference == WEAPON_SHIELDGUN) { // done with buying preferred weapon m_prefIndex = 9999; return; } const char *buyAlias = NULL; if (weaponPreference == WEAPON_SHIELDGUN) { if (TheCSBots()->AllowTacticalShield()) buyAlias = "shield"; } else { buyAlias = WeaponIDToAlias(weaponPreference); WeaponType type = GetWeaponType(buyAlias); switch (type) { case PISTOL: if (!TheCSBots()->AllowPistols()) buyAlias = NULL; break; case SHOTGUN: if (!TheCSBots()->AllowShotguns()) buyAlias = NULL; break; case SUB_MACHINE_GUN: if (!TheCSBots()->AllowSubMachineGuns()) buyAlias = NULL; break; case RIFLE: if (!TheCSBots()->AllowRifles()) buyAlias = NULL; break; case MACHINE_GUN: if (!TheCSBots()->AllowMachineGuns()) buyAlias = NULL; break; case SNIPER_RIFLE: if (!TheCSBots()->AllowSnipers()) buyAlias = NULL; break; } } if (buyAlias) { me->ClientCommand(buyAlias); me->PrintIfWatched("Tried to buy preferred weapon %s.\n", buyAlias); isPreferredAllDisallowed = false; } ++m_prefRetries; // bail out so we dont waste money on other equipment // unless everything we prefer has been disallowed, then buy at random if (isPreferredAllDisallowed == false) return; } // if we have no preferred primary weapon (or everything we want is disallowed), buy at random if (!me->m_bHasPrimary && (isPreferredAllDisallowed || !me->GetProfile()->HasPrimaryPreference())) { if (m_buyShield) { // buy a shield me->ClientCommand("shield"); me->PrintIfWatched("Tried to buy a shield.\n"); } else { // build list of allowable weapons to buy BuyInfo *masterPrimary = (me->m_iTeam == TERRORIST) ? primaryWeaponBuyInfoT : primaryWeaponBuyInfoCT; BuyInfo *stockPrimary[ PRIMARY_WEAPON_BUY_COUNT ]; int stockPrimaryCount = 0; // dont choose sniper rifles as often const float sniperRifleChance = 50.0f; bool wantSniper = (RANDOM_FLOAT(0, 100) < sniperRifleChance) ? true : false; for (int i = 0; i < PRIMARY_WEAPON_BUY_COUNT; ++i) { if ((masterPrimary[i].type == SHOTGUN && TheCSBots()->AllowShotguns()) || (masterPrimary[i].type == SUB_MACHINE_GUN && TheCSBots()->AllowSubMachineGuns()) || (masterPrimary[i].type == RIFLE && TheCSBots()->AllowRifles()) || (masterPrimary[i].type == SNIPER_RIFLE && TheCSBots()->AllowSnipers() && wantSniper) || (masterPrimary[i].type == MACHINE_GUN && TheCSBots()->AllowMachineGuns())) { stockPrimary[ stockPrimaryCount++ ] = &masterPrimary[i]; } } if (stockPrimaryCount) { // buy primary weapon if we don't have one int which; // on hard difficulty levels, bots try to buy preferred weapons on the first pass if (m_retries == 0 && TheCSBots()->GetDifficultyLevel() >= BOT_HARD) { // count up available preferred weapons int prefCount = 0; for (which = 0; which < stockPrimaryCount; ++which) { if (stockPrimary[which]->preferred) ++prefCount; } if (prefCount) { int whichPref = RANDOM_LONG(0, prefCount - 1); for (which = 0; which < stockPrimaryCount; ++which) { if (stockPrimary[which]->preferred && whichPref-- == 0) break; } } else { // no preferred weapons available, just pick randomly which = RANDOM_LONG(0, stockPrimaryCount - 1); } } else { which = RANDOM_LONG(0, stockPrimaryCount - 1); } me->ClientCommand(stockPrimary[ which ]->buyAlias); me->PrintIfWatched("Tried to buy %s.\n", stockPrimary[ which ]->buyAlias); } } } // If we now have a weapon, or have tried for too long, we're done if (me->m_bHasPrimary || m_retries++ > 5) { // primary ammo if (me->m_bHasPrimary) { me->ClientCommand("primammo"); } // buy armor last, to make sure we bought a weapon first me->ClientCommand("vesthelm"); me->ClientCommand("vest"); // pistols - if we have no preferred pistol, buy at random if (TheCSBots()->AllowPistols() && !me->GetProfile()->HasPistolPreference()) { if (m_buyPistol) { int which = RANDOM_LONG(0, SECONDARY_WEAPON_BUY_COUNT - 1); if (me->m_iTeam == TERRORIST) me->ClientCommand(secondaryWeaponBuyInfoT[ which ].buyAlias); else me->ClientCommand(secondaryWeaponBuyInfoCT[ which ].buyAlias); // only buy one pistol m_buyPistol = false; } me->ClientCommand("secammo"); } // buy a grenade if we wish, and we don't already have one if (m_buyGrenade && !me->HasGrenade()) { if (UTIL_IsTeamAllBots(me->m_iTeam)) { // only allow Flashbangs if everyone on the team is a bot (dont want to blind our friendly humans) float rnd = RANDOM_FLOAT(0, 100); if (rnd < 10.0f) { // smoke grenade me->ClientCommand("sgren"); } else if (rnd < 35.0f) { // flashbang me->ClientCommand("flash"); } else { // he grenade me->ClientCommand("hegren"); } } else { if (RANDOM_FLOAT(0, 100) < 10.0f) { // smoke grenade me->ClientCommand("sgren"); } else { // he grenade me->ClientCommand("hegren"); } } } if (m_buyDefuseKit) { me->ClientCommand("defuser"); } m_doneBuying = true; } } }