aim: do not look for danger places when no enemies left

combat: increase  new enemy update timers
combat: reduce penetrate level for last enemies detection
nav: allow camp nodes to have desired reachability distance
fix: mgr: addhs (high skilled) bot do nothing
Co-Authored-By: Max <161382234+dyspose@users.noreply.github.com>
This commit is contained in:
jeefo 2024-04-17 23:40:37 +03:00
commit ffa882a295
No known key found for this signature in database
GPG key ID: 927BCA0779BEA8ED
4 changed files with 198 additions and 195 deletions

View file

@ -271,7 +271,7 @@ bool Bot::lookupEnemies () {
m_states |= Sense::SuspectEnemy; m_states |= Sense::SuspectEnemy;
// check if last enemy can be penetrated // check if last enemy can be penetrated
const auto penetratePower = conf.findWeaponById (m_currentWeapon).penetratePower * 4; const auto penetratePower = conf.findWeaponById (m_currentWeapon).penetratePower * 3;
if (isPenetrableObstacle3 (m_lastEnemyOrigin, penetratePower)) { if (isPenetrableObstacle3 (m_lastEnemyOrigin, penetratePower)) {
m_aimFlags |= AimFlags::LastEnemy; m_aimFlags |= AimFlags::LastEnemy;
@ -360,7 +360,7 @@ bool Bot::lookupEnemies () {
} }
const float distanceSq = player->v.origin.distanceSq (pev->origin); const float distanceSq = player->v.origin.distanceSq (pev->origin);
if (distanceSq * 0.7f < nearestDistanceSq) { if (distanceSq < nearestDistanceSq) {
nearestDistanceSq = distanceSq; nearestDistanceSq = distanceSq;
newEnemy = player; newEnemy = player;
@ -371,7 +371,7 @@ bool Bot::lookupEnemies () {
} }
} }
} }
m_enemyUpdateTime = game.time () + (usesKnife () ? 1.0f : 0.5f); m_enemyUpdateTime = game.time () + (usesKnife () ? 1.25f : 0.85f);
if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) { if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) {
newEnemy = shieldEnemy; newEnemy = shieldEnemy;

View file

@ -33,7 +33,7 @@ int BotControl::cmdAddBot () {
} }
// if high-skilled bot is requested set personality to rusher and max-out difficulty // if high-skilled bot is requested set personality to rusher and max-out difficulty
if (strValue (alias).endsWith ("_hs")) { if (strValue (alias).contains ("addhs")) {
m_args.set (difficulty, "4"); m_args.set (difficulty, "4");
m_args.set (personality, "1"); m_args.set (personality, "1");
} }

View file

@ -1155,6 +1155,9 @@ bool Bot::updateNavigation () {
else if (m_pathFlags & NodeFlag::Ladder) { else if (m_pathFlags & NodeFlag::Ladder) {
desiredDistanceSq = cr::sqrf (16.0f); desiredDistanceSq = cr::sqrf (16.0f);
} }
else if (m_pathFlags & NodeFlag::Camp) {
desiredDistanceSq = cr::sqrf (32.0f);
}
else if (m_currentTravelFlags & PathFlag::Jump) { else if (m_currentTravelFlags & PathFlag::Jump) {
desiredDistanceSq = 0.0f; desiredDistanceSq = 0.0f;
} }

View file

@ -59,197 +59,6 @@ bool Bot::seesEntity (const Vector &dest, bool fromBody) {
return tr.flFraction >= 1.0f; return tr.flFraction >= 1.0f;
} }
void Bot::setAimDirection () {
uint32_t flags = m_aimFlags;
// don't allow bot to look at danger positions under certain circumstances
if (!(flags & (AimFlags::Grenade | AimFlags::Enemy | AimFlags::Entity))) {
// check if narrow place and we're duck, do not predict enemies in that situation
const bool duckedInNarrowPlace = isInNarrowPlace () && ((m_pathFlags & NodeFlag::Crouch) || (pev->button & IN_DUCK));
if (duckedInNarrowPlace || isOnLadder () || isInWater () || (m_pathFlags & NodeFlag::Ladder) || (m_currentTravelFlags & PathFlag::Jump)) {
flags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath);
m_canChooseAimDirection = false;
}
}
if (flags & AimFlags::Override) {
m_lookAt = m_lookAtSafe;
}
else if (flags & AimFlags::Grenade) {
m_lookAt = m_throw;
const float throwDistance = m_throw.distance (pev->origin);
float coordCorrection = 0.0f;
if (throwDistance > 100.0f && throwDistance < 800.0f) {
coordCorrection = 0.25f * (m_throw.z - pev->origin.z);
}
else if (throwDistance >= 800.0f) {
float angleCorrection = 37.0f * (throwDistance - 800.0f) / 800.0f;
if (angleCorrection > 45.0f) {
angleCorrection = 45.0f;
}
coordCorrection = throwDistance * cr::tanf (cr::deg2rad (angleCorrection)) + 0.25f * (m_throw.z - pev->origin.z);
}
m_lookAt.z += coordCorrection * 0.5f;
}
else if (flags & AimFlags::Enemy) {
focusEnemy ();
}
else if (flags & AimFlags::Entity) {
m_lookAt = m_entity;
// do not look at hostages legs
if (m_pickupType == Pickup::Hostage) {
m_lookAt.z += 48.0f;
}
else if (m_pickupType == Pickup::Weapon) {
m_lookAt.z += 72.0f;
}
}
else if (flags & AimFlags::LastEnemy) {
m_lookAt = m_lastEnemyOrigin;
// did bot just see enemy and is quite aggressive?
if (m_seeEnemyTime + 2.0f - m_actualReactionTime + m_baseAgressionLevel > game.time ()) {
// feel free to fire if shootable
if (!usesSniper () && lastEnemyShootable ()) {
m_wantsToFire = true;
}
}
}
else if (flags & AimFlags::PredictPath) {
bool changePredictedEnemy = true;
if (m_timeNextTracking < game.time () && m_trackingEdict == m_lastEnemy && util.isAlive (m_lastEnemy)) {
changePredictedEnemy = false;
}
auto doFailPredict = [this] () -> void {
if (m_timeNextTracking > game.time ()) {
return; // do not fail instantly
}
m_aimFlags &= ~AimFlags::PredictPath;
m_trackingEdict = nullptr;
m_lookAtPredict = nullptr;
};
auto pathLength = m_lastPredictLength;
auto predictNode = m_lastPredictIndex;
auto isPredictedIndexApplicable = [&] () -> bool {
if (!vistab.visible (m_currentNodeIndex, predictNode) || !vistab.visible (m_previousNodes[0], predictNode)) {
predictNode = kInvalidNodeIndex;
pathLength = kInfiniteDistanceLong;
}
return graph.exists (predictNode) && pathLength < cv_max_nodes_for_predict.int_ ();
};
if (changePredictedEnemy) {
if (isPredictedIndexApplicable ()) {
m_lookAtPredict = graph[predictNode].origin;
m_timeNextTracking = game.time () + rg.get (0.5f, 1.0f);
m_trackingEdict = m_lastEnemy;
}
else {
doFailPredict ();
}
}
else {
if (!isPredictedIndexApplicable ()) {
doFailPredict ();
}
}
if (!m_lookAtPredict.empty ()) {
m_lookAt = m_lookAtPredict;
}
}
else if (flags & AimFlags::Camp) {
m_lookAt = m_lookAtSafe;
}
else if (flags & AimFlags::Nav) {
const auto &destOrigin = m_destOrigin + pev->view_ofs;
m_lookAt = destOrigin;
if (m_moveToGoal && m_seeEnemyTime + 4.0f < game.time ()
&& !m_isStuck && !(pev->button & IN_DUCK)
&& m_currentNodeIndex != kInvalidNodeIndex
&& !(m_pathFlags & (NodeFlag::Ladder | NodeFlag::Crouch))
&& m_pathWalk.hasNext () && !isOnLadder ()
&& pev->origin.distanceSq (destOrigin) < cr::sqrf (512.0f)) {
const auto nextPathIndex = m_pathWalk.next ();
const auto doubleNextPath = m_pathWalk.doubleNext ();
if (vistab.visible (m_currentNodeIndex, doubleNextPath)) {
const auto &gn = graph[doubleNextPath];
m_lookAt = gn.origin + pev->view_ofs;
}
else if (vistab.visible (m_currentNodeIndex, nextPathIndex)) {
const auto &gn = graph[nextPathIndex];
m_lookAt = gn.origin + pev->view_ofs;
}
else {
m_lookAt = pev->origin + pev->view_ofs + pev->v_angle.forward () * 300.0f;
}
}
else {
m_lookAt = destOrigin;
}
const bool horizontalMovement = (m_pathFlags & NodeFlag::Ladder) || isOnLadder ();
if (m_canChooseAimDirection && m_seeEnemyTime + 4.0f < game.time () && m_currentNodeIndex != kInvalidNodeIndex && !horizontalMovement) {
const auto dangerIndex = practice.getIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
if (graph.exists (dangerIndex)
&& vistab.visible (m_currentNodeIndex, dangerIndex)
&& !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::sqrf (512.0f)) {
m_lookAt = destOrigin;
}
else {
m_lookAt = graph[dangerIndex].origin + pev->view_ofs;
// add danger flags
m_aimFlags |= AimFlags::Danger;
}
}
}
// try look at next node if on ladder
if (horizontalMovement && m_pathWalk.hasNext ()) {
const auto &nextPath = graph[m_pathWalk.next ()];
if ((nextPath.flags & NodeFlag::Ladder) && m_destOrigin.distanceSq (pev->origin) < cr::sqrf (128.0f) && nextPath.origin.z > m_pathOrigin.z + 26.0f) {
m_lookAt = nextPath.origin + pev->view_ofs;
}
}
// don't look at bottom of node, if reached it
if (m_lookAt == destOrigin && !horizontalMovement) {
m_lookAt.z = getEyesPos ().z;
}
// try to look at last victim for a little, maybe there's some one else
if (game.isNullEntity (m_enemy) && m_difficulty >= Difficulty::Normal && !m_forgetLastVictimTimer.elapsed () && !m_lastVictimOrigin.empty ()) {
m_lookAt = m_lastVictimOrigin + pev->view_ofs;
}
}
if (m_lookAt.empty ()) {
m_lookAt = m_destOrigin;
}
}
void Bot::checkDarkness () { void Bot::checkDarkness () {
// do not check for darkness at the start of the round // do not check for darkness at the start of the round
@ -602,3 +411,194 @@ bool Frustum::check (const Planes &planes, edict_t *ent) const {
} }
return true; return true;
} }
void Bot::setAimDirection () {
uint32_t flags = m_aimFlags;
// don't allow bot to look at danger positions under certain circumstances
if (!(flags & (AimFlags::Grenade | AimFlags::Enemy | AimFlags::Entity))) {
// check if narrow place and we're duck, do not predict enemies in that situation
const bool duckedInNarrowPlace = isInNarrowPlace () && ((m_pathFlags & NodeFlag::Crouch) || (pev->button & IN_DUCK));
if (duckedInNarrowPlace || isOnLadder () || isInWater () || (m_pathFlags & NodeFlag::Ladder) || (m_currentTravelFlags & PathFlag::Jump)) {
flags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath);
m_canChooseAimDirection = false;
}
}
if (flags & AimFlags::Override) {
m_lookAt = m_lookAtSafe;
}
else if (flags & AimFlags::Grenade) {
m_lookAt = m_throw;
const float throwDistance = m_throw.distance (pev->origin);
float coordCorrection = 0.0f;
if (throwDistance > 100.0f && throwDistance < 800.0f) {
coordCorrection = 0.25f * (m_throw.z - pev->origin.z);
}
else if (throwDistance >= 800.0f) {
float angleCorrection = 37.0f * (throwDistance - 800.0f) / 800.0f;
if (angleCorrection > 45.0f) {
angleCorrection = 45.0f;
}
coordCorrection = throwDistance * cr::tanf (cr::deg2rad (angleCorrection)) + 0.25f * (m_throw.z - pev->origin.z);
}
m_lookAt.z += coordCorrection * 0.5f;
}
else if (flags & AimFlags::Enemy) {
focusEnemy ();
}
else if (flags & AimFlags::Entity) {
m_lookAt = m_entity;
// do not look at hostages legs
if (m_pickupType == Pickup::Hostage) {
m_lookAt.z += 48.0f;
}
else if (m_pickupType == Pickup::Weapon) {
m_lookAt.z += 72.0f;
}
}
else if (flags & AimFlags::LastEnemy) {
m_lookAt = m_lastEnemyOrigin;
// did bot just see enemy and is quite aggressive?
if (m_seeEnemyTime + 2.0f - m_actualReactionTime + m_baseAgressionLevel > game.time ()) {
// feel free to fire if shootable
if (!usesSniper () && lastEnemyShootable ()) {
m_wantsToFire = true;
}
}
}
else if (flags & AimFlags::PredictPath) {
bool changePredictedEnemy = true;
if (m_timeNextTracking < game.time () && m_trackingEdict == m_lastEnemy && util.isAlive (m_lastEnemy)) {
changePredictedEnemy = false;
}
auto doFailPredict = [this] () -> void {
if (m_timeNextTracking > game.time ()) {
return; // do not fail instantly
}
m_aimFlags &= ~AimFlags::PredictPath;
m_trackingEdict = nullptr;
m_lookAtPredict = nullptr;
};
auto pathLength = m_lastPredictLength;
auto predictNode = m_lastPredictIndex;
auto isPredictedIndexApplicable = [&] () -> bool {
if (!vistab.visible (m_currentNodeIndex, predictNode) || !vistab.visible (m_previousNodes[0], predictNode)) {
predictNode = kInvalidNodeIndex;
pathLength = kInfiniteDistanceLong;
}
return graph.exists (predictNode) && pathLength < cv_max_nodes_for_predict.int_ ();
};
if (changePredictedEnemy) {
if (isPredictedIndexApplicable ()) {
m_lookAtPredict = graph[predictNode].origin;
m_timeNextTracking = game.time () + rg.get (0.5f, 1.0f);
m_trackingEdict = m_lastEnemy;
}
else {
doFailPredict ();
}
}
else {
if (!isPredictedIndexApplicable ()) {
doFailPredict ();
}
}
if (!m_lookAtPredict.empty ()) {
m_lookAt = m_lookAtPredict;
}
}
else if (flags & AimFlags::Camp) {
m_lookAt = m_lookAtSafe;
}
else if (flags & AimFlags::Nav) {
const auto &destOrigin = m_destOrigin + pev->view_ofs;
m_lookAt = destOrigin;
if (m_moveToGoal && m_seeEnemyTime + 4.0f < game.time ()
&& !m_isStuck && !(pev->button & IN_DUCK)
&& m_currentNodeIndex != kInvalidNodeIndex
&& !(m_pathFlags & (NodeFlag::Ladder | NodeFlag::Crouch))
&& m_pathWalk.hasNext () && !isOnLadder ()
&& pev->origin.distanceSq (destOrigin) < cr::sqrf (512.0f)) {
const auto nextPathIndex = m_pathWalk.next ();
const auto doubleNextPath = m_pathWalk.doubleNext ();
if (vistab.visible (m_currentNodeIndex, doubleNextPath)) {
const auto &gn = graph[doubleNextPath];
m_lookAt = gn.origin + pev->view_ofs;
}
else if (vistab.visible (m_currentNodeIndex, nextPathIndex)) {
const auto &gn = graph[nextPathIndex];
m_lookAt = gn.origin + pev->view_ofs;
}
else {
m_lookAt = pev->origin + pev->view_ofs + pev->v_angle.forward () * 300.0f;
}
}
else {
m_lookAt = destOrigin;
}
const bool horizontalMovement = (m_pathFlags & NodeFlag::Ladder) || isOnLadder ();
if (m_numEnemiesLeft > 0 && m_canChooseAimDirection && m_seeEnemyTime + 4.0f < game.time () && m_currentNodeIndex != kInvalidNodeIndex && !horizontalMovement) {
const auto dangerIndex = practice.getIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
if (graph.exists (dangerIndex)
&& vistab.visible (m_currentNodeIndex, dangerIndex)
&& !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::sqrf (512.0f)) {
m_lookAt = destOrigin;
}
else {
m_lookAt = graph[dangerIndex].origin + pev->view_ofs;
// add danger flags
m_aimFlags |= AimFlags::Danger;
}
}
}
// try look at next node if on ladder
if (horizontalMovement && m_pathWalk.hasNext ()) {
const auto &nextPath = graph[m_pathWalk.next ()];
if ((nextPath.flags & NodeFlag::Ladder) && m_destOrigin.distanceSq (pev->origin) < cr::sqrf (128.0f) && nextPath.origin.z > m_pathOrigin.z + 26.0f) {
m_lookAt = nextPath.origin + pev->view_ofs;
}
}
// don't look at bottom of node, if reached it
if (m_lookAt == destOrigin && !horizontalMovement) {
m_lookAt.z = getEyesPos ().z;
}
// try to look at last victim for a little, maybe there's some one else
if (game.isNullEntity (m_enemy) && m_difficulty >= Difficulty::Normal && !m_forgetLastVictimTimer.elapsed () && !m_lastVictimOrigin.empty ()) {
m_lookAt = m_lastVictimOrigin + pev->view_ofs;
}
}
if (m_lookAt.empty ()) {
m_lookAt = m_destOrigin;
}
}