Merge branch 'master' into chatter-fixes-and-improvements

This commit is contained in:
Владислав Сухов 2024-05-07 09:15:48 +00:00
commit 9e24eb6be4
15 changed files with 852 additions and 850 deletions

15
.gitattributes vendored
View file

@ -1,7 +1,8 @@
# Auto detect text files and perform LF normalization # Auto detect text files and perform LF normalization
* text=lf * text=lf
* text eol=lf
# Custom for Visual Studio
*.sln merge=union # Custom for Visual Studio
*.vcxproj merge=union *.sln merge=union
*.vcxproj merge=union

View file

@ -176,7 +176,7 @@ void GraphAnalyze::optimize () {
} }
cleanup (); cleanup ();
auto smooth = [] (const Array <int> &nodes) { auto smooth = [] (const Array <int> &nodes) {
Vector result; Vector result;
for (const auto &node : nodes) { for (const auto &node : nodes) {
@ -202,7 +202,7 @@ void GraphAnalyze::optimize () {
for (const auto &link : path.links) { for (const auto &link : path.links) {
if (graph.exists (link.index) && !m_optimizedNodes[link.index] if (graph.exists (link.index) && !m_optimizedNodes[link.index]
&& !AStarAlgo::cantSkipNode (path.number, link.index, true)) { && !AStarAlgo::cantSkipNode (path.number, link.index, true)) {
indexes.emplace (link.index); indexes.emplace (link.index);
} }
@ -342,8 +342,8 @@ void GraphAnalyze::flood (const Vector &pos, const Vector &next, float range) {
auto testPos = m_isCrouch ? Vector { nextPos.x, nextPos.y, nextPos.z - 18.0f } : nextPos; auto testPos = m_isCrouch ? Vector { nextPos.x, nextPos.y, nextPos.z - 18.0f } : nextPos;
if ((graph.isNodeReacheable (targetPos, testPos) if ((graph.isNodeReacheable (targetPos, testPos)
&& graph.isNodeReacheable (testPos, targetPos)) || (graph.isNodeReacheableWithJump (testPos, targetPos) && graph.isNodeReacheable (testPos, targetPos)) || (graph.isNodeReacheableWithJump (testPos, targetPos)
&& graph.isNodeReacheableWithJump (targetPos, testPos))) { && graph.isNodeReacheableWithJump (targetPos, testPos))) {
graph.add (NodeAddFlag::Normal, m_isCrouch ? Vector { nextPos.x, nextPos.y, nextPos.z - 9.0f } : nextPos); graph.add (NodeAddFlag::Normal, m_isCrouch ? Vector { nextPos.x, nextPos.y, nextPos.z - 9.0f } : nextPos);
} }
} }
@ -364,7 +364,7 @@ void GraphAnalyze::markGoals () {
auto updateNodeFlags = [] (int type, StringRef classname) { auto updateNodeFlags = [] (int type, StringRef classname) {
game.searchEntities ("classname", classname, [&] (edict_t *ent) { game.searchEntities ("classname", classname, [&] (edict_t *ent) {
for (auto &path : graph) { for (auto &path : graph) {
const auto &bb = path.origin + Vector (1.0f, 1.0f, 1.0f); const auto &bb = path.origin + Vector (1.0f, 1.0f, 1.0f);
if (ent->v.absmin.x > bb.x || ent->v.absmin.y > bb.y) { if (ent->v.absmin.x > bb.x || ent->v.absmin.y > bb.y) {

View file

@ -97,9 +97,9 @@ void Bot::avoidGrenades () {
auto model = pent->v.model.str (9); auto model = pent->v.model.str (9);
if (m_preventFlashing < game.time () if (m_preventFlashing < game.time ()
&& m_personality == Personality::Rusher && m_personality == Personality::Rusher
&& m_difficulty == Difficulty::Expert && m_difficulty == Difficulty::Expert
&& model == kFlashbangModelName) { && model == kFlashbangModelName) {
// don't look at flash bang // don't look at flash bang
if (!(m_states & Sense::SeeingEnemy)) { if (!(m_states & Sense::SeeingEnemy)) {
@ -175,13 +175,13 @@ void Bot::checkBreakable (edict_t *touch) {
void Bot::checkBreakablesAround () { void Bot::checkBreakablesAround () {
if (!m_buyingFinished if (!m_buyingFinished
|| !cv_destroy_breakables_around || !cv_destroy_breakables_around
|| usesKnife () || usesKnife ()
|| rg.chance (25) || rg.chance (25)
|| !game.hasBreakables () || !game.hasBreakables ()
|| m_seeEnemyTime + 4.0f > game.time () || m_seeEnemyTime + 4.0f > game.time ()
|| !game.isNullEntity (m_enemy) || !game.isNullEntity (m_enemy)
|| !hasPrimaryWeapon ()) { || !hasPrimaryWeapon ()) {
return; return;
} }
@ -421,7 +421,7 @@ void Bot::updatePickups () {
pickupType = Pickup::DroppedC4; pickupType = Pickup::DroppedC4;
} }
else if ((isWeaponBox || classname.startsWith ("armoury_entity") || (isCSDM && classname.startsWith ("csdm"))) else if ((isWeaponBox || classname.startsWith ("armoury_entity") || (isCSDM && classname.startsWith ("csdm")))
&& !m_isUsingGrenade) { && !m_isUsingGrenade) {
allowPickup = true; allowPickup = true;
pickupType = Pickup::Weapon; pickupType = Pickup::Weapon;
@ -438,15 +438,15 @@ void Bot::updatePickups () {
const auto &secondaryProp = conf.getWeaponProp (secondary.id); const auto &secondaryProp = conf.getWeaponProp (secondary.id);
if (secondaryWeaponCarried < kPrimaryWeaponMinIndex if (secondaryWeaponCarried < kPrimaryWeaponMinIndex
&& (getAmmo (secondary.id) > 0.3 * secondaryProp.ammo1Max) && (getAmmo (secondary.id) > 0.3 * secondaryProp.ammo1Max)
&& model == "357ammobox.mdl") { && model == "357ammobox.mdl") {
allowPickup = false; allowPickup = false;
} }
else if (!m_isVIP && else if (!m_isVIP &&
primaryWeaponCarried >= kPrimaryWeaponMinIndex primaryWeaponCarried >= kPrimaryWeaponMinIndex
&& (getAmmo (primary.id) > 0.3 * primaryProp.ammo1Max) && (getAmmo (primary.id) > 0.3 * primaryProp.ammo1Max)
&& !m_isUsingGrenade && !hasShield ()) { && !m_isUsingGrenade && !hasShield ()) {
auto weaponType = conf.getWeaponType (primary.id); auto weaponType = conf.getWeaponType (primary.id);
@ -475,7 +475,7 @@ void Bot::updatePickups () {
else if (m_healthValue >= 100.0f && model == "medkit.mdl") { else if (m_healthValue >= 100.0f && model == "medkit.mdl") {
allowPickup = false; allowPickup = false;
} }
else if (pev->armorvalue >= 100.0f && (model == "kevlar.mdl"|| model == "battery.mdl" || model == "assault.mdl")) { else if (pev->armorvalue >= 100.0f && (model == "kevlar.mdl" || model == "battery.mdl" || model == "assault.mdl")) {
allowPickup = false; allowPickup = false;
} }
else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && model == kFlashbangModelName) { else if ((pev->weapons & cr::bit (Weapon::Flashbang)) && model == kFlashbangModelName) {
@ -570,10 +570,10 @@ void Bot::updatePickups () {
allowPickup = false; allowPickup = false;
if (!m_defendHostage && m_personality if (!m_defendHostage && m_personality
!= Personality::Rusher && m_difficulty >= Difficulty::Normal != Personality::Rusher && m_difficulty >= Difficulty::Normal
&& rg.chance (15) && rg.chance (15)
&& m_timeCamping + 15.0f < game.time () && m_timeCamping + 15.0f < game.time ()
&& numFriendsNear (pev->origin, 384.0f) < 3) { && numFriendsNear (pev->origin, 384.0f) < 3) {
const int index = findDefendNode (origin); const int index = findDefendNode (origin);
@ -640,7 +640,7 @@ void Bot::updatePickups () {
if (allowPickup) { if (allowPickup) {
for (auto &client : util.getClients ()) { for (auto &client : util.getClients ()) {
if ((client.flags & ClientFlags::Used) && !(client.ent->v.flags & FL_FAKECLIENT) && (client.flags & ClientFlags::Alive) && if ((client.flags & ClientFlags::Used) && !(client.ent->v.flags & FL_FAKECLIENT) && (client.flags & ClientFlags::Alive) &&
client.team == m_team && client.ent->v.origin.distanceSq (ent->v.origin) <= cr::sqrf (240.0f)) { client.team == m_team && client.ent->v.origin.distanceSq (ent->v.origin) <= cr::sqrf (240.0f)) {
allowPickup = false; allowPickup = false;
break; break;
} }
@ -874,9 +874,9 @@ void Bot::showChatterIcon (bool show, bool disconnect) {
void Bot::instantChatter (int type) { void Bot::instantChatter (int type) {
// this function sends instant chatter messages. // this function sends instant chatter messages.
if (!game.is (GameFlags::HasBotVoice) if (!game.is (GameFlags::HasBotVoice)
|| cv_radio_mode.as <int> () != 2 || cv_radio_mode.as <int> () != 2
|| !conf.hasChatterBank (type) || !conf.hasChatterBank (type)
|| !conf.hasChatterBank (Chatter::DiePain)) { || !conf.hasChatterBank (Chatter::DiePain)) {
return; return;
} }
@ -1068,9 +1068,9 @@ void Bot::checkMsgQueue () {
if (m_radioSelect != -1) { if (m_radioSelect != -1) {
if ((m_radioSelect != Radio::ReportingIn && m_forceRadio) if ((m_radioSelect != Radio::ReportingIn && m_forceRadio)
|| cv_radio_mode.as <int> () != 2 || cv_radio_mode.as <int> () != 2
|| !conf.hasChatterBank (m_radioSelect) || !conf.hasChatterBank (m_radioSelect)
|| !game.is (GameFlags::HasBotVoice)) { || !game.is (GameFlags::HasBotVoice)) {
if (m_radioSelect < Radio::GoGoGo) { if (m_radioSelect < Radio::GoGoGo) {
issueCommand ("radio1"); issueCommand ("radio1");
@ -1340,8 +1340,8 @@ void Bot::buyStuff () {
} }
if (selectedWeapon->id == Weapon::Shield if (selectedWeapon->id == Weapon::Shield
&& m_moneyAmount > limit[EcoLimit::ShieldGreater] && m_moneyAmount > limit[EcoLimit::ShieldGreater]
&& rg.chance (disrespectEconomicsPct)) { && rg.chance (disrespectEconomicsPct)) {
ignoreWeapon = true; ignoreWeapon = true;
} }
@ -1443,8 +1443,8 @@ void Bot::buyStuff () {
case BuyState::ArmorVestHelm: // if armor is damaged and bot has some money, buy some armor case BuyState::ArmorVestHelm: // if armor is damaged and bot has some money, buy some armor
if (pev->armorvalue < rg (50.0f, 80.0f) if (pev->armorvalue < rg (50.0f, 80.0f)
&& teamHasGoodEconomics && teamHasGoodEconomics
&& (isPistolMode || (teamHasGoodEconomics && hasPrimaryWeapon ()))) { && (isPistolMode || (teamHasGoodEconomics && hasPrimaryWeapon ()))) {
// if bot is rich, buy kevlar + helmet, else buy a single kevlar // if bot is rich, buy kevlar + helmet, else buy a single kevlar
if (m_moneyAmount > 1500 && !isWeaponRestricted (Weapon::ArmorHelm)) { if (m_moneyAmount > 1500 && !isWeaponRestricted (Weapon::ArmorHelm)) {
@ -1458,9 +1458,9 @@ void Bot::buyStuff () {
case BuyState::SecondaryWeapon: // if bot has still some money, buy a better secondary weapon case BuyState::SecondaryWeapon: // if bot has still some money, buy a better secondary weapon
if (isPistolMode if (isPistolMode
|| (isFirstRound && hasDefaultPistols && rg.chance (60)) || (isFirstRound && hasDefaultPistols && rg.chance (60))
|| (hasDefaultPistols && bots.getLastWinner () == m_team && m_moneyAmount > rg (2000, 3000)) || (hasDefaultPistols && bots.getLastWinner () == m_team && m_moneyAmount > rg (2000, 3000))
|| (hasPrimaryWeapon () && hasDefaultPistols && m_moneyAmount > rg (7500, 9000))) { || (hasPrimaryWeapon () && hasDefaultPistols && m_moneyAmount > rg (7500, 9000))) {
do { do {
pref--; pref--;
@ -1571,10 +1571,10 @@ void Bot::buyStuff () {
case BuyState::DefusalKit: // if bot is CT and we're on a bomb map, randomly buy the defuse kit case BuyState::DefusalKit: // if bot is CT and we're on a bomb map, randomly buy the defuse kit
if (game.mapIs (MapFlags::Demolition) if (game.mapIs (MapFlags::Demolition)
&& m_team == Team::CT && m_team == Team::CT
&& rg.chance (80) && rg.chance (80)
&& m_moneyAmount > 200 && m_moneyAmount > 200
&& !isWeaponRestricted (Weapon::Defuser)) { && !isWeaponRestricted (Weapon::Defuser)) {
if (isOldGame) { if (isOldGame) {
issueCommand ("buyequip;menuselect 6"); issueCommand ("buyequip;menuselect 6");
@ -1612,7 +1612,7 @@ void Bot::updateEmotions () {
if (m_nextEmotionUpdate > game.time ()) { if (m_nextEmotionUpdate > game.time ()) {
return; return;
} }
if (m_agressionLevel > m_baseAgressionLevel) { if (m_agressionLevel > m_baseAgressionLevel) {
m_agressionLevel -= 0.05f; m_agressionLevel -= 0.05f;
} }
@ -1642,11 +1642,11 @@ void Bot::overrideConditions () {
// check if we need to escape from bomb // check if we need to escape from bomb
if (game.mapIs (MapFlags::Demolition) if (game.mapIs (MapFlags::Demolition)
&& bots.isBombPlanted () && bots.isBombPlanted ()
&& m_isAlive && m_isAlive
&& tid != Task::EscapeFromBomb && tid != Task::EscapeFromBomb
&& tid != Task::Camp && tid != Task::Camp
&& isOutOfBombTimer ()) { && isOutOfBombTimer ()) {
completeTask (); // complete current task completeTask (); // complete current task
@ -1663,8 +1663,8 @@ void Bot::overrideConditions () {
const int nearestToEnemyPoint = graph.getNearest (m_enemy->v.origin); const int nearestToEnemyPoint = graph.getNearest (m_enemy->v.origin);
if (nearestToEnemyPoint != kInvalidNodeIndex if (nearestToEnemyPoint != kInvalidNodeIndex
&& nearestToEnemyPoint != m_currentNodeIndex && nearestToEnemyPoint != m_currentNodeIndex
&& cr::abs (graph[nearestToEnemyPoint].origin.z - m_enemy->v.origin.z) < 16.0f) { && cr::abs (graph[nearestToEnemyPoint].origin.z - m_enemy->v.origin.z) < 16.0f) {
if (tid != Task::MoveToPosition && !cr::fequal (getTask ()->desire, TaskPri::Hide)) { if (tid != Task::MoveToPosition && !cr::fequal (getTask ()->desire, TaskPri::Hide)) {
startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + length / (m_moveSpeed * 2.0f), true); startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + length / (m_moveSpeed * 2.0f), true);
@ -1686,8 +1686,8 @@ 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_shootTime - 0.4f <= game.time () && m_shootTime - 0.4f <= game.time ()
&& m_sniperStopTime > game.time ()) { && m_sniperStopTime > game.time ()) {
ignoreCollision (); ignoreCollision ();
@ -1699,11 +1699,11 @@ void Bot::overrideConditions () {
// special handling for reloading // special handling for reloading
if (!bots.isRoundOver () && if (!bots.isRoundOver () &&
tid == Task::Normal tid == Task::Normal
&& m_reloadState != Reload::None && m_reloadState != Reload::None
&& m_isReloading && m_isReloading
&& !isDucking () && !isDucking ()
&& !isInNarrowPlace ()) { && !isInNarrowPlace ()) {
if (m_reloadState != Reload::None || m_isReloading) { if (m_reloadState != Reload::None || m_isReloading) {
const auto maxClip = conf.findWeaponById (m_currentWeapon).maxClip; const auto maxClip = conf.findWeaponById (m_currentWeapon).maxClip;
@ -1901,8 +1901,8 @@ 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 (m_soundUpdateTime < game.time () if (m_soundUpdateTime < game.time ()
&& m_blindTime < game.time () && m_blindTime < game.time ()
&& m_seeEnemyTime + 1.0f < game.time ()) { && m_seeEnemyTime + 1.0f < game.time ()) {
updateHearing (); updateHearing ();
m_soundUpdateTime = game.time () + 0.25f; m_soundUpdateTime = game.time () + 0.25f;
@ -2037,14 +2037,14 @@ void Bot::filterTasks () {
// if half of the round is over, allow hunting // if half of the round is over, allow hunting
if (getCurrentTaskId () != Task::EscapeFromBomb if (getCurrentTaskId () != Task::EscapeFromBomb
&& game.isNullEntity (m_enemy) && game.isNullEntity (m_enemy)
&& !m_isVIP && !m_isVIP
&& bots.getRoundMidTime () < game.time () && bots.getRoundMidTime () < game.time ()
&& !m_hasHostage && !m_hasHostage
&& !m_isUsingGrenade && !m_isUsingGrenade
&& m_currentNodeIndex != graph.getNearest (m_lastEnemyOrigin) && m_currentNodeIndex != graph.getNearest (m_lastEnemyOrigin)
&& m_personality != Personality::Careful && m_personality != Personality::Careful
&& !cv_ignore_enemies) { && !cv_ignore_enemies) {
float desireLevel = 4096.0f - ((1.0f - tempAgression) * m_lastEnemyOrigin.distance (pev->origin)); float desireLevel = 4096.0f - ((1.0f - tempAgression) * m_lastEnemyOrigin.distance (pev->origin));
@ -2343,8 +2343,8 @@ void Bot::checkRadioQueue () {
// check if line of sight to object is not blocked (i.e. visible) // check if line of sight to object is not blocked (i.e. visible)
if (seesEntity (m_radioEntity->v.origin) || m_radioOrder == Radio::StickTogetherTeam) { if (seesEntity (m_radioEntity->v.origin) || m_radioOrder == Radio::StickTogetherTeam) {
if (game.isNullEntity (m_targetEntity) if (game.isNullEntity (m_targetEntity)
&& game.isNullEntity (m_enemy) && game.isNullEntity (m_enemy)
&& rg.chance (m_personality == Personality::Careful ? 80 : 20)) { && rg.chance (m_personality == Personality::Careful ? 80 : 20)) {
int numFollowers = 0; int numFollowers = 0;
@ -2453,8 +2453,8 @@ void Bot::checkRadioQueue () {
case Chatter::ScaredEmotion: case Chatter::ScaredEmotion:
case Chatter::PinnedDown: case Chatter::PinnedDown:
if (((game.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distanceSq < cr::sqrf (2048.0f) || !m_moveToC4) if (((game.isNullEntity (m_enemy) && seesEntity (m_radioEntity->v.origin)) || distanceSq < cr::sqrf (2048.0f) || !m_moveToC4)
&& rg.chance (50) && rg.chance (50)
&& m_seeEnemyTime + 4.0f < game.time ()) { && m_seeEnemyTime + 4.0f < game.time ()) {
m_fearLevel -= 0.1f; m_fearLevel -= 0.1f;
@ -2737,9 +2737,9 @@ void Bot::checkRadioQueue () {
// check if it's a ct command // check if it's a ct command
if (game.getTeam (m_radioEntity) == Team::CT if (game.getTeam (m_radioEntity) == Team::CT
&& m_team == Team::CT && m_team == Team::CT
&& util.isFakeClient (m_radioEntity) && util.isFakeClient (m_radioEntity)
&& bots.getPlantedBombSearchTimestamp () < game.time ()) { && bots.getPlantedBombSearchTimestamp () < game.time ()) {
float nearestDistanceSq = kInfiniteDistance; float nearestDistanceSq = kInfiniteDistance;
int bombPoint = kInvalidNodeIndex; int bombPoint = kInvalidNodeIndex;
@ -2836,8 +2836,8 @@ void Bot::tryHeadTowardRadioMessage () {
} }
if ((util.isFakeClient (m_radioEntity) if ((util.isFakeClient (m_radioEntity)
&& rg.chance (25) && rg.chance (25)
&& m_personality == Personality::Normal) || !(m_radioEntity->v.flags & FL_FAKECLIENT)) { && m_personality == Personality::Normal) || !(m_radioEntity->v.flags & FL_FAKECLIENT)) {
if (tid == Task::Pause || tid == Task::Camp) { if (tid == Task::Pause || tid == Task::Camp) {
getTask ()->time = game.time (); getTask ()->time = game.time ();
@ -2894,9 +2894,9 @@ void Bot::frame () {
const Vector &bombPosition = graph.getBombOrigin (); const Vector &bombPosition = graph.getBombOrigin ();
if (!m_hasProgressBar if (!m_hasProgressBar
&& getCurrentTaskId () != Task::EscapeFromBomb && getCurrentTaskId () != Task::EscapeFromBomb
&& pev->origin.distanceSq (bombPosition) < cr::sqrf (1540.0f) && pev->origin.distanceSq (bombPosition) < cr::sqrf (1540.0f)
&& !isBombDefusing (bombPosition)) { && !isBombDefusing (bombPosition)) {
m_itemIgnore = nullptr; m_itemIgnore = nullptr;
m_itemCheckTime = game.time (); m_itemCheckTime = game.time ();
@ -2981,9 +2981,9 @@ void Bot::update () {
} }
} }
else if (m_buyingFinished else if (m_buyingFinished
&& !(pev->maxspeed < 10.0f && tid != Task::PlantBomb && tid != Task::DefuseBomb) && !(pev->maxspeed < 10.0f && tid != Task::PlantBomb && tid != Task::DefuseBomb)
&& !cv_freeze_bots && !cv_freeze_bots
&& !graph.hasChanged ()) { && !graph.hasChanged ()) {
botMovement = true; botMovement = true;
} }
@ -3055,9 +3055,9 @@ void Bot::checkSpawnConditions () {
} }
if (m_difficulty >= Difficulty::Normal if (m_difficulty >= Difficulty::Normal
&& rg.chance (m_personality == Personality::Rusher ? 99 : 50) && rg.chance (m_personality == Personality::Rusher ? 99 : 50)
&& !m_isReloading && !m_isReloading
&& game.mapIs (MapFlags::HostageRescue | MapFlags::Demolition | MapFlags::Escape | MapFlags::Assassination)) { && game.mapIs (MapFlags::HostageRescue | MapFlags::Demolition | MapFlags::Escape | MapFlags::Assassination)) {
if (isKnifeMode ()) { if (isKnifeMode ()) {
dropCurrentWeapon (); dropCurrentWeapon ();
@ -3165,15 +3165,15 @@ void Bot::logic () {
pushChatterMessage (Chatter::VIPSpotted); pushChatterMessage (Chatter::VIPSpotted);
} }
else if (!hasFriendNearby else if (!hasFriendNearby
&& rg.chance (50) && rg.chance (50)
&& game.getTeam (m_enemy) != m_team && game.getTeam (m_enemy) != m_team
&& isGroupOfEnemies (m_enemy->v.origin, 2, 384.0f)) { && isGroupOfEnemies (m_enemy->v.origin, 2, 384.0f)) {
pushChatterMessage (Chatter::ScaredEmotion); pushChatterMessage (Chatter::ScaredEmotion);
} }
else if (!hasFriendNearby else if (!hasFriendNearby
&& rg.chance (40) && rg.chance (40)
&& (m_enemy->v.weapons & kSniperWeaponMask)) { && (m_enemy->v.weapons & kSniperWeaponMask)) {
pushChatterMessage (Chatter::SniperWarning); pushChatterMessage (Chatter::SniperWarning);
} }
@ -3620,7 +3620,7 @@ void Bot::updatePracticeDamage (edict_t *attacker, int damage) {
if (victimIndex == kInvalidNodeIndex) { if (victimIndex == kInvalidNodeIndex) {
victimIndex = findNearestNode (); victimIndex = findNearestNode ();
} }
if (m_healthValue > 20.0f) { if (m_healthValue > 20.0f) {
if (victimTeam == Team::Terrorist || victimTeam == Team::CT) { if (victimTeam == Team::Terrorist || victimTeam == Team::CT) {
practice.setDamage (victimIndex, victimIndex, victimIndex, cr::clamp (practice.getDamage (victimTeam, victimIndex, victimIndex), 0, kMaxDamageValue)); practice.setDamage (victimIndex, victimIndex, victimIndex, cr::clamp (practice.getDamage (victimTeam, victimIndex, victimIndex), 0, kMaxDamageValue));
@ -3824,8 +3824,8 @@ void Bot::runMovement () {
translateInput (); translateInput ();
engfuncs.pfnRunPlayerMove (pev->pContainingEntity, engfuncs.pfnRunPlayerMove (pev->pContainingEntity,
getRpmAngles (), m_moveSpeed, m_strafeSpeed, getRpmAngles (), m_moveSpeed, m_strafeSpeed,
0.0f, static_cast <uint16_t> (pev->button), static_cast <uint8_t> (pev->impulse), msecVal); 0.0f, static_cast <uint16_t> (pev->button), static_cast <uint8_t> (pev->impulse), msecVal);
// save our own copy of old buttons, since bot bot code is not running every frame now // save our own copy of old buttons, since bot bot code is not running every frame now
m_oldButtons = pev->button; m_oldButtons = pev->button;
@ -3898,11 +3898,11 @@ void Bot::updateHearing () {
// loop through all enemy clients to check for hearable stuff // loop through all enemy clients to check for hearable stuff
for (const auto &client : util.getClients ()) { for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) if (!(client.flags & ClientFlags::Used)
|| !(client.flags & ClientFlags::Alive) || !(client.flags & ClientFlags::Alive)
|| client.ent == ent () || client.ent == ent ()
|| client.team == m_team || client.team == m_team
|| !client.ent || !client.ent
|| client.noise.last < game.time ()) { || client.noise.last < game.time ()) {
continue; continue;
} }
@ -3926,12 +3926,12 @@ void Bot::updateHearing () {
if (util.isPlayer (m_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 ()
&& m_currentWeapon != Weapon::C4 && m_currentWeapon != Weapon::C4
&& m_currentWeapon != Weapon::Explosive && m_currentWeapon != Weapon::Explosive
&& m_currentWeapon != Weapon::Smoke && m_currentWeapon != Weapon::Smoke
&& m_currentWeapon != Weapon::Flashbang && m_currentWeapon != Weapon::Flashbang
&& !isKnifeMode ()) { && !isKnifeMode ()) {
selectBestWeapon (); selectBestWeapon ();
} }
@ -3995,10 +3995,10 @@ 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 == m_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 (m_hearedEnemy->v.origin)) { && isPenetrableObstacle (m_hearedEnemy->v.origin)) {
m_enemy = m_hearedEnemy; m_enemy = m_hearedEnemy;
m_lastEnemy = m_hearedEnemy; m_lastEnemy = m_hearedEnemy;
@ -4022,11 +4022,11 @@ void Bot::enteredBuyZone (int buyState) {
// if bot is in buy zone, try to buy ammo for this weapon... // if bot is in buy zone, try to buy ammo for this weapon...
if (m_seeEnemyTime + 12.0f < game.time () if (m_seeEnemyTime + 12.0f < game.time ()
&& m_lastEquipTime + 15.0f < game.time () && m_lastEquipTime + 15.0f < game.time ()
&& m_inBuyZone && m_inBuyZone
&& (bots.getRoundStartTime () + rg (10.0f, 20.0f) + mp_buytime.as <float> () < game.time ()) && (bots.getRoundStartTime () + rg (10.0f, 20.0f) + mp_buytime.as <float> () < game.time ())
&& !bots.isBombPlanted () && !bots.isBombPlanted ()
&& m_moneyAmount > econLimit[EcoLimit::PrimaryGreater]) { && m_moneyAmount > econLimit[EcoLimit::PrimaryGreater]) {
m_ignoreBuyDelay = true; m_ignoreBuyDelay = true;
m_buyingFinished = false; m_buyingFinished = false;
@ -4095,7 +4095,7 @@ bool Bot::isBombDefusing (const Vector &bombOrigin) {
if (client.team == m_team) { if (client.team == m_team) {
// if close enough, mark as progressing // if close enough, mark as progressing
if (bombDistanceSq < distanceToBomb && ((client.ent->v.button | client.ent->v.oldbuttons) & IN_USE)) { if (bombDistanceSq < distanceToBomb && ((client.ent->v.button | client.ent->v.oldbuttons) & IN_USE)) {
defusingInProgress = true; defusingInProgress = true;
break; break;
} }
@ -4107,18 +4107,18 @@ bool Bot::isBombDefusing (const Vector &bombOrigin) {
float Bot::getShiftSpeed () { float Bot::getShiftSpeed () {
if (getCurrentTaskId () == Task::SeekCover if (getCurrentTaskId () == Task::SeekCover
|| (m_aimFlags & AimFlags::Enemy) || (m_aimFlags & AimFlags::Enemy)
|| isDucking () || isDucking ()
|| (pev->button & IN_DUCK) || (pev->button & IN_DUCK)
|| (m_oldButtons & IN_DUCK) || (m_oldButtons & IN_DUCK)
|| (m_currentTravelFlags & PathFlag::Jump) || (m_currentTravelFlags & PathFlag::Jump)
|| (m_pathFlags & NodeFlag::Ladder) || (m_pathFlags & NodeFlag::Ladder)
|| isOnLadder () || isOnLadder ()
|| isInWater () || isInWater ()
|| isKnifeMode () || isKnifeMode ()
|| m_isStuck || m_isStuck
|| m_numEnemiesLeft <= 0 || m_numEnemiesLeft <= 0
|| !m_lostReachableNodeTimer.elapsed ()) { || !m_lostReachableNodeTimer.elapsed ()) {
return pev->maxspeed; return pev->maxspeed;
} }

View file

@ -203,7 +203,7 @@ void Bot::prepareChatMessage (StringRef message) {
// get bot's victim // get bot's victim
auto getMyVictim = [&] () -> String {; auto getMyVictim = [&] () -> String {;
return humanizedName (game.indexOfPlayer (m_lastVictim)); return humanizedName (game.indexOfPlayer (m_lastVictim));
}; };
// get the game name alias // get the game name alias
@ -336,9 +336,9 @@ void Bot::checkForChat () {
// bot chatting turned on? // bot chatting turned on?
if (rg.chance (cv_chat_percent.as <int> ()) if (rg.chance (cv_chat_percent.as <int> ())
&& m_lastChatTime + rg (6.0f, 10.0f) < game.time () && m_lastChatTime + rg (6.0f, 10.0f) < game.time ()
&& bots.getLastChatTimestamp () + rg (2.5f, 5.0f) < game.time () && bots.getLastChatTimestamp () + rg (2.5f, 5.0f) < game.time ()
&& !isReplyingToChat ()) { && !isReplyingToChat ()) {
if (conf.hasChatBank (Chat::Dead)) { if (conf.hasChatBank (Chat::Dead)) {
StringRef phrase = conf.pickRandomFromChatBank (Chat::Dead); StringRef phrase = conf.pickRandomFromChatBank (Chat::Dead);

View file

@ -233,9 +233,9 @@ bool Bot::seesEnemy (edict_t *player) {
} }
if ((ignoreFieldOfView || isInViewCone (player->v.origin)) if ((ignoreFieldOfView || isInViewCone (player->v.origin))
&& frustum.check (m_viewFrustum, player) && frustum.check (m_viewFrustum, player)
&& !isBehindSmokeClouds (player->v.origin) && !isBehindSmokeClouds (player->v.origin)
&& checkBodyParts (player)) { && checkBodyParts (player)) {
return true; return true;
} }
return false; return false;
@ -275,7 +275,7 @@ bool Bot::lookupEnemies () {
&& m_shootTime + 1.5f > game.time (); && m_shootTime + 1.5f > game.time ();
if (!(m_aimFlags & (AimFlags::Enemy | AimFlags::PredictPath | AimFlags::Danger)) if (!(m_aimFlags & (AimFlags::Enemy | AimFlags::PredictPath | AimFlags::Danger))
&& !denyLastEnemy && seesEntity (m_lastEnemyOrigin, true)) { && !denyLastEnemy && seesEntity (m_lastEnemyOrigin, true)) {
m_aimFlags |= AimFlags::LastEnemy; m_aimFlags |= AimFlags::LastEnemy;
} }
} }
@ -287,9 +287,9 @@ bool Bot::lookupEnemies () {
// is player is alive // is player is alive
if (m_enemyUpdateTime > game.time () if (m_enemyUpdateTime > game.time ()
&& m_enemy->v.origin.distanceSq (pev->origin) < nearestDistanceSq && m_enemy->v.origin.distanceSq (pev->origin) < nearestDistanceSq
&& util.isAlive (player) && util.isAlive (player)
&& seesEnemy (player)) { && seesEnemy (player)) {
newEnemy = player; newEnemy = player;
} }
@ -336,10 +336,10 @@ bool Bot::lookupEnemies () {
// search the world for players... // search the world for players...
for (const auto &client : util.getClients ()) { for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) if (!(client.flags & ClientFlags::Used)
|| !(client.flags & ClientFlags::Alive) || !(client.flags & ClientFlags::Alive)
|| client.team == m_team || client.team == m_team
|| client.ent == ent () || client.ent == ent ()
|| !client.ent) { || !client.ent) {
continue; continue;
} }
player = client.ent; player = client.ent;
@ -374,7 +374,7 @@ bool Bot::lookupEnemies () {
} }
} }
m_enemyUpdateTime = game.time () + (usesKnife () ? 1.25f : 0.85f); m_enemyUpdateTime = game.time () + (usesKnife () ? 1.25f : 0.85f);
if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) { if (game.isNullEntity (newEnemy) && !game.isNullEntity (shieldEnemy)) {
newEnemy = shieldEnemy; newEnemy = shieldEnemy;
} }
@ -451,9 +451,9 @@ bool Bot::lookupEnemies () {
} }
if (other->m_seeEnemyTime + 2.0f < game.time () if (other->m_seeEnemyTime + 2.0f < game.time ()
&& game.isNullEntity (other->m_lastEnemy) && game.isNullEntity (other->m_lastEnemy)
&& util.isVisible (pev->origin, other->ent ()) && util.isVisible (pev->origin, other->ent ())
&& other->isInViewCone (pev->origin)) { && other->isInViewCone (pev->origin)) {
other->m_lastEnemy = newEnemy; other->m_lastEnemy = newEnemy;
other->m_lastEnemyOrigin = newEnemy->v.origin; other->m_lastEnemyOrigin = newEnemy->v.origin;
@ -496,8 +496,8 @@ bool Bot::lookupEnemies () {
// if no enemy visible check if last one shoot able through wall // if no enemy visible check if last one shoot able through wall
if (cv_shoots_thru_walls if (cv_shoots_thru_walls
&& rg.chance (conf.getDifficultyTweaks (m_difficulty)->seenThruPct) && rg.chance (conf.getDifficultyTweaks (m_difficulty)->seenThruPct)
&& isPenetrableObstacle (newEnemy->v.origin)) { && isPenetrableObstacle (newEnemy->v.origin)) {
m_seeEnemyTime = game.time (); m_seeEnemyTime = game.time ();
@ -514,13 +514,13 @@ bool Bot::lookupEnemies () {
// check if bots should reload... // check if bots should reload...
if ((m_aimFlags <= AimFlags::PredictPath if ((m_aimFlags <= AimFlags::PredictPath
&& m_seeEnemyTime + 3.0f < game.time () && m_seeEnemyTime + 3.0f < game.time ()
&& !(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy)) && !(m_states & (Sense::SeeingEnemy | Sense::HearingEnemy))
&& game.isNullEntity (m_lastEnemy) && game.isNullEntity (m_lastEnemy)
&& game.isNullEntity (m_enemy) && game.isNullEntity (m_enemy)
&& getCurrentTaskId () != Task::ShootBreakable && getCurrentTaskId () != Task::ShootBreakable
&& getCurrentTaskId () != Task::PlantBomb && getCurrentTaskId () != Task::PlantBomb
&& getCurrentTaskId () != Task::DefuseBomb) || bots.isRoundOver ()) { && getCurrentTaskId () != Task::DefuseBomb) || bots.isRoundOver ()) {
if (!m_reloadState) { if (!m_reloadState) {
m_reloadState = Reload::Primary; m_reloadState = Reload::Primary;
@ -550,7 +550,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)); 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));
const auto &aimError = conf.getDifficultyTweaks (m_difficulty) ->aimError; 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)); m_aimLastError += Vector (rg (-aimError.x, aimError.x), rg (-aimError.y, aimError.y), rg (-aimError.z, aimError.z));
m_aimErrorTime = game.time () + rg (0.4f, 0.8f); m_aimErrorTime = game.time () + rg (0.4f, 0.8f);
@ -661,7 +661,7 @@ Vector Bot::getCustomHeight (float distance) {
{ 0.0f, 0.0f, 0.0f }, // melee { 0.0f, 0.0f, 0.0f }, // melee
{ 0.5f, -0.1f, -1.5f }, // pistol { 0.5f, -0.1f, -1.5f }, // pistol
{ 6.5f, 6.0f, -2.0f }, // shotgun { 6.5f, 6.0f, -2.0f }, // shotgun
{ 0.5f -7.5f, -9.5f }, // zoomrifle { 0.5f - 7.5f, -9.5f }, // zoomrifle
{ 0.5f, -7.5f, -9.5f }, // rifle { 0.5f, -7.5f, -9.5f }, // rifle
{ 0.5f, -7.5f, -9.5f }, // smg { 0.5f, -7.5f, -9.5f }, // smg
{ 0.0f, -2.5f, -6.0f }, // sniper { 0.0f, -2.5f, -6.0f }, // sniper
@ -714,7 +714,7 @@ bool Bot::isFriendInLineOfFire (float distance) {
const auto friendDistanceSq = client.ent->v.origin.distanceSq (pev->origin); const auto friendDistanceSq = client.ent->v.origin.distanceSq (pev->origin);
if (friendDistanceSq <= distanceSq if (friendDistanceSq <= distanceSq
&& util.getConeDeviation (ent (), client.ent->v.origin) > friendDistanceSq / (friendDistanceSq + cr::sqrf (33.0f))) { && util.getConeDeviation (ent (), client.ent->v.origin) > friendDistanceSq / (friendDistanceSq + cr::sqrf (33.0f))) {
return true; return true;
} }
} }
@ -1010,7 +1010,7 @@ void Bot::selectWeapons (float distance, int index, int id, int choosen) {
// 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) {
if (!cr::fzero (pev->velocity.x) || !cr::fzero (pev->velocity.y) || !cr::fzero (pev->velocity.z)) { if (!cr::fzero (pev->velocity.x) || !cr::fzero (pev->velocity.y) || !cr::fzero (pev->velocity.z)) {
m_moveSpeed = 0.0f; m_moveSpeed = 0.0f;
@ -1119,12 +1119,12 @@ void Bot::fireWeapons () {
// use knife if near and good difficulty (l33t dude!) // use knife if near and good difficulty (l33t dude!)
if (cv_stab_close_enemies && m_difficulty >= Difficulty::Normal if (cv_stab_close_enemies && m_difficulty >= Difficulty::Normal
&& m_healthValue > 80.0f && m_healthValue > 80.0f
&& !game.isNullEntity (m_enemy) && !game.isNullEntity (m_enemy)
&& m_healthValue >= m_enemy->v.health && m_healthValue >= m_enemy->v.health
&& distance < 100.0f && distance < 100.0f
&& !isOnLadder () && !isOnLadder ()
&& !isGroupOfEnemies (pev->origin)) { && !isGroupOfEnemies (pev->origin)) {
selectWeapons (distance, selectIndex, selectId, choosenWeapon); selectWeapons (distance, selectIndex, selectId, choosenWeapon);
return; return;
@ -1364,8 +1364,8 @@ void Bot::attackMovement () {
// 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;
} }
m_lastFightStyleCheck = game.time (); m_lastFightStyleCheck = game.time ();
@ -1446,10 +1446,10 @@ void Bot::attackMovement () {
} }
if (m_difficulty >= Difficulty::Normal if (m_difficulty >= Difficulty::Normal
&& (m_jumpTime + 5.0f < game.time () && (m_jumpTime + 5.0f < game.time ()
&& isOnFloor () && isOnFloor ()
&& rg (0, 1000) < (m_isReloading ? 8 : 2) && rg (0, 1000) < (m_isReloading ? 8 : 2)
&& pev->velocity.length2d () > 150.0f) && !usesSniper ()) { && pev->velocity.length2d () > 150.0f) && !usesSniper ()) {
pev->button |= IN_JUMP; pev->button |= IN_JUMP;
} }
@ -1461,14 +1461,14 @@ void Bot::attackMovement () {
m_duckTime = game.time () + m_frameInterval * 3.0f; m_duckTime = game.time () + m_frameInterval * 3.0f;
} }
else if ((distance > kSprayDistanceX2 && hasPrimaryWeapon ()) else if ((distance > kSprayDistanceX2 && hasPrimaryWeapon ())
&& isFullView && isFullView
&& getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::SeekCover
&& getCurrentTaskId () != Task::Hunt) { && getCurrentTaskId () != Task::Hunt) {
const int enemyNearestIndex = graph.getNearest (m_enemy->v.origin); const int enemyNearestIndex = graph.getNearest (m_enemy->v.origin);
if (vistab.visible (m_currentNodeIndex, enemyNearestIndex, VisIndex::Crouch) if (vistab.visible (m_currentNodeIndex, enemyNearestIndex, VisIndex::Crouch)
&& vistab.visible (enemyNearestIndex, m_currentNodeIndex, VisIndex::Crouch)) { && vistab.visible (enemyNearestIndex, m_currentNodeIndex, VisIndex::Crouch)) {
m_duckTime = game.time () + m_frameInterval * 3.0f; m_duckTime = game.time () + m_frameInterval * 3.0f;
} }
} }
@ -1846,11 +1846,11 @@ void Bot::checkReload () {
// we're should not reload, while doing next tasks // we're should not reload, while doing next tasks
const bool uninterruptibleTask = (tid == Task::PlantBomb const bool uninterruptibleTask = (tid == Task::PlantBomb
|| tid == Task::DefuseBomb || tid == Task::DefuseBomb
|| tid == Task::PickupItem || tid == Task::PickupItem
|| tid == Task::ThrowExplosive || tid == Task::ThrowExplosive
|| tid == Task::ThrowFlashbang || tid == Task::ThrowFlashbang
|| tid == Task::ThrowSmoke); || tid == Task::ThrowSmoke);
// do not check for reload // do not check for reload
if (uninterruptibleTask || m_isUsingGrenade || usesKnife ()) { if (uninterruptibleTask || m_isUsingGrenade || usesKnife ()) {
@ -2130,10 +2130,10 @@ void Bot::checkGrenadesThrow () {
// special condition if we're have valid current enemy // special condition if we're have valid current enemy
if (!isGrenadeMode && ((m_states & Sense::SeeingEnemy) if (!isGrenadeMode && ((m_states & Sense::SeeingEnemy)
&& util.isAlive (m_enemy) && util.isAlive (m_enemy)
&& ((m_enemy->v.button | m_enemy->v.oldbuttons) & IN_ATTACK) && ((m_enemy->v.button | m_enemy->v.oldbuttons) & IN_ATTACK)
&& util.isVisible (pev->origin, m_enemy)) && util.isVisible (pev->origin, m_enemy))
&& util.isInViewCone (pev->origin, m_enemy)) { && util.isInViewCone (pev->origin, m_enemy)) {
// do not throw away grenades if anyone is attacking us // do not throw away grenades if anyone is attacking us
distanceSq = kInfiniteDistance; distanceSq = kInfiniteDistance;
@ -2201,7 +2201,8 @@ void Bot::checkGrenadesThrow () {
} }
break; break;
case Weapon::Flashbang: { case Weapon::Flashbang:
{
const int nearest = graph.getNearest (m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin); const int nearest = graph.getNearest (m_lastEnemy->v.velocity.get2d () + m_lastEnemy->v.origin);
if (nearest != kInvalidNodeIndex) { if (nearest != kInvalidNodeIndex) {

View file

@ -103,9 +103,9 @@ int BotControl::cmdFill () {
return BotCommandResult::BadFormat; return BotCommandResult::BadFormat;
} }
bots.serverFill (arg <int> (team), bots.serverFill (arg <int> (team),
hasArg (personality) ? arg <int> (personality) : -1, hasArg (personality) ? arg <int> (personality) : -1,
hasArg (difficulty) ? arg <int> (difficulty) : -1, hasArg (difficulty) ? arg <int> (difficulty) : -1,
hasArg (count) ? arg <int> (count) - 1 : -1); hasArg (count) ? arg <int> (count) - 1 : -1);
return BotCommandResult::Handled; return BotCommandResult::Handled;
} }
@ -1299,8 +1299,8 @@ int BotControl::menuCommands (int item) {
case 1: case 1:
case 2: case 2:
if (util.findNearestPlayer (reinterpret_cast <void **> (&m_djump), m_ent, 600.0f, true, true, true, true, false) if (util.findNearestPlayer (reinterpret_cast <void **> (&m_djump), m_ent, 600.0f, true, true, true, true, false)
&& !m_djump->m_hasC4 && !m_djump->m_hasC4
&& !m_djump->m_hasHostage) { && !m_djump->m_hasHostage) {
if (item == 1) { if (item == 1) {
m_djump->startDoubleJump (m_ent); m_djump->startDoubleJump (m_ent);

View file

@ -169,7 +169,7 @@ int BotGraph::clearConnections (int index) {
// leave alone ladder connections and don't remove jump connections.. // leave alone ladder connections and don't remove jump connections..
if (((path.flags & NodeFlag::Ladder) if (((path.flags & NodeFlag::Ladder)
&& (m_paths[prev.index].flags & NodeFlag::Ladder)) || (path.links[prev.number].flags & PathFlag::Jump)) { && (m_paths[prev.index].flags & NodeFlag::Ladder)) || (path.links[prev.number].flags & PathFlag::Jump)) {
return false; return false;
} }
@ -217,8 +217,8 @@ int BotGraph::clearConnections (int index) {
if (exists (top.index) && exists (sorted[0].index) && exists (sorted[1].index)) { if (exists (top.index) && exists (sorted[0].index) && exists (sorted[1].index)) {
if ((sorted[1].angles - top.angles < 80.0f || 360.0f - (sorted[1].angles - top.angles) < 80.0f) if ((sorted[1].angles - top.angles < 80.0f || 360.0f - (sorted[1].angles - top.angles) < 80.0f)
&& (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder)) && (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder))
&& !(path.links[sorted[0].number].flags & PathFlag::Jump)) { && !(path.links[sorted[0].number].flags & PathFlag::Jump)) {
if ((sorted[1].distance + top.distance) * 1.1f / 2.0f < sorted[0].distance) { if ((sorted[1].distance + top.distance) * 1.1f / 2.0f < sorted[0].distance) {
if (path.links[sorted[0].number].index == sorted[0].index) { if (path.links[sorted[0].number].index == sorted[0].index) {
@ -266,7 +266,7 @@ int BotGraph::clearConnections (int index) {
// leave alone ladder connections and don't remove jump connections.. // leave alone ladder connections and don't remove jump connections..
if (((path.flags & NodeFlag::Ladder) if (((path.flags & NodeFlag::Ladder)
&& (m_paths[cur.index].flags & NodeFlag::Ladder)) || (path.links[cur.number].flags & PathFlag::Jump)) { && (m_paths[cur.index].flags & NodeFlag::Ladder)) || (path.links[cur.number].flags & PathFlag::Jump)) {
return false; return false;
} }
@ -300,8 +300,8 @@ int BotGraph::clearConnections (int index) {
else if (cur.distance < prev.distance * 1.1f) { else if (cur.distance < prev.distance * 1.1f) {
// leave alone ladder connections and don't remove jump connections.. // leave alone ladder connections and don't remove jump connections..
if (((path.flags & NodeFlag::Ladder) if (((path.flags & NodeFlag::Ladder)
&& (m_paths[prev.index].flags & NodeFlag::Ladder)) && (m_paths[prev.index].flags & NodeFlag::Ladder))
|| (path.links[prev.number].flags & PathFlag::Jump)) { || (path.links[prev.number].flags & PathFlag::Jump)) {
return false; return false;
} }
@ -349,8 +349,8 @@ int BotGraph::clearConnections (int index) {
if (exists (top.index) && exists (sorted[0].index)) { if (exists (top.index) && exists (sorted[0].index)) {
if ((top.angles - sorted[0].angles < 40.0f || (360.0f - top.angles - sorted[0].angles) < 40.0f) if ((top.angles - sorted[0].angles < 40.0f || (360.0f - top.angles - sorted[0].angles) < 40.0f)
&& (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder)) && (!(m_paths[sorted[0].index].flags & NodeFlag::Ladder) || !(path.flags & NodeFlag::Ladder))
&& !(path.links[sorted[0].number].flags & PathFlag::Jump)) { && !(path.links[sorted[0].number].flags & PathFlag::Jump)) {
if (top.distance * 1.1f < sorted[0].distance) { if (top.distance * 1.1f < sorted[0].distance) {
if (path.links[sorted[0].number].index == sorted[0].index) { if (path.links[sorted[0].number].index == sorted[0].index) {
@ -435,7 +435,7 @@ void BotGraph::addPath (int addIndex, int pathIndex, float distance) {
if (link.index == kInvalidNodeIndex) { if (link.index == kInvalidNodeIndex) {
link.index = static_cast <int16_t> (pathIndex); link.index = static_cast <int16_t> (pathIndex);
link.distance = integerDistance; link.distance = integerDistance;
msg ("Path added from %d to %d.", addIndex, pathIndex); msg ("Path added from %d to %d.", addIndex, pathIndex);
return; return;
} }
@ -832,9 +832,9 @@ void BotGraph::add (int type, const Vector &pos) {
game.testLine (newOrigin, calc.origin, TraceIgnore::Monsters, m_editor, &tr); game.testLine (newOrigin, calc.origin, TraceIgnore::Monsters, m_editor, &tr);
if (cr::fequal (tr.flFraction, 1.0f) if (cr::fequal (tr.flFraction, 1.0f)
&& cr::abs (newOrigin.x - calc.origin.x) < 64.0f && cr::abs (newOrigin.x - calc.origin.x) < 64.0f
&& cr::abs (newOrigin.y - calc.origin.y) < 64.0f && cr::abs (newOrigin.y - calc.origin.y) < 64.0f
&& cr::abs (newOrigin.z - calc.origin.z) < m_autoPathDistance) { && cr::abs (newOrigin.z - calc.origin.z) < m_autoPathDistance) {
const float distance = newOrigin.distance2d (calc.origin); const float distance = newOrigin.distance2d (calc.origin);
@ -1527,7 +1527,7 @@ void BotGraph::syncInitLightLevels () {
// update light levels for all nodes // update light levels for all nodes
for (auto &path : m_paths) { for (auto &path : m_paths) {
path.light = illum.getLightLevel (path.origin + Vector { 0.0f, 0.0f, 16.0f} ); path.light = illum.getLightLevel (path.origin + Vector { 0.0f, 0.0f, 16.0f });
} }
m_lightChecked = true; m_lightChecked = true;
@ -1796,7 +1796,7 @@ bool BotGraph::loadGraphData () {
vistab.load (); // load/initialize visibility vistab.load (); // load/initialize visibility
populateNodes (); populateNodes ();
if (exten.mapSize > 0) { if (exten.mapSize > 0) {
int mapSize = getBspSize (); int mapSize = getBspSize ();
@ -2093,8 +2093,8 @@ void BotGraph::frame () {
// check if node is within a distance, and is visible // check if node is within a distance, and is visible
if (distanceSq < cr::sqrf (cv_graph_draw_distance.as <float> ()) if (distanceSq < cr::sqrf (cv_graph_draw_distance.as <float> ())
&& ((util.isVisible (path.origin, m_editor) && ((util.isVisible (path.origin, m_editor)
&& util.isInViewCone (path.origin, m_editor)) || !util.isAlive (m_editor) || distanceSq < cr::sqrf (64.0f))) { && util.isInViewCone (path.origin, m_editor)) || !util.isAlive (m_editor) || distanceSq < cr::sqrf (64.0f))) {
// check the distance // check the distance
if (distanceSq < nearestDistanceSq) { if (distanceSq < nearestDistanceSq) {
@ -2211,7 +2211,7 @@ void BotGraph::frame () {
// draw a paths, camplines and danger directions for nearest node // draw a paths, camplines and danger directions for nearest node
if (nearestDistanceSq < cr::clamp (m_paths[nearestIndex].radius, cr::sqrf (56.0f), cr::sqrf (90.0f)) if (nearestDistanceSq < cr::clamp (m_paths[nearestIndex].radius, cr::sqrf (56.0f), cr::sqrf (90.0f))
&& m_pathDisplayTime < game.time ()) { && m_pathDisplayTime < game.time ()) {
m_pathDisplayTime = game.time () + 0.96f; m_pathDisplayTime = game.time () + 0.96f;
@ -2341,17 +2341,17 @@ void BotGraph::frame () {
} }
} }
flags.assignf ("%s%s%s%s%s%s%s%s%s%s%s%s", flags.assignf ("%s%s%s%s%s%s%s%s%s%s%s%s",
(p.flags & NodeFlag::Lift) ? " LIFT" : "", (p.flags & NodeFlag::Lift) ? " LIFT" : "",
(p.flags & NodeFlag::Crouch) ? " CROUCH" : "", (p.flags & NodeFlag::Crouch) ? " CROUCH" : "",
(p.flags & NodeFlag::Camp) ? " CAMP" : "", (p.flags & NodeFlag::Camp) ? " CAMP" : "",
(p.flags & NodeFlag::TerroristOnly) ? " TERRORIST" : "", (p.flags & NodeFlag::TerroristOnly) ? " TERRORIST" : "",
(p.flags & NodeFlag::CTOnly) ? " CT" : "", (p.flags & NodeFlag::CTOnly) ? " CT" : "",
(p.flags & NodeFlag::Sniper) ? " SNIPER" : "", (p.flags & NodeFlag::Sniper) ? " SNIPER" : "",
(p.flags & NodeFlag::Goal) ? " GOAL" : "", (p.flags & NodeFlag::Goal) ? " GOAL" : "",
(p.flags & NodeFlag::Ladder) ? " LADDER" : "", (p.flags & NodeFlag::Ladder) ? " LADDER" : "",
(p.flags & NodeFlag::Rescue) ? " RESCUE" : "", (p.flags & NodeFlag::Rescue) ? " RESCUE" : "",
(p.flags & NodeFlag::DoubleJump) ? " JUMPHELP" : "", (p.flags & NodeFlag::DoubleJump) ? " JUMPHELP" : "",
(p.flags & NodeFlag::NoHostage) ? " NOHOSTAGE" : "", jumpPoint ? " JUMP" : ""); (p.flags & NodeFlag::NoHostage) ? " NOHOSTAGE" : "", jumpPoint ? " JUMP" : "");
if (flags.empty ()) { if (flags.empty ()) {
flags.assign ("(none)"); flags.assign ("(none)");
@ -2359,9 +2359,9 @@ void BotGraph::frame () {
// show the information about that point // show the information about that point
message.assignf (" %s node:\n" message.assignf (" %s node:\n"
" Node %d of %d, Radius: %.1f, Light: %s\n" " Node %d of %d, Radius: %.1f, Light: %s\n"
" Flags: %s\n" " Flags: %s\n"
" Origin: (%.1f, %.1f, %.1f)\n", type, node, m_paths.length () - 1, p.radius, p.light == kInvalidLightLevel ? "Invalid" : strings.format ("%1.f", p.light), flags, p.origin.x, p.origin.y, p.origin.z); " Origin: (%.1f, %.1f, %.1f)\n", type, node, m_paths.length () - 1, p.radius, p.light == kInvalidLightLevel ? "Invalid" : strings.format ("%1.f", p.light), flags, p.origin.x, p.origin.y, p.origin.z);
return message; return message;
}; };
@ -2386,8 +2386,8 @@ void BotGraph::frame () {
String practiceText; String practiceText;
practiceText.assignf (" Node practice data (index / damage):\n" practiceText.assignf (" Node practice data (index / damage):\n"
" CT: %d / %d\n" " CT: %d / %d\n"
" T: %d / %d\n\n", dangerIndexCT, dangerIndexCT != kInvalidNodeIndex ? practice.getDamage (Team::CT, nearestIndex, dangerIndexCT) : 0, dangerIndexT, dangerIndexT != kInvalidNodeIndex ? practice.getDamage (Team::Terrorist, nearestIndex, dangerIndexT) : 0); " T: %d / %d\n\n", dangerIndexCT, dangerIndexCT != kInvalidNodeIndex ? practice.getDamage (Team::CT, nearestIndex, dangerIndexCT) : 0, dangerIndexT, dangerIndexT != kInvalidNodeIndex ? practice.getDamage (Team::Terrorist, nearestIndex, dangerIndexT) : 0);
sendHudMessage ({ 255, 255, 255 }, 0.0f, 0.16f, practiceText + timeMessage); sendHudMessage ({ 255, 255, 255 }, 0.0f, 0.16f, practiceText + timeMessage);
} }

View file

@ -34,43 +34,43 @@ plugin_info_t Plugin_info = {
// compilers can't create lambdas with vaargs, so put this one in it's own namespace // compilers can't create lambdas with vaargs, so put this one in it's own namespace
namespace Hooks { namespace Hooks {
void handler_engClientCommand (edict_t *ent, char const *format, ...) { void handler_engClientCommand (edict_t *ent, char const *format, ...) {
// this function forces the client whose player entity is ent to issue a client command. // this function forces the client whose player entity is ent to issue a client command.
// How it works is that clients all have a argv global string in their client DLL that // How it works is that clients all have a argv global string in their client DLL that
// stores the command string; if ever that string is filled with characters, the client DLL // stores the command string; if ever that string is filled with characters, the client DLL
// sends it to the engine as a command to be executed. When the engine has executed that // sends it to the engine as a command to be executed. When the engine has executed that
// command, this argv string is reset to zero. Here is somehow a curious implementation of // command, this argv string is reset to zero. Here is somehow a curious implementation of
// ClientCommand: the engine sets the command it wants the client to issue in his argv, then // ClientCommand: the engine sets the command it wants the client to issue in his argv, then
// the client DLL sends it back to the engine, the engine receives it then executes the // the client DLL sends it back to the engine, the engine receives it then executes the
// command therein. Don't ask me why we need all this complicated crap. Anyhow since bots have // command therein. Don't ask me why we need all this complicated crap. Anyhow since bots have
// no client DLL, be certain never to call this function upon a bot entity, else it will just // no client DLL, be certain never to call this function upon a bot entity, else it will just
// make the server crash. Since hordes of uncautious, not to say stupid, programmers don't // make the server crash. Since hordes of uncautious, not to say stupid, programmers don't
// even imagine some players on their servers could be bots, this check is performed less than // even imagine some players on their servers could be bots, this check is performed less than
// sometimes actually by their side, that's why we strongly recommend to check it here too. In // sometimes actually by their side, that's why we strongly recommend to check it here too. In
// case it's a bot asking for a client command, we handle it like we do for bot commands // case it's a bot asking for a client command, we handle it like we do for bot commands
if (!game.isNullEntity (ent)) { if (!game.isNullEntity (ent)) {
if (bots[ent] || util.isFakeClient (ent) || (ent->v.flags & FL_DORMANT)) { if (bots[ent] || util.isFakeClient (ent) || (ent->v.flags & FL_DORMANT)) {
if (game.is (GameFlags::Metamod)) { if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands
}
return;
} }
return;
} }
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
va_list ap;
auto buffer = strings.chars ();
va_start (ap, format);
vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap);
va_end (ap);
engfuncs.pfnClientCommand (ent, buffer);
} }
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
va_list ap;
auto buffer = strings.chars ();
va_start (ap, format);
vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap);
va_end (ap);
engfuncs.pfnClientCommand (ent, buffer);
}
} }
CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) {
@ -488,7 +488,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) {
} }
if (entlink.needsBypass () && !game.is (GameFlags::Metamod)) { if (entlink.needsBypass () && !game.is (GameFlags::Metamod)) {
table->pfnCreateNamedEntity = [] (string_t classname) -> edict_t *{ table->pfnCreateNamedEntity = [] (string_t classname) -> edict_t * {
if (entlink.isPaused ()) { if (entlink.isPaused ()) {
entlink.enable (); entlink.enable ();
@ -924,7 +924,7 @@ CR_EXPORT int Meta_Detach (PLUG_LOADTIME now, PL_UNLOAD_REASON reason) {
worker.shutdown (); worker.shutdown ();
// kick all bots off this server // kick all bots off this server
bots.kickEveryone (true); bots.kickEveryone (true);
// save collected practice on shutdown // save collected practice on shutdown
practice.save (); practice.save ();

View file

@ -896,8 +896,8 @@ void BotManager::listBots () {
auto timelimitStr = cv_rotate_bots ? strings.format ("%-3.0f secs", bot->m_stayTime - game.time ()) : "unlimited"; auto timelimitStr = cv_rotate_bots ? strings.format ("%-3.0f secs", bot->m_stayTime - game.time ()) : "unlimited";
ctrl.msg ("[%-2.1d]\t%-22.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%s", ctrl.msg ("[%-2.1d]\t%-22.16s\t%-10.12s\t%-3.4s\t%-3.1d\t%-3.1d\t%-3.4s\t%s",
bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful",
botTeam (bot->ent ()), bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_isAlive ? "yes" : "no", timelimitStr); botTeam (bot->ent ()), bot->m_difficulty, static_cast <int> (bot->pev->frags), bot->m_isAlive ? "yes" : "no", timelimitStr);
} }
ctrl.msg ("%d bots", m_bots.length ()); ctrl.msg ("%d bots", m_bots.length ());
} }
@ -1325,10 +1325,10 @@ void BotManager::handleDeath (edict_t *killer, edict_t *victim) {
// need to send congrats on well placed shot // need to send congrats on well placed shot
for (const auto &notify : bots) { for (const auto &notify : bots) {
if (notify->m_isAlive if (notify->m_isAlive
&& killerTeam == notify->m_team && killerTeam == notify->m_team
&& killerTeam != victimTeam && killerTeam != victimTeam
&& killer != notify->ent () && killer != notify->ent ()
&& notify->seesEntity (victim->v.origin)) { && notify->seesEntity (victim->v.origin)) {
if (!(killer->v.flags & FL_FAKECLIENT)) { if (!(killer->v.flags & FL_FAKECLIENT)) {
notify->pushChatterMessage (Chatter::NiceShotCommander); notify->pushChatterMessage (Chatter::NiceShotCommander);
@ -1346,13 +1346,13 @@ void BotManager::handleDeath (edict_t *killer, edict_t *victim) {
// notice nearby to victim teammates, that attacker is near // notice nearby to victim teammates, that attacker is near
for (const auto &notify : bots) { for (const auto &notify : bots) {
if (notify->m_difficulty >= Difficulty::Hard if (notify->m_difficulty >= Difficulty::Hard
&& killerTeam != victimTeam && killerTeam != victimTeam
&& notify->m_seeEnemyTime + 2.0f < game.time () && notify->m_seeEnemyTime + 2.0f < game.time ()
&& notify->m_isAlive && notify->m_isAlive
&& notify->m_team == victimTeam && notify->m_team == victimTeam
&& game.isNullEntity (notify->m_enemy) && game.isNullEntity (notify->m_enemy)
&& game.isNullEntity (notify->m_lastEnemy) && game.isNullEntity (notify->m_lastEnemy)
&& util.isVisible (killer->v.origin, notify->ent ())) { && util.isVisible (killer->v.origin, notify->ent ())) {
// make bot look at last enemy position // make bot look at last enemy position
notify->m_actualReactionTime = 0.0f; notify->m_actualReactionTime = 0.0f;
@ -1890,10 +1890,10 @@ void BotManager::notifyBombDefuse () {
const auto task = bot->getCurrentTaskId (); const auto task = bot->getCurrentTaskId ();
if (!bot->m_defuseNotified if (!bot->m_defuseNotified
&& bot->m_isAlive && bot->m_isAlive
&& task != Task::MoveToPosition && task != Task::MoveToPosition
&& task != Task::DefuseBomb && task != Task::DefuseBomb
&& task != Task::EscapeFromBomb) { && task != Task::EscapeFromBomb) {
if (bot->m_team == Team::Terrorist && bot->pev->origin.distanceSq (bombPos) < cr::sqrf (512.0f)) { if (bot->m_team == Team::Terrorist && bot->pev->origin.distanceSq (bombPos) < cr::sqrf (512.0f)) {
bot->clearSearchNodes (); bot->clearSearchNodes ();
@ -2263,7 +2263,7 @@ bool BotManager::isLineBlockedBySmoke (const Vector &from, const Vector &to, flo
} }
} }
} }
// define how much smoke a bot can see thru // define how much smoke a bot can see thru
const float maxSmokedLength = 0.7f * kSmokeGrenadeRadius; const float maxSmokedLength = 0.7f * kSmokeGrenadeRadius;

View file

@ -302,7 +302,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) {
postprocessGoals (graph.m_goalPoints, goalChoices); postprocessGoals (graph.m_goalPoints, goalChoices);
} }
} }
else if (tactic == GoalTactic::RescueHostage && !graph.m_rescuePoints.empty ()) { else if (tactic == GoalTactic::RescueHostage && !graph.m_rescuePoints.empty ()) {
// force ct with hostage(s) to select closest rescue goal // force ct with hostage(s) to select closest rescue goal
float nearestDistanceSq = kInfiniteDistance; float nearestDistanceSq = kInfiniteDistance;
int count = 0; int count = 0;
@ -893,7 +893,7 @@ void Bot::moveToGoal () {
src.z += 12.0f; src.z += 12.0f;
dst.z += 18.0f + 28.0f; dst.z += 18.0f + 28.0f;
game.testLine (src, dst, TraceIgnore::Everything, ent (), &tr); game.testLine (src, dst, TraceIgnore::Everything, ent (), &tr);
if (tr.flFraction >= 0.95f) { if (tr.flFraction >= 0.95f) {
@ -1037,7 +1037,7 @@ bool Bot::updateNavigation () {
selectBestWeapon (); selectBestWeapon ();
} }
} }
if (m_pathFlags & NodeFlag::Ladder) { if (m_pathFlags & NodeFlag::Ladder) {
const float ladderDistance = pev->origin.distance (m_pathOrigin); const float ladderDistance = pev->origin.distance (m_pathOrigin);
@ -1074,10 +1074,10 @@ bool Bot::updateNavigation () {
// special detection if someone is using the ladder (to prevent to have bots-towers on ladders) // special detection if someone is using the ladder (to prevent to have bots-towers on ladders)
for (const auto &client : util.getClients ()) { for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) if (!(client.flags & ClientFlags::Used)
|| !(client.flags & ClientFlags::Alive) || !(client.flags & ClientFlags::Alive)
|| (client.ent->v.movetype != MOVETYPE_FLY) || (client.ent->v.movetype != MOVETYPE_FLY)
|| client.team != m_team || client.team != m_team
|| client.ent == ent ()) { || client.ent == ent ()) {
continue; continue;
} }
@ -1091,8 +1091,8 @@ bool Bot::updateNavigation () {
// someone is above or below us and is using the ladder already // someone is above or below us and is using the ladder already
if (tr.pHit == client.ent if (tr.pHit == client.ent
&& cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f
&& (client.ent->v.movetype == MOVETYPE_FLY)) { && (client.ent->v.movetype == MOVETYPE_FLY)) {
const auto numPreviousNode = rg (0, 2); const auto numPreviousNode = rg (0, 2);
@ -1246,8 +1246,8 @@ bool Bot::updateNavigation () {
// needs precise placement - check if we get past the point // needs precise placement - check if we get past the point
if (desiredDistanceSq < cr::sqrf (22.0f) if (desiredDistanceSq < cr::sqrf (22.0f)
&& nodeDistanceSq < cr::sqrf (30.0f) && nodeDistanceSq < cr::sqrf (30.0f)
&& m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= nodeDistanceSq) { && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= nodeDistanceSq) {
desiredDistanceSq = nodeDistanceSq + 1.0f; desiredDistanceSq = nodeDistanceSq + 1.0f;
} }
@ -1287,10 +1287,10 @@ bool Bot::updateNavigation () {
const int taskTarget = getTask ()->data; const int taskTarget = getTask ()->data;
if (game.mapIs (MapFlags::Demolition) if (game.mapIs (MapFlags::Demolition)
&& bots.isBombPlanted () && bots.isBombPlanted ()
&& m_team == Team::CT && m_team == Team::CT
&& getCurrentTaskId () != Task::EscapeFromBomb && getCurrentTaskId () != Task::EscapeFromBomb
&& taskTarget != kInvalidNodeIndex) { && taskTarget != kInvalidNodeIndex) {
const Vector &bombOrigin = isBombAudible (); const Vector &bombOrigin = isBombAudible ();
@ -1350,9 +1350,9 @@ bool Bot::updateLiftHandling () {
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr); game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
if (tr.flFraction < 1.0f if (tr.flFraction < 1.0f
&& util.isDoorEntity (tr.pHit) && util.isDoorEntity (tr.pHit)
&& (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside)
&& pev->groundentity != tr.pHit) { && pev->groundentity != tr.pHit) {
if (m_liftState == LiftState::None) { if (m_liftState == LiftState::None) {
m_liftState = LiftState::LookingButtonOutside; m_liftState = LiftState::LookingButtonOutside;
@ -1372,8 +1372,8 @@ bool Bot::updateLiftHandling () {
// if trace result shows us that it is a lift // if trace result shows us that it is a lift
if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && isFunc (tr.pHit->v.classname.str ()) && !liftClosedDoorExists) { if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && isFunc (tr.pHit->v.classname.str ()) && !liftClosedDoorExists) {
if ((m_liftState == LiftState::None if ((m_liftState == LiftState::None
|| m_liftState == LiftState::WaitingFor || m_liftState == LiftState::WaitingFor
|| m_liftState == LiftState::LookingButtonOutside) && cr::fzero (tr.pHit->v.velocity.z)) { || m_liftState == LiftState::LookingButtonOutside) && cr::fzero (tr.pHit->v.velocity.z)) {
if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) { if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) {
m_liftEntity = tr.pHit; m_liftEntity = tr.pHit;
@ -1449,10 +1449,10 @@ bool Bot::updateLiftHandling () {
for (const auto &bot : bots) { for (const auto &bot : bots) {
if (!bot->m_isAlive if (!bot->m_isAlive
|| bot->m_team != m_team || bot->m_team != m_team
|| bot->m_targetEntity != ent () || bot->m_targetEntity != ent ()
|| bot->getCurrentTaskId () != Task::FollowUser || bot->getCurrentTaskId () != Task::FollowUser
|| bot->m_liftEntity != m_liftEntity) { || bot->m_liftEntity != m_liftEntity) {
continue; continue;
} }
@ -1484,10 +1484,10 @@ bool Bot::updateLiftHandling () {
// got a valid button entity ? // got a valid button entity ?
if (!game.isNullEntity (button) if (!game.isNullEntity (button)
&& pev->groundentity == m_liftEntity && pev->groundentity == m_liftEntity
&& m_buttonPushTime + 1.0f < game.time () && m_buttonPushTime + 1.0f < game.time ()
&& cr::fzero (m_liftEntity->v.velocity.z) && cr::fzero (m_liftEntity->v.velocity.z)
&& isOnFloor ()) { && isOnFloor ()) {
auto buttonWithLineOfSight = lookupButton (m_liftEntity->v.targetname.str (), false); auto buttonWithLineOfSight = lookupButton (m_liftEntity->v.targetname.str (), false);
@ -1508,14 +1508,14 @@ bool Bot::updateLiftHandling () {
// is lift activated and bot is standing on it and lift is moving ? // is lift activated and bot is standing on it and lift is moving ?
if (m_liftState == LiftState::LookingButtonInside if (m_liftState == LiftState::LookingButtonInside
|| m_liftState == LiftState::EnteringIn || m_liftState == LiftState::EnteringIn
|| m_liftState == LiftState::WaitingForTeammates || m_liftState == LiftState::WaitingForTeammates
|| m_liftState == LiftState::WaitingFor) { || m_liftState == LiftState::WaitingFor) {
if (pev->groundentity == m_liftEntity if (pev->groundentity == m_liftEntity
&& !cr::fzero (m_liftEntity->v.velocity.z) && !cr::fzero (m_liftEntity->v.velocity.z)
&& isOnFloor () && isOnFloor ()
&& ((graph[m_previousNodes[0]].flags & NodeFlag::Lift) || !game.isNullEntity (m_targetEntity))) { && ((graph[m_previousNodes[0]].flags & NodeFlag::Lift) || !game.isNullEntity (m_targetEntity))) {
m_liftState = LiftState::TravelingBy; m_liftState = LiftState::TravelingBy;
m_liftUsageTime = game.time () + 14.0f; m_liftUsageTime = game.time () + 14.0f;
@ -1555,10 +1555,10 @@ bool Bot::updateLiftHandling () {
// iterate though clients, and find if lift already used // iterate though clients, and find if lift already used
for (const auto &client : util.getClients ()) { for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) if (!(client.flags & ClientFlags::Used)
|| !(client.flags & ClientFlags::Alive) || !(client.flags & ClientFlags::Alive)
|| client.team != m_team || client.team != m_team
|| client.ent == ent () || client.ent == ent ()
|| game.isNullEntity (client.ent->v.groundentity)) { || game.isNullEntity (client.ent->v.groundentity)) {
continue; continue;
} }
@ -1613,8 +1613,8 @@ bool Bot::updateLiftHandling () {
// bot fall down somewhere inside the lift's groove :) // bot fall down somewhere inside the lift's groove :)
if (pev->groundentity != m_liftEntity && graph.exists (m_previousNodes[0])) { if (pev->groundentity != m_liftEntity && graph.exists (m_previousNodes[0])) {
if ((graph[m_previousNodes[0]].flags & NodeFlag::Lift) if ((graph[m_previousNodes[0]].flags & NodeFlag::Lift)
&& (m_path->origin.z - pev->origin.z) > 50.0f && (m_path->origin.z - pev->origin.z) > 50.0f
&& (graph[m_previousNodes[0]].origin.z - pev->origin.z) > 50.0f) { && (graph[m_previousNodes[0]].origin.z - pev->origin.z) > 50.0f) {
m_liftState = LiftState::None; m_liftState = LiftState::None;
m_liftEntity = nullptr; m_liftEntity = nullptr;
@ -2129,8 +2129,8 @@ int Bot::findDefendNode (const Vector &origin) {
for (const auto &path : graph) { for (const auto &path : graph) {
if (origin.distanceSq (path.origin) < cr::sqrf (kMaxDistance) if (origin.distanceSq (path.origin) < cr::sqrf (kMaxDistance)
&& vistab.visible (path.number, posIndex) && vistab.visible (path.number, posIndex)
&& !isOccupiedNode (path.number)) { && !isOccupiedNode (path.number)) {
found.push (path.number); found.push (path.number);
} }
@ -2379,13 +2379,13 @@ bool Bot::advanceMovement () {
// only if we in normal task and bomb is not planted // only if we in normal task and bomb is not planted
if (tid == Task::Normal if (tid == Task::Normal
&& bots.getRoundMidTime () + 5.0f < game.time () && bots.getRoundMidTime () + 5.0f < game.time ()
&& m_timeCamping + 5.0f < game.time () && m_timeCamping + 5.0f < game.time ()
&& !bots.isBombPlanted () && !bots.isBombPlanted ()
&& m_personality != Personality::Rusher && m_personality != Personality::Rusher
&& !m_hasC4 && !m_isVIP && !m_hasC4 && !m_isVIP
&& m_loosedBombNodeIndex == kInvalidNodeIndex && m_loosedBombNodeIndex == kInvalidNodeIndex
&& !m_hasHostage && !m_isCreature) { && !m_hasHostage && !m_isCreature) {
m_campButtons = 0; m_campButtons = 0;
@ -2497,10 +2497,10 @@ bool Bot::advanceMovement () {
// is there a jump node right ahead and do we need to draw out the light weapon ? // is there a jump node right ahead and do we need to draw out the light weapon ?
if (willJump && !usesKnife () if (willJump && !usesKnife ()
&& m_currentWeapon != Weapon::Scout && m_currentWeapon != Weapon::Scout
&& !m_isReloading && !usesPistol () && !m_isReloading && !usesPistol ()
&& (jumpDistanceSq > cr::sqrf (145.0f) || (dst.z - 32.0f > src.z && jumpDistanceSq > cr::sqrf (125.0f))) && (jumpDistanceSq > cr::sqrf (145.0f) || (dst.z - 32.0f > src.z && jumpDistanceSq > cr::sqrf (125.0f)))
&& !(m_states & Sense::SeeingEnemy)) { && !(m_states & Sense::SeeingEnemy)) {
selectWeaponById (Weapon::Knife); // draw out the knife if we needed selectWeaponById (Weapon::Knife); // draw out the knife if we needed
} }
@ -3351,7 +3351,7 @@ void Bot::syncFindPath (int srcIndex, int destIndex, FindPath pathType) {
m_planner->setG (Heuristic::gfunctionPathDist); m_planner->setG (Heuristic::gfunctionPathDist);
} }
} }
m_chosenGoalIndex = srcIndex; m_chosenGoalIndex = srcIndex;
m_goalValue = 0.0f; m_goalValue = 0.0f;

View file

@ -391,7 +391,7 @@ bool FloydWarshallAlgo::load () {
return true; return true;
} }
rebuild (); // rebuilds matrix rebuild (); // rebuilds matrix
return true; return true;
} }

View file

@ -1,464 +1,464 @@
// //
// YaPB, based on PODBot by Markus Klinge ("CountFloyd"). // YaPB, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright © YaPB Project Developers <yapb@jeefo.net>. // Copyright © YaPB Project Developers <yapb@jeefo.net>.
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// //
#include <yapb.h> #include <yapb.h>
#if defined (BOT_STORAGE_EXPLICIT_INSTANTIATIONS) #if defined (BOT_STORAGE_EXPLICIT_INSTANTIATIONS)
template <typename U> bool BotStorage::load (SmallArray <U> &data, ExtenHeader *exten, int32_t *outOptions) { template <typename U> bool BotStorage::load (SmallArray <U> &data, ExtenHeader *exten, int32_t *outOptions) {
auto type = guessType <U> (); auto type = guessType <U> ();
String filename = buildPath (storageToBotFile (type.option), true); String filename = buildPath (storageToBotFile (type.option), true);
extern ConVar cv_debug, cv_graph_url; extern ConVar cv_debug, cv_graph_url;
// graphs can be downloaded... // graphs can be downloaded...
const bool isGraph = !!(type.option & StorageOption::Graph); const bool isGraph = !!(type.option & StorageOption::Graph);
const bool isDebug = cv_debug; const bool isDebug = cv_debug;
MemFile file (filename); // open the file MemFile file (filename); // open the file
data.clear (); data.clear ();
// resize data to fit the stuff // resize data to fit the stuff
auto resizeData = [&] (const size_t length) { auto resizeData = [&] (const size_t length) {
data.resize (length); // for non-graph data the graph should be already loaded data.resize (length); // for non-graph data the graph should be already loaded
data.shrink (); // free up memory to minimum data.shrink (); // free up memory to minimum
// ensure we're have enough memory to decompress the data // ensure we're have enough memory to decompress the data
data.ensure (length + ULZ::Excess); data.ensure (length + ULZ::Excess);
}; };
// if graph & attempted to load multiple times, bail out, we're failed // if graph & attempted to load multiple times, bail out, we're failed
if (isGraph && ++m_retries > 2) { if (isGraph && ++m_retries > 2) {
resetRetries (); resetRetries ();
return error (isGraph, isDebug, file, "Unable to load %s (filename: '%s'). Download process has failed as well. No nodes has been found.", type.name, filename); return error (isGraph, isDebug, file, "Unable to load %s (filename: '%s'). Download process has failed as well. No nodes has been found.", type.name, filename);
} }
// downloader for graph // downloader for graph
auto download = [&] () -> bool { auto download = [&] () -> bool {
if (!graph.canDownload ()) { if (!graph.canDownload ()) {
return false; return false;
} }
String lowercaseMapName = game.getMapName (); String lowercaseMapName = game.getMapName ();
lowercaseMapName = lowercaseMapName.lowercase (); lowercaseMapName = lowercaseMapName.lowercase ();
auto downloadAddress = cv_graph_url.as <StringRef> (); auto downloadAddress = cv_graph_url.as <StringRef> ();
auto toDownload = buildPath (storageToBotFile (type.option), false); auto toDownload = buildPath (storageToBotFile (type.option), false);
auto fromDownload = strings.format ("%s://%s/graph/%s.graph", product.httpScheme, downloadAddress, lowercaseMapName); auto fromDownload = strings.format ("%s://%s/graph/%s.graph", product.httpScheme, downloadAddress, lowercaseMapName);
// try to download // try to download
if (http.downloadFile (fromDownload, toDownload)) { if (http.downloadFile (fromDownload, toDownload)) {
ctrl.msg ("%s file '%s' successfully downloaded. Processing...", type.name, filename); ctrl.msg ("%s file '%s' successfully downloaded. Processing...", type.name, filename);
return true; return true;
} }
else { else {
ctrl.msg ("Can't download '%s' from '%s' to '%s'... (%d).", filename, fromDownload, toDownload, http.getLastStatusCode ()); ctrl.msg ("Can't download '%s' from '%s' to '%s'... (%d).", filename, fromDownload, toDownload, http.getLastStatusCode ());
} }
return false; return false;
}; };
// tries to reload or open pwf file // tries to reload or open pwf file
auto tryReload = [&] () -> bool { auto tryReload = [&] () -> bool {
file.close (); file.close ();
if (!isGraph) { if (!isGraph) {
return false; return false;
} }
if (download ()) { if (download ()) {
return load (data, exten, outOptions); return load (data, exten, outOptions);
} }
if (graph.convertOldFormat ()) { if (graph.convertOldFormat ()) {
return load (data, exten, outOptions); return load (data, exten, outOptions);
} }
return false; return false;
}; };
// no open no fun // no open no fun
if (!file) { if (!file) {
if (tryReload ()) { if (tryReload ()) {
return true; return true;
} }
return error (isGraph, isDebug, file, "Unable to open %s file for reading (filename: '%s').", type.name, filename); return error (isGraph, isDebug, file, "Unable to open %s file for reading (filename: '%s').", type.name, filename);
} }
// read the header // read the header
StorageHeader hdr {}; StorageHeader hdr {};
file.read (&hdr, sizeof (StorageHeader)); file.read (&hdr, sizeof (StorageHeader));
// check the magic // check the magic
if (hdr.magic != kStorageMagic && hdr.magic != kStorageMagicUB) { if (hdr.magic != kStorageMagic && hdr.magic != kStorageMagicUB) {
if (tryReload ()) { if (tryReload ()) {
return true; return true;
} }
return error (isGraph, isDebug, file, "Unable to read magic of %s (filename: '%s').", type.name, filename); return error (isGraph, isDebug, file, "Unable to read magic of %s (filename: '%s').", type.name, filename);
} }
// check the path-numbers // check the path-numbers
if (!isGraph && hdr.length != graph.length ()) { if (!isGraph && hdr.length != graph.length ()) {
return error (isGraph, isDebug, file, "Damaged %s (filename: '%s'). Mismatch number of nodes (got: '%d', need: '%d').", type.name, filename, hdr.length, graph.length ()); return error (isGraph, isDebug, file, "Damaged %s (filename: '%s'). Mismatch number of nodes (got: '%d', need: '%d').", type.name, filename, hdr.length, graph.length ());
} }
// check the count // check the count
if (hdr.length == 0 || hdr.length > kMaxNodes || hdr.length < kMaxNodeLinks) { if (hdr.length == 0 || hdr.length > kMaxNodes || hdr.length < kMaxNodeLinks) {
if (tryReload ()) { if (tryReload ()) {
return true; return true;
} }
return error (isGraph, isDebug, file, "Damaged %s (filename: '%s'). Paths length is overflowed (got: '%d').", type.name, filename, hdr.length); return error (isGraph, isDebug, file, "Damaged %s (filename: '%s'). Paths length is overflowed (got: '%d').", type.name, filename, hdr.length);
} }
// check the version // check the version
if (hdr.version > type.version && isGraph) { if (hdr.version > type.version && isGraph) {
ctrl.msg ("Graph version mismatch %s (filename: '%s'). Version number differs (got: '%d', need: '%d') Please, upgrade %s.", type.name, filename, hdr.version, type.version, product.name); ctrl.msg ("Graph version mismatch %s (filename: '%s'). Version number differs (got: '%d', need: '%d') Please, upgrade %s.", type.name, filename, hdr.version, type.version, product.name);
} }
else if (hdr.version != type.version && !isGraph) { else if (hdr.version != type.version && !isGraph) {
return error (isGraph, isDebug, file, "Damaged %s (filename: '%s'). Version number differs (got: '%d', need: '%d').", type.name, filename, hdr.version, type.version); return error (isGraph, isDebug, file, "Damaged %s (filename: '%s'). Version number differs (got: '%d', need: '%d').", type.name, filename, hdr.version, type.version);
} }
// save graph version // save graph version
if (isGraph) { if (isGraph) {
graph.setGraphHeader (&hdr); graph.setGraphHeader (&hdr);
} }
// check the storage type // check the storage type
if ((hdr.options & type.option) != type.option) { if ((hdr.options & type.option) != type.option) {
return error (isGraph, isDebug, file, "Incorrect storage format for %s (filename: '%s').", type.name, filename); return error (isGraph, isDebug, file, "Incorrect storage format for %s (filename: '%s').", type.name, filename);
} }
const auto compressedSize = static_cast <size_t> (hdr.compressed); const auto compressedSize = static_cast <size_t> (hdr.compressed);
const auto numberNodes = static_cast <size_t> (hdr.length); const auto numberNodes = static_cast <size_t> (hdr.length);
SmallArray <uint8_t> compressed (compressedSize + sizeof (uint8_t) * ULZ::Excess); SmallArray <uint8_t> compressed (compressedSize + sizeof (uint8_t) * ULZ::Excess);
// graph is not resized upon load // graph is not resized upon load
if (isGraph) { if (isGraph) {
resizeData (numberNodes); resizeData (numberNodes);
} }
else { else {
resizeData (hdr.uncompressed / sizeof (U)); resizeData (hdr.uncompressed / sizeof (U));
} }
// read compressed data // read compressed data
if (file.read (compressed.data (), sizeof (uint8_t), compressedSize) == compressedSize) { if (file.read (compressed.data (), sizeof (uint8_t), compressedSize) == compressedSize) {
// try to uncompress // try to uncompress
if (ulz.uncompress (compressed.data (), hdr.compressed, reinterpret_cast <uint8_t *> (data.data ()), hdr.uncompressed) == ULZ::UncompressFailure) { if (ulz.uncompress (compressed.data (), hdr.compressed, reinterpret_cast <uint8_t *> (data.data ()), hdr.uncompressed) == ULZ::UncompressFailure) {
return error (isGraph, isDebug, file, "Unable to decompress ULZ data for %s (filename: '%s').", type.name, filename); return error (isGraph, isDebug, file, "Unable to decompress ULZ data for %s (filename: '%s').", type.name, filename);
} }
else { else {
if (outOptions) { if (outOptions) {
outOptions = &hdr.options; outOptions = &hdr.options;
} }
// author of graph.. save // author of graph.. save
if ((hdr.options & StorageOption::Exten) && exten != nullptr) { if ((hdr.options & StorageOption::Exten) && exten != nullptr) {
const auto extenSize = sizeof (ExtenHeader); const auto extenSize = sizeof (ExtenHeader);
const auto actuallyRead = file.read (exten, extenSize) * extenSize; const auto actuallyRead = file.read (exten, extenSize) * extenSize;
if (isGraph) { if (isGraph) {
resetRetries (); resetRetries ();
ExtenHeader extenHeader; ExtenHeader extenHeader;
strings.copy (extenHeader.author, exten->author, cr::bufsize (exten->author)); strings.copy (extenHeader.author, exten->author, cr::bufsize (exten->author));
if (extenSize <= actuallyRead) { if (extenSize <= actuallyRead) {
// write modified by, only if the name is different // write modified by, only if the name is different
if (!strings.isEmpty (extenHeader.author) if (!strings.isEmpty (extenHeader.author)
&& strncmp (extenHeader.author, exten->modified, cr::bufsize (extenHeader.author)) != 0) { && strncmp (extenHeader.author, exten->modified, cr::bufsize (extenHeader.author)) != 0) {
strings.copy (extenHeader.modified, exten->modified, cr::bufsize (exten->modified)); strings.copy (extenHeader.modified, exten->modified, cr::bufsize (exten->modified));
} }
} }
else { else {
strings.copy (extenHeader.modified, "(none)", cr::bufsize (exten->modified)); strings.copy (extenHeader.modified, "(none)", cr::bufsize (exten->modified));
} }
extenHeader.mapSize = exten->mapSize; extenHeader.mapSize = exten->mapSize;
// tell graph about exten header // tell graph about exten header
graph.setExtenHeader (&extenHeader); graph.setExtenHeader (&extenHeader);
} }
} }
// for visibility tables load counts of stand/count numbers // for visibility tables load counts of stand/count numbers
if (type.option & StorageOption::Vistable) { if (type.option & StorageOption::Vistable) {
for (auto &path : graph) { for (auto &path : graph) {
file.read (&path.vis, sizeof (PathVis)); file.read (&path.vis, sizeof (PathVis));
} }
} }
ctrl.msg ("Loaded Bots %s data v%d (Memory: %.2fMB).", type.name, hdr.version, static_cast <float> (data.capacity () * sizeof (U)) / 1024.0f / 1024.0f); ctrl.msg ("Loaded Bots %s data v%d (Memory: %.2fMB).", type.name, hdr.version, static_cast <float> (data.capacity () * sizeof (U)) / 1024.0f / 1024.0f);
file.close (); file.close ();
return true; return true;
} }
} }
else { else {
return error (isGraph, isDebug, file, "Unable to read ULZ data for %s (filename: '%s').", type.name, filename); return error (isGraph, isDebug, file, "Unable to read ULZ data for %s (filename: '%s').", type.name, filename);
} }
return false; return false;
} }
template <typename U> bool BotStorage::save (const SmallArray <U> &data, ExtenHeader *exten, int32_t passOptions) { template <typename U> bool BotStorage::save (const SmallArray <U> &data, ExtenHeader *exten, int32_t passOptions) {
auto type = guessType <U> (); auto type = guessType <U> ();
// append additional options // append additional options
if (passOptions != 0) { if (passOptions != 0) {
type.option |= passOptions; type.option |= passOptions;
} }
const auto isGraph = !!(type.option & StorageOption::Graph); const auto isGraph = !!(type.option & StorageOption::Graph);
// do not allow to save graph with less than 8 nodes // do not allow to save graph with less than 8 nodes
if (isGraph && graph.length () < kMaxNodeLinks) { if (isGraph && graph.length () < kMaxNodeLinks) {
ctrl.msg ("Can't save graph data with less than %d nodes. Please add some more before saving.", kMaxNodeLinks); ctrl.msg ("Can't save graph data with less than %d nodes. Please add some more before saving.", kMaxNodeLinks);
return false; return false;
} }
String filename = buildPath (storageToBotFile (type.option)); String filename = buildPath (storageToBotFile (type.option));
if (data.empty ()) { if (data.empty ()) {
logger.error ("Unable to save %s file. Empty data. (filename: '%s').", type.name, filename); logger.error ("Unable to save %s file. Empty data. (filename: '%s').", type.name, filename);
return false; return false;
} }
else if (isGraph) { else if (isGraph) {
for (auto &path : graph) { for (auto &path : graph) {
path.display = 0.0f; path.display = 0.0f;
path.light = illum.getLightLevel (path.origin); path.light = illum.getLightLevel (path.origin);
} }
} }
// open the file // open the file
File file (filename, "wb"); File file (filename, "wb");
// no open no fun // no open no fun
if (!file) { if (!file) {
logger.error ("Unable to open %s file for writing (filename: '%s').", type.name, filename); logger.error ("Unable to open %s file for writing (filename: '%s').", type.name, filename);
return false; return false;
} }
const auto rawLength = data.length () * sizeof (U); const auto rawLength = data.length () * sizeof (U);
SmallArray <uint8_t> compressed (rawLength + sizeof (uint8_t) * ULZ::Excess); SmallArray <uint8_t> compressed (rawLength + sizeof (uint8_t) * ULZ::Excess);
// try to compress // try to compress
const auto compressedLength = static_cast <size_t> (ulz.compress (reinterpret_cast <uint8_t *> (data.data ()), static_cast <int32_t> (rawLength), reinterpret_cast <uint8_t *> (compressed.data ()))); const auto compressedLength = static_cast <size_t> (ulz.compress (reinterpret_cast <uint8_t *> (data.data ()), static_cast <int32_t> (rawLength), reinterpret_cast <uint8_t *> (compressed.data ())));
if (compressedLength > 0) { if (compressedLength > 0) {
StorageHeader hdr {}; StorageHeader hdr {};
hdr.magic = kStorageMagic; hdr.magic = kStorageMagic;
hdr.version = type.version; hdr.version = type.version;
hdr.options = type.option; hdr.options = type.option;
hdr.length = graph.length (); hdr.length = graph.length ();
hdr.compressed = static_cast <int32_t> (compressedLength); hdr.compressed = static_cast <int32_t> (compressedLength);
hdr.uncompressed = static_cast <int32_t> (rawLength); hdr.uncompressed = static_cast <int32_t> (rawLength);
file.write (&hdr, sizeof (StorageHeader)); file.write (&hdr, sizeof (StorageHeader));
file.write (compressed.data (), sizeof (uint8_t), compressedLength); file.write (compressed.data (), sizeof (uint8_t), compressedLength);
// for visibility tables save counts of stand/count numbers // for visibility tables save counts of stand/count numbers
if (type.option & StorageOption::Vistable) { if (type.option & StorageOption::Vistable) {
for (auto &path : graph) { for (auto &path : graph) {
file.write (&path.vis, sizeof (PathVis)); file.write (&path.vis, sizeof (PathVis));
} }
} }
// add extension // add extension
if ((type.option & StorageOption::Exten) && exten != nullptr) { if ((type.option & StorageOption::Exten) && exten != nullptr) {
file.write (exten, sizeof (ExtenHeader)); file.write (exten, sizeof (ExtenHeader));
} }
extern ConVar cv_debug; extern ConVar cv_debug;
// notify only about graph // notify only about graph
if (isGraph || cv_debug) { if (isGraph || cv_debug) {
ctrl.msg ("Successfully saved Bots %s data.", type.name); ctrl.msg ("Successfully saved Bots %s data.", type.name);
} }
} }
else { else {
logger.error ("Unable to compress %s data (filename: '%s').", type.name, filename); logger.error ("Unable to compress %s data (filename: '%s').", type.name, filename);
return false; return false;
} }
return true; return true;
} }
template <typename ...Args> bool BotStorage::error (bool isGraph, bool isDebug, MemFile &file, const char *fmt, Args &&...args) { template <typename ...Args> bool BotStorage::error (bool isGraph, bool isDebug, MemFile &file, const char *fmt, Args &&...args) {
auto result = strings.format (fmt, cr::forward <Args> (args)...); auto result = strings.format (fmt, cr::forward <Args> (args)...);
// display error only for graph file // display error only for graph file
if (isGraph || isDebug) { if (isGraph || isDebug) {
logger.error (result); logger.error (result);
} }
// if graph reset paths // if graph reset paths
if (isGraph) { if (isGraph) {
bots.kickEveryone (true); bots.kickEveryone (true);
graph.reset (); graph.reset ();
} }
file.close (); file.close ();
return false; return false;
} }
template <typename U> BotStorage::SaveLoadData BotStorage::guessType () { template <typename U> BotStorage::SaveLoadData BotStorage::guessType () {
if constexpr (cr::is_same <U, FloydWarshallAlgo::Matrix>::value) { if constexpr (cr::is_same <U, FloydWarshallAlgo::Matrix>::value) {
return { "Pathmatrix", StorageOption::Matrix, StorageVersion::Matrix }; return { "Pathmatrix", StorageOption::Matrix, StorageVersion::Matrix };
} }
else if constexpr (cr::is_same <U, BotPractice::DangerSaveRestore>::value) { else if constexpr (cr::is_same <U, BotPractice::DangerSaveRestore>::value) {
return { "Practice", StorageOption::Practice, StorageVersion::Practice }; return { "Practice", StorageOption::Practice, StorageVersion::Practice };
} }
else if constexpr (cr::is_same <U, GraphVistable::VisStorage>::value) { else if constexpr (cr::is_same <U, GraphVistable::VisStorage>::value) {
return { "Vistable", StorageOption::Vistable, StorageVersion::Vistable }; return { "Vistable", StorageOption::Vistable, StorageVersion::Vistable };
} }
else if constexpr (cr::is_same <U, Path>::value) { else if constexpr (cr::is_same <U, Path>::value) {
return { "Graph", StorageOption::Graph, StorageVersion::Graph }; return { "Graph", StorageOption::Graph, StorageVersion::Graph };
} }
} }
#else #else
String BotStorage::buildPath (int32_t file, bool isMemoryLoad, bool withoutMapName) { String BotStorage::buildPath (int32_t file, bool isMemoryLoad, bool withoutMapName) {
using FilePath = Twin <String, String>; using FilePath = Twin <String, String>;
static HashMap <int32_t, FilePath> paths = { static HashMap <int32_t, FilePath> paths = {
{ BotFile::Vistable, FilePath (folders.train, "vis")}, { BotFile::Vistable, FilePath (folders.train, "vis")},
{ BotFile::Practice, FilePath (folders.train, "prc")}, { BotFile::Practice, FilePath (folders.train, "prc")},
{ BotFile::Pathmatrix, FilePath (folders.train, "pmx")}, { BotFile::Pathmatrix, FilePath (folders.train, "pmx")},
{ BotFile::LogFile, FilePath (folders.logs, "txt")}, { BotFile::LogFile, FilePath (folders.logs, "txt")},
{ BotFile::Graph, FilePath (folders.graph, "graph")}, { BotFile::Graph, FilePath (folders.graph, "graph")},
{ BotFile::PodbotPWF, FilePath (folders.podbot, "pwf")}, { BotFile::PodbotPWF, FilePath (folders.podbot, "pwf")},
{ BotFile::EbotEWP, FilePath (folders.ebot, "ewp")}, { BotFile::EbotEWP, FilePath (folders.ebot, "ewp")},
}; };
static StringArray path; static StringArray path;
path.clear (); path.clear ();
// if not memory file we're don't need game dir // if not memory file we're don't need game dir
if (isMemoryLoad) { if (isMemoryLoad) {
path.emplace (getRunningPathVFS ()); path.emplace (getRunningPathVFS ());
} }
else { else {
path.emplace (getRunningPath ()); path.emplace (getRunningPath ());
} }
// the datadir // the datadir
path.emplace (folders.data); path.emplace (folders.data);
// append real filepath // append real filepath
path.emplace (paths[file].first); path.emplace (paths[file].first);
// if file is logfile use correct logfile name with date // if file is logfile use correct logfile name with date
if (file == BotFile::LogFile) { if (file == BotFile::LogFile) {
time_t ticks = time (&ticks); time_t ticks = time (&ticks);
tm timeinfo {}; tm timeinfo {};
plat.loctime (&timeinfo, &ticks); plat.loctime (&timeinfo, &ticks);
auto timebuf = strings.chars (); auto timebuf = strings.chars ();
strftime (timebuf, StringBuffer::StaticBufferSize, "L%d%m%Y", &timeinfo); strftime (timebuf, StringBuffer::StaticBufferSize, "L%d%m%Y", &timeinfo);
path.emplace (strings.format ("%s_%s.%s", product.nameLower, timebuf, paths[file].second)); path.emplace (strings.format ("%s_%s.%s", product.nameLower, timebuf, paths[file].second));
} }
else if (!withoutMapName) { else if (!withoutMapName) {
String mapName = game.getMapName (); String mapName = game.getMapName ();
path.emplace (strings.format ("%s.%s", mapName.lowercase (), paths[file].second)); path.emplace (strings.format ("%s.%s", mapName.lowercase (), paths[file].second));
} }
// finally use correct path separators for us // finally use correct path separators for us
return String::join (path, kPathSeparator); return String::join (path, kPathSeparator);
} }
int32_t BotStorage::storageToBotFile (int32_t options) { int32_t BotStorage::storageToBotFile (int32_t options) {
// converts storage option to storage filename // converts storage option to storage filename
if (options & StorageOption::Graph) { if (options & StorageOption::Graph) {
return BotFile::Graph; return BotFile::Graph;
} }
else if (options & StorageOption::Matrix) { else if (options & StorageOption::Matrix) {
return BotFile::Pathmatrix; return BotFile::Pathmatrix;
} }
else if (options & StorageOption::Vistable) { else if (options & StorageOption::Vistable) {
return BotFile::Vistable; return BotFile::Vistable;
} }
else if (options & StorageOption::Practice) { else if (options & StorageOption::Practice) {
return BotFile::Practice; return BotFile::Practice;
} }
return BotFile::Graph; return BotFile::Graph;
} }
void BotStorage::unlinkFromDisk () { void BotStorage::unlinkFromDisk () {
// this function removes graph file from the hard disk // this function removes graph file from the hard disk
StringArray unlinkable; StringArray unlinkable;
bots.kickEveryone (true); bots.kickEveryone (true);
// if we're delete graph, delete all corresponding to it files // if we're delete graph, delete all corresponding to it files
unlinkable.emplace (buildPath (BotFile::Graph)); // graph itself unlinkable.emplace (buildPath (BotFile::Graph)); // graph itself
unlinkable.emplace (buildPath (BotFile::Practice)); // corresponding to practice unlinkable.emplace (buildPath (BotFile::Practice)); // corresponding to practice
unlinkable.emplace (buildPath (BotFile::Vistable)); // corresponding to vistable unlinkable.emplace (buildPath (BotFile::Vistable)); // corresponding to vistable
unlinkable.emplace (buildPath (BotFile::Pathmatrix)); // corresponding to matrix unlinkable.emplace (buildPath (BotFile::Pathmatrix)); // corresponding to matrix
for (const auto &item : unlinkable) { for (const auto &item : unlinkable) {
if (plat.fileExists (item.chars ())) { if (plat.fileExists (item.chars ())) {
plat.removeFile (item.chars ()); plat.removeFile (item.chars ());
ctrl.msg ("File %s, has been deleted from the hard disk", item); ctrl.msg ("File %s, has been deleted from the hard disk", item);
} }
else { else {
logger.error ("Unable to open %s", item); logger.error ("Unable to open %s", item);
} }
} }
graph.reset (); // re-initialize points graph.reset (); // re-initialize points
} }
StringRef BotStorage::getRunningPath () { StringRef BotStorage::getRunningPath () {
// this function get's relative path against bot library (bot library should reside in bin dir) // this function get's relative path against bot library (bot library should reside in bin dir)
static String path; static String path;
// we're do not do relative (against bot's library) paths on android // we're do not do relative (against bot's library) paths on android
if (plat.android) { if (plat.android) {
if (path.empty ()) { if (path.empty ()) {
path = strings.joinPath (game.getRunningModName (), folders.addons, folders.bot); path = strings.joinPath (game.getRunningModName (), folders.addons, folders.bot);
} }
return path; return path;
} }
// compute the full path to the our folder // compute the full path to the our folder
if (path.empty ()) { if (path.empty ()) {
path = SharedLibrary::path (&bstor); path = SharedLibrary::path (&bstor);
if (path.startsWith ("<unk")) { if (path.startsWith ("<unk")) {
logger.fatal ("Unable to detect library path. Giving up..."); logger.fatal ("Unable to detect library path. Giving up...");
} }
auto parts = path.substr (1).split (kPathSeparator); auto parts = path.substr (1).split (kPathSeparator);
parts.pop (); // remove library name parts.pop (); // remove library name
parts.pop (); // remove bin directory parts.pop (); // remove bin directory
path = path.substr (0, 1) + String::join (parts, kPathSeparator); path = path.substr (0, 1) + String::join (parts, kPathSeparator);
} }
return path; return path;
} }
StringRef BotStorage::getRunningPathVFS () { StringRef BotStorage::getRunningPathVFS () {
static String path; static String path;
// we're do not do relative (against bot's library) paths on android // we're do not do relative (against bot's library) paths on android
if (plat.android) { if (plat.android) {
if (path.empty ()) { if (path.empty ()) {
path = strings.joinPath (folders.addons, folders.bot); path = strings.joinPath (folders.addons, folders.bot);
} }
return path; return path;
} }
if (path.empty ()) { if (path.empty ()) {
path = getRunningPath (); path = getRunningPath ();
path = path.substr (path.find (game.getRunningModName ())); // skip to the game dir path = path.substr (path.find (game.getRunningModName ())); // skip to the game dir
path = path.substr (path.find (kPathSeparator) + 1); // skip the game dir path = path.substr (path.find (kPathSeparator) + 1); // skip the game dir
} }
return path; return path;
} }
#endif // BOT_STORAGE_EXPLICIT_INSTANTIATIONS #endif // BOT_STORAGE_EXPLICIT_INSTANTIATIONS

View file

@ -328,7 +328,7 @@ void BotSupport::checkWelcome () {
// send the hud message // send the hud message
game.sendHudMessage (receiveEnt, textParams, game.sendHudMessage (receiveEnt, textParams,
sendLegacyWelcome ? legacyWelcomeMessage.chars () : modernWelcomeMessage.chars ()); sendLegacyWelcome ? legacyWelcomeMessage.chars () : modernWelcomeMessage.chars ());
m_welcomeReceiveTime = 0.0f; m_welcomeReceiveTime = 0.0f;
m_needToSendWelcome = false; m_needToSendWelcome = false;

View file

@ -63,11 +63,11 @@ void Bot::normal_ () {
// if bomb planted and it's a CT calculate new path to bomb point if he's not already heading for // if bomb planted and it's a CT calculate new path to bomb point if he's not already heading for
if (!m_bombSearchOverridden if (!m_bombSearchOverridden
&& bots.isBombPlanted () && bots.isBombPlanted ()
&& m_team == Team::CT && m_team == Team::CT
&& getTask ()->data != kInvalidNodeIndex && getTask ()->data != kInvalidNodeIndex
&& !(graph[getTask ()->data].flags & NodeFlag::Goal) && !(graph[getTask ()->data].flags & NodeFlag::Goal)
&& getCurrentTaskId () != Task::EscapeFromBomb) { && getCurrentTaskId () != Task::EscapeFromBomb) {
clearSearchNodes (); clearSearchNodes ();
getTask ()->data = kInvalidNodeIndex; getTask ()->data = kInvalidNodeIndex;
@ -77,10 +77,10 @@ void Bot::normal_ () {
if (updateNavigation ()) { if (updateNavigation ()) {
// if we're reached the goal, and there is not enemies, notify the team // if we're reached the goal, and there is not enemies, notify the team
if (!bots.isBombPlanted () if (!bots.isBombPlanted ()
&& m_currentNodeIndex != kInvalidNodeIndex && m_currentNodeIndex != kInvalidNodeIndex
&& (m_pathFlags & NodeFlag::Goal) && (m_pathFlags & NodeFlag::Goal)
&& rg.chance (15) && rg.chance (15)
&& numEnemiesNear (pev->origin, 650.0f) == 0) { && numEnemiesNear (pev->origin, 650.0f) == 0) {
pushRadioMessage (Radio::SectorClear); pushRadioMessage (Radio::SectorClear);
} }
@ -90,12 +90,12 @@ void Bot::normal_ () {
// spray logo sometimes if allowed to do so // spray logo sometimes if allowed to do so
if (!(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy)) if (!(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))
&& m_seeEnemyTime + 5.0f < game.time () && m_seeEnemyTime + 5.0f < game.time ()
&& !m_reloadState && m_timeLogoSpray < game.time () && !m_reloadState && m_timeLogoSpray < game.time ()
&& cv_spraypaints && cv_spraypaints
&& rg.chance (50) && rg.chance (50)
&& m_moveSpeed > getShiftSpeed () && m_moveSpeed > getShiftSpeed ()
&& game.isNullEntity (m_pickupItem)) { && game.isNullEntity (m_pickupItem)) {
if (!(game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_team == Team::CT)) { if (!(game.mapIs (MapFlags::Demolition) && bots.isBombPlanted () && m_team == Team::CT)) {
startTask (Task::Spraypaint, TaskPri::Spraypaint, kInvalidNodeIndex, game.time () + 1.0f, false); startTask (Task::Spraypaint, TaskPri::Spraypaint, kInvalidNodeIndex, game.time () + 1.0f, false);
@ -126,7 +126,7 @@ void Bot::normal_ () {
// don't allow vip on as_ maps to camp + don't allow terrorist carrying c4 to camp // don't allow vip on as_ maps to camp + don't allow terrorist carrying c4 to camp
if (campingAllowed if (campingAllowed
&& (m_isVIP || (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && !bots.isBombPlanted () && m_hasC4))) { && (m_isVIP || (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && !bots.isBombPlanted () && m_hasC4))) {
campingAllowed = false; campingAllowed = false;
} }
@ -180,7 +180,7 @@ void Bot::normal_ () {
// and reached a rescue point? // and reached a rescue point?
if (m_pathFlags & NodeFlag::Rescue) { if (m_pathFlags & NodeFlag::Rescue) {
m_hostages.clear (); m_hostages.clear ();
} }
} }
else if (m_team == Team::Terrorist && rg.chance (75) && !game.mapIs (MapFlags::Demolition)) { else if (m_team == Team::Terrorist && rg.chance (75) && !game.mapIs (MapFlags::Demolition)) {
const int index = findDefendNode (m_path->origin); const int index = findDefendNode (m_path->origin);
@ -268,22 +268,22 @@ void Bot::normal_ () {
const float shiftSpeed = getShiftSpeed (); const float shiftSpeed = getShiftSpeed ();
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))
&& numEnemiesNear (pev->origin, 768.0f) >= 1 && numEnemiesNear (pev->origin, 768.0f) >= 1
&& !isKnifeMode () && !isKnifeMode ()
&& !bots.isBombPlanted ()) { && !bots.isBombPlanted ()) {
m_moveSpeed = shiftSpeed; m_moveSpeed = shiftSpeed;
} }
// bot hasn't seen anything in a long time and is asking his teammates to report in // bot hasn't seen anything in a long time and is asking his teammates to report in
if (cv_radio_mode.as <int> () > 1 if (cv_radio_mode.as <int> () > 1
&& bots.getLastRadio (m_team) != Radio::ReportInTeam && bots.getLastRadio (m_team) != Radio::ReportInTeam
&& bots.getRoundStartTime () + 20.0f < game.time () && bots.getRoundStartTime () + 20.0f < game.time ()
&& m_askCheckTime < game.time () && rg.chance (15) && m_askCheckTime < game.time () && rg.chance (15)
&& m_seeEnemyTime + rg (45.0f, 80.0f) < game.time () && m_seeEnemyTime + rg (45.0f, 80.0f) < game.time ()
&& numFriendsNear (pev->origin, 1024.0f) == 0) { && numFriendsNear (pev->origin, 1024.0f) == 0) {
pushRadioMessage (Radio::ReportInTeam); pushRadioMessage (Radio::ReportInTeam);
@ -943,8 +943,8 @@ void Bot::defuseBomb_ () {
// bot is reloading and we close enough to start defusing // bot is reloading and we close enough to start defusing
if (m_isReloading && bombPos.distanceSq2d (pev->origin) < cr::sqrf (80.0f)) { if (m_isReloading && bombPos.distanceSq2d (pev->origin) < cr::sqrf (80.0f)) {
if (m_numEnemiesLeft == 0 if (m_numEnemiesLeft == 0
|| timeToBlowUp < fullDefuseTime + 7.0f || timeToBlowUp < fullDefuseTime + 7.0f
|| ((getAmmoInClip () > 8 && m_reloadState == Reload::Primary)|| (getAmmoInClip () > 5 && m_reloadState == Reload::Secondary))) { || ((getAmmoInClip () > 8 && m_reloadState == Reload::Primary) || (getAmmoInClip () > 5 && m_reloadState == Reload::Secondary))) {
const int weaponIndex = bestWeaponCarried (); const int weaponIndex = bestWeaponCarried ();
@ -1300,8 +1300,8 @@ void Bot::throwSmoke_ () {
void Bot::doublejump_ () { void Bot::doublejump_ () {
if (!util.isAlive (m_doubleJumpEntity) if (!util.isAlive (m_doubleJumpEntity)
|| (m_aimFlags & AimFlags::Enemy) || (m_aimFlags & AimFlags::Enemy)
|| (m_travelStartIndex != kInvalidNodeIndex && getTask ()->time + (graph.calculateTravelTime (pev->maxspeed, graph[m_travelStartIndex].origin, m_doubleJumpOrigin) + 11.0f) < game.time ())) { || (m_travelStartIndex != kInvalidNodeIndex && getTask ()->time + (graph.calculateTravelTime (pev->maxspeed, graph[m_travelStartIndex].origin, m_doubleJumpOrigin) + 11.0f) < game.time ())) {
resetDoubleJump (); resetDoubleJump ();
return; return;
} }
@ -1648,7 +1648,7 @@ void Bot::pickupItem_ () {
// check if hostage is with a human teammate (hack) // check if hostage is with a human teammate (hack)
for (auto &client : util.getClients ()) { for (auto &client : util.getClients ()) {
if ((client.flags & ClientFlags::Used) && !(client.ent->v.flags & FL_FAKECLIENT) && (client.flags & ClientFlags::Alive) && if ((client.flags & ClientFlags::Used) && !(client.ent->v.flags & FL_FAKECLIENT) && (client.flags & ClientFlags::Alive) &&
client.team == m_team && client.ent->v.origin.distanceSq (ent->v.origin) <= cr::sqrf (240.0f)) { client.team == m_team && client.ent->v.origin.distanceSq (ent->v.origin) <= cr::sqrf (240.0f)) {
return EntitySearchResult::Continue; return EntitySearchResult::Continue;
} }
} }

View file

@ -79,17 +79,17 @@ void Bot::checkDarkness () {
const auto tid = getCurrentTaskId (); const auto tid = getCurrentTaskId ();
if (!flashOn && if (!flashOn &&
tid != Task::Camp tid != Task::Camp
&& tid != Task::Attack && tid != Task::Attack
&& m_heardSoundTime + 3.0f < game.time () && m_heardSoundTime + 3.0f < game.time ()
&& m_flashLevel > 30 && m_flashLevel > 30
&& ((skyColor > 50.0f && lightLevel < 10.0f) || (skyColor <= 50.0f && lightLevel < 40.0f))) { && ((skyColor > 50.0f && lightLevel < 10.0f) || (skyColor <= 50.0f && lightLevel < 40.0f))) {
pev->impulse = 100; pev->impulse = 100;
} }
else if (flashOn else if (flashOn
&& (((lightLevel > 15.0f && skyColor > 50.0f) || (lightLevel > 45.0f && skyColor <= 50.0f)) && (((lightLevel > 15.0f && skyColor > 50.0f) || (lightLevel > 45.0f && skyColor <= 50.0f))
|| tid == Task::Camp || tid == Task::Attack || m_flashLevel <= 0 || m_heardSoundTime + 3.0f >= game.time ())) { || tid == Task::Camp || tid == Task::Attack || m_flashLevel <= 0 || m_heardSoundTime + 3.0f >= game.time ())) {
pev->impulse = 100; pev->impulse = 100;
} }
@ -201,9 +201,9 @@ void Bot::updateLookAngles () {
// just force directioon // just force directioon
if (m_difficulty == Difficulty::Expert if (m_difficulty == Difficulty::Expert
&& (m_aimFlags & AimFlags::Enemy) && (m_aimFlags & AimFlags::Enemy)
&& (m_wantsToFire || usesSniper ()) && (m_wantsToFire || usesSniper ())
&& cv_whose_your_daddy) { && cv_whose_your_daddy) {
pev->v_angle = direction; pev->v_angle = direction;
pev->v_angle.clampAngles (); pev->v_angle.clampAngles ();
@ -300,8 +300,8 @@ void Bot::updateLookAnglesNewbie (const Vector &direction, float delta) {
else { else {
// is it time for bot to randomize the aim direction again (more often where moving) ? // is it time for bot to randomize the aim direction again (more often where moving) ?
if (m_randomizeAnglesTime < game.time () if (m_randomizeAnglesTime < game.time ()
&& ((pev->velocity.length () > 1.0f && ((pev->velocity.length () > 1.0f
&& m_angularDeviation.length () < 5.0f) || m_angularDeviation.length () < 1.0f)) { && m_angularDeviation.length () < 5.0f) || m_angularDeviation.length () < 1.0f)) {
// is the bot standing still ? // is the bot standing still ?
if (pev->velocity.length () < 1.0f) { if (pev->velocity.length () < 1.0f) {
@ -428,10 +428,10 @@ 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 + 1.5f > game.time () || m_seeEnemyTime + 1.5 > game.time ()) if ((m_shootTime + 1.5f > game.time () || m_seeEnemyTime + 1.5 > game.time ())
&& m_forgetLastVictimTimer.elapsed () && m_forgetLastVictimTimer.elapsed ()
&& !m_lastEnemyOrigin.empty () && !m_lastEnemyOrigin.empty ()
&& util.isAlive (m_lastEnemy) && util.isAlive (m_lastEnemy)
&& game.isNullEntity (m_enemy)) { && game.isNullEntity (m_enemy)) {
flags |= AimFlags::LastEnemy; flags |= AimFlags::LastEnemy;
} }
@ -543,11 +543,11 @@ void Bot::setAimDirection () {
if (m_moveToGoal && m_seeEnemyTime + 4.0f < game.time () if (m_moveToGoal && m_seeEnemyTime + 4.0f < game.time ()
&& !m_isStuck && !(pev->button & IN_DUCK) && !m_isStuck && !(pev->button & IN_DUCK)
&& m_currentNodeIndex != kInvalidNodeIndex && m_currentNodeIndex != kInvalidNodeIndex
&& !(m_pathFlags & (NodeFlag::Ladder | NodeFlag::Crouch)) && !(m_pathFlags & (NodeFlag::Ladder | NodeFlag::Crouch))
&& m_pathWalk.hasNext () && !isOnLadder () && m_pathWalk.hasNext () && !isOnLadder ()
&& pev->origin.distanceSq (destOrigin) < cr::sqrf (512.0f)) { && pev->origin.distanceSq (destOrigin) < cr::sqrf (512.0f)) {
const auto nextPathIndex = m_pathWalk.next (); const auto nextPathIndex = m_pathWalk.next ();
const auto nextPathX2 = m_pathWalk.nextX2 (); const auto nextPathX2 = m_pathWalk.nextX2 ();
@ -573,8 +573,8 @@ void Bot::setAimDirection () {
const auto dangerIndex = practice.getIndex (m_team, m_currentNodeIndex, m_currentNodeIndex); const auto dangerIndex = practice.getIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
if (graph.exists (dangerIndex) if (graph.exists (dangerIndex)
&& vistab.visible (m_currentNodeIndex, dangerIndex) && vistab.visible (m_currentNodeIndex, dangerIndex)
&& !(graph[dangerIndex].flags & NodeFlag::Crouch)) { && !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::sqrf (512.0f)) { if (pev->origin.distanceSq (graph[dangerIndex].origin) < cr::sqrf (512.0f)) {
m_lookAt = destOrigin; m_lookAt = destOrigin;