diff --git a/bot/bot_skill.cfg b/bot/bot_skill.cfg index e1c096a1..a3169379 100644 --- a/bot/bot_skill.cfg +++ b/bot/bot_skill.cfg @@ -76,6 +76,13 @@ # bot can still hear a sound. # Unit of Measure = Integer (100 is max). # +# field_of_view +# Description = Sets bot field of view. +# Unit of Measure = Integer (degrees, 240 = max) +# +# max_always_detect_distance +# Description = Sets the maximum distance at which the bot will always spot the enemy +# Unit of Measure = Float (distance in cubes) # # SKILL LEVELS: # diff --git a/source/src/bot/ac_bot_ai.cpp b/source/src/bot/ac_bot_ai.cpp index 43d62fd5..7fa187a5 100644 --- a/source/src/bot/ac_bot_ai.cpp +++ b/source/src/bot/ac_bot_ai.cpp @@ -19,16 +19,16 @@ weaponinfo_s WeaponInfoTable[MAX_WEAPONS] = // FD: fire distance // mA: min desired Ammo // ---- Type : -- minDD -- maxDD -- minFD -- maxFD -- mA - { TYPE_MELEE, 0.0f, 4.0f, 0.0f, 3.5f, 0 }, // KNIFE - { TYPE_NORMAL, 0.0f, 15.0f, 0.0f, 50.0f, 6 }, // PISTOL - { TYPE_SNIPER, 20.0f, 80.0f, 2.0f, 180.0f, 2 }, // RIFLE // min and max FD just guesstimates for now - { TYPE_SHOTGUN, 0.0f, 15.0f, 0.0f, 40.0f, 3 }, // SHOTGUN - { TYPE_AUTO, 0.0f, 25.0f, 0.0f, 60.0f, 10 }, // SUBGUN - { TYPE_SNIPER, 15.0f, 125.0f, 3.0f, 200.0f, 3 }, // SNIPER - { TYPE_AUTO, 20.0f, 80.0f, 0.0f, 150.0f, 6 }, // ASSAULT - { TYPE_AUTO, 0.0f, 0.0f, 0.0f, 0.0f, 6 }, // CPISTOL - { TYPE_GRENADE, 30.0f, 25.0f, 0.0f, 50.0f, 1 }, // GRENADE - { TYPE_AUTO, 0.0f, 50.0f, 0.0f, 50.0f, 0 } // AKIMBO + { TYPE_MELEE, 0.0f, 5.0f, 0.0f, 3.5f, 0 }, // KNIFE + { TYPE_NORMAL, 3.5f, 15.0f, 3.0f, 50.0f, 6 }, // PISTOL + { TYPE_SNIPER, 20.0f, 80.0f, 3.0f, 180.0f, 2 }, // RIFLE // min and max FD just guesstimates for now + { TYPE_SHOTGUN, 0.0f, 15.0f, 3.0f, 40.0f, 3 }, // SHOTGUN + { TYPE_AUTO, 0.0f, 25.0f, 3.0f, 60.0f, 10 }, // SUBGUN + { TYPE_SNIPER, 15.0f, 125.0f, 3.0f, 200.0f, 3 }, // SNIPER + { TYPE_AUTO, 20.0f, 80.0f, 3.0f, 150.0f, 6 }, // ASSAULT + { TYPE_AUTO, 0.0f, 0.0f, 3.0f, 0.0f, 6 }, // CPISTOL + { TYPE_GRENADE, 5.0f, 30.0f, 3.0f, 50.0f, 1 }, // GRENADE + { TYPE_AUTO, 0.0f, 50.0f, 3.0f, 50.0f, 0 } // AKIMBO }; // Code of CACBot - Start @@ -38,87 +38,104 @@ bool CACBot::ChoosePreferredWeapon() short bestWeapon = m_pMyEnt->gunselect; short bestWeaponScore = 0; - short sWeaponScore; - float flDist = GetDistance(m_pMyEnt->enemy->o); + short sWeaponScore; + float flDist = GetDistance(m_pMyEnt->enemy->o); - // Choose a weapon - for(int i=0;i 1 ? 5 : 0; // Primary are usually better - - if (!m_pMyEnt->mag[i] && WeaponInfoTable[i].eWeaponType != TYPE_MELEE) - { - continue; - // If no ammo for this weapon, skip it - if(!m_pMyEnt->ammo[i]) continue; - } - else - { - sWeaponScore += 5; - } + // Choose a weapon + for(int i=0;i 1 ? 5 : 0; // Primary are usually better + + if (!m_pMyEnt->mag[i] && WeaponInfoTable[i].eWeaponType != TYPE_MELEE) + { + continue; + // If no ammo for this weapon, skip it + if(!m_pMyEnt->ammo[i]) continue; + } + else + { + sWeaponScore += 5; + } - if((flDist >= WeaponInfoTable[i].flMinDesiredDistance) && - (flDist <= WeaponInfoTable[i].flMaxDesiredDistance)) - { - // In desired range for this weapon - sWeaponScore += 5; // Increase score much - // Pistol & knife are better with sniper-like weapons as primary - if((i == GUN_PISTOL || WeaponInfoTable[i].eWeaponType == TYPE_MELEE) && WeaponInfoTable[m_pMyEnt->primary].eWeaponType == TYPE_SNIPER) - { - sWeaponScore += 10; // pistol is considered stronger than a primary weapon with sniper or carbine when range is valid - } - } - else - { - continue; // Not in range - } + if((flDist >= WeaponInfoTable[i].flMinDesiredDistance) && + (flDist <= WeaponInfoTable[i].flMaxDesiredDistance)) + { + // In desired range for this weapon + sWeaponScore += 5; // Increase score much - // The ideal distance would be between the Min and Max desired distance. - // Score on the difference of the avarage of the Min and Max desired distance. - float flAvarage = (WeaponInfoTable[i].flMinDesiredDistance + - WeaponInfoTable[i].flMaxDesiredDistance) / 2.0f; - float flIdealDiff = fabs(flDist - flAvarage); + if(i == GUN_PISTOL || WeaponInfoTable[i].eWeaponType == TYPE_MELEE) + { + if(WeaponInfoTable[m_pMyEnt->primary].eWeaponType == TYPE_SNIPER) + { + sWeaponScore += 10; // At a close range, knife & pistol are strong with a sniper like primary weapon + } + if(WeaponInfoTable[i].eWeaponType == TYPE_MELEE && WeaponInfoTable[m_pMyEnt->primary].eWeaponType == TYPE_SHOTGUN) + { + sWeaponScore -= 2; // Penalize a bit knife on close range with shotgun + } + } + } + else if ((flDist < WeaponInfoTable[i].flMinFireDistance) || + (flDist > WeaponInfoTable[i].flMaxFireDistance)) + { + continue; // Wrong distance for this weapon + } - if (flIdealDiff < 0.5f) // Close to ideal distance - sWeaponScore += 4; - else if (flIdealDiff <= 1.0f) - sWeaponScore += 2; + if(i == GUN_GRENADE) + { + sWeaponScore += 30; // Nades have high priority + } - // Now rate the weapon on available ammo... - if (WeaponInfoTable[i].sMinDesiredAmmo > 0) - { - // Calculate how much percent of the min desired ammo the bot has - float flDesiredPercent = (float(m_pMyEnt->ammo[i]) / - float(WeaponInfoTable[i].sMinDesiredAmmo)) * - 100.0f; + // The ideal distance would be between the Min and Max desired distance. + // Score on the difference of the avarage of the Min and Max desired distance. + float flAvarage = (WeaponInfoTable[i].flMinDesiredDistance + + WeaponInfoTable[i].flMaxDesiredDistance) / 2.0f; + float flIdealDiff = fabs(flDist - flAvarage); - if (flDesiredPercent >= 400.0f) - sWeaponScore += 4; - else if (flDesiredPercent >= 200.0f) - sWeaponScore += 3; - else if (flDesiredPercent >= 100.0f) - sWeaponScore += 1; - } - else - { - // Not needing ammo is an advantage... - sWeaponScore += 10; - } + if (flIdealDiff < 0.5f) // Close to ideal distance + sWeaponScore += 4; + else if (flIdealDiff <= 1.0f) + sWeaponScore += 2; - if(sWeaponScore > bestWeaponScore) - { - bestWeaponScore = sWeaponScore; + // Now rate the weapon on available ammo... + if (WeaponInfoTable[i].sMinDesiredAmmo > 0) + { + // Calculate how much percent of the min desired ammo the bot has + float flDesiredPercent = (float(m_pMyEnt->ammo[i]) / + float(WeaponInfoTable[i].sMinDesiredAmmo)) * + 100.0f; + + if (flDesiredPercent >= 400.0f) + sWeaponScore += 4; + else if (flDesiredPercent >= 200.0f) + sWeaponScore += 3; + else if (flDesiredPercent >= 100.0f) + sWeaponScore += 1; + } + else + { + // Not needing ammo is an advantage... + sWeaponScore += 10; + } + + if(sWeaponScore > bestWeaponScore) + { + bestWeaponScore = sWeaponScore; bestWeapon = i; - } - } + } + } - { - m_iChangeWeaponDelay = lastmillis + 8000; //RandomLong(2000, 8000); - m_bShootAtFeet = false; - SelectGun(bestWeapon); - return true; - } + if (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_ROCKET) + { + m_bShootAtFeet = true; + + } + if(lastmillis > m_iChangeWeaponDelay) + { + SelectGun(bestWeapon); + } + return true; }; void CACBot::Reload(int Gun) @@ -128,220 +145,221 @@ void CACBot::Reload(int Gun) entity *CACBot::SearchForEnts(bool bUseWPs, float flRange, float flMaxHeight) { - /* Entities are scored on the following things: - - Visibility - - For ammo: Need(ie has this bot much of this type or not) - - distance - */ + /* Entities are scored on the following things: + - Visibility + - For ammo: Need(ie has this bot much of this type or not) + - distance + */ - float flDist; - entity *pNewTargetEnt = NULL; - waypoint_s *pWptNearBot = NULL, *pBestWpt = NULL; - short sScore, sHighestScore = 0; + float flDist; + entity *pNewTargetEnt = NULL; + waypoint_s *pWptNearBot = NULL, *pBestWpt = NULL; + short sScore, sHighestScore = 0; - if ((WaypointClass.m_iWaypointCount >= 1) && bUseWPs) - pWptNearBot = GetNearestWaypoint(15.0f); + if ((WaypointClass.m_iWaypointCount >= 1) && bUseWPs) + pWptNearBot = GetNearestWaypoint(15.0f); #ifdef WP_FLOOD - if (!pWptNearBot && bUseWPs) - pWptNearBot = GetNearestFloodWP(5.0f); + if (!pWptNearBot && bUseWPs) + pWptNearBot = GetNearestFloodWP(5.0f); #endif - loopv(ents) - { - sScore = 0; - entity &e = ents[i]; - vec o(e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight); + loopv(ents) + { + sScore = 0; + entity &e = ents[i]; + vec o(e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight); - if (!ents[i].spawned) continue; - if (OUTBORD(e.x, e.y)) continue; + if (!ents[i].spawned) continue; + if (OUTBORD(e.x, e.y)) continue; - bool bInteresting = false; - short sAmmo = 0, sMaxAmmo = 0; + bool bInteresting = false; + short sAmmo = 0, sMaxAmmo = 0; - switch(e.type) - { - case I_CLIPS: - sMaxAmmo = ammostats[GUN_PISTOL].max; - bInteresting = (m_pMyEnt->ammo[GUN_PISTOL]ammo[GUN_PISTOL]; - break; - case I_AMMO: - sMaxAmmo = ammostats[m_pMyEnt->primary].max; - bInteresting = (m_pMyEnt->ammo[m_pMyEnt->primary]ammo[m_pMyEnt->primary]; - break; - case I_GRENADE: - sMaxAmmo = ammostats[GUN_GRENADE].max; - bInteresting = (m_pMyEnt->mag[GUN_GRENADE]mag[GUN_GRENADE]; - break; - case I_HEALTH: - sMaxAmmo = powerupstats[0].max; - bInteresting = (m_pMyEnt->health < sMaxAmmo); - sAmmo = m_pMyEnt->health; - break; - case I_HELMET: - case I_ARMOUR: + switch(e.type) + { + case I_CLIPS: + sMaxAmmo = ammostats[GUN_PISTOL].max; + bInteresting = (m_pMyEnt->ammo[GUN_PISTOL]ammo[GUN_PISTOL]; + break; + case I_AMMO: + sMaxAmmo = ammostats[m_pMyEnt->primary].max; + bInteresting = (m_pMyEnt->ammo[m_pMyEnt->primary]ammo[m_pMyEnt->primary]; + break; + case I_GRENADE: + sMaxAmmo = ammostats[GUN_GRENADE].max; + bInteresting = (m_pMyEnt->mag[GUN_GRENADE]health < sMaxAmmo); + sAmmo = m_pMyEnt->health; + break; + case I_HELMET: + case I_ARMOUR: sMaxAmmo = powerupstats[I_ARMOUR-I_HEALTH].max; bInteresting = (m_pMyEnt->armour < sMaxAmmo); sAmmo = m_pMyEnt->armour; break; - }; + case I_AKIMBO: + bInteresting = !m_pMyEnt->akimbo; + sAmmo = -1; + break; + }; - if (!bInteresting) - continue; // Not an interesting item, skip + if (!bInteresting) + continue; // Not an interesting item, skip - // Score on ammo and need - if (sAmmo == -1) - { - // This entity doesn't have/need ammo - // Score on type instead - //switch(e.type) - { // UNDONE - } - } - else - { - // Calculate current percentage of max ammo - float percent = ((float)sAmmo / (float)sMaxAmmo) * 100.0f; - if (percent > 100.0f) percent = 100.0f; - sScore += ((100 - short(percent))/2); - } + // Score on ammo and need + // Akimbo & nade + if (sAmmo == -1) + { + sScore += 75; // Bonus + } + else + { + // Calculate current percentage of max ammo + float percent = ((float)sAmmo / (float)sMaxAmmo) * 100.0f; + if (percent > 100.0f) percent = 100.0f; + sScore += ((100 - short(percent))/2); + } - flDist = GetDistance(o); + flDist = GetDistance(o); - if (flDist > flRange) continue; + if (flDist > flRange) continue; - // Score on distance - float f = flDist; - if (f > 100.0f) f = 100.0f; - sScore += ((100 - short(f)) / 2); + // Score on distance + float f = flDist; + if (f > 100.0f) f = 100.0f; + sScore += ((100 - short(f)) / 2); - waypoint_s *pWptNearEnt = NULL; - // If this entity isn't visible check if there is a nearby waypoint - if (!IsReachable(o, flMaxHeight))//(!IsVisible(o)) - { - if (!pWptNearBot) continue; + waypoint_s *pWptNearEnt = NULL; + // If this entity isn't visible check if there is a nearby waypoint + if (!IsReachable(o, flMaxHeight))//(!IsVisible(o)) + { + if (!pWptNearBot) continue; #ifdef WP_FLOOD - if (pWptNearBot->pNode->iFlags & W_FL_FLOOD) - pWptNearEnt = GetNearestFloodWP(o, 8.0f); - else + if (pWptNearBot->pNode->iFlags & W_FL_FLOOD) + pWptNearEnt = GetNearestFloodWP(o, 8.0f); + else #endif - pWptNearEnt = GetNearestWaypoint(o, 15.0f); + pWptNearEnt = GetNearestWaypoint(o, 15.0f); - if (!pWptNearEnt) continue; - } + if (!pWptNearEnt) continue; + } - // Score on visibility - if (pWptNearEnt == NULL) // Ent is visible - sScore += 30; - else - sScore += 15; + // Score on visibility + if (pWptNearEnt == NULL) // Ent is visible + sScore += 30; + else + sScore += 15; - if (sScore > sHighestScore) - { - // Found a valid wp near the bot and the ent,so...lets store it :) - if (pWptNearEnt) - pBestWpt = pWptNearEnt; - else - pBestWpt = NULL; // Best ent so far doesn't need any waypoints + if (sScore > sHighestScore) + { + // Found a valid wp near the bot and the ent,so...lets store it :) + if (pWptNearEnt) + pBestWpt = pWptNearEnt; + else + pBestWpt = NULL; // Best ent so far doesn't need any waypoints - sHighestScore = sScore; - pNewTargetEnt = &ents[i]; - } - } + sHighestScore = sScore; + pNewTargetEnt = &ents[i]; + } + } - if (pNewTargetEnt) - { - // Need waypoints to reach it? - if (pBestWpt) - { - ResetWaypointVars(); - SetCurrentWaypoint(pWptNearBot); - SetCurrentGoalWaypoint(pBestWpt); - } + if (pNewTargetEnt) + { + // Need waypoints to reach it? + if (pBestWpt) + { + ResetWaypointVars(); + SetCurrentWaypoint(pWptNearBot); + SetCurrentGoalWaypoint(pBestWpt); + } - m_vGoal.x = pNewTargetEnt->x; - m_vGoal.y = pNewTargetEnt->y; - m_vGoal.z = S(pNewTargetEnt->x, pNewTargetEnt->y)->floor+player1->eyeheight; - } + m_vGoal.x = pNewTargetEnt->x; + m_vGoal.y = pNewTargetEnt->y; + m_vGoal.z = S(pNewTargetEnt->x, pNewTargetEnt->y)->floor+player1->eyeheight; + } - return pNewTargetEnt; + return pNewTargetEnt; } bool CACBot::HeadToTargetEnt() { - if (m_pTargetEnt) - { - vec o(m_pTargetEnt->x, m_pTargetEnt->y, - S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight); + if (m_pTargetEnt) + { + vec o(m_pTargetEnt->x, m_pTargetEnt->y, + S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight); - if (m_pTargetEnt->spawned && (!UnderWater(m_pMyEnt->o) || - !UnderWater(o))) - { - bool bIsVisible = false; - if (m_pCurrentGoalWaypoint) - { - if ((GetDistance(o) <= 20.0f) && IsReachable(o, 1.0f)) - bIsVisible = true; - else if (HeadToGoal()) - { - //debugbeam(m_pMyEnt->o, m_pCurrentWaypoint->pNode->v_origin); - //debugbeam(m_pMyEnt->o, - // m_pCurrentGoalWaypoint->pNode->v_origin); - AddDebugText("Using WPs for ents"); - return true; - } - } - else - bIsVisible = IsVisible(o); + if (m_pTargetEnt->spawned && (!UnderWater(m_pMyEnt->o) || + !UnderWater(o))) + { + bool bIsVisible = false; + if (m_pCurrentGoalWaypoint) + { + if ((GetDistance(o) <= 20.0f) && IsReachable(o, 1.0f)) + bIsVisible = true; + else if (HeadToGoal()) + { + //debugbeam(m_pMyEnt->o, m_pCurrentWaypoint->pNode->v_origin); + //debugbeam(m_pMyEnt->o, + // m_pCurrentGoalWaypoint->pNode->v_origin); + AddDebugText("Using WPs for ents"); + return true; + } + } + else + bIsVisible = IsVisible(o); - if (bIsVisible) - { - if (m_pCurrentWaypoint || m_pCurrentGoalWaypoint) - { - condebug("ent is now visible"); - ResetWaypointVars(); - } + if (bIsVisible) + { + if (m_pCurrentWaypoint || m_pCurrentGoalWaypoint) + { + condebug("ent is now visible"); + ResetWaypointVars(); + } - float flHeightDiff = o.z - m_pMyEnt->o.z; - bool bToHigh = false; - if (Get2DDistance(o) <= 2.0f) + float flHeightDiff = o.z - m_pMyEnt->o.z; + bool bToHigh = false; + if (Get2DDistance(o) <= 2.0f) + { + if (flHeightDiff >= 1.5f) { - if (flHeightDiff >= 1.5f) - { - if (flHeightDiff <= JUMP_HEIGHT) - { + if (flHeightDiff <= JUMP_HEIGHT) + { #ifndef RELEASE_BUILD - char sz[64]; - sprintf(sz, "Ent z diff: %f", o.z-m_pMyEnt->o.z); - condebug(sz); + char sz[64]; + sprintf(sz, "Ent z diff: %f", o.z-m_pMyEnt->o.z); + condebug(sz); #endif - // Jump if close to ent and the ent is high - m_pMyEnt->jumpnext = true; - } - else - bToHigh = true; - } + // Jump if close to ent and the ent is high + m_pMyEnt->jumpnext = true; + } + else + bToHigh = true; } + } - if (!bToHigh) - { - AimToVec(o); - return true; - } - } - } - } + if (!bToHigh) + { + AimToVec(o); + return true; + } + } + } + } - return false; + return false; } bool CACBot::DoSPStuff() { - return false; + return false; } // Code of CACBot - End diff --git a/source/src/bot/bot.cpp b/source/src/bot/bot.cpp index 69f00caf..14d3df00 100644 --- a/source/src/bot/bot.cpp +++ b/source/src/bot/bot.cpp @@ -284,12 +284,14 @@ float CBot::GetDistance(entity *e) bool CBot::SelectGun(int Gun) { + if(!m_pMyEnt->weapons[Gun]->selectable()) return false; if(m_pMyEnt->weaponsel->reloading || m_pMyEnt->weaponchanging) return false; - if (m_pMyEnt->gunselect != Gun) audiomgr.playsound(S_GUNCHANGE, m_pMyEnt); m_pMyEnt->gunselect = Gun; if(m_pMyEnt->weaponsel->type != Gun) { + audiomgr.playsound(S_GUNCHANGE, m_pMyEnt); m_pMyEnt->weaponswitch(m_pMyEnt->weapons[Gun]); + m_iChangeWeaponDelay = lastmillis + 1000; } return true; } diff --git a/source/src/bot/bot.h b/source/src/bot/bot.h index 3d83b516..4a72cbb2 100644 --- a/source/src/bot/bot.h +++ b/source/src/bot/bot.h @@ -106,6 +106,7 @@ struct bot_skill_s // enemy again float flMaxEnemySearchDelay; // Maximal delay for when a bot can search for an // enemy again + float flAlwaysDetectDistance; short sShootAtFeetWithRLPercent; // Percent that a bot shoot with a rocket // launcher at the enemy feet. int iMaxHearVolume; // Max volume that bot can hear @@ -241,6 +242,7 @@ public: void ResetCurrentTask(); // AI Functions + bool DetectEnemy(playerent *p); bool FindEnemy(void); void CheckReload(void); void CheckWeaponSwitch(void); // 2011jan17:ft: fix non-shooting bots diff --git a/source/src/bot/bot_ai.cpp b/source/src/bot/bot_ai.cpp index 2071837d..1bbd76ca 100644 --- a/source/src/bot/bot_ai.cpp +++ b/source/src/bot/bot_ai.cpp @@ -105,6 +105,12 @@ vec CBot::GetEnemyPos(playerent *d) return o; } +bool CBot::DetectEnemy(playerent *p) +{ + return (IsInFOV(p) || (m_pBotSkill->flAlwaysDetectDistance > m_pMyEnt->o.dist(p->o))) + && IsVisible(p); +} + bool CBot::FindEnemy(void) { // UNDONE: Enemies are now only scored on their distance @@ -148,7 +154,7 @@ bool CBot::FindEnemy(void) continue; // Check if the enemy is visible - if(!IsInFOV(d) || !IsVisible(d)) + if(!DetectEnemy(d)) continue; flDist = GetDistance(d->o); @@ -172,7 +178,7 @@ bool CBot::FindEnemy(void) (player1->state == CS_ALIVE)) { // Check if the enemy is visible - if(IsInFOV(player1) && IsVisible(player1)) + if(DetectEnemy(player1)) { flDist = GetDistance(player1->o); EnemyVal = 1; @@ -628,6 +634,7 @@ void CBot::CheckWeaponSwitch() m_pMyEnt->weaponsel = m_pMyEnt->nextweaponsel; if(m_pMyEnt->weaponsel!=NULL) addmsg(SV_WEAPCHANGE, "ri", m_pMyEnt->weaponsel->type); // 2011jan17:ft: message possibly not needed in a local game!?! m_pMyEnt->weaponchanging = 0; + m_iChangeWeaponDelay = 0; if(!m_pMyEnt->weaponsel->mag) { tryreload(m_pMyEnt); @@ -684,7 +691,13 @@ void CBot::ShootEnemy() TraceLine(m_pMyEnt->o, dest, m_pMyEnt, false, &tr); debugbeam(m_pMyEnt->o, tr.end); - // Shoot + if(m_pMyEnt->gunselect == GUN_GRENADE) + { + player1->o.x = m_pMyEnt->o.x + 1.0f; + player1->o.y = m_pMyEnt->o.y + 1.0f; + player1->o.z = m_pMyEnt->o.z + 1.0f; + } + shoot(m_pMyEnt, tr.end); // Add shoot delay @@ -703,95 +716,13 @@ void CBot::ShootEnemy() bool CBot::ChoosePreferredWeapon() { - TMultiChoice WeaponChoices; - short sWeaponScore; - float flDist = GetDistance(m_pMyEnt->enemy->o); - - if ((m_iChangeWeaponDelay > lastmillis) && (m_pMyEnt->ammo[m_pMyEnt->gunselect])) - { - if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType != TYPE_MELEE) || (flDist <= 3.5f)) - return true; - } - - // Choose a weapon - for(int i=0;imag[i]) - { - // If no ammo for this weapon, skip it - if(!m_pMyEnt->ammo[i]) continue; - - // If only the mag is empty, just penalize it - sWeaponScore -= 5; - } - - // advantage for primary weapons - sWeaponScore += i > 1 ? 5 : 0; - - if ((flDist >= WeaponInfoTable[i].flMinDesiredDistance) && - (flDist <= WeaponInfoTable[i].flMaxDesiredDistance)) - { - // In desired range for this weapon - sWeaponScore += 5; // Increase score much - if(WeaponInfoTable[i].eWeaponType == TYPE_MELEE) sWeaponScore += 20; // knife is a powerful weapon - // pistol & knife are better with sniper-like weapons as primary - if((i == GUN_PISTOL || WeaponInfoTable[i].eWeaponType == TYPE_MELEE) && WeaponInfoTable[m_pMyEnt->primary].eWeaponType == TYPE_SNIPER) - { - sWeaponScore += 5; - } - } - else if ((flDist < WeaponInfoTable[i].flMinFireDistance) || - (flDist > WeaponInfoTable[i].flMaxFireDistance)) - continue; // Wrong distance for this weapon - - // The ideal distance would be between the Min and Max desired distance. - // Score on the difference of the avarage of the Min and Max desired distance. - float flAvarage = (WeaponInfoTable[i].flMinDesiredDistance + - WeaponInfoTable[i].flMaxDesiredDistance) / 2.0f; - float flIdealDiff = fabs(flDist - flAvarage); - - if (flIdealDiff < 0.5f) // Close to ideal distance - sWeaponScore += 4; - else if (flIdealDiff <= 1.0f) - sWeaponScore += 2; - - // Now rate the weapon on available ammo... - if (WeaponInfoTable[i].sMinDesiredAmmo > 0) - { - // Calculate how much percent of the min desired ammo the bot has - float flDesiredPercent = (float(m_pMyEnt->ammo[i]) / - float(WeaponInfoTable[i].sMinDesiredAmmo)) * - 100.0f; - - if (flDesiredPercent >= 400.0f) - sWeaponScore += 4; - else if (flDesiredPercent >= 200.0f) - sWeaponScore += 3; - else if (flDesiredPercent >= 100.0f) - sWeaponScore += 1; - } - - WeaponChoices.Insert(i, sWeaponScore); - } - - int WeaponSelect; - if (WeaponChoices.GetSelection(WeaponSelect)) - { - m_iChangeWeaponDelay = lastmillis + RandomLong(2000, 8000); - m_bShootAtFeet = ((WeaponInfoTable[WeaponSelect].eWeaponType==TYPE_ROCKET) && - (RandomLong(1, 100) <= - m_pBotSkill->sShootAtFeetWithRLPercent)); - return SelectGun(WeaponSelect); - } - - return false; + return true; } int CBot::GetShootDelay() { // UNDONE + return m_pMyEnt->gunwait[m_pMyEnt->gunselect]; if ((WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_MELEE) || (WeaponInfoTable[m_pMyEnt->gunselect].eWeaponType == TYPE_AUTO)) return m_pMyEnt->gunwait[m_pMyEnt->gunselect]; @@ -804,17 +735,9 @@ int CBot::GetShootDelay() void CBot::CheckReload() // reload gun if no enemies are around { //if(m_pMyEnt->mag[m_pMyEnt->weaponsel->type] >= WeaponInfoTable[m_pMyEnt->weaponsel->type].sMinDesiredAmmo) return; // do not reload if mindesiredammo is satisfied - if(m_pMyEnt->enemy) + if(m_pMyEnt->enemy && m_pMyEnt->mag[m_pMyEnt->weaponsel->type]) { - if(m_pMyEnt->mag[m_pMyEnt->weaponsel->type]) - { return; // ignore the enemy, if no ammo in mag. - } - else if(!m_pMyEnt->ammo[m_pMyEnt->weaponsel->type]) - { - ChoosePreferredWeapon(); - return; - } } tryreload(m_pMyEnt); return; diff --git a/source/src/bot/botmanager.cpp b/source/src/bot/botmanager.cpp index e9a4a83d..8f92a833 100644 --- a/source/src/bot/botmanager.cpp +++ b/source/src/bot/botmanager.cpp @@ -615,6 +615,10 @@ void CBotManager::CreateSkillData() { m_BotSkills[SkillNr].flMaxEnemySearchDelay = value; } + else if (strcasecmp(cmd, "max_always_detect_distance") == 0) + { + m_BotSkills[SkillNr].flAlwaysDetectDistance = value; + } else if (strcasecmp(cmd, "shoot_at_feet_percent") == 0) { if (value < 0) value = 0; @@ -667,6 +671,7 @@ void CBotManager::InitSkillData() m_BotSkills[0].flMaxAttackDelay = 0.4f; m_BotSkills[0].flMinEnemySearchDelay = 0.09f; m_BotSkills[0].flMaxEnemySearchDelay = 0.12f; + m_BotSkills[0].flAlwaysDetectDistance = 12.0f; m_BotSkills[0].sShootAtFeetWithRLPercent = 85; m_BotSkills[0].bCanPredict = true; m_BotSkills[0].iMaxHearVolume = 75; @@ -689,6 +694,7 @@ void CBotManager::InitSkillData() m_BotSkills[1].flMaxAttackDelay = 0.6f; m_BotSkills[1].flMinEnemySearchDelay = 0.12f; m_BotSkills[1].flMaxEnemySearchDelay = 0.17f; + m_BotSkills[1].flAlwaysDetectDistance = 10.0f; m_BotSkills[1].sShootAtFeetWithRLPercent = 60; m_BotSkills[1].bCanPredict = true; m_BotSkills[1].iMaxHearVolume = 60; @@ -711,6 +717,7 @@ void CBotManager::InitSkillData() m_BotSkills[2].flMaxAttackDelay = 1.0f; m_BotSkills[2].flMinEnemySearchDelay = 0.18f; m_BotSkills[2].flMaxEnemySearchDelay = 0.22f; + m_BotSkills[2].flAlwaysDetectDistance = 8.0f; m_BotSkills[2].sShootAtFeetWithRLPercent = 25; m_BotSkills[2].bCanPredict = false; m_BotSkills[2].iMaxHearVolume = 45; @@ -733,6 +740,7 @@ void CBotManager::InitSkillData() m_BotSkills[3].flMaxAttackDelay = 1.6f; m_BotSkills[3].flMinEnemySearchDelay = 0.25f; m_BotSkills[3].flMaxEnemySearchDelay = 0.30f; + m_BotSkills[3].flAlwaysDetectDistance = 6.0f; m_BotSkills[3].sShootAtFeetWithRLPercent = 10; m_BotSkills[3].bCanPredict = false; m_BotSkills[3].iMaxHearVolume = 30; @@ -755,6 +763,7 @@ void CBotManager::InitSkillData() m_BotSkills[4].flMaxAttackDelay = 2.0f; m_BotSkills[4].flMinEnemySearchDelay = 0.30f; m_BotSkills[4].flMaxEnemySearchDelay = 0.36f; + m_BotSkills[4].flAlwaysDetectDistance = 4.0f; m_BotSkills[4].sShootAtFeetWithRLPercent = 0; m_BotSkills[4].bCanPredict = false; m_BotSkills[4].iMaxHearVolume = 15; diff --git a/source/src/server.cpp b/source/src/server.cpp index f3bb29c9..c7c64e14 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -1350,7 +1350,10 @@ int numplayers() extern vector bots; loopv(bots) if(bots[i]) count++; } - loopv(players) if(players[i]) count++; + if(m_demo) + { + count = numclients(); + } #endif return count; } diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 3aaceb40..8842394f 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -435,7 +435,7 @@ void radialeffect(playerent *o, vec &v, int qdam, playerent *at, int gun) float dist = expdist(o, dir, v); if(disto = from; this->resetinterp(); inwater = hdr.waterlevel>o.z; - if(local) { addmsg(SV_THROWNADE, "ri7", int(o.x*DMF), int(o.y*DMF), int(o.z*DMF), int(vel.x*DMF), int(vel.y*DMF), int(vel.z*DMF), lastmillis-millis);