fix: bots not throwing grenades since last commit

combat: various fixes to combat movements
combat: tweaked grenade throwing code
refactor: convert some arrays initializations  to initializer lists
build: switch docker image to clang 18.1
This commit is contained in:
jeefo 2024-03-09 01:06:11 +03:00
commit 2caa65f6ad
No known key found for this signature in database
GPG key ID: 927BCA0779BEA8ED
14 changed files with 253 additions and 221 deletions

View file

@ -1108,16 +1108,16 @@ void Bot::checkMsgQueue () {
}
}
bool Bot::isWeaponRestricted (int weaponIndex) {
bool Bot::isWeaponRestricted (int wid) {
// this function checks for weapon restrictions.
auto val = cv_restricted_weapons.str ();
if (val.empty ()) {
return isWeaponRestrictedAMX (weaponIndex); // no banned weapons
return isWeaponRestrictedAMX (wid); // no banned weapons
}
const auto &bannedWeapons = val.split <String> (";");
const auto &alias = util.weaponIdToAlias (weaponIndex);
const auto &alias = util.weaponIdToAlias (wid);
for (const auto &ban : bannedWeapons) {
// check is this weapon is banned
@ -1125,24 +1125,24 @@ bool Bot::isWeaponRestricted (int weaponIndex) {
return true;
}
}
return isWeaponRestrictedAMX (weaponIndex);
return isWeaponRestrictedAMX (wid);
}
bool Bot::isWeaponRestrictedAMX (int weaponIndex) {
bool Bot::isWeaponRestrictedAMX (int wid) {
// this function checks restriction set by AMX Mod, this function code is courtesy of KWo.
if (!game.is (GameFlags::Metamod)) {
return false;
}
auto checkRestriction = [&weaponIndex] (StringRef cvar, const int *data) -> bool {
auto checkRestriction = [&wid] (StringRef cvar, const int *data) -> bool {
auto restrictedWeapons = game.findCvar (cvar);
if (restrictedWeapons.empty ()) {
return false;
}
// find the weapon index
int index = data[weaponIndex - 1];
const auto index = data[wid - 1];
// validate index range
if (index < 0 || index >= static_cast <int> (restrictedWeapons.length ())) {
@ -1152,7 +1152,7 @@ bool Bot::isWeaponRestrictedAMX (int weaponIndex) {
};
// check for weapon restrictions
if (cr::bit (weaponIndex) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) {
if (cr::bit (wid) & (kPrimaryWeaponMask | kSecondaryWeaponMask | Weapon::Shield)) {
constexpr int ids[] = { 4, 25, 20, -1, 8, -1, 12, 19, -1, 5, 6, 13, 23, 17, 18, 1, 2, 21, 9, 24, 7, 16, 10, 22, -1, 3, 15, 14, 0, 11 };
// verify restrictions
@ -1827,10 +1827,7 @@ void Bot::setConditions () {
pushRadioMessage (Radio::EnemyDown);
}
else if (rg.chance (60)) {
if ((m_lastVictim->v.weapons & cr::bit (Weapon::AWP))
|| (m_lastVictim->v.weapons & cr::bit (Weapon::Scout))
|| (m_lastVictim->v.weapons & cr::bit (Weapon::G3SG1))
|| (m_lastVictim->v.weapons & cr::bit (Weapon::SG550))) {
if (m_lastVictim->v.weapons & kSniperWeaponMask) {
pushChatterMessage (Chatter::SniperKilled);
}
@ -1907,7 +1904,7 @@ void Bot::setConditions () {
// clear the last enemy pointers if time has passed or enemy far away
if (!m_lastEnemyOrigin.empty ()) {
auto distanceSq = pev->origin.distanceSq (m_lastEnemyOrigin);
const auto distanceSq = pev->origin.distanceSq (m_lastEnemyOrigin);
if (distanceSq > cr::sqrf (2048.0f) || (game.isNullEntity (m_enemy) && m_seeEnemyTime + 10.0f < game.time ())) {
m_lastEnemyOrigin = nullptr;
@ -2312,7 +2309,7 @@ bool Bot::lastEnemyShootable () {
if (!(m_aimFlags & (AimFlags::LastEnemy | AimFlags::PredictPath)) || m_lastEnemyOrigin.empty () || game.isNullEntity (m_lastEnemy)) {
return false;
}
return util.getShootingCone (ent (), m_lastEnemyOrigin) >= 0.90f && isPenetrableObstacle (m_lastEnemyOrigin);
return util.getConeDeviation (ent (), m_lastEnemyOrigin) >= 0.90f && isPenetrableObstacle (m_lastEnemyOrigin);
}
void Bot::checkRadioQueue () {
@ -2966,7 +2963,8 @@ void Bot::logicDuringFreezetime () {
pev->button |= IN_JUMP;
m_jumpTime = game.time ();
}
Array <Bot *> teammates;
static Array <Bot *> teammates;
teammates.clear ();
for (const auto &bot : bots) {
if (bot->m_isAlive && bot->m_team == m_team && seesEntity (bot->pev->origin) && bot.get () != this) {
@ -3127,10 +3125,7 @@ void Bot::logic () {
}
else if (!hasFriendNearby
&& rg.chance (40)
&& ((m_enemy->v.weapons & cr::bit (Weapon::AWP))
|| (m_enemy->v.weapons & cr::bit (Weapon::Scout))
|| (m_enemy->v.weapons & cr::bit (Weapon::G3SG1))
|| (m_enemy->v.weapons & cr::bit (Weapon::SG550)))) {
&& (m_enemy->v.weapons & kSniperWeaponMask)) {
pushChatterMessage (Chatter::SniperWarning);
}
@ -3163,7 +3158,7 @@ void Bot::logic () {
updateLookAngles (); // and turn to chosen aim direction
// the bots wants to fire at something?
if (m_shootAtDeadTime > game.time () || (m_wantsToFire && !m_isUsingGrenade && (m_shootTime <= game.time ()))) {
if (m_shootAtDeadTime > game.time () || (m_wantsToFire && !m_isUsingGrenade && m_shootTime <= game.time ())) {
fireWeapons (); // if bot didn't fire a bullet try again next frame
}
@ -3750,32 +3745,29 @@ void Bot::runMovement () {
// problems, such as bots getting stuck into each others. That's because the model's
// bounding boxes, which are the boxes the engine uses to compute and detect all the
// collisions of the model, only exist, and are only valid, while in the duration of the
// movement. That's why if you get a pfnRunPlayerMove for one boINFt that lasts a little too
// movement. That's why if you get a pfnRunPlayerMove for one bot that lasts a little too
// short in comparison with the frame's duration, the remaining time until the frame
// elapses, that bot will behave like a ghost : no movement, but bullets and players can
// pass through it. Then, when the next frame will begin, the stucking problem will arise !
m_frameInterval = game.time () - m_lastCommandTime;
const uint8_t msecVal = computeMsec ();
const auto msecVal = computeMsec ();
m_lastCommandTime = game.time ();
engfuncs.pfnRunPlayerMove (pev->pContainingEntity, m_moveAngles, m_moveSpeed, m_strafeSpeed, 0.0f, static_cast <uint16_t> (pev->button), static_cast <uint8_t> (pev->impulse), msecVal);
engfuncs.pfnRunPlayerMove (pev->pContainingEntity,
getRpmAngles (), m_moveSpeed, m_strafeSpeed,
0.0f, static_cast <uint16_t> (pev->button), static_cast <uint8_t> (pev->impulse), msecVal);
// save our own copy of old buttons, since bot ai code is not running every frame now
// save our own copy of old buttons, since bot bot code is not running every frame now
m_oldButtons = pev->button;
}
float Bot::getBombTimeleft () {
float Bot::getBombTimeleft () const {
if (!bots.isBombPlanted ()) {
return 0.0f;
}
const float timeLeft = ((bots.getTimeBombPlanted () + mp_c4timer.float_ ()) - game.time ());
if (timeLeft < 0.0f) {
return 0.0f;
}
return timeLeft;
return cr::max (bots.getTimeBombPlanted () + mp_c4timer.float_ () - game.time (), 0.0f);
}
bool Bot::isOutOfBombTimer () {

View file

@ -11,28 +11,14 @@ ConVar cv_chat ("chat", "1", "Enables or disables bots chat functionality.");
ConVar cv_chat_percent ("chat_percent", "30", "Bot chances to send random dead chat when killed.", true, 0.0f, 100.0f);
BotChatManager::BotChatManager () {
m_clanTags.emplace ("[[", "]]");
m_clanTags.emplace ("-=", "=-");
m_clanTags.emplace ("-[", "]-");
m_clanTags.emplace ("-]", "[-");
m_clanTags.emplace ("-}", "{-");
m_clanTags.emplace ("-{", "}-");
m_clanTags.emplace ("<[", "]>");
m_clanTags.emplace ("<]", "[>");
m_clanTags.emplace ("[-", "-]");
m_clanTags.emplace ("]-", "-[");
m_clanTags.emplace ("{-", "-}");
m_clanTags.emplace ("}-", "-{");
m_clanTags.emplace ("[", "]");
m_clanTags.emplace ("{", "}");
m_clanTags.emplace ("<", "[");
m_clanTags.emplace (">", "<");
m_clanTags.emplace ("-", "-");
m_clanTags.emplace ("|", "|");
m_clanTags.emplace ("=", "=");
m_clanTags.emplace ("+", "+");
m_clanTags.emplace ("(", ")");
m_clanTags.emplace (")", "(");
m_clanTags = {
{ "[[", "]]" }, { "-=", "=-" }, { "-[", "]-" }, { "-]", "[-" },
{ "-}", "{-" }, { "-{", "}-" }, { "<[", "]>" }, { "<]", "[>" },
{ "[-", "-]" }, { "]-", "-[" }, { "{-", "-}" }, { "}-", "-{" },
{ "[", "]" }, { "{", "}" }, { "<", "[" }, { ">", "<" }, { ")", "(" },
{ "-", "-" }, { "|", "|" }, { "=", "=" }, { "+", "+" }, { "(", ")" },
};
}
void BotChatManager::stripTags (String &line) {

View file

@ -656,7 +656,7 @@ bool Bot::isFriendInLineOfFire (float distance) {
const auto friendDistanceSq = client.ent->v.origin.distanceSq (pev->origin);
if (friendDistanceSq <= distanceSq
&& util.getShootingCone (ent (), client.ent->v.origin) > friendDistanceSq / (friendDistanceSq + cr::sqrf (33.0f))) {
&& util.getConeDeviation (ent (), client.ent->v.origin) > friendDistanceSq / (friendDistanceSq + cr::sqrf (33.0f))) {
return true;
}
}
@ -805,7 +805,7 @@ bool Bot::needToPauseFiring (float distance) {
}
if ((m_aimFlags & AimFlags::Enemy) && !m_enemyOrigin.empty ()) {
if (util.getShootingCone (ent (), m_enemyOrigin) > 0.92f && isEnemyBehindShield (m_enemy)) {
if (util.getConeDeviation (ent (), m_enemyOrigin) > 0.92f && isEnemyBehindShield (m_enemy)) {
return true;
}
}
@ -1148,13 +1148,13 @@ void Bot::focusEnemy () {
}
}
else {
const float dot = util.getShootingCone (ent (), m_enemyOrigin);
const float dot = util.getConeDeviation (ent (), m_enemyOrigin);
if (dot < 0.90f) {
m_wantsToFire = false;
}
else {
const float enemyDot = util.getShootingCone (m_enemy, pev->origin);
const float enemyDot = util.getConeDeviation (m_enemy, pev->origin);
// enemy faces bot?
if (enemyDot >= 0.90f) {
@ -1209,9 +1209,16 @@ void Bot::attackMovement () {
// only take cover when bomb is not planted and enemy can see the bot or the bot is VIP
if (!game.is (GameFlags::CSDM)) {
if (m_retreatTime < game.time () && (m_states & Sense::SeeingEnemy) && approach < 30 && !bots.isBombPlanted () && (isInViewCone (m_enemy->v.origin) || m_isVIP)) {
m_moveSpeed = -pev->maxspeed;
startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true);
if (m_retreatTime < game.time () && approach < 30 && !bots.isBombPlanted ()) {
const auto enemyCone = util.getConeDeviation (m_enemy, pev->origin);
const auto seeingEnemy = (m_states & Sense::SeeingEnemy);
const auto enemyWeaponIsSniper = (m_enemy->v.weapons & kSniperWeaponMask);
// make bot seek cover
if ((enemyCone > 0.8f && seeingEnemy) || (enemyWeaponIsSniper && enemyCone > 0.95f)) {
startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, false);
m_moveSpeed = -pev->maxspeed;
}
}
else if (approach < 50) {
m_moveSpeed = 0.0f;
@ -1268,7 +1275,7 @@ void Bot::attackMovement () {
const auto pistolStrafeDistance = game.is (GameFlags::CSDM) ? kDoubleSprayDistance * 3.0f : kDoubleSprayDistance;
// fire hurts friend value here is from previous frame, but acceptable, and saves us alot of cpu cycles
if (m_fireHurtsFriend || ((usesPistol () || usesShotgun ())
if (approach < 30 || m_fireHurtsFriend || ((usesPistol () || usesShotgun ())
&& distance < pistolStrafeDistance
&& isInViewCone (m_enemyOrigin))) {
m_fightStyle = Fight::Strafe;
@ -1290,7 +1297,7 @@ void Bot::attackMovement () {
};
auto strafeUpdateTime = [] () {
return game.time () + rg.get (1.0f, 1.5f);
return game.time () + rg.get (0.8f, 1.25f);
};
// to start strafing, we have to first figure out if the target is on the left side or right side
@ -1346,7 +1353,7 @@ void Bot::attackMovement () {
}
// we're setting strafe speed regardless of move angles, so not resetting forward move here cause bots to behave strange
if (!usesKnife ()) {
if (!usesKnife () && approach >= 30) {
m_moveSpeed = 0.0f;
}
@ -1463,7 +1470,7 @@ int Bot::bestPrimaryCarried () {
int weaponIndex = 0;
int weapons = pev->weapons;
const auto &tab = conf.getRawWeapons ();
const auto tab = conf.getRawWeapons ();
// take the shield in account
if (hasShield ()) {
@ -1588,6 +1595,7 @@ void Bot::selectBestWeapon () {
// loop through all the weapons until terminator is found...
while (tab[selectIndex].id) {
// is the bot NOT carrying this weapon?
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
++selectIndex; // skip to next weapon
@ -1627,7 +1635,7 @@ void Bot::selectBestWeapon () {
}
void Bot::selectSecondary () {
int oldWeapons = pev->weapons;
const int oldWeapons = pev->weapons;
pev->weapons &= ~kPrimaryWeaponMask;
selectBestWeapon ();
@ -1649,14 +1657,15 @@ int Bot::bestWeaponCarried () {
num = i;
}
++i;
tab++;
++tab;
}
return num;
}
void Bot::decideFollowUser () {
// this function forces bot to follow user
Array <edict_t *> users;
static Array <edict_t *> users;
users.clear ();
// search friends near us
for (const auto &client : util.getClients ()) {
@ -1975,7 +1984,6 @@ void Bot::checkGrenadesThrow () {
|| isInNarrowPlace ()
|| cv_ignore_enemies.bool_ ()
|| m_isUsingGrenade
|| m_grenadeRequested
|| m_isReloading
|| (isKnifeMode () && !bots.isBombPlanted ())
|| m_grenadeCheckTime >= game.time ()
@ -2006,7 +2014,7 @@ void Bot::checkGrenadesThrow () {
clearThrowStates (m_states);
return;
}
else {
else if (!isGrenadeMode) {
int cancelProb = m_agressionLevel > m_fearLevel ? 5 : 20;
if (grenadeToThrow == Weapon::Flashbang) {
@ -2034,23 +2042,25 @@ void Bot::checkGrenadesThrow () {
// special condition if we're have valid current enemy
if (!isGrenadeMode && ((m_states & Sense::SeeingEnemy)
&& util.isAlive (m_enemy)
&& ((m_enemy->v.button | m_enemy->v.oldbuttons) & IN_ATTACK)
&& util.isInViewCone (pev->origin, m_enemy))) {
&& util.isAlive (m_enemy)
&& ((m_enemy->v.button | m_enemy->v.oldbuttons) & IN_ATTACK)
&& util.isVisible (pev->origin, m_enemy))
&& util.isInViewCone (pev->origin, m_enemy)) {
// do not throw away grenades if anyone is attacking us
distanceSq = kInfiniteDistance;
}
// don't throw away nades if just seen the enemy
if (!isGrenadeMode && m_seeEnemyTime + kGrenadeCheckTime * 0.2f < game.time ()) {
if (!isGrenadeMode && m_seeEnemyTime + kGrenadeCheckTime * 0.2f > game.time ()) {
distanceSq = kInfiniteDistance;
}
// enemy within a good throw distance?
const auto grenadeToThrowCondition = isGrenadeMode ? 100.0f : grenadeToThrow == Weapon::Smoke ? 200.0f : 350.0f;
const auto grenadeToThrowCondition =
isGrenadeMode ? kGrenadeDamageRadius / 4.0f : grenadeToThrow == Weapon::Smoke ? 200.0f : kGrenadeDamageRadius;
if (distanceSq > cr::sqrf (grenadeToThrowCondition) && distanceSq < cr::sqrf (1200.0f)) {
if (distanceSq > cr::sqrf (grenadeToThrowCondition) && distanceSq < cr::sqrf (kGrenadeDamageRadius * 3.0f)) {
bool allowThrowing = true;
// care about different grenades
@ -2060,8 +2070,8 @@ void Bot::checkGrenadesThrow () {
allowThrowing = false;
}
else {
const float radius = cr::max (192.0f, m_lastEnemy->v.velocity.length2d ());
const Vector &pos = m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin;
const auto radius = cr::max (192.0f, m_lastEnemy->v.velocity.length2d ());
const auto &pos = m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin;
auto predicted = graph.getNearestInRadius (radius, pos, 12);
@ -2143,7 +2153,7 @@ void Bot::checkGrenadesThrow () {
case Weapon::Smoke:
if (allowThrowing && !game.isNullEntity (m_lastEnemy)) {
if (util.getShootingCone (m_lastEnemy, pev->origin) >= 0.9f) {
if (util.getConeDeviation (m_lastEnemy, pev->origin) >= 0.9f) {
allowThrowing = false;
}
}
@ -2160,16 +2170,16 @@ void Bot::checkGrenadesThrow () {
clearThrowStates (m_states);
return;
}
const float kMaxThrowTime = game.time () + kGrenadeCheckTime * 4.0f;
const float maxThrowTime = game.time () + kGrenadeCheckTime * 3.6f;
if (m_states & Sense::ThrowExplosive) {
startTask (Task::ThrowExplosive, TaskPri::Throw, kInvalidNodeIndex, kMaxThrowTime, false);
startTask (Task::ThrowExplosive, TaskPri::Throw, kInvalidNodeIndex, maxThrowTime, false);
}
else if (m_states & Sense::ThrowFlashbang) {
startTask (Task::ThrowFlashbang, TaskPri::Throw, kInvalidNodeIndex, kMaxThrowTime, false);
startTask (Task::ThrowFlashbang, TaskPri::Throw, kInvalidNodeIndex, maxThrowTime, false);
}
else if (m_states & Sense::ThrowSmoke) {
startTask (Task::ThrowSmoke, TaskPri::Throw, kInvalidNodeIndex, kMaxThrowTime, false);
startTask (Task::ThrowSmoke, TaskPri::Throw, kInvalidNodeIndex, maxThrowTime, false);
}
}
else {

View file

@ -777,35 +777,37 @@ void BotConfig::initWeapons () {
m_weapons.clear ();
// fill array with available weapons
m_weapons.emplace (Weapon::Knife, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, WeaponType::Melee, true);
m_weapons.emplace (Weapon::USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, 12, WeaponType::Pistol, false);
m_weapons.emplace (Weapon::Glock18, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, 20, WeaponType::Pistol, false);
m_weapons.emplace (Weapon::Deagle, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, 7, WeaponType::Pistol, false);
m_weapons.emplace (Weapon::P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0, 13, WeaponType::Pistol, false);
m_weapons.emplace (Weapon::Elite, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, 30, WeaponType::Pistol, false);
m_weapons.emplace (Weapon::FiveSeven, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, 20, WeaponType::Pistol, false);
m_weapons.emplace (Weapon::M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, 8, WeaponType::Shotgun, false);
m_weapons.emplace (Weapon::XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, 7, WeaponType::Shotgun, false);
m_weapons.emplace (Weapon::MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, 30, WeaponType::SMG, true);
m_weapons.emplace (Weapon::TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, 30, WeaponType::SMG, true);
m_weapons.emplace (Weapon::P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, 50, WeaponType::SMG, true);
m_weapons.emplace (Weapon::MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, 30, WeaponType::SMG, true);
m_weapons.emplace (Weapon::UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, 25, WeaponType::SMG, true);
m_weapons.emplace (Weapon::AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, 30, WeaponType::Rifle, true);
m_weapons.emplace (Weapon::SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, 30, WeaponType::ZoomRifle, true);
m_weapons.emplace (Weapon::M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, 30, WeaponType::Rifle, true);
m_weapons.emplace (Weapon::Galil, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, 35, WeaponType::Rifle, true);
m_weapons.emplace (Weapon::Famas, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, 25, WeaponType::Rifle, true);
m_weapons.emplace (Weapon::AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, 30, WeaponType::ZoomRifle, true);
m_weapons.emplace (Weapon::Scout, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, 10, WeaponType::Sniper, false);
m_weapons.emplace (Weapon::AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, 10, WeaponType::Sniper, false);
m_weapons.emplace (Weapon::G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, 20, WeaponType::Sniper, false);
m_weapons.emplace (Weapon::SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, 30, WeaponType::Sniper, false);
m_weapons.emplace (Weapon::M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, 100, WeaponType::Heavy, true);
m_weapons.emplace (Weapon::Shield, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, 0, WeaponType::Pistol, false);
m_weapons = {
{ Weapon::Knife, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, WeaponType::Melee, true },
{ Weapon::USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, 12, WeaponType::Pistol, false },
{ Weapon::Glock18, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, 20, WeaponType::Pistol, false },
{ Weapon::Deagle, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, 7, WeaponType::Pistol, false },
{ Weapon::P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0, 13, WeaponType::Pistol, false },
{ Weapon::Elite, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, 30, WeaponType::Pistol, false },
{ Weapon::FiveSeven, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, 20, WeaponType::Pistol, false },
{ Weapon::M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, 8, WeaponType::Shotgun, false },
{ Weapon::XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, 7, WeaponType::Shotgun, false },
{ Weapon::MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, 30, WeaponType::SMG, true },
{ Weapon::TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, 30, WeaponType::SMG, true },
{ Weapon::P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, 50, WeaponType::SMG, true },
{ Weapon::MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, 30, WeaponType::SMG, true },
{ Weapon::UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, 25, WeaponType::SMG, true },
{ Weapon::AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, 30, WeaponType::Rifle, true },
{ Weapon::SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, 30, WeaponType::ZoomRifle, true },
{ Weapon::M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, 30, WeaponType::Rifle, true },
{ Weapon::Galil, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, 35, WeaponType::Rifle, true },
{ Weapon::Famas, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, 25, WeaponType::Rifle, true },
{ Weapon::AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, 30, WeaponType::ZoomRifle, true },
{ Weapon::Scout, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, 10, WeaponType::Sniper, false },
{ Weapon::AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, 10, WeaponType::Sniper, false },
{ Weapon::G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, 20, WeaponType::Sniper, false },
{ Weapon::SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, 30, WeaponType::Sniper, false },
{ Weapon::M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, 100, WeaponType::Heavy, true },
{ Weapon::Shield, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, 0, WeaponType::Pistol, false },
// not needed actually, but cause too much refactoring for now. todo
m_weapons.emplace (0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WeaponType::None, false);
// not needed actually, but cause too much refactoring for now. todo
{ 0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WeaponType::None, false }
};
}
void BotConfig::adjustWeaponPrices () {

View file

@ -562,26 +562,28 @@ void BotManager::reset () {
void BotManager::initFilters () {
// table with all available actions for the bots (filtered in & out in bot::setconditions) some of them have subactions included
m_filters.emplace (&Bot::normal_, Task::Normal, 0.0f, kInvalidNodeIndex, 0.0f, true);
m_filters.emplace (&Bot::pause_, Task::Pause, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::moveToPos_, Task::MoveToPosition, 0.0f, kInvalidNodeIndex, 0.0f, true);
m_filters.emplace (&Bot::followUser_, Task::FollowUser, 0.0f, kInvalidNodeIndex, 0.0f, true);
m_filters.emplace (&Bot::pickupItem_, Task::PickupItem, 0.0f, kInvalidNodeIndex, 0.0f, true);
m_filters.emplace (&Bot::camp_, Task::Camp, 0.0f, kInvalidNodeIndex, 0.0f, true);
m_filters.emplace (&Bot::plantBomb_, Task::PlantBomb, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::defuseBomb_, Task::DefuseBomb, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::attackEnemy_, Task::Attack, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::huntEnemy_, Task::Hunt, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::seekCover_, Task::SeekCover, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::throwExplosive_, Task::ThrowExplosive, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::throwFlashbang_, Task::ThrowFlashbang, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::throwSmoke_, Task::ThrowSmoke, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::doublejump_, Task::DoubleJump, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::escapeFromBomb_, Task::EscapeFromBomb, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::shootBreakable_, Task::ShootBreakable, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::hide_, Task::Hide, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::blind_, Task::Blind, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters.emplace (&Bot::spraypaint_, Task::Spraypaint, 0.0f, kInvalidNodeIndex, 0.0f, false);
m_filters = {
{ &Bot::normal_, Task::Normal, 0.0f, kInvalidNodeIndex, 0.0f, true },
{ &Bot::pause_, Task::Pause, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::moveToPos_, Task::MoveToPosition, 0.0f, kInvalidNodeIndex, 0.0f, true },
{ &Bot::followUser_, Task::FollowUser, 0.0f, kInvalidNodeIndex, 0.0f, true },
{ &Bot::pickupItem_, Task::PickupItem, 0.0f, kInvalidNodeIndex, 0.0f, true },
{ &Bot::camp_, Task::Camp, 0.0f, kInvalidNodeIndex, 0.0f, true },
{ &Bot::plantBomb_, Task::PlantBomb, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::defuseBomb_, Task::DefuseBomb, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::attackEnemy_, Task::Attack, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::huntEnemy_, Task::Hunt, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::seekCover_, Task::SeekCover, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::throwExplosive_, Task::ThrowExplosive, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::throwFlashbang_, Task::ThrowFlashbang, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::throwSmoke_, Task::ThrowSmoke, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::doublejump_, Task::DoubleJump, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::escapeFromBomb_, Task::EscapeFromBomb, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::shootBreakable_, Task::ShootBreakable, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::hide_, Task::Hide, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::blind_, Task::Blind, 0.0f, kInvalidNodeIndex, 0.0f, false },
{ &Bot::spraypaint_, Task::Spraypaint, 0.0f, kInvalidNodeIndex, 0.0f, false }
};
}
void BotManager::resetFilters () {
@ -1246,7 +1248,7 @@ int BotManager::getAliveHumansCount () {
int count = 0;
for (const auto &client : util.getClients ()) {
if ((client.flags & ClientFlags::Alive) && bots[client.ent] == nullptr && !(client.ent->v.flags & FL_FAKECLIENT)) {
if ((client.flags & ClientFlags::Alive) && !bots[client.ent] && !(client.ent->v.flags & FL_FAKECLIENT)) {
++count;
}
}
@ -1413,7 +1415,6 @@ void Bot::newRound () {
m_loosedBombNodeIndex = kInvalidNodeIndex;
m_plantedBombNodeIndex = kInvalidNodeIndex;
m_grenadeRequested = false;
m_moveToC4 = false;
m_defuseNotified = false;
m_duckDefuse = false;

View file

@ -18,55 +18,59 @@ BotSupport::BotSupport () {
m_welcomeReceiveTime = 0.0f;
// add default messages
m_sentences.push ("hello user,communication is acquired");
m_sentences.push ("your presence is acknowledged");
m_sentences.push ("high man, your in command now");
m_sentences.push ("blast your hostile for good");
m_sentences.push ("high man, kill some idiot here");
m_sentences.push ("is there a doctor in the area");
m_sentences.push ("warning, experimental materials detected");
m_sentences.push ("high amigo, shoot some but");
m_sentences.push ("time for some bad ass explosion");
m_sentences.push ("bad ass son of a breach device activated");
m_sentences.push ("high, do not question this great service");
m_sentences.push ("engine is operative, hello and goodbye");
m_sentences.push ("high amigo, your administration has been great last day");
m_sentences.push ("attention, expect experimental armed hostile presence");
m_sentences.push ("warning, medical attention required");
m_sentences = {
"hello user,communication is acquired",
"your presence is acknowledged",
"high man, your in command now",
"blast your hostile for good",
"high man, kill some idiot here",
"is there a doctor in the area",
"warning, experimental materials detected",
"high amigo, shoot some but",
"time for some bad ass explosion",
"bad ass son of a breach device activated",
"high, do not question this great service",
"engine is operative, hello and goodbye",
"high amigo, your administration has been great last day",
"attention, expect experimental armed hostile presence",
"warning, medical attention required"
};
// register weapon aliases
m_weaponAlias[Weapon::USP] = "usp"; // HK USP .45 Tactical
m_weaponAlias[Weapon::Glock18] = "glock"; // Glock18 Select Fire
m_weaponAlias[Weapon::Deagle] = "deagle"; // Desert Eagle .50AE
m_weaponAlias[Weapon::P228] = "p228"; // SIG P228
m_weaponAlias[Weapon::Elite] = "elite"; // Dual Beretta 96G Elite
m_weaponAlias[Weapon::FiveSeven] = "fn57"; // FN Five-Seven
m_weaponAlias[Weapon::M3] = "m3"; // Benelli M3 Super90
m_weaponAlias[Weapon::XM1014] = "xm1014"; // Benelli XM1014
m_weaponAlias[Weapon::MP5] = "mp5"; // HK MP5-Navy
m_weaponAlias[Weapon::TMP] = "tmp"; // Steyr Tactical Machine Pistol
m_weaponAlias[Weapon::P90] = "p90"; // FN P90
m_weaponAlias[Weapon::MAC10] = "mac10"; // Ingram MAC-10
m_weaponAlias[Weapon::UMP45] = "ump45"; // HK UMP45
m_weaponAlias[Weapon::AK47] = "ak47"; // Automat Kalashnikov AK-47
m_weaponAlias[Weapon::Galil] = "galil"; // IMI Galil
m_weaponAlias[Weapon::Famas] = "famas"; // GIAT FAMAS
m_weaponAlias[Weapon::SG552] = "sg552"; // Sig SG-552 Commando
m_weaponAlias[Weapon::M4A1] = "m4a1"; // Colt M4A1 Carbine
m_weaponAlias[Weapon::AUG] = "aug"; // Steyr Aug
m_weaponAlias[Weapon::Scout] = "scout"; // Steyr Scout
m_weaponAlias[Weapon::AWP] = "awp"; // AI Arctic Warfare/Magnum
m_weaponAlias[Weapon::G3SG1] = "g3sg1"; // HK G3/SG-1 Sniper Rifle
m_weaponAlias[Weapon::SG550] = "sg550"; // Sig SG-550 Sniper
m_weaponAlias[Weapon::M249] = "m249"; // FN M249 Para
m_weaponAlias[Weapon::Flashbang] = "flash"; // Concussion Grenade
m_weaponAlias[Weapon::Explosive] = "hegren"; // High-Explosive Grenade
m_weaponAlias[Weapon::Smoke] = "sgren"; // Smoke Grenade
m_weaponAlias[Weapon::Armor] = "vest"; // Kevlar Vest
m_weaponAlias[Weapon::ArmorHelm] = "vesthelm"; // Kevlar Vest and Helmet
m_weaponAlias[Weapon::Defuser] = "defuser"; // Defuser Kit
m_weaponAlias[Weapon::Shield] = "shield"; // Tactical Shield
m_weaponAlias[Weapon::Knife] = "knife"; // Knife
m_weaponAliases = {
{ Weapon::USP, "usp" }, // HK USP .45 Tactical
{ Weapon::Glock18, "glock" }, // Glock18 Select Fire
{ Weapon::Deagle, "deagle" }, // Desert Eagle .50AE
{ Weapon::P228, "p228" }, // SIG P228
{ Weapon::Elite, "elite" }, // Dual Beretta 96G Elite
{ Weapon::FiveSeven, "fn57" }, // FN Five-Seven
{ Weapon::M3, "m3" }, // Benelli M3 Super90
{ Weapon::XM1014, "xm1014" }, // Benelli XM1014
{ Weapon::MP5, "mp5" }, // HK MP5-Navy
{ Weapon::TMP, "tmp" }, // Steyr Tactical Machine Pistol
{ Weapon::P90, "p90" }, // FN P90
{ Weapon::MAC10, "mac10" }, // Ingram MAC-10
{ Weapon::UMP45, "ump45" }, // HK UMP45
{ Weapon::AK47, "ak47" }, // Automat Kalashnikov AK-47
{ Weapon::Galil, "galil" }, // IMI Galil
{ Weapon::Famas, "famas" }, // GIAT FAMAS
{ Weapon::SG552, "sg552" }, // Sig SG-552 Commando
{ Weapon::M4A1, "m4a1" }, // Colt M4A1 Carbine
{ Weapon::AUG, "aug" }, // Steyr Aug
{ Weapon::Scout, "scout" }, // Steyr Scout
{ Weapon::AWP, "awp" }, // AI Arctic Warfare/Magnum
{ Weapon::G3SG1, "g3sg1" }, // HK G3/SG-1 Sniper Rifle
{ Weapon::SG550, "sg550" }, // Sig SG-550 Sniper
{ Weapon::M249, "m249" }, // FN M249 Para
{ Weapon::Flashbang, "flash" }, // Concussion Grenade
{ Weapon::Explosive, "hegren" }, // High-Explosive Grenade
{ Weapon::Smoke, "sgren" }, // Smoke Grenade
{ Weapon::Armor, "vest" }, // Kevlar Vest
{ Weapon::ArmorHelm, "vesthelm" }, // Kevlar Vest and Helmet
{ Weapon::Defuser, "defuser" }, // Defuser Kit
{ Weapon::Shield, "shield" }, // Tactical Shield
{ Weapon::Knife, "knife" } // Knife
};
m_clients.resize (kGameMaxPlayers + 1);
}
@ -573,8 +577,8 @@ StringRef BotSupport::getFakeSteamId (edict_t *ent) {
StringRef BotSupport::weaponIdToAlias (int32_t id) {
StringRef none = "none";
if (m_weaponAlias.exists (id)) {
return m_weaponAlias[id];
if (m_weaponAliases.exists (id)) {
return m_weaponAliases[id];
}
return none;
}

View file

@ -21,11 +21,28 @@ void Bot::normal_ () {
const int debugGoal = cv_debug_goal.int_ ();
// user forced a node as a goal?
if (debugGoal != kInvalidNodeIndex && getTask ()->data != debugGoal) {
clearSearchNodes ();
if (debugGoal != kInvalidNodeIndex) {
if (getTask ()->data != debugGoal) {
clearSearchNodes ();
getTask ()->data = debugGoal;
m_chosenGoalIndex = debugGoal;
getTask ()->data = debugGoal;
m_chosenGoalIndex = debugGoal;
}
// stop the bot if precisely reached debug goal
if (m_currentNodeIndex == debugGoal) {
const auto &debugOrigin = graph[debugGoal].origin;
if (debugOrigin.distanceSq (pev->origin) < cr::sqrf (22.0f) && util.isVisible (debugOrigin, ent ())) {
m_moveToGoal = false;
m_checkTerrain = false;
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;
return;
}
}
}
// bots rushing with knife, when have no enemy (thanks for idea to nicebot project)
@ -1120,7 +1137,7 @@ void Bot::throwExplosive_ () {
ignoreCollision ();
if (!isGrenadeWar () && pev->origin.distanceSq (dest) < cr::sqrf (450.0f)) {
if (!isGrenadeWar () && pev->origin.distanceSq (dest) < cr::sqrf (kGrenadeDamageRadius)) {
// heck, I don't wanna blow up myself
m_grenadeCheckTime = game.time () + kGrenadeCheckTime * 2.0f;
@ -1147,14 +1164,11 @@ void Bot::throwExplosive_ () {
auto grenade = setCorrectGrenadeVelocity (kExplosiveModelName);
if (game.isNullEntity (grenade)) {
if (m_currentWeapon != Weapon::Explosive && !m_grenadeRequested) {
if (m_currentWeapon != Weapon::Explosive) {
if (pev->weapons & cr::bit (Weapon::Explosive)) {
m_grenadeRequested = true;
selectWeaponById (Weapon::Explosive);
}
else {
m_grenadeRequested = false;
selectBestWeapon ();
completeTask ();
@ -1163,7 +1177,6 @@ void Bot::throwExplosive_ () {
}
else if (!(m_oldButtons & IN_ATTACK)) {
pev->button |= IN_ATTACK;
m_grenadeRequested = false;
}
}
}
@ -1187,7 +1200,7 @@ void Bot::throwFlashbang_ () {
ignoreCollision ();
if (pev->origin.distanceSq (dest) < cr::sqrf (450.0f)) {
if (pev->origin.distanceSq (dest) < cr::sqrf (kGrenadeDamageRadius)) {
m_grenadeCheckTime = game.time () + kGrenadeCheckTime * 2.0f; // heck, I don't wanna blow up myself
selectBestWeapon ();
@ -1213,14 +1226,11 @@ void Bot::throwFlashbang_ () {
auto grenade = setCorrectGrenadeVelocity (kFlashbangModelName);
if (game.isNullEntity (grenade)) {
if (m_currentWeapon != Weapon::Flashbang && !m_grenadeRequested) {
if (m_currentWeapon != Weapon::Flashbang) {
if (pev->weapons & cr::bit (Weapon::Flashbang)) {
m_grenadeRequested = true;
selectWeaponById (Weapon::Flashbang);
}
else {
m_grenadeRequested = false;
selectBestWeapon ();
completeTask ();
@ -1229,7 +1239,6 @@ void Bot::throwFlashbang_ () {
}
else if (!(m_oldButtons & IN_ATTACK)) {
pev->button |= IN_ATTACK;
m_grenadeRequested = false;
}
}
}
@ -1261,18 +1270,14 @@ void Bot::throwSmoke_ () {
return;
}
if (m_currentWeapon != Weapon::Smoke && !m_grenadeRequested) {
if (m_currentWeapon != Weapon::Smoke) {
m_aimFlags |= AimFlags::Grenade;
if (pev->weapons & cr::bit (Weapon::Smoke)) {
m_grenadeRequested = true;
selectWeaponById (Weapon::Smoke);
getTask ()->time = game.time () + kGrenadeCheckTime * 2.0f;
}
else {
m_grenadeRequested = false;
selectBestWeapon ();
completeTask ();
@ -1281,7 +1286,6 @@ void Bot::throwSmoke_ () {
}
else if (!(m_oldButtons & IN_ATTACK)) {
pev->button |= IN_ATTACK;
m_grenadeRequested = false;
}
pev->button |= m_campButtons;
}
@ -1439,7 +1443,7 @@ void Bot::shootBreakable_ () {
m_lookAtSafe = m_breakableOrigin;
// is bot facing the breakable?
if (util.getShootingCone (ent (), m_breakableOrigin) >= 0.90f) {
if (util.getConeDeviation (ent (), m_breakableOrigin) >= 0.90f) {
m_moveSpeed = 0.0f;
m_strafeSpeed = 0.0f;