bot: perform a seek cover search mission to avoid infected creatures. (#707)
bot: perform a seek cover search mission to avoid infected creatures. nav: several changes in controlling the terrain.
This commit is contained in:
parent
590471d94c
commit
286e1c8621
10 changed files with 155 additions and 114 deletions
|
|
@ -305,6 +305,13 @@ yb_use_engine_pvs_check "0"
|
|||
//
|
||||
yb_use_hitbox_enemy_targeting "0"
|
||||
|
||||
//
|
||||
// Bots will consider glass when deciding to shoot enemies. Required for very special maps only.
|
||||
// ---
|
||||
// Default: "0", Min: "0", Max: "1"
|
||||
//
|
||||
yb_aim_trace_consider_glass "0"
|
||||
|
||||
//
|
||||
// Binds specified key for opening bots menu.
|
||||
// ---
|
||||
|
|
@ -777,9 +784,9 @@ yb_random_knife_attacks "1"
|
|||
//
|
||||
// Maximum number for path length, to predict the enemy.
|
||||
// ---
|
||||
// Default: "25", Min: "15", Max: "256"
|
||||
// Default: "22", Min: "15", Max: "256"
|
||||
//
|
||||
yb_max_nodes_for_predict "25"
|
||||
yb_max_nodes_for_predict "22"
|
||||
|
||||
//
|
||||
// Enables or disables extra hard difficulty for bots.
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ public:
|
|||
void syncCollectOnline ();
|
||||
void collectOnline ();
|
||||
|
||||
IntArray getNearestInRadius (float radius, const Vector &origin, int maxCount = -1);
|
||||
IntArray getNearestInRadius (const float radius, const Vector &origin, int maxCount = -1);
|
||||
|
||||
public:
|
||||
StringRef getAuthor () const {
|
||||
|
|
|
|||
|
|
@ -244,6 +244,7 @@ private:
|
|||
float m_prevTime {}; // time previously checked movement speed
|
||||
float m_heavyTimestamp {}; // is it time to execute heavy-weight functions
|
||||
float m_prevSpeed {}; // speed some frames before
|
||||
float m_prevVelocity {}; // velocity some frames before
|
||||
float m_timeDoorOpen {}; // time to next door open check
|
||||
float m_timeHitDoor {}; // specific time after hitting the door
|
||||
float m_lastChatTime {}; // time bot last chatted
|
||||
|
|
@ -297,6 +298,7 @@ private:
|
|||
float m_lastVictimTime {}; // time when bot killed an enemy
|
||||
float m_killsInterval {}; // interval between kills
|
||||
float m_lastDamageTimestamp {}; // last damage from take damage fn
|
||||
float m_movedDistance {}; // bot moved distance
|
||||
|
||||
bool m_moveToGoal {}; // bot currently moving to goal??
|
||||
bool m_isStuck {}; // bot is stuck
|
||||
|
|
@ -318,6 +320,7 @@ private:
|
|||
bool m_needToSendWelcomeChat {}; // bot needs to greet people on server?
|
||||
bool m_isCreature {}; // bot is not a player, but something else ? zombie ?
|
||||
bool m_isOnInfectedTeam {}; // bot is zombie (this assumes bot is a creature)
|
||||
bool m_infectedEnemyTeam {}; // the enemy is a zombie (assumed to be a hostile creature)
|
||||
bool m_defuseNotified {}; // bot is notified about bomb defusion
|
||||
bool m_jumpSequence {}; // next path link will be jump link
|
||||
bool m_checkFall {}; // check bot fall
|
||||
|
|
@ -360,7 +363,6 @@ private:
|
|||
Vector m_breakableOrigin {}; // origin of breakable
|
||||
Vector m_rightRef {}; // right referential vector
|
||||
Vector m_checkFallPoint[2] {}; // check fall point
|
||||
Vector m_prevVelocity {}; // velocity some frames before
|
||||
|
||||
Array <edict_t *> m_ignoredBreakable {}; // list of ignored breakables
|
||||
Array <edict_t *> m_ignoredItems {}; // list of pointers to entity to ignore for pickup
|
||||
|
|
@ -375,7 +377,6 @@ private:
|
|||
CountdownTimer m_forgetLastVictimTimer {}; // time to forget last victim position ?
|
||||
CountdownTimer m_approachingLadderTimer {}; // bot is approaching ladder
|
||||
CountdownTimer m_lostReachableNodeTimer {}; // bot's issuing next node, probably he's lost
|
||||
CountdownTimer m_fixFallTimer {}; // timer we're fixed fall last time
|
||||
CountdownTimer m_repathTimer {}; // bots is going to repath his route
|
||||
|
||||
private:
|
||||
|
|
@ -493,7 +494,7 @@ private:
|
|||
void postProcessGoals (const IntArray &goals, int result[]);
|
||||
void updatePickups ();
|
||||
void ensurePickupEntitiesClear ();
|
||||
void checkTerrain (float movedDistance, const Vector &dirNormal);
|
||||
void checkTerrain (const Vector &dirNormal);
|
||||
void checkFall ();
|
||||
void checkDarkness ();
|
||||
void checkParachute ();
|
||||
|
|
|
|||
|
|
@ -157,7 +157,10 @@ void Bot::checkBreakable (edict_t *touch) {
|
|||
}
|
||||
|
||||
if (game.isNullEntity (touch)) {
|
||||
m_breakableEntity = lookupBreakable ();
|
||||
auto breakable = lookupBreakable ();
|
||||
|
||||
m_breakableEntity = breakable;
|
||||
m_breakableOrigin = game.getEntityOrigin (breakable);
|
||||
}
|
||||
else {
|
||||
if (m_breakableEntity != touch) {
|
||||
|
|
@ -225,7 +228,7 @@ void Bot::checkBreakablesAround () {
|
|||
|
||||
// maybe time to give up?
|
||||
if (m_lastBreakable == breakable && m_breakableTime + 1.5f < game.time ()) {
|
||||
m_ignoredBreakable.emplace (breakable);
|
||||
m_ignoredBreakable.push (breakable);
|
||||
m_breakableOrigin.clear ();
|
||||
|
||||
m_lastBreakable = nullptr;
|
||||
|
|
@ -305,7 +308,7 @@ edict_t *Bot::lookupBreakable () {
|
|||
return hit;
|
||||
}
|
||||
m_breakableEntity = nullptr;
|
||||
m_breakableOrigin = nullptr;
|
||||
m_breakableOrigin.clear ();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -1702,7 +1705,7 @@ void Bot::overrideConditions () {
|
|||
// then start escape from bomb immediate
|
||||
startTask (Task::EscapeFromBomb, TaskPri::EscapeFromBomb, kInvalidNodeIndex, 0.0f, true);
|
||||
}
|
||||
float reachEnemyWikKnifeDistanceSq = cr::sqrf (128.0f);
|
||||
float reachEnemyKnifeDistanceSq = cr::sqrf (128.0f);
|
||||
|
||||
// special handling, if we have a knife in our hands
|
||||
if (isKnifeMode () && (util.isPlayer (m_enemy) || (cv_attack_monsters && util.isMonster (m_enemy)))) {
|
||||
|
|
@ -1710,12 +1713,12 @@ void Bot::overrideConditions () {
|
|||
const auto nearestToEnemyPoint = graph.getNearest (m_enemy->v.origin);
|
||||
|
||||
if (nearestToEnemyPoint != kInvalidNodeIndex && nearestToEnemyPoint != m_currentNodeIndex) {
|
||||
reachEnemyWikKnifeDistanceSq = graph[nearestToEnemyPoint].origin.distanceSq (m_enemy->v.origin);
|
||||
reachEnemyWikKnifeDistanceSq += cr::sqrf (48.0f);
|
||||
reachEnemyKnifeDistanceSq = graph[nearestToEnemyPoint].origin.distanceSq (m_enemy->v.origin);
|
||||
reachEnemyKnifeDistanceSq += cr::sqrf (48.0f);
|
||||
}
|
||||
|
||||
// do nodes movement if enemy is not reachable with a knife
|
||||
if (distanceSq2d > reachEnemyWikKnifeDistanceSq && (m_states & Sense::SeeingEnemy)) {
|
||||
if (distanceSq2d > reachEnemyKnifeDistanceSq && (m_states & Sense::SeeingEnemy)) {
|
||||
if (nearestToEnemyPoint != kInvalidNodeIndex
|
||||
&& nearestToEnemyPoint != m_currentNodeIndex
|
||||
&& cr::abs (graph[nearestToEnemyPoint].origin.z - m_enemy->v.origin.z) < 16.0f) {
|
||||
|
|
@ -1725,24 +1728,20 @@ void Bot::overrideConditions () {
|
|||
if (tid != Task::MoveToPosition && !cr::fequal (getTask ()->desire, TaskPri::Hide)) {
|
||||
startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, taskTime, true);
|
||||
}
|
||||
else {
|
||||
if (tid == Task::MoveToPosition && getTask ()->data != nearestToEnemyPoint) {
|
||||
else if (tid == Task::MoveToPosition && getTask ()->data != nearestToEnemyPoint) {
|
||||
clearTask (Task::MoveToPosition);
|
||||
startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, taskTime, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!m_isCreature
|
||||
&& distanceSq2d <= reachEnemyWikKnifeDistanceSq
|
||||
else if (!m_isCreature
|
||||
&& distanceSq2d <= reachEnemyKnifeDistanceSq
|
||||
&& (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))
|
||||
|
|
@ -1781,6 +1780,18 @@ void Bot::overrideConditions () {
|
|||
m_navTimeset = game.time ();
|
||||
}
|
||||
}
|
||||
|
||||
// start searching for seek cover to avoid infected creatures
|
||||
if (game.is (GameFlags::ZombieMod)
|
||||
&& !m_isCreature
|
||||
&& m_infectedEnemyTeam
|
||||
&& util.isAlive (m_enemy)
|
||||
&& m_retreatTime < game.time ()
|
||||
&& pev->origin.distanceSq2d (m_enemy->v.origin) < cr::sqrf (512.0f)) {
|
||||
|
||||
completeTask ();
|
||||
startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::syncUpdatePredictedIndex () {
|
||||
|
|
@ -2332,8 +2343,21 @@ bool Bot::isEnemyThreat () {
|
|||
return false;
|
||||
}
|
||||
|
||||
auto isOnAttackDistance = [&] (edict_t *enemy, float distance) -> bool {
|
||||
if (enemy->v.origin.distanceSq (pev->origin) < cr::sqrf (distance)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (usesKnife ()) {
|
||||
if (enemy->v.origin.distanceSq (pev->origin) < cr::sqrf (distance)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// if enemy is near or facing us directly
|
||||
if (m_enemy->v.origin.distanceSq (pev->origin) < cr::sqrf (256.0f) || (!usesKnife () && isInViewCone (m_enemy->v.origin))) {
|
||||
if (isOnAttackDistance (m_enemy, 256.0f) || (!usesKnife () && isInViewCone (m_enemy->v.origin))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -2350,7 +2374,7 @@ bool Bot::reactOnEnemy () {
|
|||
if (m_isCreature && !game.isNullEntity (m_enemy)) {
|
||||
m_isEnemyReachable = false;
|
||||
|
||||
if (pev->origin.distanceSq2d (m_enemy->v.origin) < cr::sqrf (118.0f)) {
|
||||
if (pev->origin.distanceSq2d (m_enemy->v.origin) < cr::sqrf (128.0f)) {
|
||||
m_navTimeset = game.time ();
|
||||
m_isEnemyReachable = true;
|
||||
}
|
||||
|
|
@ -3043,7 +3067,7 @@ void Bot::update () {
|
|||
m_canChooseAimDirection = true;
|
||||
m_isAlive = util.isAlive (ent ());
|
||||
m_team = game.getTeam (ent ());
|
||||
m_healthValue = cr::clamp (pev->health, 0.0f, 100.0f);
|
||||
m_healthValue = cr::clamp (pev->health, 0.0f, 99999.9f);
|
||||
|
||||
if (m_team == Team::Terrorist && game.mapIs (MapFlags::Demolition)) {
|
||||
m_hasC4 = !!(pev->weapons & cr::bit (Weapon::C4));
|
||||
|
|
@ -3060,6 +3084,11 @@ void Bot::update () {
|
|||
}
|
||||
m_isCreature = isCreature ();
|
||||
|
||||
// damage victim action
|
||||
if (m_lastDamageTimestamp < game.time () && !cr::fzero (m_lastDamageTimestamp)) {
|
||||
m_lastDamageTimestamp = 0.0f;
|
||||
}
|
||||
|
||||
// is bot movement enabled
|
||||
m_botMovement = false;
|
||||
|
||||
|
|
@ -3262,7 +3291,7 @@ void Bot::checkSpawnConditions () {
|
|||
void Bot::logic () {
|
||||
// this function gets called each frame and is the core of all bot ai. from here all other subroutines are called
|
||||
|
||||
float movedDistance = kMinMovedDistance; // length of different vector (distance bot moved)
|
||||
m_movedDistance = kMinMovedDistance; // length of different vector (distance bot moved)
|
||||
|
||||
resetMovement ();
|
||||
|
||||
|
|
@ -3289,7 +3318,7 @@ void Bot::logic () {
|
|||
|
||||
// see how far bot has moved since the previous position...
|
||||
if (m_checkTerrain) {
|
||||
movedDistance = m_prevOrigin.distance (pev->origin);
|
||||
m_movedDistance = m_prevOrigin.distance (pev->origin);
|
||||
}
|
||||
|
||||
// save current position as previous
|
||||
|
|
@ -3397,7 +3426,7 @@ void Bot::logic () {
|
|||
checkBreakable (nullptr);
|
||||
|
||||
doPlayerAvoidance (dirNormal);
|
||||
checkTerrain (movedDistance, dirNormal);
|
||||
checkTerrain (dirNormal);
|
||||
}
|
||||
|
||||
// if we have fallen from the place of move, the nearest point is allowed
|
||||
|
|
@ -3443,7 +3472,7 @@ void Bot::logic () {
|
|||
|
||||
// save the previous speed (for checking if stuck)
|
||||
m_prevSpeed = cr::abs (m_moveSpeed);
|
||||
m_prevVelocity = pev->velocity;
|
||||
m_prevVelocity = cr::abs (pev->velocity.length2d ());
|
||||
|
||||
m_lastDamageType = -1; // reset damage
|
||||
}
|
||||
|
|
@ -3953,7 +3982,7 @@ Vector Bot::isBombAudible () {
|
|||
}
|
||||
|
||||
// we hear bomb if length greater than radius
|
||||
if (cr::sqrf (desiredRadius) < pev->origin.distanceSq2d (bombOrigin)) {
|
||||
if (pev->origin.distanceSq2d (bombOrigin) > cr::sqrf (desiredRadius)) {
|
||||
return bombOrigin;
|
||||
}
|
||||
return nullptr;
|
||||
|
|
@ -4306,9 +4335,7 @@ float Bot::getShiftSpeed () {
|
|||
if (getCurrentTaskId () == Task::SeekCover
|
||||
|| (m_aimFlags & AimFlags::Enemy)
|
||||
|| isDucking ()
|
||||
|| m_isCreature
|
||||
|| (pev->button & IN_DUCK)
|
||||
|| (m_oldButtons & IN_DUCK)
|
||||
|| ((pev->button | pev->oldbuttons) & IN_DUCK)
|
||||
|| (m_currentTravelFlags & PathFlag::Jump)
|
||||
|| (m_pathFlags & NodeFlag::Ladder)
|
||||
|| isOnLadder ()
|
||||
|
|
@ -4337,9 +4364,10 @@ void Bot::refreshCreatureStatus (char *infobuffer) {
|
|||
|
||||
// if bot is on infected team, and zombie mode is active, assume bot is a creature/zombie
|
||||
m_isOnInfectedTeam = game.getRealTeam (ent ()) == infectedTeam;
|
||||
m_infectedEnemyTeam = game.getRealTeam (m_enemy) == infectedTeam;
|
||||
|
||||
// do not process next if already infected
|
||||
if (m_isOnInfectedTeam) {
|
||||
if (m_isOnInfectedTeam || m_infectedEnemyTeam) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -669,8 +669,7 @@ Vector Bot::getBodyOffsetError (float distance) {
|
|||
m_aimLastError = Vector (
|
||||
rg (mins.x * hitError, maxs.x * hitError),
|
||||
rg (mins.y * hitError, maxs.y * hitError),
|
||||
rg (mins.z * hitError * 0.5f, maxs.z * hitError * 0.5f)
|
||||
);
|
||||
rg (mins.z * hitError * 0.5f, maxs.z * hitError * 0.5f));
|
||||
|
||||
const auto &aimError = conf.getDifficultyTweaks (m_difficulty)->aimError;
|
||||
m_aimLastError += Vector (rg (-aimError.x, aimError.x), rg (-aimError.y, aimError.y), rg (-aimError.z, aimError.z));
|
||||
|
|
@ -815,7 +814,7 @@ bool Bot::isFriendInLineOfFire (float distance) const {
|
|||
}
|
||||
|
||||
TraceResult tr {};
|
||||
game.testLine (getEyesPos (), getEyesPos () + distance * pev->v_angle.normalize (), TraceIgnore::None, ent (), &tr);
|
||||
game.testLine (getEyesPos (), getEyesPos () + pev->v_angle.normalize_apx () * distance, TraceIgnore::None, ent (), &tr);
|
||||
|
||||
// check if we hit something
|
||||
if (util.isPlayer (tr.pHit) && tr.pHit != ent ()) {
|
||||
|
|
@ -1125,7 +1124,7 @@ void Bot::selectWeapons (float distance, int, int id, int choosen) {
|
|||
else if (isShieldDrawn ()
|
||||
|| m_isReloading
|
||||
|| (hasEnemy && (m_enemy->v.button & IN_RELOAD))
|
||||
|| (hasEnemy && !seesEntity (m_enemy->v.origin))) {
|
||||
|| (!hasEnemy && !seesEntity (m_enemy->v.origin))) {
|
||||
|
||||
pev->button |= IN_ATTACK2; // draw out the shield
|
||||
}
|
||||
|
|
@ -1814,14 +1813,13 @@ bool Bot::hasAnyAmmoInClip () {
|
|||
}
|
||||
|
||||
bool Bot::isKnifeMode () {
|
||||
return cv_jasonmode ||
|
||||
(usesKnife () && !hasAnyWeapons ())
|
||||
return cv_jasonmode || (usesKnife () && !hasAnyWeapons ())
|
||||
|| m_isCreature
|
||||
|| ((m_states & Sense::SeeingEnemy) && usesKnife () && !hasAnyAmmoInClip ());
|
||||
}
|
||||
|
||||
bool Bot::isGrenadeWar () {
|
||||
const bool hasSomeGreandes = bestGrenadeCarried () != -1;
|
||||
const bool hasSomeGreandes = bestGrenadeCarried () != kGrenadeInventoryEmpty;
|
||||
|
||||
// if has grenade an not other weapons, assume we're in grenade war
|
||||
if (!hasAnyWeapons () && hasSomeGreandes) {
|
||||
|
|
@ -2113,10 +2111,10 @@ void Bot::checkReload () {
|
|||
}
|
||||
|
||||
float Bot::calculateScaleFactor (edict_t *ent) const {
|
||||
Vector entSize = ent->v.maxs - ent->v.mins;
|
||||
const auto &entSize = ent->v.maxs - ent->v.mins;
|
||||
const float entArea = 2.0f * (entSize.x * entSize.y + entSize.y * entSize.z + entSize.x * entSize.z);
|
||||
|
||||
Vector botSize = pev->maxs - pev->mins;
|
||||
const auto &botSize = pev->maxs - pev->mins;
|
||||
const float botArea = 2.0f * (botSize.x * botSize.y + botSize.y * botSize.z + botSize.x * botSize.z);
|
||||
|
||||
return entArea / botArea;
|
||||
|
|
|
|||
|
|
@ -566,7 +566,7 @@ int BotGraph::getNearest (const Vector &origin, const float range, int flags) {
|
|||
return index;
|
||||
}
|
||||
|
||||
IntArray BotGraph::getNearestInRadius (float radius, const Vector &origin, int maxCount) {
|
||||
IntArray BotGraph::getNearestInRadius (const float radius, const Vector &origin, int maxCount) {
|
||||
// returns all nodes within radius from position
|
||||
|
||||
const float radiusSq = cr::sqrf (radius);
|
||||
|
|
|
|||
|
|
@ -1518,6 +1518,7 @@ void Bot::newRound () {
|
|||
m_askCheckTime = rg (30.0f, 90.0f);
|
||||
m_minSpeed = 260.0f;
|
||||
m_prevSpeed = 0.0f;
|
||||
m_prevVelocity = 0.0f;
|
||||
m_prevOrigin = Vector (kInfiniteDistance, kInfiniteDistance, kInfiniteDistance);
|
||||
m_prevTime = game.time ();
|
||||
m_lookUpdateTime = game.time ();
|
||||
|
|
@ -1602,7 +1603,6 @@ void Bot::newRound () {
|
|||
m_approachingLadderTimer.invalidate ();
|
||||
m_forgetLastVictimTimer.invalidate ();
|
||||
m_lostReachableNodeTimer.invalidate ();
|
||||
m_fixFallTimer.invalidate ();
|
||||
m_repathTimer.invalidate ();
|
||||
|
||||
for (auto &timer : m_chatterTimes) {
|
||||
|
|
|
|||
|
|
@ -173,7 +173,10 @@ int Bot::findBestGoal () {
|
|||
float campDesire = rg (0.0f, 100.0f) + defensive;
|
||||
|
||||
if (!usesCampGun ()) {
|
||||
campDesire *= 0.5f;
|
||||
campDesire = 0.0f;
|
||||
}
|
||||
else if (usesSniper ()) {
|
||||
campDesire = rg (1.5f, 2.5f) * campDesire;
|
||||
}
|
||||
|
||||
int tactic = GoalTactic::Defensive;
|
||||
|
|
@ -285,14 +288,14 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) {
|
|||
else if (tactic == GoalTactic::Goal && !graph.m_goalPoints.empty ()) { // map goal node
|
||||
|
||||
// force bomber to select closest goal, if round-start goal was reset by something
|
||||
if (m_hasC4 && bots.getRoundStartTime () + 20.0f < game.time ()) {
|
||||
if (m_isVIP || (m_hasC4 && bots.getRoundStartTime () + 20.0f < game.time ())) {
|
||||
float nearestDistanceSq = kInfiniteDistance;
|
||||
int count = 0;
|
||||
|
||||
for (const auto &point : graph.m_goalPoints) {
|
||||
const float distanceSq = graph[point].origin.distanceSq (pev->origin);
|
||||
|
||||
if (distanceSq > cr::sqrf (1024.0f)) {
|
||||
if (distanceSq > cr::sqrf (1024.0f) || isGroupOfEnemies (graph[point].origin)) {
|
||||
continue;
|
||||
}
|
||||
if (distanceSq < nearestDistanceSq) {
|
||||
|
|
@ -589,7 +592,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
}
|
||||
}
|
||||
|
||||
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||
void Bot::checkTerrain (const Vector &dirNormal) {
|
||||
|
||||
// if avoiding someone do not consider stuck
|
||||
TraceResult tr {};
|
||||
|
|
@ -599,6 +602,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
|
||||
// minimal speed for consider stuck
|
||||
const float minimalSpeed = isDucking () ? kMinMovedDistance : kMinMovedDistance * 4;
|
||||
const auto randomProbeTime = rg (0.75f, 1.15f);
|
||||
|
||||
// standing still, no need to check?
|
||||
if ((cr::abs (m_moveSpeed) >= minimalSpeed || cr::abs (m_strafeSpeed) >= minimalSpeed)
|
||||
|
|
@ -612,7 +616,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
m_firstCollideTime = 0.0f;
|
||||
}
|
||||
// didn't we move enough previously?
|
||||
else if (movedDistance < kMinMovedDistance && m_prevSpeed > 20.0f) {
|
||||
else if (m_movedDistance < kMinMovedDistance && (m_prevSpeed > 20.0f || m_prevVelocity < m_moveSpeed / 2)) {
|
||||
m_prevTime = game.time (); // then consider being stuck
|
||||
m_isStuck = true;
|
||||
|
||||
|
|
@ -642,9 +646,16 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
}
|
||||
|
||||
if (m_probeTime >= game.time ()) {
|
||||
m_isStuck = true;
|
||||
}
|
||||
else if (m_probeTime + randomProbeTime < game.time () && !cr::fzero (m_probeTime)) {
|
||||
resetCollision (); // resets its collision state because it was too long time in probing state
|
||||
}
|
||||
|
||||
// not stuck?
|
||||
if (!m_isStuck) {
|
||||
if (m_probeTime + rg (0.75f, 1.15f) < game.time ()) {
|
||||
if (m_probeTime + randomProbeTime < game.time ()) {
|
||||
resetCollision (); // reset collision memory if not being stuck for 0.5 secs
|
||||
}
|
||||
else {
|
||||
|
|
@ -845,7 +856,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
|
||||
m_collideTime = game.time ();
|
||||
m_probeTime = game.time () + 0.5f;
|
||||
m_probeTime = game.time () + randomProbeTime;
|
||||
m_collisionProbeBits = bits;
|
||||
m_collisionState = CollisionState::Probing;
|
||||
m_collStateIndex = 0;
|
||||
|
|
@ -855,10 +866,9 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
if (m_collisionState == CollisionState::Probing) {
|
||||
if (m_probeTime < game.time ()) {
|
||||
m_collStateIndex++;
|
||||
m_probeTime = game.time () + 0.5f;
|
||||
m_probeTime = game.time () + randomProbeTime;
|
||||
|
||||
if (m_collStateIndex >= kMaxCollideMoves) {
|
||||
m_navTimeset = game.time () - 5.0f;
|
||||
resetCollision ();
|
||||
}
|
||||
}
|
||||
|
|
@ -867,8 +877,14 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
switch (m_collideMoves[m_collStateIndex]) {
|
||||
case CollisionState::Jump:
|
||||
if ((isOnFloor () || isInWater ()) && !isOnLadder ()) {
|
||||
if (isInWater ()
|
||||
|| !m_isCreature
|
||||
|| m_lastDamageTimestamp < game.time ()
|
||||
|| (m_currentTravelFlags & PathFlag::Jump)) {
|
||||
|
||||
pev->button |= IN_JUMP;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CollisionState::Duck:
|
||||
|
|
@ -894,18 +910,16 @@ void Bot::checkFall () {
|
|||
if (isPreviousLadder ()) {
|
||||
return;
|
||||
}
|
||||
else if (graph.exists (m_currentNodeIndex)) {
|
||||
if (graph[m_currentNodeIndex].flags & NodeFlag::Ladder) {
|
||||
else if ((m_pathFlags & NodeFlag::Ladder) && isPreviousLadder () && isOnLadder ()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_checkFall) {
|
||||
if (isOnFloor ()) {
|
||||
m_checkFallPoint[0] = pev->origin;
|
||||
|
||||
if (!game.isNullEntity (m_enemy)) {
|
||||
m_checkFallPoint[1] = game.getEntityOrigin (m_enemy);
|
||||
m_checkFallPoint[1] = m_enemy->v.origin;
|
||||
}
|
||||
else if (m_currentNodeIndex != kInvalidNodeIndex) {
|
||||
m_checkFallPoint[1] = m_pathOrigin;
|
||||
|
|
@ -921,7 +935,7 @@ void Bot::checkFall () {
|
|||
}
|
||||
}
|
||||
|
||||
if (!m_checkFall || !isOnFloor () || !m_fixFallTimer.elapsed ()) {
|
||||
if (!m_checkFall || !isOnFloor ()) {
|
||||
return;
|
||||
}
|
||||
m_checkFall = false;
|
||||
|
|
@ -931,25 +945,23 @@ void Bot::checkFall () {
|
|||
const float nowDistanceSq = pev->origin.distanceSq (m_checkFallPoint[1]);
|
||||
|
||||
if (nowDistanceSq > baseDistanceSq
|
||||
&& (nowDistanceSq > baseDistanceSq * 1.8f || nowDistanceSq > baseDistanceSq + 260.0f)
|
||||
&& baseDistanceSq >= cr::sqrf (124.0f) && nowDistanceSq >= cr::sqrf (146.0f)) {
|
||||
&& (nowDistanceSq > baseDistanceSq * 1.2f || nowDistanceSq > baseDistanceSq + 200.0f)
|
||||
&& baseDistanceSq >= cr::sqrf (80.0f) && nowDistanceSq >= cr::sqrf (100.0f)) {
|
||||
fixFall = true;
|
||||
}
|
||||
else if (cr::abs (m_checkFallPoint[1].z) > cr::abs (pev->origin.z) + 138.0f
|
||||
|| cr::abs (m_checkFallPoint[0].z) > cr::abs (pev->origin.z) + 138.0f) {
|
||||
else if (m_checkFallPoint[1].z > pev->origin.z + 128.0f
|
||||
&& m_checkFallPoint[0].z > pev->origin.z + 128.0f) {
|
||||
fixFall = true;
|
||||
}
|
||||
else if (m_currentNodeIndex != kInvalidNodeIndex
|
||||
&& nowDistanceSq > cr::sqrf (32.0f)
|
||||
&& cr::abs (m_checkFallPoint[1].z) > cr::abs (pev->origin.z) + 72.0f) {
|
||||
&& nowDistanceSq > cr::sqrf (16.0f)
|
||||
&& m_checkFallPoint[1].z > pev->origin.z + 62.0f) {
|
||||
fixFall = true;
|
||||
}
|
||||
|
||||
if (fixFall) {
|
||||
m_currentNodeIndex = kInvalidNodeIndex;
|
||||
findValidNode ();
|
||||
|
||||
m_fixFallTimer.start (1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1295,8 +1307,8 @@ bool Bot::updateNavigation () {
|
|||
const auto &dirToPoint = (pev->origin - origin).normalize2d_apx ();
|
||||
const auto &forwardMove = m_moveAngles.forward ().normalize2d_apx ();
|
||||
|
||||
if (distanceSq < cr::sqrf (80.0f)) {
|
||||
if ((dirToPoint | forwardMove) < 0.0f && !checkWallOnBehind ()) {
|
||||
if (distanceSq < cr::sqrf (96.0f)) {
|
||||
if ((dirToPoint | forwardMove) < 0.0f) {
|
||||
m_moveSpeed = -pev->maxspeed;
|
||||
}
|
||||
}
|
||||
|
|
@ -1308,7 +1320,7 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
float desiredDistanceSq = cr::sqrf (8.0f);
|
||||
float nodeDistanceSq = pev->origin.distanceSq (m_pathOrigin);
|
||||
const float nodeDistanceSq = pev->origin.distanceSq (m_pathOrigin);
|
||||
|
||||
// initialize the radius for a special node type, where the node is considered to be reached
|
||||
if (m_pathFlags & NodeFlag::Lift) {
|
||||
|
|
@ -1948,8 +1960,7 @@ bool Bot::findNextBestNodeEx (const IntArray &data, bool handleFails) {
|
|||
float Bot::getEstimatedNodeReachTime () {
|
||||
const bool longTermReachability = (m_pathFlags & NodeFlag::Crouch)
|
||||
|| (m_pathFlags & NodeFlag::Ladder)
|
||||
|| (pev->button & IN_DUCK)
|
||||
|| (m_oldButtons & IN_DUCK);
|
||||
|| ((pev->button | pev->oldbuttons) & IN_DUCK);
|
||||
|
||||
float estimatedTime = longTermReachability ? 8.5f : 3.5f;
|
||||
|
||||
|
|
@ -1958,6 +1969,10 @@ float Bot::getEstimatedNodeReachTime () {
|
|||
return estimatedTime;
|
||||
}
|
||||
|
||||
if (m_lastDamageTimestamp < game.time () && !cr::fzero (m_lastDamageTimestamp) && !m_isStuck && m_isCreature) {
|
||||
return estimatedTime;
|
||||
}
|
||||
|
||||
// calculate 'real' time that we need to get from one node to another
|
||||
if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) {
|
||||
const float distanceSq = graph[m_previousNodes[0]].origin.distanceSq (graph[m_currentNodeIndex].origin);
|
||||
|
|
@ -3097,7 +3112,7 @@ bool Bot::isBlockedLeft () {
|
|||
pev->angles.angleVectors (&forward, &right, nullptr);
|
||||
|
||||
// do a trace to the left...
|
||||
game.testLine (pev->origin, forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||
game.testLine (pev->origin, pev->origin - forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
|
||||
|
||||
// check if the trace hit something...
|
||||
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && !util.isDoorEntity (tr.pHit)) {
|
||||
|
|
@ -3110,8 +3125,8 @@ bool Bot::isBlockedRight () {
|
|||
TraceResult tr {};
|
||||
float direction = 48.0f;
|
||||
|
||||
if (m_moveSpeed < 0.0f) {
|
||||
direction = -48.0f;
|
||||
if (m_moveSpeed > 0.0f) {
|
||||
direction = 48.0f;
|
||||
}
|
||||
Vector right {}, forward {};
|
||||
pev->angles.angleVectors (&forward, &right, nullptr);
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ void Bot::normal_ () {
|
|||
if (allowedCampWeapon && m_timeCamping + 10.0f < game.time () && !m_hasHostage) {
|
||||
bool campingAllowed = true;
|
||||
|
||||
// Check if it's not allowed for this team to camp here
|
||||
// check if it's not allowed for this team to camp here
|
||||
if (m_team == Team::Terrorist) {
|
||||
if (m_pathFlags & NodeFlag::CTOnly) {
|
||||
campingAllowed = false;
|
||||
|
|
@ -134,27 +134,20 @@ void Bot::normal_ () {
|
|||
}
|
||||
|
||||
// don't allow vip on as_ maps to camp + don't allow terrorist carrying c4 to camp
|
||||
if (campingAllowed
|
||||
&& (m_isVIP || (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && !bots.isBombPlanted () && m_hasC4))) {
|
||||
if (m_isVIP || (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && !bots.isBombPlanted () && m_hasC4)) {
|
||||
campingAllowed = false;
|
||||
}
|
||||
|
||||
// check if another bot is already camping here
|
||||
if (campingAllowed && isOccupiedNode (m_currentNodeIndex)) {
|
||||
if (isOccupiedNode (m_currentNodeIndex)) {
|
||||
campingAllowed = false;
|
||||
}
|
||||
|
||||
// skip sniper node if we don't have sniper weapon
|
||||
if (campingAllowed && !usesSniper () && (m_pathFlags & NodeFlag::Sniper)) {
|
||||
if (!usesSniper () && (m_pathFlags & NodeFlag::Sniper)) {
|
||||
campingAllowed = false;
|
||||
}
|
||||
|
||||
// if the bot is about to come to the camp spot, but there is already someone else camping
|
||||
if (!campingAllowed && getTask ()->data == m_currentNodeIndex && getTask ()->data != kInvalidNodeIndex) {
|
||||
clearSearchNodes ();
|
||||
getTask ()->data = kInvalidNodeIndex;
|
||||
}
|
||||
|
||||
if (campingAllowed) {
|
||||
// crouched camping here?
|
||||
if (m_pathFlags & NodeFlag::Crouch) {
|
||||
|
|
@ -406,17 +399,16 @@ void Bot::huntEnemy_ () {
|
|||
}
|
||||
|
||||
// bots skill higher than 60?
|
||||
if (cv_walking_allowed && mp_footsteps && m_difficulty >= Difficulty::Normal && !isKnifeMode ()) {
|
||||
if (cv_walking_allowed && mp_footsteps && m_difficulty >= Difficulty::Normal) {
|
||||
|
||||
// then make him move slow if near enemy
|
||||
if (!(m_currentTravelFlags & PathFlag::Jump)) {
|
||||
if (m_currentNodeIndex != kInvalidNodeIndex) {
|
||||
if (m_currentNodeIndex != kInvalidNodeIndex && !(m_currentTravelFlags & PathFlag::Jump)) {
|
||||
if (m_path->radius < 32.0f && !isOnLadder () && !isInWater () && m_seeEnemyTime + 4.0f > game.time ()) {
|
||||
m_moveSpeed = getShiftSpeed ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::seekCover_ () {
|
||||
m_aimFlags |= AimFlags::Nav;
|
||||
|
|
@ -623,7 +615,7 @@ void Bot::blind_ () {
|
|||
}
|
||||
|
||||
void Bot::camp_ () {
|
||||
if (!cv_camping_allowed || m_isCreature) {
|
||||
if (!cv_camping_allowed || isKnifeMode ()) {
|
||||
completeTask ();
|
||||
return;
|
||||
}
|
||||
|
|
@ -1480,7 +1472,7 @@ void Bot::shootBreakable_ () {
|
|||
m_ignoredBreakable.push (tr.pHit);
|
||||
|
||||
m_breakableEntity = nullptr;
|
||||
m_breakableOrigin = nullptr;
|
||||
m_breakableOrigin.clear ();
|
||||
|
||||
completeTask ();
|
||||
return;
|
||||
|
|
@ -1514,7 +1506,7 @@ void Bot::shootBreakable_ () {
|
|||
// if with knife with no ammo, recompute breakable distance
|
||||
if (!hasAnyAmmoInClip ()
|
||||
&& usesKnife ()
|
||||
&& distToObstacle > cr::sqrf (72.0f)) {
|
||||
&& distToObstacle > cr::sqrf (32.0f)) {
|
||||
|
||||
completeTask ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ void Bot::setAimDirection () {
|
|||
}
|
||||
|
||||
// don't switch view right away after loosing focus with current enemy
|
||||
if ((m_shootTime + 1.5f > game.time () || m_seeEnemyTime + 1.5f > game.time ())
|
||||
if (m_seeEnemyTime + 1.5f > game.time ()
|
||||
&& m_forgetLastVictimTimer.elapsed ()
|
||||
&& !m_lastEnemyOrigin.empty ()
|
||||
&& util.isAlive (m_lastEnemy)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue