fix: texts have been corrected. (#477)
task: normal: skip sniper node if no sniper weapon. task: normal: don't walk if there are no enemies left and they are using knives. task: attack: get proper function if bots lose close node after attack task. nav: unnecessary codes cleared. combat: attack movements reviewed. bot: enemy hearing is now better. (thanks for the @spodlesniy idea) desire: increased desire to seek cover by distance. (made for zombie plague mod) --------- Co-authored-by: jeefo <jeefo@rwsh.ru>
This commit is contained in:
parent
e753efd678
commit
ae9beff151
9 changed files with 92 additions and 84 deletions
|
|
@ -235,13 +235,6 @@ yb_pickup_best "1"
|
|||
//
|
||||
yb_ignore_objectives "0"
|
||||
|
||||
//
|
||||
// Allows or disallows the ability for random knife attacks when bot is rushing and no enemy is nearby.
|
||||
// ---
|
||||
// Default: "1", Min: "0", Max: "1"
|
||||
//
|
||||
yb_random_knife_attacks "1"
|
||||
|
||||
//
|
||||
// Enables or disables bots chat functionality.
|
||||
// ---
|
||||
|
|
@ -681,6 +674,13 @@ yb_camping_time_min "15.0"
|
|||
//
|
||||
yb_camping_time_max "45.0"
|
||||
|
||||
//
|
||||
// Allows or disallows the ability for random knife attacks when bot is rushing and no enemy is nearby.
|
||||
// ---
|
||||
// Default: "1", Min: "0", Max: "1"
|
||||
//
|
||||
yb_random_knife_attacks "1"
|
||||
|
||||
//
|
||||
// Maximum number for path length, to predict the enemy.
|
||||
// ---
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ public:
|
|||
void showFileInfo ();
|
||||
void emitNotify (int32_t sound);
|
||||
|
||||
IntArray getNarestInRadius (float radius, const Vector &origin, int maxCount = -1);
|
||||
IntArray getNearestInRadius (float radius, const Vector &origin, int maxCount = -1);
|
||||
const IntArray &getNodesInBucket (const Vector &pos);
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -883,7 +883,6 @@ extern ConVar cv_shoots_thru_walls;
|
|||
extern ConVar cv_debug;
|
||||
extern ConVar cv_debug_goal;
|
||||
extern ConVar cv_save_bots_names;
|
||||
extern ConVar cv_random_knife_attacks;
|
||||
extern ConVar cv_rotate_bots;
|
||||
extern ConVar cv_graph_url;
|
||||
extern ConVar cv_graph_url_upload;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ ConVar cv_pickup_custom_items ("pickup_custom_items", "0", "Allows or disallows
|
|||
ConVar cv_pickup_ammo_and_kits ("pickup_ammo_and_kits", "0", "Allows bots pickup mod items like ammo, health kits and suits.");
|
||||
ConVar cv_pickup_best ("pickup_best", "1", "Allows or disallows bots to pickup best weapons.");
|
||||
ConVar cv_ignore_objectives ("ignore_objectives", "0", "Allows or disallows bots to do map objectives, i.e. plant/defuse bombs, and saves hostages.");
|
||||
ConVar cv_random_knife_attacks ("random_knife_attacks", "1", "Allows or disallows the ability for random knife attacks when bot is rushing and no enemy is nearby.");
|
||||
|
||||
// game console variables
|
||||
ConVar mp_c4timer ("mp_c4timer", nullptr, Var::GameRef);
|
||||
|
|
@ -1684,7 +1683,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 (384.0f) && (distanceToLastEnemySq < cr::sqrf (2048.0f) || usesSniper ())) {
|
||||
if (distanceToLastEnemySq > cr::sqrf (128.0f) && (distanceToLastEnemySq < cr::sqrf (2048.0f) || usesSniper ())) {
|
||||
m_aimFlags |= AimFlags::PredictPath;
|
||||
}
|
||||
const bool denyLastEnemy = pev->velocity.lengthSq2d () > 0.0f && distanceToLastEnemySq < cr::sqrf (256.0f);
|
||||
|
|
@ -1902,6 +1901,9 @@ 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_isCreature) {
|
||||
ratio = 0.0f;
|
||||
}
|
||||
|
|
@ -3354,12 +3356,12 @@ void Bot::updatePracticeValue (int damage) {
|
|||
const auto health = static_cast <int> (m_healthValue);
|
||||
|
||||
// max goal value
|
||||
constexpr int maxGoalValue = PracticeLimit::Goal;
|
||||
constexpr int kMaxGoalValue = PracticeLimit::Goal;
|
||||
|
||||
// only rate goal node if bot died because of the damage
|
||||
// FIXME: could be done a lot better, however this cares most about damage done by sniping or really deadly weapons
|
||||
if (health - damage <= 0) {
|
||||
practice.setValue (m_team, m_chosenGoalIndex, m_prevGoalIndex, cr::clamp (practice.getValue (m_team, m_chosenGoalIndex, m_prevGoalIndex) - health / 20, -maxGoalValue, maxGoalValue));
|
||||
practice.setValue (m_team, m_chosenGoalIndex, m_prevGoalIndex, cr::clamp (practice.getValue (m_team, m_chosenGoalIndex, m_prevGoalIndex) - health / 20, -kMaxGoalValue, kMaxGoalValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3376,7 +3378,7 @@ void Bot::updatePracticeDamage (edict_t *attacker, int damage) {
|
|||
if (attackerTeam == victimTeam) {
|
||||
return;
|
||||
}
|
||||
constexpr int maxDamageValue = PracticeLimit::Damage;
|
||||
constexpr int kMaxDamageValue = PracticeLimit::Damage;
|
||||
|
||||
// if these are bots also remember damage to rank destination of the bot
|
||||
m_goalValue -= static_cast <float> (damage);
|
||||
|
|
@ -3398,13 +3400,13 @@ void Bot::updatePracticeDamage (edict_t *attacker, int damage) {
|
|||
|
||||
if (m_healthValue > 20.0f) {
|
||||
if (victimTeam == Team::Terrorist || victimTeam == Team::CT) {
|
||||
practice.setDamage (victimIndex, victimIndex, victimIndex, cr::clamp (practice.getDamage (victimTeam, victimIndex, victimIndex), 0, maxDamageValue));
|
||||
practice.setDamage (victimIndex, victimIndex, victimIndex, cr::clamp (practice.getDamage (victimTeam, victimIndex, victimIndex), 0, kMaxDamageValue));
|
||||
}
|
||||
}
|
||||
const auto updateDamage = util.isFakeClient (attacker) ? 10 : 7;
|
||||
|
||||
// store away the damage done
|
||||
const auto damageValue = cr::clamp (practice.getDamage (m_team, victimIndex, attackerIndex) + damage / updateDamage, 0, maxDamageValue);
|
||||
const auto damageValue = cr::clamp (practice.getDamage (m_team, victimIndex, attackerIndex) + damage / updateDamage, 0, kMaxDamageValue);
|
||||
|
||||
if (damageValue > practice.getHighestDamageForTeam (m_team)) {
|
||||
practice.setHighestDamageForTeam (m_team, damageValue);
|
||||
|
|
@ -3650,17 +3652,18 @@ bool Bot::isOutOfBombTimer () {
|
|||
}
|
||||
|
||||
void Bot::updateHearing () {
|
||||
int hearEnemyIndex = kInvalidNodeIndex;
|
||||
if (game.is (GameFlags::FreeForAll)) {
|
||||
return;
|
||||
}
|
||||
edict_t *hearedEnemy = nullptr;
|
||||
float nearestDistanceSq = kInfiniteDistance;
|
||||
|
||||
// setup potential visibility set from engine
|
||||
auto set = game.getVisibilitySet (this, false);
|
||||
|
||||
// loop through all enemy clients to check for hearable stuff
|
||||
for (int i = 0; i < game.maxClients (); ++i) {
|
||||
const auto &client = util.getClient (i);
|
||||
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent () || client.team == m_team || client.noise.last < game.time ()) {
|
||||
for (const auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent () || client.team == m_team || !client.ent || client.noise.last < game.time ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -3674,18 +3677,13 @@ void Bot::updateHearing () {
|
|||
}
|
||||
|
||||
if (distanceSq < nearestDistanceSq) {
|
||||
hearEnemyIndex = i;
|
||||
hearedEnemy = client.ent;
|
||||
nearestDistanceSq = distanceSq;
|
||||
}
|
||||
}
|
||||
edict_t *player = nullptr;
|
||||
|
||||
if (hearEnemyIndex >= 0 && util.getClient (hearEnemyIndex).team != m_team && !game.is (GameFlags::FreeForAll)) {
|
||||
player = util.getClient (hearEnemyIndex).ent;
|
||||
}
|
||||
|
||||
// did the bot hear someone ?
|
||||
if (player != nullptr && util.isPlayer (player)) {
|
||||
if (hearedEnemy != nullptr && util.isPlayer (hearedEnemy)) {
|
||||
// change to best weapon if heard something
|
||||
if (m_shootTime < game.time () - 5.0f && isOnFloor () && m_currentWeapon != Weapon::C4 && m_currentWeapon != Weapon::Explosive && m_currentWeapon != Weapon::Smoke && m_currentWeapon != Weapon::Flashbang && !isKnifeMode ()) {
|
||||
selectBestWeapon ();
|
||||
|
|
@ -3700,26 +3698,26 @@ void Bot::updateHearing () {
|
|||
|
||||
// didn't bot already have an enemy ? take this one...
|
||||
if (m_lastEnemyOrigin.empty () || m_lastEnemy == nullptr) {
|
||||
m_lastEnemy = player;
|
||||
m_lastEnemyOrigin = player->v.origin;
|
||||
m_lastEnemy = hearedEnemy;
|
||||
m_lastEnemyOrigin = hearedEnemy->v.origin;
|
||||
}
|
||||
|
||||
// bot had an enemy, check if it's the heard one
|
||||
else {
|
||||
if (player == m_lastEnemy) {
|
||||
if (hearedEnemy == m_lastEnemy) {
|
||||
// bot sees enemy ? then bail out !
|
||||
if (m_states & Sense::SeeingEnemy) {
|
||||
return;
|
||||
}
|
||||
m_lastEnemyOrigin = player->v.origin;
|
||||
m_lastEnemyOrigin = hearedEnemy->v.origin;
|
||||
}
|
||||
else {
|
||||
// if bot had an enemy but the heard one is nearer, take it instead
|
||||
const float distanceSq = m_lastEnemyOrigin.distanceSq (pev->origin);
|
||||
|
||||
if (distanceSq > player->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) {
|
||||
m_lastEnemy = player;
|
||||
m_lastEnemyOrigin = player->v.origin;
|
||||
if (distanceSq > hearedEnemy->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) {
|
||||
m_lastEnemy = hearedEnemy;
|
||||
m_lastEnemyOrigin = hearedEnemy->v.origin;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
|
|
@ -3728,9 +3726,9 @@ void Bot::updateHearing () {
|
|||
}
|
||||
|
||||
// check if heard enemy can be seen
|
||||
if (checkBodyParts (player)) {
|
||||
m_enemy = player;
|
||||
m_lastEnemy = player;
|
||||
if (checkBodyParts (hearedEnemy)) {
|
||||
m_enemy = hearedEnemy;
|
||||
m_lastEnemy = hearedEnemy;
|
||||
m_lastEnemyOrigin = m_enemyOrigin;
|
||||
|
||||
m_states |= Sense::SeeingEnemy;
|
||||
|
|
@ -3739,11 +3737,11 @@ void Bot::updateHearing () {
|
|||
|
||||
// check if heard enemy can be shoot through some obstacle
|
||||
else {
|
||||
if (cv_shoots_thru_walls.bool_ () && m_difficulty >= Difficulty::Normal && m_lastEnemy == player && rg.chance (conf.getDifficultyTweaks (m_difficulty)->hearThruPct) && m_seeEnemyTime + 3.0f > game.time () && isPenetrableObstacle (player->v.origin)) {
|
||||
m_enemy = player;
|
||||
m_lastEnemy = player;
|
||||
m_enemyOrigin = player->v.origin;
|
||||
m_lastEnemyOrigin = player->v.origin;
|
||||
if (cv_shoots_thru_walls.bool_ () && m_difficulty >= Difficulty::Normal && m_lastEnemy == hearedEnemy && rg.chance (conf.getDifficultyTweaks (m_difficulty)->hearThruPct) && m_seeEnemyTime + 3.0f > game.time () && isPenetrableObstacle (hearedEnemy->v.origin)) {
|
||||
m_enemy = hearedEnemy;
|
||||
m_lastEnemy = hearedEnemy;
|
||||
m_enemyOrigin = hearedEnemy->v.origin;
|
||||
m_lastEnemyOrigin = hearedEnemy->v.origin;
|
||||
|
||||
m_states |= (Sense::SeeingEnemy | Sense::SuspectEnemy);
|
||||
m_seeEnemyTime = game.time ();
|
||||
|
|
@ -3840,7 +3838,7 @@ bool Bot::isBombDefusing (const Vector &bombOrigin) {
|
|||
}
|
||||
|
||||
float Bot::getShiftSpeed () {
|
||||
if (getCurrentTaskId () == Task::SeekCover || (m_aimFlags & AimFlags::Enemy) || isDucking () || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PathFlag::Jump) || (m_pathFlags & NodeFlag::Ladder) || isOnLadder () || isInWater () || m_isStuck) {
|
||||
if (getCurrentTaskId () == Task::SeekCover || (m_aimFlags & AimFlags::Enemy) || isDucking () || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK) || (m_currentTravelFlags & PathFlag::Jump) || (m_pathFlags & NodeFlag::Ladder) || isOnLadder () || isInWater () || isKnifeMode () || m_isStuck || m_numEnemiesLeft <= 0) {
|
||||
return pev->maxspeed;
|
||||
}
|
||||
return pev->maxspeed * 0.4f;
|
||||
|
|
|
|||
|
|
@ -1161,11 +1161,14 @@ void Bot::attackMovement () {
|
|||
else if (usesRifle () || usesSubmachine () || usesHeavy ()) {
|
||||
const int rand = rg.get (1, 100);
|
||||
|
||||
if (distance < 500.0f) {
|
||||
if (distance < 768.0f) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
else if (distance < 1024.0f) {
|
||||
if (rand < (usesSubmachine () ? 50 : 30)) {
|
||||
if (isGroupOfEnemies (m_enemy->v.origin)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
else if (rand < (usesSubmachine () ? 50 : 30)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1173,7 +1176,10 @@ void Bot::attackMovement () {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (rand < (usesSubmachine () ? 80 : 90)) {
|
||||
if (isGroupOfEnemies (m_enemy->v.origin)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
else if (rand < (usesSubmachine () ? 80 : 90)) {
|
||||
m_fightStyle = Fight::Stay;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1199,9 +1205,12 @@ void Bot::attackMovement () {
|
|||
m_moveSpeed = -pev->maxspeed;
|
||||
}
|
||||
|
||||
if (usesKnife ()) {
|
||||
m_fightStyle = Fight::None;
|
||||
m_lastFightStyleCheck = game.time ();
|
||||
if (usesKnife () && isInViewCone (m_enemy->v.origin)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
|
||||
if (usesPistol () && distance < 768.0f) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
|
||||
if (m_fightStyle == Fight::Strafe) {
|
||||
|
|
@ -1270,7 +1279,7 @@ void Bot::attackMovement () {
|
|||
}
|
||||
|
||||
// do not move forward/backward is too far
|
||||
if (distance > 1024.0) {
|
||||
if (distance > 1024.0f) {
|
||||
m_moveSpeed = 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -1280,7 +1289,7 @@ void Bot::attackMovement () {
|
|||
if (alreadyDucking) {
|
||||
m_duckTime = game.time () + m_frameInterval * 2.0f;
|
||||
}
|
||||
else if ((distance > kDoubleSprayDistance && hasPrimaryWeapon ()) && (m_enemyParts & (Visibility::Head | Visibility::Body)) && getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::Hunt) {
|
||||
else if ((distance > 768.0f && hasPrimaryWeapon ()) && (m_enemyParts & (Visibility::Head | Visibility::Body)) && getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::Hunt) {
|
||||
const int enemyNearestIndex = graph.getNearest (m_enemy->v.origin);
|
||||
|
||||
if (vistab.visible (m_currentNodeIndex, enemyNearestIndex, VisIndex::Crouch) && vistab.visible (enemyNearestIndex, m_currentNodeIndex, VisIndex::Crouch)) {
|
||||
|
|
@ -1293,7 +1302,7 @@ void Bot::attackMovement () {
|
|||
|
||||
if (m_difficulty >= Difficulty::Normal && isOnFloor () && m_duckTime < game.time ()) {
|
||||
if (distance < 768.0f) {
|
||||
if (rg.get (0, 1000) < rg.get (7, 12) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) {
|
||||
if (rg.get (0, 1000) < rg.get (5, 10) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) {
|
||||
pev->button |= IN_JUMP;
|
||||
}
|
||||
}
|
||||
|
|
@ -1308,7 +1317,7 @@ void Bot::attackMovement () {
|
|||
Vector right, forward;
|
||||
pev->v_angle.angleVectors (&forward, &right, nullptr);
|
||||
|
||||
const auto &front = right * m_moveSpeed * 0.2f;
|
||||
const auto &front = forward * m_moveSpeed * 0.2f;
|
||||
const auto &side = right * m_strafeSpeed * 0.2f;
|
||||
const auto &spot = pev->origin + front + side + pev->velocity * m_frameInterval;
|
||||
|
||||
|
|
@ -1907,7 +1916,7 @@ void Bot::checkGrenadesThrow () {
|
|||
const float radius = cr::max (192.0f, m_lastEnemy->v.velocity.length2d ());
|
||||
const Vector &pos = m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin;
|
||||
|
||||
auto predicted = graph.getNarestInRadius (radius, pos, 12);
|
||||
auto predicted = graph.getNearestInRadius (radius, pos, 12);
|
||||
|
||||
if (predicted.empty ()) {
|
||||
m_states &= ~Sense::ThrowExplosive;
|
||||
|
|
|
|||
|
|
@ -539,7 +539,7 @@ int BotGraph::getNearest (const Vector &origin, const float range, int flags) {
|
|||
return index;
|
||||
}
|
||||
|
||||
IntArray BotGraph::getNarestInRadius (float radius, const Vector &origin, int maxCount) {
|
||||
IntArray BotGraph::getNearestInRadius (float radius, const Vector &origin, int maxCount) {
|
||||
// returns all nodes within radius from position
|
||||
|
||||
const float radiusSq = cr::sqrf (radius);
|
||||
|
|
|
|||
|
|
@ -429,9 +429,9 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
}
|
||||
|
||||
m_hindrance = nullptr;
|
||||
float distanceSq = cr::sqrf (348.0f);
|
||||
float distanceSq = cr::sqrf (384.0f);
|
||||
|
||||
if (getCurrentTaskId () == Task::Attack || isOnLadder () || isInNarrowPlace ()) {
|
||||
if (isOnLadder () || isInNarrowPlace ()) {
|
||||
return;
|
||||
}
|
||||
const auto ownPrio = bots.getPlayerPriority (ent ());
|
||||
|
|
@ -512,7 +512,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
m_isStuck = false;
|
||||
|
||||
// standing still, no need to check?
|
||||
if (m_lastCollTime < game.time () && getCurrentTaskId () != Task::Attack) {
|
||||
if (m_lastCollTime < game.time () && getCurrentTaskId () != Task::Attack) {
|
||||
// didn't we move enough previously?
|
||||
if (movedDistance < 2.0f && (m_prevSpeed > 20.0f || m_prevVelocity < m_moveSpeed / 2)) {
|
||||
m_prevTime = game.time (); // then consider being stuck
|
||||
|
|
@ -610,19 +610,19 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
dirLeft = true;
|
||||
}
|
||||
const auto &testDir = m_moveSpeed > 0.0f ? forward : -forward;
|
||||
constexpr float blockDistance = 32.0f;
|
||||
constexpr float kBlockDistance = 32.0f;
|
||||
|
||||
// now check which side is blocked
|
||||
src = pev->origin + right * blockDistance;
|
||||
dst = src + testDir * blockDistance;
|
||||
src = pev->origin + right * kBlockDistance;
|
||||
dst = src + testDir * kBlockDistance;
|
||||
|
||||
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
||||
|
||||
if (!cr::fequal (tr.flFraction, 1.0f)) {
|
||||
blockedRight = true;
|
||||
}
|
||||
src = pev->origin - right * blockDistance;
|
||||
dst = src + testDir * blockDistance;
|
||||
src = pev->origin - right * kBlockDistance;
|
||||
dst = src + testDir * kBlockDistance;
|
||||
|
||||
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
|
||||
|
||||
|
|
@ -714,8 +714,9 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
state[i] += 5;
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
state[i] = 0;
|
||||
}
|
||||
++i;
|
||||
|
||||
// weighted all possible moves, now sort them to start with most probable
|
||||
|
|
@ -823,6 +824,7 @@ void Bot::moveToGoal () {
|
|||
}
|
||||
}
|
||||
}
|
||||
m_lastUsedNodesTime = game.time ();
|
||||
|
||||
// special movement for swimming here
|
||||
if (isInWater ()) {
|
||||
|
|
@ -959,7 +961,7 @@ bool Bot::updateNavigation () {
|
|||
constexpr auto kLadderOffset = Vector (0.0f, 0.0f, 16.0f);
|
||||
m_pathOrigin = m_path->origin + kLadderOffset;
|
||||
}
|
||||
else if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !(pev->flags & FL_DUCKING)) {
|
||||
else if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) {
|
||||
m_moveSpeed = pev->origin.distance (m_pathOrigin);
|
||||
|
||||
if (m_moveSpeed < 150.0f) {
|
||||
|
|
@ -1037,7 +1039,6 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1173,13 +1174,13 @@ bool Bot::updateNavigation () {
|
|||
// did we reach a destination node?
|
||||
if (getTask ()->data == m_currentNodeIndex) {
|
||||
if (m_chosenGoalIndex != kInvalidNodeIndex) {
|
||||
constexpr int maxGoalValue = PracticeLimit::Goal;
|
||||
constexpr int kMaxGoalValue = PracticeLimit::Goal;
|
||||
|
||||
// add goal values
|
||||
int goalValue = practice.getValue (m_team, m_chosenGoalIndex, m_currentNodeIndex);
|
||||
const int addedValue = static_cast <int> (m_healthValue * 0.5f + m_goalValue * 0.5f);
|
||||
|
||||
goalValue = cr::clamp (goalValue + addedValue, -maxGoalValue, maxGoalValue);
|
||||
goalValue = cr::clamp (goalValue + addedValue, -kMaxGoalValue, kMaxGoalValue);
|
||||
|
||||
// update the practice for team
|
||||
practice.setValue (m_team, m_chosenGoalIndex, m_currentNodeIndex, goalValue);
|
||||
|
|
@ -1782,7 +1783,7 @@ int Bot::findNearestNode () {
|
|||
// try to search ANYTHING that can be reached
|
||||
if (!graph.exists (index)) {
|
||||
nearestDistanceSq = cr::sqrf (kMaxDistance);
|
||||
const auto &nearestNodes = graph.getNarestInRadius (kMaxDistance, pev->origin);
|
||||
const auto &nearestNodes = graph.getNearestInRadius (kMaxDistance, pev->origin);
|
||||
|
||||
for (const auto &i : nearestNodes) {
|
||||
const auto &path = graph[i];
|
||||
|
|
@ -1809,7 +1810,6 @@ int Bot::findNearestNode () {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// worst case, take any node...
|
||||
if (!graph.exists (index)) {
|
||||
index = graph.getNearestNoBuckets (origin);
|
||||
|
|
@ -2174,7 +2174,7 @@ bool Bot::selectBestNextNode () {
|
|||
continue;
|
||||
}
|
||||
|
||||
// skip itn't connected links
|
||||
// skip isn't connected links
|
||||
if (!graph.isConnected (link.index, nextNodeIndex) || !graph.isConnected (link.index, prevNodeIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -3095,7 +3095,6 @@ edict_t *Bot::lookupButton (StringRef target) {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Bot::isReachableNode (int index) {
|
||||
// this function return whether bot able to reach index node or not, depending on several factors.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ ConVar cv_camping_allowed ("camping_allowed", "1", "Allows or disallows bots to
|
|||
ConVar cv_camping_time_min ("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 ("camping_time_max", "45.0", "Upper bound of time until which time for camping is calculated", true, 15.0f, 120.0f);
|
||||
|
||||
ConVar cv_random_knife_attacks ("random_knife_attacks", "1", "Allows or disallows the ability for random knife attacks when bot is rushing and no enemy is nearby.");
|
||||
|
||||
void Bot::normal_ () {
|
||||
m_aimFlags |= AimFlags::Nav;
|
||||
|
||||
|
|
@ -95,6 +97,11 @@ void Bot::normal_ () {
|
|||
campingAllowed = false;
|
||||
}
|
||||
|
||||
// skip sniper node if we don't have sniper weapon
|
||||
if (campingAllowed && !usesSniper () && (m_pathFlags & NodeFlag::Sniper)) {
|
||||
campingAllowed = false;
|
||||
}
|
||||
|
||||
if (campingAllowed) {
|
||||
// crouched camping here?
|
||||
if (m_pathFlags & NodeFlag::Crouch) {
|
||||
|
|
@ -446,7 +453,11 @@ void Bot::attackEnemy_ () {
|
|||
}
|
||||
else {
|
||||
completeTask ();
|
||||
m_destOrigin = m_lastEnemyOrigin;
|
||||
findNextBestNode ();
|
||||
|
||||
if (!m_lastEnemyOrigin.empty ()) {
|
||||
m_destOrigin = m_lastEnemyOrigin;
|
||||
}
|
||||
}
|
||||
m_navTimeset = game.time ();
|
||||
}
|
||||
|
|
@ -1034,7 +1045,7 @@ void Bot::followUser_ () {
|
|||
// didn't choose goal node yet?
|
||||
if (!hasActiveGoal ()) {
|
||||
int destIndex = graph.getNearest (m_targetEntity->v.origin);
|
||||
auto points = graph.getNarestInRadius (200.0f, m_targetEntity->v.origin);
|
||||
auto points = graph.getNearestInRadius (200.0f, m_targetEntity->v.origin);
|
||||
|
||||
for (auto &newIndex : points) {
|
||||
// if node not yet used, assign it as dest
|
||||
|
|
@ -1560,7 +1571,7 @@ void Bot::pickupItem_ () {
|
|||
int nearestHostageNodeIndex = kInvalidNodeIndex;
|
||||
|
||||
// find the nearest 'unused' hostage within the area
|
||||
game.searchEntities (pev->origin, 768.0f, [&] (edict_t *ent) {
|
||||
game.searchEntities (pev->origin, 1024.0f, [&] (edict_t *ent) {
|
||||
if (!util.isHostageEntity (ent)) {
|
||||
return EntitySearchResult::Continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,11 +155,6 @@ void Bot::updateAimDir () {
|
|||
|
||||
m_timeNextTracking = game.time () + rg.get (0.5f, 1.0f);
|
||||
m_trackingEdict = m_lastEnemy;
|
||||
|
||||
// feel free to fire if shootable
|
||||
if (!usesSniper () && lastEnemyShootable ()) {
|
||||
m_wantsToFire = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
doFailPredict ();
|
||||
|
|
@ -195,9 +190,6 @@ void Bot::updateAimDir () {
|
|||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
}
|
||||
else if (m_seeEnemyTime + 3.0f > game.time () && !m_lastEnemyOrigin.empty ()) {
|
||||
m_lookAt = m_lastEnemyOrigin;
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue