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:
parent
290a74f5b3
commit
214b56f37b
23 changed files with 734 additions and 625 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 37f616f5839faa6cdd40ea22258d564675c45201
|
||||
Subproject commit 67733ef6ffd51c538692f311e6cfb26affb3e50e
|
||||
449
inc/constant.h
Normal file
449
inc/constant.h
Normal 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;
|
||||
|
|
@ -447,7 +447,7 @@ public:
|
|||
return ptr->value;
|
||||
}
|
||||
|
||||
const char *str () const {
|
||||
StringRef str () const {
|
||||
return ptr->string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
441
inc/yapb.h
441
inc/yapb.h
|
|
@ -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 ();
|
||||
|
|
|
|||
197
src/botlib.cpp
197
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,46 +372,30 @@ 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;
|
||||
}
|
||||
else if (cr::strncmp ("weapon_shield", classname, 13) == 0 && !m_isUsingGrenade) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::Shield;
|
||||
}
|
||||
else if (cr::strncmp ("item_thighpack", classname, 14) == 0 && m_team == Team::CT && !m_hasDefuser) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::DefusalKit;
|
||||
}
|
||||
else if (cr::strncmp ("grenade", classname, 7) == 0 && 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) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::None;
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot found something it can pickup...
|
||||
if (allowPickup) {
|
||||
|
||||
// found weapon on ground?
|
||||
if (pickupType == Pickup::Weapon) {
|
||||
if (cv_pickup_ammo_and_kits.bool_ ()) {
|
||||
int primaryWeaponCarried = bestPrimaryCarried ();
|
||||
int secondaryWeaponCarried = bestSecondaryCarried ();
|
||||
|
||||
|
|
@ -387,10 +406,10 @@ void Bot::updatePickups () {
|
|||
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 (secondaryWeaponCarried < kPrimaryWeaponMinIndex && (getAmmo (secondary.id) > 0.3 * secondaryProp.ammo1Max) && model == "357ammobox.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (!m_isVIP && primaryWeaponCarried >= 7 && (m_ammo[primary.id] > 0.3 * primaryProp.ammo1Max) && cr::strncmp (model, "w_", 2) == 0) {
|
||||
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;
|
||||
|
|
@ -398,38 +417,82 @@ void Bot::updatePickups () {
|
|||
const bool isShotgun = weaponType == WeaponType::Shotgun;
|
||||
const bool isRifle = weaponType == WeaponType::Rifle || weaponType == WeaponType::ZoomRifle;
|
||||
|
||||
if (!isRifle && cr::strcmp (model, "w_9mmarclip.mdl") == 0) {
|
||||
if (!isRifle && model == "9mmarclip.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (!isShotgun && cr::strcmp (model, "w_shotbox.mdl") == 0) {
|
||||
else if (!isShotgun && model == "shotbox.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (!isSubmachine && cr::strcmp (model, "w_9mmclip.mdl") == 0) {
|
||||
else if (!isSubmachine && model == "9mmclip.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (!isSniperRifle && cr::strcmp (model, "w_crossbow_clip.mdl") == 0) {
|
||||
else if (!isSniperRifle && model == "crossbow_clip.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (primaryWeaponCarried != Weapon::M249 && cr::strcmp (model, "w_chainammo.mdl") == 0) {
|
||||
else if (primaryWeaponCarried != Weapon::M249 && model == "chainammo.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
}
|
||||
else if (m_isVIP || !rateGroundWeapon (ent)) {
|
||||
else if (m_healthValue >= 100.0f && model == "medkit.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (m_healthValue >= 100.0f && cr::strcmp (model, "medkit.mdl") == 0) {
|
||||
else if (pev->armorvalue >= 100.0f && (model == "kevlar.mdl"|| model == "battery.mdl" || model == "assault.mdl")) {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if (pev->armorvalue >= 100.0f && (cr::strcmp (model, "kevlar.mdl") == 0 || cr::strcmp (model, "battery.mdl") == 0)) {
|
||||
|
||||
if (allowPickup) {
|
||||
pickupType = Pickup::AmmoAndKits;
|
||||
}
|
||||
}
|
||||
|
||||
// weapon replacement is not allowed
|
||||
if (!cv_pickup_best.bool_ ()) {
|
||||
allowPickup = false;
|
||||
pickupType = Pickup::None;
|
||||
}
|
||||
}
|
||||
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 (isDemolitionMap && m_team == Team::CT && !m_hasDefuser && classname.startsWith ("item_thighpack")) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::DefusalKit;
|
||||
}
|
||||
else if (isDemolitionMap && classname.startsWith ("grenade") && conf.getBombModelName () == model) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::PlantedC4;
|
||||
}
|
||||
else if (cv_pickup_custom_items.bool_ () && util.isItem (ent) && !classname.startsWith ("item_thighpack")) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::Items;
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot found something it can pickup...
|
||||
if (allowPickup) {
|
||||
|
||||
// found weapon on ground?
|
||||
if (pickupType == Pickup::Weapon) {
|
||||
if (m_isVIP) {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && cr::strcmp (model, "flashbang.mdl") == 0) {
|
||||
else if (!rateGroundWeapon (ent)) {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if ((pev->weapons & cr::bit (Weapon::Explosive)) && cr::strcmp (model, "hegrenade.mdl") == 0) {
|
||||
else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && model == "flashbang.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
else if ((pev->weapons & cr::bit (Weapon::Smoke)) && cr::strcmp (model, "smokegrenade.mdl") == 0) {
|
||||
else if ((pev->weapons & cr::bit (Weapon::Explosive)) && model == "hegrenade.mdl") {
|
||||
allowPickup = false;
|
||||
}
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,16 +1043,17 @@ 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_states |= Sense::SeeingEnemy | Sense::SuspectEnemy;
|
||||
m_aimFlags |= AimFlags::Enemy;
|
||||
|
||||
m_lastEnemy = pent;
|
||||
|
|
@ -1065,6 +1066,10 @@ bool Bot::updateNavigation () {
|
|||
m_tryOpenDoor = 0;
|
||||
}
|
||||
}
|
||||
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 ()) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue