diff --git a/inc/config.h b/inc/config.h index 750a798..52bb2c8 100644 --- a/inc/config.h +++ b/inc/config.h @@ -185,6 +185,16 @@ public: return m_weaponProps[id]; } + // get's weapons type by id + int32 getWeaponType (int id) const { + for (const auto &weapon : m_weapons) { + if (weapon.id == id) { + return weapon.type; + } + } + return WeaponType::None; + } + // get's weapon preferences for personality int32 *getWeaponPrefs (int personality) const { switch (personality) { diff --git a/inc/yapb.h b/inc/yapb.h index ea0acda..58db048 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -239,6 +239,19 @@ CR_DECLARE_SCOPED_ENUM (Chatter, 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, @@ -524,6 +537,7 @@ struct WeaponInfo { int buySelectCT; // for counter-strike v1.6 int penetratePower; // penetrate power int maxClip; // max ammo in clip + int type; // weapon class bool primaryFireHold; // hold down primary fire button to use? public: @@ -540,9 +554,10 @@ public: int buySelectCT, int penetratePower, int maxClip, + int type, bool fireHold) : id (id), name (name), model (model), price (price), minPrimaryAmmo (minPriAmmo), teamStandard (teamStd), teamAS (teamAs), buyGroup (buyGroup), buySelect (buySelect), buySelectT (buySelectT), buySelectCT (buySelectCT), - penetratePower (penetratePower), maxClip (maxClip), primaryFireHold (fireHold) + penetratePower (penetratePower), maxClip (maxClip), type (type), primaryFireHold (fireHold) { } }; @@ -980,6 +995,7 @@ public: int m_lastDamageType; // stores last damage int m_team; // bot team int m_currentWeapon; // one current weapon for each bot + int m_weaponType; // current weapon type int m_ammoInClip[kMaxWeapons]; // ammo in clip for each weapons int m_ammo[MAX_AMMO_SLOTS]; // total ammo amounts @@ -1025,6 +1041,7 @@ public: public: void logic (); /// the things that can be executed while skipping frames + void spawned (); void takeBlind (int alpha); void takeDamage (edict_t *inflictor, int damage, int armor, int bits); void showDebugOverlay (); @@ -1057,9 +1074,12 @@ public: bool usesPistol (); bool usesSniper (); bool usesSubmachine (); + bool usesShotgun (); + bool usesHeavy (); bool usesZoomableRifle (); bool usesBadWeapon (); bool usesCampGun (); + bool usesKnife (); bool hasPrimaryWeapon (); bool hasSecondaryWeapon (); bool hasShield (); diff --git a/src/botlib.cpp b/src/botlib.cpp index bf9dc36..1500cae 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -409,18 +409,25 @@ void Bot::checkBreakable (edict_t *touch) { } void Bot::checkBreakablesAround () { - if (!cv_destroy_breakables_around.bool_ () || m_currentWeapon == Weapon::Knife || rg.chance (25) || !game.hasBreakables () || m_seeEnemyTime + 4.0f > game.time () || !game.isNullEntity (m_enemy) || !hasPrimaryWeapon ()) { + if (!cv_destroy_breakables_around.bool_ () || usesKnife () || rg.chance (25) || !game.hasBreakables () || m_seeEnemyTime + 4.0f > game.time () || !game.isNullEntity (m_enemy) || !hasPrimaryWeapon ()) { return; } - // check if we're have some breakbles in 450 units range + // check if we're have some breakbles in 400 units range for (const auto &breakable : game.getBreakables ()) { if (!game.isShootableBreakable (breakable)) { continue; } const auto &origin = game.getEntityWorldOrigin (breakable); + const auto lengthToObstacle = (origin - pev->origin).lengthSq (); - if ((origin - pev->origin).lengthSq () > cr::square (450.0f)) { + // too far, skip it + if (lengthToObstacle > cr::square (400.0f)) { + continue; + } + + // too close, skip it + if (lengthToObstacle < cr::square (100.0f)) { continue; } @@ -605,11 +612,12 @@ void Bot::updatePickups () { allowPickup = false; } else if (!m_isVIP && primaryWeaponCarried >= 7 && (m_ammo[primary.id] > 0.3 * primaryProp.ammo1Max) && strncmp (model, "w_", 2) == 0) { + auto weaponType = conf.getWeaponType (primaryWeaponCarried); - const bool isSniperRifle = primaryWeaponCarried == Weapon::AWP || primaryWeaponCarried == Weapon::G3SG1 || primaryWeaponCarried == Weapon::SG550; - const bool isSubmachine = primaryWeaponCarried == Weapon::MP5 || primaryWeaponCarried == Weapon::TMP || primaryWeaponCarried == Weapon::P90 || primaryWeaponCarried == Weapon::MAC10 || primaryWeaponCarried == Weapon::UMP45; - const bool isShotgun = primaryWeaponCarried == Weapon::M3; - const bool isRifle = primaryWeaponCarried == Weapon::Famas || primaryWeaponCarried == Weapon::AK47 || primaryWeaponCarried == Weapon::M4A1 || primaryWeaponCarried == Weapon::Galil || primaryWeaponCarried == Weapon::AUG || primaryWeaponCarried == Weapon::SG552; + 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 (strcmp (model, "w_9mmarclip.mdl") == 0 && !isRifle) { allowPickup = false; @@ -762,7 +770,7 @@ void Bot::updatePickups () { m_defendedBomb = true; int index = findDefendNode (origin); - const Path &path = graph[index]; + const auto &path = graph[index]; float timeToExplode = bots.getTimeBombPlanted () + mp_c4timer.float_ () - graph.calculateTravelTime (pev->maxspeed, pev->origin, path.origin); @@ -1232,7 +1240,7 @@ bool Bot::canReplaceWeapon () { else if (m_currentWeapon == Weapon::MP5 && m_moneyAmount > 6000) { return true; } - else if ((m_currentWeapon == Weapon::M3 || m_currentWeapon == Weapon::XM1014) && m_moneyAmount > 4000) { + else if (usesShotgun () && m_moneyAmount > 4000) { return true; } return isWeaponRestricted (m_currentWeapon); @@ -1689,7 +1697,7 @@ void Bot::updateEmotions () { void Bot::overrideConditions () { - if (m_currentWeapon != Weapon::Knife && m_difficulty > Difficulty::Normal && ((m_aimFlags & AimFlags::Enemy) || (m_states & Sense::SeeingEnemy)) && !cv_jasonmode.bool_ () && getCurrentTaskId () != Task::Camp && getCurrentTaskId () != Task::SeekCover && !isOnLadder ()) { + if (!usesKnife () && m_difficulty > Difficulty::Normal && ((m_aimFlags & AimFlags::Enemy) || (m_states & Sense::SeeingEnemy)) && !cv_jasonmode.bool_ () && getCurrentTaskId () != Task::Camp && getCurrentTaskId () != Task::SeekCover && !isOnLadder ()) { m_moveToGoal = false; // don't move to goal m_navTimeset = game.time (); @@ -1707,7 +1715,7 @@ void Bot::overrideConditions () { } // special handling, if we have a knife in our hands - if ((bots.getRoundStartTime () + 6.0f > game.time () || !hasAnyWeapons ()) && m_currentWeapon == Weapon::Knife && util.isPlayer (m_enemy)) { + if ((bots.getRoundStartTime () + 6.0f > game.time () || !hasAnyWeapons ()) && usesKnife () && util.isPlayer (m_enemy)) { float length = (pev->origin - m_enemy->v.origin).length2d (); // do waypoint movement if enemy is not reachable with a knife @@ -1808,7 +1816,7 @@ void Bot::setConditions () { } // if no more enemies found AND bomb planted, switch to knife to get to bombplace faster - if (m_team == Team::CT && m_currentWeapon != Weapon::Knife && m_numEnemiesLeft == 0 && bots.isBombPlanted ()) { + if (m_team == Team::CT && !usesKnife () && m_numEnemiesLeft == 0 && bots.isBombPlanted ()) { selectWeaponByName ("weapon_knife"); m_plantedBombNodeIndex = getNearestToPlantedBomb (); @@ -1945,7 +1953,7 @@ void Bot::filterTasks () { } bool lowAmmo = m_ammoInClip[m_currentWeapon] < conf.findWeaponById (m_currentWeapon).maxClip * 0.18f; - if (bots.isBombPlanted () || m_isStuck || m_currentWeapon == Weapon::Knife) { + if (bots.isBombPlanted () || m_isStuck || usesKnife ()) { ratio /= 3.0f; // reduce the seek cover desire if bomb is planted } else if (m_isVIP || m_isReloading || (lowAmmo && usesSniper ())) { @@ -2427,7 +2435,7 @@ void Bot::checkRadioQueue () { case Radio::RegroupTeam: // if no more enemies found AND bomb planted, switch to knife to get to bombplace faster - if (m_team == Team::CT && m_currentWeapon != Weapon::Knife && m_numEnemiesLeft == 0 && bots.isBombPlanted () && getCurrentTaskId () != Task::DefuseBomb) { + if (m_team == Team::CT && !usesKnife () && m_numEnemiesLeft == 0 && bots.isBombPlanted () && getCurrentTaskId () != Task::DefuseBomb) { selectWeaponByName ("weapon_knife"); clearSearchNodes (); @@ -3055,7 +3063,7 @@ void Bot::normal_ () { } // bots rushing with knife, when have no enemy (thanks for idea to nicebot project) - if (m_currentWeapon == Weapon::Knife && (game.isNullEntity (m_lastEnemy) || !util.isAlive (m_lastEnemy)) && game.isNullEntity (m_enemy) && m_knifeAttackTime < game.time () && !hasShield () && numFriendsNear (pev->origin, 96.0f) == 0) { + if (usesKnife () && (game.isNullEntity (m_lastEnemy) || !util.isAlive (m_lastEnemy)) && game.isNullEntity (m_enemy) && m_knifeAttackTime < game.time () && !hasShield () && numFriendsNear (pev->origin, 96.0f) == 0) { if (rg.chance (40)) { pev->button |= IN_ATTACK; } @@ -3233,7 +3241,12 @@ void Bot::normal_ () { ignoreCollision (); // did we already decide about a goal before? - int destIndex = getTask ()->data != kInvalidNodeIndex ? getTask ()->data : findBestGoal (); + auto destIndex = graph.exists (getTask ()->data) ? getTask ()->data : findBestGoal (); + + // check for existance (this is failover, for i.e. csdm, this should be not true with normal gameplay, only when spawned outside of waypointed area) + if (!graph.exists (destIndex)) { + destIndex = graph.getFarest (pev->origin, 512.0f); + } m_prevGoalIndex = destIndex; @@ -3483,7 +3496,7 @@ void Bot::attackEnemy_ () { ignoreCollision (); attackMovement (); - if (m_currentWeapon == Weapon::Knife && !m_lastEnemyOrigin.empty ()) { + if (usesKnife () && !m_lastEnemyOrigin.empty ()) { m_destOrigin = m_lastEnemyOrigin; } } @@ -4348,7 +4361,7 @@ void Bot::escapeFromBomb_ () { pev->button |= IN_ATTACK2; } - if (m_currentWeapon != Weapon::Knife && m_numEnemiesLeft == 0) { + if (!usesKnife () && m_numEnemiesLeft == 0) { selectWeaponByName ("weapon_knife"); } @@ -4423,7 +4436,7 @@ void Bot::shootBreakable_ () { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; - if (m_currentWeapon == Weapon::Knife) { + if (usesKnife ()) { selectBestWeapon (); } m_wantsToFire = true; @@ -4919,6 +4932,12 @@ void Bot::logic () { m_lastDamageType = -1; // reset damage } +void Bot::spawned () { + if (game.is (GameFlags::CSDM)) { + newRound (); + } +} + void Bot::showDebugOverlay () { bool displayDebugOverlay = false; @@ -5050,7 +5069,6 @@ void Bot::showDebugOverlay () { for (size_t i = 0; i < m_pathWalk.length () && i + 1 < m_pathWalk.length (); ++i) { game.drawLine (game.getLocalEntity (), graph[m_pathWalk.at (i)].origin, graph[m_pathWalk.at (i + 1)].origin, 15, 0, { 255, 100, 55 }, 200, 5, 1, DrawLine::Arrow); } - } bool Bot::hasHostage () { diff --git a/src/combat.cpp b/src/combat.cpp index 72a3560..fd1d322 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -461,27 +461,20 @@ const Vector &Bot::getEnemyBodyOffset () { aimPos += getBodyOffsetError (distance); } else { - - + // now take in account different parts of enemy body if (m_enemyParts & (Visibility::Head | Visibility::Body)) { - // forced to use body? - bool useBody = !usesPistol () && distance >= kSprayDistance && distance < 3072.0f; // now check is our skill match to aim at head, else aim at enemy body - if (rg.chance (conf.getDifficultyTweaks (m_difficulty)->headshotPct) && !useBody) { + if (rg.chance (conf.getDifficultyTweaks (m_difficulty)->headshotPct)) { aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance); } else { - aimPos.z += getEnemyBodyOffsetCorrection (distance); - - if (useBody) { - aimPos.z += 4.5f; - } + aimPos.z += 3.5f; } } else if (m_enemyParts & Visibility::Body) { - aimPos.z += getEnemyBodyOffsetCorrection (distance); + aimPos.z += 3.5f; } else if (m_enemyParts & Visibility::Other) { aimPos = m_enemyOrigin; @@ -502,44 +495,41 @@ const Vector &Bot::getEnemyBodyOffset () { } float Bot::getEnemyBodyOffsetCorrection (float distance) { - bool sniper = usesSniper (); - bool pistol = usesPistol (); - bool rifle = usesRifle (); + enum DistanceIndex { + Long, Middle, Short + }; - bool zoomableRifle = usesZoomableRifle (); - bool submachine = usesSubmachine (); - bool shotgun = (m_currentWeapon == Weapon::XM1014 || m_currentWeapon == Weapon::M3); - bool m249 = m_currentWeapon == Weapon::M249; + static float offsetRanges[9][3] = { + { 0.0f, 0.0f, 0.0f }, // none + { 0.0f, 0.0f, 0.0f }, // melee + { 6.5f, 6.5f, 4.5f }, // pistol + { 9.5f, 9.0f, -5.0f }, // shotgun + { 4.5f, 3.5f, -5.0f }, // zoomrifle + { 5.5f, 1.0f, -4.5f }, // rifle + { 5.5f, 3.5f, -4.5f }, // smg + { 3.5f, 3.5f, 4.5f }, // sniper + { 2.5f, -2.0f, -6.0f } // heavy + }; - float result = -2.0f; - - if (distance >= kDoubleSprayDistance) { - if (sniper) { - result = 0.18f; - } - else if (zoomableRifle) { - result = 1.5f; - } - else if (pistol) { - result = 2.5f; - } - else if (submachine) { - result = 1.5f; - } - else if (rifle) { - result = -1.0f; - } - else if (m249) { - result = -5.5f; - } - else if (shotgun) { - result = -4.5f; - } + // only highskilled bots do that + if (m_difficulty < Difficulty::Normal) { + return 0.0f; } - else { - result = -5.6f; + + // default distance index is short + int32 distanceIndex = DistanceIndex::Short; + + // set distance index appropriate to distance + if (distance < 3072.0f && distance > kDoubleSprayDistance) { + distanceIndex = DistanceIndex::Long; } - return result; + else if (distance > kSprayDistance && distance <= kDoubleSprayDistance) { + distanceIndex = DistanceIndex::Middle; + } + else if (distance < kSprayDistance) { + distanceIndex = DistanceIndex::Short; + } + return offsetRanges[m_weaponType][distanceIndex]; } bool Bot::isFriendInLineOfFire (float distance) { @@ -967,12 +957,12 @@ bool Bot::isWeaponBadAtDistance (int weaponIndex, float distance) { } // better use pistol in short range distances, when using sniper weapons - if ((wid == Weapon::Scout || wid == Weapon::AWP || wid == Weapon::G3SG1 || wid == Weapon::SG550) && distance < 450.0f) { + if (m_weaponType == WeaponType::Sniper && distance < 450.0f) { return true; } // shotguns is too inaccurate at long distances, so weapon is bad - if ((wid == Weapon::M3 || wid == Weapon::XM1014) && distance > 750.0f) { + if (m_weaponType == WeaponType::Shotgun && distance > 750.0f) { return true; } return false; @@ -992,7 +982,7 @@ void Bot::focusEnemy () { float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum? if (distance < 128.0f && !usesSniper ()) { - if (m_currentWeapon == Weapon::Knife) { + if (usesKnife ()) { if (distance < 80.0f) { m_wantsToFire = true; } @@ -1039,7 +1029,7 @@ void Bot::attackMovement () { if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.time ()) { int approach; - if (m_currentWeapon == Weapon::Knife) { + if (usesKnife ()) { approach = 100; } else if ((m_states & Sense::SuspectEnemy) && !(m_states & Sense::SeeingEnemy)) { @@ -1068,7 +1058,7 @@ void Bot::attackMovement () { m_moveSpeed = pev->maxspeed; } - if (distance < 96.0f && m_currentWeapon != Weapon::Knife) { + if (distance < 96.0f && !usesKnife ()) { m_moveSpeed = -pev->maxspeed; } @@ -1106,7 +1096,7 @@ void Bot::attackMovement () { m_fightStyle = Fight::Strafe; } - if (m_fightStyle == Fight::Strafe || ((pev->button & IN_RELOAD) || m_isReloading) || (usesPistol () && distance < 400.0f) || m_currentWeapon == Weapon::Knife) { + if (m_fightStyle == Fight::Strafe || ((pev->button & IN_RELOAD) || m_isReloading) || (usesPistol () && distance < 400.0f) || usesKnife ()) { if (m_strafeSetTime < game.time ()) { // to start strafing, we have to first figure out if the target is on the left side or right side @@ -1149,11 +1139,11 @@ void Bot::attackMovement () { pev->button |= IN_JUMP; } - if (m_moveSpeed > 0.0f && distance > 100.0f && m_currentWeapon != Weapon::Knife) { + if (m_moveSpeed > 0.0f && distance > 100.0f && !usesKnife ()) { m_moveSpeed = 0.0f; } - if (m_currentWeapon == Weapon::Knife) { + if (usesKnife ()) { m_strafeSpeed = 0.0f; } } @@ -1172,7 +1162,7 @@ void Bot::attackMovement () { } if (m_fightStyle == Fight::Stay || (m_duckTime > game.time () || m_sniperStopTime > game.time ())) { - if (m_moveSpeed > 0.0f && m_currentWeapon != Weapon::Knife) { + if (m_moveSpeed > 0.0f && !usesKnife ()) { m_moveSpeed = 0.0f; } } @@ -1237,60 +1227,43 @@ bool Bot::isEnemyBehindShield (edict_t *enemy) { bool Bot::usesSniper () { // this function returns true, if returns if bot is using a sniper rifle - return m_currentWeapon == Weapon::AWP || m_currentWeapon == Weapon::G3SG1 || m_currentWeapon == Weapon::Scout || m_currentWeapon == Weapon::SG550; + return m_weaponType == WeaponType::Sniper; } bool Bot::usesRifle () { - auto tab = conf.getRawWeapons (); - int count = 0; - - while (tab->id) { - if (m_currentWeapon == tab->id) { - break; - } - tab++; - count++; - } - - if (tab->id && count > 13) { - return true; - } - return false; -} - -bool Bot::usesPistol () { - auto tab = conf.getRawWeapons (); - int count = 0; - - // loop through all the weapons until terminator is found - while (tab->id) { - if (m_currentWeapon == tab->id) { - break; - } - tab++; - count++; - } - - if (tab->id && count < 7) { - return true; - } - return false; -} - -bool Bot::usesCampGun () { - return usesSubmachine () || usesRifle () || usesSniper (); -} - -bool Bot::usesSubmachine () { - return m_currentWeapon == Weapon::MP5 || m_currentWeapon == Weapon::TMP || m_currentWeapon == Weapon::P90 || m_currentWeapon == Weapon::MAC10 || m_currentWeapon == Weapon::UMP45; + return usesZoomableRifle () || m_weaponType == WeaponType::Rifle; } bool Bot::usesZoomableRifle () { - return m_currentWeapon == Weapon::AUG || m_currentWeapon == Weapon::SG552; + return m_weaponType == WeaponType::ZoomRifle; +} + +bool Bot::usesPistol () { + return m_weaponType == WeaponType::Pistol; +} + +bool Bot::usesSubmachine () { + return m_weaponType == WeaponType::SMG;; +} + +bool Bot::usesShotgun () { + return m_weaponType == WeaponType::Shotgun; +} + +bool Bot::usesHeavy () { + return m_weaponType == WeaponType::Heavy; } bool Bot::usesBadWeapon () { - return m_currentWeapon == Weapon::XM1014 || m_currentWeapon == Weapon::M3 || m_currentWeapon == Weapon::UMP45 || m_currentWeapon == Weapon::MAC10 || m_currentWeapon == Weapon::TMP || m_currentWeapon == Weapon::P90; + return usesShotgun () || m_currentWeapon == Weapon::UMP45 || m_currentWeapon == Weapon::MAC10 || m_currentWeapon == Weapon::TMP; +} + +bool Bot::usesCampGun () { + return usesSubmachine () || usesRifle () || usesSniper () || usesHeavy (); +} + +bool Bot::usesKnife (){ + return m_weaponType == WeaponType::Melee; } int Bot::bestPrimaryCarried () { @@ -1301,7 +1274,7 @@ int Bot::bestPrimaryCarried () { int weaponIndex = 0; int weapons = pev->weapons; - auto &weaponTab = conf.getWeapons (); + const auto &tab = conf.getWeapons (); // take the shield in account if (hasShield ()) { @@ -1309,7 +1282,7 @@ int Bot::bestPrimaryCarried () { } for (int i = 0; i < kNumWeapons; ++i) { - if (weapons & cr::bit (weaponTab[*pref].id)) { + if (weapons & cr::bit (tab[*pref].id)) { weaponIndex = i; } pref++; @@ -1329,7 +1302,7 @@ int Bot::bestSecondaryCarried () { if (hasShield ()) { weapons |= cr::bit (Weapon::Shield); } - auto tab = conf.getRawWeapons (); + const auto tab = conf.getRawWeapons (); for (int i = 0; i < kNumWeapons; ++i) { int id = tab[*pref].id; diff --git a/src/config.cpp b/src/config.cpp index b5f58c5..40a074a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -696,35 +696,35 @@ 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, true); - m_weapons.emplace (Weapon::USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, 12, false); - m_weapons.emplace (Weapon::Glock18, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, 20, false); - m_weapons.emplace (Weapon::Deagle, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, 7, false); - m_weapons.emplace (Weapon::P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0, 13, false); - m_weapons.emplace (Weapon::Elite, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, 30, false); - m_weapons.emplace (Weapon::FiveSeven, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, 20, false); - m_weapons.emplace (Weapon::M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, 8, false); - m_weapons.emplace (Weapon::XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, 7, false); - m_weapons.emplace (Weapon::MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, 30, true); - m_weapons.emplace (Weapon::TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, 30, true); - m_weapons.emplace (Weapon::P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, 50, true); - m_weapons.emplace (Weapon::MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, 30, true); - m_weapons.emplace (Weapon::UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, 25, true); - m_weapons.emplace (Weapon::AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, 30, true); - m_weapons.emplace (Weapon::SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, 30, true); - m_weapons.emplace (Weapon::M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, 30, true); - m_weapons.emplace (Weapon::Galil, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, 35, true); - m_weapons.emplace (Weapon::Famas, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, 25, true); - m_weapons.emplace (Weapon::AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, 30, true); - m_weapons.emplace (Weapon::Scout, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, 10, false); - m_weapons.emplace (Weapon::AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, 10, false); - m_weapons.emplace (Weapon::G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, 20, false); - m_weapons.emplace (Weapon::SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, 30, false); - m_weapons.emplace (Weapon::M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, 100, true); - m_weapons.emplace (Weapon::Shield, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, 0, false); + 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); // 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, false); + m_weapons.emplace (0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WeaponType::None, false); } void BotConfig::adjustWeaponPrices () { diff --git a/src/engine.cpp b/src/engine.cpp index 7804abd..60e4c21 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -137,6 +137,11 @@ void Game::levelInitialize (edict_t *entities, int max) { } else if (strcmp (classname, "func_escapezone") == 0) { m_mapFlags |= MapFlags::Escape; + + // strange thing on some ES maps, where hostage entity present there + if (m_mapFlags & MapFlags::HostageRescue) { + m_mapFlags &= ~MapFlags::HostageRescue; + } } else if (strncmp (classname, "func_door", 9) == 0) { m_mapFlags |= MapFlags::HasDoors; @@ -366,7 +371,7 @@ void Game::registerEngineCommand (const char *command, void func ()) { // check for hl pre 1.1.0.4, as it's doesn't have pfnAddServerCommand if (!plat.checkPointer (engfuncs.pfnAddServerCommand)) { - logger.fatal ("%s's minimum HL engine version is 1.1.0.6 and minimum Counter-Strike is Beta 7.1. Please update your engine / game version.", product.name); + logger.fatal ("%s's minimum HL engine version is 1.1.0.4 and minimum Counter-Strike is Beta 6.5. Please update your engine / game version.", product.name); } engfuncs.pfnAddServerCommand (const_cast (command), func); } diff --git a/src/linkage.cpp b/src/linkage.cpp index 2475810..6c76137 100644 --- a/src/linkage.cpp +++ b/src/linkage.cpp @@ -133,6 +133,12 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int) { // Spawn() function is one of the functions any entity is supposed to have in the game DLL, // and any MOD is supposed to implement one for each of its entities. + auto bot = bots[ent]; + + if (bot) { + bot->spawned (); + } + if (game.is (GameFlags::Metamod)) { RETURN_META_VALUE (MRES_IGNORED, 0); } @@ -954,3 +960,4 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t // add linkents for android #include "android.cpp" + diff --git a/src/manager.cpp b/src/manager.cpp index 82b1639..c2937c4 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -958,6 +958,8 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) { plat.bzero (&m_ammo, sizeof (m_ammo)); m_currentWeapon = 0; // current weapon is not assigned at start + m_weaponType = WeaponType::None; // current weapon type is not assigned at start + m_voicePitch = rg.int_ (80, 115); // assign voice pitch // copy them over to the temp level variables @@ -1255,6 +1257,7 @@ void Bot::newRound () { plat.bzero (&m_ammo, sizeof (m_ammo)); m_currentWeapon = 0; + m_weaponType = 0; } m_flashLevel = 100.0f; m_checkDarkTime = game.time (); diff --git a/src/message.cpp b/src/message.cpp index 7f4661f..08c927f 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -166,6 +166,7 @@ void MessageDispatcher::netMsgCurWeapon () { if (m_args[id].long_ < kMaxWeapons) { if (m_args[state].long_ != 0) { m_bot->m_currentWeapon = m_args[id].long_; + m_bot->m_weaponType = conf.getWeaponType (m_args[id].long_); } // ammo amount decreased ? must have fired a bullet... @@ -329,13 +330,6 @@ void MessageDispatcher::netMsgTeamInfo () { // update player team client.team2 = m_teamInfoCache[m_args[team].chars_]; // update real team client.team = game.is (GameFlags::FreeForAll) ? m_args[index].long_ : client.team2; - - auto bot = bots[client.ent]; - - // clear the routes so we're have no error in pathfinding in case team info update (respawn/change team) - if (bot) { - bot->clearSearchNodes (); - } } void MessageDispatcher::netMsgBarTime () { diff --git a/src/navigate.cpp b/src/navigate.cpp index 5827828..d617371 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -676,7 +676,7 @@ bool Bot::updateNavigation () { m_desiredVelocity = nullptr; } } - else if (!cv_jasonmode.bool_ () && m_currentWeapon == Weapon::Knife && isOnFloor ()) { + else if (!cv_jasonmode.bool_ () && usesKnife () && isOnFloor ()) { selectBestWeapon (); } } @@ -1176,11 +1176,11 @@ void Bot::findShortestPath (int srcIndex, int destIndex) { // this function finds the shortest path from source index to destination index if (!graph.exists (srcIndex)){ - logger.error ("Pathfinder source path index not valid (%d).", srcIndex); + logger.error ("%s source path index not valid (%d).", __FUNCTION__, srcIndex); return; } else if (!graph.exists (destIndex)) { - logger.error ("Pathfinder destination path index not valid (%d).", destIndex); + logger.error ("%s destination path index not valid (%d).", __FUNCTION__, destIndex); return; } clearSearchNodes (); @@ -1329,7 +1329,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: float euclidean = cr::powf (cr::powf (x, 2.0f) + cr::powf (y, 2.0f) + cr::powf (z, 2.0f), 0.5f); if (cv_debug_heuristic_type.int_ () == 4) { - return 1000.0f *(cr::ceilf (euclidean) - euclidean); + return 1000.0f * (cr::ceilf (euclidean) - euclidean); } return euclidean; } @@ -1348,8 +1348,18 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: return hfunctionPathDist (index, startIndex, goalIndex) / 128.0f * 10.0f; }; + if (!graph.exists (srcIndex)) { + logger.error ("%s source path index not valid (%d).", __FUNCTION__, srcIndex); + return; + } + else if (!graph.exists (destIndex)) { + + logger.error ("%s destination path index not valid (%d).", __FUNCTION__, destIndex); + return; + } + // holders for heuristic functions - Lambda gcalc, hcalc; + static Lambda gcalc, hcalc; // get correct calculation for heuristic if (pathType == FindPath::Optimal) { @@ -1382,15 +1392,6 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath: gcalc = gfunctionPathDist; } } - - if (!graph.exists (srcIndex)) { - logger.error ("Pathfinder source path index not valid (%d).", srcIndex); - return; - } - else if (!graph.exists (destIndex)) { - logger.error ("Pathfinder destination path index not valid (%d).", destIndex); - return; - } clearSearchNodes (); m_chosenGoalIndex = srcIndex; @@ -2182,7 +2183,7 @@ bool Bot::advanceMovement () { } // is there a jump node right ahead and do we need to draw out the light weapon ? - if (willJump && m_currentWeapon != Weapon::Knife && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (jumpDistance > 200.0f || (dst.z - 32.0f > src.z && jumpDistance > 150.0f)) && !(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))) { + if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (jumpDistance > 200.0f || (dst.z - 32.0f > src.z && jumpDistance > 150.0f)) && !(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))) { selectWeaponByName ("weapon_knife"); // draw out the knife if we needed } @@ -2206,7 +2207,7 @@ bool Bot::advanceMovement () { // if wayzone radius non zero vary origin a bit depending on the body angles if (m_path->radius > 0.0f) { - m_pathOrigin = m_pathOrigin + Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f).forward () * rg.float_ (0.0f, m_path->radius); + m_pathOrigin += Vector (pev->angles.x, cr::normalizeAngles (pev->angles.y + rg.float_ (-90.0f, 90.0f)), 0.0f).forward () * rg.float_ (0.0f, m_path->radius); } if (isOnLadder ()) {