Refactoring (#246)

add: yb_chat_percent, yb_camping_time_[min/max], yb_danger_factor[min/max], yb_chat_percent cvars
fix: possible crash with difficulty 0
fix: debug_goal should not be used as last history
fix:  startup on old hlds, because of missing pfnGetFileSize
fix: crash with out-bounds read
change: again tweaked some aiming code
change: player avoidance code so bots will jump less when stuck
change: max followers is just /4 of max players
refactor: redone distance between vectors
refactor: remove magic numbers in graph.add function
This commit is contained in:
jeefo 2021-09-20 13:50:00 +03:00 committed by GitHub
commit 6f912eb056
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 214 additions and 177 deletions

@ -1 +1 @@
Subproject commit 26c617d0a0cd18238a62f6e829da9161b7d56bd0 Subproject commit b5392f30bd5c0f2d0de0c6b3f39104c9aa1b1ee0

View file

@ -43,11 +43,6 @@ CR_DECLARE_SCOPED_ENUM (PathConnection,
Bidirectional Bidirectional
) )
// defines node add commands
CR_DECLARE_SCOPED_ENUM (GraphAdd,
Normal = 0,
)
// a* route state // a* route state
CR_DECLARE_SCOPED_ENUM (RouteState, CR_DECLARE_SCOPED_ENUM (RouteState,
Open = 0, Open = 0,
@ -94,6 +89,20 @@ CR_DECLARE_SCOPED_ENUM (LiftState,
Leaving Leaving
) )
// node add flags
CR_DECLARE_SCOPED_ENUM (NodeAddFlag,
Normal = 0,
TOnly = 1,
CTOnly = 2,
NoHostage = 3,
Rescue = 4,
Camp = 5,
CampEnd = 6,
JumpStart = 9,
JumpEnd = 10,
Goal = 100
)
// a* route // a* route
struct Route { struct Route {
float g, f; float g, f;
@ -312,6 +321,7 @@ public:
int getDangerDamage (int team, int start, int goal); int getDangerDamage (int team, int start, int goal);
int getPathDist (int srcIndex, int destIndex); int getPathDist (int srcIndex, int destIndex);
int clearConnections (int index); int clearConnections (int index);
int getBspSize ();
float calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin); float calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin);

View file

@ -10,14 +10,17 @@
ConVar cv_debug ("yb_debug", "0", "Enables or disables useful messages about bot states. Not required for end users.", true, 0.0f, 4.0f); ConVar cv_debug ("yb_debug", "0", "Enables or disables useful messages about bot states. Not required for end users.", true, 0.0f, 4.0f);
ConVar cv_debug_goal ("yb_debug_goal", "-1", "Forces all alive bots to build path and go to the specified here graph node.", true, -1.0f, kMaxNodes); ConVar cv_debug_goal ("yb_debug_goal", "-1", "Forces all alive bots to build path and go to the specified here graph node.", true, -1.0f, kMaxNodes);
ConVar cv_user_follow_percent ("yb_user_follow_percent", "20", "Specifies the percent of bots, than can follow leader on each round start.", true, 0.0f, 100.0f); ConVar cv_user_follow_percent ("yb_user_follow_percent", "20", "Specifies the percent of bots, than can follow leader on each round start.", true, 0.0f, 100.0f);
ConVar cv_user_max_followers ("yb_user_max_followers", "1", "Specifies how many bots can follow a single user.", true, 0.0f, static_cast <float> (kGameMaxPlayers / 2)); ConVar cv_user_max_followers ("yb_user_max_followers", "1", "Specifies how many bots can follow a single user.", true, 0.0f, static_cast <float> (kGameMaxPlayers / 4));
ConVar cv_jasonmode ("yb_jasonmode", "0", "If enabled, all bots will be forced only the knife, skipping weapon buying routines."); ConVar cv_jasonmode ("yb_jasonmode", "0", "If enabled, all bots will be forced only the knife, skipping weapon buying routines.");
ConVar cv_radio_mode ("yb_radio_mode", "2", "Allows bots to use radio or chattter.\nAllowed values: '0', '1', '2'.\nIf '0', radio and chatter is disabled.\nIf '1', only radio allowed.\nIf '2' chatter and radio allowed.", true, 0.0f, 2.0f); ConVar cv_radio_mode ("yb_radio_mode", "2", "Allows bots to use radio or chattter.\nAllowed values: '0', '1', '2'.\nIf '0', radio and chatter is disabled.\nIf '1', only radio allowed.\nIf '2' chatter and radio allowed.", true, 0.0f, 2.0f);
ConVar cv_economics_rounds ("yb_economics_rounds", "1", "Specifies whether bots able to use team economics, like do not buy any weapons for whole team to keep money for better guns."); ConVar cv_economics_rounds ("yb_economics_rounds", "1", "Specifies whether bots able to use team economics, like do not buy any weapons for whole team to keep money for better guns.");
ConVar cv_walking_allowed ("yb_walking_allowed", "1", "Specifies whether bots able to use 'shift' if they thinks that enemy is near."); ConVar cv_walking_allowed ("yb_walking_allowed", "1", "Specifies whether bots able to use 'shift' if they thinks that enemy is near.");
ConVar cv_camping_allowed ("yb_camping_allowed", "1", "Allows or disallows bots to camp. Doesn't affects bomb/hostage defending tasks"); ConVar cv_camping_allowed ("yb_camping_allowed", "1", "Allows or disallows bots to camp. Doesn't affects bomb/hostage defending tasks.");
ConVar cv_camping_time_min ("yb_camping_time_min", "15.0", "Lower bound of time from which time for camping is calculated", true, 5.0f, 90.0f);
ConVar cv_camping_time_max ("yb_camping_time_max", "45.0", "Upper bound of time from which time for camping is calculated", true, 15.0f, 120.0f);
ConVar cv_tkpunish ("yb_tkpunish", "1", "Allows or disallows bots to take revenge of teamkillers / team attacks."); ConVar cv_tkpunish ("yb_tkpunish", "1", "Allows or disallows bots to take revenge of teamkillers / team attacks.");
ConVar cv_freeze_bots ("yb_freeze_bots", "0", "If enabled the bots think function is disabled, so bots will not move anywhere from their spawn spots."); ConVar cv_freeze_bots ("yb_freeze_bots", "0", "If enabled the bots think function is disabled, so bots will not move anywhere from their spawn spots.");
@ -30,7 +33,7 @@ ConVar cv_restricted_weapons ("yb_restricted_weapons", "", "Specifies semicolon
ConVar cv_attack_monsters ("yb_attack_monsters", "0", "Allows or disallows bots to attack monsters."); ConVar cv_attack_monsters ("yb_attack_monsters", "0", "Allows or disallows bots to attack monsters.");
ConVar cv_pickup_custom_items ("yb_pickup_custom_items", "0", "Allows or disallows bots to pickup custom items."); ConVar cv_pickup_custom_items ("yb_pickup_custom_items", "0", "Allows or disallows bots to pickup custom items.");
ConVar cv_ignore_objectives ("yb_ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages"); ConVar cv_ignore_objectives ("yb_ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages.");
// game console variables // game console variables
ConVar mp_c4timer ("mp_c4timer", nullptr, Var::GameRef); ConVar mp_c4timer ("mp_c4timer", nullptr, Var::GameRef);
@ -154,7 +157,7 @@ void Bot::checkGrenadesThrow () {
return; return;
} }
} }
float distance = (m_lastEnemyOrigin - pev->origin).length2d (); float distance = m_lastEnemyOrigin.distance2d (pev->origin);
// don't throw grenades at anything that isn't on the ground! // don't throw grenades at anything that isn't on the ground!
if (!(m_lastEnemy->v.flags & FL_ONGROUND) && !m_lastEnemy->v.waterlevel && m_lastEnemyOrigin.z > pev->absmax.z) { if (!(m_lastEnemy->v.flags & FL_ONGROUND) && !m_lastEnemy->v.waterlevel && m_lastEnemyOrigin.z > pev->absmax.z) {
@ -346,8 +349,8 @@ void Bot::avoidGrenades () {
} }
if (!(pent->v.flags & FL_ONGROUND)) { if (!(pent->v.flags & FL_ONGROUND)) {
float distance = (pent->v.origin - pev->origin).lengthSq (); float distance = pent->v.origin.distanceSq (pev->origin);
float distanceMoved = ((pent->v.origin + pent->v.velocity * getFrameInterval ()) - pev->origin).lengthSq (); float distanceMoved = pev->origin.distance (pent->v.origin + pent->v.velocity * getFrameInterval ());
if (distanceMoved < distance && distance < cr::square (500.0f)) { if (distanceMoved < distance && distance < cr::square (500.0f)) {
const auto &dirToPoint = (pev->origin - pent->v.origin).normalize2d (); const auto &dirToPoint = (pev->origin - pent->v.origin).normalize2d ();
@ -365,7 +368,7 @@ void Bot::avoidGrenades () {
} }
else if ((pent->v.flags & FL_ONGROUND) && strcmp (model, "smokegrenade.mdl") == 0) { else if ((pent->v.flags & FL_ONGROUND) && strcmp (model, "smokegrenade.mdl") == 0) {
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) { if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) {
float distance = (pent->v.origin - pev->origin).length (); float distance = pent->v.origin.distance (pev->origin);
// shrink bot's viewing distance to smoke grenade's distance // shrink bot's viewing distance to smoke grenade's distance
if (m_viewDistance > distance) { if (m_viewDistance > distance) {
@ -404,7 +407,7 @@ void Bot::checkBreakablesAround () {
continue; continue;
} }
const auto &origin = game.getEntityOrigin (breakable); const auto &origin = game.getEntityOrigin (breakable);
const auto lengthToObstacle = (origin - pev->origin).lengthSq (); const auto lengthToObstacle = origin.distanceSq (pev->origin);
// too far, skip it // too far, skip it
if (lengthToObstacle > cr::square (400.0f)) { if (lengthToObstacle > cr::square (400.0f)) {
@ -505,7 +508,7 @@ void Bot::updatePickups () {
const Vector &origin = game.getEntityOrigin (ent); const Vector &origin = game.getEntityOrigin (ent);
// too far from us ? // too far from us ?
if ((pev->origin - origin).lengthSq () > radius) { if (pev->origin.distanceSq (origin) > radius) {
continue; continue;
} }
@ -544,7 +547,7 @@ void Bot::updatePickups () {
} }
// too far from us ? // too far from us ?
if ((pev->origin - origin).lengthSq () > radius) { if (pev->origin.distanceSq (origin) > radius) {
continue; continue;
} }
@ -776,7 +779,7 @@ void Bot::updatePickups () {
} }
} }
if ((pev->origin - origin).lengthSq () > cr::square (60.0f)) { if (pev->origin.distanceSq (origin) > cr::square (60.0f)) {
if (!graph.isNodeReacheable (pev->origin, origin)) { if (!graph.isNodeReacheable (pev->origin, origin)) {
allowPickup = false; allowPickup = false;
@ -855,7 +858,7 @@ void Bot::getCampDirection (Vector *dest) {
// check if the trace hit something... // check if the trace hit something...
if (tr.flFraction < 1.0f) { if (tr.flFraction < 1.0f) {
float length = (tr.vecEndPos - src).lengthSq (); float length = tr.vecEndPos.distanceSq (src);
if (length > 10000.0f) { if (length > 10000.0f) {
return; return;
@ -1697,7 +1700,7 @@ void Bot::overrideConditions () {
// special handling, if we have a knife in our hands // special handling, if we have a knife in our hands
if ((bots.getRoundStartTime () + 6.0f > game.time () || !hasAnyWeapons ()) && usesKnife () && (util.isPlayer (m_enemy) || (cv_attack_monsters.bool_ () && util.isMonster (m_enemy)))) { if ((bots.getRoundStartTime () + 6.0f > game.time () || !hasAnyWeapons ()) && usesKnife () && (util.isPlayer (m_enemy) || (cv_attack_monsters.bool_ () && util.isMonster (m_enemy)))) {
float length = (pev->origin - m_enemy->v.origin).length2d (); float length = pev->origin.distance2d (m_enemy->v.origin);
// do waypoint movement if enemy is not reachable with a knife // do waypoint movement if enemy is not reachable with a knife
if (length > 100.0f && (m_states & Sense::SeeingEnemy)) { if (length > 100.0f && (m_states & Sense::SeeingEnemy)) {
@ -1891,7 +1894,7 @@ void Bot::filterTasks () {
filter[Task::PickupItem].desire = 50.0f; // always pickup button filter[Task::PickupItem].desire = 50.0f; // always pickup button
} }
else { else {
float distance = (500.0f - (game.getEntityOrigin (m_pickupItem) - pev->origin).length ()) * 0.2f; float distance = (500.0f - pev->origin.distance (game.getEntityOrigin (m_pickupItem))) * 0.2f;
if (distance > 50.0f) { if (distance > 50.0f) {
distance = 50.0f; distance = 50.0f;
@ -1955,7 +1958,7 @@ void Bot::filterTasks () {
// if half of the round is over, allow hunting // if half of the round is over, allow hunting
if (getCurrentTaskId () != Task::EscapeFromBomb && game.isNullEntity (m_enemy) && bots.getRoundMidTime () < game.time () && !m_isUsingGrenade && m_currentNodeIndex != graph.getNearest (m_lastEnemyOrigin) && m_personality != Personality::Careful && !cv_ignore_enemies.bool_ ()) { if (getCurrentTaskId () != Task::EscapeFromBomb && game.isNullEntity (m_enemy) && bots.getRoundMidTime () < game.time () && !m_isUsingGrenade && m_currentNodeIndex != graph.getNearest (m_lastEnemyOrigin) && m_personality != Personality::Careful && !cv_ignore_enemies.bool_ ()) {
float desireLevel = 4096.0f - ((1.0f - tempAgression) * (m_lastEnemyOrigin - pev->origin).length ()); float desireLevel = 4096.0f - ((1.0f - tempAgression) * m_lastEnemyOrigin.distance (pev->origin));
desireLevel = (100.0f * desireLevel) / 4096.0f; desireLevel = (100.0f * desireLevel) / 4096.0f;
desireLevel -= retreatLevel; desireLevel -= retreatLevel;
@ -2167,7 +2170,7 @@ bool Bot::isEnemyThreat () {
} }
// if enemy is near or facing us directly // if enemy is near or facing us directly
if ((m_enemy->v.origin - pev->origin).lengthSq () < cr::square (256.0f) || isInViewCone (m_enemy->v.origin)) { if (m_enemy->v.origin.distanceSq (pev->origin) < cr::square (256.0f) || isInViewCone (m_enemy->v.origin)) {
return true; return true;
} }
return false; return false;
@ -2188,7 +2191,7 @@ bool Bot::reactOnEnemy () {
} }
int enemyIndex = graph.getNearest (m_enemy->v.origin); int enemyIndex = graph.getNearest (m_enemy->v.origin);
auto lineDist = (m_enemy->v.origin - pev->origin).length (); auto lineDist = m_enemy->v.origin.distance (pev->origin);
auto pathDist = static_cast <float> (graph.getPathDist (ownIndex, enemyIndex)); auto pathDist = static_cast <float> (graph.getPathDist (ownIndex, enemyIndex));
if (pathDist - lineDist > 112.0f || isOnLadder ()) { if (pathDist - lineDist > 112.0f || isOnLadder ()) {
@ -2224,7 +2227,7 @@ void Bot::checkRadioQueue () {
m_radioOrder = 0; m_radioOrder = 0;
return; return;
} }
float distance = (m_radioEntity->v.origin - pev->origin).length (); float distance = m_radioEntity->v.origin.distance (pev->origin);
switch (m_radioOrder) { switch (m_radioOrder) {
case Radio::CoverMe: case Radio::CoverMe:
@ -2497,7 +2500,7 @@ void Bot::checkRadioQueue () {
} }
auto enemy = client.ent; auto enemy = client.ent;
float curDist = (m_radioEntity->v.origin - enemy->v.origin).lengthSq (); float curDist = m_radioEntity->v.origin.distanceSq (enemy->v.origin);
if (curDist < nearestDistance) { if (curDist < nearestDistance) {
nearestDistance = curDist; nearestDistance = curDist;
@ -2599,7 +2602,7 @@ void Bot::checkRadioQueue () {
// find nearest bomb waypoint to player // find nearest bomb waypoint to player
for (auto &point : graph.m_goalPoints) { for (auto &point : graph.m_goalPoints) {
distance = (graph[point].origin - m_radioEntity->v.origin).lengthSq (); distance = graph[point].origin.distanceSq (m_radioEntity->v.origin);
if (distance < minDistance) { if (distance < minDistance) {
minDistance = distance; minDistance = distance;
@ -2652,7 +2655,7 @@ void Bot::checkRadioQueue () {
} }
auto enemy = client.ent; auto enemy = client.ent;
float dist = (m_radioEntity->v.origin - enemy->v.origin).lengthSq (); float dist = m_radioEntity->v.origin.distanceSq (enemy->v.origin);
if (dist < nearestDistance) { if (dist < nearestDistance) {
nearestDistance = dist; nearestDistance = dist;
@ -2723,7 +2726,7 @@ void Bot::updateAimDir () {
else if (flags & AimFlags::Grenade) { else if (flags & AimFlags::Grenade) {
m_lookAt = m_throw; m_lookAt = m_throw;
float throwDistance = (m_throw - pev->origin).length (); float throwDistance = m_throw.distance (pev->origin);
float coordCorrection = 0.0f; float coordCorrection = 0.0f;
if (throwDistance > 100.0f && throwDistance < 800.0f) { if (throwDistance > 100.0f && throwDistance < 800.0f) {
@ -2799,7 +2802,7 @@ void Bot::updateAimDir () {
return nullptr; return nullptr;
}; };
if (m_moveToGoal && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && (pev->origin - m_destOrigin).lengthSq () < cr::square (160.0f)) { if (m_moveToGoal && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && pev->origin.distanceSq (m_destOrigin) < cr::square (160.0f)) {
auto nextPathIndex = m_pathWalk.next (); auto nextPathIndex = m_pathWalk.next ();
if (graph.isVisible (m_currentNodeIndex, nextPathIndex)) { if (graph.isVisible (m_currentNodeIndex, nextPathIndex)) {
@ -2817,7 +2820,7 @@ void Bot::updateAimDir () {
auto dangerIndex = graph.getDangerIndex (m_team, m_currentNodeIndex, m_currentNodeIndex); auto dangerIndex = graph.getDangerIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
if (graph.exists (dangerIndex) && graph.isVisible (m_currentNodeIndex, dangerIndex) && !(graph[dangerIndex].flags & NodeFlag::Crouch)) { if (graph.exists (dangerIndex) && graph.isVisible (m_currentNodeIndex, dangerIndex) && !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
if ((graph[dangerIndex].origin - pev->origin).lengthSq () < cr::square (160.0f)) { if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::square (160.0f)) {
m_lookAt = m_destOrigin; m_lookAt = m_destOrigin;
} }
else { else {
@ -2910,7 +2913,7 @@ void Bot::frame () {
if (bots.isBombPlanted () && m_team == Team::CT && m_notKilled) { if (bots.isBombPlanted () && m_team == Team::CT && m_notKilled) {
const Vector &bombPosition = graph.getBombOrigin (); const Vector &bombPosition = graph.getBombOrigin ();
if (!m_hasProgressBar && getCurrentTaskId () != Task::EscapeFromBomb && (pev->origin - bombPosition).lengthSq () < cr::square (1540.0f) && !isBombDefusing (bombPosition)) { if (!m_hasProgressBar && getCurrentTaskId () != Task::EscapeFromBomb && pev->origin.distanceSq (bombPosition) < cr::square (1540.0f) && !isBombDefusing (bombPosition)) {
m_itemIgnore = nullptr; m_itemIgnore = nullptr;
m_itemCheckTime = game.time (); m_itemCheckTime = game.time ();
@ -2927,7 +2930,7 @@ void Bot::frame () {
} }
// clear enemy far away // clear enemy far away
if (!m_lastEnemyOrigin.empty () && !game.isNullEntity (m_lastEnemy) && (pev->origin - m_lastEnemyOrigin).lengthSq () >= cr::square (1600.0f)) { if (!m_lastEnemyOrigin.empty () && !game.isNullEntity (m_lastEnemy) && pev->origin.distanceSq (m_lastEnemyOrigin) >= cr::square (1600.0f)) {
m_lastEnemy = nullptr; m_lastEnemy = nullptr;
m_lastEnemyOrigin = nullptr; m_lastEnemyOrigin = nullptr;
} }
@ -3134,7 +3137,7 @@ void Bot::normal_ () {
if (!(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && !m_reloadState) { if (!(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && !m_reloadState) {
m_reloadState = Reload::Primary; m_reloadState = Reload::Primary;
} }
m_timeCamping = game.time () + rg.get (10.0f, 25.0f); m_timeCamping = game.time () + rg.get (cv_camping_time_min.float_ (), cv_camping_time_max.float_ ());
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, m_timeCamping, true); startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, m_timeCamping, true);
m_camp = m_path->origin + m_path->start.forward () * 500.0f; m_camp = m_path->origin + m_path->start.forward () * 500.0f;
@ -3239,7 +3242,7 @@ void Bot::normal_ () {
destIndex = graph.getFarest (pev->origin, 512.0f); destIndex = graph.getFarest (pev->origin, 512.0f);
} }
if (m_prevGoalIndex == currIndex && !(graph[currIndex].flags & NodeFlag::Goal)) { if (!graph.exists (cv_debug_goal.int_ ()) && graph.exists (currIndex) && m_prevGoalIndex == currIndex && !(graph[currIndex].flags & NodeFlag::Goal)) {
m_goalHistory.push (currIndex); m_goalHistory.push (currIndex);
} }
m_prevGoalIndex = destIndex; m_prevGoalIndex = destIndex;
@ -3388,7 +3391,7 @@ void Bot::huntEnemy_ () {
} }
} }
if ((m_lastEnemyOrigin - pev->origin).lengthSq () < cr::square (512.0f)) { if (m_lastEnemyOrigin.distanceSq (pev->origin) < cr::square (512.0f)) {
m_moveSpeed = getShiftSpeed (); m_moveSpeed = getShiftSpeed ();
} }
} }
@ -3616,7 +3619,7 @@ void Bot::camp_ () {
const Vector &dotB = (graph[i].origin - pev->origin).normalize2d (); const Vector &dotB = (graph[i].origin - pev->origin).normalize2d ();
if ((dotA | dotB) > 0.9f) { if ((dotA | dotB) > 0.9f) {
int distance = static_cast <int> ((pev->origin - graph[i].origin).length ()); int distance = static_cast <int> (graph[i].origin.distance (pev->origin));
if (numFoundPoints >= 3) { if (numFoundPoints >= 3) {
for (int j = 0; j < 3; ++j) { for (int j = 0; j < 3; ++j) {
@ -3900,7 +3903,7 @@ void Bot::defuseBomb_ () {
m_strafeSpeed = 0.0f; m_strafeSpeed = 0.0f;
// bot is reloading and we close enough to start defusing // bot is reloading and we close enough to start defusing
if (m_isReloading && (bombPos - pev->origin).length2d () < 80.0f) { if (m_isReloading && bombPos.distance2d (pev->origin) < 80.0f) {
if (m_numEnemiesLeft == 0 || timeToBlowUp < fullDefuseTime + 7.0f || ((getAmmoInClip () > 8 && m_reloadState == Reload::Primary) || (getAmmoInClip () > 5 && m_reloadState == Reload::Secondary))) { if (m_numEnemiesLeft == 0 || timeToBlowUp < fullDefuseTime + 7.0f || ((getAmmoInClip () > 8 && m_reloadState == Reload::Primary) || (getAmmoInClip () > 5 && m_reloadState == Reload::Secondary))) {
int weaponIndex = bestWeaponCarried (); int weaponIndex = bestWeaponCarried ();
@ -3942,8 +3945,8 @@ void Bot::defuseBomb_ () {
botStandOrigin = pev->origin; botStandOrigin = pev->origin;
} }
float duckLength = (m_entity - botDuckOrigin).lengthSq (); float duckLength = m_entity.distanceSq (botDuckOrigin);
float standLength = (m_entity - botStandOrigin).lengthSq (); float standLength = m_entity.distanceSq (botStandOrigin);
if (duckLength > 5625.0f || standLength > 5625.0f) { if (duckLength > 5625.0f || standLength > 5625.0f) {
if (standLength < duckLength) { if (standLength < duckLength) {
@ -4026,7 +4029,7 @@ void Bot::followUser_ () {
m_reloadState = Reload::Primary; m_reloadState = Reload::Primary;
} }
if ((m_targetEntity->v.origin - pev->origin).lengthSq () > cr::square (130.0f)) { if (m_targetEntity->v.origin.distanceSq (pev->origin) > cr::square (130.0f)) {
m_followWaitTime = 0.0f; m_followWaitTime = 0.0f;
} }
else { else {
@ -4106,7 +4109,7 @@ void Bot::throwExplosive_ () {
ignoreCollision (); ignoreCollision ();
if ((pev->origin - dest).lengthSq () < cr::square (400.0f)) { if (pev->origin.distanceSq (dest) < cr::square (400.0f)) {
// heck, I don't wanna blow up myself // heck, I don't wanna blow up myself
m_grenadeCheckTime = game.time () + kGrenadeCheckTime; m_grenadeCheckTime = game.time () + kGrenadeCheckTime;
@ -4173,7 +4176,7 @@ void Bot::throwFlashbang_ () {
ignoreCollision (); ignoreCollision ();
if ((pev->origin - dest).lengthSq () < cr::square (400.0f)) { if (pev->origin.distanceSq (dest) < cr::square (400.0f)) {
m_grenadeCheckTime = game.time () + kGrenadeCheckTime; // heck, I don't wanna blow up myself m_grenadeCheckTime = game.time () + kGrenadeCheckTime; // heck, I don't wanna blow up myself
selectBestWeapon (); selectBestWeapon ();
@ -4380,7 +4383,7 @@ void Bot::escapeFromBomb_ () {
float safeRadius = rg.get (1513.0f, 2048.0f); float safeRadius = rg.get (1513.0f, 2048.0f);
for (int i = 0; i < graph.length (); ++i) { for (int i = 0; i < graph.length (); ++i) {
if ((graph[i].origin - graph.getBombOrigin ()).length () < safeRadius || isOccupiedNode (i)) { if (graph[i].origin.distance (graph.getBombOrigin ()) < safeRadius || isOccupiedNode (i)) {
continue; continue;
} }
int pathDistance = graph.getPathDist (m_currentNodeIndex, i); int pathDistance = graph.getPathDist (m_currentNodeIndex, i);
@ -4457,7 +4460,7 @@ void Bot::pickupItem_ () {
m_entity = dest; m_entity = dest;
// find the distance to the item // find the distance to the item
float itemDistance = (dest - pev->origin).length (); float itemDistance = dest.distance (pev->origin);
switch (m_pickupType) { switch (m_pickupType) {
case Pickup::DroppedC4: case Pickup::DroppedC4:
@ -4730,7 +4733,7 @@ void Bot::logic () {
// see how far bot has moved since the previous position... // see how far bot has moved since the previous position...
if (m_checkTerrain) { if (m_checkTerrain) {
movedDistance = (m_prevOrigin - pev->origin).length (); movedDistance = m_prevOrigin.distance (pev->origin);
} }
// save current position as previous // save current position as previous
@ -5074,7 +5077,7 @@ bool Bot::hasHostage () {
if (!game.isNullEntity (hostage)) { if (!game.isNullEntity (hostage)) {
// don't care about dead hostages // don't care about dead hostages
if (hostage->v.health <= 0.0f || (pev->origin - hostage->v.origin).lengthSq () > cr::square (600.0f)) { if (hostage->v.health <= 0.0f || pev->origin.distanceSq (hostage->v.origin) > cr::square (600.0f)) {
hostage = nullptr; hostage = nullptr;
continue; continue;
} }
@ -5273,7 +5276,7 @@ void Bot::dropWeaponForUser (edict_t *user, bool discardC4) {
// this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop* // this function, asks bot to discard his current primary weapon (or c4) to the user that requsted it with /drop*
// command, very useful, when i'm don't have money to buy anything... ) // command, very useful, when i'm don't have money to buy anything... )
if (util.isAlive (user) && m_moneyAmount >= 2000 && hasPrimaryWeapon () && (user->v.origin - pev->origin).length () <= 450.0f) { if (util.isAlive (user) && m_moneyAmount >= 2000 && hasPrimaryWeapon () && user->v.origin.distance (pev->origin) <= 450.0f) {
m_aimFlags |= AimFlags::Entity; m_aimFlags |= AimFlags::Entity;
m_lookAt = user->v.origin; m_lookAt = user->v.origin;
@ -5506,7 +5509,7 @@ Vector Bot::isBombAudible () {
} }
// we hear bomb if length greater than radius // we hear bomb if length greater than radius
if (desiredRadius < (pev->origin - bombOrigin).length2d ()) { if (desiredRadius < pev->origin.distance2d (bombOrigin)) {
return bombOrigin; return bombOrigin;
} }
return nullptr; return nullptr;
@ -5645,7 +5648,7 @@ bool Bot::isOutOfBombTimer () {
const Vector &bombOrigin = graph.getBombOrigin (); const Vector &bombOrigin = graph.getBombOrigin ();
// for terrorist, if timer is lower than 13 seconds, return true // for terrorist, if timer is lower than 13 seconds, return true
if (timeLeft < 13.0f && m_team == Team::Terrorist && (bombOrigin - pev->origin).lengthSq () < cr::square (964.0f)) { if (timeLeft < 13.0f && m_team == Team::Terrorist && bombOrigin.distanceSq (pev->origin) < cr::square (964.0f)) {
return true; return true;
} }
bool hasTeammatesWithDefuserKit = false; bool hasTeammatesWithDefuserKit = false;
@ -5653,7 +5656,7 @@ bool Bot::isOutOfBombTimer () {
// check if our teammates has defusal kit // check if our teammates has defusal kit
for (const auto &bot : bots) { for (const auto &bot : bots) {
// search players with defuse kit // search players with defuse kit
if (bot.get () != this && bot->m_team == Team::CT && bot->m_hasDefuser && (bombOrigin - bot->pev->origin).lengthSq () < cr::square (512.0f)) { if (bot.get () != this && bot->m_team == Team::CT && bot->m_hasDefuser && bombOrigin.distanceSq (bot->pev->origin) < cr::square (512.0f)) {
hasTeammatesWithDefuserKit = true; hasTeammatesWithDefuserKit = true;
break; break;
} }
@ -5691,7 +5694,7 @@ void Bot::updateHearing () {
if (!game.checkVisibility (client.ent, set)) { if (!game.checkVisibility (client.ent, set)) {
continue; continue;
} }
float distance = (client.noise.pos - pev->origin).length (); float distance = client.noise.pos.distance (pev->origin);
if (distance > client.noise.dist) { if (distance > client.noise.dist) {
continue; continue;
@ -5739,9 +5742,9 @@ void Bot::updateHearing () {
} }
else { else {
// if bot had an enemy but the heard one is nearer, take it instead // if bot had an enemy but the heard one is nearer, take it instead
float distance = (m_lastEnemyOrigin - pev->origin).lengthSq (); float distance = m_lastEnemyOrigin.distanceSq (pev->origin);
if (distance > (player->v.origin - pev->origin).lengthSq () && m_seeEnemyTime + 2.0f < game.time ()) { if (distance > player->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) {
m_lastEnemy = player; m_lastEnemy = player;
m_lastEnemyOrigin = player->v.origin; m_lastEnemyOrigin = player->v.origin;
} }
@ -5810,7 +5813,7 @@ bool Bot::isBombDefusing (const Vector &bombOrigin) {
} }
auto bot = bots[client.ent]; auto bot = bots[client.ent];
auto bombDistance = (client.origin - bombOrigin).lengthSq (); auto bombDistance = client.origin.distanceSq (bombOrigin);
if (bot && !bot->m_notKilled) { if (bot && !bot->m_notKilled) {
if (m_team != bot->m_team || bot->getCurrentTaskId () == Task::EscapeFromBomb) { if (m_team != bot->m_team || bot->getCurrentTaskId () == Task::EscapeFromBomb) {

View file

@ -8,6 +8,7 @@
#include <yapb.h> #include <yapb.h>
ConVar cv_chat ("yb_chat", "1", "Enables or disables bots chat functionality."); ConVar cv_chat ("yb_chat", "1", "Enables or disables bots chat functionality.");
ConVar cv_chat_percent ("yb_chat_percent", "30", "Chances bot will send random dead chat when killed.", true, 0.0f, 100.0f);
void BotSupport::stripTags (String &line) { void BotSupport::stripTags (String &line) {
if (line.empty ()) { if (line.empty ()) {
@ -316,7 +317,7 @@ bool Bot::isReplyingToChat () {
void Bot::checkForChat () { void Bot::checkForChat () {
// say a text every now and then // say a text every now and then
if (rg.chance (30) || m_notKilled || !cv_chat.bool_ ()) { if (rg.chance (cv_chat_percent.int_ ()) || m_notKilled || !cv_chat.bool_ ()) {
return; return;
} }

View file

@ -23,7 +23,7 @@ int Bot::numFriendsNear (const Vector &origin, float radius) {
continue; continue;
} }
if ((client.origin - origin).lengthSq () < cr::square (radius)) { if (client.origin.distanceSq (origin) < cr::square (radius)) {
count++; count++;
} }
} }
@ -38,7 +38,7 @@ int Bot::numEnemiesNear (const Vector &origin, float radius) {
continue; continue;
} }
if ((client.origin - origin).lengthSq () < cr::square (radius)) { if (client.origin.distanceSq (origin) < cr::square (radius)) {
count++; count++;
} }
} }
@ -246,7 +246,7 @@ bool Bot::lookupEnemies () {
player = m_enemy; player = m_enemy;
// is player is alive // is player is alive
if (m_enemyUpdateTime > game.time () && (m_enemy->v.origin - pev->origin).lengthSq () < nearestDistance && util.isAlive (player) && seesEnemy (player)) { if (m_enemyUpdateTime > game.time () && m_enemy->v.origin.distanceSq (pev->origin) < nearestDistance && util.isAlive (player) && seesEnemy (player)) {
newEnemy = player; newEnemy = player;
} }
} }
@ -274,7 +274,7 @@ bool Bot::lookupEnemies () {
if (seesEnemy (intresting)) { if (seesEnemy (intresting)) {
// higher priority for big monsters // higher priority for big monsters
float scaleFactor = (1.0f / calculateScaleFactor (intresting)); float scaleFactor = (1.0f / calculateScaleFactor (intresting));
float distance = (intresting->v.origin - pev->origin).lengthSq () * scaleFactor; float distance = intresting->v.origin.distanceSq (pev->origin) * scaleFactor;
if (distance * 0.7f < nearestDistance) { if (distance * 0.7f < nearestDistance) {
nearestDistance = distance; nearestDistance = distance;
@ -307,7 +307,7 @@ bool Bot::lookupEnemies () {
shieldEnemy = player; shieldEnemy = player;
continue; continue;
} }
float distance = (player->v.origin - pev->origin).lengthSq (); float distance = player->v.origin.distanceSq (pev->origin);
if (distance * 0.7f < nearestDistance) { if (distance * 0.7f < nearestDistance) {
nearestDistance = distance; nearestDistance = distance;
@ -482,7 +482,7 @@ const Vector &Bot::getEnemyBodyOffset () {
if (!m_enemyParts) { if (!m_enemyParts) {
return m_enemyOrigin; return m_enemyOrigin;
} }
float distance = (m_enemy->v.origin - pev->origin).length (); float distance = m_enemy->v.origin.distance (pev->origin);
// do not aim at head, at long distance (only if not using sniper weapon) // do not aim at head, at long distance (only if not using sniper weapon)
if ((m_enemyParts & Visibility::Body) && !usesSniper () && distance > (m_difficulty > Difficulty::Normal ? 2000.0f : 1000.0f)) { if ((m_enemyParts & Visibility::Body) && !usesSniper () && distance > (m_difficulty > Difficulty::Normal ? 2000.0f : 1000.0f)) {
@ -547,9 +547,9 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
{ 0.0f, 0.0f, 0.0f }, // none { 0.0f, 0.0f, 0.0f }, // none
{ 0.0f, 0.0f, 0.0f }, // melee { 0.0f, 0.0f, 0.0f }, // melee
{ 2.5f, 1.5f, 0.2f }, // pistol { 2.5f, 1.5f, 0.2f }, // pistol
{ 6.5f, 2.0f, -9.9f }, // shotgun { 6.5f, 0.0f, -9.9f }, // shotgun
{ 0.5f, -3.5f, -9.0f }, // zoomrifle { 0.5f, -6.5f, -9.0f }, // zoomrifle
{ 0.5f, -3.5f, -9.5f }, // rifle { 0.5f, -6.5f, -9.5f }, // rifle
{ 2.5f, 0.5f, -4.5f }, // smg { 2.5f, 0.5f, -4.5f }, // smg
{ 0.5f, 0.5f, 1.5f }, // sniper { 0.5f, 0.5f, 1.5f }, // sniper
{ 1.5f, -2.0f, -9.0f } // heavy { 1.5f, -2.0f, -9.0f } // heavy
@ -597,7 +597,7 @@ bool Bot::isFriendInLineOfFire (float distance) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team || client.ent == ent ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.team != m_team || client.ent == ent ()) {
continue; continue;
} }
auto friendDistance = (client.ent->v.origin - pev->origin).lengthSq (); auto friendDistance = client.ent->v.origin.distanceSq (pev->origin);
if (friendDistance <= distance && util.getShootingCone (ent (), client.ent->v.origin) > friendDistance / (friendDistance + 1089.0f)) { if (friendDistance <= distance && util.getShootingCone (ent (), client.ent->v.origin) > friendDistance / (friendDistance + 1089.0f)) {
return true; return true;
@ -632,14 +632,14 @@ bool Bot::isPenetrableObstacle (const Vector &dest) {
game.testLine (dest, source, TraceIgnore::Monsters, ent (), &tr); game.testLine (dest, source, TraceIgnore::Monsters, ent (), &tr);
if (!cr::fequal (tr.flFraction, 1.0f)) { if (!cr::fequal (tr.flFraction, 1.0f)) {
if ((tr.vecEndPos - dest).lengthSq () > cr::square (800.0f)) { if (tr.vecEndPos.distanceSq (dest) > cr::square (800.0f)) {
return false; return false;
} }
if (tr.vecEndPos.z >= dest.z + 200.0f) { if (tr.vecEndPos.z >= dest.z + 200.0f) {
return false; return false;
} }
obstacleDistance = (tr.vecEndPos - source).lengthSq (); obstacleDistance = tr.vecEndPos.distanceSq (source);
} }
} }
const float distance = cr::square (75.0f); const float distance = cr::square (75.0f);
@ -690,7 +690,7 @@ bool Bot::isPenetrableObstacle2 (const Vector &dest) {
} }
if (numHits < 3 && thikness < 98) { if (numHits < 3 && thikness < 98) {
if ((dest - point).lengthSq () < 13143.0f) { if (dest.distanceSq (point) < 13143.0f) {
return true; return true;
} }
} }
@ -900,7 +900,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
void Bot::fireWeapons () { void Bot::fireWeapons () {
// this function will return true if weapon was fired, false otherwise // this function will return true if weapon was fired, false otherwise
float distance = (m_lookAt - getEyesPos ()).length (); // how far away is the enemy? float distance = m_lookAt.distance (getEyesPos ()); // how far away is the enemy?
// or if friend in line of fire, stop this too but do not update shoot time // or if friend in line of fire, stop this too but do not update shoot time
if (!game.isNullEntity (m_enemy)) { if (!game.isNullEntity (m_enemy)) {
@ -1020,7 +1020,7 @@ void Bot::focusEnemy () {
if (m_enemySurpriseTime > game.time ()) { if (m_enemySurpriseTime > game.time ()) {
return; return;
} }
float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum? float distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum?
if (distance < 128.0f && !usesSniper ()) { if (distance < 128.0f && !usesSniper ()) {
if (usesKnife ()) { if (usesKnife ()) {
@ -1065,7 +1065,7 @@ void Bot::attackMovement () {
if (game.isNullEntity (m_enemy)) { if (game.isNullEntity (m_enemy)) {
return; return;
} }
float distance = (m_lookAt - getEyesPos ()).length2d (); // how far away is the enemy scum? float distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum?
if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.time ()) { if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.time ()) {
int approach; int approach;
@ -1570,7 +1570,7 @@ bool Bot::isGroupOfEnemies (const Vector &location, int numEnemies, float radius
continue; continue;
} }
if ((client.ent->v.origin - location).lengthSq () < cr::square (radius)) { if (client.ent->v.origin.distanceSq (location) < cr::square (radius)) {
// don't target our teammates... // don't target our teammates...
if (client.team == m_team) { if (client.team == m_team) {
return false; return false;

View file

@ -1351,7 +1351,7 @@ int BotControl::menuGraphType (int item) {
break; break;
case 8: case 8:
graph.add (100); graph.add (NodeAddFlag::Goal);
showMenu (Menu::NodeType); showMenu (Menu::NodeType);
break; break;

View file

@ -386,6 +386,15 @@ int BotGraph::clearConnections (int index) {
return numFixedLinks; return numFixedLinks;
} }
int BotGraph::getBspSize () {
MemFile file (strings.format ("maps/%s.bsp", game.getMapName ()));
if (file) {
return static_cast <int> (file.length ());
}
return 0;
}
void BotGraph::addPath (int addIndex, int pathIndex, float distance) { void BotGraph::addPath (int addIndex, int pathIndex, float distance) {
if (!exists (addIndex) || !exists (pathIndex)) { if (!exists (addIndex) || !exists (pathIndex)) {
return; return;
@ -437,7 +446,7 @@ int BotGraph::getFarest (const Vector &origin, float maxDistance) {
maxDistance = cr::square (maxDistance); maxDistance = cr::square (maxDistance);
for (const auto &path : m_paths) { for (const auto &path : m_paths) {
float distance = (path.origin - origin).lengthSq (); float distance = path.origin.distanceSq (origin);
if (distance > maxDistance) { if (distance > maxDistance) {
index = path.number; index = path.number;
@ -458,7 +467,7 @@ int BotGraph::getNearestNoBuckets (const Vector &origin, float minDistance, int
if (flags != -1 && !(path.flags & flags)) { if (flags != -1 && !(path.flags & flags)) {
continue; // if flag not -1 and node has no this flag, skip node continue; // if flag not -1 and node has no this flag, skip node
} }
float distance = (path.origin - origin).lengthSq (); float distance = path.origin.distanceSq (origin);
if (distance < minDistance) { if (distance < minDistance) {
index = path.number; index = path.number;
@ -491,7 +500,7 @@ int BotGraph::getNearest (const Vector &origin, float minDistance, int flags) {
if (flags != -1 && !(m_paths[at].flags & flags)) { if (flags != -1 && !(m_paths[at].flags & flags)) {
continue; // if flag not -1 and node has no this flag, skip node continue; // if flag not -1 and node has no this flag, skip node
} }
float distance = (m_paths[at].origin - origin).lengthSq (); float distance = origin.distanceSq (m_paths[at].origin);
if (distance < minDistanceSq) { if (distance < minDistanceSq) {
index = at; index = at;
@ -523,7 +532,7 @@ IntArray BotGraph::searchRadius (float radius, const Vector &origin, int maxCoun
break; break;
} }
if ((m_paths[at].origin - origin).lengthSq () < radius) { if (origin.distanceSq (m_paths[at].origin) < radius) {
result.push (at); result.push (at);
} }
} }
@ -548,7 +557,7 @@ void BotGraph::add (int type, const Vector &pos) {
m_hasChanged = true; m_hasChanged = true;
switch (type) { switch (type) {
case 5: case NodeAddFlag::Camp:
index = getEditorNeareset (); index = getEditorNeareset ();
if (index != kInvalidNodeIndex) { if (index != kInvalidNodeIndex) {
@ -564,7 +573,7 @@ void BotGraph::add (int type, const Vector &pos) {
} }
break; break;
case 6: case NodeAddFlag::CampEnd:
index = getEditorNeareset (); index = getEditorNeareset ();
if (index != kInvalidNodeIndex) { if (index != kInvalidNodeIndex) {
@ -581,11 +590,11 @@ void BotGraph::add (int type, const Vector &pos) {
} }
return; return;
case 9: case NodeAddFlag::JumpStart:
index = getEditorNeareset (); index = getEditorNeareset ();
if (index != kInvalidNodeIndex && m_paths[index].number >= 0) { if (index != kInvalidNodeIndex && m_paths[index].number >= 0) {
float distance = (m_paths[index].origin - m_editor->v.origin).length (); float distance = m_editor->v.origin.distance (m_paths[index].origin);
if (distance < 50.0f) { if (distance < 50.0f) {
addNewNode = false; addNewNode = false;
@ -599,11 +608,11 @@ void BotGraph::add (int type, const Vector &pos) {
} }
break; break;
case 10: case NodeAddFlag::JumpEnd:
index = getEditorNeareset (); index = getEditorNeareset ();
if (index != kInvalidNodeIndex && m_paths[index].number >= 0) { if (index != kInvalidNodeIndex && m_paths[index].number >= 0) {
float distance = (m_paths[index].origin - m_editor->v.origin).length (); float distance = m_editor->v.origin.distance (m_paths[index].origin);
if (distance < 50.0f) { if (distance < 50.0f) {
addNewNode = false; addNewNode = false;
@ -627,7 +636,7 @@ void BotGraph::add (int type, const Vector &pos) {
auto nearest = getEditorNeareset (); auto nearest = getEditorNeareset ();
// do not allow to place waypoints "inside" waypoints, make at leat 10 units range // do not allow to place waypoints "inside" waypoints, make at leat 10 units range
if (exists (nearest) && (m_paths[nearest].origin - newOrigin).lengthSq () < cr::square (10.0f)) { if (exists (nearest) && newOrigin.distanceSq (m_paths[nearest].origin) < cr::square (10.0f)) {
ctrl.msg ("Can't add node. It's way to near to %d node. Please move some units anywhere.", nearest); ctrl.msg ("Can't add node. It's way to near to %d node. Please move some units anywhere.", nearest);
return; return;
} }
@ -666,11 +675,11 @@ void BotGraph::add (int type, const Vector &pos) {
m_lastNode = m_editor->v.origin; m_lastNode = m_editor->v.origin;
} }
if (type == 9) { if (type == NodeAddFlag::JumpStart) {
m_lastJumpNode = index; m_lastJumpNode = index;
} }
else if (type == 10) { else if (type == NodeAddFlag::JumpEnd) {
float distance = (m_paths[m_lastJumpNode].origin - m_editor->v.origin).length (); float distance = m_paths[m_lastJumpNode].origin.distance (m_editor->v.origin);
addPath (m_lastJumpNode, index, distance); addPath (m_lastJumpNode, index, distance);
for (auto &link : m_paths[m_lastJumpNode].links) { for (auto &link : m_paths[m_lastJumpNode].links) {
@ -701,32 +710,32 @@ void BotGraph::add (int type, const Vector &pos) {
} }
switch (type) { switch (type) {
case 1: case NodeAddFlag::TOnly:
path->flags |= NodeFlag::Crossing; path->flags |= NodeFlag::Crossing;
path->flags |= NodeFlag::TerroristOnly; path->flags |= NodeFlag::TerroristOnly;
break; break;
case 2: case NodeAddFlag::CTOnly:
path->flags |= NodeFlag::Crossing; path->flags |= NodeFlag::Crossing;
path->flags |= NodeFlag::CTOnly; path->flags |= NodeFlag::CTOnly;
break; break;
case 3: case NodeAddFlag::NoHostage:
path->flags |= NodeFlag::NoHostage; path->flags |= NodeFlag::NoHostage;
break; break;
case 4: case NodeAddFlag::Rescue:
path->flags |= NodeFlag::Rescue; path->flags |= NodeFlag::Rescue;
break; break;
case 5: case NodeAddFlag::Camp:
path->flags |= NodeFlag::Crossing; path->flags |= NodeFlag::Crossing;
path->flags |= NodeFlag::Camp; path->flags |= NodeFlag::Camp;
path->start = m_editor->v.v_angle; path->start = m_editor->v.v_angle;
break; break;
case 100: case NodeAddFlag::Goal:
path->flags |= NodeFlag::Goal; path->flags |= NodeFlag::Goal;
break; break;
} }
@ -750,7 +759,7 @@ void BotGraph::add (int type, const Vector &pos) {
game.testLine (newOrigin, calc.origin, TraceIgnore::Monsters, m_editor, &tr); game.testLine (newOrigin, calc.origin, TraceIgnore::Monsters, m_editor, &tr);
if (cr::fequal (tr.flFraction, 1.0f) && cr::abs (newOrigin.x - calc.origin.x) < 64.0f && cr::abs (newOrigin.y - calc.origin.y) < 64.0f && cr::abs (newOrigin.z - calc.origin.z) < m_autoPathDistance) { if (cr::fequal (tr.flFraction, 1.0f) && cr::abs (newOrigin.x - calc.origin.x) < 64.0f && cr::abs (newOrigin.y - calc.origin.y) < 64.0f && cr::abs (newOrigin.z - calc.origin.z) < m_autoPathDistance) {
float distance = (calc.origin - newOrigin).length (); float distance = newOrigin.distance (calc.origin);
addPath (index, calc.number, distance); addPath (index, calc.number, distance);
addPath (calc.number, index, distance); addPath (calc.number, index, distance);
@ -759,7 +768,7 @@ void BotGraph::add (int type, const Vector &pos) {
else { else {
// check if the node is reachable from the new one // check if the node is reachable from the new one
if (isNodeReacheable (newOrigin, calc.origin) || isNodeReacheable (calc.origin, newOrigin)) { if (isNodeReacheable (newOrigin, calc.origin) || isNodeReacheable (calc.origin, newOrigin)) {
float distance = (calc.origin - newOrigin).length (); float distance = newOrigin.distance (calc.origin);
if (distance < minDistance) { if (distance < minDistance) {
destIndex = calc.number; destIndex = calc.number;
@ -772,12 +781,12 @@ void BotGraph::add (int type, const Vector &pos) {
if (exists (destIndex)) { if (exists (destIndex)) {
// check if the node is reachable from the new one (one-way) // check if the node is reachable from the new one (one-way)
if (isNodeReacheable (newOrigin, m_paths[destIndex].origin)) { if (isNodeReacheable (newOrigin, m_paths[destIndex].origin)) {
addPath (index, destIndex, (m_paths[destIndex].origin - newOrigin).length ()); addPath (index, destIndex, newOrigin.distance (m_paths[destIndex].origin));
} }
// check if the new one is reachable from the node (other way) // check if the new one is reachable from the node (other way)
if (isNodeReacheable (m_paths[destIndex].origin, newOrigin)) { if (isNodeReacheable (m_paths[destIndex].origin, newOrigin)) {
addPath (destIndex, index, (m_paths[destIndex].origin - newOrigin).length ()); addPath (destIndex, index, newOrigin.distance (m_paths[destIndex].origin));
} }
} }
} }
@ -790,12 +799,12 @@ void BotGraph::add (int type, const Vector &pos) {
// check if the node is reachable from the new one (one-way) // check if the node is reachable from the new one (one-way)
if (isNodeReacheable (newOrigin, calc.origin)) { if (isNodeReacheable (newOrigin, calc.origin)) {
addPath (index, calc.number, (calc.origin - newOrigin).length ()); addPath (index, calc.number, calc.origin.distance (newOrigin));
} }
// check if the new one is reachable from the node (other way) // check if the new one is reachable from the node (other way)
if (isNodeReacheable (calc.origin, newOrigin)) { if (isNodeReacheable (calc.origin, newOrigin)) {
addPath (calc.number, index, (calc.origin - newOrigin).length ()); addPath (calc.number, index, calc.origin.distance (newOrigin));
} }
} }
clearConnections (index); clearConnections (index);
@ -975,7 +984,7 @@ void BotGraph::pathCreate (char dir) {
return; return;
} }
float distance = (m_paths[nodeTo].origin - m_paths[nodeFrom].origin).length (); float distance = m_paths[nodeFrom].origin.distance (m_paths[nodeTo].origin);
if (dir == PathConnection::Outgoing) { if (dir == PathConnection::Outgoing) {
addPath (nodeFrom, nodeTo, distance); addPath (nodeFrom, nodeTo, distance);
@ -1787,7 +1796,7 @@ bool BotGraph::loadGraphData () {
loadPractice (); loadPractice ();
if (exten.mapSize > 0) { if (exten.mapSize > 0) {
int mapSize = engfuncs.pfnGetFileSize (strings.format ("maps/%s.bsp", game.getMapName ())); int mapSize = getBspSize ();
if (mapSize != exten.mapSize) { if (mapSize != exten.mapSize) {
ctrl.msg ("Warning: Graph data is probably not for this map. Please check bots behaviour."); ctrl.msg ("Warning: Graph data is probably not for this map. Please check bots behaviour.");
@ -1829,7 +1838,7 @@ bool BotGraph::saveGraphData () {
ExtenHeader exten {}; ExtenHeader exten {};
strings.copy (exten.author, author.chars (), cr::bufsize (exten.author)); strings.copy (exten.author, author.chars (), cr::bufsize (exten.author));
exten.mapSize = engfuncs.pfnGetFileSize (strings.format ("maps/%s.bsp", game.getMapName ())); exten.mapSize = getBspSize ();
// ensure narrow places saved into file // ensure narrow places saved into file
m_narrowChecked = false; m_narrowChecked = false;
@ -1883,13 +1892,13 @@ const char *BotGraph::getOldFormatGraphName (bool isMemoryFile) {
float BotGraph::calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin) { float BotGraph::calculateTravelTime (float maxSpeed, const Vector &src, const Vector &origin) {
// this function returns 2D traveltime to a position // this function returns 2D traveltime to a position
return (origin - src).length2d () / maxSpeed; return origin.distance2d (src) / maxSpeed;
} }
bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) { bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) {
TraceResult tr {}; TraceResult tr {};
float distance = (destination - src).length (); float distance = destination.distance (src);
// is the destination not close enough? // is the destination not close enough?
if (distance > m_autoPathDistance) { if (distance > m_autoPathDistance) {
@ -1945,7 +1954,7 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) {
game.testLine (check, down, TraceIgnore::Monsters, m_editor, &tr); game.testLine (check, down, TraceIgnore::Monsters, m_editor, &tr);
float lastHeight = tr.flFraction * 1000.0f; // height from ground float lastHeight = tr.flFraction * 1000.0f; // height from ground
distance = (destination - check).length (); // distance from goal distance = destination.distance (check); // distance from goal
while (distance > 10.0f) { while (distance > 10.0f) {
// move 10 units closer to the goal... // move 10 units closer to the goal...
@ -1963,7 +1972,7 @@ bool BotGraph::isNodeReacheable (const Vector &src, const Vector &destination) {
return false; // can't get there without jumping... return false; // can't get there without jumping...
} }
lastHeight = height; lastHeight = height;
distance = (destination - check).length (); // distance from goal distance = destination.distance (check); // distance from goal
} }
return true; return true;
} }
@ -2115,7 +2124,7 @@ void BotGraph::frame () {
if (m_jumpLearnNode) { if (m_jumpLearnNode) {
if (!m_endJumpPoint) { if (!m_endJumpPoint) {
if (m_editor->v.button & IN_JUMP) { if (m_editor->v.button & IN_JUMP) {
add (9); add (NodeAddFlag::JumpStart);
m_timeJumpStarted = game.time (); m_timeJumpStarted = game.time ();
m_endJumpPoint = true; m_endJumpPoint = true;
@ -2126,7 +2135,7 @@ void BotGraph::frame () {
} }
} }
else if (((m_editor->v.flags & FL_ONGROUND) || m_editor->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1f < game.time () && m_endJumpPoint) { else if (((m_editor->v.flags & FL_ONGROUND) || m_editor->v.movetype == MOVETYPE_FLY) && m_timeJumpStarted + 0.1f < game.time () && m_endJumpPoint) {
add (10); add (NodeAddFlag::JumpEnd);
m_jumpLearnNode = false; m_jumpLearnNode = false;
m_endJumpPoint = false; m_endJumpPoint = false;
@ -2136,13 +2145,13 @@ void BotGraph::frame () {
// check if it's a auto-add-node mode enabled // check if it's a auto-add-node mode enabled
if (hasEditFlag (GraphEdit::Auto) && (m_editor->v.flags & (FL_ONGROUND | FL_PARTIALGROUND))) { if (hasEditFlag (GraphEdit::Auto) && (m_editor->v.flags & (FL_ONGROUND | FL_PARTIALGROUND))) {
// find the distance from the last used node // find the distance from the last used node
float distance = (m_lastNode - m_editor->v.origin).lengthSq (); float distance = m_lastNode.distanceSq (m_editor->v.origin);
if (distance > cr::square (128.0f)) { if (distance > cr::square (128.0f)) {
// check that no other reachable nodes are nearby... // check that no other reachable nodes are nearby...
for (const auto &path : m_paths) { for (const auto &path : m_paths) {
if (isNodeReacheable (m_editor->v.origin, path.origin)) { if (isNodeReacheable (m_editor->v.origin, path.origin)) {
distance = (path.origin - m_editor->v.origin).lengthSq (); distance = path.origin.distanceSq (m_editor->v.origin);
if (distance < nearestDistance) { if (distance < nearestDistance) {
nearestDistance = distance; nearestDistance = distance;
@ -2152,7 +2161,7 @@ void BotGraph::frame () {
// make sure nearest node is far enough away... // make sure nearest node is far enough away...
if (nearestDistance >= cr::square (128.0f)) { if (nearestDistance >= cr::square (128.0f)) {
add (GraphAdd::Normal); // place a node here add (NodeAddFlag::Normal); // place a node here
} }
} }
} }
@ -2163,7 +2172,7 @@ void BotGraph::frame () {
// now iterate through all nodes in a map, and draw required ones // now iterate through all nodes in a map, and draw required ones
for (auto &path : m_paths) { for (auto &path : m_paths) {
float distance = (path.origin - m_editor->v.origin).length (); float distance = path.origin.distance (m_editor->v.origin);
// check if node is whitin a distance, and is visible // check if node is whitin a distance, and is visible
if (distance < 512.0f && ((util.isVisible (path.origin, m_editor) && util.isInViewCone (path.origin, m_editor)) || !util.isAlive (m_editor) || distance < 128.0f)) { if (distance < 512.0f && ((util.isVisible (path.origin, m_editor) && util.isInViewCone (path.origin, m_editor)) || !util.isAlive (m_editor) || distance < 128.0f)) {
@ -2705,7 +2714,7 @@ void BotGraph::addBasic () {
do { do {
if (getNearestNoBuckets (point, 50.0f) == kInvalidNodeIndex) { if (getNearestNoBuckets (point, 50.0f) == kInvalidNodeIndex) {
add (3, point); add (NodeAddFlag::NoHostage, point);
} }
point.z += 160; point.z += 160;
} while (point.z < down.z - 40.0f); } while (point.z < down.z - 40.0f);
@ -2713,7 +2722,7 @@ void BotGraph::addBasic () {
point = down + Vector (0.0f, 0.0f, 38.0f); point = down + Vector (0.0f, 0.0f, 38.0f);
if (getNearestNoBuckets (point, 50.0f) == kInvalidNodeIndex) { if (getNearestNoBuckets (point, 50.0f) == kInvalidNodeIndex) {
add (3, point); add (NodeAddFlag::NoHostage, point);
} }
m_isOnLadder = false; m_isOnLadder = false;
@ -2731,31 +2740,30 @@ void BotGraph::addBasic () {
}); });
}; };
autoCreateForEntity (0, "info_player_deathmatch"); // then terrortist spawnpoints autoCreateForEntity (NodeAddFlag::Normal, "info_player_deathmatch"); // then terrortist spawnpoints
autoCreateForEntity (0, "info_player_start"); // then add ct spawnpoints autoCreateForEntity (NodeAddFlag::Normal, "info_player_start"); // then add ct spawnpoints
autoCreateForEntity (0, "info_vip_start"); // then vip spawnpoint autoCreateForEntity (NodeAddFlag::Normal, "info_vip_start"); // then vip spawnpoint
autoCreateForEntity (0, "armoury_entity"); // weapons on the map ? autoCreateForEntity (NodeAddFlag::Normal, "armoury_entity"); // weapons on the map ?
autoCreateForEntity (4, "func_hostage_rescue"); // hostage rescue zone autoCreateForEntity (NodeAddFlag::Rescue, "func_hostage_rescue"); // hostage rescue zone
autoCreateForEntity (4, "info_hostage_rescue"); // hostage rescue zone (same as above) autoCreateForEntity (NodeAddFlag::Rescue, "info_hostage_rescue"); // hostage rescue zone (same as above)
autoCreateForEntity (100, "func_bomb_target"); // bombspot zone autoCreateForEntity (NodeAddFlag::Goal, "func_bomb_target"); // bombspot zone
autoCreateForEntity (100, "info_bomb_target"); // bombspot zone (same as above) autoCreateForEntity (NodeAddFlag::Goal, "info_bomb_target"); // bombspot zone (same as above)
autoCreateForEntity (100, "hostage_entity"); // hostage entities autoCreateForEntity (NodeAddFlag::Goal, "hostage_entity"); // hostage entities
autoCreateForEntity (100, "func_vip_safetyzone"); // vip rescue (safety) zone autoCreateForEntity (NodeAddFlag::Goal, "func_vip_safetyzone"); // vip rescue (safety) zone
autoCreateForEntity (100, "func_escapezone"); // terrorist escape zone autoCreateForEntity (NodeAddFlag::Goal, "func_escapezone"); // terrorist escape zone
} }
void BotGraph::eraseFromDisk () { void BotGraph::eraseFromDisk () {
// this function removes graph file from the hard disk // this function removes graph file from the hard disk
StringArray forErase; StringArray forErase;
bots.kickEveryone (true);
auto map = game.getMapName (); auto map = game.getMapName ();
auto data = getDataDirectory (); auto data = getDataDirectory ();
bots.kickEveryone (true);
// if we're delete graph, delete all corresponding to it files // if we're delete graph, delete all corresponding to it files
forErase.push (strings.format ("%spwf/%s.pwf", data, map)); // graph itself forErase.push (strings.format ("%spwf/%s.pwf", data, map)); // graph itself
forErase.push (strings.format ("%strain/%s.prc", data, map)); // corresponding to practice forErase.push (strings.format ("%strain/%s.prc", data, map)); // corresponding to practice

View file

@ -12,7 +12,7 @@ ConVar cv_autovacate ("yb_autovacate", "1", "Kick bots to automatically make roo
ConVar cv_quota ("yb_quota", "9", "Specifies the number bots to be added to the game.", true, 0.0f, static_cast <float> (kGameMaxPlayers)); ConVar cv_quota ("yb_quota", "9", "Specifies the number bots to be added to the game.", true, 0.0f, static_cast <float> (kGameMaxPlayers));
ConVar cv_quota_mode ("yb_quota_mode", "normal", "Specifies the type of quota.\nAllowed values: 'normal', 'fill', and 'match'.\nIf 'fill', the server will adjust bots to keep N players in the game, where N is yb_quota.\nIf 'match', the server will maintain a 1:N ratio of humans to bots, where N is yb_quota_match.", false); ConVar cv_quota_mode ("yb_quota_mode", "normal", "Specifies the type of quota.\nAllowed values: 'normal', 'fill', and 'match'.\nIf 'fill', the server will adjust bots to keep N players in the game, where N is yb_quota.\nIf 'match', the server will maintain a 1:N ratio of humans to bots, where N is yb_quota_match.", false);
ConVar cv_quota_match ("yb_quota_match", "0", "Number of players to match if yb_quota_mode set to 'match'", true, 0.0f, static_cast <float> (kGameMaxPlayers)); ConVar cv_quota_match ("yb_quota_match", "0", "Number of players to match if yb_quota_mode set to 'match'", true, 0.0f, static_cast <float> (kGameMaxPlayers));
ConVar cv_think_fps ("yb_think_fps", "30.0", "Specifies how many times per second bot code will run.", true, 30.0f, 90.0f); ConVar cv_think_fps ("yb_think_fps", "26.0", "Specifies how many times per second bot code will run.", true, 24.0f, 90.0f);
ConVar cv_autokill_delay ("yb_autokill_delay", "0.0", "Specifies amount of time in seconds when bots will be killed if no humans left alive.", true, 0.0f, 90.0f); ConVar cv_autokill_delay ("yb_autokill_delay", "0.0", "Specifies amount of time in seconds when bots will be killed if no humans left alive.", true, 0.0f, 90.0f);
ConVar cv_join_after_player ("yb_join_after_player", "0", "Specifies whether bots should join server, only when at least one human player in game."); ConVar cv_join_after_player ("yb_join_after_player", "0", "Specifies whether bots should join server, only when at least one human player in game.");

View file

@ -8,7 +8,10 @@
#include <yapb.h> #include <yapb.h>
ConVar cv_whose_your_daddy ("yb_whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots."); ConVar cv_whose_your_daddy ("yb_whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots.");
ConVar cv_debug_heuristic_type ("yb_debug_heuristic_type", "0", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f); ConVar cv_path_heuristic_mode ("yb_path_heuristic_mode", "4", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f);
ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bound of danger factor that used to add additional danger to path based on practice.", true, 100.0f, 2400.0f);
ConVar cv_path_danger_factor_max ("yb_path_danger_factor_max", "400", "Upper bound of danger factor that used to add additional danger to path based on practice.", true, 200.0f, 4800.0f);
int Bot::findBestGoal () { int Bot::findBestGoal () {
@ -180,7 +183,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
int count = 0; int count = 0;
for (auto &point : graph.m_goalPoints) { for (auto &point : graph.m_goalPoints) {
float distance = (graph[point].origin - pev->origin).lengthSq (); float distance = graph[point].origin.distanceSq (pev->origin);
if (distance > cr::square (1024.0f)) { if (distance > cr::square (1024.0f)) {
continue; continue;
@ -295,6 +298,10 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
edict_t *hindrance = nullptr; edict_t *hindrance = nullptr;
float distance = cr::square (300.0f); float distance = cr::square (300.0f);
if (getCurrentTaskId () == Task::Attack || getCurrentTaskId () == Task::SeekCover || isOnLadder ()) {
return false;
}
// find nearest player to bot // find nearest player to bot
for (const auto &client : util.getClients ()) { for (const auto &client : util.getClients ()) {
@ -312,7 +319,7 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
if (client.team != m_team || client.ent == ent ()) { if (client.team != m_team || client.ent == ent ()) {
continue; continue;
} }
auto nearest = (client.ent->v.origin - pev->origin).lengthSq (); auto nearest = client.ent->v.origin.distanceSq (pev->origin);
if (nearest < cr::square (pev->maxspeed) && nearest < distance) { if (nearest < cr::square (pev->maxspeed) && nearest < distance) {
hindrance = client.ent; hindrance = client.ent;
@ -324,7 +331,7 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
if (!hindrance) { if (!hindrance) {
return false; return false;
} }
const float interval = getFrameInterval (); const float interval = getFrameInterval () * 4.0f;
// use our movement angles, try to predict where we should be next frame // use our movement angles, try to predict where we should be next frame
Vector right, forward; Vector right, forward;
@ -335,8 +342,8 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
predict += right * m_strafeSpeed * interval; predict += right * m_strafeSpeed * interval;
predict += pev->velocity * interval; predict += pev->velocity * interval;
auto movedDistance = (hindrance->v.origin - predict).lengthSq (); auto movedDistance = hindrance->v.origin.distanceSq (predict);
auto nextFrameDistance = ((hindrance->v.origin + hindrance->v.velocity * interval) - pev->origin).lengthSq (); auto nextFrameDistance = pev->origin.distanceSq (hindrance->v.origin + hindrance->v.velocity * interval);
// is player that near now or in future that we need to steer away? // is player that near now or in future that we need to steer away?
if (movedDistance <= cr::square (48.0f) || (distance <= cr::square (56.0f) && nextFrameDistance < distance)) { if (movedDistance <= cr::square (48.0f) || (distance <= cr::square (56.0f) && nextFrameDistance < distance)) {
@ -349,13 +356,11 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
else { else {
setStrafeSpeed (normal, -pev->maxspeed); setStrafeSpeed (normal, -pev->maxspeed);
} }
#if 0
if (distance < cr::square (56.0f)) { if (distance < cr::square (56.0f)) {
if ((dir | forward.get2d ()) < 0.0f) if ((dir | forward.get2d ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed; m_moveSpeed = -pev->maxspeed;
} }
#endif }
return true; return true;
} }
return false; return false;
@ -365,9 +370,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
// if avoiding someone do not consider stuck // if avoiding someone do not consider stuck
TraceResult tr {}; TraceResult tr {};
doPlayerAvoidance (dirNormal); m_isStuck = doPlayerAvoidance (dirNormal);
m_isStuck = false; if (m_isStuck) {
resetCollision ();
return;
}
// Standing still, no need to check? // Standing still, no need to check?
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way // FIXME: doesn't care for ladder movement (handled separately) should be included in some way
@ -655,7 +663,7 @@ bool Bot::updateNavigation () {
} }
m_destOrigin = m_pathOrigin + pev->view_ofs; m_destOrigin = m_pathOrigin + pev->view_ofs;
float nodeDistance = (pev->origin - m_pathOrigin).length (); float nodeDistance = pev->origin.distance (m_pathOrigin);
// this node has additional travel flags - care about them // this node has additional travel flags - care about them
if (m_currentTravelFlags & PathFlag::Jump) { if (m_currentTravelFlags & PathFlag::Jump) {
@ -714,7 +722,7 @@ bool Bot::updateNavigation () {
if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) { if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && strncmp (tr.pHit->v.classname.chars (), "func_door", 9) == 0) {
// if the door is near enough... // if the door is near enough...
if ((game.getEntityOrigin (tr.pHit) - pev->origin).lengthSq () < 2500.0f) { if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < 2500.0f) {
ignoreCollision (); // don't consider being stuck ignoreCollision (); // don't consider being stuck
if (rg.chance (50)) { if (rg.chance (50)) {
@ -795,7 +803,7 @@ bool Bot::updateNavigation () {
} }
// needs precise placement - check if we get past the point // needs precise placement - check if we get past the point
if (desiredDistance < 22.0f && nodeDistance < 30.0f && (pev->origin + (pev->velocity * getFrameInterval ()) - m_pathOrigin).lengthSq () >= cr::square (nodeDistance)) { if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * getFrameInterval ()) >= cr::square (nodeDistance)) {
desiredDistance = nodeDistance + 1.0f; desiredDistance = nodeDistance + 1.0f;
} }
@ -837,7 +845,7 @@ bool Bot::updateNavigation () {
// bot within 'hearable' bomb tick noises? // bot within 'hearable' bomb tick noises?
if (!bombOrigin.empty ()) { if (!bombOrigin.empty ()) {
float distance = (bombOrigin - graph[taskTarget].origin).length (); float distance = bombOrigin.distance (graph[taskTarget].origin);
if (distance > 512.0f) { if (distance > 512.0f) {
if (rg.chance (50) && !graph.isVisited (taskTarget)) { if (rg.chance (50) && !graph.isVisited (taskTarget)) {
@ -930,7 +938,7 @@ bool Bot::updateLiftHandling () {
m_destOrigin = m_liftTravelPos; m_destOrigin = m_liftTravelPos;
// check if we enough to destination // check if we enough to destination
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
// need to wait our following teammate ? // need to wait our following teammate ?
@ -984,7 +992,7 @@ bool Bot::updateLiftHandling () {
if (needWaitForTeammate) { if (needWaitForTeammate) {
m_destOrigin = m_liftTravelPos; m_destOrigin = m_liftTravelPos;
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
} }
} }
@ -1015,7 +1023,7 @@ bool Bot::updateLiftHandling () {
m_liftState = LiftState::TravelingBy; m_liftState = LiftState::TravelingBy;
m_liftUsageTime = game.time () + 14.0f; m_liftUsageTime = game.time () + 14.0f;
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
} }
} }
@ -1025,7 +1033,7 @@ bool Bot::updateLiftHandling () {
if (m_liftState == LiftState::TravelingBy) { if (m_liftState == LiftState::TravelingBy) {
m_destOrigin = Vector (m_liftTravelPos.x, m_liftTravelPos.y, pev->origin.z); m_destOrigin = Vector (m_liftTravelPos.x, m_liftTravelPos.y, pev->origin.z);
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
} }
} }
@ -1042,7 +1050,7 @@ bool Bot::updateLiftHandling () {
m_destOrigin = pev->origin; m_destOrigin = pev->origin;
} }
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
} }
} }
@ -1075,7 +1083,7 @@ bool Bot::updateLiftHandling () {
m_destOrigin = button->v.origin; m_destOrigin = button->v.origin;
} }
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
} }
} }
@ -1106,7 +1114,7 @@ bool Bot::updateLiftHandling () {
} }
} }
if ((pev->origin - m_destOrigin).lengthSq () < cr::square (20.0f)) { if (pev->origin.distanceSq (m_destOrigin) < cr::square (20.0f)) {
wait (); wait ();
} }
} }
@ -1204,8 +1212,12 @@ void Bot::findShortestPath (int srcIndex, int destIndex) {
void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath::Fast */) { void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath::Fast */) {
// this function finds a path from srcIndex to destIndex // this function finds a path from srcIndex to destIndex
auto dangerFactor = [&] () -> float {
return rg.get (cv_path_danger_factor_min.float_ (), cv_path_danger_factor_max.float_ ()) * 2.0f / cr::clamp (m_difficulty, 1, 3);
};
// least kills and number of nodes to goal for a team // least kills and number of nodes to goal for a team
auto gfunctionKillsDist = [] (int team, int currentIndex, int parentIndex) -> float { auto gfunctionKillsDist = [&dangerFactor] (int team, int currentIndex, int parentIndex) -> float {
if (parentIndex == kInvalidNodeIndex) { if (parentIndex == kInvalidNodeIndex) {
return 0.0f; return 0.0f;
} }
@ -1221,7 +1233,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
if (current.flags & NodeFlag::Crouch) { if (current.flags & NodeFlag::Crouch) {
cost *= 1.5f; cost *= 1.5f;
} }
return cost; return cost + dangerFactor ();
}; };
// least kills and number of nodes to goal for a team // least kills and number of nodes to goal for a team
@ -1238,7 +1250,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
}; };
// least kills to goal for a team // least kills to goal for a team
auto gfunctionKills = [] (int team, int currentIndex, int) -> float { auto gfunctionKills = [&dangerFactor] (int team, int currentIndex, int) -> float {
auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex)); auto cost = static_cast <float> (graph.getDangerDamage (team, currentIndex, currentIndex));
const auto &current = graph[currentIndex]; const auto &current = graph[currentIndex];
@ -1251,7 +1263,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
if (current.flags & NodeFlag::Crouch) { if (current.flags & NodeFlag::Crouch) {
cost *= 1.5f; cost *= 1.5f;
} }
return cost + 0.5f; return cost + dangerFactor () + 1.0f;
}; };
// least kills to goal for a team // least kills to goal for a team
@ -1310,7 +1322,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
float y = cr::abs (start.origin.y - goal.origin.y); float y = cr::abs (start.origin.y - goal.origin.y);
float z = cr::abs (start.origin.z - goal.origin.z); float z = cr::abs (start.origin.z - goal.origin.z);
switch (cv_debug_heuristic_type.int_ ()) { switch (cv_path_heuristic_mode.int_ ()) {
case 0: case 0:
default: default:
return cr::max (cr::max (x, y), z); // chebyshev distance return cr::max (cr::max (x, y), z); // chebyshev distance
@ -1326,7 +1338,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
// euclidean based distance // euclidean based distance
float euclidean = cr::powf (cr::powf (x, 2.0f) + cr::powf (y, 2.0f) + cr::powf (z, 2.0f), 0.5f); 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) { if (cv_path_heuristic_mode.int_ () == 4) {
return 1000.0f * (cr::ceilf (euclidean) - euclidean); return 1000.0f * (cr::ceilf (euclidean) - euclidean);
} }
return euclidean; return euclidean;
@ -1343,7 +1355,7 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
// none heuristic // none heuristic
auto hfunctionNone = [&hfunctionPathDist] (int index, int startIndex, int goalIndex) -> float { auto hfunctionNone = [&hfunctionPathDist] (int index, int startIndex, int goalIndex) -> float {
return hfunctionPathDist (index, startIndex, goalIndex) / 128.0f * 10.0f; return hfunctionPathDist (index, startIndex, goalIndex) / (128.0f * 10.0f);
}; };
if (!graph.exists (srcIndex)) { if (!graph.exists (srcIndex)) {
@ -1578,7 +1590,7 @@ bool Bot::findBestNearestNode () {
} }
// if we're still here, find some close nodes // if we're still here, find some close nodes
float distance = (pev->origin - graph[at].origin).lengthSq (); float distance = pev->origin.distanceSq (graph[at].origin);
if (distance < lessDist[0]) { if (distance < lessDist[0]) {
lessDist[2] = lessDist[1]; lessDist[2] = lessDist[1];
@ -1650,7 +1662,7 @@ float Bot::getReachTime () {
// calculate 'real' time that we need to get from one node to another // calculate 'real' time that we need to get from one node to another
if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) { if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) {
float distance = (graph[m_previousNodes[0]].origin - graph[m_currentNodeIndex].origin).length (); float distance = graph[m_previousNodes[0]].origin.distance (graph[m_currentNodeIndex].origin);
// caclulate estimated time // caclulate estimated time
if (pev->maxspeed <= 0.0f) { if (pev->maxspeed <= 0.0f) {
@ -1735,7 +1747,7 @@ int Bot::findNearestNode () {
if (at == m_currentNodeIndex) { if (at == m_currentNodeIndex) {
continue; continue;
} }
float distance = (graph[at].origin - pev->origin).lengthSq (); float distance = graph[at].origin.distanceSq (pev->origin);
if (distance < minimum) { if (distance < minimum) {
@ -1763,7 +1775,7 @@ int Bot::findBombNode () {
const auto &audible = isBombAudible (); const auto &audible = isBombAudible ();
// take the nearest to bomb nodes instead of goal if close enough // take the nearest to bomb nodes instead of goal if close enough
if ((pev->origin - bomb).lengthSq () < cr::square (96.0f)) { if (pev->origin.distanceSq (bomb) < cr::square (96.0f)) {
int node = graph.getNearest (bomb, 420.0f); int node = graph.getNearest (bomb, 420.0f);
m_bombSearchOverridden = true; m_bombSearchOverridden = true;
@ -1786,7 +1798,7 @@ int Bot::findBombNode () {
// find nearest goal node either to bomb (if "heard" or player) // find nearest goal node either to bomb (if "heard" or player)
for (auto &point : goals) { for (auto &point : goals) {
float distance = (graph[point].origin - bomb).lengthSq (); float distance = bomb.distanceSq (graph[point].origin);
// check if we got more close distance // check if we got more close distance
if (distance < lastDistance) { if (distance < lastDistance) {
@ -1888,7 +1900,7 @@ int Bot::findDefendNode (const Vector &origin) {
IntArray found; IntArray found;
for (int i = 0; i < graph.length (); ++i) { for (int i = 0; i < graph.length (); ++i) {
if ((graph[i].origin - origin).lengthSq () <= cr::square (1248.0f) && !graph.isVisible (i, posIndex) && !isOccupiedNode (i)) { if (origin.distanceSq (graph[i].origin) <= cr::square (1248.0f) && !graph.isVisible (i, posIndex) && !isOccupiedNode (i)) {
found.push (i); found.push (i);
} }
} }
@ -1911,7 +1923,7 @@ int Bot::findDefendNode (const Vector &origin) {
int Bot::findCoverNode (float maxDistance) { int Bot::findCoverNode (float maxDistance) {
// this function tries to find a good cover node if bot wants to hide // this function tries to find a good cover node if bot wants to hide
const float enemyMax = (m_lastEnemyOrigin - pev->origin).length (); const float enemyMax = m_lastEnemyOrigin.distance (pev->origin);
// do not move to a position near to the enemy // do not move to a position near to the enemy
if (maxDistance > enemyMax) { if (maxDistance > enemyMax) {
@ -2172,7 +2184,7 @@ bool Bot::advanceMovement () {
src = path.origin; src = path.origin;
dst = next.origin; dst = next.origin;
jumpDistance = (path.origin - next.origin).length (); jumpDistance = path.origin.distance (next.origin);
willJump = true; willJump = true;
break; break;
@ -2661,7 +2673,7 @@ bool Bot::isDeadlyMove (const Vector &to) {
} }
float lastHeight = tr.flFraction * 1000.0f; // height from ground float lastHeight = tr.flFraction * 1000.0f; // height from ground
float distance = (to - check).length (); // distance from goal float distance = to.distance (check); // distance from goal
if (distance <= 30.0f && lastHeight > 150.0f) { if (distance <= 30.0f && lastHeight > 150.0f) {
return true; return true;
@ -2686,7 +2698,7 @@ bool Bot::isDeadlyMove (const Vector &to) {
return true; return true;
} }
lastHeight = height; lastHeight = height;
distance = (to - check).length (); // distance from goal distance = to.distance (check); // distance from goal
} }
return false; return false;
} }
@ -2774,13 +2786,13 @@ int Bot::findCampingDirection () {
if (count < 3) { if (count < 3) {
indices[count] = i; indices[count] = i;
distTab[count] = (pev->origin - path.origin).lengthSq (); distTab[count] = pev->origin.distanceSq (path.origin);
visibility[count] = path.vis.crouch + path.vis.stand; visibility[count] = path.vis.crouch + path.vis.stand;
++count; ++count;
} }
else { else {
float distance = (pev->origin - path.origin).lengthSq (); float distance = pev->origin.distanceSq (path.origin);
uint16 visBits = path.vis.crouch + path.vis.stand; uint16 visBits = path.vis.crouch + path.vis.stand;
for (int j = 0; j < 3; ++j) { for (int j = 0; j < 3; ++j) {
@ -3005,14 +3017,14 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
} }
// do not check clients far away from us // do not check clients far away from us
if ((pev->origin - client.origin).lengthSq () > cr::square (320.0f)) { if (pev->origin.distanceSq (client.origin) > cr::square (320.0f)) {
continue; continue;
} }
if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) { if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) {
continue; continue;
} }
auto length = (graph[index].origin - client.origin).lengthSq (); auto length = client.origin.distanceSq (graph[index].origin);
if (length < cr::clamp (cr::square (graph[index].radius), cr::square (60.0f), cr::square (90.0f))) { if (length < cr::clamp (cr::square (graph[index].radius), cr::square (60.0f), cr::square (90.0f))) {
return true; return true;
@ -3049,7 +3061,7 @@ edict_t *Bot::lookupButton (const char *target) {
// check if this place safe // check if this place safe
if (!isDeadlyMove (pos)) { if (!isDeadlyMove (pos)) {
float distance = (pev->origin - pos).lengthSq (); float distance = pev->origin.distanceSq (pos);
// check if we got more close button // check if we got more close button
if (distance <= nearest) { if (distance <= nearest) {
@ -3073,7 +3085,7 @@ bool Bot::isReachableNode (int index) {
const Vector &dst = graph[index].origin; const Vector &dst = graph[index].origin;
// is the destination close enough? // is the destination close enough?
if ((dst - src).lengthSq () >= cr::square (320.0f)) { if (dst.distanceSq (src) >= cr::square (320.0f)) {
return false; return false;
} }
@ -3081,7 +3093,7 @@ bool Bot::isReachableNode (int index) {
if (isOccupiedNode (index, true)) { if (isOccupiedNode (index, true)) {
return true; return true;
} }
float ladderDist = (dst - src).length2d (); float ladderDist = dst.distance2d (src);
TraceResult tr {}; TraceResult tr {};
game.testLine (src, dst, TraceIgnore::Monsters, ent (), &tr); game.testLine (src, dst, TraceIgnore::Monsters, ent (), &tr);
@ -3112,6 +3124,9 @@ bool Bot::isReachableNode (int index) {
} }
bool Bot::isBannedNode (int index) { bool Bot::isBannedNode (int index) {
if (graph.exists (cv_debug_goal.int_ ())) {
return false;
}
for (const auto &node : m_goalHistory) { for (const auto &node : m_goalHistory) {
if (node == index) { if (node == index) {
return true; return true;

View file

@ -347,7 +347,7 @@ bool BotSupport::findNearestPlayer (void **pvHolder, edict_t *to, float searchDi
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & ClientFlags::Alive)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & Weapon::C4))) { if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & ClientFlags::Alive)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & Weapon::C4))) {
continue; // filter players with parameters continue; // filter players with parameters
} }
float distance = (client.ent->v.origin - to->v.origin).length (); float distance = client.ent->v.origin.distance (to->v.origin);
if (distance < nearestPlayer && distance < searchDistance) { if (distance < nearestPlayer && distance < searchDistance) {
nearestPlayer = distance; nearestPlayer = distance;
@ -398,7 +398,7 @@ void BotSupport::listenNoise (edict_t *ent, StringRef sample, float volume) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) {
continue; continue;
} }
auto distance = (client.origin - origin).lengthSq (); auto distance = client.origin.distanceSq (origin);
// now find nearest player // now find nearest player
if (distance < nearest) { if (distance < nearest) {