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;
|
return ptr->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *str () const {
|
StringRef str () const {
|
||||||
return ptr->string;
|
return ptr->string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SmallArray <Path> m_paths {};
|
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_graphAuthor {};
|
||||||
String m_graphModified {};
|
String m_graphModified {};
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ public:
|
||||||
void resetFilters ();
|
void resetFilters ();
|
||||||
void updateActiveGrenade ();
|
void updateActiveGrenade ();
|
||||||
void updateInterestingEntities ();
|
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 notifyBombDefuse ();
|
||||||
void execGameEntity (edict_t *ent);
|
void execGameEntity (edict_t *ent);
|
||||||
void forEach (ForEachBot handler);
|
void forEach (ForEachBot handler);
|
||||||
|
|
|
||||||
|
|
@ -242,8 +242,8 @@ public:
|
||||||
// the bot path planner
|
// the bot path planner
|
||||||
class PathPlanner : public Singleton <PathPlanner> {
|
class PathPlanner : public Singleton <PathPlanner> {
|
||||||
private:
|
private:
|
||||||
UniquePtr <DijkstraAlgo> m_dijkstra;
|
UniquePtr <DijkstraAlgo> m_dijkstra {};
|
||||||
UniquePtr <FloydWarshallAlgo> m_floyd;
|
UniquePtr <FloydWarshallAlgo> m_floyd {};
|
||||||
bool m_memoryLimitHit {};
|
bool m_memoryLimitHit {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
441
inc/yapb.h
441
inc/yapb.h
|
|
@ -18,442 +18,7 @@ using namespace cr;
|
||||||
|
|
||||||
#include <product.h>
|
#include <product.h>
|
||||||
#include <module.h>
|
#include <module.h>
|
||||||
|
#include <constant.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));
|
|
||||||
|
|
||||||
// links keywords and replies together
|
// links keywords and replies together
|
||||||
struct ChatKeywords {
|
struct ChatKeywords {
|
||||||
|
|
@ -774,7 +339,7 @@ private:
|
||||||
FindPath m_pathType {}; // which pathfinder to use
|
FindPath m_pathType {}; // which pathfinder to use
|
||||||
uint8_t m_enemyParts {}; // visibility flags
|
uint8_t m_enemyParts {}; // visibility flags
|
||||||
uint16_t m_modelMask {}; // model mask bits
|
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_pickupItem {}; // pointer to entity of item to use/pickup
|
||||||
edict_t *m_itemIgnore {}; // pointer to entity to ignore for pickup
|
edict_t *m_itemIgnore {}; // pointer to entity to ignore for pickup
|
||||||
|
|
@ -856,7 +421,7 @@ private:
|
||||||
bool advanceMovement ();
|
bool advanceMovement ();
|
||||||
bool isBombDefusing (const Vector &bombOrigin);
|
bool isBombDefusing (const Vector &bombOrigin);
|
||||||
bool isOccupiedNode (int index, bool needZeroVelocity = false);
|
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 lastEnemyShootable ();
|
||||||
bool rateGroundWeapon (edict_t *ent);
|
bool rateGroundWeapon (edict_t *ent);
|
||||||
bool reactOnEnemy ();
|
bool reactOnEnemy ();
|
||||||
|
|
|
||||||
225
src/botlib.cpp
225
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_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_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_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_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.");
|
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) {
|
if (!seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) > pev->fov * 0.5f) {
|
||||||
continue;
|
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
|
// don't look at flash bang
|
||||||
if (!(m_states & Sense::SeeingEnemy)) {
|
if (!(m_states & Sense::SeeingEnemy)) {
|
||||||
m_lookAt.y = cr::wrapAngle ((game.getEntityOrigin (pent) - getEyesPos ()).angles ().y + 180.0f);
|
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);
|
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 ()) {
|
if (game.getTeam (pent->v.owner) == m_team || pent->v.owner == ent ()) {
|
||||||
continue;
|
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) {
|
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) {
|
||||||
float distance = pent->v.origin.distance (pev->origin);
|
float distance = pent->v.origin.distance (pev->origin);
|
||||||
|
|
||||||
|
|
@ -270,8 +271,42 @@ void Bot::setIdealReactionTimers (bool actual) {
|
||||||
void Bot::updatePickups () {
|
void Bot::updatePickups () {
|
||||||
// this function finds Items to collect or use in the near of a bot
|
// 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...
|
// utility to check if this function is currently doesn't allowed to run
|
||||||
if (m_isCreature || (m_states & Sense::SeeingEnemy) || isOnLadder () || getCurrentTaskId () == Task::EscapeFromBomb || !cv_pickup_best.bool_ () || cv_jasonmode.bool_ () || !bots.hasInterestingEntities ()) {
|
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_pickupItem = nullptr;
|
||||||
m_pickupType = Pickup::None;
|
m_pickupType = Pickup::None;
|
||||||
|
|
||||||
|
|
@ -299,7 +334,7 @@ void Bot::updatePickups () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent == pickupItem) {
|
if (ent == pickupItem) {
|
||||||
if (seesItem (origin, ent->v.classname.chars ())) {
|
if (seesItem (origin, ent->v.classname.str ())) {
|
||||||
itemExists = true;
|
itemExists = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -337,38 +372,106 @@ void Bot::updatePickups () {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto classname = ent->v.classname.chars ();
|
auto classname = ent->v.classname.str ();
|
||||||
auto model = ent->v.model.chars (9);
|
auto model = ent->v.model.str (9);
|
||||||
|
|
||||||
// check if line of sight to object is not blocked (i.e. visible)
|
// check if line of sight to object is not blocked (i.e. visible)
|
||||||
if (seesItem (origin, classname)) {
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::Hostage;
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::DroppedC4;
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::Weapon;
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::Shield;
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::DefusalKit;
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::PlantedC4;
|
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;
|
allowPickup = true;
|
||||||
pickupType = Pickup::None;
|
pickupType = Pickup::Items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,59 +480,19 @@ void Bot::updatePickups () {
|
||||||
|
|
||||||
// found weapon on ground?
|
// found weapon on ground?
|
||||||
if (pickupType == Pickup::Weapon) {
|
if (pickupType == Pickup::Weapon) {
|
||||||
int primaryWeaponCarried = bestPrimaryCarried ();
|
if (m_isVIP) {
|
||||||
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) {
|
|
||||||
allowPickup = false;
|
allowPickup = false;
|
||||||
}
|
}
|
||||||
else if (!m_isVIP && primaryWeaponCarried >= 7 && (m_ammo[primary.id] > 0.3 * primaryProp.ammo1Max) && cr::strncmp (model, "w_", 2) == 0) {
|
else if (!rateGroundWeapon (ent)) {
|
||||||
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)) {
|
|
||||||
allowPickup = false;
|
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;
|
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;
|
allowPickup = false;
|
||||||
}
|
}
|
||||||
else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && cr::strcmp (model, "flashbang.mdl") == 0) {
|
else if ((pev->weapons & cr::bit (Weapon::Smoke)) && model == "smokegrenade.mdl") {
|
||||||
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) {
|
|
||||||
allowPickup = false;
|
allowPickup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -982,10 +1045,12 @@ void Bot::checkMsgQueue () {
|
||||||
bool Bot::isWeaponRestricted (int weaponIndex) {
|
bool Bot::isWeaponRestricted (int weaponIndex) {
|
||||||
// this function checks for weapon restrictions.
|
// 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
|
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);
|
const auto &alias = util.weaponIdToAlias (weaponIndex);
|
||||||
|
|
||||||
for (const auto &ban : bannedWeapons) {
|
for (const auto &ban : bannedWeapons) {
|
||||||
|
|
@ -1314,7 +1379,7 @@ void Bot::buyStuff () {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BuyState::SecondaryWeapon: // if bot has still some money, buy a better secondary weapon
|
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 {
|
do {
|
||||||
pref--;
|
pref--;
|
||||||
|
|
||||||
|
|
@ -1380,18 +1445,18 @@ void Bot::buyStuff () {
|
||||||
|
|
||||||
|
|
||||||
case BuyState::Ammo: // buy enough primary & secondary ammo (do not check for money here)
|
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
|
issueCommand ("buyammo%d", rg.get (1, 2)); // simulate human
|
||||||
}
|
}
|
||||||
|
|
||||||
// buy enough secondary ammo
|
// buy enough ammo
|
||||||
if (hasPrimaryWeapon ()) {
|
if (hasPrimaryWeapon ()) {
|
||||||
|
issueCommand ("buy;menuselect 6");
|
||||||
|
}
|
||||||
|
else {
|
||||||
issueCommand ("buy;menuselect 7");
|
issueCommand ("buy;menuselect 7");
|
||||||
}
|
}
|
||||||
|
|
||||||
// buy enough primary ammo
|
|
||||||
issueCommand ("buy;menuselect 6");
|
|
||||||
|
|
||||||
// try to reload secondary weapon
|
// try to reload secondary weapon
|
||||||
if (m_reloadState != Reload::Primary) {
|
if (m_reloadState != Reload::Primary) {
|
||||||
m_reloadState = Reload::Secondary;
|
m_reloadState = Reload::Secondary;
|
||||||
|
|
@ -3023,7 +3088,7 @@ void Bot::showDebugOverlay () {
|
||||||
static float timeDebugUpdate = 0.0f;
|
static float timeDebugUpdate = 0.0f;
|
||||||
static int index = kInvalidNodeIndex, goal = kInvalidNodeIndex, taskID = 0;
|
static int index = kInvalidNodeIndex, goal = kInvalidNodeIndex, taskID = 0;
|
||||||
|
|
||||||
static HashMap <int32_t, String> tasks {
|
static HashMap <int32_t, StringRef> tasks {
|
||||||
{ Task::Normal, "Normal" },
|
{ Task::Normal, "Normal" },
|
||||||
{ Task::Pause, "Pause" },
|
{ Task::Pause, "Pause" },
|
||||||
{ Task::MoveToPosition, "Move" },
|
{ Task::MoveToPosition, "Move" },
|
||||||
|
|
@ -3046,13 +3111,13 @@ void Bot::showDebugOverlay () {
|
||||||
{ Task::Spraypaint, "Spray" }
|
{ Task::Spraypaint, "Spray" }
|
||||||
};
|
};
|
||||||
|
|
||||||
static HashMap <int32_t, String> personalities {
|
static HashMap <int32_t, StringRef> personalities {
|
||||||
{ Personality::Rusher, "Rusher" },
|
{ Personality::Rusher, "Rusher" },
|
||||||
{ Personality::Normal, "Normal" },
|
{ Personality::Normal, "Normal" },
|
||||||
{ Personality::Careful, "Careful" }
|
{ Personality::Careful, "Careful" }
|
||||||
};
|
};
|
||||||
|
|
||||||
static HashMap <int32_t, String> flags {
|
static HashMap <int32_t, StringRef> flags {
|
||||||
{ AimFlags::Nav, "Nav" },
|
{ AimFlags::Nav, "Nav" },
|
||||||
{ AimFlags::Camp, "Camp" },
|
{ AimFlags::Camp, "Camp" },
|
||||||
{ AimFlags::PredictPath, "Predict" },
|
{ AimFlags::PredictPath, "Predict" },
|
||||||
|
|
@ -3080,15 +3145,15 @@ void Bot::showDebugOverlay () {
|
||||||
String enemy = "(none)";
|
String enemy = "(none)";
|
||||||
|
|
||||||
if (!game.isNullEntity (m_enemy)) {
|
if (!game.isNullEntity (m_enemy)) {
|
||||||
enemy = m_enemy->v.netname.chars ();
|
enemy = m_enemy->v.netname.str ();
|
||||||
}
|
}
|
||||||
else if (!game.isNullEntity (m_lastEnemy)) {
|
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)";
|
String pickup = "(none)";
|
||||||
|
|
||||||
if (!game.isNullEntity (m_pickupItem)) {
|
if (!game.isNullEntity (m_pickupItem)) {
|
||||||
pickup = m_pickupItem->v.classname.chars ();
|
pickup = m_pickupItem->v.classname.str ();
|
||||||
}
|
}
|
||||||
String aimFlags;
|
String aimFlags;
|
||||||
|
|
||||||
|
|
@ -3102,7 +3167,7 @@ void Bot::showDebugOverlay () {
|
||||||
auto weapon = util.weaponIdToAlias (m_currentWeapon);
|
auto weapon = util.weaponIdToAlias (m_currentWeapon);
|
||||||
|
|
||||||
String debugData;
|
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)
|
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, overlayEntity)
|
||||||
.writeByte (TE_TEXTMESSAGE)
|
.writeByte (TE_TEXTMESSAGE)
|
||||||
|
|
@ -3378,7 +3443,7 @@ void Bot::startDoubleJump (edict_t *ent) {
|
||||||
m_doubleJumpEntity = ent;
|
m_doubleJumpEntity = ent;
|
||||||
|
|
||||||
startTask (Task::DoubleJump, TaskPri::DoubleJump, kInvalidNodeIndex, game.time (), true);
|
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) {
|
void Bot::sendBotToOrigin (const Vector &origin) {
|
||||||
|
|
@ -3410,7 +3475,7 @@ void Bot::debugMsgInternal (const char *str) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String printBuf;
|
String printBuf;
|
||||||
printBuf.assignf ("%s: %s", pev->netname.chars (), str);
|
printBuf.assignf ("%s: %s", pev->netname.str (), str);
|
||||||
|
|
||||||
bool playMessage = false;
|
bool playMessage = false;
|
||||||
|
|
||||||
|
|
@ -3778,7 +3843,7 @@ void Bot::refreshModelName (char *infobuffer) {
|
||||||
union ModelTest {
|
union ModelTest {
|
||||||
char model[2];
|
char model[2];
|
||||||
uint16_t mask;
|
uint16_t mask;
|
||||||
ModelTest (StringRef m) : model { m[0], m[1] } {};
|
ModelTest (StringRef m) : model { m[0], m[1] } {}
|
||||||
} modelTest { modelName };
|
} modelTest { modelName };
|
||||||
|
|
||||||
// assign our model mask (tests against model done every bot update)
|
// 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
|
// 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 ();
|
playerName.lowercase ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotSupport::addChatErrors (String &line) {
|
void BotSupport::addChatErrors (String &line) {
|
||||||
// sometimes switch name to lower characters, only valid for the english languge
|
// 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 ();
|
line.lowercase ();
|
||||||
}
|
}
|
||||||
auto length = static_cast <int32_t> (line.length ());
|
auto length = static_cast <int32_t> (line.length ());
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@ Vector Bot::getBodyOffsetError (float distance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_aimErrorTime < game.time ()) {
|
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;
|
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));
|
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 () {
|
bool Bot::hasShield () {
|
||||||
// this function returns true, if bot has a tactical shield
|
// 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 () {
|
bool Bot::isShieldDrawn () {
|
||||||
|
|
@ -1352,7 +1352,7 @@ bool Bot::isEnemyBehindShield (edict_t *enemy) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if enemy has shield and this shield is drawn
|
// 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)) {
|
if (util.isInViewCone (pev->origin, enemy)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1443,7 +1443,7 @@ int Bot::bestSecondaryCarried () {
|
||||||
for (int i = 0; i < kNumWeapons; ++i) {
|
for (int i = 0; i < kNumWeapons; ++i) {
|
||||||
int id = tab[*pref].id;
|
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;
|
weaponIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1468,21 +1468,22 @@ int Bot::bestGrenadeCarried () {
|
||||||
bool Bot::rateGroundWeapon (edict_t *ent) {
|
bool Bot::rateGroundWeapon (edict_t *ent) {
|
||||||
// this function compares weapons on the ground to the one the bot is using
|
// 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;
|
int groundIndex = 0;
|
||||||
|
|
||||||
const int *pref = conf.getWeaponPrefs (m_personality);
|
const int *pref = conf.getWeaponPrefs (m_personality);
|
||||||
auto tab = conf.getRawWeapons ();
|
auto tab = conf.getRawWeapons ();
|
||||||
|
|
||||||
for (int i = 0; i < kNumWeapons; ++i) {
|
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;
|
groundIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pref++;
|
pref++;
|
||||||
}
|
}
|
||||||
int hasWeapon = 0;
|
auto hasWeapon = 0;
|
||||||
|
|
||||||
if (groundIndex < 7) {
|
if (groundIndex < kPrimaryWeaponMinIndex) {
|
||||||
hasWeapon = bestSecondaryCarried ();
|
hasWeapon = bestSecondaryCarried ();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -1521,7 +1522,7 @@ void Bot::selectBestWeapon () {
|
||||||
while (tab[selectIndex].id) {
|
while (tab[selectIndex].id) {
|
||||||
// is the bot NOT carrying this weapon?
|
// is the bot NOT carrying this weapon?
|
||||||
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
|
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
|
||||||
selectIndex++; // skip to next weapon
|
++selectIndex; // skip to next weapon
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1541,7 +1542,7 @@ void Bot::selectBestWeapon () {
|
||||||
if (ammoLeft) {
|
if (ammoLeft) {
|
||||||
chosenWeaponIndex = selectIndex;
|
chosenWeaponIndex = selectIndex;
|
||||||
}
|
}
|
||||||
selectIndex++;
|
++selectIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
chosenWeaponIndex %= kNumWeapons + 1;
|
chosenWeaponIndex %= kNumWeapons + 1;
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,12 @@ void BotConfig::loadMainConfig (bool isFirstLoad) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bind the correct menu key for bot menu...
|
// bind the correct menu key for bot menu...
|
||||||
if (!game.isDedicated () && !strings.isEmpty (cv_bind_menu_key.str ())) {
|
if (!game.isDedicated ()) {
|
||||||
game.serverCommand ("bind \"%s\" \"yb menu\"", cv_bind_menu_key.str ());
|
auto val = cv_bind_menu_key.str ();
|
||||||
|
|
||||||
|
if (!val.empty ()) {
|
||||||
|
game.serverCommand ("bind \"%s\" \"yb menu\"", val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable logger if requested
|
// disable logger if requested
|
||||||
|
|
@ -500,7 +504,7 @@ void BotConfig::loadLanguageConfig () {
|
||||||
}
|
}
|
||||||
file.close ();
|
file.close ();
|
||||||
}
|
}
|
||||||
else if (cr::strcmp (cv_language.str (), "en") != 0) {
|
else if (cv_language.str () != "en") {
|
||||||
logger.error ("Couldn't load language configuration");
|
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);
|
auto configDir = strings.joinPath (folders.addons, folders.bot, folders.config);
|
||||||
|
|
||||||
if (languageDependant) {
|
if (languageDependant) {
|
||||||
if (fileName.startsWith ("lang") && cr::strcmp (cv_language.str (), "en") == 0) {
|
if (fileName.startsWith ("lang") && cv_language.str () == "en") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto langConfig = strings.joinPath (configDir, folders.lang, strings.format ("%s_%s.%s", cv_language.str (), fileName, kConfigExtension));
|
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 ())) {
|
if (!isSave && !match.empty () && !strstr (cvar.reg.name, match.chars ())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto val = cvar.self->str ();
|
||||||
|
|
||||||
// float value ?
|
// float value ?
|
||||||
bool isFloat = !strings.isEmpty (cvar.self->str ()) && strchr (cvar.self->str (), '.');
|
bool isFloat = !val.empty () && val.find (".") != StringRef::InvalidIndex;
|
||||||
|
|
||||||
if (isSave) {
|
if (isSave) {
|
||||||
cfg.puts ("//\n");
|
cfg.puts ("//\n");
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,12 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
||||||
if (!ent || ent->v.classname == 0) {
|
if (!ent || ent->v.classname == 0) {
|
||||||
continue;
|
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;
|
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 ())) {
|
if (is (GameFlags::Legacy) && strings.isEmpty (ent->v.target.chars ())) {
|
||||||
ent->v.target = ent->v.targetname = engfuncs.pfnAllocString ("fake");
|
ent->v.target = ent->v.targetname = engfuncs.pfnAllocString ("fake");
|
||||||
}
|
}
|
||||||
|
|
@ -107,30 +107,30 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
||||||
engfuncs.pfnRemoveEntity (ent);
|
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.rendermode = kRenderTransAlpha; // set its render mode to transparency
|
||||||
ent->v.renderamt = 127; // set its transparency amount
|
ent->v.renderamt = 127; // set its transparency amount
|
||||||
ent->v.effects |= EF_NODRAW;
|
ent->v.effects |= EF_NODRAW;
|
||||||
|
|
||||||
++m_spawnCount[Team::CT];
|
++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.rendermode = kRenderTransAlpha; // set its render mode to transparency
|
||||||
ent->v.renderamt = 127; // set its transparency amount
|
ent->v.renderamt = 127; // set its transparency amount
|
||||||
ent->v.effects |= EF_NODRAW;
|
ent->v.effects |= EF_NODRAW;
|
||||||
|
|
||||||
++m_spawnCount[Team::Terrorist];
|
++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
|
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
|
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
|
m_mapFlags |= MapFlags::Demolition; // defusion map
|
||||||
}
|
}
|
||||||
else if (cr::strcmp (classname, "func_escapezone") == 0) {
|
else if (classname == "func_escapezone") {
|
||||||
m_mapFlags |= MapFlags::Escape;
|
m_mapFlags |= MapFlags::Escape;
|
||||||
|
|
||||||
// strange thing on some ES maps, where hostage entity present there
|
// 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;
|
m_mapFlags &= ~MapFlags::HostageRescue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cr::strncmp (classname, "func_door", 9) == 0) {
|
else if (classname.startsWith ("func_door")) {
|
||||||
m_mapFlags |= MapFlags::HasDoors;
|
m_mapFlags |= MapFlags::HasDoors;
|
||||||
}
|
}
|
||||||
else if (cr::strncmp (classname, "func_button", 11) == 0) {
|
else if (classname.startsWith ("func_button")) {
|
||||||
m_mapFlags |= MapFlags::HasButtons;
|
m_mapFlags |= MapFlags::HasButtons;
|
||||||
}
|
}
|
||||||
else if (isShootableBreakable (ent)) {
|
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
|
// next maps doesn't have map-specific entities, so determine it by name
|
||||||
if (!cv_ignore_map_prefix_game_mode.bool_ ()) {
|
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;
|
m_mapFlags |= MapFlags::FightYard;
|
||||||
}
|
}
|
||||||
else if (cr::strncmp (getMapName (), "ka_", 3) == 0) {
|
else if (prefix.startsWith ("ka_")) {
|
||||||
m_mapFlags |= MapFlags::KnifeArena;
|
m_mapFlags |= MapFlags::KnifeArena;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -767,9 +769,9 @@ void Game::registerCvars (bool gameVars) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Game::loadCSBinary () {
|
bool Game::loadCSBinary () {
|
||||||
auto modname = getRunningModName ();
|
StringRef modname = getRunningModName ();
|
||||||
|
|
||||||
if (!modname) {
|
if (modname.empty ()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Array <StringRef> libs { "mp", "cs", "cs_i386" };
|
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
|
// 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);
|
m_gameFlags |= (GameFlags::ConditionZero | GameFlags::HasBotVoice | GameFlags::HasFakePings);
|
||||||
|
|
||||||
if (is (GameFlags::Metamod)) {
|
if (is (GameFlags::Metamod)) {
|
||||||
|
|
@ -1071,9 +1073,15 @@ bool Game::isShootableBreakable (edict_t *ent) {
|
||||||
}
|
}
|
||||||
auto limit = cv_breakable_health_limit.float_ ();
|
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)) {
|
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;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1274,7 +1274,7 @@ void BotGraph::calculatePathRadius (int index) {
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
game.testLine (radiusStart, radiusEnd, TraceIgnore::Monsters, nullptr, &tr);
|
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;
|
path.radius = 0.0f;
|
||||||
wayBlocked = true;
|
wayBlocked = true;
|
||||||
|
|
||||||
|
|
@ -1571,12 +1571,13 @@ bool BotGraph::loadGraphData () {
|
||||||
for (const auto &path : m_paths) {
|
for (const auto &path : m_paths) {
|
||||||
addToBucket (path.origin, path.number);
|
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);
|
m_graphAuthor.assign (product.name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_graphAuthor.assign (exten.author);
|
m_graphAuthor.assign (author);
|
||||||
}
|
}
|
||||||
StringRef modified = exten.modified;
|
StringRef modified = exten.modified;
|
||||||
|
|
||||||
|
|
@ -1607,7 +1608,7 @@ bool BotGraph::loadGraphData () {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BotGraph::canDownload () {
|
bool BotGraph::canDownload () {
|
||||||
return !strings.isEmpty (cv_graph_url.str ());
|
return !cv_graph_url.str ().empty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BotGraph::saveGraphData () {
|
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
|
// check if we go through a func_illusionary, in which case return false
|
||||||
game.testHull (src, destination, TraceIgnore::Monsters, head_hull, m_editor, &tr);
|
game.testHull (src, destination, TraceIgnore::Monsters, head_hull, m_editor, &tr);
|
||||||
|
|
||||||
if (tr.pHit && cr::strcmp ("func_illusionary", tr.pHit->v.classname.chars ()) == 0) {
|
if (tr.pHit && tr.pHit->v.classname.str () == "func_illusionary") {
|
||||||
return false; // don't add pathnodes through func_illusionaries
|
return false; // don't add path nodes through func_illusionaries
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this node is "visible"...
|
// check if this node is "visible"...
|
||||||
game.testLine (src, destination, TraceIgnore::Monsters, m_editor, &tr);
|
game.testLine (src, destination, TraceIgnore::Monsters, m_editor, &tr);
|
||||||
|
|
||||||
// if node is visible from current position (even behind head)...
|
// 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 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);
|
game.testLine (tr.vecEndPos, destination, TraceIgnore::Monsters, tr.pHit, &tr);
|
||||||
|
|
||||||
if (tr.flFraction < 1.0f) {
|
if (tr.flFraction < 1.0f) {
|
||||||
|
|
|
||||||
|
|
@ -1016,7 +1016,7 @@ void EntityLinkage::callPlayerFunction (edict_t *ent) {
|
||||||
playerFunction = game.lib ().resolve <EntityFunction> ("player");
|
playerFunction = game.lib ().resolve <EntityFunction> ("player");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
playerFunction = reinterpret_cast <EntityFunction> (reinterpret_cast <void *> (lookup (game.lib ().handle (), "player")));
|
playerFunction = reinterpret_cast <EntityFunction> (lookup (game.lib ().handle (), "player"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!playerFunction) {
|
if (!playerFunction) {
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ BotCreateResult BotManager::create (StringRef name, int difficulty, int personal
|
||||||
else {
|
else {
|
||||||
resultName = name;
|
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
|
// disable save bots names if prefix is enabled
|
||||||
if (hasNamePrefix && cv_save_bots_names.bool_ ()) {
|
if (hasNamePrefix && cv_save_bots_names.bool_ ()) {
|
||||||
|
|
@ -390,10 +390,10 @@ void BotManager::maintainQuota () {
|
||||||
int desiredBotCount = cv_quota.int_ ();
|
int desiredBotCount = cv_quota.int_ ();
|
||||||
int botsInGame = getBotCount ();
|
int botsInGame = getBotCount ();
|
||||||
|
|
||||||
if (strings.matches (cv_quota_mode.str (), "fill")) {
|
if (cv_quota_mode.str () == "fill") {
|
||||||
botsInGame += humanPlayersInGame;
|
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_ ();
|
int detectQuotaMatch = cv_quota_match.int_ () == 0 ? cv_quota.int_ () : cv_quota_match.int_ ();
|
||||||
|
|
||||||
desiredBotCount = cr::max <int> (0, detectQuotaMatch * humanPlayersInGame);
|
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 ()) {
|
if (game.isBotCmd ()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strings.matches (cmd, "say") || strings.matches (cmd, "say_team")) {
|
if (cmd.startsWith ("say")) {
|
||||||
bool alive = util.isAlive (ent);
|
bool alive = util.isAlive (ent);
|
||||||
int team = -1;
|
int team = -1;
|
||||||
|
|
||||||
if (cr::strcmp (cmd, "say_team") == 0) {
|
if (cmd == "say_team") {
|
||||||
team = game.getTeam (ent);
|
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));
|
auto &target = util.getClient (game.indexOfPlayer (ent));
|
||||||
|
|
||||||
// check if this player alive, and issue something
|
// check if this player alive, and issue something
|
||||||
if ((target.flags & ClientFlags::Alive) && target.radio != 0 && cr::strncmp (cmd, "menuselect", 10) == 0) {
|
if ((target.flags & ClientFlags::Alive) && target.radio != 0 && cmd.startsWith ("menuselect")) {
|
||||||
int radioCommand = atoi (arg);
|
auto radioCommand = arg.int_ ();
|
||||||
|
|
||||||
if (radioCommand != 0) {
|
if (radioCommand != 0) {
|
||||||
radioCommand += 10 * (target.radio - 1);
|
radioCommand += 10 * (target.radio - 1);
|
||||||
|
|
@ -1717,8 +1717,8 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
|
||||||
}
|
}
|
||||||
target.radio = 0;
|
target.radio = 0;
|
||||||
}
|
}
|
||||||
else if (cr::strncmp (cmd, "radio", 5) == 0) {
|
else if (cmd.startsWith ("radio")) {
|
||||||
target.radio = atoi (&cmd[5]);
|
target.radio = cmd.substr (5).int_ ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1774,25 +1774,25 @@ void BotManager::updateInterestingEntities () {
|
||||||
|
|
||||||
// search the map for any type of grenade
|
// search the map for any type of grenade
|
||||||
game.searchEntities (nullptr, kInfiniteDistance, [&] (edict_t *e) {
|
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
|
// 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);
|
m_interestingEntities.push (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pickup some csdm stuff if we're running csdm
|
// pickup some hostage if on cs_ maps
|
||||||
if (game.mapIs (MapFlags::HostageRescue) && cr::strncmp ("hostage", classname, 7) == 0) {
|
if (game.mapIs (MapFlags::HostageRescue) && classname.startsWith ("hostage")) {
|
||||||
m_interestingEntities.push (e);
|
m_interestingEntities.push (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add buttons
|
// 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);
|
m_interestingEntities.push (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pickup some csdm stuff if we're running csdm
|
// 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);
|
m_interestingEntities.push (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -621,7 +621,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seesEntity (m_destOrigin)) {
|
if (seesEntity (m_destOrigin)) {
|
||||||
const auto &right = m_moveAngles.right ();
|
auto right = m_moveAngles.right ();
|
||||||
|
|
||||||
src = getEyesPos ();
|
src = getEyesPos ();
|
||||||
src = src + right * 15.0f;
|
src = src + right * 15.0f;
|
||||||
|
|
@ -1012,7 +1012,7 @@ bool Bot::updateNavigation () {
|
||||||
if (game.mapIs (MapFlags::HasDoors)) {
|
if (game.mapIs (MapFlags::HasDoors)) {
|
||||||
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Monsters, ent (), &tr);
|
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 the door is near enough...
|
||||||
if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) {
|
if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) {
|
||||||
ignoreCollision (); // don't consider being stuck
|
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 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);
|
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
|
m_timeDoorOpen = game.time () + 1.0f; // retry in 1 sec until door is open
|
||||||
|
|
||||||
edict_t *pent = nullptr;
|
edict_t *pent = nullptr;
|
||||||
|
|
||||||
if (++m_tryOpenDoor > 2 && util.findNearestPlayer (reinterpret_cast <void **> (&pent), ent (), 256.0f, false, false, true, true, false)) {
|
if (++m_tryOpenDoor > 1 && util.findNearestPlayer (reinterpret_cast <void **> (&pent), ent (), 384.0f, false, false, true, true, false)) {
|
||||||
m_seeEnemyTime = game.time () - 0.5f;
|
if (isPenetrableObstacle (pent->v.origin)) {
|
||||||
|
m_seeEnemyTime = game.time ();
|
||||||
|
|
||||||
m_states |= Sense::SeeingEnemy;
|
m_states |= Sense::SeeingEnemy | Sense::SuspectEnemy;
|
||||||
m_aimFlags |= AimFlags::Enemy;
|
m_aimFlags |= AimFlags::Enemy;
|
||||||
|
|
||||||
m_lastEnemy = pent;
|
m_lastEnemy = pent;
|
||||||
m_enemy = pent;
|
m_enemy = pent;
|
||||||
m_lastEnemyOrigin = pent->v.origin;
|
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;
|
m_tryOpenDoor = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1191,7 +1196,7 @@ bool Bot::updateLiftHandling () {
|
||||||
// trace line to door
|
// trace line to door
|
||||||
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
|
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) {
|
if (m_liftState == LiftState::None) {
|
||||||
m_liftState = LiftState::LookingButtonOutside;
|
m_liftState = LiftState::LookingButtonOutside;
|
||||||
m_liftUsageTime = game.time () + 7.0f;
|
m_liftUsageTime = game.time () + 7.0f;
|
||||||
|
|
@ -1199,11 +1204,16 @@ bool Bot::updateLiftHandling () {
|
||||||
liftClosedDoorExists = true;
|
liftClosedDoorExists = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper
|
||||||
|
auto isFunc = [] (StringRef cls) -> bool {
|
||||||
|
return cls.startsWith ("func_door") || cls == "func_plat" || cls == "func_train";
|
||||||
|
};
|
||||||
|
|
||||||
// trace line down
|
// trace line down
|
||||||
game.testLine (m_path->origin, m_pathOrigin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
|
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 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 ((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) {
|
if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) {
|
||||||
m_liftEntity = tr.pHit;
|
m_liftEntity = tr.pHit;
|
||||||
|
|
@ -1225,7 +1235,7 @@ bool Bot::updateLiftHandling () {
|
||||||
if (graph.exists (nextNode) && (graph[nextNode].flags & NodeFlag::Lift)) {
|
if (graph.exists (nextNode) && (graph[nextNode].flags & NodeFlag::Lift)) {
|
||||||
game.testLine (m_path->origin, graph[nextNode].origin, TraceIgnore::Everything, ent (), &tr);
|
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;
|
m_liftEntity = tr.pHit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1832,7 +1842,7 @@ int Bot::findDefendNode (const Vector &origin) {
|
||||||
int srcIndex = m_currentNodeIndex;
|
int srcIndex = m_currentNodeIndex;
|
||||||
|
|
||||||
// max search distance
|
// 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
|
// some of points not found, return random one
|
||||||
if (srcIndex == kInvalidNodeIndex || posIndex == kInvalidNodeIndex) {
|
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...
|
// first do a trace from the bot's eyes forward...
|
||||||
auto src = getEyesPos ();
|
auto src = getEyesPos ();
|
||||||
auto forward = src + normal * 24.0f;
|
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) {
|
auto checkDoor = [] (TraceResult *result) {
|
||||||
if (!game.mapIs (MapFlags::HasDoors)) {
|
if (!game.mapIs (MapFlags::HasDoors)) {
|
||||||
return false;
|
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...
|
// 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...
|
// check if the trace hit something...
|
||||||
if (tr->flFraction < 1.0f) {
|
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 false;
|
||||||
}
|
}
|
||||||
return true; // bot's head will hit something
|
return true; // bot's head will hit something
|
||||||
|
|
@ -2529,7 +2539,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
||||||
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
|
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
|
||||||
return false;
|
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...
|
// check for normal jump height first...
|
||||||
auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
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...
|
// 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...
|
// now check same height to one side of the bot...
|
||||||
src = baseHeight + right * 16.0f;
|
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);
|
game.testLine (pev->origin, forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// 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 true; // bot's body will hit something
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2754,7 +2764,7 @@ bool Bot::isBlockedRight () {
|
||||||
game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||||
|
|
||||||
// check if the trace hit something...
|
// 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 true; // bot's body will hit something
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2939,7 +2949,7 @@ int Bot::getRandomCampDir () {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) {
|
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 ();
|
float dot = los | pev->angles.forward ().get2d ();
|
||||||
|
|
||||||
if (dot > 0.0f && !checkWallOnRight ()) {
|
if (dot > 0.0f && !checkWallOnRight ()) {
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ AStarResult AStarAlgo::find (int botTeam, int srcIndex, int destIndex, NodeAdder
|
||||||
|
|
||||||
void FloydWarshallAlgo::rebuild () {
|
void FloydWarshallAlgo::rebuild () {
|
||||||
m_length = graph.length ();
|
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] () {
|
worker.enqueue ([this] () {
|
||||||
syncRebuild ();
|
syncRebuild ();
|
||||||
|
|
@ -416,8 +416,10 @@ bool FloydWarshallAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNo
|
||||||
void DijkstraAlgo::init (const int length) {
|
void DijkstraAlgo::init (const int length) {
|
||||||
m_length = length;
|
m_length = length;
|
||||||
|
|
||||||
m_distance.resize (length);
|
auto ulength = static_cast <size_t> (length);
|
||||||
m_parent.resize (length);
|
|
||||||
|
m_distance.resize (ulength);
|
||||||
|
m_parent.resize (ulength);
|
||||||
|
|
||||||
m_distance.shrink ();
|
m_distance.shrink ();
|
||||||
m_parent.shrink ();
|
m_parent.shrink ();
|
||||||
|
|
@ -435,7 +437,7 @@ bool DijkstraAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, i
|
||||||
m_distance[srcIndex] = 0;
|
m_distance[srcIndex] = 0;
|
||||||
|
|
||||||
while (!m_queue.empty ()) {
|
while (!m_queue.empty ()) {
|
||||||
auto &&route = cr::move (m_queue.pop ());
|
auto route = m_queue.pop ();
|
||||||
auto current = route.second;
|
auto current = route.second;
|
||||||
|
|
||||||
// finished search
|
// finished search
|
||||||
|
|
@ -500,7 +502,7 @@ void PathPlanner::init () {
|
||||||
const int length = graph.length ();
|
const int length = graph.length ();
|
||||||
|
|
||||||
const float limitInMb = cv_path_floyd_memory_limit.float_ ();
|
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 we're have too much memory for floyd matrices, planner will use dijkstra or uniform planner for other than pathfinding needs
|
||||||
if (memoryUse > limitInMb) {
|
if (memoryUse > limitInMb) {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ void BotPractice::syncUpdate () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constexpr auto kFullDamageVal = static_cast <int32_t> (PracticeLimit::Damage);
|
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
|
// adjust values if overflow is about to happen
|
||||||
if (adjustValues) {
|
if (adjustValues) {
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ bool BotSupport::isMonster (edict_t *ent) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cr::strncmp ("hostage", ent->v.classname.chars (), 7) == 0) {
|
if (ent->v.classname.str ().startsWith ("hostage")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +212,7 @@ bool BotSupport::isMonster (edict_t *ent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BotSupport::isItem (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) {
|
bool BotSupport::isPlayerVIP (edict_t *ent) {
|
||||||
|
|
|
||||||
|
|
@ -1426,9 +1426,11 @@ void Bot::pickupItem_ () {
|
||||||
switch (m_pickupType) {
|
switch (m_pickupType) {
|
||||||
case Pickup::DroppedC4:
|
case Pickup::DroppedC4:
|
||||||
case Pickup::None:
|
case Pickup::None:
|
||||||
|
case Pickup::Items:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Pickup::Weapon:
|
case Pickup::Weapon:
|
||||||
|
case Pickup::AmmoAndKits:
|
||||||
m_aimFlags |= AimFlags::Nav;
|
m_aimFlags |= AimFlags::Nav;
|
||||||
|
|
||||||
// near to weapon?
|
// near to weapon?
|
||||||
|
|
@ -1436,17 +1438,17 @@ void Bot::pickupItem_ () {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
auto &info = conf.getWeapons ();
|
auto &info = conf.getWeapons ();
|
||||||
|
|
||||||
for (index = 0; index < 7; ++index) {
|
for (index = 0; index < kPrimaryWeaponMinIndex; ++index) {
|
||||||
if (cr::strcmp (info[index].model, m_pickupItem->v.model.chars (9)) == 0) {
|
if (m_pickupItem->v.model.str (9) == info[index].model) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < 7) {
|
if (index < kPrimaryWeaponMinIndex) {
|
||||||
// secondary weapon. i.e., pistol
|
// secondary weapon. i.e., pistol
|
||||||
int weaponIndex = 0;
|
int weaponIndex = 0;
|
||||||
|
|
||||||
for (index = 0; index < 7; ++index) {
|
for (index = 0; index < kPrimaryWeaponMinIndex; ++index) {
|
||||||
if (pev->weapons & cr::bit (info[index].id)) {
|
if (pev->weapons & cr::bit (info[index].id)) {
|
||||||
weaponIndex = index;
|
weaponIndex = index;
|
||||||
}
|
}
|
||||||
|
|
@ -1469,7 +1471,7 @@ void Bot::pickupItem_ () {
|
||||||
|
|
||||||
auto tab = conf.getRawWeapons ();
|
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);
|
selectWeaponByIndex (weaponIndex);
|
||||||
dropCurrentWeapon ();
|
dropCurrentWeapon ();
|
||||||
}
|
}
|
||||||
|
|
@ -1559,9 +1561,9 @@ void Bot::pickupItem_ () {
|
||||||
|
|
||||||
// find the nearest 'unused' hostage within the area
|
// find the nearest 'unused' hostage within the area
|
||||||
game.searchEntities (pev->origin, 768.0f, [&] (edict_t *ent) {
|
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;
|
return EntitySearchResult::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ bool Bot::isInViewCone (const Vector &origin) {
|
||||||
return util.isInViewCone (origin, ent ());
|
return util.isInViewCone (origin, ent ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::seesItem (const Vector &destination, const char *classname) {
|
bool Bot::seesItem (const Vector &destination, StringRef classname) {
|
||||||
TraceResult tr {};
|
TraceResult tr {};
|
||||||
|
|
||||||
// trace a line from bot's eyes to destination..
|
// 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)
|
// check if line of sight to object is not blocked (i.e. visible)
|
||||||
if (tr.flFraction < 1.0f && tr.pHit && !tr.fStartSolid) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +106,7 @@ void Bot::updateAimDir () {
|
||||||
m_lookAt.z += 48.0f;
|
m_lookAt.z += 48.0f;
|
||||||
}
|
}
|
||||||
else if (m_pickupType == Pickup::Weapon) {
|
else if (m_pickupType == Pickup::Weapon) {
|
||||||
m_lookAt.z += 72.0;
|
m_lookAt.z += 72.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (flags & AimFlags::LastEnemy) {
|
else if (flags & AimFlags::LastEnemy) {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
<ClInclude Include="..\ext\linkage\linkage\physint.h" />
|
<ClInclude Include="..\ext\linkage\linkage\physint.h" />
|
||||||
<ClInclude Include="..\inc\analyze.h" />
|
<ClInclude Include="..\inc\analyze.h" />
|
||||||
<ClInclude Include="..\inc\config.h" />
|
<ClInclude Include="..\inc\config.h" />
|
||||||
|
<ClInclude Include="..\inc\constant.h" />
|
||||||
<ClInclude Include="..\inc\control.h" />
|
<ClInclude Include="..\inc\control.h" />
|
||||||
<ClInclude Include="..\inc\engine.h" />
|
<ClInclude Include="..\inc\engine.h" />
|
||||||
<ClInclude Include="..\inc\graph.h" />
|
<ClInclude Include="..\inc\graph.h" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue