diff --git a/src/botlib.cpp b/src/botlib.cpp index 99cdd19..0e7e5f6 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -1566,8 +1566,10 @@ void Bot::updateEmotions () { } void Bot::overrideConditions () { + const auto tid = getCurrentTaskId (); + // check if we need to escape from bomb - if (game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_isAlive && getCurrentTaskId () != Task::EscapeFromBomb && getCurrentTaskId () != Task::Camp && isOutOfBombTimer ()) { + if (game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_isAlive && tid != Task::EscapeFromBomb && tid != Task::Camp && isOutOfBombTimer ()) { completeTask (); // complete current task // then start escape from bomb immediate @@ -1583,11 +1585,11 @@ void Bot::overrideConditions () { const int nearestToEnemyPoint = graph.getNearest (m_enemy->v.origin); if (nearestToEnemyPoint != kInvalidNodeIndex && nearestToEnemyPoint != m_currentNodeIndex && cr::abs (graph[nearestToEnemyPoint].origin.z - m_enemy->v.origin.z) < 16.0f) { - if (getCurrentTaskId () != Task::MoveToPosition && !cr::fequal (getTask ()->desire, TaskPri::Hide)) { + if (tid != Task::MoveToPosition && !cr::fequal (getTask ()->desire, TaskPri::Hide)) { startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + length / (m_moveSpeed * 2.0f), true); } else { - if (getCurrentTaskId () == Task::MoveToPosition && getTask ()->data != nearestToEnemyPoint) { + if (tid == Task::MoveToPosition && getTask ()->data != nearestToEnemyPoint) { clearTask (Task::MoveToPosition); startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + length / (m_moveSpeed * 2.0f), true); } @@ -1595,14 +1597,14 @@ void Bot::overrideConditions () { } } else { - if (length <= 250.0f && (m_states & Sense::SeeingEnemy) && getCurrentTaskId () == Task::MoveToPosition) { + if (length <= 250.0f && (m_states & Sense::SeeingEnemy) && tid == Task::MoveToPosition) { clearTask (Task::MoveToPosition); // remove any move tasks } } } // special handling for sniping - if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) && m_sniperStopTime > game.time () && getCurrentTaskId () != Task::SeekCover) { + if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) && m_sniperStopTime > game.time () && tid != Task::SeekCover) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; @@ -1610,7 +1612,7 @@ void Bot::overrideConditions () { } // special handling for reloading - if (!bots.isRoundOver () && getCurrentTaskId () == Task::Normal && m_reloadState != Reload::None && m_isReloading && !isDucking () && !isInNarrowPlace ()) { + if (!bots.isRoundOver () && tid == Task::Normal && m_reloadState != Reload::None && m_isReloading && !isDucking () && !isInNarrowPlace ()) { if (m_reloadState != Reload::None || m_isReloading) { const auto maxClip = conf.findWeaponById (m_currentWeapon).maxClip; const auto curClip = getAmmoInClip (); @@ -1689,7 +1691,7 @@ void Bot::refreshEnemyPredict () { if (game.isNullEntity (m_enemy) && !game.isNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.empty ()) { const auto distanceToLastEnemySq = m_lastEnemyOrigin.distanceSq (pev->origin); - if (distanceToLastEnemySq > cr::sqrf (128.0f) && (distanceToLastEnemySq < cr::sqrf (2048.0f) || usesSniper ())) { + if (distanceToLastEnemySq > cr::sqrf (256.0f) && (distanceToLastEnemySq < cr::sqrf (2048.0f) || usesSniper ())) { m_aimFlags |= AimFlags::PredictPath; } const bool denyLastEnemy = pev->velocity.lengthSq2d () > 0.0f && distanceToLastEnemySq < cr::sqrf (256.0f) && m_shootTime + 2.5f > game.time (); @@ -1921,8 +1923,8 @@ void Bot::filterTasks () { else if (m_isVIP || m_isReloading || (sniping && usesSniper ())) { ratio *= 3.0f; // triple the seek cover desire if bot is VIP or reloading } - else if (m_lastEnemyOrigin.distance2d (pev->origin) < 200.0f) { - ratio *= 5.0f; + else if (m_lastEnemyOrigin.distanceSq2d (pev->origin) < cr::sqrf (200.0f)) { + ratio *= 3.0f; } else if (m_isCreature) { ratio = 0.0f; @@ -3208,12 +3210,12 @@ void Bot::showDebugOverlay () { StringRef debugData = strings.format ( "\n\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\n" "Item: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\n" - "SP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\n" + "SP=%.02f SSP=%.02f I=%d CG=%d PG=%d G=%d T: %.02f MT: %d\n" "Enemy=%s Pickup=%s Type=%s Terrain=%s Stuck=%s\n", pev->netname.str (), m_healthValue, pev->armorvalue, tid, tasks[tid], getTask ()->desire, weapon, getAmmoInClip (), getAmmo (), m_isReloading ? " (R)" : "", m_moneyAmount, aimFlags.trim (), - m_moveSpeed, m_strafeSpeed, index, m_prevGoalIndex, goal, m_navTimeset - game.time (), + m_moveSpeed, m_strafeSpeed, index, m_chosenGoalIndex, m_prevGoalIndex, goal, m_navTimeset - game.time (), pev->movetype, enemy, pickup, personalities[m_personality], boolValue (m_checkTerrain), boolValue (m_isStuck)); diff --git a/src/combat.cpp b/src/combat.cpp index 27e4572..d177fd8 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -504,7 +504,7 @@ Vector Bot::getBodyOffsetError (float distance) { return m_aimLastError; } - Vector Bot::getEnemyBodyOffset () { +Vector Bot::getEnemyBodyOffset () { // the purpose of this function, is to make bot aiming not so ideal. it's mutate m_enemyOrigin enemy vector // returned from visibility check function. @@ -586,7 +586,7 @@ Vector Bot::getCustomHeight (float distance) { Long, Middle, Short }; - constexpr float offsetRanges[9][3] = { + constexpr float kOffsetRanges[9][3] = { { 0.0f, 0.0f, 0.0f }, // none { 0.0f, 0.0f, 0.0f }, // melee { 0.5f, -0.1f, -1.5f }, // pistol @@ -613,7 +613,7 @@ Vector Bot::getCustomHeight (float distance) { else if (distance > kSprayDistance && distance <= kDoubleSprayDistance) { distanceIndex = DistanceIndex::Middle; } - return { 0.0f, 0.0f, offsetRanges[m_weaponType][distanceIndex] }; + return { 0.0f, 0.0f, kOffsetRanges[m_weaponType][distanceIndex] }; } bool Bot::isFriendInLineOfFire (float distance) { @@ -711,7 +711,9 @@ bool Bot::isPenetrableObstacle (const Vector &dest) { bool Bot::isPenetrableObstacle2 (const Vector &dest) { // this function returns if enemy can be shoot through some obstacle - if (m_isUsingGrenade || m_difficulty < Difficulty::Normal || !conf.findWeaponById (m_currentWeapon).penetratePower) { + auto power = conf.findWeaponById (m_currentWeapon).penetratePower; + + if (m_isUsingGrenade || m_difficulty < Difficulty::Normal || !power) { return false; } @@ -1357,6 +1359,9 @@ void Bot::attackMovement () { pev->button |= IN_JUMP; } } + else { + m_duckTime = game.time () + m_frameInterval * 2.0f; + } } if (m_isReloading) { diff --git a/src/navigate.cpp b/src/navigate.cpp index 5ad3dc2..5b9a6c1 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -320,6 +320,15 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) { if (goalChoices[0] == kInvalidNodeIndex) { return m_chosenGoalIndex = graph.random (); } + + // rusher bots does not care any danger (idea from pbmm) + if (m_personality == Personality::Rusher) { + const auto randomGoal = goalChoices[rg.get (0, 3)]; + + if (graph.exists (randomGoal)) { + return m_chosenGoalIndex = randomGoal; + } + } bool sorting = false; do { @@ -344,7 +353,7 @@ void Bot::postprocessGoals (const IntArray &goals, int result[]) { int recurseCount = 0; - auto isRecentOrHistorical = [&] (int index) -> bool { + auto isRecentOrHistorical = [&] (int index) -> bool { if (m_prevGoalIndex == index || m_previousNodes[0] == index) { return true; } @@ -609,7 +618,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { dirLeft = true; } const auto &testDir = m_moveSpeed > 0.0f ? forward : -forward; - constexpr float kBlockDistance = 32.0f; + constexpr float kBlockDistance = 42.0f; // now check which side is blocked src = pev->origin + right * kBlockDistance; @@ -787,44 +796,12 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) { void Bot::moveToGoal () { findValidNode (); - bool prevLadder = false; - - if (graph.exists (m_previousNodes[0])) { - if (graph[m_previousNodes[0]].flags & NodeFlag::Ladder) { - prevLadder = true; - } - } - // press duck button if we need to if ((m_pathFlags & NodeFlag::Crouch) && !(m_pathFlags & (NodeFlag::Camp | NodeFlag::Goal))) { pev->button |= IN_DUCK; } m_lastUsedNodesTime = game.time (); - // press jump button if we need to leave the ladder - if (!(m_pathFlags & NodeFlag::Ladder) && prevLadder && isOnFloor () && isOnLadder () && m_moveSpeed > 50.0f && pev->velocity.length () < 50.0f) { - pev->button |= IN_JUMP; - m_jumpTime = game.time () + 1.0f; - } - - if (m_pathFlags & NodeFlag::Ladder) { - if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) { - if (!prevLadder) { - m_moveSpeed = pev->origin.distance (m_pathOrigin); - } - else { - m_moveSpeed = 150.0f; - } - if (m_moveSpeed < 150.0f) { - m_moveSpeed = 150.0f; - } - else if (m_moveSpeed > pev->maxspeed) { - m_moveSpeed = pev->maxspeed; - } - } - } - m_lastUsedNodesTime = game.time (); - // special movement for swimming here if (isInWater ()) { // check if we need to go forward or back press the correct buttons @@ -955,7 +932,7 @@ bool Bot::updateNavigation () { } } - if ((m_pathFlags & NodeFlag::Ladder) || isOnLadder ()) { + if (m_pathFlags & NodeFlag::Ladder) { if (m_pathOrigin.z >= (pev->origin.z + 16.0f)) { constexpr auto kLadderOffset = Vector (0.0f, 0.0f, 16.0f); m_pathOrigin = m_path->origin + kLadderOffset; @@ -973,7 +950,7 @@ bool Bot::updateNavigation () { // special detection if someone is using the ladder (to prevent to have bots-towers on ladders) for (const auto &client : util.getClients ()) { - if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || (client.ent->v.movetype != MOVETYPE_FLY) || client.ent == ent ()) { + if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || (client.ent->v.movetype != MOVETYPE_FLY) || client.team != m_team || client.ent == ent ()) { continue; } TraceResult tr {}; @@ -981,61 +958,24 @@ bool Bot::updateNavigation () { int previousNode = 0; // more than likely someone is already using our ladder... - if (client.ent->v.origin.distanceSq (m_path->origin) < cr::sqrf (40.0f)) { - if ((client.team != m_team || game.is (GameFlags::FreeForAll)) && !cv_ignore_enemies.bool_ ()) { - game.testLine (getEyesPos (), client.ent->v.origin, TraceIgnore::Monsters, ent (), &tr); + if (client.ent->v.origin.distanceSq (m_path->origin) < cr::sqrf (48.0f)) { + game.testHull (getEyesPos (), m_pathOrigin, TraceIgnore::Monsters, isDucking () ? head_hull : human_hull, ent (), &tr); - // bot found an enemy on his ladder - he should see him... - if (tr.pHit == client.ent) { - m_enemy = client.ent; - m_lastEnemy = client.ent; - m_lastEnemyOrigin = client.ent->v.origin; + // someone is above or below us and is using the ladder already + if (tr.pHit == client.ent && cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) { + const auto numPreviousNode = rg.get (0, 2); - m_enemyParts = Visibility::None; - m_enemyParts |= (Visibility::Head | Visibility::Body); - - m_states |= Sense::SeeingEnemy; - m_seeEnemyTime = game.time (); - break; - } - } - else { - game.testHull (getEyesPos (), m_pathOrigin, TraceIgnore::Monsters, isDucking () ? head_hull : human_hull, ent (), &tr); - - // someone is above or below us and is using the ladder already - if (tr.pHit == client.ent && cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) { - if (graph.exists (m_previousNodes[0])) { - if (!(graph[m_previousNodes[0]].flags & NodeFlag::Ladder)) { - foundGround = true; - previousNode = m_previousNodes[0]; - } - else if (graph.exists (m_previousNodes[1])) { - if (!(graph[m_previousNodes[1]].flags & NodeFlag::Ladder)) { - foundGround = true; - previousNode = m_previousNodes[1]; - } - else if (graph.exists (m_previousNodes[2])) { - if (!(graph[m_previousNodes[2]].flags & NodeFlag::Ladder)) { - foundGround = true; - previousNode = m_previousNodes[2]; - } - else if (graph.exists (m_previousNodes[3])) { - if (!(graph[m_previousNodes[3]].flags & NodeFlag::Ladder)) { - foundGround = true; - previousNode = m_previousNodes[3]; - } - } - } - } - } - if (foundGround) { - if (getCurrentTaskId () != Task::MoveToPosition || !cr::fequal (getTask ()->desire, TaskPri::PlantBomb)) { - changeNodeIndex (m_previousNodes[0]); - startTask (Task::MoveToPosition, TaskPri::PlantBomb, previousNode, 0.0f, true); - } + for (int i = 0; i < numPreviousNode; ++i) { + if (graph.exists (m_previousNodes[i]) && (graph[m_previousNodes[i]].flags & NodeFlag::Ladder)) { + foundGround = true; + previousNode = m_previousNodes[i]; break; } } + if (foundGround) { + changeNodeIndex (m_previousNodes[0]); + findPath (m_previousNodes[0], previousNode, m_pathType); + } } } } @@ -2528,7 +2468,7 @@ bool Bot::canStrafeLeft (TraceResult *tr) { pev->v_angle.angleVectors (&forward, &right, nullptr); Vector src = pev->origin; - Vector dest = src - right * -40.0f; + Vector dest = src - right * 40.0f; // trace from the bot's waist straight left... game.testLine (src, dest, TraceIgnore::Monsters, ent (), tr); @@ -2888,66 +2828,6 @@ bool Bot::isDeadlyMove (const Vector &to) { return false; } -void Bot::changePitch (float speed) { - // this function turns a bot towards its ideal_pitch - - const float idealPitch = cr::wrapAngle (pev->idealpitch); - const float curent = cr::wrapAngle (pev->v_angle.x); - - // turn from the current v_angle pitch to the idealpitch by selecting - // the quickest way to turn to face that direction - - // find the difference in the curent and ideal angle - float normalizePitch = cr::wrapAngle (idealPitch - curent); - - if (normalizePitch > 0.0f) { - if (normalizePitch > speed) { - normalizePitch = speed; - } - } - else { - if (normalizePitch < -speed) { - normalizePitch = -speed; - } - } - pev->v_angle.x = cr::wrapAngle (curent + normalizePitch); - - if (pev->v_angle.x > 89.9f) { - pev->v_angle.x = 89.9f; - } - - if (pev->v_angle.x < -89.9f) { - pev->v_angle.x = -89.9f; - } - pev->angles.x = -pev->v_angle.x / 3; -} - -void Bot::changeYaw (float speed) { - // this function turns a bot towards its ideal_yaw - - const float idealPitch = cr::wrapAngle (pev->ideal_yaw); - const float curent = cr::wrapAngle (pev->v_angle.y); - - // turn from the current v_angle yaw to the ideal_yaw by selecting - // the quickest way to turn to face that direction - - // find the difference in the curent and ideal angle - float normalizePitch = cr::wrapAngle (idealPitch - curent); - - if (normalizePitch > 0.0f) { - if (normalizePitch > speed) { - normalizePitch = speed; - } - } - else { - if (normalizePitch < -speed) { - normalizePitch = -speed; - } - } - pev->v_angle.y = cr::wrapAngle (curent + normalizePitch); - pev->angles.y = pev->v_angle.y; -} - int Bot::getRandomCampDir () { // find a good node to look at when camping diff --git a/src/vision.cpp b/src/vision.cpp index c622a39..bb2bc7d 100644 --- a/src/vision.cpp +++ b/src/vision.cpp @@ -277,6 +277,66 @@ void Bot::checkDarkness () { m_checkDarkTime = game.time () + rg.get (2.0f, 4.0f); } +void Bot::changePitch (float speed) { + // this function turns a bot towards its ideal_pitch + + const float idealPitch = cr::wrapAngle (pev->idealpitch); + const float curent = cr::wrapAngle (pev->v_angle.x); + + // turn from the current v_angle pitch to the idealpitch by selecting + // the quickest way to turn to face that direction + + // find the difference in the curent and ideal angle + float normalizePitch = cr::wrapAngle (idealPitch - curent); + + if (normalizePitch > 0.0f) { + if (normalizePitch > speed) { + normalizePitch = speed; + } + } + else { + if (normalizePitch < -speed) { + normalizePitch = -speed; + } + } + pev->v_angle.x = cr::wrapAngle (curent + normalizePitch); + + if (pev->v_angle.x > 89.9f) { + pev->v_angle.x = 89.9f; + } + + if (pev->v_angle.x < -89.9f) { + pev->v_angle.x = -89.9f; + } + pev->angles.x = -pev->v_angle.x / 3; +} + +void Bot::changeYaw (float speed) { + // this function turns a bot towards its ideal_yaw + + const float idealPitch = cr::wrapAngle (pev->ideal_yaw); + const float curent = cr::wrapAngle (pev->v_angle.y); + + // turn from the current v_angle yaw to the ideal_yaw by selecting + // the quickest way to turn to face that direction + + // find the difference in the curent and ideal angle + float normalizePitch = cr::wrapAngle (idealPitch - curent); + + if (normalizePitch > 0.0f) { + if (normalizePitch > speed) { + normalizePitch = speed; + } + } + else { + if (normalizePitch < -speed) { + normalizePitch = -speed; + } + } + pev->v_angle.y = cr::wrapAngle (curent + normalizePitch); + pev->angles.y = pev->v_angle.y; +} + void Bot::updateBodyAngles () { // set the body angles to point the gun correctly pev->angles.x = -pev->v_angle.x * (1.0f / 3.0f);