nav: added fall control so that bots now take a new path when falling from heights.
nav: increase player avoidance distance. combat: added zoom check for better use of the sniper gun (inspired by podbot).
This commit is contained in:
parent
1443d8b4d0
commit
b720eaf51e
6 changed files with 198 additions and 121 deletions
|
|
@ -132,7 +132,7 @@ public:
|
||||||
~PathWalk () = default;
|
~PathWalk () = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int32_t &doubleNext () {
|
int32_t &nextX2 () {
|
||||||
return at (2);
|
return at (2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,6 +307,7 @@ private:
|
||||||
bool m_isCreature {}; // bot is not a player, but something else ? zombie ?
|
bool m_isCreature {}; // bot is not a player, but something else ? zombie ?
|
||||||
bool m_defuseNotified {}; // bot is notified about bomb defusion
|
bool m_defuseNotified {}; // bot is notified about bomb defusion
|
||||||
bool m_jumpSequence {}; // next path link will be jump link
|
bool m_jumpSequence {}; // next path link will be jump link
|
||||||
|
bool m_checkFall {}; // check bot fall
|
||||||
|
|
||||||
PathWalk m_pathWalk {}; // pointer to current node from path
|
PathWalk m_pathWalk {}; // pointer to current node from path
|
||||||
Dodge m_dodgeStrafeDir {}; // direction to strafe
|
Dodge m_dodgeStrafeDir {}; // direction to strafe
|
||||||
|
|
@ -325,6 +326,7 @@ private:
|
||||||
edict_t *m_targetEntity {}; // the entity that the bot is trying to reach
|
edict_t *m_targetEntity {}; // the entity that the bot is trying to reach
|
||||||
edict_t *m_avoidGrenade {}; // pointer to grenade entity to avoid
|
edict_t *m_avoidGrenade {}; // pointer to grenade entity to avoid
|
||||||
edict_t *m_hindrance {}; // the hindrance
|
edict_t *m_hindrance {}; // the hindrance
|
||||||
|
edict_t *m_hearedEnemy {}; // the heared enemy
|
||||||
|
|
||||||
Vector m_liftTravelPos {}; // lift travel position
|
Vector m_liftTravelPos {}; // lift travel position
|
||||||
Vector m_moveAngles {}; // bot move angles
|
Vector m_moveAngles {}; // bot move angles
|
||||||
|
|
@ -344,6 +346,7 @@ private:
|
||||||
Vector m_desiredVelocity {}; // desired velocity for jump nodes
|
Vector m_desiredVelocity {}; // desired velocity for jump nodes
|
||||||
Vector m_breakableOrigin {}; // origin of breakable
|
Vector m_breakableOrigin {}; // origin of breakable
|
||||||
Vector m_rightRef {}; // right referential vector
|
Vector m_rightRef {}; // right referential vector
|
||||||
|
Vector m_checkFallPoint[2] {}; // check fall point
|
||||||
|
|
||||||
Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
|
Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
|
||||||
Array <edict_t *> m_hostages {}; // pointer to used hostage entities
|
Array <edict_t *> m_hostages {}; // pointer to used hostage entities
|
||||||
|
|
@ -359,7 +362,6 @@ private:
|
||||||
private:
|
private:
|
||||||
int pickBestWeapon (Array <int> &vec, int moneySave);
|
int pickBestWeapon (Array <int> &vec, int moneySave);
|
||||||
int getRandomCampDir ();
|
int getRandomCampDir ();
|
||||||
int findAimingNode (const Vector &to, int &pathLength);
|
|
||||||
int findNearestNode ();
|
int findNearestNode ();
|
||||||
int findBombNode ();
|
int findBombNode ();
|
||||||
int findCoverNode (float maxDistance);
|
int findCoverNode (float maxDistance);
|
||||||
|
|
@ -416,6 +418,7 @@ private:
|
||||||
bool isOutOfBombTimer ();
|
bool isOutOfBombTimer ();
|
||||||
bool isWeaponBadAtDistance (int weaponIndex, float distance);
|
bool isWeaponBadAtDistance (int weaponIndex, float distance);
|
||||||
bool needToPauseFiring (float distance);
|
bool needToPauseFiring (float distance);
|
||||||
|
bool checkZoom (float distance);
|
||||||
bool lookupEnemies ();
|
bool lookupEnemies ();
|
||||||
bool isEnemyHidden (edict_t *enemy);
|
bool isEnemyHidden (edict_t *enemy);
|
||||||
bool isEnemyInvincible (edict_t *enemy);
|
bool isEnemyInvincible (edict_t *enemy);
|
||||||
|
|
@ -464,6 +467,7 @@ private:
|
||||||
void updatePickups ();
|
void updatePickups ();
|
||||||
void ensureEntitiesClear ();
|
void ensureEntitiesClear ();
|
||||||
void checkTerrain (float movedDistance, const Vector &dirNormal);
|
void checkTerrain (float movedDistance, const Vector &dirNormal);
|
||||||
|
void checkFall ();
|
||||||
void checkDarkness ();
|
void checkDarkness ();
|
||||||
void checkParachute ();
|
void checkParachute ();
|
||||||
void updatePracticeValue (int damage);
|
void updatePracticeValue (int damage);
|
||||||
|
|
|
||||||
|
|
@ -133,11 +133,11 @@ void Bot::avoidGrenades () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cv_smoke_grenade_checks.as <int> () == 1 && (pent->v.flags & FL_ONGROUND) && model == kSmokeModelName) {
|
else if (cv_smoke_grenade_checks.as <int> () == 1 && (pent->v.flags & FL_ONGROUND) && model == kSmokeModelName) {
|
||||||
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) {
|
if (seesEntity (pent->v.origin) && isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) {
|
||||||
const auto &entOrigin = game.getEntityOrigin (pent);
|
const auto &entOrigin = game.getEntityOrigin (pent);
|
||||||
const auto &betweenUs = (entOrigin - pev->origin).normalize_apx ();
|
const auto &betweenUs = (entOrigin - pev->origin).normalize_apx ();
|
||||||
const auto &betweenNade = (entOrigin - pev->origin).normalize_apx ();
|
const auto &betweenNade = (entOrigin - pev->origin).normalize_apx ();
|
||||||
const auto &betweenResult = ((Vector (betweenNade.y, betweenNade.x, 0.0f) * 150.0f + entOrigin) - pev->origin).normalize_apx ();
|
const auto &betweenResult = ((betweenNade.get2d () * 150.0f + entOrigin) - pev->origin).normalize_apx ();
|
||||||
|
|
||||||
if ((betweenNade | betweenUs) > (betweenNade | betweenResult) && util.isVisible (pent->v.origin, ent ())) {
|
if ((betweenNade | betweenUs) > (betweenNade | betweenResult) && util.isVisible (pent->v.origin, ent ())) {
|
||||||
const float distance = entOrigin.distance (pev->origin);
|
const float distance = entOrigin.distance (pev->origin);
|
||||||
|
|
@ -751,9 +751,6 @@ void Bot::updatePickups () {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::ensureEntitiesClear () {
|
void Bot::ensureEntitiesClear () {
|
||||||
if ((!m_isStuck || m_navTimeset + getEstimatedNodeReachTime () + m_frameInterval * 2.0f > game.time ()) && !(m_states & Sense::SeeingEnemy)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto tid = getCurrentTaskId ();
|
const auto tid = getCurrentTaskId ();
|
||||||
|
|
||||||
if (tid == Task::PickupItem || (m_states & Sense::PickupItem)) {
|
if (tid == Task::PickupItem || (m_states & Sense::PickupItem)) {
|
||||||
|
|
@ -1685,8 +1682,10 @@ void Bot::overrideConditions () {
|
||||||
|
|
||||||
// special handling for sniping
|
// special handling for sniping
|
||||||
if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))
|
if (usesSniper () && (m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))
|
||||||
&& m_sniperStopTime > game.time ()
|
&& m_shootTime - 0.4f <= game.time ()
|
||||||
&& tid != Task::SeekCover) {
|
&& m_sniperStopTime > game.time ()) {
|
||||||
|
|
||||||
|
ignoreCollision ();
|
||||||
|
|
||||||
m_moveSpeed = 0.0f;
|
m_moveSpeed = 0.0f;
|
||||||
m_strafeSpeed = 0.0f;
|
m_strafeSpeed = 0.0f;
|
||||||
|
|
@ -1897,8 +1896,7 @@ void Bot::setConditions () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't listen if seeing enemy, just checked for sounds or being blinded (because its inhuman)
|
// don't listen if seeing enemy, just checked for sounds or being blinded (because its inhuman)
|
||||||
if (!cv_ignore_enemies
|
if (m_soundUpdateTime < game.time ()
|
||||||
&& m_soundUpdateTime < game.time ()
|
|
||||||
&& m_blindTime < game.time ()
|
&& m_blindTime < game.time ()
|
||||||
&& m_seeEnemyTime + 1.0f < game.time ()) {
|
&& m_seeEnemyTime + 1.0f < game.time ()) {
|
||||||
|
|
||||||
|
|
@ -3203,6 +3201,9 @@ void Bot::logic () {
|
||||||
checkTerrain (movedDistance, dirNormal);
|
checkTerrain (movedDistance, dirNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we have fallen from the place of move, the nearest point is allowed
|
||||||
|
checkFall ();
|
||||||
|
|
||||||
// check the darkness
|
// check the darkness
|
||||||
if (cv_check_darkness) {
|
if (cv_check_darkness) {
|
||||||
checkDarkness ();
|
checkDarkness ();
|
||||||
|
|
@ -3216,7 +3217,11 @@ void Bot::logic () {
|
||||||
m_moveSpeed = -pev->maxspeed;
|
m_moveSpeed = -pev->maxspeed;
|
||||||
m_strafeSpeed = pev->maxspeed * static_cast <float> (m_needAvoidGrenade);
|
m_strafeSpeed = pev->maxspeed * static_cast <float> (m_needAvoidGrenade);
|
||||||
}
|
}
|
||||||
ensureEntitiesClear ();
|
|
||||||
|
// ensure we're not stuck destroying/picking something
|
||||||
|
if (m_navTimeset + getEstimatedNodeReachTime () < game.time () && !(m_states & Sense::SeeingEnemy) && m_moveToGoal) {
|
||||||
|
ensureEntitiesClear ();
|
||||||
|
}
|
||||||
|
|
||||||
// check if need to use parachute
|
// check if need to use parachute
|
||||||
checkParachute ();
|
checkParachute ();
|
||||||
|
|
@ -3839,10 +3844,10 @@ bool Bot::isOutOfBombTimer () {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::updateHearing () {
|
void Bot::updateHearing () {
|
||||||
if (game.is (GameFlags::FreeForAll) || m_enemyIgnoreTimer > game.time ()) {
|
if (game.is (GameFlags::FreeForAll) || m_enemyIgnoreTimer > game.time () || cv_ignore_enemies) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
edict_t *hearedEnemy = nullptr;
|
m_hearedEnemy = nullptr;
|
||||||
float nearestDistanceSq = kInfiniteDistance;
|
float nearestDistanceSq = kInfiniteDistance;
|
||||||
|
|
||||||
// setup potential visibility set from engine
|
// setup potential visibility set from engine
|
||||||
|
|
@ -3870,13 +3875,13 @@ void Bot::updateHearing () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distanceSq < nearestDistanceSq) {
|
if (distanceSq < nearestDistanceSq) {
|
||||||
hearedEnemy = client.ent;
|
m_hearedEnemy = client.ent;
|
||||||
nearestDistanceSq = distanceSq;
|
nearestDistanceSq = distanceSq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// did the bot hear someone ?
|
// did the bot hear someone ?
|
||||||
if (util.isPlayer (hearedEnemy)) {
|
if (util.isPlayer (m_hearedEnemy)) {
|
||||||
// change to best weapon if heard something
|
// change to best weapon if heard something
|
||||||
if (m_shootTime < game.time () - 5.0f
|
if (m_shootTime < game.time () - 5.0f
|
||||||
&& isOnFloor ()
|
&& isOnFloor ()
|
||||||
|
|
@ -3898,7 +3903,7 @@ void Bot::updateHearing () {
|
||||||
|
|
||||||
auto getHeardOriginWithError = [&] () -> Vector {
|
auto getHeardOriginWithError = [&] () -> Vector {
|
||||||
auto error = kSprayDistance * cr::powf (nearestDistanceSq, 0.5f) / 2048.0f;
|
auto error = kSprayDistance * cr::powf (nearestDistanceSq, 0.5f) / 2048.0f;
|
||||||
auto origin = hearedEnemy->v.origin;
|
auto origin = m_hearedEnemy->v.origin;
|
||||||
|
|
||||||
origin.x = origin.x + rg (-error, error);
|
origin.x = origin.x + rg (-error, error);
|
||||||
origin.y = origin.y + rg (-error, error);
|
origin.y = origin.y + rg (-error, error);
|
||||||
|
|
@ -3908,13 +3913,13 @@ void Bot::updateHearing () {
|
||||||
|
|
||||||
// didn't bot already have an enemy ? take this one...
|
// didn't bot already have an enemy ? take this one...
|
||||||
if (m_lastEnemyOrigin.empty () || game.isNullEntity (m_lastEnemy)) {
|
if (m_lastEnemyOrigin.empty () || game.isNullEntity (m_lastEnemy)) {
|
||||||
m_lastEnemy = hearedEnemy;
|
m_lastEnemy = m_hearedEnemy;
|
||||||
m_lastEnemyOrigin = getHeardOriginWithError ();
|
m_lastEnemyOrigin = getHeardOriginWithError ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// bot had an enemy, check if it's the heard one
|
// bot had an enemy, check if it's the heard one
|
||||||
else {
|
else {
|
||||||
if (hearedEnemy == m_lastEnemy) {
|
if (m_hearedEnemy == m_lastEnemy) {
|
||||||
// bot sees enemy ? then bail out !
|
// bot sees enemy ? then bail out !
|
||||||
if (m_states & Sense::SeeingEnemy) {
|
if (m_states & Sense::SeeingEnemy) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -3925,8 +3930,8 @@ void Bot::updateHearing () {
|
||||||
// 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
|
||||||
const float distanceSq = m_lastEnemyOrigin.distanceSq (pev->origin);
|
const float distanceSq = m_lastEnemyOrigin.distanceSq (pev->origin);
|
||||||
|
|
||||||
if (distanceSq > hearedEnemy->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) {
|
if (distanceSq > m_hearedEnemy->v.origin.distanceSq (pev->origin) && m_seeEnemyTime + 2.0f < game.time ()) {
|
||||||
m_lastEnemy = hearedEnemy;
|
m_lastEnemy = m_hearedEnemy;
|
||||||
m_lastEnemyOrigin = getHeardOriginWithError ();
|
m_lastEnemyOrigin = getHeardOriginWithError ();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -3936,9 +3941,9 @@ void Bot::updateHearing () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if heard enemy can be seen
|
// check if heard enemy can be seen
|
||||||
if (checkBodyParts (hearedEnemy)) {
|
if (checkBodyParts (m_hearedEnemy)) {
|
||||||
m_enemy = hearedEnemy;
|
m_enemy = m_hearedEnemy;
|
||||||
m_lastEnemy = hearedEnemy;
|
m_lastEnemy = m_hearedEnemy;
|
||||||
m_lastEnemyOrigin = m_enemyOrigin;
|
m_lastEnemyOrigin = m_enemyOrigin;
|
||||||
|
|
||||||
m_states |= Sense::SeeingEnemy;
|
m_states |= Sense::SeeingEnemy;
|
||||||
|
|
@ -3948,15 +3953,15 @@ void Bot::updateHearing () {
|
||||||
// check if heard enemy can be shoot through some obstacle
|
// check if heard enemy can be shoot through some obstacle
|
||||||
else {
|
else {
|
||||||
if (cv_shoots_thru_walls
|
if (cv_shoots_thru_walls
|
||||||
&& m_lastEnemy == hearedEnemy
|
&& m_lastEnemy == m_hearedEnemy
|
||||||
&& rg.chance (conf.getDifficultyTweaks (m_difficulty)->hearThruPct)
|
&& rg.chance (conf.getDifficultyTweaks (m_difficulty)->hearThruPct)
|
||||||
&& m_seeEnemyTime + 3.0f > game.time ()
|
&& m_seeEnemyTime + 3.0f > game.time ()
|
||||||
&& isPenetrableObstacle (hearedEnemy->v.origin)) {
|
&& isPenetrableObstacle (m_hearedEnemy->v.origin)) {
|
||||||
|
|
||||||
m_enemy = hearedEnemy;
|
m_enemy = m_hearedEnemy;
|
||||||
m_lastEnemy = hearedEnemy;
|
m_lastEnemy = m_hearedEnemy;
|
||||||
m_enemyOrigin = hearedEnemy->v.origin;
|
m_enemyOrigin = m_hearedEnemy->v.origin;
|
||||||
m_lastEnemyOrigin = hearedEnemy->v.origin;
|
m_lastEnemyOrigin = m_hearedEnemy->v.origin;
|
||||||
|
|
||||||
m_states |= (Sense::SeeingEnemy | Sense::SuspectEnemy);
|
m_states |= (Sense::SeeingEnemy | Sense::SuspectEnemy);
|
||||||
m_seeEnemyTime = game.time ();
|
m_seeEnemyTime = game.time ();
|
||||||
|
|
|
||||||
126
src/combat.cpp
126
src/combat.cpp
|
|
@ -876,6 +876,70 @@ bool Bot::needToPauseFiring (float distance) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bot::checkZoom (float distance) {
|
||||||
|
int zoomMagnification = 0;
|
||||||
|
bool zoomChange = false;
|
||||||
|
|
||||||
|
// is the bot holding a sniper rifle?
|
||||||
|
if (usesSniper ()) {
|
||||||
|
// should the bot switch to the long-range zoom?
|
||||||
|
if (distance > 1500.0f) {
|
||||||
|
zoomMagnification = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else should the bot switch to the close-range zoom ?
|
||||||
|
else if (distance > 150.0f) {
|
||||||
|
zoomMagnification = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else should the bot restore the normal view ?
|
||||||
|
else if (distance <= 150.0f) {
|
||||||
|
zoomMagnification = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// else is the bot holding a zoomable rifle?
|
||||||
|
else if (m_difficulty < Difficulty::Hard && usesZoomableRifle ()) {
|
||||||
|
// should the bot switch to zoomed mode?
|
||||||
|
if (distance > 800.0f) {
|
||||||
|
zoomMagnification = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else should the bot restore the normal view?
|
||||||
|
else if (distance <= 800.0f) {
|
||||||
|
zoomMagnification = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (zoomMagnification) {
|
||||||
|
case 0:
|
||||||
|
if (pev->fov < 90.0f) {
|
||||||
|
zoomChange = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if (pev->fov >= 90.0f) {
|
||||||
|
zoomChange = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if (pev->fov >= 40.0f) {
|
||||||
|
zoomChange = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoomChange && m_zoomCheckTime < game.time ()) {
|
||||||
|
pev->button |= IN_ATTACK2;
|
||||||
|
m_shootTime = game.time () + 0.15f;
|
||||||
|
|
||||||
|
m_zoomCheckTime = game.time () + 0.5f;
|
||||||
|
}
|
||||||
|
return zoomChange;
|
||||||
|
}
|
||||||
|
|
||||||
void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
||||||
const auto tab = conf.getRawWeapons ();
|
const auto tab = conf.getRawWeapons ();
|
||||||
|
|
||||||
|
|
@ -922,51 +986,23 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
|
||||||
m_shieldCheckTime = game.time () + 1.0f;
|
m_shieldCheckTime = game.time () + 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the bot holding a sniper rifle?
|
if (checkZoom (distance)) {
|
||||||
if (usesSniper () && m_zoomCheckTime < game.time ()) {
|
return;
|
||||||
// should the bot switch to the long-range zoom?
|
|
||||||
if (distance > 1500.0f && pev->fov >= 40.0f) {
|
|
||||||
pev->button |= IN_ATTACK2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// else should the bot switch to the close-range zoom ?
|
|
||||||
else if (distance > 150.0f && pev->fov >= 90.0f) {
|
|
||||||
pev->button |= IN_ATTACK2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// else should the bot restore the normal view ?
|
|
||||||
else if (distance <= 150.0f && pev->fov < 90.0f) {
|
|
||||||
pev->button |= IN_ATTACK2;
|
|
||||||
}
|
|
||||||
m_zoomCheckTime = game.time () + 0.25f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// else is the bot holding a zoomable rifle?
|
|
||||||
else if (m_difficulty < Difficulty::Hard && usesZoomableRifle () && m_zoomCheckTime < game.time ()) {
|
|
||||||
// should the bot switch to zoomed mode?
|
|
||||||
if (distance > 800.0f && pev->fov >= 90.0f) {
|
|
||||||
pev->button |= IN_ATTACK2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// else should the bot restore the normal view?
|
|
||||||
else if (distance <= 800.0f && pev->fov < 90.0f) {
|
|
||||||
pev->button |= IN_ATTACK2;
|
|
||||||
}
|
|
||||||
m_zoomCheckTime = game.time () + 0.5f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're should stand still before firing sniper weapons, else sniping is useless..
|
// we're should stand still before firing sniper weapons, else sniping is useless..
|
||||||
if (usesSniper () && (m_aimFlags & (AimFlags::Enemy | AimFlags::LastEnemy))
|
if (usesSniper () && (m_aimFlags & (AimFlags::Enemy | AimFlags::LastEnemy))
|
||||||
&& !m_isReloading && pev->velocity.lengthSq () > 0.0f
|
&& !m_isReloading && pev->velocity.lengthSq () > 0.0f) {
|
||||||
&& getCurrentTaskId () != Task::SeekCover) {
|
|
||||||
|
|
||||||
m_moveSpeed = 0.0f;
|
if (!cr::fzero (pev->velocity.x) || !cr::fzero (pev->velocity.y) || !cr::fzero (pev->velocity.z)) {
|
||||||
m_strafeSpeed = 0.0f;
|
m_moveSpeed = 0.0f;
|
||||||
m_navTimeset = game.time ();
|
m_strafeSpeed = 0.0f;
|
||||||
|
m_navTimeset = game.time ();
|
||||||
|
|
||||||
if (cr::abs (pev->velocity.x) > 5.0f || cr::abs (pev->velocity.y) > 5.0f || cr::abs (pev->velocity.z) > 5.0f) {
|
if (cr::abs (pev->velocity.x) > 5.0f || cr::abs (pev->velocity.y) > 5.0f || cr::abs (pev->velocity.z) > 5.0f) {
|
||||||
m_sniperStopTime = game.time () + 2.0f;
|
m_sniperStopTime = game.time () + 2.0f;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1309,7 +1345,7 @@ void Bot::attackMovement () {
|
||||||
const auto pistolStrafeDistance = game.is (GameFlags::CSDM) ? kSprayDistanceX2 * 3.0f : kSprayDistanceX2;
|
const auto pistolStrafeDistance = game.is (GameFlags::CSDM) ? kSprayDistanceX2 * 3.0f : kSprayDistanceX2;
|
||||||
|
|
||||||
// fire hurts friend value here is from previous frame, but acceptable, and saves us alot of cpu cycles
|
// fire hurts friend value here is from previous frame, but acceptable, and saves us alot of cpu cycles
|
||||||
if (approach < 30 || m_fireHurtsFriend || ((usesPistol () || usesShotgun ())
|
if (approach >= 30 || m_fireHurtsFriend || ((usesPistol () || usesShotgun ())
|
||||||
&& distance < pistolStrafeDistance
|
&& distance < pistolStrafeDistance
|
||||||
&& isInViewCone (m_enemyOrigin))) {
|
&& isInViewCone (m_enemyOrigin))) {
|
||||||
m_fightStyle = Fight::Strafe;
|
m_fightStyle = Fight::Strafe;
|
||||||
|
|
@ -1401,7 +1437,7 @@ void Bot::attackMovement () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_fightStyle == Fight::Stay) {
|
else if (m_fightStyle == Fight::Stay) {
|
||||||
const bool alreadyDucking = m_duckTime > game.time () || isDucking ();
|
const bool alreadyDucking = m_duckTime < game.time () || isDucking ();
|
||||||
|
|
||||||
if (alreadyDucking) {
|
if (alreadyDucking) {
|
||||||
m_duckTime = game.time () + m_frameInterval * 3.0f;
|
m_duckTime = game.time () + m_frameInterval * 3.0f;
|
||||||
|
|
@ -1422,14 +1458,6 @@ void Bot::attackMovement () {
|
||||||
m_strafeSpeed = 0.0f;
|
m_strafeSpeed = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_difficulty >= Difficulty::Normal && isOnFloor () && m_duckTime < game.time ()) {
|
|
||||||
if (distance < kSprayDistanceX2) {
|
|
||||||
if (rg (0, 1000) < rg (5, 10) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) {
|
|
||||||
pev->button |= IN_JUMP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_isReloading) {
|
if (m_isReloading) {
|
||||||
m_moveSpeed = -pev->maxspeed;
|
m_moveSpeed = -pev->maxspeed;
|
||||||
m_duckTime = game.time () - 1.0f;
|
m_duckTime = game.time () - 1.0f;
|
||||||
|
|
|
||||||
|
|
@ -1467,6 +1467,11 @@ void Bot::newRound () {
|
||||||
|
|
||||||
m_timeDoorOpen = 0.0f;
|
m_timeDoorOpen = 0.0f;
|
||||||
|
|
||||||
|
for (auto &fall : m_checkFallPoint) {
|
||||||
|
fall = nullptr;
|
||||||
|
}
|
||||||
|
m_checkFall = false;
|
||||||
|
|
||||||
resetCollision ();
|
resetCollision ();
|
||||||
resetDoubleJump ();
|
resetDoubleJump ();
|
||||||
|
|
||||||
|
|
|
||||||
100
src/navigate.cpp
100
src/navigate.cpp
|
|
@ -459,7 +459,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hindrance = nullptr;
|
m_hindrance = nullptr;
|
||||||
float distanceSq = cr::sqrf (384.0f);
|
float distanceSq = cr::sqrf (512.0f);
|
||||||
|
|
||||||
if (isOnLadder () || isInNarrowPlace ()) {
|
if (isOnLadder () || isInNarrowPlace ()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -489,6 +489,16 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
||||||
if (ownPrio > otherPrio) {
|
if (ownPrio > otherPrio) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// they are higher priority - make way, unless we're already making way for someone more important
|
||||||
|
if (!game.isNullEntity (m_hindrance) && m_hindrance != client.ent) {
|
||||||
|
const auto avoidPrio = bots.getPlayerPriority (m_hindrance);
|
||||||
|
|
||||||
|
// ignore because we're already avoiding someone better
|
||||||
|
if (avoidPrio > otherPrio) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
const auto nearestDistanceSq = client.ent->v.origin.distanceSq (pev->origin);
|
const auto nearestDistanceSq = client.ent->v.origin.distanceSq (pev->origin);
|
||||||
|
|
||||||
if (nearestDistanceSq < cr::sqrf (pev->maxspeed) && nearestDistanceSq < distanceSq) {
|
if (nearestDistanceSq < cr::sqrf (pev->maxspeed) && nearestDistanceSq < distanceSq) {
|
||||||
|
|
@ -815,6 +825,59 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bot::checkFall () {
|
||||||
|
|
||||||
|
if (!m_checkFall) {
|
||||||
|
if (isOnFloor ()) {
|
||||||
|
m_checkFallPoint[0] = pev->origin;
|
||||||
|
|
||||||
|
if (!game.isNullEntity (m_enemy)) {
|
||||||
|
m_checkFallPoint[1] = game.getEntityOrigin (m_enemy);
|
||||||
|
}
|
||||||
|
else if (m_currentNodeIndex != kInvalidNodeIndex) {
|
||||||
|
m_checkFallPoint[1] = m_pathOrigin;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_checkFallPoint[1] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!isOnLadder () && !isInWater ()) {
|
||||||
|
if (!m_checkFallPoint[0].empty () && !m_checkFallPoint[1].empty ()) {
|
||||||
|
m_checkFall = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_checkFall || !isOnFloor ()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_checkFall = false;
|
||||||
|
bool fixFall = false;
|
||||||
|
|
||||||
|
const float baseDistanceSq = m_checkFallPoint[0].distanceSq (m_checkFallPoint[1]);
|
||||||
|
const float nowDistanceSq = pev->origin.distanceSq (m_checkFallPoint[1]);
|
||||||
|
|
||||||
|
if (nowDistanceSq > baseDistanceSq
|
||||||
|
&& (nowDistanceSq > baseDistanceSq * 1.2f || nowDistanceSq > baseDistanceSq + 200.0f)
|
||||||
|
&& baseDistanceSq >= cr::sqrf (80.0f) && nowDistanceSq >= cr::sqrf (100.0f)) {
|
||||||
|
fixFall = true;
|
||||||
|
}
|
||||||
|
else if (pev->origin.z + 128.0f < m_checkFallPoint[1].z && pev->origin.z + 128.0f < m_checkFallPoint[0].z) {
|
||||||
|
fixFall = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_currentNodeIndex != kInvalidNodeIndex) {
|
||||||
|
if (pev->origin.distanceSq (m_checkFallPoint[1]) <= cr::sqrf (32.0f) && pev->origin.z + 16.0f < m_checkFallPoint[1].z) {
|
||||||
|
fixFall = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixFall) {
|
||||||
|
m_currentNodeIndex = kInvalidNodeIndex;
|
||||||
|
findValidNode ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Bot::moveToGoal () {
|
void Bot::moveToGoal () {
|
||||||
findValidNode ();
|
findValidNode ();
|
||||||
|
|
||||||
|
|
@ -1607,33 +1670,6 @@ void Bot::clearSearchNodes () {
|
||||||
m_chosenGoalIndex = kInvalidNodeIndex;
|
m_chosenGoalIndex = kInvalidNodeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Bot::findAimingNode (const Vector &to, int &pathLength) {
|
|
||||||
// return the most distant node which is seen from the bot to the target and is within count
|
|
||||||
ensureCurrentNodeIndex ();
|
|
||||||
|
|
||||||
const int destIndex = graph.getNearest (to);
|
|
||||||
int bestIndex = m_currentNodeIndex;
|
|
||||||
|
|
||||||
if (destIndex == kInvalidNodeIndex) {
|
|
||||||
return kInvalidNodeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = planner.find (destIndex, m_currentNodeIndex, [&] (int index) {
|
|
||||||
++pathLength;
|
|
||||||
|
|
||||||
if (vistab.visible (m_currentNodeIndex, index)) {
|
|
||||||
bestIndex = index;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result && bestIndex == m_currentNodeIndex) {
|
|
||||||
return kInvalidNodeIndex;
|
|
||||||
}
|
|
||||||
return bestIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Bot::findNextBestNode () {
|
bool Bot::findNextBestNode () {
|
||||||
// this function find a node in the near of the bot if bot had lost his path of pathfinder needs
|
// this function find a node in the near of the bot if bot had lost his path of pathfinder needs
|
||||||
// to be restarted over again.
|
// to be restarted over again.
|
||||||
|
|
@ -1641,7 +1677,7 @@ bool Bot::findNextBestNode () {
|
||||||
int busyIndex = kInvalidNodeIndex;
|
int busyIndex = kInvalidNodeIndex;
|
||||||
|
|
||||||
float lessDist[3] = { kInfiniteDistance, kInfiniteDistance, kInfiniteDistance };
|
float lessDist[3] = { kInfiniteDistance, kInfiniteDistance, kInfiniteDistance };
|
||||||
int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex , kInvalidNodeIndex };
|
int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex, kInvalidNodeIndex };
|
||||||
|
|
||||||
const auto &origin = pev->origin + pev->velocity * m_frameInterval;
|
const auto &origin = pev->origin + pev->velocity * m_frameInterval;
|
||||||
const auto &bucket = graph.getNodesInBucket (origin);
|
const auto &bucket = graph.getNodesInBucket (origin);
|
||||||
|
|
@ -3093,9 +3129,9 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
|
||||||
if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) {
|
if (needZeroVelocity && client.ent->v.velocity.length2d () > 0.0f) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto length = client.origin.distanceSq (graph[index].origin);
|
const auto distanceSq = client.origin.distanceSq (graph[index].origin);
|
||||||
|
|
||||||
if (length < cr::clamp (cr::sqrf (graph[index].radius) * 2.0f, cr::sqrf (90.0f), cr::sqrf (120.0f))) {
|
if (distanceSq < cr::clamp (cr::sqrf (graph[index].radius) * 2.0f, cr::sqrf (90.0f), cr::sqrf (120.0f))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto bot = bots[client.ent];
|
auto bot = bots[client.ent];
|
||||||
|
|
@ -3103,7 +3139,7 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
|
||||||
if (bot == nullptr || bot == this || !bot->m_isAlive) {
|
if (bot == nullptr || bot == this || !bot->m_isAlive) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return bot->m_currentNodeIndex == index;
|
return bot->m_currentNodeIndex == index || bot->m_previousNodes[0] == index;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ void Bot::normal_ () {
|
||||||
else if (m_team == Team::CT) {
|
else if (m_team == Team::CT) {
|
||||||
if (!bots.isBombPlanted () && numFriendsNear (pev->origin, 210.0f) < 4) {
|
if (!bots.isBombPlanted () && numFriendsNear (pev->origin, 210.0f) < 4) {
|
||||||
const int index = findDefendNode (m_path->origin);
|
const int index = findDefendNode (m_path->origin);
|
||||||
float campTime = rg (25.0f, 40.f);
|
float campTime = rg (25.0f, 40.0f);
|
||||||
|
|
||||||
// rusher bots don't like to camp too much
|
// rusher bots don't like to camp too much
|
||||||
if (m_personality == Personality::Rusher) {
|
if (m_personality == Personality::Rusher) {
|
||||||
|
|
@ -271,7 +271,7 @@ void Bot::normal_ () {
|
||||||
if ((!cr::fzero (m_moveSpeed) && m_moveSpeed > shiftSpeed) && (cv_walking_allowed && mp_footsteps)
|
if ((!cr::fzero (m_moveSpeed) && m_moveSpeed > shiftSpeed) && (cv_walking_allowed && mp_footsteps)
|
||||||
&& m_difficulty >= Difficulty::Normal
|
&& m_difficulty >= Difficulty::Normal
|
||||||
&& (m_heardSoundTime + 6.0f >= game.time () || (m_states & Sense::HearingEnemy))
|
&& (m_heardSoundTime + 6.0f >= game.time () || (m_states & Sense::HearingEnemy))
|
||||||
&& pev->origin.distanceSq (m_lastEnemyOrigin) < cr::sqrf (768.0f)
|
&& numEnemiesNear (pev->origin, 768.0f) >= 1
|
||||||
&& !isKnifeMode ()
|
&& !isKnifeMode ()
|
||||||
&& !bots.isBombPlanted ()) {
|
&& !bots.isBombPlanted ()) {
|
||||||
|
|
||||||
|
|
@ -624,12 +624,12 @@ void Bot::camp_ () {
|
||||||
|
|
||||||
// random camp dir, or prediction
|
// random camp dir, or prediction
|
||||||
auto useRandomCampDirOrPredictEnemy = [&] () {
|
auto useRandomCampDirOrPredictEnemy = [&] () {
|
||||||
if (game.isNullEntity (m_lastEnemy) || !m_lastEnemyOrigin.empty ()) {
|
if (!m_lastEnemyOrigin.empty ()) {
|
||||||
auto pathLength = 0;
|
auto pathLength = m_lastPredictLength;
|
||||||
auto lastEnemyNearestIndex = findAimingNode (m_lastEnemyOrigin, pathLength);
|
auto predictNode = m_lastPredictIndex;
|
||||||
|
|
||||||
if (pathLength > 1 && graph.exists (lastEnemyNearestIndex)) {
|
if (pathLength > 1 && graph.exists (predictNode)) {
|
||||||
m_lookAtSafe = graph[lastEnemyNearestIndex].origin;
|
m_lookAtSafe = graph[predictNode].origin + pev->view_ofs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -638,10 +638,8 @@ void Bot::camp_ () {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (m_nextCampDirTime < game.time ()) {
|
if (m_nextCampDirTime < game.time ()) {
|
||||||
m_nextCampDirTime = game.time () + rg (2.0f, 5.0f);
|
|
||||||
|
|
||||||
if (m_pathFlags & NodeFlag::Camp) {
|
if (m_pathFlags & NodeFlag::Camp) {
|
||||||
Vector dest;
|
Vector dest {};
|
||||||
|
|
||||||
// switch from 1 direction to the other
|
// switch from 1 direction to the other
|
||||||
if (m_campDirection < 1) {
|
if (m_campDirection < 1) {
|
||||||
|
|
@ -674,6 +672,7 @@ void Bot::camp_ () {
|
||||||
else {
|
else {
|
||||||
useRandomCampDirOrPredictEnemy ();
|
useRandomCampDirOrPredictEnemy ();
|
||||||
}
|
}
|
||||||
|
m_nextCampDirTime = game.time () + rg (1.0f, 4.0f);
|
||||||
}
|
}
|
||||||
// press remembered crouch button
|
// press remembered crouch button
|
||||||
pev->button |= m_campButtons;
|
pev->button |= m_campButtons;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue