diff --git a/ext/crlib b/ext/crlib index 37f616f..67733ef 160000 --- a/ext/crlib +++ b/ext/crlib @@ -1 +1 @@ -Subproject commit 37f616f5839faa6cdd40ea22258d564675c45201 +Subproject commit 67733ef6ffd51c538692f311e6cfb26affb3e50e diff --git a/inc/constant.h b/inc/constant.h new file mode 100644 index 0000000..ef351ae --- /dev/null +++ b/inc/constant.h @@ -0,0 +1,449 @@ +// +// YaPB, based on PODBot by Markus Klinge ("CountFloyd"). +// Copyright © YaPB Project Developers . +// +// SPDX-License-Identifier: MIT +// + +#pragma once + +// forwards +class Bot; +class BotGraph; +class BotManager; + +// defines bots tasks +CR_DECLARE_SCOPED_ENUM (Task, + Normal = 0, + Pause, + MoveToPosition, + FollowUser, + PickupItem, + Camp, + PlantBomb, + DefuseBomb, + Attack, + Hunt, + SeekCover, + ThrowExplosive, + ThrowFlashbang, + ThrowSmoke, + DoubleJump, + EscapeFromBomb, + ShootBreakable, + Hide, + Blind, + Spraypaint, + Max +) + +// bot menu ids +CR_DECLARE_SCOPED_ENUM (Menu, + None = 0, + Main, + Features, + Control, + WeaponMode, + Personality, + Difficulty, + TeamSelect, + TerroristSelect, + CTSelect, + TerroristSelectCZ, + CTSelectCZ, + Commands, + NodeMainPage1, + NodeMainPage2, + NodeRadius, + NodeType, + NodeFlag, + NodeAutoPath, + NodePath, + NodeDebug, + CampDirections, + KickPage1, + KickPage2, + KickPage3, + KickPage4 +) + +// bomb say string +CR_DECLARE_SCOPED_ENUM (BombPlantedSay, + ChatSay = cr::bit (1), + Chatter = cr::bit (2) +) + +// chat types id's +CR_DECLARE_SCOPED_ENUM (Chat, + Kill = 0, // id to kill chat array + Dead, // id to dead chat array + Plant, // id to bomb chat array + TeamAttack, // id to team-attack chat array + TeamKill, // id to team-kill chat array + Hello, // id to welcome chat array + NoKeyword, // id to no keyword chat array + Count // number for array +) + +// personalities defines +CR_DECLARE_SCOPED_ENUM (Personality, + Normal = 0, + Rusher, + Careful, + Invalid = -1 +) + +// bot difficulties +CR_DECLARE_SCOPED_ENUM (Difficulty, + Noob, + Easy, + Normal, + Hard, + Expert, + Invalid = -1 +) + +// collision states +CR_DECLARE_SCOPED_ENUM (CollisionState, + Undecided, + Probing, + NoMove, + Jump, + Duck, + StrafeLeft, + StrafeRight +) + +// counter-strike team id's +CR_DECLARE_SCOPED_ENUM (Team, + Terrorist = 0, + CT, + Spectator, + Unassigned, + Invalid = -1 +) + +// item status for StatusIcon message +CR_DECLARE_SCOPED_ENUM (ItemStatus, + Nightvision = cr::bit (0), + DefusalKit = cr::bit (1) +) + +// client flags +CR_DECLARE_SCOPED_ENUM (ClientFlags, + Used = cr::bit (0), + Alive = cr::bit (1), + Admin = cr::bit (2), + Icon = cr::bit (3) +) + +// bot create status +CR_DECLARE_SCOPED_ENUM (BotCreateResult, + Success, + MaxPlayersReached, + GraphError, + TeamStacked +) + +// radio messages +CR_DECLARE_SCOPED_ENUM (Radio, + CoverMe = 1, + YouTakeThePoint = 2, + HoldThisPosition = 3, + RegroupTeam = 4, + FollowMe = 5, + TakingFireNeedAssistance = 6, + GoGoGo = 11, + TeamFallback = 12, + StickTogetherTeam = 13, + GetInPositionAndWaitForGo = 14, + StormTheFront = 15, + ReportInTeam = 16, + RogerThat = 21, + EnemySpotted = 22, + NeedBackup = 23, + SectorClear = 24, + ImInPosition = 25, + ReportingIn = 26, + ShesGonnaBlow = 27, + Negative = 28, + EnemyDown = 29 +) + +// chatter system (extending enum above, messages 30-39 is reserved) +CR_DECLARE_SCOPED_ENUM (Chatter, + SpotTheBomber = 40, + FriendlyFire, + DiePain, + Blind, + GoingToPlantBomb, + RescuingHostages, + GoingToCamp, + HeardNoise, + TeamAttack, + TeamKill, + ReportingIn, + GuardingDroppedC4, + Camping, + PlantingBomb, + DefusingBomb, + InCombat, + SeekingEnemies, + Nothing, + EnemyDown, + UsingHostages, + FoundC4, + WonTheRound, + ScaredEmotion, + HeardTheEnemy, + SniperWarning, + SniperKilled, + VIPSpotted, + GuardingVIPSafety, + GoingToGuardVIPSafety, + QuickWonRound, + OneEnemyLeft, + TwoEnemiesLeft, + ThreeEnemiesLeft, + NoEnemiesLeft, + FoundC4Plant, + WhereIsTheC4, + DefendingBombsite, + BarelyDefused, + NiceShotCommander, + NiceShotPall, + GoingToGuardHostages, + GoingToGuardDroppedC4, + OnMyWay, + LeadOnSir, + PinnedDown, + GottaFindC4, + YouHeardTheMan, + LostCommander, + NewRound, + CoverMe, + BehindSmoke, + BombsiteSecured, + Count +) + +// counter strike weapon classes (types) +CR_DECLARE_SCOPED_ENUM (WeaponType, + None, + Melee, + Pistol, + Shotgun, + ZoomRifle, + Rifle, + SMG, + Sniper, + Heavy +) + +// counter-strike weapon id's +CR_DECLARE_SCOPED_ENUM (Weapon, + P228 = 1, + Shield = 2, + Scout = 3, + Explosive = 4, + XM1014 = 5, + C4 = 6, + MAC10 = 7, + AUG = 8, + Smoke = 9, + Elite = 10, + FiveSeven = 11, + UMP45 = 12, + SG550 = 13, + Galil = 14, + Famas = 15, + USP = 16, + Glock18 = 17, + AWP = 18, + MP5 = 19, + M249 = 20, + M3 = 21, + M4A1 = 22, + TMP = 23, + G3SG1 = 24, + Flashbang = 25, + Deagle = 26, + SG552 = 27, + AK47 = 28, + Knife = 29, + P90 = 30, + Armor = 31, + ArmorHelm = 32, + Defuser = 33 +) + +// buy counts +CR_DECLARE_SCOPED_ENUM (BuyState, + PrimaryWeapon = 0, + ArmorVestHelm, + SecondaryWeapon, + Ammo, + DefusalKit, + Grenades, + NightVision, + Done +) + +// economics limits +CR_DECLARE_SCOPED_ENUM (EcoLimit, + PrimaryGreater = 0, + SmgCTGreater, + SmgTEGreater, + ShotgunGreater, + ShotgunLess, + HeavyGreater, + HeavyLess, + ProstockNormal, + ProstockRusher, + ProstockCareful, + ShieldGreater +) + +// defines for pickup items +CR_DECLARE_SCOPED_ENUM (Pickup, + None = 0, + Weapon, + DroppedC4, + PlantedC4, + Hostage, + Button, + Shield, + DefusalKit, + Items, + AmmoAndKits +) + +// fight style type +CR_DECLARE_SCOPED_ENUM (Fight, + None = 0, + Strafe, + Stay +) + +// dodge type +CR_DECLARE_SCOPED_ENUM (Dodge, + None = 0, + Left, + Right +) + +// reload state +CR_DECLARE_SCOPED_ENUM (Reload, + None = 0, // no reload state currently + Primary, // primary weapon reload state + Secondary // secondary weapon reload state +) + +// collision probes +CR_DECLARE_SCOPED_ENUM (CollisionProbe, uint32_t, + Jump = cr::bit (0), // probe jump when colliding + Duck = cr::bit (1), // probe duck when colliding + Strafe = cr::bit (2) // probe strafing when colliding +) + +// game start messages for counter-strike... +CR_DECLARE_SCOPED_ENUM (BotMsg, + None = 1, + TeamSelect = 2, + ClassSelect = 3, + Buy = 100, + Radio = 200, + Say = 10000, + SayTeam = 10001 +) + +// sensing states +CR_DECLARE_SCOPED_ENUM_TYPE (Sense, uint32_t, + SeeingEnemy = cr::bit (0), // seeing an enemy + HearingEnemy = cr::bit (1), // hearing an enemy + SuspectEnemy = cr::bit (2), // suspect enemy behind obstacle + PickupItem = cr::bit (3), // pickup item nearby + ThrowExplosive = cr::bit (4), // could throw he grenade + ThrowFlashbang = cr::bit (5), // could throw flashbang + ThrowSmoke = cr::bit (6) // could throw smokegrenade +) + +// positions to aim at +CR_DECLARE_SCOPED_ENUM_TYPE (AimFlags, uint32_t, + Nav = cr::bit (0), // aim at nav point + Camp = cr::bit (1), // aim at camp vector + PredictPath = cr::bit (2), // aim at predicted path + LastEnemy = cr::bit (3), // aim at last enemy + Entity = cr::bit (4), // aim at entity like buttons, hostages + Enemy = cr::bit (5), // aim at enemy + Grenade = cr::bit (6), // aim for grenade throw + Override = cr::bit (7), // overrides all others (blinded) + Danger = cr::bit (8) // additional danger flag +) + +// famas/glock burst mode status + m4a1/usp silencer +CR_DECLARE_SCOPED_ENUM (BurstMode, + On = cr::bit (0), + Off = cr::bit (1) +) + +// visibility flags +CR_DECLARE_SCOPED_ENUM (Visibility, + Head = cr::bit (1), + Body = cr::bit (2), + Other = cr::bit (3), + None = 0 +) + +// frustum sides +CR_DECLARE_SCOPED_ENUM (FrustumSide, + Top = 0, + Bottom, + Left, + Right, + Near, + Far, + Num +) + +// some hard-coded desire defines used to override calculated ones +struct TaskPri { + static constexpr auto Normal { 35.0f }; + static constexpr auto Pause { 36.0f }; + static constexpr auto Camp { 37.0f }; + static constexpr auto Spraypaint { 38.0f }; + static constexpr auto FollowUser { 39.0f }; + static constexpr auto MoveToPosition { 50.0f }; + static constexpr auto DefuseBomb { 89.0f }; + static constexpr auto PlantBomb { 89.0f }; + static constexpr auto Attack { 90.0f }; + static constexpr auto SeekCover { 91.0f }; + static constexpr auto Hide { 92.0f }; + static constexpr auto Throw { 99.0f }; + static constexpr auto DoubleJump { 99.0f }; + static constexpr auto Blind { 100.0f }; + static constexpr auto ShootBreakable { 100.0f }; + static constexpr auto EscapeFromBomb { 100.0f }; +}; + +constexpr auto kInfiniteDistance = 9999999.0f; +constexpr auto kGrenadeCheckTime = 0.6f; +constexpr auto kSprayDistance = 260.0f; +constexpr auto kDoubleSprayDistance = kSprayDistance * 2; +constexpr auto kMaxChatterRepeatInterval = 99.0f; + +constexpr auto kInfiniteDistanceLong = static_cast (kInfiniteDistance); +constexpr auto kMaxWeapons = 32; +constexpr auto kNumWeapons = 26; +constexpr auto kMaxCollideMoves = 3; +constexpr auto kGameMaxPlayers = 32; +constexpr auto kGameTeamNum = 2; +constexpr auto kInvalidNodeIndex = -1; +constexpr auto kConfigExtension = "cfg"; + +// weapon masks +constexpr auto kPrimaryWeaponMask = (cr::bit (Weapon::XM1014) | cr::bit (Weapon::M3) | cr::bit (Weapon::MAC10) | cr::bit (Weapon::UMP45) | cr::bit (Weapon::MP5) | cr::bit (Weapon::TMP) | cr::bit (Weapon::P90) | cr::bit (Weapon::AUG) | cr::bit (Weapon::M4A1) | cr::bit (Weapon::SG552) | cr::bit (Weapon::AK47) | cr::bit (Weapon::Scout) | cr::bit (Weapon::SG550) | cr::bit (Weapon::AWP) | cr::bit (Weapon::G3SG1) | cr::bit (Weapon::M249) | cr::bit (Weapon::Famas) | cr::bit (Weapon::Galil)); +constexpr auto kSecondaryWeaponMask = (cr::bit (Weapon::P228) | cr::bit (Weapon::Elite) | cr::bit (Weapon::USP) | cr::bit (Weapon::Glock18) | cr::bit (Weapon::Deagle) | cr::bit (Weapon::FiveSeven)); + +// weapons < 7 are secondary +constexpr auto kPrimaryWeaponMinIndex = 7; diff --git a/inc/engine.h b/inc/engine.h index 3d93303..37d6837 100644 --- a/inc/engine.h +++ b/inc/engine.h @@ -447,7 +447,7 @@ public: return ptr->value; } - const char *str () const { + StringRef str () const { return ptr->string; } diff --git a/inc/graph.h b/inc/graph.h index 874d823..4645d09 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -183,7 +183,7 @@ private: public: SmallArray m_paths {}; - HashMap , EmptyHash > m_hashTable; + HashMap , EmptyHash > m_hashTable {}; String m_graphAuthor {}; String m_graphModified {}; diff --git a/inc/manager.h b/inc/manager.h index 130f81f..adc70fd 100644 --- a/inc/manager.h +++ b/inc/manager.h @@ -144,7 +144,7 @@ public: void resetFilters (); void updateActiveGrenade (); void updateInterestingEntities (); - void captureChatRadio (const char *cmd, const char *arg, edict_t *ent); + void captureChatRadio (StringRef cmd, StringRef arg, edict_t *ent); void notifyBombDefuse (); void execGameEntity (edict_t *ent); void forEach (ForEachBot handler); diff --git a/inc/planner.h b/inc/planner.h index 53a454e..d4dcc8a 100644 --- a/inc/planner.h +++ b/inc/planner.h @@ -242,8 +242,8 @@ public: // the bot path planner class PathPlanner : public Singleton { private: - UniquePtr m_dijkstra; - UniquePtr m_floyd; + UniquePtr m_dijkstra {}; + UniquePtr m_floyd {}; bool m_memoryLimitHit {}; public: diff --git a/inc/yapb.h b/inc/yapb.h index 016c3f5..b12b066 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -18,442 +18,7 @@ using namespace cr; #include #include - -// forwards -class Bot; -class BotGraph; -class BotManager; - -// defines bots tasks -CR_DECLARE_SCOPED_ENUM (Task, - Normal = 0, - Pause, - MoveToPosition, - FollowUser, - PickupItem, - Camp, - PlantBomb, - DefuseBomb, - Attack, - Hunt, - SeekCover, - ThrowExplosive, - ThrowFlashbang, - ThrowSmoke, - DoubleJump, - EscapeFromBomb, - ShootBreakable, - Hide, - Blind, - Spraypaint, - Max -) - -// bot menu ids -CR_DECLARE_SCOPED_ENUM (Menu, - None = 0, - Main, - Features, - Control, - WeaponMode, - Personality, - Difficulty, - TeamSelect, - TerroristSelect, - CTSelect, - TerroristSelectCZ, - CTSelectCZ, - Commands, - NodeMainPage1, - NodeMainPage2, - NodeRadius, - NodeType, - NodeFlag, - NodeAutoPath, - NodePath, - NodeDebug, - CampDirections, - KickPage1, - KickPage2, - KickPage3, - KickPage4 -) - -// bomb say string -CR_DECLARE_SCOPED_ENUM (BombPlantedSay, - ChatSay = cr::bit (1), - Chatter = cr::bit (2) -) - -// chat types id's -CR_DECLARE_SCOPED_ENUM (Chat, - Kill = 0, // id to kill chat array - Dead, // id to dead chat array - Plant, // id to bomb chat array - TeamAttack, // id to team-attack chat array - TeamKill, // id to team-kill chat array - Hello, // id to welcome chat array - NoKeyword, // id to no keyword chat array - Count // number for array -) - -// personalities defines -CR_DECLARE_SCOPED_ENUM (Personality, - Normal = 0, - Rusher, - Careful, - Invalid = -1 -) - -// bot difficulties -CR_DECLARE_SCOPED_ENUM (Difficulty, - Noob, - Easy, - Normal, - Hard, - Expert, - Invalid = -1 -) - -// collision states -CR_DECLARE_SCOPED_ENUM (CollisionState, - Undecided, - Probing, - NoMove, - Jump, - Duck, - StrafeLeft, - StrafeRight -) - -// counter-strike team id's -CR_DECLARE_SCOPED_ENUM (Team, - Terrorist = 0, - CT, - Spectator, - Unassigned, - Invalid = -1 -) - -// item status for StatusIcon message -CR_DECLARE_SCOPED_ENUM (ItemStatus, - Nightvision = cr::bit (0), - DefusalKit = cr::bit (1) -) - -// client flags -CR_DECLARE_SCOPED_ENUM (ClientFlags, - Used = cr::bit (0), - Alive = cr::bit (1), - Admin = cr::bit (2), - Icon = cr::bit (3) -) - -// bot create status -CR_DECLARE_SCOPED_ENUM (BotCreateResult, - Success, - MaxPlayersReached, - GraphError, - TeamStacked -) - -// radio messages -CR_DECLARE_SCOPED_ENUM (Radio, - CoverMe = 1, - YouTakeThePoint = 2, - HoldThisPosition = 3, - RegroupTeam = 4, - FollowMe = 5, - TakingFireNeedAssistance = 6, - GoGoGo = 11, - TeamFallback = 12, - StickTogetherTeam = 13, - GetInPositionAndWaitForGo = 14, - StormTheFront = 15, - ReportInTeam = 16, - RogerThat = 21, - EnemySpotted = 22, - NeedBackup = 23, - SectorClear = 24, - ImInPosition = 25, - ReportingIn = 26, - ShesGonnaBlow = 27, - Negative = 28, - EnemyDown = 29 -) - -// chatter system (extending enum above, messages 30-39 is reserved) -CR_DECLARE_SCOPED_ENUM (Chatter, - SpotTheBomber = 40, - FriendlyFire, - DiePain, - Blind, - GoingToPlantBomb, - RescuingHostages, - GoingToCamp, - HeardNoise, - TeamAttack, - TeamKill, - ReportingIn, - GuardingDroppedC4, - Camping, - PlantingBomb, - DefusingBomb, - InCombat, - SeekingEnemies, - Nothing, - EnemyDown, - UsingHostages, - FoundC4, - WonTheRound, - ScaredEmotion, - HeardTheEnemy, - SniperWarning, - SniperKilled, - VIPSpotted, - GuardingVIPSafety, - GoingToGuardVIPSafety, - QuickWonRound, - OneEnemyLeft, - TwoEnemiesLeft, - ThreeEnemiesLeft, - NoEnemiesLeft, - FoundC4Plant, - WhereIsTheC4, - DefendingBombsite, - BarelyDefused, - NiceShotCommander, - NiceShotPall, - GoingToGuardHostages, - GoingToGuardDroppedC4, - OnMyWay, - LeadOnSir, - PinnedDown, - GottaFindC4, - YouHeardTheMan, - LostCommander, - NewRound, - CoverMe, - BehindSmoke, - BombsiteSecured, - Count -) - -// counter strike weapon classes (types) -CR_DECLARE_SCOPED_ENUM (WeaponType, - None, - Melee, - Pistol, - Shotgun, - ZoomRifle, - Rifle, - SMG, - Sniper, - Heavy -) - -// counter-strike weapon id's -CR_DECLARE_SCOPED_ENUM (Weapon, - P228 = 1, - Shield = 2, - Scout = 3, - Explosive = 4, - XM1014 = 5, - C4 = 6, - MAC10 = 7, - AUG = 8, - Smoke = 9, - Elite = 10, - FiveSeven = 11, - UMP45 = 12, - SG550 = 13, - Galil = 14, - Famas = 15, - USP = 16, - Glock18 = 17, - AWP = 18, - MP5 = 19, - M249 = 20, - M3 = 21, - M4A1 = 22, - TMP = 23, - G3SG1 = 24, - Flashbang = 25, - Deagle = 26, - SG552 = 27, - AK47 = 28, - Knife = 29, - P90 = 30, - Armor = 31, - ArmorHelm = 32, - Defuser = 33 -) - -// buy counts -CR_DECLARE_SCOPED_ENUM (BuyState, - PrimaryWeapon = 0, - ArmorVestHelm, - SecondaryWeapon, - Ammo, - DefusalKit, - Grenades, - NightVision, - Done -) - -// economics limits -CR_DECLARE_SCOPED_ENUM (EcoLimit, - PrimaryGreater = 0, - SmgCTGreater, - SmgTEGreater, - ShotgunGreater, - ShotgunLess, - HeavyGreater, - HeavyLess, - ProstockNormal, - ProstockRusher, - ProstockCareful, - ShieldGreater -) - -// defines for pickup items -CR_DECLARE_SCOPED_ENUM (Pickup, - None = 0, - Weapon, - DroppedC4, - PlantedC4, - Hostage, - Button, - Shield, - DefusalKit -) - -// fight style type -CR_DECLARE_SCOPED_ENUM (Fight, - None = 0, - Strafe, - Stay -) - -// dodge type -CR_DECLARE_SCOPED_ENUM (Dodge, - None = 0, - Left, - Right -) - -// reload state -CR_DECLARE_SCOPED_ENUM (Reload, - None = 0, // no reload state currently - Primary, // primary weapon reload state - Secondary // secondary weapon reload state -) - -// collision probes -CR_DECLARE_SCOPED_ENUM (CollisionProbe, uint32_t, - Jump = cr::bit (0), // probe jump when colliding - Duck = cr::bit (1), // probe duck when colliding - Strafe = cr::bit (2) // probe strafing when colliding -) - -// game start messages for counter-strike... -CR_DECLARE_SCOPED_ENUM (BotMsg, - None = 1, - TeamSelect = 2, - ClassSelect = 3, - Buy = 100, - Radio = 200, - Say = 10000, - SayTeam = 10001 -) - -// sensing states -CR_DECLARE_SCOPED_ENUM_TYPE (Sense, uint32_t, - SeeingEnemy = cr::bit (0), // seeing an enemy - HearingEnemy = cr::bit (1), // hearing an enemy - SuspectEnemy = cr::bit (2), // suspect enemy behind obstacle - PickupItem = cr::bit (3), // pickup item nearby - ThrowExplosive = cr::bit (4), // could throw he grenade - ThrowFlashbang = cr::bit (5), // could throw flashbang - ThrowSmoke = cr::bit (6) // could throw smokegrenade -) - -// positions to aim at -CR_DECLARE_SCOPED_ENUM_TYPE (AimFlags, uint32_t, - Nav = cr::bit (0), // aim at nav point - Camp = cr::bit (1), // aim at camp vector - PredictPath = cr::bit (2), // aim at predicted path - LastEnemy = cr::bit (3), // aim at last enemy - Entity = cr::bit (4), // aim at entity like buttons, hostages - Enemy = cr::bit (5), // aim at enemy - Grenade = cr::bit (6), // aim for grenade throw - Override = cr::bit (7), // overrides all others (blinded) - Danger = cr::bit (8) // additional danger flag -) - -// famas/glock burst mode status + m4a1/usp silencer -CR_DECLARE_SCOPED_ENUM (BurstMode, - On = cr::bit (0), - Off = cr::bit (1) -) - -// visibility flags -CR_DECLARE_SCOPED_ENUM (Visibility, - Head = cr::bit (1), - Body = cr::bit (2), - Other = cr::bit (3), - None = 0 -) - -// frustum sides -CR_DECLARE_SCOPED_ENUM (FrustumSide, - Top = 0, - Bottom, - Left, - Right, - Near, - Far, - Num -) - -// some hard-coded desire defines used to override calculated ones -struct TaskPri { - static constexpr auto Normal { 35.0f }; - static constexpr auto Pause { 36.0f }; - static constexpr auto Camp { 37.0f }; - static constexpr auto Spraypaint { 38.0f }; - static constexpr auto FollowUser { 39.0f }; - static constexpr auto MoveToPosition { 50.0f }; - static constexpr auto DefuseBomb { 89.0f }; - static constexpr auto PlantBomb { 89.0f }; - static constexpr auto Attack { 90.0f }; - static constexpr auto SeekCover { 91.0f }; - static constexpr auto Hide { 92.0f }; - static constexpr auto Throw { 99.0f }; - static constexpr auto DoubleJump { 99.0f }; - static constexpr auto Blind { 100.0f }; - static constexpr auto ShootBreakable { 100.0f }; - static constexpr auto EscapeFromBomb { 100.0f }; -}; - -constexpr auto kInfiniteDistance = 9999999.0f; -constexpr auto kGrenadeCheckTime = 0.6f; -constexpr auto kSprayDistance = 260.0f; -constexpr auto kDoubleSprayDistance = kSprayDistance * 2; -constexpr auto kMaxChatterRepeatInterval = 99.0f; - -constexpr auto kInfiniteDistanceLong = static_cast (kInfiniteDistance); -constexpr auto kMaxWeapons = 32; -constexpr auto kNumWeapons = 26; -constexpr auto kMaxCollideMoves = 3; -constexpr auto kGameMaxPlayers = 32; -constexpr auto kGameTeamNum = 2; -constexpr auto kInvalidNodeIndex = -1; -constexpr auto kConfigExtension = "cfg"; - -// weapon masks -constexpr auto kPrimaryWeaponMask = (cr::bit (Weapon::XM1014) | cr::bit (Weapon::M3) | cr::bit (Weapon::MAC10) | cr::bit (Weapon::UMP45) | cr::bit (Weapon::MP5) | cr::bit (Weapon::TMP) | cr::bit (Weapon::P90) | cr::bit (Weapon::AUG) | cr::bit (Weapon::M4A1) | cr::bit (Weapon::SG552) | cr::bit (Weapon::AK47) | cr::bit (Weapon::Scout) | cr::bit (Weapon::SG550) | cr::bit (Weapon::AWP) | cr::bit (Weapon::G3SG1) | cr::bit (Weapon::M249) | cr::bit (Weapon::Famas) | cr::bit (Weapon::Galil)); -constexpr auto kSecondaryWeaponMask = (cr::bit (Weapon::P228) | cr::bit (Weapon::Elite) | cr::bit (Weapon::USP) | cr::bit (Weapon::Glock18) | cr::bit (Weapon::Deagle) | cr::bit (Weapon::FiveSeven)); +#include // links keywords and replies together struct ChatKeywords { @@ -774,7 +339,7 @@ private: FindPath m_pathType {}; // which pathfinder to use uint8_t m_enemyParts {}; // visibility flags uint16_t m_modelMask {}; // model mask bits - UniquePtr m_planner; + UniquePtr m_planner {}; edict_t *m_pickupItem {}; // pointer to entity of item to use/pickup edict_t *m_itemIgnore {}; // pointer to entity to ignore for pickup @@ -856,7 +421,7 @@ private: bool advanceMovement (); bool isBombDefusing (const Vector &bombOrigin); bool isOccupiedNode (int index, bool needZeroVelocity = false); - bool seesItem (const Vector &dest, const char *classname); + bool seesItem (const Vector &dest, StringRef classname); bool lastEnemyShootable (); bool rateGroundWeapon (edict_t *ent); bool reactOnEnemy (); diff --git a/src/botlib.cpp b/src/botlib.cpp index 460151e..f000a95 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -35,6 +35,7 @@ ConVar cv_restricted_weapons ("yb_restricted_weapons", "", "Specifies semicolon ConVar cv_attack_monsters ("yb_attack_monsters", "0", "Allows or disallows bots to attack monsters."); ConVar cv_pickup_custom_items ("yb_pickup_custom_items", "0", "Allows or disallows bots to pickup custom items."); +ConVar cv_pickup_ammo_and_kits ("yb_pickup_ammo_and_kits", "0", "Allows bots pickup mod items like ammo, health kits and suits."); ConVar cv_pickup_best ("yb_pickup_best", "1", "Allows or disallows bots to pickup best weapons."); ConVar cv_ignore_objectives ("yb_ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages."); ConVar cv_random_knife_attacks ("yb_random_knife_attacks", "1", "Allows or disallows the ability for random knife attacks when bot is rushing and no enemy is nearby."); @@ -93,9 +94,9 @@ void Bot::avoidGrenades () { if (!seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) > pev->fov * 0.5f) { continue; } - auto model = pent->v.model.chars (9); + auto model = pent->v.model.str (9); - if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == Difficulty::Expert && cr::strcmp (model, "flashbang.mdl") == 0) { + if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == Difficulty::Expert && model == "flashbang.mdl") { // don't look at flash bang if (!(m_states & Sense::SeeingEnemy)) { m_lookAt.y = cr::wrapAngle ((game.getEntityOrigin (pent) - getEyesPos ()).angles ().y + 180.0f); @@ -104,7 +105,7 @@ void Bot::avoidGrenades () { m_preventFlashing = game.time () + rg.get (1.0f, 2.0f); } } - else if (game.isNullEntity (m_avoidGrenade) && cr::strcmp (model, "hegrenade.mdl") == 0) { + else if (game.isNullEntity (m_avoidGrenade) && model == "hegrenade.mdl") { if (game.getTeam (pent->v.owner) == m_team || pent->v.owner == ent ()) { continue; } @@ -127,7 +128,7 @@ void Bot::avoidGrenades () { } } } - else if ((pent->v.flags & FL_ONGROUND) && cr::strcmp (model, "smokegrenade.mdl") == 0) { + else if ((pent->v.flags & FL_ONGROUND) && model == "smokegrenade.mdl") { if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) { float distance = pent->v.origin.distance (pev->origin); @@ -270,8 +271,42 @@ void Bot::setIdealReactionTimers (bool actual) { void Bot::updatePickups () { // this function finds Items to collect or use in the near of a bot - // don't try to pickup anything while on ladder or trying to escape from bomb... - if (m_isCreature || (m_states & Sense::SeeingEnemy) || isOnLadder () || getCurrentTaskId () == Task::EscapeFromBomb || !cv_pickup_best.bool_ () || cv_jasonmode.bool_ () || !bots.hasInterestingEntities ()) { + // utility to check if this function is currently doesn't allowed to run + const auto isPickupBlocked = [&] () -> bool { + // zombie or chickens not allowed to pickup anything + if (m_isCreature) { + return true; + } + + // seeing enemy now, not good time to pickup anything + else if (m_states & Sense::SeeingEnemy) { + return true; + } + + // bots on ladder don't have to search anything + else if (isOnLadder ()) { + return true; + } + + // we're escaping from the bomb, don't bother! + else if (getCurrentTaskId () == Task::EscapeFromBomb) { + return true; + } + + // knife mode is in progress ? + else if (cv_jasonmode.bool_ ()) { + return true; + } + + // no interesting entities, how ? + else if (!bots.hasInterestingEntities ()) { + return true; + } + return false; + }; + + // we're not allowed to run now + if (isPickupBlocked ()) { m_pickupItem = nullptr; m_pickupType = Pickup::None; @@ -299,7 +334,7 @@ void Bot::updatePickups () { } if (ent == pickupItem) { - if (seesItem (origin, ent->v.classname.chars ())) { + if (seesItem (origin, ent->v.classname.str ())) { itemExists = true; } break; @@ -337,38 +372,106 @@ void Bot::updatePickups () { continue; } - auto classname = ent->v.classname.chars (); - auto model = ent->v.model.chars (9); + auto classname = ent->v.classname.str (); + auto model = ent->v.model.str (9); // check if line of sight to object is not blocked (i.e. visible) if (seesItem (origin, classname)) { - if (cr::strncmp ("hostage_entity", classname, 14) == 0 || cr::strncmp ("monster_scientist", classname, 17) == 0) { + const bool isWeaponBox = classname.startsWith ("weaponbox"); + + const bool isDemolitionMap = game.mapIs (MapFlags::Demolition); + const bool isHostageRescueMap = game.mapIs (MapFlags::HostageRescue); + const bool isCSDM = game.is (GameFlags::CSDM); + + if (isHostageRescueMap && (classname.startsWith ("hostage_entity") || classname.startsWith ("monster_scientist"))) { allowPickup = true; pickupType = Pickup::Hostage; } - else if (cr::strncmp ("weaponbox", classname, 9) == 0 && cr::strcmp (model, "backpack.mdl") == 0) { + else if (isDemolitionMap && isWeaponBox && model == "backpack.mdl") { allowPickup = true; pickupType = Pickup::DroppedC4; } - else if ((cr::strncmp ("weaponbox", classname, 9) == 0 || cr::strncmp ("armoury_entity", classname, 14) == 0 || cr::strncmp ("csdm", classname, 4) == 0) && !m_isUsingGrenade) { + else if ((isWeaponBox || classname.startsWith ("armoury_entity") || (isCSDM && classname.startsWith ("csdm"))) && !m_isUsingGrenade) { allowPickup = true; pickupType = Pickup::Weapon; + + if (cv_pickup_ammo_and_kits.bool_ ()) { + int primaryWeaponCarried = bestPrimaryCarried (); + int secondaryWeaponCarried = bestSecondaryCarried (); + + const auto &config = conf.getWeapons (); + const auto &primary = config[primaryWeaponCarried]; + const auto &secondary = config[secondaryWeaponCarried]; + + const auto &primaryProp = conf.getWeaponProp (primary.id); + const auto &secondaryProp = conf.getWeaponProp (secondary.id); + + if (secondaryWeaponCarried < kPrimaryWeaponMinIndex && (getAmmo (secondary.id) > 0.3 * secondaryProp.ammo1Max) && model == "357ammobox.mdl") { + allowPickup = false; + } + else if (!m_isVIP && primaryWeaponCarried >= kPrimaryWeaponMinIndex && (getAmmo (primary.id) > 0.3 * primaryProp.ammo1Max) && !m_isUsingGrenade && !hasShield ()) { + auto weaponType = conf.getWeaponType (primaryWeaponCarried); + + const bool isSniperRifle = weaponType == WeaponType::Sniper; + const bool isSubmachine = weaponType == WeaponType::SMG; + const bool isShotgun = weaponType == WeaponType::Shotgun; + const bool isRifle = weaponType == WeaponType::Rifle || weaponType == WeaponType::ZoomRifle; + + if (!isRifle && model == "9mmarclip.mdl") { + allowPickup = false; + } + else if (!isShotgun && model == "shotbox.mdl") { + allowPickup = false; + } + else if (!isSubmachine && model == "9mmclip.mdl") { + allowPickup = false; + } + else if (!isSniperRifle && model == "crossbow_clip.mdl") { + allowPickup = false; + } + else if (primaryWeaponCarried != Weapon::M249 && model == "chainammo.mdl") { + allowPickup = false; + } + } + else if (m_healthValue >= 100.0f && model == "medkit.mdl") { + allowPickup = false; + } + else if (pev->armorvalue >= 100.0f && (model == "kevlar.mdl"|| model == "battery.mdl" || model == "assault.mdl")) { + allowPickup = false; + } + + if (allowPickup) { + pickupType = Pickup::AmmoAndKits; + } + } + + // weapon replacement is not allowed + if (!cv_pickup_best.bool_ ()) { + allowPickup = false; + pickupType = Pickup::None; + } } - else if (cr::strncmp ("weapon_shield", classname, 13) == 0 && !m_isUsingGrenade) { + else if (classname.startsWith ("weapon_shield") && !m_isUsingGrenade) { allowPickup = true; pickupType = Pickup::Shield; + + // weapon replacement is not allowed + if (!cv_pickup_best.bool_ ()) { + allowPickup = false; + pickupType = Pickup::None; + } } - else if (cr::strncmp ("item_thighpack", classname, 14) == 0 && m_team == Team::CT && !m_hasDefuser) { + else if (isDemolitionMap && m_team == Team::CT && !m_hasDefuser && classname.startsWith ("item_thighpack")) { allowPickup = true; pickupType = Pickup::DefusalKit; } - else if (cr::strncmp ("grenade", classname, 7) == 0 && conf.getBombModelName () == model) { + else if (isDemolitionMap && classname.startsWith ("grenade") && conf.getBombModelName () == model) { allowPickup = true; pickupType = Pickup::PlantedC4; } - else if (cv_pickup_custom_items.bool_ () && util.isItem (ent) && cr::strncmp ("item_thighpack", classname, 14) != 0) { + else if (cv_pickup_custom_items.bool_ () && util.isItem (ent) && !classname.startsWith ("item_thighpack")) { allowPickup = true; - pickupType = Pickup::None; + pickupType = Pickup::Items; } } @@ -377,59 +480,19 @@ void Bot::updatePickups () { // found weapon on ground? if (pickupType == Pickup::Weapon) { - int primaryWeaponCarried = bestPrimaryCarried (); - int secondaryWeaponCarried = bestSecondaryCarried (); - - const auto &config = conf.getWeapons (); - const auto &primary = config[primaryWeaponCarried]; - const auto &secondary = config[secondaryWeaponCarried]; - - const auto &primaryProp = conf.getWeaponProp (primary.id); - const auto &secondaryProp = conf.getWeaponProp (secondary.id); - - if (secondaryWeaponCarried < 7 && (m_ammo[secondary.id] > 0.3 * secondaryProp.ammo1Max) && cr::strcmp (model, "w_357ammobox.mdl") == 0) { + if (m_isVIP) { allowPickup = false; } - else if (!m_isVIP && primaryWeaponCarried >= 7 && (m_ammo[primary.id] > 0.3 * primaryProp.ammo1Max) && cr::strncmp (model, "w_", 2) == 0) { - auto weaponType = conf.getWeaponType (primaryWeaponCarried); - - const bool isSniperRifle = weaponType == WeaponType::Sniper; - const bool isSubmachine = weaponType == WeaponType::SMG; - const bool isShotgun = weaponType == WeaponType::Shotgun; - const bool isRifle = weaponType == WeaponType::Rifle || weaponType == WeaponType::ZoomRifle; - - if (!isRifle && cr::strcmp (model, "w_9mmarclip.mdl") == 0) { - allowPickup = false; - } - else if (!isShotgun && cr::strcmp (model, "w_shotbox.mdl") == 0) { - allowPickup = false; - } - else if (!isSubmachine && cr::strcmp (model, "w_9mmclip.mdl") == 0) { - allowPickup = false; - } - else if (!isSniperRifle && cr::strcmp (model, "w_crossbow_clip.mdl") == 0) { - allowPickup = false; - } - else if (primaryWeaponCarried != Weapon::M249 && cr::strcmp (model, "w_chainammo.mdl") == 0) { - allowPickup = false; - } - } - else if (m_isVIP || !rateGroundWeapon (ent)) { + else if (!rateGroundWeapon (ent)) { allowPickup = false; } - else if (m_healthValue >= 100.0f && cr::strcmp (model, "medkit.mdl") == 0) { + else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && model == "flashbang.mdl") { allowPickup = false; } - else if (pev->armorvalue >= 100.0f && (cr::strcmp (model, "kevlar.mdl") == 0 || cr::strcmp (model, "battery.mdl") == 0)) { + else if ((pev->weapons & cr::bit (Weapon::Explosive)) && model == "hegrenade.mdl") { allowPickup = false; } - else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && cr::strcmp (model, "flashbang.mdl") == 0) { - allowPickup = false; - } - else if ((pev->weapons & cr::bit (Weapon::Explosive)) && cr::strcmp (model, "hegrenade.mdl") == 0) { - allowPickup = false; - } - else if ((pev->weapons & cr::bit (Weapon::Smoke)) && cr::strcmp (model, "smokegrenade.mdl") == 0) { + else if ((pev->weapons & cr::bit (Weapon::Smoke)) && model == "smokegrenade.mdl") { allowPickup = false; } } @@ -982,10 +1045,12 @@ void Bot::checkMsgQueue () { bool Bot::isWeaponRestricted (int weaponIndex) { // this function checks for weapon restrictions. - if (strings.isEmpty (cv_restricted_weapons.str ())) { + auto val = cv_restricted_weapons.str (); + + if (val.empty ()) { return isWeaponRestrictedAMX (weaponIndex); // no banned weapons } - const auto &bannedWeapons = String (cv_restricted_weapons.str ()).split (";"); + const auto &bannedWeapons = val.split (";"); const auto &alias = util.weaponIdToAlias (weaponIndex); for (const auto &ban : bannedWeapons) { @@ -1314,7 +1379,7 @@ void Bot::buyStuff () { break; case BuyState::SecondaryWeapon: // if bot has still some money, buy a better secondary weapon - if (isPistolMode || (isFirstRound && hasDefaultPistols && rg.chance (50)) || (hasDefaultPistols && bots.getLastWinner () == m_team && m_moneyAmount > rg.get (2000, 3000)) || (hasPrimaryWeapon () && hasDefaultPistols && m_moneyAmount > rg.get (7500, 9000))) { + if (isPistolMode || (isFirstRound && hasDefaultPistols && rg.chance (60)) || (hasDefaultPistols && bots.getLastWinner () == m_team && m_moneyAmount > rg.get (2000, 3000)) || (hasPrimaryWeapon () && hasDefaultPistols && m_moneyAmount > rg.get (7500, 9000))) { do { pref--; @@ -1380,18 +1445,18 @@ void Bot::buyStuff () { case BuyState::Ammo: // buy enough primary & secondary ammo (do not check for money here) - for (int i = 0; i <= 5; ++i) { + for (int i = 0; i < 7; ++i) { issueCommand ("buyammo%d", rg.get (1, 2)); // simulate human } - // buy enough secondary ammo + // buy enough ammo if (hasPrimaryWeapon ()) { + issueCommand ("buy;menuselect 6"); + } + else { issueCommand ("buy;menuselect 7"); } - // buy enough primary ammo - issueCommand ("buy;menuselect 6"); - // try to reload secondary weapon if (m_reloadState != Reload::Primary) { m_reloadState = Reload::Secondary; @@ -3023,7 +3088,7 @@ void Bot::showDebugOverlay () { static float timeDebugUpdate = 0.0f; static int index = kInvalidNodeIndex, goal = kInvalidNodeIndex, taskID = 0; - static HashMap tasks { + static HashMap tasks { { Task::Normal, "Normal" }, { Task::Pause, "Pause" }, { Task::MoveToPosition, "Move" }, @@ -3046,13 +3111,13 @@ void Bot::showDebugOverlay () { { Task::Spraypaint, "Spray" } }; - static HashMap personalities { + static HashMap personalities { { Personality::Rusher, "Rusher" }, { Personality::Normal, "Normal" }, { Personality::Careful, "Careful" } }; - static HashMap flags { + static HashMap flags { { AimFlags::Nav, "Nav" }, { AimFlags::Camp, "Camp" }, { AimFlags::PredictPath, "Predict" }, @@ -3080,15 +3145,15 @@ void Bot::showDebugOverlay () { String enemy = "(none)"; if (!game.isNullEntity (m_enemy)) { - enemy = m_enemy->v.netname.chars (); + enemy = m_enemy->v.netname.str (); } else if (!game.isNullEntity (m_lastEnemy)) { - enemy.assignf ("%s (L)", m_lastEnemy->v.netname.chars ()); + enemy.assignf ("%s (L)", m_lastEnemy->v.netname.str ()); } String pickup = "(none)"; if (!game.isNullEntity (m_pickupItem)) { - pickup = m_pickupItem->v.classname.chars (); + pickup = m_pickupItem->v.classname.str (); } String aimFlags; @@ -3102,7 +3167,7 @@ void Bot::showDebugOverlay () { auto weapon = util.weaponIdToAlias (m_currentWeapon); String debugData; - debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s Terrain=%s Stuck=%s\n", pev->netname.chars (), m_healthValue, pev->armorvalue, taskID, tasks[taskID], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality], boolValue (m_checkTerrain), boolValue (m_isStuck)); + debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s Terrain=%s Stuck=%s\n", pev->netname.str (), m_healthValue, pev->armorvalue, taskID, tasks[taskID], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality], boolValue (m_checkTerrain), boolValue (m_isStuck)); MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, overlayEntity) .writeByte (TE_TEXTMESSAGE) @@ -3378,7 +3443,7 @@ void Bot::startDoubleJump (edict_t *ent) { m_doubleJumpEntity = ent; startTask (Task::DoubleJump, TaskPri::DoubleJump, kInvalidNodeIndex, game.time (), true); - sendToChat (strings.format ("Ok %s, i will help you!", ent->v.netname.chars ()), true); + sendToChat (strings.format ("Ok %s, i will help you!", ent->v.netname.str ()), true); } void Bot::sendBotToOrigin (const Vector &origin) { @@ -3410,7 +3475,7 @@ void Bot::debugMsgInternal (const char *str) { return; } String printBuf; - printBuf.assignf ("%s: %s", pev->netname.chars (), str); + printBuf.assignf ("%s: %s", pev->netname.str (), str); bool playMessage = false; @@ -3778,7 +3843,7 @@ void Bot::refreshModelName (char *infobuffer) { union ModelTest { char model[2]; uint16_t mask; - ModelTest (StringRef m) : model { m[0], m[1] } {}; + ModelTest (StringRef m) : model { m[0], m[1] } {} } modelTest { modelName }; // assign our model mask (tests against model done every bot update) diff --git a/src/chatlib.cpp b/src/chatlib.cpp index 9a258bb..2ca175d 100644 --- a/src/chatlib.cpp +++ b/src/chatlib.cpp @@ -44,14 +44,14 @@ void BotSupport::humanizePlayerName (String &playerName) { } // sometimes switch name to lower characters, only valid for the english languge - if (rg.chance (8) && cr::strcmp (cv_language.str (), "en") == 0) { + if (rg.chance (8) && cv_language.str () == "en") { playerName.lowercase (); } } void BotSupport::addChatErrors (String &line) { // sometimes switch name to lower characters, only valid for the english languge - if (rg.chance (8) && cr::strcmp (cv_language.str (), "en") == 0) { + if (rg.chance (8) && cv_language.str () == "en") { line.lowercase (); } auto length = static_cast (line.length ()); diff --git a/src/combat.cpp b/src/combat.cpp index cb68525..d081ebf 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -454,7 +454,7 @@ Vector Bot::getBodyOffsetError (float distance) { } if (m_aimErrorTime < game.time ()) { - const float hitError = distance / (cr::clamp (m_difficulty, 1, 4) * 1000.0f); + const float hitError = distance / (cr::clamp (static_cast (m_difficulty), 1.0f, 4.0f) * 1000.0f); auto &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins; m_aimLastError = Vector (rg.get (mins.x * hitError, maxs.x * hitError), rg.get (mins.y * hitError, maxs.y * hitError), rg.get (mins.z * hitError, maxs.z * hitError)); @@ -1332,7 +1332,7 @@ bool Bot::hasSecondaryWeapon () { bool Bot::hasShield () { // this function returns true, if bot has a tactical shield - return cr::strncmp (pev->viewmodel.chars (14), "v_shield_", 9) == 0; + return pev->viewmodel.str (14).startsWith ("v_shield_"); } bool Bot::isShieldDrawn () { @@ -1352,7 +1352,7 @@ bool Bot::isEnemyBehindShield (edict_t *enemy) { } // check if enemy has shield and this shield is drawn - if ((enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7) && cr::strncmp (enemy->v.viewmodel.chars (14), "v_shield_", 9) == 0) { + if ((enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7) && enemy->v.viewmodel.str (14).startsWith ("v_shield_")) { if (util.isInViewCone (pev->origin, enemy)) { return true; } @@ -1443,7 +1443,7 @@ int Bot::bestSecondaryCarried () { for (int i = 0; i < kNumWeapons; ++i) { int id = tab[*pref].id; - if ((weapons & cr::bit (tab[*pref].id)) && (id == Weapon::USP || id == Weapon::Glock18 || id == Weapon::Deagle || id == Weapon::P228 || id == Weapon::Elite || id == Weapon::FiveSeven)) { + if ((weapons & cr::bit (tab[*pref].id)) && conf.getWeaponType (id) == WeaponType::Pistol) { weaponIndex = i; break; } @@ -1468,21 +1468,22 @@ int Bot::bestGrenadeCarried () { bool Bot::rateGroundWeapon (edict_t *ent) { // this function compares weapons on the ground to the one the bot is using + // weapon rating blocked, due to we picked up not-preferred weapon some time ago, because out of ammo int groundIndex = 0; const int *pref = conf.getWeaponPrefs (m_personality); auto tab = conf.getRawWeapons (); for (int i = 0; i < kNumWeapons; ++i) { - if (cr::strcmp (tab[*pref].model, ent->v.model.chars (9)) == 0) { + if (ent->v.model.str (9) == tab[*pref].model) { groundIndex = i; break; } pref++; } - int hasWeapon = 0; + auto hasWeapon = 0; - if (groundIndex < 7) { + if (groundIndex < kPrimaryWeaponMinIndex) { hasWeapon = bestSecondaryCarried (); } else { @@ -1521,7 +1522,7 @@ void Bot::selectBestWeapon () { while (tab[selectIndex].id) { // is the bot NOT carrying this weapon? if (!(pev->weapons & cr::bit (tab[selectIndex].id))) { - selectIndex++; // skip to next weapon + ++selectIndex; // skip to next weapon continue; } @@ -1541,7 +1542,7 @@ void Bot::selectBestWeapon () { if (ammoLeft) { chosenWeaponIndex = selectIndex; } - selectIndex++; + ++selectIndex; } chosenWeaponIndex %= kNumWeapons + 1; diff --git a/src/config.cpp b/src/config.cpp index 71e363e..824a1a8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -106,8 +106,12 @@ void BotConfig::loadMainConfig (bool isFirstLoad) { } // bind the correct menu key for bot menu... - if (!game.isDedicated () && !strings.isEmpty (cv_bind_menu_key.str ())) { - game.serverCommand ("bind \"%s\" \"yb menu\"", cv_bind_menu_key.str ()); + if (!game.isDedicated ()) { + auto val = cv_bind_menu_key.str (); + + if (!val.empty ()) { + game.serverCommand ("bind \"%s\" \"yb menu\"", val); + } } // disable logger if requested @@ -500,7 +504,7 @@ void BotConfig::loadLanguageConfig () { } file.close (); } - else if (cr::strcmp (cv_language.str (), "en") != 0) { + else if (cv_language.str () != "en") { logger.error ("Couldn't load language configuration"); } } @@ -834,7 +838,7 @@ bool BotConfig::openConfig (StringRef fileName, StringRef errorIfNotExists, MemF auto configDir = strings.joinPath (folders.addons, folders.bot, folders.config); if (languageDependant) { - if (fileName.startsWith ("lang") && cr::strcmp (cv_language.str (), "en") == 0) { + if (fileName.startsWith ("lang") && cv_language.str () == "en") { return false; } auto langConfig = strings.joinPath (configDir, folders.lang, strings.format ("%s_%s.%s", cv_language.str (), fileName, kConfigExtension)); diff --git a/src/control.cpp b/src/control.cpp index 10ce606..f042b4b 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -218,9 +218,10 @@ int BotControl::cmdCvars () { if (!isSave && !match.empty () && !strstr (cvar.reg.name, match.chars ())) { continue; } + auto val = cvar.self->str (); // float value ? - bool isFloat = !strings.isEmpty (cvar.self->str ()) && strchr (cvar.self->str (), '.'); + bool isFloat = !val.empty () && val.find (".") != StringRef::InvalidIndex; if (isSave) { cfg.puts ("//\n"); diff --git a/src/engine.cpp b/src/engine.cpp index 8a072fa..80bcd99 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -94,12 +94,12 @@ void Game::levelInitialize (edict_t *entities, int max) { if (!ent || ent->v.classname == 0) { continue; } - auto classname = ent->v.classname.chars (); + auto classname = ent->v.classname.str (); - if (cr::strcmp (classname, "worldspawn") == 0) { + if (classname == "worldspawn") { m_startEntity = ent; } - else if (cr::strcmp (classname, "player_weaponstrip") == 0) { + else if (classname == "player_weaponstrip") { if (is (GameFlags::Legacy) && strings.isEmpty (ent->v.target.chars ())) { ent->v.target = ent->v.targetname = engfuncs.pfnAllocString ("fake"); } @@ -107,30 +107,30 @@ void Game::levelInitialize (edict_t *entities, int max) { engfuncs.pfnRemoveEntity (ent); } } - else if (cr::strcmp (classname, "info_player_start") == 0 || cr::strcmp (classname, "info_vip_start") == 0) { + else if (classname == "info_player_start" || classname == "info_vip_start") { ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency ent->v.renderamt = 127; // set its transparency amount ent->v.effects |= EF_NODRAW; ++m_spawnCount[Team::CT]; } - else if (cr::strcmp (classname, "info_player_deathmatch") == 0) { + else if (classname == "info_player_deathmatch") { ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency ent->v.renderamt = 127; // set its transparency amount ent->v.effects |= EF_NODRAW; ++m_spawnCount[Team::Terrorist]; } - else if (cr::strcmp (classname, "func_vip_safetyzone") == 0 || cr::strcmp (classname, "info_vip_safetyzone") == 0) { + else if (classname == "func_vip_safetyzone" || classname == "info_vip_safetyzone") { m_mapFlags |= MapFlags::Assassination; // assassination map } - else if (cr::strcmp (classname, "hostage_entity") == 0 || cr::strcmp (classname, "monster_scientist") == 0) { + else if (classname == "hostage_entity" || classname == "monster_scientist") { m_mapFlags |= MapFlags::HostageRescue; // rescue map } - else if (cr::strcmp (classname, "func_bomb_target") == 0 || cr::strcmp (classname, "info_bomb_target") == 0) { + else if (classname == "func_bomb_target" || classname == "info_bomb_target") { m_mapFlags |= MapFlags::Demolition; // defusion map } - else if (cr::strcmp (classname, "func_escapezone") == 0) { + else if (classname == "func_escapezone") { m_mapFlags |= MapFlags::Escape; // strange thing on some ES maps, where hostage entity present there @@ -138,10 +138,10 @@ void Game::levelInitialize (edict_t *entities, int max) { m_mapFlags &= ~MapFlags::HostageRescue; } } - else if (cr::strncmp (classname, "func_door", 9) == 0) { + else if (classname.startsWith ("func_door")) { m_mapFlags |= MapFlags::HasDoors; } - else if (cr::strncmp (classname, "func_button", 11) == 0) { + else if (classname.startsWith ("func_button")) { m_mapFlags |= MapFlags::HasButtons; } else if (isShootableBreakable (ent)) { @@ -151,10 +151,12 @@ void Game::levelInitialize (edict_t *entities, int max) { // next maps doesn't have map-specific entities, so determine it by name if (!cv_ignore_map_prefix_game_mode.bool_ ()) { - if (cr::strncmp (getMapName (), "fy_", 3) == 0) { + StringRef prefix = getMapName (); + + if (prefix.startsWith ("fy_")) { m_mapFlags |= MapFlags::FightYard; } - else if (cr::strncmp (getMapName (), "ka_", 3) == 0) { + else if (prefix.startsWith ("ka_")) { m_mapFlags |= MapFlags::KnifeArena; } } @@ -767,9 +769,9 @@ void Game::registerCvars (bool gameVars) { } bool Game::loadCSBinary () { - auto modname = getRunningModName (); + StringRef modname = getRunningModName (); - if (!modname) { + if (modname.empty ()) { return false; } Array libs { "mp", "cs", "cs_i386" }; @@ -803,7 +805,7 @@ bool Game::loadCSBinary () { } // special case, czero is always detected first, as it's has custom directory - if (cr::strcmp (modname, "czero") == 0) { + if (modname == "czero") { m_gameFlags |= (GameFlags::ConditionZero | GameFlags::HasBotVoice | GameFlags::HasFakePings); if (is (GameFlags::Metamod)) { @@ -1071,9 +1073,15 @@ bool Game::isShootableBreakable (edict_t *ent) { } auto limit = cv_breakable_health_limit.float_ (); - if ((cr::strcmp (ent->v.classname.chars (), "func_breakable") == 0 && ent->v.health < limit) || (cr::strcmp (ent->v.classname.chars (), "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE) && ent->v.health < limit) || (cr::strcmp (ent->v.classname.chars (), "func_wall") == 0 && ent->v.health < limit)) { + constexpr auto kFuncBreakable = StringRef::fnv1a32 ("func_breakable"); + constexpr auto kFuncPushable = StringRef::fnv1a32 ("func_pushable"); + constexpr auto kFuncWall = StringRef::fnv1a32 ("func_wall"); + + auto classnameHash = ent->v.classname.str ().hash (); + + if ((classnameHash == kFuncBreakable && ent->v.health < limit) || (classnameHash == kFuncPushable && (ent->v.spawnflags & SF_PUSH_BREAKABLE) && ent->v.health < limit) || (classnameHash == kFuncWall && ent->v.health < limit)) { if (ent->v.takedamage > 0.0f && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY)) { - return (ent->v.movetype == MOVETYPE_PUSH || ent->v.movetype == MOVETYPE_PUSHSTEP); + return ent->v.movetype == MOVETYPE_PUSH || ent->v.movetype == MOVETYPE_PUSHSTEP; } } return false; diff --git a/src/graph.cpp b/src/graph.cpp index ba8bf56..6fd205b 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -1274,7 +1274,7 @@ void BotGraph::calculatePathRadius (int index) { if (tr.flFraction < 1.0f) { game.testLine (radiusStart, radiusEnd, TraceIgnore::Monsters, nullptr, &tr); - if (tr.pHit && cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) { + if (tr.pHit && tr.pHit->v.classname.str ().startsWith ("func_door")) { path.radius = 0.0f; wayBlocked = true; @@ -1571,12 +1571,13 @@ bool BotGraph::loadGraphData () { for (const auto &path : m_paths) { addToBucket (path.origin, path.number); } + StringRef author = exten.author; - if ((outOptions & StorageOption::Official) || cr::strncmp (exten.author, "official", 8) == 0 || cr::strlen (exten.author) < 2) { + if ((outOptions & StorageOption::Official) || author.startsWith ("official") || author.length () < 2) { m_graphAuthor.assign (product.name); } else { - m_graphAuthor.assign (exten.author); + m_graphAuthor.assign (author); } StringRef modified = exten.modified; @@ -1607,7 +1608,7 @@ bool BotGraph::loadGraphData () { } bool BotGraph::canDownload () { - return !strings.isEmpty (cv_graph_url.str ()); + return !cv_graph_url.str ().empty (); } bool BotGraph::saveGraphData () { @@ -1727,17 +1728,17 @@ bool BotGraph::isNodeReacheableEx (const Vector &src, const Vector &destination, // check if we go through a func_illusionary, in which case return false game.testHull (src, destination, TraceIgnore::Monsters, head_hull, m_editor, &tr); - if (tr.pHit && cr::strcmp ("func_illusionary", tr.pHit->v.classname.chars ()) == 0) { - return false; // don't add pathnodes through func_illusionaries + if (tr.pHit && tr.pHit->v.classname.str () == "func_illusionary") { + return false; // don't add path nodes through func_illusionaries } // check if this node is "visible"... game.testLine (src, destination, TraceIgnore::Monsters, m_editor, &tr); // if node is visible from current position (even behind head)... - if (tr.pHit && (tr.flFraction >= 1.0f || cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0)) { + if (tr.pHit && (tr.flFraction >= 1.0f || tr.pHit->v.classname.str ().startsWith ("func_door"))) { // if it's a door check if nothing blocks behind - if (cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) { + if (tr.pHit->v.classname.str ().startsWith ("func_door")) { game.testLine (tr.vecEndPos, destination, TraceIgnore::Monsters, tr.pHit, &tr); if (tr.flFraction < 1.0f) { diff --git a/src/linkage.cpp b/src/linkage.cpp index 68b901b..626586f 100644 --- a/src/linkage.cpp +++ b/src/linkage.cpp @@ -1016,7 +1016,7 @@ void EntityLinkage::callPlayerFunction (edict_t *ent) { playerFunction = game.lib ().resolve ("player"); } else { - playerFunction = reinterpret_cast (reinterpret_cast (lookup (game.lib ().handle (), "player"))); + playerFunction = reinterpret_cast (lookup (game.lib ().handle (), "player")); } if (!playerFunction) { diff --git a/src/manager.cpp b/src/manager.cpp index b0d1835..5bbac60 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -220,7 +220,7 @@ BotCreateResult BotManager::create (StringRef name, int difficulty, int personal else { resultName = name; } - const bool hasNamePrefix = !strings.isEmpty (cv_name_prefix.str ()); + const bool hasNamePrefix = !cv_name_prefix.str ().empty (); // disable save bots names if prefix is enabled if (hasNamePrefix && cv_save_bots_names.bool_ ()) { @@ -390,10 +390,10 @@ void BotManager::maintainQuota () { int desiredBotCount = cv_quota.int_ (); int botsInGame = getBotCount (); - if (strings.matches (cv_quota_mode.str (), "fill")) { + if (cv_quota_mode.str () == "fill") { botsInGame += humanPlayersInGame; } - else if (strings.matches (cv_quota_mode.str (), "match")) { + else if (cv_quota_mode.str () == "match") { int detectQuotaMatch = cv_quota_match.int_ () == 0 ? cv_quota.int_ () : cv_quota_match.int_ (); desiredBotCount = cr::max (0, detectQuotaMatch * humanPlayersInGame); @@ -1664,16 +1664,16 @@ void Bot::updateTeamJoin () { } } -void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *ent) { +void BotManager::captureChatRadio (StringRef cmd, StringRef arg, edict_t *ent) { if (game.isBotCmd ()) { return; } - if (strings.matches (cmd, "say") || strings.matches (cmd, "say_team")) { + if (cmd.startsWith ("say")) { bool alive = util.isAlive (ent); int team = -1; - if (cr::strcmp (cmd, "say_team") == 0) { + if (cmd == "say_team") { team = game.getTeam (ent); } @@ -1697,8 +1697,8 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en auto &target = util.getClient (game.indexOfPlayer (ent)); // check if this player alive, and issue something - if ((target.flags & ClientFlags::Alive) && target.radio != 0 && cr::strncmp (cmd, "menuselect", 10) == 0) { - int radioCommand = atoi (arg); + if ((target.flags & ClientFlags::Alive) && target.radio != 0 && cmd.startsWith ("menuselect")) { + auto radioCommand = arg.int_ (); if (radioCommand != 0) { radioCommand += 10 * (target.radio - 1); @@ -1717,8 +1717,8 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en } target.radio = 0; } - else if (cr::strncmp (cmd, "radio", 5) == 0) { - target.radio = atoi (&cmd[5]); + else if (cmd.startsWith ("radio")) { + target.radio = cmd.substr (5).int_ (); } } @@ -1774,25 +1774,25 @@ void BotManager::updateInterestingEntities () { // search the map for any type of grenade game.searchEntities (nullptr, kInfiniteDistance, [&] (edict_t *e) { - auto classname = e->v.classname.chars (); + auto classname = e->v.classname.str (); // search for grenades, weaponboxes, weapons, items and armoury entities - if (cr::strncmp ("weaponbox", classname, 9) == 0 || cr::strncmp ("grenade", classname, 7) == 0 || util.isItem (e) || cr::strncmp ("armoury", classname, 7) == 0) { + if (classname.startsWith ("weaponbox") || classname.startsWith ("grenade") || util.isItem (e) || classname.startsWith ("armoury")) { m_interestingEntities.push (e); } - // pickup some csdm stuff if we're running csdm - if (game.mapIs (MapFlags::HostageRescue) && cr::strncmp ("hostage", classname, 7) == 0) { + // pickup some hostage if on cs_ maps + if (game.mapIs (MapFlags::HostageRescue) && classname.startsWith ("hostage")) { m_interestingEntities.push (e); } // add buttons - if (game.mapIs (MapFlags::HasButtons) && cr::strncmp ("func_button", classname, 11) == 0) { + if (game.mapIs (MapFlags::HasButtons) && classname.startsWith ("func_button")) { m_interestingEntities.push (e); } // pickup some csdm stuff if we're running csdm - if (game.is (GameFlags::CSDM) && cr::strncmp ("csdm", classname, 4) == 0) { + if (game.is (GameFlags::CSDM) && classname.startsWith ("csdm")) { m_interestingEntities.push (e); } diff --git a/src/navigate.cpp b/src/navigate.cpp index 1e09681..6a3c320 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -621,7 +621,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { } if (seesEntity (m_destOrigin)) { - const auto &right = m_moveAngles.right (); + auto right = m_moveAngles.right (); src = getEyesPos (); src = src + right * 15.0f; @@ -1012,7 +1012,7 @@ bool Bot::updateNavigation () { if (game.mapIs (MapFlags::HasDoors)) { game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Monsters, ent (), &tr); - if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && cr::strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) { + if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && tr.pHit->v.classname.str ().startsWith ("func_door")) { // if the door is near enough... if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) { ignoreCollision (); // don't consider being stuck @@ -1043,25 +1043,30 @@ bool Bot::updateNavigation () { } // if bot hits the door, then it opens, so wait a bit to let it open safely - if (pev->velocity.length2d () < 2 && m_timeDoorOpen < game.time ()) { + if (pev->velocity.length2d () < 10 && m_timeDoorOpen < game.time ()) { startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.5f, false); m_timeDoorOpen = game.time () + 1.0f; // retry in 1 sec until door is open edict_t *pent = nullptr; - if (++m_tryOpenDoor > 2 && util.findNearestPlayer (reinterpret_cast (&pent), ent (), 256.0f, false, false, true, true, false)) { - m_seeEnemyTime = game.time () - 0.5f; + if (++m_tryOpenDoor > 1 && util.findNearestPlayer (reinterpret_cast (&pent), ent (), 384.0f, false, false, true, true, false)) { + if (isPenetrableObstacle (pent->v.origin)) { + m_seeEnemyTime = game.time (); - m_states |= Sense::SeeingEnemy; - m_aimFlags |= AimFlags::Enemy; + m_states |= Sense::SeeingEnemy | Sense::SuspectEnemy; + m_aimFlags |= AimFlags::Enemy; - m_lastEnemy = pent; - m_enemy = pent; - m_lastEnemyOrigin = pent->v.origin; + m_lastEnemy = pent; + m_enemy = pent; + m_lastEnemyOrigin = pent->v.origin; - m_tryOpenDoor = 0; + m_tryOpenDoor = 0; + } + else { + m_tryOpenDoor = 0; + } } - else { + else if (m_timeDoorOpen + 2.0f < game.time ()) { m_tryOpenDoor = 0; } } @@ -1191,7 +1196,7 @@ bool Bot::updateLiftHandling () { // trace line to door game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr); - if (tr.flFraction < 1.0f && tr.pHit && cr::strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) { + if (tr.flFraction < 1.0f && tr.pHit && tr.pHit->v.classname.str ().startsWith ("func_door") && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) { if (m_liftState == LiftState::None) { m_liftState = LiftState::LookingButtonOutside; m_liftUsageTime = game.time () + 7.0f; @@ -1199,11 +1204,16 @@ bool Bot::updateLiftHandling () { liftClosedDoorExists = true; } + // helper + auto isFunc = [] (StringRef cls) -> bool { + return cls.startsWith ("func_door") || cls == "func_plat" || cls == "func_train"; + }; + // trace line down game.testLine (m_path->origin, m_pathOrigin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr); // if trace result shows us that it is a lift - if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (cr::strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_train") == 0) && !liftClosedDoorExists) { + if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && isFunc (tr.pHit->v.classname.str ()) && !liftClosedDoorExists) { if ((m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && cr::fzero (tr.pHit->v.velocity.z)) { if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) { m_liftEntity = tr.pHit; @@ -1225,7 +1235,7 @@ bool Bot::updateLiftHandling () { if (graph.exists (nextNode) && (graph[nextNode].flags & NodeFlag::Lift)) { game.testLine (m_path->origin, graph[nextNode].origin, TraceIgnore::Everything, ent (), &tr); - if (!game.isNullEntity (tr.pHit) && (cr::strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_train") == 0)) { + if (!game.isNullEntity (tr.pHit) && isFunc (tr.pHit->v.classname.str ())) { m_liftEntity = tr.pHit; } } @@ -1832,7 +1842,7 @@ int Bot::findDefendNode (const Vector &origin) { int srcIndex = m_currentNodeIndex; // max search distance - const auto kMaxDistance = cr::clamp (148.0f * bots.getBotCount (), 256.0f, 1024.0f); + const auto kMaxDistance = cr::clamp (static_cast (148 * bots.getBotCount ()), 256.0f, 1024.0f); // some of points not found, return random one if (srcIndex == kInvalidNodeIndex || posIndex == kInvalidNodeIndex) { @@ -2369,13 +2379,13 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) { // first do a trace from the bot's eyes forward... auto src = getEyesPos (); auto forward = src + normal * 24.0f; - const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); + auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); auto checkDoor = [] (TraceResult *result) { if (!game.mapIs (MapFlags::HasDoors)) { return false; } - return result->flFraction < 1.0f && cr::strncmp ("func_door", result->pHit->v.classname.chars (), 9) != 0; + return result->flFraction < 1.0f && result->pHit && !result->pHit->v.classname.str ().startsWith ("func_door"); }; // trace from the bot's eyes straight forward... @@ -2383,7 +2393,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) { // check if the trace hit something... if (tr->flFraction < 1.0f) { - if (game.mapIs (MapFlags::HasDoors) && cr::strncmp ("func_door", tr->pHit->v.classname.chars (), 9) == 0) { + if (game.mapIs (MapFlags::HasDoors) && tr->pHit && tr->pHit->v.classname.str ().startsWith ("func_door")) { return false; } return true; // bot's head will hit something @@ -2529,7 +2539,7 @@ bool Bot::canJumpUp (const Vector &normal) { if (!isOnFloor () && (isOnLadder () || !isInWater ())) { return false; } - const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math... + auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math... // check for normal jump height first... auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f); @@ -2695,7 +2705,7 @@ bool Bot::canDuckUnder (const Vector &normal) { } // convert current view angle to vectors for TraceLine math... - const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); + auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // now check same height to one side of the bot... src = baseHeight + right * 16.0f; @@ -2734,7 +2744,7 @@ bool Bot::isBlockedLeft () { game.testLine (pev->origin, forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr); // check if the trace hit something... - if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) != 0) { + if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && !tr.pHit->v.classname.str ().startsWith ("func_door")) { return true; // bot's body will hit something } return false; @@ -2754,7 +2764,7 @@ bool Bot::isBlockedRight () { game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr); // check if the trace hit something... - if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) != 0) { + if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && !tr.pHit->v.classname.str ().startsWith ("func_door")) { return true; // bot's body will hit something } return false; @@ -2939,7 +2949,7 @@ int Bot::getRandomCampDir () { } void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) { - const Vector &los = (moveDir - pev->origin).normalize2d (); + const Vector &los = (moveDir - pev->origin).normalize2d_apx (); float dot = los | pev->angles.forward ().get2d (); if (dot > 0.0f && !checkWallOnRight ()) { diff --git a/src/planner.cpp b/src/planner.cpp index 93bcc6f..100008c 100644 --- a/src/planner.cpp +++ b/src/planner.cpp @@ -323,7 +323,7 @@ AStarResult AStarAlgo::find (int botTeam, int srcIndex, int destIndex, NodeAdder void FloydWarshallAlgo::rebuild () { m_length = graph.length (); - m_matrix.resize (cr::sqrf (m_length)); + m_matrix.resize (static_cast (cr::sqrf (m_length))); worker.enqueue ([this] () { syncRebuild (); @@ -416,8 +416,10 @@ bool FloydWarshallAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNo void DijkstraAlgo::init (const int length) { m_length = length; - m_distance.resize (length); - m_parent.resize (length); + auto ulength = static_cast (length); + + m_distance.resize (ulength); + m_parent.resize (ulength); m_distance.shrink (); m_parent.shrink (); @@ -435,7 +437,7 @@ bool DijkstraAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, i m_distance[srcIndex] = 0; while (!m_queue.empty ()) { - auto &&route = cr::move (m_queue.pop ()); + auto route = m_queue.pop (); auto current = route.second; // finished search @@ -500,7 +502,7 @@ void PathPlanner::init () { const int length = graph.length (); const float limitInMb = cv_path_floyd_memory_limit.float_ (); - const float memoryUse = static_cast (sizeof (FloydWarshallAlgo::Matrix) * cr::sqrf (length) / 1024 / 1024); + const float memoryUse = static_cast (sizeof (FloydWarshallAlgo::Matrix) * cr::sqrf (static_cast (length)) / 1024 / 1024); // if we're have too much memory for floyd matrices, planner will use dijkstra or uniform planner for other than pathfinding needs if (memoryUse > limitInMb) { diff --git a/src/practice.cpp b/src/practice.cpp index 411f9f4..d64e8ff 100644 --- a/src/practice.cpp +++ b/src/practice.cpp @@ -127,7 +127,7 @@ void BotPractice::syncUpdate () { } } constexpr auto kFullDamageVal = static_cast (PracticeLimit::Damage); - constexpr auto kHalfDamageVal = static_cast (PracticeLimit::Damage / 2); + constexpr auto kHalfDamageVal = kFullDamageVal / 2; // adjust values if overflow is about to happen if (adjustValues) { diff --git a/src/support.cpp b/src/support.cpp index 9a9c673..419db24 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -204,7 +204,7 @@ bool BotSupport::isMonster (edict_t *ent) { return false; } - if (cr::strncmp ("hostage", ent->v.classname.chars (), 7) == 0) { + if (ent->v.classname.str ().startsWith ("hostage")) { return false; } @@ -212,7 +212,7 @@ bool BotSupport::isMonster (edict_t *ent) { } bool BotSupport::isItem (edict_t *ent) { - return !!(strstr (ent->v.classname.chars (), "item_")); + return ent && ent->v.classname.str ().contains ("item_"); } bool BotSupport::isPlayerVIP (edict_t *ent) { diff --git a/src/tasks.cpp b/src/tasks.cpp index c88abf0..8b6aab3 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -1426,9 +1426,11 @@ void Bot::pickupItem_ () { switch (m_pickupType) { case Pickup::DroppedC4: case Pickup::None: + case Pickup::Items: break; case Pickup::Weapon: + case Pickup::AmmoAndKits: m_aimFlags |= AimFlags::Nav; // near to weapon? @@ -1436,17 +1438,17 @@ void Bot::pickupItem_ () { int index = 0; auto &info = conf.getWeapons (); - for (index = 0; index < 7; ++index) { - if (cr::strcmp (info[index].model, m_pickupItem->v.model.chars (9)) == 0) { + for (index = 0; index < kPrimaryWeaponMinIndex; ++index) { + if (m_pickupItem->v.model.str (9) == info[index].model) { break; } } - if (index < 7) { + if (index < kPrimaryWeaponMinIndex) { // secondary weapon. i.e., pistol int weaponIndex = 0; - for (index = 0; index < 7; ++index) { + for (index = 0; index < kPrimaryWeaponMinIndex; ++index) { if (pev->weapons & cr::bit (info[index].id)) { weaponIndex = index; } @@ -1469,7 +1471,7 @@ void Bot::pickupItem_ () { auto tab = conf.getRawWeapons (); - if ((tab->id == Weapon::Shield || weaponIndex > 6 || hasShield ()) && niceWeapon) { + if ((tab[weaponIndex].id == Weapon::Shield || weaponIndex >= kPrimaryWeaponMinIndex || hasShield ()) && niceWeapon) { selectWeaponByIndex (weaponIndex); dropCurrentWeapon (); } @@ -1559,9 +1561,9 @@ void Bot::pickupItem_ () { // find the nearest 'unused' hostage within the area game.searchEntities (pev->origin, 768.0f, [&] (edict_t *ent) { - auto classname = ent->v.classname.chars (); + auto classname = ent->v.classname.str (); - if (cr::strncmp ("hostage_entity", classname, 14) != 0 && cr::strncmp ("monster_scientist", classname, 17) != 0) { + if (!classname.startsWith ("hostage_entity") && !classname.startsWith ("monster_scientist")) { return EntitySearchResult::Continue; } diff --git a/src/vision.cpp b/src/vision.cpp index 76b844e..9e464cf 100644 --- a/src/vision.cpp +++ b/src/vision.cpp @@ -35,7 +35,7 @@ bool Bot::isInViewCone (const Vector &origin) { return util.isInViewCone (origin, ent ()); } -bool Bot::seesItem (const Vector &destination, const char *classname) { +bool Bot::seesItem (const Vector &destination, StringRef classname) { TraceResult tr {}; // trace a line from bot's eyes to destination.. @@ -43,7 +43,7 @@ bool Bot::seesItem (const Vector &destination, const char *classname) { // check if line of sight to object is not blocked (i.e. visible) if (tr.flFraction < 1.0f && tr.pHit && !tr.fStartSolid) { - return cr::strcmp (tr.pHit->v.classname.chars (), classname) == 0; + return classname == tr.pHit->v.classname.str (); } return true; } @@ -106,7 +106,7 @@ void Bot::updateAimDir () { m_lookAt.z += 48.0f; } else if (m_pickupType == Pickup::Weapon) { - m_lookAt.z += 72.0; + m_lookAt.z += 72.0f; } } else if (flags & AimFlags::LastEnemy) { diff --git a/vc/yapb.vcxproj b/vc/yapb.vcxproj index f1ba187..a389406 100644 --- a/vc/yapb.vcxproj +++ b/vc/yapb.vcxproj @@ -53,6 +53,7 @@ +