misc: refactored weapon type recognition.

fix: reverted body offsets from 2.7 version.
fix: do not destroy breakable around if closer than 100 units. (#156 possible can be fixed).
fix: minimum supported hl & cs version display.
fix: csdm respawn outside of waypointed area causing pathfinder errors.
This commit is contained in:
ds 2020-09-13 02:21:15 +03:00
commit a675c40927
10 changed files with 205 additions and 174 deletions

View file

@ -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) {

View file

@ -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 ();

View file

@ -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 () {

View file

@ -462,26 +462,19 @@ const Vector &Bot::getEnemyBodyOffset () {
}
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;
// only highskilled bots do that
if (m_difficulty < Difficulty::Normal) {
return 0.0f;
}
if (distance >= kDoubleSprayDistance) {
if (sniper) {
result = 0.18f;
// default distance index is short
int32 distanceIndex = DistanceIndex::Short;
// set distance index appropriate to distance
if (distance < 3072.0f && distance > kDoubleSprayDistance) {
distanceIndex = DistanceIndex::Long;
}
else if (zoomableRifle) {
result = 1.5f;
else if (distance > kSprayDistance && distance <= kDoubleSprayDistance) {
distanceIndex = DistanceIndex::Middle;
}
else if (pistol) {
result = 2.5f;
else if (distance < kSprayDistance) {
distanceIndex = DistanceIndex::Short;
}
else if (submachine) {
result = 1.5f;
}
else if (rifle) {
result = -1.0f;
}
else if (m249) {
result = -5.5f;
}
else if (shotgun) {
result = -4.5f;
}
}
else {
result = -5.6f;
}
return result;
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;

View file

@ -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 () {

View file

@ -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 <char *> (command), func);
}

View file

@ -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"

View file

@ -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 ();

View file

@ -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 () {

View file

@ -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 <float (int, int, int)> gcalc, hcalc;
static Lambda <float (int, int, int)> 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 ()) {