nav: send terrorists directly guard planted bomp
aim: various fixes and tweaks bot: removed yb_whose_your_daddy cvar
This commit is contained in:
parent
38f2716edb
commit
722e4eda93
5 changed files with 269 additions and 256 deletions
|
|
@ -737,7 +737,7 @@ private:
|
|||
Vector m_enemyOrigin {}; // target origin chosen for shooting
|
||||
Vector m_grenade {}; // calculated vector for grenades
|
||||
Vector m_entity {}; // origin of entities like buttons etc.
|
||||
Vector m_camp {}; // aiming vector when camping.
|
||||
Vector m_lookAtSafe {}; // aiming vector when camping.
|
||||
Vector m_desiredVelocity {}; // desired velocity for jump nodes
|
||||
Vector m_breakableOrigin {}; // origin of breakable
|
||||
|
||||
|
|
@ -754,12 +754,13 @@ private:
|
|||
private:
|
||||
int pickBestWeapon (int *vec, int count, int moneySave);
|
||||
int findCampingDirection ();
|
||||
int findAimingNode (const Vector &to);
|
||||
int findAimingNode (const Vector &to, int &pathLength);
|
||||
int findNearestNode ();
|
||||
int findBombNode ();
|
||||
int findCoverNode (float maxDistance);
|
||||
int findDefendNode (const Vector &origin);
|
||||
int findBestGoal ();
|
||||
int findBestGoalWhenBombAction ();
|
||||
int findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive);
|
||||
int bestPrimaryCarried ();
|
||||
int bestSecondaryCarried ();
|
||||
|
|
@ -1181,7 +1182,6 @@ extern ConVar cv_chat;
|
|||
extern ConVar cv_language;
|
||||
extern ConVar cv_show_latency;
|
||||
extern ConVar cv_enable_query_hook;
|
||||
extern ConVar cv_whose_your_daddy;
|
||||
extern ConVar cv_chatter_path;
|
||||
extern ConVar cv_quota;
|
||||
extern ConVar cv_difficulty;
|
||||
|
|
@ -1202,6 +1202,7 @@ extern ConVar mp_limitteams;
|
|||
extern ConVar mp_autoteambalance;
|
||||
extern ConVar mp_footsteps;
|
||||
extern ConVar mp_startmoney;
|
||||
extern ConVar mp_c4timer;
|
||||
|
||||
// execute client command helper
|
||||
template <typename ...Args> void Bot::issueCommand (const char *fmt, Args &&...args) {
|
||||
|
|
|
|||
137
src/botlib.cpp
137
src/botlib.cpp
|
|
@ -502,14 +502,6 @@ edict_t *Bot::lookupBreakable () {
|
|||
}
|
||||
|
||||
void Bot::setIdealReactionTimers (bool actual) {
|
||||
|
||||
// zero out reaction times for extreme mode
|
||||
if (cv_whose_your_daddy.bool_ ()) {
|
||||
m_idealReactionTime = 0.05f;
|
||||
m_actualReactionTime = 0.095f;
|
||||
|
||||
return;
|
||||
}
|
||||
const auto tweak = conf.getDifficultyTweaks (m_difficulty);
|
||||
|
||||
if (actual) {
|
||||
|
|
@ -1886,8 +1878,17 @@ void Bot::setConditions () {
|
|||
m_states &= ~Sense::HearingEnemy;
|
||||
}
|
||||
|
||||
if (game.isNullEntity (m_enemy) && !game.isNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.empty ()) {
|
||||
if (game.isNullEntity (m_enemy) && !game.isNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.empty () && m_seeEnemyTime + 0.5f < game.time ()) {
|
||||
auto distanceToLastEnemySq = m_lastEnemyOrigin.distanceSq (pev->origin);
|
||||
|
||||
if (distanceToLastEnemySq < cr::square (1600.0f)) {
|
||||
auto pathLength = 0;
|
||||
auto nodeIndex = findAimingNode (m_lastEnemyOrigin, pathLength);
|
||||
|
||||
if (graph.exists (nodeIndex) && pathLength < kMaxBucketsInsidePos * 2 && pev->origin.distanceSq (graph[nodeIndex].origin) > cr::square (384.0f)) {
|
||||
m_aimFlags |= AimFlags::PredictPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (seesEntity (m_lastEnemyOrigin, true)) {
|
||||
m_aimFlags |= AimFlags::LastEnemy;
|
||||
|
|
@ -1985,7 +1986,7 @@ void Bot::filterTasks () {
|
|||
ratio = timeHeard * 0.1f;
|
||||
}
|
||||
bool lowAmmo = isLowOnAmmo (m_currentWeapon, 0.18f);
|
||||
bool sniping = m_sniperStopTime <= game.time () && lowAmmo;
|
||||
bool sniping = m_sniperStopTime > game.time () && lowAmmo;
|
||||
|
||||
if (bots.isBombPlanted () || m_isStuck || usesKnife ()) {
|
||||
ratio /= 3.0f; // reduce the seek cover desire if bomb is planted
|
||||
|
|
@ -2080,7 +2081,7 @@ void Bot::filterTasks () {
|
|||
auto survive = thresholdDesire (&filter[Task::SeekCover], 40.0f, 0.0f);
|
||||
survive = subsumeDesire (&filter[Task::Hide], survive);
|
||||
|
||||
auto def = thresholdDesire (&filter[Task::Hunt], 41.0f, 0.0f); // don't allow hunting if desires 60<
|
||||
auto def = thresholdDesire (&filter[Task::Hunt], 60.0f, 0.0f); // don't allow hunting if desires 60<
|
||||
offensive = subsumeDesire (offensive, pickup); // if offensive task, don't allow picking up stuff
|
||||
|
||||
auto sub = maxDesire (offensive, def); // default normal & careful tasks against offensive actions
|
||||
|
|
@ -2771,7 +2772,7 @@ void Bot::updateAimDir () {
|
|||
}
|
||||
|
||||
if (flags & AimFlags::Override) {
|
||||
m_lookAt = m_camp;
|
||||
m_lookAt = m_lookAtSafe;
|
||||
}
|
||||
else if (flags & AimFlags::Grenade) {
|
||||
m_lookAt = m_throw;
|
||||
|
|
@ -2802,6 +2803,9 @@ void Bot::updateAimDir () {
|
|||
if (m_pickupType == Pickup::Hostage) {
|
||||
m_lookAt.z += 48.0f;
|
||||
}
|
||||
else if (m_pickupType == Pickup::Weapon) {
|
||||
m_lookAt.z += 72.0;
|
||||
}
|
||||
}
|
||||
else if (flags & AimFlags::LastEnemy) {
|
||||
m_lookAt = m_lastEnemyOrigin;
|
||||
|
|
@ -2814,100 +2818,59 @@ void Bot::updateAimDir () {
|
|||
m_wantsToFire = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto distanceToLastEnemySq = m_lastEnemyOrigin.distanceSq (pev->origin);
|
||||
auto maxDistanceToEnemySq = cr::square (1600.0f);
|
||||
|
||||
if (distanceToLastEnemySq >= maxDistanceToEnemySq) {
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
m_aimFlags &= ~AimFlags::LastEnemy;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (flags & AimFlags::PredictPath) {
|
||||
TraceResult tr {};
|
||||
bool changePredictedEnemy = true;
|
||||
|
||||
auto distanceToLastEnemySq = m_lastEnemyOrigin.distanceSq (pev->origin);
|
||||
auto maxDistanceToEnemySq = cr::square (1600.0f);
|
||||
|
||||
if ((distanceToLastEnemySq < maxDistanceToEnemySq || usesSniper ()) && util.isAlive (m_lastEnemy)) {
|
||||
game.testLine (getEyesPos (), m_lastEnemyOrigin, TraceIgnore::Glass, pev->pContainingEntity, &tr);
|
||||
|
||||
if (tr.flFraction >= 0.2f || tr.pHit != game.getStartEntity ()) {
|
||||
auto changePredictedEnemy = true;
|
||||
|
||||
if (m_timeNextTracking < game.time () && m_trackingEdict == m_lastEnemy && util.isAlive (m_lastEnemy)) {
|
||||
if (m_timeNextTracking > game.time () && m_trackingEdict == m_lastEnemy) {
|
||||
changePredictedEnemy = false;
|
||||
}
|
||||
|
||||
if (changePredictedEnemy) {
|
||||
auto aimPoint = findAimingNode (m_lastEnemyOrigin);
|
||||
auto pathLength = 0;
|
||||
auto aimNode = findAimingNode (m_lastEnemyOrigin, pathLength);
|
||||
|
||||
if (aimPoint != kInvalidNodeIndex) {
|
||||
m_lookAt = graph[aimPoint].origin + pev->view_ofs;
|
||||
m_camp = m_lookAt;
|
||||
if (graph.exists (aimNode) && pathLength < kMaxBucketsInsidePos * 2) {
|
||||
m_lookAt = graph[aimNode].origin;
|
||||
m_lookAtSafe = m_lookAt;
|
||||
|
||||
m_timeNextTracking = game.time () + 0.5f;
|
||||
m_trackingEdict = m_lastEnemy;
|
||||
|
||||
if (!usesSniper () && lastEnemyShootable ()) {
|
||||
m_wantsToFire = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_camp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_camp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_aimFlags &= ~AimFlags::PredictPath; // forget enemy far away
|
||||
|
||||
if (distanceToLastEnemySq >= maxDistanceToEnemySq) {
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
if (!m_lookAtSafe.empty ()) {
|
||||
m_lookAt = m_lookAtSafe;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_aimFlags &= ~AimFlags::PredictPath; // forget enemy far away
|
||||
|
||||
if (distanceToLastEnemySq >= maxDistanceToEnemySq) {
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
}
|
||||
m_lookAt = m_lookAtSafe;
|
||||
}
|
||||
}
|
||||
else if (flags & AimFlags::Camp) {
|
||||
m_lookAt = m_camp;
|
||||
m_lookAt = m_lookAtSafe;
|
||||
}
|
||||
else if (flags & AimFlags::Nav) {
|
||||
m_lookAt = m_destOrigin;
|
||||
|
||||
auto smoothView = [&] (int32 index) -> Vector {
|
||||
auto radius = graph[index].radius;
|
||||
|
||||
if (radius > 0.0f) {
|
||||
return Vector (pev->angles.x, cr::wrapAngle (pev->angles.y + rg.get (-90.0f, 90.0f)), 0.0f).forward () * rg.get (2.0f, 4.0f);
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
if (m_moveToGoal && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && pev->origin.distanceSq (m_destOrigin) < cr::square (160.0f)) {
|
||||
if (m_moveToGoal && m_seeEnemyTime + 4.0f < game.time () && !m_isStuck && m_moveSpeed > getShiftSpeed () && !(pev->button & IN_DUCK) && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & (NodeFlag::Ladder | NodeFlag::Crouch)) && m_pathWalk.hasNext () && pev->origin.distanceSq (m_destOrigin) < cr::square (240.0f)) {
|
||||
auto nextPathIndex = m_pathWalk.next ();
|
||||
|
||||
if (graph.isVisible (m_currentNodeIndex, nextPathIndex)) {
|
||||
m_lookAt = graph[nextPathIndex].origin + pev->view_ofs + smoothView (nextPathIndex);
|
||||
m_lookAt = graph[nextPathIndex].origin;
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
}
|
||||
else if (m_seeEnemyTime + 3.0f > game.time () && !m_lastEnemyOrigin.empty ()){
|
||||
m_lookAt = m_lastEnemyOrigin;;
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
|
||||
if (m_canChooseAimDirection && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & NodeFlag::Ladder)) {
|
||||
if (m_canChooseAimDirection && m_seeEnemyTime + 4.0f < game.time () && m_currentNodeIndex != kInvalidNodeIndex && !(m_path->flags & NodeFlag::Ladder)) {
|
||||
auto dangerIndex = graph.getDangerIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
|
||||
|
||||
if (graph.exists (dangerIndex) && graph.isVisible (m_currentNodeIndex, dangerIndex) && !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
|
||||
|
|
@ -2915,13 +2878,18 @@ void Bot::updateAimDir () {
|
|||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
else {
|
||||
m_lookAt = graph[dangerIndex].origin + pev->view_ofs + smoothView (dangerIndex);
|
||||
m_lookAt = graph[dangerIndex].origin + pev->view_ofs;
|
||||
|
||||
// add danger flags
|
||||
m_aimFlags |= AimFlags::Danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aloways use our z
|
||||
if (m_lookAt == m_destOrigin) {
|
||||
m_lookAt.z = getEyesPos ().z;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_lookAt.empty ()) {
|
||||
|
|
@ -3028,12 +2996,6 @@ void Bot::frame () {
|
|||
kick ();
|
||||
return;
|
||||
}
|
||||
|
||||
// clear enemy far away
|
||||
if (!m_lastEnemyOrigin.empty () && !game.isNullEntity (m_lastEnemy) && pev->origin.distanceSq (m_lastEnemyOrigin) >= cr::square (1600.0f)) {
|
||||
m_lastEnemy = nullptr;
|
||||
m_lastEnemyOrigin = nullptr;
|
||||
}
|
||||
m_slowFrameTimestamp = game.time () + 0.5f;
|
||||
}
|
||||
|
||||
|
|
@ -3227,7 +3189,7 @@ void Bot::normal_ () {
|
|||
m_timeCamping = game.time () + rg.get (cv_camping_time_min.float_ (), cv_camping_time_max.float_ ());
|
||||
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, m_timeCamping, true);
|
||||
|
||||
m_camp = m_path->origin + m_path->start.forward () * 500.0f;
|
||||
m_lookAtSafe = m_path->origin + m_path->start.forward () * 500.0f;
|
||||
m_aimFlags |= AimFlags::Camp;
|
||||
m_campDirection = 0;
|
||||
|
||||
|
|
@ -3507,7 +3469,7 @@ void Bot::seekCover_ () {
|
|||
getCampDirection (&dest);
|
||||
|
||||
m_aimFlags |= AimFlags::Camp;
|
||||
m_camp = dest;
|
||||
m_lookAtSafe = dest;
|
||||
m_campDirection = 0;
|
||||
|
||||
// chosen waypoint is a camp waypoint?
|
||||
|
|
@ -3609,7 +3571,7 @@ void Bot::pause_ () {
|
|||
if (m_moveSpeed < -pev->maxspeed) {
|
||||
m_moveSpeed = -pev->maxspeed;
|
||||
}
|
||||
m_camp = getEyesPos () + pev->v_angle.forward () * 500.0f;
|
||||
m_lookAtSafe = getEyesPos () + pev->v_angle.forward () * 500.0f;
|
||||
|
||||
m_aimFlags |= AimFlags::Override;
|
||||
m_wantsToFire = true;
|
||||
|
|
@ -3757,22 +3719,23 @@ void Bot::camp_ () {
|
|||
}
|
||||
|
||||
if (--numFoundPoints >= 0) {
|
||||
m_camp = graph[campPoints[rg.get (0, numFoundPoints)]].origin;
|
||||
m_lookAtSafe = graph[campPoints[rg.get (0, numFoundPoints)]].origin;
|
||||
}
|
||||
else {
|
||||
m_camp = graph[findCampingDirection ()].origin;
|
||||
m_lookAtSafe = graph[findCampingDirection ()].origin;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((!game.isNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.empty ()) || util.isAlive (m_lastEnemy)) {
|
||||
auto lastEnemyNearestIndex = findAimingNode (m_lastEnemyOrigin);
|
||||
auto pathLength = 0;
|
||||
auto lastEnemyNearestIndex = findAimingNode (m_lastEnemyOrigin, pathLength);
|
||||
|
||||
if (lastEnemyNearestIndex != kInvalidNodeIndex && graph.isVisible (m_currentNodeIndex, lastEnemyNearestIndex)) {
|
||||
m_camp = graph[lastEnemyNearestIndex].origin;
|
||||
if (pathLength > 0 && graph.exists (lastEnemyNearestIndex)) {
|
||||
m_lookAtSafe = graph[lastEnemyNearestIndex].origin;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_camp = graph[findCampingDirection ()].origin;
|
||||
m_lookAtSafe = graph[findCampingDirection ()].origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4551,7 +4514,7 @@ void Bot::shootBreakable_ () {
|
|||
m_checkTerrain = false;
|
||||
m_moveToGoal = false;
|
||||
m_navTimeset = game.time ();
|
||||
m_camp = m_breakableOrigin;
|
||||
m_lookAtSafe = m_breakableOrigin;
|
||||
|
||||
// is bot facing the breakable?
|
||||
if (util.getShootingCone (ent (), m_breakableOrigin) >= 0.90f) {
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ bool Bot::seesEnemy (edict_t *player, bool ignoreFOV) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (cv_whose_your_daddy.bool_ () && util.isPlayer (pev->dmg_inflictor) && game.getTeam (pev->dmg_inflictor) != m_team) {
|
||||
if (m_difficulty == Difficulty::Expert && m_kpdRatio < 1.5f && util.isPlayer (pev->dmg_inflictor) && game.getTeam (pev->dmg_inflictor) != m_team) {
|
||||
ignoreFOV = true;
|
||||
}
|
||||
|
||||
|
|
@ -276,7 +276,7 @@ bool Bot::lookupEnemies () {
|
|||
float scaleFactor = (1.0f / calculateScaleFactor (intresting));
|
||||
float distance = intresting->v.origin.distanceSq (pev->origin) * scaleFactor;
|
||||
|
||||
if (distance * 0.7f < nearestDistance) {
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
newEnemy = intresting;
|
||||
}
|
||||
|
|
@ -297,7 +297,7 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
|
||||
// extra skill player can see thru smoke... if beeing attacked
|
||||
if ((player->v.button & (IN_ATTACK | IN_ATTACK2)) && m_viewDistance < m_maxViewDistance && cv_whose_your_daddy.bool_ ()) {
|
||||
if ((player->v.button & (IN_ATTACK | IN_ATTACK2)) && m_viewDistance < m_maxViewDistance && m_difficulty == Difficulty::Expert) {
|
||||
nearestDistance = cr::square (m_maxViewDistance);
|
||||
}
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
float distance = player->v.origin.distanceSq (pev->origin);
|
||||
|
||||
if (distance * 0.7f < nearestDistance) {
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
newEnemy = player;
|
||||
|
||||
|
|
@ -350,7 +350,7 @@ bool Bot::lookupEnemies () {
|
|||
}
|
||||
m_targetEntity = nullptr; // stop following when we see an enemy...
|
||||
|
||||
if (cv_whose_your_daddy.bool_ ()) {
|
||||
if (m_difficulty == Difficulty::Expert) {
|
||||
m_enemySurpriseTime = m_actualReactionTime * 0.5f;
|
||||
}
|
||||
else {
|
||||
|
|
@ -403,7 +403,7 @@ bool Bot::lookupEnemies () {
|
|||
// shoot at dying players if no new enemy to give some more human-like illusion
|
||||
if (m_seeEnemyTime + 0.1f > game.time ()) {
|
||||
if (!usesSniper ()) {
|
||||
m_shootAtDeadTime = game.time () + cr::clamp (m_agressionLevel * 1.25f, 0.45f, 0.60f);
|
||||
m_shootAtDeadTime = game.time () + cr::clamp (m_agressionLevel * 1.25f, 0.25f, 0.45f);
|
||||
m_actualReactionTime = 0.0f;
|
||||
m_states |= Sense::SuspectEnemy;
|
||||
|
||||
|
|
@ -493,10 +493,13 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
else if (distance < 800.0f && usesSniper ()) {
|
||||
m_enemyParts &= ~Visibility::Head;
|
||||
}
|
||||
else if (distance < kSprayDistance / 2 && !usesPistol ()) {
|
||||
m_enemyParts &= ~Visibility::Head;
|
||||
}
|
||||
Vector aimPos = m_enemy->v.origin;
|
||||
|
||||
if (m_difficulty > Difficulty::Normal) {
|
||||
aimPos += (m_enemy->v.velocity - pev->velocity) * (getFrameInterval () * 1.25f);
|
||||
aimPos += (m_enemy->v.velocity - pev->velocity) * (getFrameInterval () * 2.0f);
|
||||
}
|
||||
|
||||
// if we only suspect an enemy behind a wall take the worst skill
|
||||
|
|
@ -504,12 +507,12 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
aimPos += getBodyOffsetError (distance);
|
||||
}
|
||||
else if (util.isPlayer (m_enemy)) {
|
||||
const float highOffset = cv_whose_your_daddy.bool_ () ? 1.5f : 0.0f;
|
||||
const float highOffset = m_kpdRatio < 1.0f ? 1.5f : 0.0f;
|
||||
|
||||
// now take in account different parts of enemy body
|
||||
if (m_enemyParts & (Visibility::Head | Visibility::Body)) {
|
||||
auto headshotPct = conf.getDifficultyTweaks (m_difficulty)->headshotPct;
|
||||
auto onLoosingStreak = m_fearLevel > m_agressionLevel || m_kpdRatio < 1.5f;
|
||||
auto onLoosingStreak = (m_fearLevel > m_agressionLevel || m_kpdRatio < 1.25f);
|
||||
|
||||
// reduce headshot percent in case we're play too good
|
||||
if (!onLoosingStreak) {
|
||||
|
|
@ -518,7 +521,7 @@ const Vector &Bot::getEnemyBodyOffset () {
|
|||
|
||||
// now check is our skill match to aim at head, else aim at enemy body
|
||||
if (rg.chance (headshotPct)) {
|
||||
if (onLoosingStreak || cv_whose_your_daddy.bool_ ()) {
|
||||
if (onLoosingStreak) {
|
||||
aimPos.z = headOffset (m_enemy) + getEnemyBodyOffsetCorrection (distance);
|
||||
}
|
||||
else {
|
||||
|
|
@ -558,13 +561,13 @@ float Bot::getEnemyBodyOffsetCorrection (float distance) {
|
|||
static float offsetRanges[9][3] = {
|
||||
{ 0.0f, 0.0f, 0.0f }, // none
|
||||
{ 0.0f, 0.0f, 0.0f }, // melee
|
||||
{ 2.5f, 1.5f, 0.2f }, // pistol
|
||||
{ 1.5f, 1.5f, -1.2f }, // pistol
|
||||
{ 6.5f, 0.0f, -9.9f }, // shotgun
|
||||
{ 0.5f, -6.5f, -9.0f }, // zoomrifle
|
||||
{ 0.5f, -6.5f, -9.5f }, // rifle
|
||||
{ 0.5f, -8.5f, -11.0f }, // zoomrifle
|
||||
{ 0.5f, -8.5f, -11.5f }, // rifle
|
||||
{ 2.5f, 0.5f, -4.5f }, // smg
|
||||
{ 0.5f, 0.5f, 1.5f }, // sniper
|
||||
{ 1.5f, -2.0f, -9.0f } // heavy
|
||||
{ 1.5f, -2.0f, -12.0f } // heavy
|
||||
};
|
||||
|
||||
// only highskilled bots do that
|
||||
|
|
@ -769,16 +772,16 @@ bool Bot::needToPauseFiring (float distance) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
float offset = 5.0f;
|
||||
float offset = 4.25f;
|
||||
|
||||
if (distance < kSprayDistance * 0.5f) {
|
||||
if (distance < kSprayDistance / 4) {
|
||||
return false;
|
||||
}
|
||||
else if (distance < kSprayDistance) {
|
||||
offset = 12.0f;
|
||||
offset = 10.0f;
|
||||
}
|
||||
else if (distance < kDoubleSprayDistance) {
|
||||
offset = 10.0f;
|
||||
offset = 8.0f;
|
||||
}
|
||||
const float xPunch = cr::deg2rad (pev->punchangle.x);
|
||||
const float yPunch = cr::deg2rad (pev->punchangle.y);
|
||||
|
|
@ -1117,10 +1120,13 @@ void Bot::attackMovement () {
|
|||
if (game.isNullEntity (m_enemy)) {
|
||||
return;
|
||||
}
|
||||
float distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum?
|
||||
|
||||
if (m_lastUsedNodesTime + getFrameInterval () + 0.5f < game.time ()) {
|
||||
int approach;
|
||||
if (m_lastUsedNodesTime - getFrameInterval () > game.time ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto approach = 0;
|
||||
auto distance = m_lookAt.distance2d (getEyesPos ()); // how far away is the enemy scum?
|
||||
|
||||
if (usesKnife ()) {
|
||||
approach = 100;
|
||||
|
|
@ -1155,12 +1161,11 @@ void Bot::attackMovement () {
|
|||
m_moveSpeed = -pev->maxspeed;
|
||||
}
|
||||
|
||||
if (m_lastFightStyleCheck + 3.0f < game.time ()) {
|
||||
if (usesSniper ()) {
|
||||
m_fightStyle = Fight::Stay;
|
||||
m_lastFightStyleCheck = game.time ();
|
||||
}
|
||||
else if (usesRifle () || usesSubmachine ()) {
|
||||
if (m_lastFightStyleCheck + 3.0f < game.time ()) {
|
||||
else if (usesRifle () || usesSubmachine () || usesHeavy ()) {
|
||||
int rand = rg.get (1, 100);
|
||||
|
||||
if (distance < 500.0f) {
|
||||
|
|
@ -1182,18 +1187,17 @@ void Bot::attackMovement () {
|
|||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rg.get (0, 100) < (isInNarrowPlace () ? 25 : 75)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
else {
|
||||
m_fightStyle = Fight::Stay;
|
||||
}
|
||||
m_lastFightStyleCheck = game.time ();
|
||||
}
|
||||
}
|
||||
else if (rg.get (0, 100) < (isInNarrowPlace () ? 25 : 100)) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
|
||||
if (isInViewCone (m_enemy->v.origin) && usesKnife ()) {
|
||||
m_fightStyle = Fight::Strafe;
|
||||
}
|
||||
|
||||
if ((m_difficulty >= Difficulty::Normal && m_fightStyle == Fight::Strafe) || ((pev->button & IN_RELOAD) || m_isReloading) || (usesPistol () && distance < 768.0f) || usesKnife ()) {
|
||||
if (m_fightStyle == Fight::Strafe) {
|
||||
if (m_strafeSetTime < game.time ()) {
|
||||
|
||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||
|
|
@ -1210,7 +1214,7 @@ void Bot::attackMovement () {
|
|||
if (rg.chance (30)) {
|
||||
m_combatStrafeDir = (m_combatStrafeDir == Dodge::Left ? Dodge::Right : Dodge::Left);
|
||||
}
|
||||
m_strafeSetTime = game.time () + rg.get (1.0f, 3.0f);
|
||||
m_strafeSetTime = game.time () + rg.get (1.2f, 2.4f);
|
||||
}
|
||||
|
||||
if (m_combatStrafeDir == Dodge::Right) {
|
||||
|
|
@ -1219,7 +1223,7 @@ void Bot::attackMovement () {
|
|||
}
|
||||
else {
|
||||
m_combatStrafeDir = Dodge::Left;
|
||||
m_strafeSetTime = game.time () + rg.get (1.0f, 1.5f);
|
||||
m_strafeSetTime = game.time () + rg.get (0.8f, 1.2f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -1228,7 +1232,7 @@ void Bot::attackMovement () {
|
|||
}
|
||||
else {
|
||||
m_combatStrafeDir = Dodge::Right;
|
||||
m_strafeSetTime = game.time () + rg.get (1.0f, 1.5f);
|
||||
m_strafeSetTime = game.time () + rg.get (0.8f, 1.2f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1248,11 +1252,10 @@ void Bot::attackMovement () {
|
|||
m_strafeSpeed = 0.0f;
|
||||
m_navTimeset = game.time ();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_difficulty >= Difficulty::Hard && isOnFloor () && (m_duckTime < game.time ())) {
|
||||
if (distance < 768.0f) {
|
||||
if (rg.get (0, 1000) < rg.get (5, 10) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) {
|
||||
if (rg.get (0, 1000) < rg.get (7, 12) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) {
|
||||
pev->button |= IN_JUMP;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -912,10 +912,6 @@ void BotManager::updateBotDifficulties () {
|
|||
}
|
||||
|
||||
void BotManager::balanceBotDifficulties () {
|
||||
// with nightmare difficulty, there is no balance
|
||||
if (cv_whose_your_daddy.bool_ ()) {
|
||||
return;
|
||||
}
|
||||
// difficulty chaning once per round (time)
|
||||
auto updateDifficulty = [] (Bot *bot, int32 offset) {
|
||||
bot->m_difficulty = cr::clamp (static_cast <Difficulty> (bot->m_difficulty + offset), Difficulty::Noob, Difficulty::Expert);
|
||||
|
|
|
|||
106
src/navigate.cpp
106
src/navigate.cpp
|
|
@ -7,42 +7,22 @@
|
|||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar cv_whose_your_daddy ("yb_whose_your_daddy", "0", "Enables or disables extra hard difficulty for bots.");
|
||||
ConVar cv_path_heuristic_mode ("yb_path_heuristic_mode", "4", "Selects the heuristic function mode. For debug purposes only.", true, 0.0f, 4.0f);
|
||||
|
||||
ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bound of danger factor that used to add additional danger to path based on practice.", true, 100.0f, 2400.0f);
|
||||
ConVar cv_path_danger_factor_max ("yb_path_danger_factor_max", "400", "Upper bound of danger factor that used to add additional danger to path based on practice.", true, 200.0f, 4800.0f);
|
||||
|
||||
int Bot::findBestGoal () {
|
||||
|
||||
auto pushToHistroy = [&] (int32 goal) -> int32 {
|
||||
m_goalHistory.push (goal);
|
||||
return goal;
|
||||
};
|
||||
|
||||
// chooses a destination (goal) node for a bot
|
||||
if (!bots.isBombPlanted () && m_team == Team::Terrorist && game.mapIs (MapFlags::Demolition)) {
|
||||
int result = kInvalidNodeIndex;
|
||||
|
||||
game.searchEntities ("classname", "weaponbox", [&] (edict_t *ent) {
|
||||
if (util.isModel (ent, "backpack.mdl")) {
|
||||
result = graph.getNearest (game.getEntityOrigin (ent));
|
||||
if (m_team == Team::Terrorist && game.mapIs (MapFlags::Demolition)) {
|
||||
auto result = findBestGoalWhenBombAction ();
|
||||
|
||||
if (graph.exists (result)) {
|
||||
return EntitySearchResult::Break;
|
||||
}
|
||||
}
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
|
||||
// found one ?
|
||||
if (graph.exists (result)) {
|
||||
return m_loosedBombNodeIndex = result;
|
||||
}
|
||||
|
||||
// forcing terrorist bot to not move to another bomb spot
|
||||
if (m_inBombZone && !m_hasProgressBar && m_hasC4) {
|
||||
return graph.getNearest (pev->origin, 768.0f, NodeFlag::Goal);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
int tactic = 0;
|
||||
|
|
@ -165,6 +145,68 @@ int Bot::findBestGoal () {
|
|||
return pushToHistroy (findGoalPost (tactic, defensiveNodes, offensiveNodes));
|
||||
}
|
||||
|
||||
int Bot::findBestGoalWhenBombAction () {
|
||||
int result = kInvalidNodeIndex;
|
||||
|
||||
if (!bots.isBombPlanted ()) {
|
||||
game.searchEntities ("classname", "weaponbox", [&] (edict_t *ent) {
|
||||
if (util.isModel (ent, "backpack.mdl")) {
|
||||
result = graph.getNearest (game.getEntityOrigin (ent));
|
||||
|
||||
if (graph.exists (result)) {
|
||||
return EntitySearchResult::Break;
|
||||
}
|
||||
}
|
||||
return EntitySearchResult::Continue;
|
||||
});
|
||||
|
||||
// found one ?
|
||||
if (graph.exists (result)) {
|
||||
return m_loosedBombNodeIndex = result;
|
||||
}
|
||||
|
||||
// forcing terrorist bot to not move to another bomb spot
|
||||
if (m_inBombZone && !m_hasProgressBar && m_hasC4) {
|
||||
return graph.getNearest (pev->origin, 1024.0f, NodeFlag::Goal);
|
||||
}
|
||||
}
|
||||
else if (!m_defendedBomb) {
|
||||
const auto &bombOrigin = graph.getBombOrigin ();
|
||||
|
||||
if (!bombOrigin.empty ()) {
|
||||
m_defendedBomb = true;
|
||||
|
||||
result = findDefendNode (bombOrigin);
|
||||
const Path &path = graph[result];
|
||||
|
||||
float bombTimer = mp_c4timer.float_ ();
|
||||
float timeMidBlowup = bots.getTimeBombPlanted () + (bombTimer * 0.5f + bombTimer * 0.25f) - graph.calculateTravelTime (pev->maxspeed, pev->origin, path.origin);
|
||||
|
||||
if (timeMidBlowup > game.time ()) {
|
||||
clearTask (Task::MoveToPosition); // remove any move tasks
|
||||
|
||||
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, timeMidBlowup, true); // push camp task on to stack
|
||||
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, result, timeMidBlowup, true); // push move command
|
||||
|
||||
if (path.vis.crouch <= path.vis.stand) {
|
||||
m_campButtons |= IN_DUCK;
|
||||
}
|
||||
else {
|
||||
m_campButtons &= ~IN_DUCK;
|
||||
}
|
||||
if (rg.chance (90)) {
|
||||
pushChatterMessage (Chatter::DefendingBombsite);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pushRadioMessage (Radio::ShesGonnaBlow); // issue an additional radio message
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
|
||||
int goalChoices[4] = { kInvalidNodeIndex, kInvalidNodeIndex, kInvalidNodeIndex, kInvalidNodeIndex };
|
||||
|
||||
|
|
@ -349,6 +391,11 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// skip if it's not standing
|
||||
if (client.ent->v.flags & FL_DUCKING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// our team, alive and not myself?
|
||||
if (client.team != m_team || client.ent == ent ()) {
|
||||
continue;
|
||||
|
|
@ -882,7 +929,7 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
float desiredDistance = 8.0f;
|
||||
float nodeDistance = pev->origin.distance (m_pathOrigin);
|
||||
float nodeDistance = pev->origin.distance2d (m_pathOrigin);
|
||||
|
||||
// initialize the radius for a special node type, where the node is considered to be reached
|
||||
if (m_path->flags & NodeFlag::Lift) {
|
||||
|
|
@ -900,6 +947,9 @@ bool Bot::updateNavigation () {
|
|||
else if (m_path->number == cv_debug_goal.int_ ()) {
|
||||
desiredDistance = 0.0f;
|
||||
}
|
||||
else if (isOccupiedNode (m_path->number)) {
|
||||
desiredDistance = 72.0f;
|
||||
}
|
||||
else {
|
||||
desiredDistance = m_path->radius;
|
||||
}
|
||||
|
|
@ -1620,7 +1670,7 @@ void Bot::clearRoute () {
|
|||
m_routes.clear ();
|
||||
}
|
||||
|
||||
int Bot::findAimingNode (const Vector &to) {
|
||||
int Bot::findAimingNode (const Vector &to, int &pathLength) {
|
||||
// return the most distant node which is seen from the bot to the target and is within count
|
||||
|
||||
if (m_currentNodeIndex == kInvalidNodeIndex) {
|
||||
|
|
@ -1633,7 +1683,6 @@ int Bot::findAimingNode (const Vector &to) {
|
|||
if (destIndex == kInvalidNodeIndex) {
|
||||
return kInvalidNodeIndex;
|
||||
}
|
||||
const float kMaxDistance = ((m_states & Sense::HearingEnemy) || (m_states & Sense::SuspectEnemy) || m_seeEnemyTime + 3.0f > game.time ()) ? 0.0f : 512.0f;
|
||||
|
||||
while (destIndex != m_currentNodeIndex) {
|
||||
destIndex = (graph.m_matrix.data () + (destIndex * graph.length ()) + m_currentNodeIndex)->index;
|
||||
|
|
@ -1641,8 +1690,9 @@ int Bot::findAimingNode (const Vector &to) {
|
|||
if (destIndex < 0) {
|
||||
break;
|
||||
}
|
||||
++pathLength;
|
||||
|
||||
if (graph.isVisible (m_currentNodeIndex, destIndex) && graph.isVisible (destIndex, m_currentNodeIndex) && kMaxDistance > 0.0f && graph[destIndex].origin.distanceSq (graph[m_currentNodeIndex].origin) > cr::square (kMaxDistance)) {
|
||||
if (graph.isVisible (m_currentNodeIndex, destIndex)) {
|
||||
bestIndex = destIndex;
|
||||
break;
|
||||
}
|
||||
|
|
@ -3039,7 +3089,7 @@ void Bot::updateLookAngles () {
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_difficulty == Difficulty::Expert && (m_aimFlags & AimFlags::Enemy) && (m_wantsToFire || usesSniper ()) && cv_whose_your_daddy.bool_ ()) {
|
||||
if (m_difficulty == Difficulty::Expert && (m_aimFlags & AimFlags::Enemy) && (m_wantsToFire || usesSniper ()) && m_kpdRatio < 1.0f && m_healthValue < 50.0f) {
|
||||
pev->v_angle = direction;
|
||||
updateBodyAngles ();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue