fix: make ammo pickups actually to work

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

View file

@ -35,6 +35,7 @@ ConVar cv_restricted_weapons ("yb_restricted_weapons", "", "Specifies semicolon
ConVar cv_attack_monsters ("yb_attack_monsters", "0", "Allows or disallows bots to attack monsters.");
ConVar cv_pickup_custom_items ("yb_pickup_custom_items", "0", "Allows or disallows bots to pickup custom items.");
ConVar cv_pickup_ammo_and_kits ("yb_pickup_ammo_and_kits", "0", "Allows bots pickup mod items like ammo, health kits and suits.");
ConVar cv_pickup_best ("yb_pickup_best", "1", "Allows or disallows bots to pickup best weapons.");
ConVar cv_ignore_objectives ("yb_ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages.");
ConVar cv_random_knife_attacks ("yb_random_knife_attacks", "1", "Allows or disallows the ability for random knife attacks when bot is rushing and no enemy is nearby.");
@ -93,9 +94,9 @@ void Bot::avoidGrenades () {
if (!seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) > pev->fov * 0.5f) {
continue;
}
auto model = pent->v.model.chars (9);
auto model = pent->v.model.str (9);
if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == Difficulty::Expert && cr::strcmp (model, "flashbang.mdl") == 0) {
if (m_preventFlashing < game.time () && m_personality == Personality::Rusher && m_difficulty == Difficulty::Expert && model == "flashbang.mdl") {
// don't look at flash bang
if (!(m_states & Sense::SeeingEnemy)) {
m_lookAt.y = cr::wrapAngle ((game.getEntityOrigin (pent) - getEyesPos ()).angles ().y + 180.0f);
@ -104,7 +105,7 @@ void Bot::avoidGrenades () {
m_preventFlashing = game.time () + rg.get (1.0f, 2.0f);
}
}
else if (game.isNullEntity (m_avoidGrenade) && cr::strcmp (model, "hegrenade.mdl") == 0) {
else if (game.isNullEntity (m_avoidGrenade) && model == "hegrenade.mdl") {
if (game.getTeam (pent->v.owner) == m_team || pent->v.owner == ent ()) {
continue;
}
@ -127,7 +128,7 @@ void Bot::avoidGrenades () {
}
}
}
else if ((pent->v.flags & FL_ONGROUND) && cr::strcmp (model, "smokegrenade.mdl") == 0) {
else if ((pent->v.flags & FL_ONGROUND) && model == "smokegrenade.mdl") {
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) {
float distance = pent->v.origin.distance (pev->origin);
@ -270,8 +271,42 @@ void Bot::setIdealReactionTimers (bool actual) {
void Bot::updatePickups () {
// this function finds Items to collect or use in the near of a bot
// don't try to pickup anything while on ladder or trying to escape from bomb...
if (m_isCreature || (m_states & Sense::SeeingEnemy) || isOnLadder () || getCurrentTaskId () == Task::EscapeFromBomb || !cv_pickup_best.bool_ () || cv_jasonmode.bool_ () || !bots.hasInterestingEntities ()) {
// utility to check if this function is currently doesn't allowed to run
const auto isPickupBlocked = [&] () -> bool {
// zombie or chickens not allowed to pickup anything
if (m_isCreature) {
return true;
}
// seeing enemy now, not good time to pickup anything
else if (m_states & Sense::SeeingEnemy) {
return true;
}
// bots on ladder don't have to search anything
else if (isOnLadder ()) {
return true;
}
// we're escaping from the bomb, don't bother!
else if (getCurrentTaskId () == Task::EscapeFromBomb) {
return true;
}
// knife mode is in progress ?
else if (cv_jasonmode.bool_ ()) {
return true;
}
// no interesting entities, how ?
else if (!bots.hasInterestingEntities ()) {
return true;
}
return false;
};
// we're not allowed to run now
if (isPickupBlocked ()) {
m_pickupItem = nullptr;
m_pickupType = Pickup::None;
@ -299,7 +334,7 @@ void Bot::updatePickups () {
}
if (ent == pickupItem) {
if (seesItem (origin, ent->v.classname.chars ())) {
if (seesItem (origin, ent->v.classname.str ())) {
itemExists = true;
}
break;
@ -337,38 +372,106 @@ void Bot::updatePickups () {
continue;
}
auto classname = ent->v.classname.chars ();
auto model = ent->v.model.chars (9);
auto classname = ent->v.classname.str ();
auto model = ent->v.model.str (9);
// check if line of sight to object is not blocked (i.e. visible)
if (seesItem (origin, classname)) {
if (cr::strncmp ("hostage_entity", classname, 14) == 0 || cr::strncmp ("monster_scientist", classname, 17) == 0) {
const bool isWeaponBox = classname.startsWith ("weaponbox");
const bool isDemolitionMap = game.mapIs (MapFlags::Demolition);
const bool isHostageRescueMap = game.mapIs (MapFlags::HostageRescue);
const bool isCSDM = game.is (GameFlags::CSDM);
if (isHostageRescueMap && (classname.startsWith ("hostage_entity") || classname.startsWith ("monster_scientist"))) {
allowPickup = true;
pickupType = Pickup::Hostage;
}
else if (cr::strncmp ("weaponbox", classname, 9) == 0 && cr::strcmp (model, "backpack.mdl") == 0) {
else if (isDemolitionMap && isWeaponBox && model == "backpack.mdl") {
allowPickup = true;
pickupType = Pickup::DroppedC4;
}
else if ((cr::strncmp ("weaponbox", classname, 9) == 0 || cr::strncmp ("armoury_entity", classname, 14) == 0 || cr::strncmp ("csdm", classname, 4) == 0) && !m_isUsingGrenade) {
else if ((isWeaponBox || classname.startsWith ("armoury_entity") || (isCSDM && classname.startsWith ("csdm"))) && !m_isUsingGrenade) {
allowPickup = true;
pickupType = Pickup::Weapon;
if (cv_pickup_ammo_and_kits.bool_ ()) {
int primaryWeaponCarried = bestPrimaryCarried ();
int secondaryWeaponCarried = bestSecondaryCarried ();
const auto &config = conf.getWeapons ();
const auto &primary = config[primaryWeaponCarried];
const auto &secondary = config[secondaryWeaponCarried];
const auto &primaryProp = conf.getWeaponProp (primary.id);
const auto &secondaryProp = conf.getWeaponProp (secondary.id);
if (secondaryWeaponCarried < kPrimaryWeaponMinIndex && (getAmmo (secondary.id) > 0.3 * secondaryProp.ammo1Max) && model == "357ammobox.mdl") {
allowPickup = false;
}
else if (!m_isVIP && primaryWeaponCarried >= kPrimaryWeaponMinIndex && (getAmmo (primary.id) > 0.3 * primaryProp.ammo1Max) && !m_isUsingGrenade && !hasShield ()) {
auto weaponType = conf.getWeaponType (primaryWeaponCarried);
const bool isSniperRifle = weaponType == WeaponType::Sniper;
const bool isSubmachine = weaponType == WeaponType::SMG;
const bool isShotgun = weaponType == WeaponType::Shotgun;
const bool isRifle = weaponType == WeaponType::Rifle || weaponType == WeaponType::ZoomRifle;
if (!isRifle && model == "9mmarclip.mdl") {
allowPickup = false;
}
else if (!isShotgun && model == "shotbox.mdl") {
allowPickup = false;
}
else if (!isSubmachine && model == "9mmclip.mdl") {
allowPickup = false;
}
else if (!isSniperRifle && model == "crossbow_clip.mdl") {
allowPickup = false;
}
else if (primaryWeaponCarried != Weapon::M249 && model == "chainammo.mdl") {
allowPickup = false;
}
}
else if (m_healthValue >= 100.0f && model == "medkit.mdl") {
allowPickup = false;
}
else if (pev->armorvalue >= 100.0f && (model == "kevlar.mdl"|| model == "battery.mdl" || model == "assault.mdl")) {
allowPickup = false;
}
if (allowPickup) {
pickupType = Pickup::AmmoAndKits;
}
}
// weapon replacement is not allowed
if (!cv_pickup_best.bool_ ()) {
allowPickup = false;
pickupType = Pickup::None;
}
}
else if (cr::strncmp ("weapon_shield", classname, 13) == 0 && !m_isUsingGrenade) {
else if (classname.startsWith ("weapon_shield") && !m_isUsingGrenade) {
allowPickup = true;
pickupType = Pickup::Shield;
// weapon replacement is not allowed
if (!cv_pickup_best.bool_ ()) {
allowPickup = false;
pickupType = Pickup::None;
}
}
else if (cr::strncmp ("item_thighpack", classname, 14) == 0 && m_team == Team::CT && !m_hasDefuser) {
else if (isDemolitionMap && m_team == Team::CT && !m_hasDefuser && classname.startsWith ("item_thighpack")) {
allowPickup = true;
pickupType = Pickup::DefusalKit;
}
else if (cr::strncmp ("grenade", classname, 7) == 0 && conf.getBombModelName () == model) {
else if (isDemolitionMap && classname.startsWith ("grenade") && conf.getBombModelName () == model) {
allowPickup = true;
pickupType = Pickup::PlantedC4;
}
else if (cv_pickup_custom_items.bool_ () && util.isItem (ent) && cr::strncmp ("item_thighpack", classname, 14) != 0) {
else if (cv_pickup_custom_items.bool_ () && util.isItem (ent) && !classname.startsWith ("item_thighpack")) {
allowPickup = true;
pickupType = Pickup::None;
pickupType = Pickup::Items;
}
}
@ -377,59 +480,19 @@ void Bot::updatePickups () {
// found weapon on ground?
if (pickupType == Pickup::Weapon) {
int primaryWeaponCarried = bestPrimaryCarried ();
int secondaryWeaponCarried = bestSecondaryCarried ();
const auto &config = conf.getWeapons ();
const auto &primary = config[primaryWeaponCarried];
const auto &secondary = config[secondaryWeaponCarried];
const auto &primaryProp = conf.getWeaponProp (primary.id);
const auto &secondaryProp = conf.getWeaponProp (secondary.id);
if (secondaryWeaponCarried < 7 && (m_ammo[secondary.id] > 0.3 * secondaryProp.ammo1Max) && cr::strcmp (model, "w_357ammobox.mdl") == 0) {
if (m_isVIP) {
allowPickup = false;
}
else if (!m_isVIP && primaryWeaponCarried >= 7 && (m_ammo[primary.id] > 0.3 * primaryProp.ammo1Max) && cr::strncmp (model, "w_", 2) == 0) {
auto weaponType = conf.getWeaponType (primaryWeaponCarried);
const bool isSniperRifle = weaponType == WeaponType::Sniper;
const bool isSubmachine = weaponType == WeaponType::SMG;
const bool isShotgun = weaponType == WeaponType::Shotgun;
const bool isRifle = weaponType == WeaponType::Rifle || weaponType == WeaponType::ZoomRifle;
if (!isRifle && cr::strcmp (model, "w_9mmarclip.mdl") == 0) {
allowPickup = false;
}
else if (!isShotgun && cr::strcmp (model, "w_shotbox.mdl") == 0) {
allowPickup = false;
}
else if (!isSubmachine && cr::strcmp (model, "w_9mmclip.mdl") == 0) {
allowPickup = false;
}
else if (!isSniperRifle && cr::strcmp (model, "w_crossbow_clip.mdl") == 0) {
allowPickup = false;
}
else if (primaryWeaponCarried != Weapon::M249 && cr::strcmp (model, "w_chainammo.mdl") == 0) {
allowPickup = false;
}
}
else if (m_isVIP || !rateGroundWeapon (ent)) {
else if (!rateGroundWeapon (ent)) {
allowPickup = false;
}
else if (m_healthValue >= 100.0f && cr::strcmp (model, "medkit.mdl") == 0) {
else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && model == "flashbang.mdl") {
allowPickup = false;
}
else if (pev->armorvalue >= 100.0f && (cr::strcmp (model, "kevlar.mdl") == 0 || cr::strcmp (model, "battery.mdl") == 0)) {
else if ((pev->weapons & cr::bit (Weapon::Explosive)) && model == "hegrenade.mdl") {
allowPickup = false;
}
else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && cr::strcmp (model, "flashbang.mdl") == 0) {
allowPickup = false;
}
else if ((pev->weapons & cr::bit (Weapon::Explosive)) && cr::strcmp (model, "hegrenade.mdl") == 0) {
allowPickup = false;
}
else if ((pev->weapons & cr::bit (Weapon::Smoke)) && cr::strcmp (model, "smokegrenade.mdl") == 0) {
else if ((pev->weapons & cr::bit (Weapon::Smoke)) && model == "smokegrenade.mdl") {
allowPickup = false;
}
}
@ -982,10 +1045,12 @@ void Bot::checkMsgQueue () {
bool Bot::isWeaponRestricted (int weaponIndex) {
// this function checks for weapon restrictions.
if (strings.isEmpty (cv_restricted_weapons.str ())) {
auto val = cv_restricted_weapons.str ();
if (val.empty ()) {
return isWeaponRestrictedAMX (weaponIndex); // no banned weapons
}
const auto &bannedWeapons = String (cv_restricted_weapons.str ()).split (";");
const auto &bannedWeapons = val.split <String> (";");
const auto &alias = util.weaponIdToAlias (weaponIndex);
for (const auto &ban : bannedWeapons) {
@ -1314,7 +1379,7 @@ void Bot::buyStuff () {
break;
case BuyState::SecondaryWeapon: // if bot has still some money, buy a better secondary weapon
if (isPistolMode || (isFirstRound && hasDefaultPistols && rg.chance (50)) || (hasDefaultPistols && bots.getLastWinner () == m_team && m_moneyAmount > rg.get (2000, 3000)) || (hasPrimaryWeapon () && hasDefaultPistols && m_moneyAmount > rg.get (7500, 9000))) {
if (isPistolMode || (isFirstRound && hasDefaultPistols && rg.chance (60)) || (hasDefaultPistols && bots.getLastWinner () == m_team && m_moneyAmount > rg.get (2000, 3000)) || (hasPrimaryWeapon () && hasDefaultPistols && m_moneyAmount > rg.get (7500, 9000))) {
do {
pref--;
@ -1380,18 +1445,18 @@ void Bot::buyStuff () {
case BuyState::Ammo: // buy enough primary & secondary ammo (do not check for money here)
for (int i = 0; i <= 5; ++i) {
for (int i = 0; i < 7; ++i) {
issueCommand ("buyammo%d", rg.get (1, 2)); // simulate human
}
// buy enough secondary ammo
// buy enough ammo
if (hasPrimaryWeapon ()) {
issueCommand ("buy;menuselect 6");
}
else {
issueCommand ("buy;menuselect 7");
}
// buy enough primary ammo
issueCommand ("buy;menuselect 6");
// try to reload secondary weapon
if (m_reloadState != Reload::Primary) {
m_reloadState = Reload::Secondary;
@ -3023,7 +3088,7 @@ void Bot::showDebugOverlay () {
static float timeDebugUpdate = 0.0f;
static int index = kInvalidNodeIndex, goal = kInvalidNodeIndex, taskID = 0;
static HashMap <int32_t, String> tasks {
static HashMap <int32_t, StringRef> tasks {
{ Task::Normal, "Normal" },
{ Task::Pause, "Pause" },
{ Task::MoveToPosition, "Move" },
@ -3046,13 +3111,13 @@ void Bot::showDebugOverlay () {
{ Task::Spraypaint, "Spray" }
};
static HashMap <int32_t, String> personalities {
static HashMap <int32_t, StringRef> personalities {
{ Personality::Rusher, "Rusher" },
{ Personality::Normal, "Normal" },
{ Personality::Careful, "Careful" }
};
static HashMap <int32_t, String> flags {
static HashMap <int32_t, StringRef> flags {
{ AimFlags::Nav, "Nav" },
{ AimFlags::Camp, "Camp" },
{ AimFlags::PredictPath, "Predict" },
@ -3080,15 +3145,15 @@ void Bot::showDebugOverlay () {
String enemy = "(none)";
if (!game.isNullEntity (m_enemy)) {
enemy = m_enemy->v.netname.chars ();
enemy = m_enemy->v.netname.str ();
}
else if (!game.isNullEntity (m_lastEnemy)) {
enemy.assignf ("%s (L)", m_lastEnemy->v.netname.chars ());
enemy.assignf ("%s (L)", m_lastEnemy->v.netname.str ());
}
String pickup = "(none)";
if (!game.isNullEntity (m_pickupItem)) {
pickup = m_pickupItem->v.classname.chars ();
pickup = m_pickupItem->v.classname.str ();
}
String aimFlags;
@ -3102,7 +3167,7 @@ void Bot::showDebugOverlay () {
auto weapon = util.weaponIdToAlias (m_currentWeapon);
String debugData;
debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s Terrain=%s Stuck=%s\n", pev->netname.chars (), m_healthValue, pev->armorvalue, taskID, tasks[taskID], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality], boolValue (m_checkTerrain), boolValue (m_isStuck));
debugData.assignf ("\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\nItem: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\nSP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\nEnemy=%s Pickup=%s Type=%s Terrain=%s Stuck=%s\n", pev->netname.str (), m_healthValue, pev->armorvalue, taskID, tasks[taskID], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality], boolValue (m_checkTerrain), boolValue (m_isStuck));
MessageWriter (MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, nullptr, overlayEntity)
.writeByte (TE_TEXTMESSAGE)
@ -3378,7 +3443,7 @@ void Bot::startDoubleJump (edict_t *ent) {
m_doubleJumpEntity = ent;
startTask (Task::DoubleJump, TaskPri::DoubleJump, kInvalidNodeIndex, game.time (), true);
sendToChat (strings.format ("Ok %s, i will help you!", ent->v.netname.chars ()), true);
sendToChat (strings.format ("Ok %s, i will help you!", ent->v.netname.str ()), true);
}
void Bot::sendBotToOrigin (const Vector &origin) {
@ -3410,7 +3475,7 @@ void Bot::debugMsgInternal (const char *str) {
return;
}
String printBuf;
printBuf.assignf ("%s: %s", pev->netname.chars (), str);
printBuf.assignf ("%s: %s", pev->netname.str (), str);
bool playMessage = false;
@ -3778,7 +3843,7 @@ void Bot::refreshModelName (char *infobuffer) {
union ModelTest {
char model[2];
uint16_t mask;
ModelTest (StringRef m) : model { m[0], m[1] } {};
ModelTest (StringRef m) : model { m[0], m[1] } {}
} modelTest { modelName };
// assign our model mask (tests against model done every bot update)

View file

@ -44,14 +44,14 @@ void BotSupport::humanizePlayerName (String &playerName) {
}
// sometimes switch name to lower characters, only valid for the english languge
if (rg.chance (8) && cr::strcmp (cv_language.str (), "en") == 0) {
if (rg.chance (8) && cv_language.str () == "en") {
playerName.lowercase ();
}
}
void BotSupport::addChatErrors (String &line) {
// sometimes switch name to lower characters, only valid for the english languge
if (rg.chance (8) && cr::strcmp (cv_language.str (), "en") == 0) {
if (rg.chance (8) && cv_language.str () == "en") {
line.lowercase ();
}
auto length = static_cast <int32_t> (line.length ());

View file

@ -454,7 +454,7 @@ Vector Bot::getBodyOffsetError (float distance) {
}
if (m_aimErrorTime < game.time ()) {
const float hitError = distance / (cr::clamp (m_difficulty, 1, 4) * 1000.0f);
const float hitError = distance / (cr::clamp (static_cast <float> (m_difficulty), 1.0f, 4.0f) * 1000.0f);
auto &maxs = m_enemy->v.maxs, &mins = m_enemy->v.mins;
m_aimLastError = Vector (rg.get (mins.x * hitError, maxs.x * hitError), rg.get (mins.y * hitError, maxs.y * hitError), rg.get (mins.z * hitError, maxs.z * hitError));
@ -1332,7 +1332,7 @@ bool Bot::hasSecondaryWeapon () {
bool Bot::hasShield () {
// this function returns true, if bot has a tactical shield
return cr::strncmp (pev->viewmodel.chars (14), "v_shield_", 9) == 0;
return pev->viewmodel.str (14).startsWith ("v_shield_");
}
bool Bot::isShieldDrawn () {
@ -1352,7 +1352,7 @@ bool Bot::isEnemyBehindShield (edict_t *enemy) {
}
// check if enemy has shield and this shield is drawn
if ((enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7) && cr::strncmp (enemy->v.viewmodel.chars (14), "v_shield_", 9) == 0) {
if ((enemy->v.weaponanim == 6 || enemy->v.weaponanim == 7) && enemy->v.viewmodel.str (14).startsWith ("v_shield_")) {
if (util.isInViewCone (pev->origin, enemy)) {
return true;
}
@ -1443,7 +1443,7 @@ int Bot::bestSecondaryCarried () {
for (int i = 0; i < kNumWeapons; ++i) {
int id = tab[*pref].id;
if ((weapons & cr::bit (tab[*pref].id)) && (id == Weapon::USP || id == Weapon::Glock18 || id == Weapon::Deagle || id == Weapon::P228 || id == Weapon::Elite || id == Weapon::FiveSeven)) {
if ((weapons & cr::bit (tab[*pref].id)) && conf.getWeaponType (id) == WeaponType::Pistol) {
weaponIndex = i;
break;
}
@ -1468,21 +1468,22 @@ int Bot::bestGrenadeCarried () {
bool Bot::rateGroundWeapon (edict_t *ent) {
// this function compares weapons on the ground to the one the bot is using
// weapon rating blocked, due to we picked up not-preferred weapon some time ago, because out of ammo
int groundIndex = 0;
const int *pref = conf.getWeaponPrefs (m_personality);
auto tab = conf.getRawWeapons ();
for (int i = 0; i < kNumWeapons; ++i) {
if (cr::strcmp (tab[*pref].model, ent->v.model.chars (9)) == 0) {
if (ent->v.model.str (9) == tab[*pref].model) {
groundIndex = i;
break;
}
pref++;
}
int hasWeapon = 0;
auto hasWeapon = 0;
if (groundIndex < 7) {
if (groundIndex < kPrimaryWeaponMinIndex) {
hasWeapon = bestSecondaryCarried ();
}
else {
@ -1521,7 +1522,7 @@ void Bot::selectBestWeapon () {
while (tab[selectIndex].id) {
// is the bot NOT carrying this weapon?
if (!(pev->weapons & cr::bit (tab[selectIndex].id))) {
selectIndex++; // skip to next weapon
++selectIndex; // skip to next weapon
continue;
}
@ -1541,7 +1542,7 @@ void Bot::selectBestWeapon () {
if (ammoLeft) {
chosenWeaponIndex = selectIndex;
}
selectIndex++;
++selectIndex;
}
chosenWeaponIndex %= kNumWeapons + 1;

View file

@ -106,8 +106,12 @@ void BotConfig::loadMainConfig (bool isFirstLoad) {
}
// bind the correct menu key for bot menu...
if (!game.isDedicated () && !strings.isEmpty (cv_bind_menu_key.str ())) {
game.serverCommand ("bind \"%s\" \"yb menu\"", cv_bind_menu_key.str ());
if (!game.isDedicated ()) {
auto val = cv_bind_menu_key.str ();
if (!val.empty ()) {
game.serverCommand ("bind \"%s\" \"yb menu\"", val);
}
}
// disable logger if requested
@ -500,7 +504,7 @@ void BotConfig::loadLanguageConfig () {
}
file.close ();
}
else if (cr::strcmp (cv_language.str (), "en") != 0) {
else if (cv_language.str () != "en") {
logger.error ("Couldn't load language configuration");
}
}
@ -834,7 +838,7 @@ bool BotConfig::openConfig (StringRef fileName, StringRef errorIfNotExists, MemF
auto configDir = strings.joinPath (folders.addons, folders.bot, folders.config);
if (languageDependant) {
if (fileName.startsWith ("lang") && cr::strcmp (cv_language.str (), "en") == 0) {
if (fileName.startsWith ("lang") && cv_language.str () == "en") {
return false;
}
auto langConfig = strings.joinPath (configDir, folders.lang, strings.format ("%s_%s.%s", cv_language.str (), fileName, kConfigExtension));

View file

@ -218,9 +218,10 @@ int BotControl::cmdCvars () {
if (!isSave && !match.empty () && !strstr (cvar.reg.name, match.chars ())) {
continue;
}
auto val = cvar.self->str ();
// float value ?
bool isFloat = !strings.isEmpty (cvar.self->str ()) && strchr (cvar.self->str (), '.');
bool isFloat = !val.empty () && val.find (".") != StringRef::InvalidIndex;
if (isSave) {
cfg.puts ("//\n");

View file

@ -94,12 +94,12 @@ void Game::levelInitialize (edict_t *entities, int max) {
if (!ent || ent->v.classname == 0) {
continue;
}
auto classname = ent->v.classname.chars ();
auto classname = ent->v.classname.str ();
if (cr::strcmp (classname, "worldspawn") == 0) {
if (classname == "worldspawn") {
m_startEntity = ent;
}
else if (cr::strcmp (classname, "player_weaponstrip") == 0) {
else if (classname == "player_weaponstrip") {
if (is (GameFlags::Legacy) && strings.isEmpty (ent->v.target.chars ())) {
ent->v.target = ent->v.targetname = engfuncs.pfnAllocString ("fake");
}
@ -107,30 +107,30 @@ void Game::levelInitialize (edict_t *entities, int max) {
engfuncs.pfnRemoveEntity (ent);
}
}
else if (cr::strcmp (classname, "info_player_start") == 0 || cr::strcmp (classname, "info_vip_start") == 0) {
else if (classname == "info_player_start" || classname == "info_vip_start") {
ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency
ent->v.renderamt = 127; // set its transparency amount
ent->v.effects |= EF_NODRAW;
++m_spawnCount[Team::CT];
}
else if (cr::strcmp (classname, "info_player_deathmatch") == 0) {
else if (classname == "info_player_deathmatch") {
ent->v.rendermode = kRenderTransAlpha; // set its render mode to transparency
ent->v.renderamt = 127; // set its transparency amount
ent->v.effects |= EF_NODRAW;
++m_spawnCount[Team::Terrorist];
}
else if (cr::strcmp (classname, "func_vip_safetyzone") == 0 || cr::strcmp (classname, "info_vip_safetyzone") == 0) {
else if (classname == "func_vip_safetyzone" || classname == "info_vip_safetyzone") {
m_mapFlags |= MapFlags::Assassination; // assassination map
}
else if (cr::strcmp (classname, "hostage_entity") == 0 || cr::strcmp (classname, "monster_scientist") == 0) {
else if (classname == "hostage_entity" || classname == "monster_scientist") {
m_mapFlags |= MapFlags::HostageRescue; // rescue map
}
else if (cr::strcmp (classname, "func_bomb_target") == 0 || cr::strcmp (classname, "info_bomb_target") == 0) {
else if (classname == "func_bomb_target" || classname == "info_bomb_target") {
m_mapFlags |= MapFlags::Demolition; // defusion map
}
else if (cr::strcmp (classname, "func_escapezone") == 0) {
else if (classname == "func_escapezone") {
m_mapFlags |= MapFlags::Escape;
// strange thing on some ES maps, where hostage entity present there
@ -138,10 +138,10 @@ void Game::levelInitialize (edict_t *entities, int max) {
m_mapFlags &= ~MapFlags::HostageRescue;
}
}
else if (cr::strncmp (classname, "func_door", 9) == 0) {
else if (classname.startsWith ("func_door")) {
m_mapFlags |= MapFlags::HasDoors;
}
else if (cr::strncmp (classname, "func_button", 11) == 0) {
else if (classname.startsWith ("func_button")) {
m_mapFlags |= MapFlags::HasButtons;
}
else if (isShootableBreakable (ent)) {
@ -151,10 +151,12 @@ void Game::levelInitialize (edict_t *entities, int max) {
// next maps doesn't have map-specific entities, so determine it by name
if (!cv_ignore_map_prefix_game_mode.bool_ ()) {
if (cr::strncmp (getMapName (), "fy_", 3) == 0) {
StringRef prefix = getMapName ();
if (prefix.startsWith ("fy_")) {
m_mapFlags |= MapFlags::FightYard;
}
else if (cr::strncmp (getMapName (), "ka_", 3) == 0) {
else if (prefix.startsWith ("ka_")) {
m_mapFlags |= MapFlags::KnifeArena;
}
}
@ -767,9 +769,9 @@ void Game::registerCvars (bool gameVars) {
}
bool Game::loadCSBinary () {
auto modname = getRunningModName ();
StringRef modname = getRunningModName ();
if (!modname) {
if (modname.empty ()) {
return false;
}
Array <StringRef> libs { "mp", "cs", "cs_i386" };
@ -803,7 +805,7 @@ bool Game::loadCSBinary () {
}
// special case, czero is always detected first, as it's has custom directory
if (cr::strcmp (modname, "czero") == 0) {
if (modname == "czero") {
m_gameFlags |= (GameFlags::ConditionZero | GameFlags::HasBotVoice | GameFlags::HasFakePings);
if (is (GameFlags::Metamod)) {
@ -1071,9 +1073,15 @@ bool Game::isShootableBreakable (edict_t *ent) {
}
auto limit = cv_breakable_health_limit.float_ ();
if ((cr::strcmp (ent->v.classname.chars (), "func_breakable") == 0 && ent->v.health < limit) || (cr::strcmp (ent->v.classname.chars (), "func_pushable") == 0 && (ent->v.spawnflags & SF_PUSH_BREAKABLE) && ent->v.health < limit) || (cr::strcmp (ent->v.classname.chars (), "func_wall") == 0 && ent->v.health < limit)) {
constexpr auto kFuncBreakable = StringRef::fnv1a32 ("func_breakable");
constexpr auto kFuncPushable = StringRef::fnv1a32 ("func_pushable");
constexpr auto kFuncWall = StringRef::fnv1a32 ("func_wall");
auto classnameHash = ent->v.classname.str ().hash ();
if ((classnameHash == kFuncBreakable && ent->v.health < limit) || (classnameHash == kFuncPushable && (ent->v.spawnflags & SF_PUSH_BREAKABLE) && ent->v.health < limit) || (classnameHash == kFuncWall && ent->v.health < limit)) {
if (ent->v.takedamage > 0.0f && ent->v.impulse <= 0 && !(ent->v.flags & FL_WORLDBRUSH) && !(ent->v.spawnflags & SF_BREAK_TRIGGER_ONLY)) {
return (ent->v.movetype == MOVETYPE_PUSH || ent->v.movetype == MOVETYPE_PUSHSTEP);
return ent->v.movetype == MOVETYPE_PUSH || ent->v.movetype == MOVETYPE_PUSHSTEP;
}
}
return false;

View file

@ -1274,7 +1274,7 @@ void BotGraph::calculatePathRadius (int index) {
if (tr.flFraction < 1.0f) {
game.testLine (radiusStart, radiusEnd, TraceIgnore::Monsters, nullptr, &tr);
if (tr.pHit && cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) {
if (tr.pHit && tr.pHit->v.classname.str ().startsWith ("func_door")) {
path.radius = 0.0f;
wayBlocked = true;
@ -1571,12 +1571,13 @@ bool BotGraph::loadGraphData () {
for (const auto &path : m_paths) {
addToBucket (path.origin, path.number);
}
StringRef author = exten.author;
if ((outOptions & StorageOption::Official) || cr::strncmp (exten.author, "official", 8) == 0 || cr::strlen (exten.author) < 2) {
if ((outOptions & StorageOption::Official) || author.startsWith ("official") || author.length () < 2) {
m_graphAuthor.assign (product.name);
}
else {
m_graphAuthor.assign (exten.author);
m_graphAuthor.assign (author);
}
StringRef modified = exten.modified;
@ -1607,7 +1608,7 @@ bool BotGraph::loadGraphData () {
}
bool BotGraph::canDownload () {
return !strings.isEmpty (cv_graph_url.str ());
return !cv_graph_url.str ().empty ();
}
bool BotGraph::saveGraphData () {
@ -1727,17 +1728,17 @@ bool BotGraph::isNodeReacheableEx (const Vector &src, const Vector &destination,
// check if we go through a func_illusionary, in which case return false
game.testHull (src, destination, TraceIgnore::Monsters, head_hull, m_editor, &tr);
if (tr.pHit && cr::strcmp ("func_illusionary", tr.pHit->v.classname.chars ()) == 0) {
return false; // don't add pathnodes through func_illusionaries
if (tr.pHit && tr.pHit->v.classname.str () == "func_illusionary") {
return false; // don't add path nodes through func_illusionaries
}
// check if this node is "visible"...
game.testLine (src, destination, TraceIgnore::Monsters, m_editor, &tr);
// if node is visible from current position (even behind head)...
if (tr.pHit && (tr.flFraction >= 1.0f || cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0)) {
if (tr.pHit && (tr.flFraction >= 1.0f || tr.pHit->v.classname.str ().startsWith ("func_door"))) {
// if it's a door check if nothing blocks behind
if (cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) == 0) {
if (tr.pHit->v.classname.str ().startsWith ("func_door")) {
game.testLine (tr.vecEndPos, destination, TraceIgnore::Monsters, tr.pHit, &tr);
if (tr.flFraction < 1.0f) {

View file

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

View file

@ -220,7 +220,7 @@ BotCreateResult BotManager::create (StringRef name, int difficulty, int personal
else {
resultName = name;
}
const bool hasNamePrefix = !strings.isEmpty (cv_name_prefix.str ());
const bool hasNamePrefix = !cv_name_prefix.str ().empty ();
// disable save bots names if prefix is enabled
if (hasNamePrefix && cv_save_bots_names.bool_ ()) {
@ -390,10 +390,10 @@ void BotManager::maintainQuota () {
int desiredBotCount = cv_quota.int_ ();
int botsInGame = getBotCount ();
if (strings.matches (cv_quota_mode.str (), "fill")) {
if (cv_quota_mode.str () == "fill") {
botsInGame += humanPlayersInGame;
}
else if (strings.matches (cv_quota_mode.str (), "match")) {
else if (cv_quota_mode.str () == "match") {
int detectQuotaMatch = cv_quota_match.int_ () == 0 ? cv_quota.int_ () : cv_quota_match.int_ ();
desiredBotCount = cr::max <int> (0, detectQuotaMatch * humanPlayersInGame);
@ -1664,16 +1664,16 @@ void Bot::updateTeamJoin () {
}
}
void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *ent) {
void BotManager::captureChatRadio (StringRef cmd, StringRef arg, edict_t *ent) {
if (game.isBotCmd ()) {
return;
}
if (strings.matches (cmd, "say") || strings.matches (cmd, "say_team")) {
if (cmd.startsWith ("say")) {
bool alive = util.isAlive (ent);
int team = -1;
if (cr::strcmp (cmd, "say_team") == 0) {
if (cmd == "say_team") {
team = game.getTeam (ent);
}
@ -1697,8 +1697,8 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
auto &target = util.getClient (game.indexOfPlayer (ent));
// check if this player alive, and issue something
if ((target.flags & ClientFlags::Alive) && target.radio != 0 && cr::strncmp (cmd, "menuselect", 10) == 0) {
int radioCommand = atoi (arg);
if ((target.flags & ClientFlags::Alive) && target.radio != 0 && cmd.startsWith ("menuselect")) {
auto radioCommand = arg.int_ ();
if (radioCommand != 0) {
radioCommand += 10 * (target.radio - 1);
@ -1717,8 +1717,8 @@ void BotManager::captureChatRadio (const char *cmd, const char *arg, edict_t *en
}
target.radio = 0;
}
else if (cr::strncmp (cmd, "radio", 5) == 0) {
target.radio = atoi (&cmd[5]);
else if (cmd.startsWith ("radio")) {
target.radio = cmd.substr (5).int_ ();
}
}
@ -1774,25 +1774,25 @@ void BotManager::updateInterestingEntities () {
// search the map for any type of grenade
game.searchEntities (nullptr, kInfiniteDistance, [&] (edict_t *e) {
auto classname = e->v.classname.chars ();
auto classname = e->v.classname.str ();
// search for grenades, weaponboxes, weapons, items and armoury entities
if (cr::strncmp ("weaponbox", classname, 9) == 0 || cr::strncmp ("grenade", classname, 7) == 0 || util.isItem (e) || cr::strncmp ("armoury", classname, 7) == 0) {
if (classname.startsWith ("weaponbox") || classname.startsWith ("grenade") || util.isItem (e) || classname.startsWith ("armoury")) {
m_interestingEntities.push (e);
}
// pickup some csdm stuff if we're running csdm
if (game.mapIs (MapFlags::HostageRescue) && cr::strncmp ("hostage", classname, 7) == 0) {
// pickup some hostage if on cs_ maps
if (game.mapIs (MapFlags::HostageRescue) && classname.startsWith ("hostage")) {
m_interestingEntities.push (e);
}
// add buttons
if (game.mapIs (MapFlags::HasButtons) && cr::strncmp ("func_button", classname, 11) == 0) {
if (game.mapIs (MapFlags::HasButtons) && classname.startsWith ("func_button")) {
m_interestingEntities.push (e);
}
// pickup some csdm stuff if we're running csdm
if (game.is (GameFlags::CSDM) && cr::strncmp ("csdm", classname, 4) == 0) {
if (game.is (GameFlags::CSDM) && classname.startsWith ("csdm")) {
m_interestingEntities.push (e);
}

View file

@ -621,7 +621,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
}
if (seesEntity (m_destOrigin)) {
const auto &right = m_moveAngles.right ();
auto right = m_moveAngles.right ();
src = getEyesPos ();
src = src + right * 15.0f;
@ -1012,7 +1012,7 @@ bool Bot::updateNavigation () {
if (game.mapIs (MapFlags::HasDoors)) {
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Monsters, ent (), &tr);
if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && cr::strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) {
if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && tr.pHit->v.classname.str ().startsWith ("func_door")) {
// if the door is near enough...
if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) {
ignoreCollision (); // don't consider being stuck
@ -1043,25 +1043,30 @@ bool Bot::updateNavigation () {
}
// if bot hits the door, then it opens, so wait a bit to let it open safely
if (pev->velocity.length2d () < 2 && m_timeDoorOpen < game.time ()) {
if (pev->velocity.length2d () < 10 && m_timeDoorOpen < game.time ()) {
startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.5f, false);
m_timeDoorOpen = game.time () + 1.0f; // retry in 1 sec until door is open
edict_t *pent = nullptr;
if (++m_tryOpenDoor > 2 && util.findNearestPlayer (reinterpret_cast <void **> (&pent), ent (), 256.0f, false, false, true, true, false)) {
m_seeEnemyTime = game.time () - 0.5f;
if (++m_tryOpenDoor > 1 && util.findNearestPlayer (reinterpret_cast <void **> (&pent), ent (), 384.0f, false, false, true, true, false)) {
if (isPenetrableObstacle (pent->v.origin)) {
m_seeEnemyTime = game.time ();
m_states |= Sense::SeeingEnemy;
m_aimFlags |= AimFlags::Enemy;
m_states |= Sense::SeeingEnemy | Sense::SuspectEnemy;
m_aimFlags |= AimFlags::Enemy;
m_lastEnemy = pent;
m_enemy = pent;
m_lastEnemyOrigin = pent->v.origin;
m_lastEnemy = pent;
m_enemy = pent;
m_lastEnemyOrigin = pent->v.origin;
m_tryOpenDoor = 0;
m_tryOpenDoor = 0;
}
else {
m_tryOpenDoor = 0;
}
}
else {
else if (m_timeDoorOpen + 2.0f < game.time ()) {
m_tryOpenDoor = 0;
}
}
@ -1191,7 +1196,7 @@ bool Bot::updateLiftHandling () {
// trace line to door
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
if (tr.flFraction < 1.0f && tr.pHit && cr::strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) {
if (tr.flFraction < 1.0f && tr.pHit && tr.pHit->v.classname.str ().startsWith ("func_door") && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) {
if (m_liftState == LiftState::None) {
m_liftState = LiftState::LookingButtonOutside;
m_liftUsageTime = game.time () + 7.0f;
@ -1199,11 +1204,16 @@ bool Bot::updateLiftHandling () {
liftClosedDoorExists = true;
}
// helper
auto isFunc = [] (StringRef cls) -> bool {
return cls.startsWith ("func_door") || cls == "func_plat" || cls == "func_train";
};
// trace line down
game.testLine (m_path->origin, m_pathOrigin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
// if trace result shows us that it is a lift
if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (cr::strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_train") == 0) && !liftClosedDoorExists) {
if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && isFunc (tr.pHit->v.classname.str ()) && !liftClosedDoorExists) {
if ((m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && cr::fzero (tr.pHit->v.velocity.z)) {
if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) {
m_liftEntity = tr.pHit;
@ -1225,7 +1235,7 @@ bool Bot::updateLiftHandling () {
if (graph.exists (nextNode) && (graph[nextNode].flags & NodeFlag::Lift)) {
game.testLine (m_path->origin, graph[nextNode].origin, TraceIgnore::Everything, ent (), &tr);
if (!game.isNullEntity (tr.pHit) && (cr::strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || cr::strcmp (tr.pHit->v.classname.chars (), "func_train") == 0)) {
if (!game.isNullEntity (tr.pHit) && isFunc (tr.pHit->v.classname.str ())) {
m_liftEntity = tr.pHit;
}
}
@ -1832,7 +1842,7 @@ int Bot::findDefendNode (const Vector &origin) {
int srcIndex = m_currentNodeIndex;
// max search distance
const auto kMaxDistance = cr::clamp (148.0f * bots.getBotCount (), 256.0f, 1024.0f);
const auto kMaxDistance = cr::clamp (static_cast <float> (148 * bots.getBotCount ()), 256.0f, 1024.0f);
// some of points not found, return random one
if (srcIndex == kInvalidNodeIndex || posIndex == kInvalidNodeIndex) {
@ -2369,13 +2379,13 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
// first do a trace from the bot's eyes forward...
auto src = getEyesPos ();
auto forward = src + normal * 24.0f;
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
auto checkDoor = [] (TraceResult *result) {
if (!game.mapIs (MapFlags::HasDoors)) {
return false;
}
return result->flFraction < 1.0f && cr::strncmp ("func_door", result->pHit->v.classname.chars (), 9) != 0;
return result->flFraction < 1.0f && result->pHit && !result->pHit->v.classname.str ().startsWith ("func_door");
};
// trace from the bot's eyes straight forward...
@ -2383,7 +2393,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
// check if the trace hit something...
if (tr->flFraction < 1.0f) {
if (game.mapIs (MapFlags::HasDoors) && cr::strncmp ("func_door", tr->pHit->v.classname.chars (), 9) == 0) {
if (game.mapIs (MapFlags::HasDoors) && tr->pHit && tr->pHit->v.classname.str ().startsWith ("func_door")) {
return false;
}
return true; // bot's head will hit something
@ -2529,7 +2539,7 @@ bool Bot::canJumpUp (const Vector &normal) {
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
return false;
}
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
// check for normal jump height first...
auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
@ -2695,7 +2705,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
}
// convert current view angle to vectors for TraceLine math...
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
// now check same height to one side of the bot...
src = baseHeight + right * 16.0f;
@ -2734,7 +2744,7 @@ bool Bot::isBlockedLeft () {
game.testLine (pev->origin, forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
// check if the trace hit something...
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) != 0) {
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && !tr.pHit->v.classname.str ().startsWith ("func_door")) {
return true; // bot's body will hit something
}
return false;
@ -2754,7 +2764,7 @@ bool Bot::isBlockedRight () {
game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
// check if the trace hit something...
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && cr::strncmp ("func_door", tr.pHit->v.classname.chars (), 9) != 0) {
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && tr.pHit && !tr.pHit->v.classname.str ().startsWith ("func_door")) {
return true; // bot's body will hit something
}
return false;
@ -2939,7 +2949,7 @@ int Bot::getRandomCampDir () {
}
void Bot::setStrafeSpeed (const Vector &moveDir, float strafeSpeed) {
const Vector &los = (moveDir - pev->origin).normalize2d ();
const Vector &los = (moveDir - pev->origin).normalize2d_apx ();
float dot = los | pev->angles.forward ().get2d ();
if (dot > 0.0f && !checkWallOnRight ()) {

View file

@ -323,7 +323,7 @@ AStarResult AStarAlgo::find (int botTeam, int srcIndex, int destIndex, NodeAdder
void FloydWarshallAlgo::rebuild () {
m_length = graph.length ();
m_matrix.resize (cr::sqrf (m_length));
m_matrix.resize (static_cast <size_t> (cr::sqrf (m_length)));
worker.enqueue ([this] () {
syncRebuild ();
@ -416,8 +416,10 @@ bool FloydWarshallAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNo
void DijkstraAlgo::init (const int length) {
m_length = length;
m_distance.resize (length);
m_parent.resize (length);
auto ulength = static_cast <size_t> (length);
m_distance.resize (ulength);
m_parent.resize (ulength);
m_distance.shrink ();
m_parent.shrink ();
@ -435,7 +437,7 @@ bool DijkstraAlgo::find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, i
m_distance[srcIndex] = 0;
while (!m_queue.empty ()) {
auto &&route = cr::move (m_queue.pop ());
auto route = m_queue.pop ();
auto current = route.second;
// finished search
@ -500,7 +502,7 @@ void PathPlanner::init () {
const int length = graph.length ();
const float limitInMb = cv_path_floyd_memory_limit.float_ ();
const float memoryUse = static_cast <float> (sizeof (FloydWarshallAlgo::Matrix) * cr::sqrf (length) / 1024 / 1024);
const float memoryUse = static_cast <float> (sizeof (FloydWarshallAlgo::Matrix) * cr::sqrf (static_cast <size_t> (length)) / 1024 / 1024);
// if we're have too much memory for floyd matrices, planner will use dijkstra or uniform planner for other than pathfinding needs
if (memoryUse > limitInMb) {

View file

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

View file

@ -204,7 +204,7 @@ bool BotSupport::isMonster (edict_t *ent) {
return false;
}
if (cr::strncmp ("hostage", ent->v.classname.chars (), 7) == 0) {
if (ent->v.classname.str ().startsWith ("hostage")) {
return false;
}
@ -212,7 +212,7 @@ bool BotSupport::isMonster (edict_t *ent) {
}
bool BotSupport::isItem (edict_t *ent) {
return !!(strstr (ent->v.classname.chars (), "item_"));
return ent && ent->v.classname.str ().contains ("item_");
}
bool BotSupport::isPlayerVIP (edict_t *ent) {

View file

@ -1426,9 +1426,11 @@ void Bot::pickupItem_ () {
switch (m_pickupType) {
case Pickup::DroppedC4:
case Pickup::None:
case Pickup::Items:
break;
case Pickup::Weapon:
case Pickup::AmmoAndKits:
m_aimFlags |= AimFlags::Nav;
// near to weapon?
@ -1436,17 +1438,17 @@ void Bot::pickupItem_ () {
int index = 0;
auto &info = conf.getWeapons ();
for (index = 0; index < 7; ++index) {
if (cr::strcmp (info[index].model, m_pickupItem->v.model.chars (9)) == 0) {
for (index = 0; index < kPrimaryWeaponMinIndex; ++index) {
if (m_pickupItem->v.model.str (9) == info[index].model) {
break;
}
}
if (index < 7) {
if (index < kPrimaryWeaponMinIndex) {
// secondary weapon. i.e., pistol
int weaponIndex = 0;
for (index = 0; index < 7; ++index) {
for (index = 0; index < kPrimaryWeaponMinIndex; ++index) {
if (pev->weapons & cr::bit (info[index].id)) {
weaponIndex = index;
}
@ -1469,7 +1471,7 @@ void Bot::pickupItem_ () {
auto tab = conf.getRawWeapons ();
if ((tab->id == Weapon::Shield || weaponIndex > 6 || hasShield ()) && niceWeapon) {
if ((tab[weaponIndex].id == Weapon::Shield || weaponIndex >= kPrimaryWeaponMinIndex || hasShield ()) && niceWeapon) {
selectWeaponByIndex (weaponIndex);
dropCurrentWeapon ();
}
@ -1559,9 +1561,9 @@ void Bot::pickupItem_ () {
// find the nearest 'unused' hostage within the area
game.searchEntities (pev->origin, 768.0f, [&] (edict_t *ent) {
auto classname = ent->v.classname.chars ();
auto classname = ent->v.classname.str ();
if (cr::strncmp ("hostage_entity", classname, 14) != 0 && cr::strncmp ("monster_scientist", classname, 17) != 0) {
if (!classname.startsWith ("hostage_entity") && !classname.startsWith ("monster_scientist")) {
return EntitySearchResult::Continue;
}

View file

@ -35,7 +35,7 @@ bool Bot::isInViewCone (const Vector &origin) {
return util.isInViewCone (origin, ent ());
}
bool Bot::seesItem (const Vector &destination, const char *classname) {
bool Bot::seesItem (const Vector &destination, StringRef classname) {
TraceResult tr {};
// trace a line from bot's eyes to destination..
@ -43,7 +43,7 @@ bool Bot::seesItem (const Vector &destination, const char *classname) {
// check if line of sight to object is not blocked (i.e. visible)
if (tr.flFraction < 1.0f && tr.pHit && !tr.fStartSolid) {
return cr::strcmp (tr.pHit->v.classname.chars (), classname) == 0;
return classname == tr.pHit->v.classname.str ();
}
return true;
}
@ -106,7 +106,7 @@ void Bot::updateAimDir () {
m_lookAt.z += 48.0f;
}
else if (m_pickupType == Pickup::Weapon) {
m_lookAt.z += 72.0;
m_lookAt.z += 72.0f;
}
}
else if (flags & AimFlags::LastEnemy) {