diff --git a/regamedll/dlls/bot/cs_bot.h b/regamedll/dlls/bot/cs_bot.h index 9670744f..ea22a18d 100644 --- a/regamedll/dlls/bot/cs_bot.h +++ b/regamedll/dlls/bot/cs_bot.h @@ -533,6 +533,11 @@ public: bool IsAwareOfEnemyDeath() const; // return true if we *noticed* that our enemy died int GetLastVictimID() const; // return the ID (entindex) of the last victim we killed, or zero +#ifdef REGAMEDLL_ADD + bool CanSeeSniper(void) const; ///< return true if we can see an enemy sniper + bool HasSeenSniperRecently(void) const; ///< return true if we have seen a sniper recently +#endif + // navigation bool HasPath() const; void DestroyPath(); @@ -921,6 +926,11 @@ private: float m_fireWeaponTimestamp; +#ifdef REGAMEDLL_ADD + bool m_isEnemySniperVisible; ///< do we see an enemy sniper right now + CountdownTimer m_sawEnemySniperTimer; ///< tracking time since saw enemy sniper +#endif + // reaction time system enum { MAX_ENEMY_QUEUE = 20 }; struct ReactionState @@ -1250,6 +1260,18 @@ inline int CCSBot::GetLastVictimID() const return m_lastVictimID; } +#ifdef REGAMEDLL_ADD +inline bool CCSBot::CanSeeSniper(void) const +{ + return m_isEnemySniperVisible; +} + +inline bool CCSBot::HasSeenSniperRecently(void) const +{ + return !m_sawEnemySniperTimer.IsElapsed(); +} +#endif + inline bool CCSBot::HasPath() const { return m_pathLength != 0; diff --git a/regamedll/dlls/bot/cs_bot_chatter.cpp b/regamedll/dlls/bot/cs_bot_chatter.cpp index f58759fe..d48898ae 100644 --- a/regamedll/dlls/bot/cs_bot_chatter.cpp +++ b/regamedll/dlls/bot/cs_bot_chatter.cpp @@ -246,6 +246,17 @@ void BotHostageBeingTakenMeme::Interpret(CCSBot *pSender, CCSBot *pReceiver) con pReceiver->GetChatter()->Say("Affirmative"); } +#ifdef REGAMEDLL_ADD +//--------------------------------------------------------------------------------------------------------------- +/** + * A teammate warned about snipers, so we shouldn't warn again for awhile + */ +void BotWarnSniperMeme::Interpret(CCSBot* sender, CCSBot* receiver) const +{ + receiver->GetChatter()->FriendSpottedSniper(); +} +#endif + BotSpeakable::BotSpeakable() { m_phrase = nullptr; @@ -1270,6 +1281,9 @@ void BotChatterInterface::Reset() m_planInterval.Invalidate(); m_encourageTimer.Invalidate(); m_escortingHostageTimer.Invalidate(); +#ifdef REGAMEDLL_ADD + m_warnSniperTimer.Invalidate(); +#endif } // Register a statement for speaking @@ -1626,6 +1640,42 @@ void BotChatterInterface::EnemySpotted() AddStatement(say); } +#ifdef REGAMEDLL_ADD +//--------------------------------------------------------------------------------------------------------------- +/** + * If a friend warned of snipers, don't warn again for awhile + */ +void BotChatterInterface::FriendSpottedSniper(void) +{ + m_warnSniperTimer.Start(60.0f); +} + +//--------------------------------------------------------------------------------------------------------------- +/** + * Warn of an enemy sniper + */ +void BotChatterInterface::SpottedSniper(void) +{ + if (!m_warnSniperTimer.IsElapsed()) + { + return; + } + + if (m_me->GetFriendsRemaining() == 0) + { + // no-one to warn + return; + } + + BotStatement* say = new BotStatement(this, REPORT_INFORMATION, 10.0f); + + say->AppendPhrase(TheBotPhrases->GetPhrase("SniperWarning")); + say->AttachMeme(new BotWarnSniperMeme()); + + AddStatement(say); +} +#endif + NOXREF void BotChatterInterface::Clear(Place place) { BotStatement *say = new BotStatement(this, REPORT_INFORMATION, 10.0f); diff --git a/regamedll/dlls/bot/cs_bot_chatter.h b/regamedll/dlls/bot/cs_bot_chatter.h index 4a4591cf..8d3a5baa 100644 --- a/regamedll/dlls/bot/cs_bot_chatter.h +++ b/regamedll/dlls/bot/cs_bot_chatter.h @@ -140,6 +140,14 @@ public: virtual void Interpret(CCSBot *pSender, CCSBot *pReceiver) const; // cause the given bot to act on this meme }; +#ifdef REGAMEDLL_ADD +class BotWarnSniperMeme : public BotMeme +{ +public: + virtual void Interpret(CCSBot* sender, CCSBot* receiver) const; ///< cause the given bot to act on this meme +}; +#endif + enum BotStatementType { REPORT_VISIBLE_ENEMIES, @@ -531,6 +539,10 @@ public: void KilledFriend(); void FriendlyFire(); +#ifdef REGAMEDLL_ADD + void SpottedSniper(void); + void FriendSpottedSniper(void); +#endif bool SeesAtLeastOneEnemy() const { return m_seeAtLeastOneEnemy; } private: @@ -557,6 +569,9 @@ private: CountdownTimer m_spottedLooseBombTimer; CountdownTimer m_heardNoiseTimer; CountdownTimer m_escortingHostageTimer; +#ifdef REGAMEDLL_ADD + CountdownTimer m_warnSniperTimer; +#endif }; inline BotChatterInterface::VerbosityType BotChatterInterface::GetVerbosity() const diff --git a/regamedll/dlls/bot/cs_bot_init.cpp b/regamedll/dlls/bot/cs_bot_init.cpp index 665b3c33..c7a19bb0 100644 --- a/regamedll/dlls/bot/cs_bot_init.cpp +++ b/regamedll/dlls/bot/cs_bot_init.cpp @@ -194,6 +194,11 @@ void CCSBot::ResetValues() m_currentArea = nullptr; m_lastKnownArea = nullptr; +#ifdef REGAMEDLL_ADD + m_isEnemySniperVisible = false; + m_sawEnemySniperTimer.Invalidate(); +#endif + m_avoidFriendTimer.Invalidate(); m_isFriendInTheWay = false; m_isWaitingBehindFriend = false; diff --git a/regamedll/dlls/bot/cs_bot_update.cpp b/regamedll/dlls/bot/cs_bot_update.cpp index 37d49665..de75bf6a 100644 --- a/regamedll/dlls/bot/cs_bot_update.cpp +++ b/regamedll/dlls/bot/cs_bot_update.cpp @@ -255,7 +255,7 @@ void CCSBot::Update() Vector dir = m_spotEncounter->path.to - m_spotEncounter->path.from; float length = dir.NormalizeInPlace(); - for (auto &order : m_spotEncounter->spotList) { + for (auto& order : m_spotEncounter->spotList) { UTIL_DrawBeamPoints(m_spotEncounter->path.from + order.t * length * dir, *order.spot->GetPosition(), 3, 0, 255, 255); } } @@ -339,7 +339,7 @@ void CCSBot::Update() UpdateReactionQueue(); // "threat" may be the same as our current enemy - CBasePlayer *threat = GetRecognizedEnemy(); + CBasePlayer* threat = GetRecognizedEnemy(); if (threat) { // adjust our personal "safe" time @@ -592,6 +592,10 @@ void CCSBot::Update() SecondaryAttack(); } +#ifdef REGAMEDLL_ADD + if (!IsBlind()) + { +#endif // check encounter spots UpdatePeripheralVision(); @@ -601,11 +605,24 @@ void CCSBot::Update() GetChatter()->SpottedBomber(GetBomber()); } +#ifdef REGAMEDLL_ADD + // watch for snipers + if (CanSeeSniper() && !HasSeenSniperRecently()) + { + GetChatter()->SpottedSniper(); + + const float sniperRecentInterval = 20.0f; + m_sawEnemySniperTimer.Start(sniperRecentInterval); + } +#endif + if (CanSeeLooseBomb()) { GetChatter()->SpottedLooseBomb(TheCSBots()->GetLooseBomb()); } - +#ifdef REGAMEDLL_ADD +} +#endif // Scenario interrupts switch (TheCSBots()->GetScenario()) { diff --git a/regamedll/dlls/bot/cs_bot_vision.cpp b/regamedll/dlls/bot/cs_bot_vision.cpp index f7b16acc..6e9d8ddc 100644 --- a/regamedll/dlls/bot/cs_bot_vision.cpp +++ b/regamedll/dlls/bot/cs_bot_vision.cpp @@ -697,6 +697,13 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() m_closestVisibleFriend = nullptr; m_closestVisibleHumanFriend = nullptr; +#ifdef REGAMEDLL_ADD + m_isEnemySniperVisible = false; + CBasePlayer* sniperThreat = NULL; + float sniperThreatRange = 99999999999.9f; + bool sniperThreatIsFacingMe = false; +#endif + float closeFriendRange = 99999999999.9f; float closeHumanFriendRange = 99999999999.9f; @@ -789,6 +796,51 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() Vector d = pev->origin - pPlayer->pev->origin; float distSq = d.LengthSquared(); +#ifdef REGAMEDLL_ADD + if (isSniperRifle(pPlayer->m_pActiveItem)) { + m_isEnemySniperVisible = true; + if (sniperThreat) + { + if (IsPlayerLookingAtMe(pPlayer)) + { + if (sniperThreatIsFacingMe) + { + // several snipers are facing us - keep closest + if (distSq < sniperThreatRange) + { + sniperThreat = pPlayer; + sniperThreatRange = distSq; + sniperThreatIsFacingMe = true; + } + } + else + { + // even if this sniper is farther away, keep it because he's aiming at us + sniperThreat = pPlayer; + sniperThreatRange = distSq; + sniperThreatIsFacingMe = true; + } + } + else + { + // this sniper is not looking at us, only consider it if we dont have a sniper facing us + if (!sniperThreatIsFacingMe && distSq < sniperThreatRange) + { + sniperThreat = pPlayer; + sniperThreatRange = distSq; + } + } + } + else + { + // first sniper we see + sniperThreat = pPlayer; + sniperThreatRange = distSq; + sniperThreatIsFacingMe = IsPlayerLookingAtMe(pPlayer); + } + } +#endif + // maintain set of visible threats, sorted by increasing distance if (threatCount == 0) { @@ -950,6 +1002,23 @@ CBasePlayer *CCSBot::FindMostDangerousThreat() { return currentThreat; } + + // if we are a sniper and we see a sniper threat, attack it unless + // there are other close enemies facing me + if (IsSniper() && sniperThreat) + { + const float closeCombatRange = 500.0f; + + for (t = 0; t < threatCount; ++t) + { + if (threat[t].range < closeCombatRange && IsPlayerLookingAtMe(threat[t].enemy)) + { + return threat[t].enemy; + } + } + + return sniperThreat; + } #endif // otherwise, find the closest threat that without using shield