fix: hide chatter icon even for players that changed team

fix: mark bot as finished buying on csdm spawn (ref #734)
fix: do not start any map analysis if already analyzing (ref #726)
combat: improved head/body aiming (ref #734)
This commit is contained in:
jeefo 2025-09-10 15:32:40 +03:00
commit 6604145481
No known key found for this signature in database
GPG key ID: D696786B81B667C8
6 changed files with 53 additions and 19 deletions

View file

@ -520,7 +520,7 @@ private:
void setPathOrigin (); void setPathOrigin ();
void fireWeapons (); void fireWeapons ();
void doFireWeapons (); void doFireWeapons ();
void selectWeapons (float distance, int index, int id, int choosen); void handleWeapons (float distance, int index, int id, int choosen);
void focusEnemy (); void focusEnemy ();
void selectBestWeapon (); void selectBestWeapon ();
void selectSecondary (); void selectSecondary ();

View file

@ -17,6 +17,10 @@ ConVar cv_graph_analyze_optimize_nodes_on_finish ("graph_analyze_optimize_nodes_
ConVar cv_graph_analyze_mark_goals_on_finish ("graph_analyze_mark_goals_on_finish", "1", "Specifies if the analyzer should mark nodes as map goals automatically upon finishing."); ConVar cv_graph_analyze_mark_goals_on_finish ("graph_analyze_mark_goals_on_finish", "1", "Specifies if the analyzer should mark nodes as map goals automatically upon finishing.");
void GraphAnalyze::start () { void GraphAnalyze::start () {
if (m_isAnalyzing) {
return;
}
// start analyzer in few seconds after level initialized // start analyzer in few seconds after level initialized
if (cv_graph_analyze_auto_start) { if (cv_graph_analyze_auto_start) {
m_updateInterval = game.time () + 3.0f; m_updateInterval = game.time () + 3.0f;

View file

@ -881,7 +881,9 @@ void Bot::showChatterIcon (bool show, bool disconnect) const {
// do not respect timers while disconnecting bot // do not respect timers while disconnecting bot
for (auto &client : util.getClients ()) { for (auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) { if (!(client.flags & ClientFlags::Used)
|| (client.ent->v.flags & FL_FAKECLIENT)
|| (client.team != m_team && !disconnect)) {
continue; continue;
} }
@ -891,7 +893,7 @@ void Bot::showChatterIcon (bool show, bool disconnect) const {
} }
// do not respect timers while disconnecting bot // do not respect timers while disconnecting bot
if (!show && (client.iconFlags[ownIndex] & ClientFlags::Icon) && (disconnect || client.iconTimestamp[ownIndex] < game.time ())) { if (!show && (disconnect || (client.iconFlags[ownIndex] & ClientFlags::Icon)) && (disconnect || client.iconTimestamp[ownIndex] < game.time ())) {
sendBotVoice (false, client.ent, entindex ()); sendBotVoice (false, client.ent, entindex ());
client.iconTimestamp[ownIndex] = 0.0f; client.iconTimestamp[ownIndex] = 0.0f;
@ -1828,7 +1830,9 @@ void Bot::syncUpdatePredictedIndex () {
const float distToBotSq = botOrigin.distanceSq (graph[index].origin); const float distToBotSq = botOrigin.distanceSq (graph[index].origin);
if (vistab.visible (currentNodeIndex, index) && distToBotSq < cr::sqrf (2048.0f)) { if (vistab.visible (currentNodeIndex, index)
&& distToBotSq < cr::sqrf (2048.0f)
&& distToBotSq > cr::sqrf (128.0f)) {
bestIndex = index; bestIndex = index;
return false; return false;
} }
@ -3283,7 +3287,15 @@ void Bot::checkSpawnConditions () {
dropCurrentWeapon (); dropCurrentWeapon ();
} }
else { else {
selectWeaponById (Weapon::Knife); bool switchToKnife = true;
if (m_currentWeapon == Weapon::Scout) {
switchToKnife = rg.chance (25);
}
if (switchToKnife) {
selectWeaponById (Weapon::Knife);
}
} }
} }
m_checkKnifeSwitch = false; m_checkKnifeSwitch = false;
@ -3474,6 +3486,8 @@ void Bot::spawned () {
if (game.is (GameFlags::CSDM | GameFlags::ZombieMod)) { if (game.is (GameFlags::CSDM | GameFlags::ZombieMod)) {
newRound (); newRound ();
clearTasks (); clearTasks ();
m_buyingFinished = true;
} }
} }

View file

@ -710,6 +710,11 @@ Vector Bot::getEnemyBodyOffset () {
compensation.clear (); compensation.clear ();
} }
// get the correct head origin
const auto &headOrigin = [&] (edict_t *e, const float distance) -> Vector {
return Vector { e->v.origin.x, e->v.origin.y, e->v.absmin.z + e->v.size.z * 0.81f } + getCustomHeight (distance);
};
// if we only suspect an enemy behind a wall take the worst skill // if we only suspect an enemy behind a wall take the worst skill
if (!m_enemyParts && (m_states & Sense::SuspectEnemy)) { if (!m_enemyParts && (m_states & Sense::SuspectEnemy)) {
spot += getBodyOffsetError (distance); spot += getBodyOffsetError (distance);
@ -728,11 +733,13 @@ Vector Bot::getEnemyBodyOffset () {
} }
// now check is our skill match to aim at head, else aim at enemy body // now check is our skill match to aim at head, else aim at enemy body
if (m_enemyBodyPartSet == m_enemy || rg.chance (headshotPct)) { if (m_enemyBodyPartSet == m_enemy
spot = m_enemyOrigin + getCustomHeight (distance); || ((m_enemyBodyPartSet != m_enemy) && rg.chance (headshotPct))) {
spot = headOrigin (m_enemy, distance);
if (usesSniper ()) { if (usesSniper ()) {
spot.z -= pev->view_ofs.z * 0.5f; spot.z -= pev->view_ofs.z * 0.35f;
} }
// set's the enemy shooting spot to head, if headshot pct allows, and use head for that // set's the enemy shooting spot to head, if headshot pct allows, and use head for that
@ -741,6 +748,10 @@ Vector Bot::getEnemyBodyOffset () {
} }
else { else {
spot = m_enemy->v.origin; spot = m_enemy->v.origin;
if (m_difficulty == Difficulty::Expert) {
spot.z += pev->view_ofs.z * 0.35f;
}
} }
} }
else if (m_enemyParts & Visibility::Body) { else if (m_enemyParts & Visibility::Body) {
@ -750,10 +761,10 @@ Vector Bot::getEnemyBodyOffset () {
spot = m_enemyOrigin; spot = m_enemyOrigin;
} }
else if (m_enemyParts & Visibility::Head) { else if (m_enemyParts & Visibility::Head) {
spot = m_enemyOrigin + getCustomHeight (distance); spot = headOrigin (m_enemy, distance);
} }
} }
auto idealSpot = m_enemyOrigin; auto idealSpot = spot;
if (m_difficulty < Difficulty::Hard && isEnemyInSight (idealSpot)) { if (m_difficulty < Difficulty::Hard && isEnemyInSight (idealSpot)) {
spot = idealSpot + ((spot - idealSpot) * 0.005f); // gradually adjust the aiming direction spot = idealSpot + ((spot - idealSpot) * 0.005f); // gradually adjust the aiming direction
@ -1079,7 +1090,7 @@ bool Bot::checkZoom (float distance) {
return zoomChange; return zoomChange;
} }
void Bot::selectWeapons (float distance, int, int id, int choosen) { void Bot::handleWeapons (float distance, int, int id, int choosen) {
const auto tab = conf.getRawWeapons (); const auto tab = conf.getRawWeapons ();
// we want to fire weapon, don't reload now // we want to fire weapon, don't reload now
@ -1249,7 +1260,7 @@ void Bot::fireWeapons () {
// if knife mode use knife only // if knife mode use knife only
if (isKnifeMode ()) { if (isKnifeMode ()) {
selectWeapons (distance, selectIndex, selectId, choosenWeapon); handleWeapons (distance, selectIndex, selectId, choosenWeapon);
return; return;
} }
@ -1263,7 +1274,7 @@ void Bot::fireWeapons () {
&& !isGroupOfEnemies (pev->origin) && !isGroupOfEnemies (pev->origin)
&& getCurrentTaskId () != Task::Camp) { && getCurrentTaskId () != Task::Camp) {
selectWeapons (distance, selectIndex, selectId, choosenWeapon); handleWeapons (distance, selectIndex, selectId, choosenWeapon);
return; return;
} }
@ -1316,7 +1327,7 @@ void Bot::fireWeapons () {
} }
selectId = Weapon::Knife; // no available ammo, use knife! selectId = Weapon::Knife; // no available ammo, use knife!
} }
selectWeapons (distance, selectIndex, selectId, choosenWeapon); handleWeapons (distance, selectIndex, selectId, choosenWeapon);
} }
bool Bot::isWeaponBadAtDistance (int weaponIndex, float distance) { bool Bot::isWeaponBadAtDistance (int weaponIndex, float distance) {

View file

@ -648,14 +648,19 @@ void Bot::camp_ () {
auto pathLength = m_lastPredictLength; auto pathLength = m_lastPredictLength;
auto predictNode = m_lastPredictIndex; auto predictNode = m_lastPredictIndex;
if (isNodeValidForPredict (predictNode) && pathLength > 1) { if (isNodeValidForPredict (predictNode)
&& pathLength > 1
&& vistab.visible (predictNode, m_currentNodeIndex)) {
m_lookAtSafe = graph[predictNode].origin + pev->view_ofs; m_lookAtSafe = graph[predictNode].origin + pev->view_ofs;
} }
else { else {
pathLength = 0; pathLength = 0;
predictNode = findAimingNode (m_lastEnemyOrigin, pathLength); predictNode = findAimingNode (m_lastEnemyOrigin, pathLength);
if (isNodeValidForPredict (predictNode) && pathLength > 1) { if (isNodeValidForPredict (predictNode) && pathLength > 1
&& vistab.visible ( predictNode, m_currentNodeIndex)) {
m_lookAtSafe = graph[predictNode].origin + pev->view_ofs; m_lookAtSafe = graph[predictNode].origin + pev->view_ofs;
} }
} }

View file

@ -441,12 +441,12 @@ void Bot::setAimDirection () {
// don't switch view right away after loosing focus with current enemy // don't switch view right away after loosing focus with current enemy
if ((m_shootTime + rg (0.75f, 1.25f) > game.time () if ((m_shootTime + rg (0.75f, 1.25f) > game.time ()
|| m_seeEnemyTime + 1.5f > game.time ()) || m_seeEnemyTime + rg (1.0f, 1.25f) > game.time ())
&& m_forgetLastVictimTimer.elapsed () && m_forgetLastVictimTimer.elapsed ()
&& !m_lastEnemyOrigin.empty () && !m_lastEnemyOrigin.empty ()
&& util.isAlive (m_lastEnemy) && util.isPlayer (m_lastEnemy)
&& game.isNullEntity (m_enemy)) { && !util.isPlayer (m_enemy)) {
flags |= AimFlags::LastEnemy; flags |= AimFlags::LastEnemy;
} }