From d82124e5953117199b5eedddc266f628617b7b11 Mon Sep 17 00:00:00 2001 From: jeefo Date: Mon, 29 Jan 2024 08:08:07 +0300 Subject: [PATCH] bot: return of the cheat cvar yb_whose_your_daddy (resolved #513) combat: resolve strafe movement issues combat: resolve bots always standing still with pistols and shotguns vision: take a look at recent victim for some time before changing view angles control: allow bots to be killed silently (ref #514) via commands control: bots that are killed with auto kill timer are now killed silently --- cfg/addons/yapb/conf/lang/de_lang.cfg | 6 ++ cfg/addons/yapb/conf/lang/ru_lang.cfg | 6 ++ ext/crlib | 2 +- inc/manager.h | 2 +- inc/yapb.h | 7 +- src/botlib.cpp | 17 ++++- src/combat.cpp | 98 +++++++++++++++++---------- src/control.cpp | 13 ++-- src/engine.cpp | 3 + src/linkage.cpp | 5 -- src/manager.cpp | 26 +++++-- src/navigate.cpp | 4 +- src/tasks.cpp | 6 +- src/vision.cpp | 17 ++++- 14 files changed, 150 insertions(+), 62 deletions(-) diff --git a/cfg/addons/yapb/conf/lang/de_lang.cfg b/cfg/addons/yapb/conf/lang/de_lang.cfg index 94d95ad..4b2fcc3 100644 --- a/cfg/addons/yapb/conf/lang/de_lang.cfg +++ b/cfg/addons/yapb/conf/lang/de_lang.cfg @@ -2001,6 +2001,12 @@ Specifies the language for bot messages and menus. [TRANSLATED] Gibt die Sprache für Bot-Meldungen und Menüs an. +[ORIGINAL] +Enables or disables extra hard difficulty for bots. + +[TRANSLATED] +Aktiviert oder deaktiviert den extra schweren Schwierigkeitsgrad für Bots. + [ORIGINAL] Selects the heuristic function mode. For debug purposes only. diff --git a/cfg/addons/yapb/conf/lang/ru_lang.cfg b/cfg/addons/yapb/conf/lang/ru_lang.cfg index da55e6c..41c4336 100644 --- a/cfg/addons/yapb/conf/lang/ru_lang.cfg +++ b/cfg/addons/yapb/conf/lang/ru_lang.cfg @@ -2433,6 +2433,12 @@ Specifies minimum amount of seconds bot keep connected, if rotation active. [TRANSLATED] Задаёт минимальное количество секунд, в течение которых бот остаётся подключённым, если чередование активно. +[ORIGINAL] +Enables or disables extra hard difficulty for bots. + +[TRANSLATED] +Включает или выключает очень тяжёлую сложность для ботов. + [ORIGINAL] Specifies maximum amount of seconds bot keep connected, if rotation active. diff --git a/ext/crlib b/ext/crlib index fa4e412..aa8e1e0 160000 --- a/ext/crlib +++ b/ext/crlib @@ -1 +1 @@ -Subproject commit fa4e412972f6dcc136b3f687bd76ef46c18d923f +Subproject commit aa8e1e0eaed2e4b75e6b2a0fff5a68756cddc972 diff --git a/inc/manager.h b/inc/manager.h index 6e902a8..cbbcb4f 100644 --- a/inc/manager.h +++ b/inc/manager.h @@ -95,7 +95,7 @@ public: void kickEveryone (bool instant = false, bool zeroQuota = true); void kickBot (int index); void kickFromTeam (Team team, bool removeAll = false); - void killAllBots (int team = -1); + void killAllBots (int team = -1, bool silent = false); void maintainQuota (); void maintainAutoKill (); void maintainLeaders (); diff --git a/inc/yapb.h b/inc/yapb.h index 66a6429..fa72c35 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -346,6 +346,7 @@ private: Path *m_path {}; // pointer to the current path node String m_chatBuffer {}; // space for strings (say text...) Frustum::Planes m_viewFrustum {}; + CountdownTimer m_forgetLastVictimTimer {}; // time to forget last victim position ? private: int pickBestWeapon (Array &vec, int moneySave); @@ -441,7 +442,7 @@ private: void checkGrenadesThrow (); void checkBurstMode (float distance); void checkSilencer (); - void updateAimDir (); + void setAimDirection (); void updateLookAngles (); void updateBodyAngles (); void updateLookAnglesNewbie (const Vector &direction, float delta); @@ -492,6 +493,7 @@ private: void moveToGoal (); void resetMovement (); void refreshEnemyPredict (); + void setLastVictim (edict_t *victim); void normal_ (); void spraypaint_ (); @@ -646,6 +648,7 @@ public: bool m_isEnemyReachable {}; // direct line to enemy bool m_kickedByRotation {}; // is bot kicked due to rotation ? bool m_kickMeFromServer {}; // kick the bot off the server? + bool m_fireHurtsFriend {}; // firing at enemy will hurt our friend? edict_t *m_doubleJumpEntity {}; // pointer to entity that request double jump edict_t *m_radioEntity {}; // pointer to entity issuing a radio command @@ -659,6 +662,7 @@ public: Vector m_position {}; // position to move to in move to position task Vector m_doubleJumpOrigin {}; // origin of double jump Vector m_lastEnemyOrigin {}; // vector to last enemy origin + Vector m_lastVictimOrigin {}; // last victim origin to watch it ChatCollection m_sayTextBuffer {}; // holds the index & the actual message of the last unprocessed text message of a player BurstMode m_weaponBurstMode {}; // bot using burst mode? (famas/glock18, but also silencer mode) @@ -862,6 +866,7 @@ extern ConVar cv_graph_url_upload; extern ConVar cv_graph_auto_save_count; extern ConVar cv_graph_analyze_max_jump_height; extern ConVar cv_spraypaints; +extern ConVar cv_whose_your_daddy; extern ConVar mp_freezetime; extern ConVar mp_roundtime; diff --git a/src/botlib.cpp b/src/botlib.cpp index d3cb24d..99cdd19 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -263,6 +263,12 @@ edict_t *Bot::lookupBreakable () { } void Bot::setIdealReactionTimers (bool actual) { + if (cv_whose_your_daddy.bool_ ()) { + m_idealReactionTime = 0.05f; + m_actualReactionTime = 0.095f; + + return; // zero out reaction times for extreme mode + } const auto tweak = conf.getDifficultyTweaks (m_difficulty); if (actual) { @@ -1698,6 +1704,13 @@ void Bot::refreshEnemyPredict () { } } +void Bot::setLastVictim (edict_t *ent) { + m_lastVictim = ent; + m_lastVictimOrigin = ent->v.origin + ent->v.view_ofs; + + m_forgetLastVictimTimer.start (rg.get (0.5f, 0.8f)); +} + void Bot::setConditions () { // this function carried out each frame. does all of the sensing, calculates emotions and finally sets the desired // action after applying all of the Filters @@ -3014,7 +3027,7 @@ void Bot::logic () { m_isUsingGrenade = false; executeTasks (); // execute current task - updateAimDir (); // choose aim direction + setAimDirection (); // choose aim direction updateLookAngles (); // and turn to chosen aim direction // the bots wants to fire at something? @@ -3193,7 +3206,7 @@ void Bot::showDebugOverlay () { } StringRef weapon = util.weaponIdToAlias (m_currentWeapon); StringRef debugData = strings.format ( - "\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\n" + "\n\n\n\n\n\n%s (H:%.1f/A:%.1f)- Task: %d=%s Desire:%.02f\n" "Item: %s Clip: %d Ammo: %d%s Money: %d AimFlags: %s\n" "SP=%.02f SSP=%.02f I=%d PG=%d G=%d T: %.02f MT: %d\n" "Enemy=%s Pickup=%s Type=%s Terrain=%s Stuck=%s\n", diff --git a/src/combat.cpp b/src/combat.cpp index 57b99fd..46f4a38 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -218,8 +218,13 @@ bool Bot::seesEnemy (edict_t *player) { if (game.isNullEntity (player)) { return false; } + bool ignoreFieldOfView = false; - if (isInViewCone (player->v.origin) && frustum.check (m_viewFrustum, player) && checkBodyParts (player)) { + if (cv_whose_your_daddy.bool_ () && util.isPlayer (pev->dmg_inflictor) && game.getTeam (pev->dmg_inflictor) != m_team) { + ignoreFieldOfView = true; + } + + if ((ignoreFieldOfView || isInViewCone (player->v.origin)) && frustum.check (m_viewFrustum, player) && checkBodyParts (player)) { m_seeEnemyTime = game.time (); m_lastEnemy = player; m_lastEnemyOrigin = m_enemyOrigin; @@ -314,6 +319,11 @@ bool Bot::lookupEnemies () { continue; } + // extra skill player can see thru smoke... if beeing attacked + if (cv_whose_your_daddy.bool_ () && (player->v.button & (IN_ATTACK | IN_ATTACK2)) && m_viewDistance < m_maxViewDistance) { + nearestDistanceSq = cr::sqrf (m_maxViewDistance); + } + // see if bot can see the player... if (seesEnemy (player)) { if (isEnemyBehindShield (player)) { @@ -362,7 +372,13 @@ bool Bot::lookupEnemies () { pushRadioMessage (Radio::EnemySpotted); } m_targetEntity = nullptr; // stop following when we see an enemy... - m_enemySurpriseTime = game.time () + m_actualReactionTime; + + if (cv_whose_your_daddy.bool_ ()) { + m_enemySurpriseTime = m_actualReactionTime * 0.5f; + } + else { + m_enemySurpriseTime = m_actualReactionTime; + } // zero out reaction time m_actualReactionTime = 0.0f; @@ -976,8 +992,12 @@ void Bot::fireWeapons () { // or if friend in line of fire, stop this too but do not update shoot time if (isFriendInLineOfFire (distance)) { + m_fireHurtsFriend = true; return; } + else { + m_fireHurtsFriend = false; + } int selectId = Weapon::Knife, selectIndex = 0, choosenWeapon = 0; const auto tab = conf.getRawWeapons (); @@ -1148,7 +1168,7 @@ void Bot::attackMovement () { } auto approach = 0; - const auto distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum? + const auto distance = m_lookAt.distance (getEyesPos ()); // how far away is the enemy scum? if (usesKnife ()) { approach = 100; @@ -1168,15 +1188,17 @@ void Bot::attackMovement () { } // only take cover when bomb is not planted and enemy can see the bot or the bot is VIP - if (!game.is (GameFlags::CSDM) && (m_states & Sense::SeeingEnemy) && approach < 30 && !bots.isBombPlanted () && (isInViewCone (m_enemy->v.origin) || m_isVIP)) { - m_moveSpeed = -pev->maxspeed; - startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true); - } - else if (approach < 50) { - m_moveSpeed = 0.0f; - } - else { - m_moveSpeed = pev->maxspeed; + if (!game.is (GameFlags::CSDM)) { + if ((m_states & Sense::SeeingEnemy) && approach < 30 && !bots.isBombPlanted () && (isInViewCone (m_enemy->v.origin) || m_isVIP)) { + m_moveSpeed = -pev->maxspeed; + startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true); + } + else if (approach < 50) { + m_moveSpeed = 0.0f; + } + else { + m_moveSpeed = pev->maxspeed; + } } if (m_lastFightStyleCheck + 3.0f < game.time ()) { @@ -1215,9 +1237,6 @@ void Bot::attackMovement () { else if (usesKnife ()) { m_fightStyle = Fight::Strafe; } - else if (usesKnife () && isInViewCone (m_enemy->v.origin) && game.is (GameFlags::CSDM) && !isInNarrowPlace ()) { - m_fightStyle = Fight::Strafe; - } else { m_fightStyle = Fight::Stay; } @@ -1226,6 +1245,14 @@ void Bot::attackMovement () { if (isDucking () || isInNarrowPlace ()) { m_fightStyle = Fight::Stay; } + const auto pistolStrafeDistance = game.is (GameFlags::CSDM) ? kDoubleSprayDistance * 3.0f : kDoubleSprayDistance; + + // fire hurts friend value here is from previous frame, but acceptable, and saves us alot of cpu cycles + if (m_fireHurtsFriend || ((usesPistol () || usesShotgun ()) + && distance < pistolStrafeDistance + && isInViewCone (m_enemyOrigin))) { + m_fightStyle = Fight::Strafe; + } m_lastFightStyleCheck = game.time (); } @@ -1252,10 +1279,10 @@ void Bot::attackMovement () { const auto &rightSide = m_enemy->v.v_angle.right ().normalize2d_apx (); if ((dirToPoint | rightSide) < 0.0f) { - m_combatStrafeDir = Dodge::Left; + m_combatStrafeDir = Dodge::Right; } else { - m_combatStrafeDir = Dodge::Right; + m_combatStrafeDir = Dodge::Left; } if (rg.chance (30)) { @@ -1267,14 +1294,14 @@ void Bot::attackMovement () { const bool wallOnRight = checkWallOnRight (); const bool wallOnLeft = checkWallOnLeft (); - if (m_combatStrafeDir == Dodge::Right) { + if (m_combatStrafeDir == Dodge::Left) { if (!wallOnLeft) { m_strafeSpeed = -pev->maxspeed; } else if (!wallOnRight) { swapStrafeCombatDir (); - m_strafeSetTime = strafeUpdateTime (); + m_strafeSetTime = strafeUpdateTime (); m_strafeSpeed = pev->maxspeed; } else { @@ -1288,8 +1315,8 @@ void Bot::attackMovement () { } else if (!wallOnLeft) { swapStrafeCombatDir (); - m_strafeSetTime = strafeUpdateTime (); + m_strafeSetTime = strafeUpdateTime (); m_strafeSpeed = -pev->maxspeed; } else { @@ -1298,14 +1325,12 @@ void Bot::attackMovement () { } } + // we're setting strafe speed regardless of move angles, so not resetting forward move here cause bots to behave strange + m_moveSpeed = 0.0f; + if (m_difficulty >= Difficulty::Normal && (m_jumpTime + 5.0f < game.time () && isOnFloor () && rg.get (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.length2d () > 150.0f) && !usesSniper ()) { pev->button |= IN_JUMP; } - - // do not move forward/backward is too far - if (distance > 1024.0f) { - m_moveSpeed = 0.0f; - } } else if (m_fightStyle == Fight::Stay) { const bool alreadyDucking = m_duckTime > game.time () || isDucking (); @@ -1649,16 +1674,18 @@ void Bot::updateTeamCommands () { bool Bot::isGroupOfEnemies (const Vector &location, int numEnemies, float radius) { int numPlayers = 0; + // needs a square radius + const float radiusSq = cr::sqrf (radius); + // search the world for enemy players... for (const auto &client : util.getClients ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent ()) { continue; } - if (client.ent->v.origin.distanceSq (location) < cr::sqrf (radius)) { - // don't target our teammates... + if (client.ent->v.origin.distanceSq (location) < radiusSq) { if (client.team == m_team) { - return false; + return false; // don't target our teammates... } if (numPlayers++ > numEnemies) { @@ -1877,16 +1904,16 @@ void Bot::checkGrenadesThrow () { }; // check if throwing a grenade is a good thing to do... - auto throwingCondition = game.mapIs(MapFlags::GrenadeWar) + const auto throwingCondition = game.mapIs (MapFlags::GrenadeWar) ? false : (preventibleTasks - || isInNarrowPlace() - || cv_ignore_enemies.bool_() + || isInNarrowPlace () + || cv_ignore_enemies.bool_ () || m_isUsingGrenade || m_grenadeRequested || m_isReloading - || (isKnifeMode() && !bots.isBombPlanted()) - || m_grenadeCheckTime >= game.time()); + || (isKnifeMode () && !bots.isBombPlanted ()) + || m_grenadeCheckTime >= game.time ()); if (throwingCondition) { clearThrowStates (m_states); @@ -1896,7 +1923,8 @@ void Bot::checkGrenadesThrow () { // check again in some seconds m_grenadeCheckTime = game.time () + kGrenadeCheckTime; - auto senseCondition = game.mapIs(MapFlags::GrenadeWar) ? false : !(m_states & (Sense::SuspectEnemy | Sense::HearingEnemy)); + const auto senseCondition = game.mapIs (MapFlags::GrenadeWar) ? false : !(m_states & (Sense::SuspectEnemy | Sense::HearingEnemy)); + if (!util.isAlive (m_lastEnemy) || senseCondition) { clearThrowStates (m_states); return; @@ -1939,7 +1967,7 @@ void Bot::checkGrenadesThrow () { } // enemy within a good throw distance? - auto grenadeToThrowCondition = game.mapIs(MapFlags::GrenadeWar) + const auto grenadeToThrowCondition = game.mapIs (MapFlags::GrenadeWar) ? 100.0f : grenadeToThrow == Weapon::Smoke ? 200.0f : 400.0f; diff --git a/src/control.cpp b/src/control.cpp index 49b831f..cf50df1 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -77,17 +77,20 @@ int BotControl::cmdKickBots () { } int BotControl::cmdKillBots () { - enum args { alias = 1, team, max }; + enum args { alias = 1, team, silent, max }; + + // do not issue any messages + bool silentKill = hasArg (silent) && strValue (silent).startsWith ("si"); // if team is specified, kick from specified tram if (strValue (alias).endsWith ("_ct") || intValue (team) == 2 || strValue (team) == "ct") { - bots.killAllBots (Team::CT); + bots.killAllBots (Team::CT, silentKill); } else if (strValue (alias).endsWith ("_t") || intValue (team) == 1 || strValue (team) == "t") { - bots.killAllBots (Team::Terrorist); + bots.killAllBots (Team::Terrorist, silentKill); } else { - bots.killAllBots (); + bots.killAllBots (-1, silentKill); } return BotCommandResult::Handled; } @@ -2138,7 +2141,7 @@ BotControl::BotControl () { m_cmds.emplace ("add/addbot/add_ct/addbot_ct/add_t/addbot_t/addhs/addhs_t/addhs_ct", "add [difficulty] [personality] [team] [model] [name]", "Adding specific bot into the game.", &BotControl::cmdAddBot); m_cmds.emplace ("kick/kickone/kick_ct/kick_t/kickbot_ct/kickbot_t", "kick [team]", "Kicks off the random bot from the game.", &BotControl::cmdKickBot); m_cmds.emplace ("removebots/kickbots/kickall/kickall_ct/kickall_t", "removebots [instant] [team]", "Kicks all the bots from the game.", &BotControl::cmdKickBots); - m_cmds.emplace ("kill/killbots/killall/kill_ct/kill_t", "kill [team]", "Kills the specified team / all the bots.", &BotControl::cmdKillBots); + m_cmds.emplace ("kill/killbots/killall/kill_ct/kill_t", "kill [team] [silent]", "Kills the specified team / all the bots.", &BotControl::cmdKillBots); m_cmds.emplace ("fill/fillserver", "fill [team] [count] [difficulty] [personality]", "Fill the server (add bots) with specified parameters.", &BotControl::cmdFill); m_cmds.emplace ("vote/votemap", "vote [map_id]", "Forces all the bot to vote to specified map.", &BotControl::cmdVote); m_cmds.emplace ("weapons/weaponmode", "weapons [knife|pistol|shotgun|smg|rifle|sniper|standard]", "Sets the bots weapon mode to use", &BotControl::cmdWeaponMode); diff --git a/src/engine.cpp b/src/engine.cpp index 7101472..5747b07 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -86,6 +86,9 @@ void Game::levelInitialize (edict_t *entities, int max) { // flush any print queue ctrl.resetFlushTimestamp (); + // set the global timer function + timerStorage.setTimeAddress (&globals->time); + // go thru the all entities on map, and do whatever we're want for (int i = 0; i < max; ++i) { auto ent = entities + i; diff --git a/src/linkage.cpp b/src/linkage.cpp index a674044..5cf9be1 100644 --- a/src/linkage.cpp +++ b/src/linkage.cpp @@ -953,11 +953,6 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *table, globalvars_t *glob) { memcpy (&engfuncs, table, sizeof (enginefuncs_t)); globals = glob; - // set the global timer function - timerStorage.setTimeFunction ([] () { - return globals->time; - }); - if (game.postload ()) { return; } diff --git a/src/manager.cpp b/src/manager.cpp index 91bc8df..411f486 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -507,7 +507,7 @@ void BotManager::maintainAutoKill () { // check if we're reached the delay, so kill out bots if (!cr::fzero (m_autoKillCheckTime) && m_autoKillCheckTime < game.time ()) { - killAllBots (); + killAllBots (-1, true); m_autoKillCheckTime = 0.0f; return; @@ -679,7 +679,7 @@ void BotManager::kickFromTeam (Team team, bool removeAll) { } } -void BotManager::killAllBots (int team) { +void BotManager::killAllBots (int team, bool silent) { // this function kills all bots on server (only this dll controlled bots) for (const auto &bot : m_bots) { @@ -688,7 +688,10 @@ void BotManager::killAllBots (int team) { } bot->kill (); } - ctrl.msg ("All bots died..."); + + if (!silent) { + ctrl.msg ("All bots died..."); + } } void BotManager::kickBot (int index) { @@ -873,7 +876,10 @@ void BotManager::listBots () { for (const auto &bot : bots) { auto timelimitStr = cv_rotate_bots.bool_ () ? 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", bot->index (), bot->pev->netname.chars (), bot->m_personality == Personality::Rusher ? "rusher" : bot->m_personality == Personality::Normal ? "normal" : "careful", botTeam (bot->m_team), bot->m_difficulty, static_cast (bot->pev->frags), bot->m_isAlive ? "yes" : "no", timelimitStr); + + 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", + botTeam (bot->m_team), bot->m_difficulty, static_cast (bot->pev->frags), bot->m_isAlive ? "yes" : "no", timelimitStr); } ctrl.msg ("%d bots", m_bots.length ()); } @@ -1003,11 +1009,16 @@ void BotManager::updateBotDifficulties () { } void BotManager::balanceBotDifficulties () { - // difficulty chaning once per round (time) + // difficulty changing once per round (time) auto updateDifficulty = [] (Bot *bot, int32_t offset) { bot->m_difficulty = cr::clamp (static_cast (bot->m_difficulty + offset), Difficulty::Noob, Difficulty::Expert); }; + // with nightmare difficulty, there is no balance + if (cv_whose_your_daddy.bool_ ()) { + return; + } + if (cv_difficulty_auto.bool_ () && m_difficultyBalanceTime < game.time ()) { const auto ratioPlayer = getAverageTeamKPD (false); const auto ratioBots = getAverageTeamKPD (true); @@ -1343,7 +1354,7 @@ void BotManager::handleDeath (edict_t *killer, edict_t *victim) { // is this message about a bot who killed somebody? if (killerBot != nullptr) { - killerBot->m_lastVictim = victim; + killerBot->setLastVictim (victim); } // did a human kill a bot on his team? @@ -1441,6 +1452,7 @@ void Bot::newRound () { m_lastVictim = nullptr; m_lastEnemy = nullptr; m_lastEnemyOrigin = nullptr; + m_lastVictimOrigin = nullptr; m_trackingEdict = nullptr; m_timeNextTracking = 0.0f; @@ -1475,6 +1487,7 @@ void Bot::newRound () { m_followWaitTime = 0.0f; m_hostages.clear (); + m_forgetLastVictimTimer.invalidate (); for (auto &timer : m_chatterTimes) { timer = kMaxChatterRepeatInterval; @@ -1494,6 +1507,7 @@ void Bot::newRound () { m_grenadeCheckTime = 0.0f; m_isUsingGrenade = false; m_bombSearchOverridden = false; + m_fireHurtsFriend = false; m_blindButton = 0; m_blindTime = 0.0f; diff --git a/src/navigate.cpp b/src/navigate.cpp index 2ff2e75..5ad3dc2 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -2820,7 +2820,7 @@ bool Bot::isBlockedRight () { bool Bot::checkWallOnLeft () { TraceResult tr {}; - game.testLine (pev->origin, pev->origin + -pev->angles.right () * 45.0f, TraceIgnore::Monsters, ent (), &tr); + game.testLine (pev->origin, pev->origin - pev->angles.right () * 42.0f, TraceIgnore::Monsters, ent (), &tr); // check if the trace hit something... if (tr.flFraction < 1.0f) { @@ -2833,7 +2833,7 @@ bool Bot::checkWallOnRight () { TraceResult tr {}; // do a trace to the right... - game.testLine (pev->origin, pev->origin + pev->angles.right () * 45.0f, TraceIgnore::Monsters, ent (), &tr); + game.testLine (pev->origin, pev->origin + pev->angles.right () * 42.0f, TraceIgnore::Monsters, ent (), &tr); // check if the trace hit something... if (tr.flFraction < 1.0f) { diff --git a/src/tasks.cpp b/src/tasks.cpp index 4672199..3339de9 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -1084,8 +1084,8 @@ void Bot::throwExplosive_ () { ignoreCollision (); - if (!game.mapIs(MapFlags::GrenadeWar) && (pev->origin.distanceSq (dest) < cr::sqrf (450.0f))) { - // heck, I don't wanna blow up myself + if (!game.mapIs (MapFlags::GrenadeWar) && (pev->origin.distanceSq (dest) < cr::sqrf (450.0f))) { + // heck, I don't wanna blow up myself m_grenadeCheckTime = game.time () + kGrenadeCheckTime * 2.0f; selectBestWeapon (); @@ -1099,7 +1099,7 @@ void Bot::throwExplosive_ () { m_grenade = calcToss (pev->origin, dest); } - if (!game.mapIs(MapFlags::GrenadeWar) && (m_grenade.lengthSq () <= 100.0f)) { + if (!game.mapIs (MapFlags::GrenadeWar) && (m_grenade.lengthSq () <= 100.0f)) { m_grenadeCheckTime = game.time () + kGrenadeCheckTime * 2.0f; selectBestWeapon (); diff --git a/src/vision.cpp b/src/vision.cpp index 91bb9e7..c622a39 100644 --- a/src/vision.cpp +++ b/src/vision.cpp @@ -8,6 +8,7 @@ #include ConVar cv_max_nodes_for_predict ("max_nodes_for_predict", "25", "Maximum number for path length, to predict the enemy.", true, 15.0f, 256.0f); +ConVar cv_whose_your_daddy ("whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots."); // game console variables ConVar mp_flashlight ("mp_flashlight", nullptr, Var::GameRef); @@ -58,7 +59,7 @@ bool Bot::seesEntity (const Vector &dest, bool fromBody) { return tr.flFraction >= 1.0f; } -void Bot::updateAimDir () { +void Bot::setAimDirection () { uint32_t flags = m_aimFlags; // don't allow bot to look at danger positions under certain circumstances @@ -220,6 +221,11 @@ void Bot::updateAimDir () { } } + // try to look at last victim for a little, maybe there's some one else + if (game.isNullEntity (m_enemy) && m_difficulty >= Difficulty::Normal && !m_forgetLastVictimTimer.elapsed () && !m_lastVictimOrigin.empty ()) { + m_lookAt = m_lastVictimOrigin; + } + // don't look at bottom of node, if reached it if (m_lookAt == m_destOrigin && !onLadder) { m_lookAt.z = getEyesPos ().z; @@ -299,6 +305,15 @@ void Bot::updateLookAngles () { return; } + + // just force directioon + if (m_difficulty == Difficulty::Expert && (m_aimFlags & AimFlags::Enemy) && (m_wantsToFire || usesSniper ()) && cv_whose_your_daddy.bool_ ()) { + pev->v_angle = direction; + pev->v_angle.clampAngles (); + + updateBodyAngles (); + return; + } const float aimSkill = cr::clamp (static_cast (m_difficulty), 1.0f, 4.0f) * 25.0f; float accelerate = aimSkill * 30.0f;