fix: descending ladder triggers nav fall recovery

nav: various fixes to player avoiding
build: fix cmake postfix when building as part of cs16-client
Co-Authored-By: Max <161382234+dyspose@users.noreply.github.com>
This commit is contained in:
jeefo 2025-02-14 20:28:36 +03:00
commit c322e36d9b
No known key found for this signature in database
GPG key ID: D696786B81B667C8
6 changed files with 105 additions and 89 deletions

View file

@ -145,6 +145,10 @@ target_include_directories(${PROJECT_NAME} PRIVATE
"ext/linkage" "ext/linkage"
) )
if(COMMAND set_target_postfix)
set_target_postfix(${PROJECT_NAME})
endif()
install(TARGETS ${PROJECT_NAME} install(TARGETS ${PROJECT_NAME}
DESTINATION "${GAME_DIR}/${SERVER_INSTALL_DIR}/" DESTINATION "${GAME_DIR}/${SERVER_INSTALL_DIR}/"
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE

View file

@ -365,6 +365,7 @@ private:
CountdownTimer m_forgetLastVictimTimer {}; // time to forget last victim position ? CountdownTimer m_forgetLastVictimTimer {}; // time to forget last victim position ?
CountdownTimer m_approachingLadderTimer {}; // bot is approaching ladder CountdownTimer m_approachingLadderTimer {}; // bot is approaching ladder
CountdownTimer m_lostReachableNodeTimer {}; // bot's issuing next node, probably he's lost CountdownTimer m_lostReachableNodeTimer {}; // bot's issuing next node, probably he's lost
CountdownTimer m_fixFallTimer {}; // timer we're fixed fall last time
private: private:
int pickBestWeapon (Array <int> &vec, int moneySave); int pickBestWeapon (Array <int> &vec, int moneySave);

View file

@ -2942,20 +2942,22 @@ void Bot::checkParachute () {
void Bot::frame () { void Bot::frame () {
pev->flags |= FL_CLIENT | FL_FAKECLIENT; // restore fake client bit, just in case pev->flags |= FL_CLIENT | FL_FAKECLIENT; // restore fake client bit, just in case
if (m_thinkDelay.time <= game.time ()) { const auto timestamp = game.time ();
if (m_thinkDelay.time <= timestamp) {
update (); update ();
// delay next execution for thinking // delay next execution for thinking
m_thinkDelay.time = game.time () + m_thinkDelay.interval; m_thinkDelay.time = timestamp + m_thinkDelay.interval;
}
// run bot command on twice speed // run bot command on twice speed
if (m_commandDelay.time <= game.time ()) { if (m_commandDelay.time <= timestamp) {
runMovement (); runMovement ();
m_commandDelay.time = game.time () + m_commandDelay.interval; m_commandDelay.time = timestamp + m_commandDelay.interval;
}
} }
if (m_slowFrameTimestamp > game.time ()) { if (m_slowFrameTimestamp > timestamp) {
return; return;
} }
@ -3162,7 +3164,7 @@ void Bot::checkSpawnConditions () {
// switch to knife if time to do this // switch to knife if time to do this
if (m_checkKnifeSwitch && m_buyingFinished && m_spawnTime + rg (5.0f, 7.5f) < game.time ()) { if (m_checkKnifeSwitch && m_buyingFinished && m_spawnTime + rg (5.0f, 7.5f) < game.time ()) {
if (rg (1, 100) < 30 && cv_spraypaints) { if (rg (1, 100) < 30 && cv_spraypaints && pev->groundentity == game.getStartEntity ()) {
startTask (Task::Spraypaint, TaskPri::Spraypaint, kInvalidNodeIndex, game.time () + 1.0f, false); startTask (Task::Spraypaint, TaskPri::Spraypaint, kInvalidNodeIndex, game.time () + 1.0f, false);
} }
@ -3249,7 +3251,7 @@ void Bot::logic () {
// save current position as previous // save current position as previous
m_prevOrigin = pev->origin; m_prevOrigin = pev->origin;
m_prevTime = game.time () + 0.2f; m_prevTime = game.time () + (0.2f - m_frameInterval * 2.0f);
} }
// if there's some radio message to respond, check it // if there's some radio message to respond, check it
@ -3920,7 +3922,7 @@ uint8_t Bot::computeMsec () {
const Vector &Bot::getRpmAngles () { const Vector &Bot::getRpmAngles () {
// get angles to pass to run player move function // get angles to pass to run player move function
if (!m_approachingLadderTimer.elapsed () || getCurrentTaskId () == Task::Attack) { if (m_isStuck || !m_approachingLadderTimer.elapsed () || getCurrentTaskId () == Task::Attack) {
return pev->v_angle; return pev->v_angle;
} }
return m_moveAngles; return m_moveAngles;

View file

@ -1571,6 +1571,7 @@ void Bot::newRound () {
m_approachingLadderTimer.invalidate (); m_approachingLadderTimer.invalidate ();
m_forgetLastVictimTimer.invalidate (); m_forgetLastVictimTimer.invalidate ();
m_lostReachableNodeTimer.invalidate (); m_lostReachableNodeTimer.invalidate ();
m_fixFallTimer.invalidate ();
for (auto &timer : m_chatterTimes) { for (auto &timer : m_chatterTimes) {
timer = kMaxChatterRepeatInterval; timer = kMaxChatterRepeatInterval;

View file

@ -577,7 +577,10 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
const auto tid = getCurrentTaskId (); const auto tid = getCurrentTaskId ();
// standing still, no need to check? // standing still, no need to check?
if (m_lastCollTime < game.time () && tid != Task::Attack && tid != Task::Camp) { if ((m_moveSpeed >= 10 || m_strafeSpeed >= 10)
&& m_lastCollTime < game.time ()
&& tid != Task::Attack
&& tid != Task::Camp) {
// didn't we move enough previously? // didn't we move enough previously?
if (movedDistance < kMinMovedDistance && m_prevSpeed > 20.0f) { if (movedDistance < kMinMovedDistance && m_prevSpeed > 20.0f) {
m_prevTime = game.time (); // then consider being stuck m_prevTime = game.time (); // then consider being stuck
@ -653,78 +656,11 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
Vector src {}, dst {}; Vector src {}, dst {};
// first 4 entries hold the possible collision states // first 4 entries hold the possible collision states
state[i++] = CollisionState::Jump;
state[i++] = CollisionState::StrafeLeft; state[i++] = CollisionState::StrafeLeft;
state[i++] = CollisionState::StrafeRight; state[i++] = CollisionState::StrafeRight;
state[i++] = CollisionState::Jump;
state[i++] = CollisionState::Duck; state[i++] = CollisionState::Duck;
if (bits & CollisionProbe::Strafe) {
state[i] = 0;
state[i + 1] = 0;
// to start strafing, we have to first figure out if the target is on the left side or right side
Vector right {}, forward {};
m_moveAngles.angleVectors (&forward, &right, nullptr);
const Vector &dirToPoint = (pev->origin - m_destOrigin).normalize2d_apx ();
const Vector &rightSide = right.normalize2d_apx ();
bool dirRight = false;
bool dirLeft = false;
bool blockedLeft = false;
bool blockedRight = false;
if ((dirToPoint | rightSide) > 0.0f) {
dirRight = true;
}
else {
dirLeft = true;
}
const auto &testDir = m_moveSpeed > 0.0f ? forward : -forward;
constexpr float kBlockDistance = 52.0f;
// now check which side is blocked
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 * kBlockDistance;
dst = src + testDir * kBlockDistance;
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
if (!cr::fequal (tr.flFraction, 1.0f)) {
blockedLeft = true;
}
if (dirLeft) {
state[i] += 5;
}
else {
state[i] -= 5;
}
if (blockedLeft) {
state[i] -= 5;
}
++i;
if (dirRight) {
state[i] += 5;
}
else {
state[i] -= 5;
}
if (blockedRight) {
state[i] -= 5;
}
}
// now weight all possible states // now weight all possible states
if (bits & CollisionProbe::Jump) { if (bits & CollisionProbe::Jump) {
state[i] = 0; state[i] = 0;
@ -774,6 +710,74 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
} }
++i; ++i;
if (bits & CollisionProbe::Strafe) {
state[i] = 0;
state[i + 1] = 0;
// to start strafing, we have to first figure out if the target is on the left side or right side
Vector right {}, forward {};
m_moveAngles.angleVectors (&forward, &right, nullptr);
const Vector &dirToPoint = (pev->origin - m_destOrigin).normalize2d_apx ();
const Vector &rightSide = right.normalize2d_apx ();
bool dirRight = false;
bool dirLeft = false;
bool blockedLeft = false;
bool blockedRight = false;
if ((dirToPoint | rightSide) > 0.0f) {
dirRight = true;
}
else {
dirLeft = true;
}
const auto &testDir = m_moveSpeed > 0.0f ? forward : -forward;
constexpr float kBlockDistance = 32.0f;
// now check which side is blocked
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 * kBlockDistance;
dst = src + testDir * kBlockDistance;
game.testHull (src, dst, TraceIgnore::Monsters, head_hull, ent (), &tr);
if (!cr::fequal (tr.flFraction, 1.0f)) {
blockedLeft = true;
}
if (dirLeft) {
state[i] += 5;
}
else {
state[i] -= 5;
}
if (blockedLeft) {
state[i] -= 5;
}
++i;
if (dirRight) {
state[i] += 5;
}
else {
state[i] -= 5;
}
if (blockedRight) {
state[i] -= 5;
}
}
if (bits & CollisionProbe::Duck) { if (bits & CollisionProbe::Duck) {
state[i] = 0; state[i] = 0;
@ -881,13 +885,13 @@ void Bot::checkFall () {
} }
} }
else if (!isOnLadder () && !isInWater ()) { else if (!isOnLadder () && !isInWater ()) {
if (!m_checkFallPoint[0].empty () || !m_checkFallPoint[1].empty ()) { if (!m_checkFallPoint[0].empty () && !m_checkFallPoint[1].empty ()) {
m_checkFall = true; m_checkFall = true;
} }
} }
} }
if (!m_checkFall || !isOnFloor ()) { if (!m_checkFall || !isOnFloor () || !m_fixFallTimer.elapsed ()) {
return; return;
} }
m_checkFall = false; m_checkFall = false;
@ -897,22 +901,25 @@ void Bot::checkFall () {
const float nowDistanceSq = pev->origin.distanceSq (m_checkFallPoint[1]); const float nowDistanceSq = pev->origin.distanceSq (m_checkFallPoint[1]);
if (nowDistanceSq > baseDistanceSq if (nowDistanceSq > baseDistanceSq
&& (nowDistanceSq > baseDistanceSq * 1.2f || nowDistanceSq > baseDistanceSq + 200.0f) && (nowDistanceSq > baseDistanceSq * 1.8f || nowDistanceSq > baseDistanceSq + 260.0f)
&& baseDistanceSq >= cr::sqrf (80.0f) && nowDistanceSq >= cr::sqrf (100.0f)) { && baseDistanceSq >= cr::sqrf (124.0f) && nowDistanceSq >= cr::sqrf (146.0f)) {
fixFall = true; fixFall = true;
} }
else if (m_checkFallPoint[1].z > pev->origin.z + 128.0f || m_checkFallPoint[0].z > pev->origin.z + 128.0f) { 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) {
fixFall = true; fixFall = true;
} }
else if (m_currentNodeIndex != kInvalidNodeIndex else if (m_currentNodeIndex != kInvalidNodeIndex
&& nowDistanceSq > cr::sqrf (16.0f) && nowDistanceSq > cr::sqrf (32.0f)
&& m_checkFallPoint[1].z > pev->origin.z + 62.0f) { && cr::abs (m_checkFallPoint[1].z) > cr::abs (pev->origin.z) + 72.0f) {
fixFall = true; fixFall = true;
} }
if (fixFall) { if (fixFall) {
m_currentNodeIndex = kInvalidNodeIndex; m_currentNodeIndex = kInvalidNodeIndex;
findValidNode (); findValidNode ();
m_fixFallTimer.start (1.0f);
} }
} }
@ -1119,7 +1126,7 @@ bool Bot::updateNavigation () {
if (!isOnLadder ()) { if (!isOnLadder ()) {
pev->button &= ~IN_DUCK; pev->button &= ~IN_DUCK;
} }
m_approachingLadderTimer.start (m_frameInterval * 4.0f); m_approachingLadderTimer.start (m_frameInterval * 6.0f);
} }
if (!isOnLadder () && isOnFloor () && !isDucking ()) { if (!isOnLadder () && isOnFloor () && !isDucking ()) {

View file

@ -102,6 +102,7 @@ void Bot::normal_ () {
&& m_reloadState == Reload::None && m_reloadState == Reload::None
&& m_timeLogoSpray < game.time () && m_timeLogoSpray < game.time ()
&& cv_spraypaints && cv_spraypaints
&& pev->groundentity == game.getStartEntity ()
&& m_moveSpeed >= getShiftSpeed () && m_moveSpeed >= getShiftSpeed ()
&& game.isNullEntity (m_pickupItem)) { && game.isNullEntity (m_pickupItem)) {