fix: make ammo pickups actually to work

fix: buy: bots never buy enough ammo for secondary weapons
fix: yb_pickup_best disabling all pickups instead of weapons only
add: yb_pickup_ammo_and_kits, that allows to enable ammos medkits and kevlars
refactor: switched to crlib strings where possible
refactor: fix some compiler warnings at high levels
refactor: move constants to separate header (thx @spodlesniy)
This commit is contained in:
jeefo 2023-06-20 15:18:35 +03:00
commit 214b56f37b
No known key found for this signature in database
GPG key ID: 927BCA0779BEA8ED
23 changed files with 734 additions and 625 deletions

@ -1 +1 @@
Subproject commit 37f616f5839faa6cdd40ea22258d564675c45201
Subproject commit 67733ef6ffd51c538692f311e6cfb26affb3e50e

449
inc/constant.h Normal file
View file

@ -0,0 +1,449 @@
//
// YaPB, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright © YaPB Project Developers <yapb@jeefo.net>.
//
// 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 <int> (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;

View file

@ -447,7 +447,7 @@ public:
return ptr->value;
}
const char *str () const {
StringRef str () const {
return ptr->string;
}

View file

@ -183,7 +183,7 @@ private:
public:
SmallArray <Path> m_paths {};
HashMap <int32_t, Array <int32_t>, EmptyHash <int32_t>> m_hashTable;
HashMap <int32_t, Array <int32_t>, EmptyHash <int32_t>> m_hashTable {};
String m_graphAuthor {};
String m_graphModified {};

View file

@ -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);

View file

@ -242,8 +242,8 @@ public:
// the bot path planner
class PathPlanner : public Singleton <PathPlanner> {
private:
UniquePtr <DijkstraAlgo> m_dijkstra;
UniquePtr <FloydWarshallAlgo> m_floyd;
UniquePtr <DijkstraAlgo> m_dijkstra {};
UniquePtr <FloydWarshallAlgo> m_floyd {};
bool m_memoryLimitHit {};
public:

View file

@ -18,442 +18,7 @@ using namespace cr;
#include <product.h>
#include <module.h>
// 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 <int> (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 <constant.h>
// 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 <class AStarAlgo> m_planner;
UniquePtr <class AStarAlgo> 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 ();

View file

@ -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 <String> (";");
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 <int32_t, String> tasks {
static HashMap <int32_t, StringRef> tasks {
{ Task::Normal, "Normal" },
{ Task::Pause, "Pause" },
{ Task::MoveToPosition, "Move" },
@ -3046,13 +3111,13 @@ void Bot::showDebugOverlay () {
{ Task::Spraypaint, "Spray" }
};
static HashMap <int32_t, String> personalities {
static HashMap <int32_t, StringRef> personalities {
{ Personality::Rusher, "Rusher" },
{ Personality::Normal, "Normal" },
{ Personality::Careful, "Careful" }
};
static HashMap <int32_t, String> flags {
static HashMap <int32_t, StringRef> 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)

View file

@ -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 <int32_t> (line.length ());

View file

@ -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 <float> (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;

View file

@ -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));

View file

@ -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");

View file

@ -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 <StringRef> 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;

View file

@ -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) {

View file

@ -1016,7 +1016,7 @@ void EntityLinkage::callPlayerFunction (edict_t *ent) {
playerFunction = game.lib ().resolve <EntityFunction> ("player");
}
else {
playerFunction = reinterpret_cast <EntityFunction> (reinterpret_cast <void *> (lookup (game.lib ().handle (), "player")));
playerFunction = reinterpret_cast <EntityFunction> (lookup (game.lib ().handle (), "player"));
}
if (!playerFunction) {

View file

@ -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 <int> (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);
}

View file

@ -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 <void **> (&pent), ent (), 256.0f, false, false, true, true, false)) {
m_seeEnemyTime = game.time () - 0.5f;
if (++m_tryOpenDoor > 1 && util.findNearestPlayer (reinterpret_cast <void **> (&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 <float> (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 ()) {

View file

@ -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 <size_t> (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 <size_t> (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 <float> (sizeof (FloydWarshallAlgo::Matrix) * cr::sqrf (length) / 1024 / 1024);
const float memoryUse = static_cast <float> (sizeof (FloydWarshallAlgo::Matrix) * cr::sqrf (static_cast <size_t> (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) {

View file

@ -127,7 +127,7 @@ void BotPractice::syncUpdate () {
}
}
constexpr auto kFullDamageVal = static_cast <int32_t> (PracticeLimit::Damage);
constexpr auto kHalfDamageVal = static_cast <int32_t> (PracticeLimit::Damage / 2);
constexpr auto kHalfDamageVal = kFullDamageVal / 2;
// adjust values if overflow is about to happen
if (adjustValues) {

View file

@ -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) {

View file

@ -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;
}

View file

@ -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) {

View file

@ -53,6 +53,7 @@
<ClInclude Include="..\ext\linkage\linkage\physint.h" />
<ClInclude Include="..\inc\analyze.h" />
<ClInclude Include="..\inc\config.h" />
<ClInclude Include="..\inc\constant.h" />
<ClInclude Include="..\inc\control.h" />
<ClInclude Include="..\inc\engine.h" />
<ClInclude Include="..\inc\graph.h" />