[Bots] Enraged positioning (#4789)

- Non-taunting melee bots will now properly go behind their target when it is enraged.
- Reduces how often taunting bots adjust their positioning by removing unnecessary rules.
- Cleans up CombatPositioning a bit
This commit is contained in:
nytmyr 2025-03-29 15:00:08 -05:00 committed by GitHub
parent 19e785b842
commit deb298dda7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 67 deletions

View File

@ -863,8 +863,6 @@ RULE_REAL(Bots, UpperMeleeDistanceMultiplier, 0.55, "Furthest % of the hit box a
RULE_REAL(Bots, UpperTauntingMeleeDistanceMultiplier, 0.45, "Furthest % of the hit box a taunting melee bot will get from the target. Default 0.45")
RULE_REAL(Bots, UpperMaxMeleeRangeDistanceMultiplier, 0.95, "Furthest % of the hit box a max melee range melee bot will get from the target. Default 0.95")
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")

View File

@ -2287,7 +2287,7 @@ void Bot::AI_Process()
// COMBAT RANGE CALCS
bool front_mob = InFrontMob(tar, GetX(), GetY());
bool behind_mob = BehindMob(tar, GetX(), GetY());
uint8 stop_melee_level = GetStopMeleeLevel();
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
tar_distance = sqrt(tar_distance); // sqrt this for future calculations
// Item variables
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
@ -2442,7 +2442,7 @@ void Bot::AI_Process()
ranged_timer.Start();
}
else if (!IsBotRanged() && GetLevel() < stop_melee_level) {
else if (!IsBotRanged() && !stop_melee_level) {
if (
IsTaunting() ||
!GetMaxMeleeRange() ||
@ -3128,7 +3128,6 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
bool is_stop_melee_level = GetLevel() >= input.stop_melee_level;
if (IsTaunting()) { // Taunting bots
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerTauntingMeleeDistanceMultiplier);
@ -3143,7 +3142,7 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
o.melee_distance = std::min(max_distance, desired_range);
}
else if (is_stop_melee_level) { // Casters
else if (input.stop_melee_level) { // Casters
float desired_range = GetBotDistanceRanged();
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range / 2));
@ -12020,74 +12019,53 @@ void Bot::DoCombatPositioning(
bool behind_mob,
bool front_mob
) {
if (HasTargetReflection()) {
if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range
if (tar_distance <= melee_distance_max) {
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 1.25f), GetBehindMob(), false)) {
RunToGoalWithJitter(Goal);
if (!tar->IsFeared()) {
bool is_too_close = tar_distance < melee_distance_min;
bool los_adjust = !HasRequiredLoSForPositioning(tar);
if (tar->IsRooted() && !IsTaunting()) { // Move non-taunting melee out of range
bool rooted_adjust = tar_distance <= melee_distance_max && HasTargetReflection();
if (rooted_adjust) {
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 1.25f), GetBehindMob(), !GetBehindMob())) {
RunToGoalWithJitter(Goal);
return;
}
}
}
else if (
tar_distance < melee_distance_min ||
(!front_mob && IsTaunting())
) { // Back up any bots that are too close or if they're taunting and not in front of the mob
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
RunToGoalWithJitter(Goal);
return;
}
}
}
else {
if (!tar->IsFeared()) {
if (
tar_distance < melee_distance_min ||
(GetBehindMob() && !behind_mob) ||
(IsTaunting() && !front_mob) ||
!HasRequiredLoSForPositioning(tar)
) { // Regular adjustment
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
RunToGoalWithJitter(Goal);
return;
}
}
else if (
tar->IsEnraged() &&
!IsTaunting() &&
!stop_melee_level &&
!behind_mob
) { // Move non-taunting melee bots behind target during enrage
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
RunToGoalWithJitter(Goal);
return;
}
}
else {
if (IsTaunting()) { // Taunting adjustments
Mob* mob_tar = tar->GetTarget();
bool taunting_adjust = (!front_mob || is_too_close || los_adjust);
if (mob_tar) {
if (
RuleB(Bots, TauntingBotsFollowTopHate) &&
(Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))
) { // If enabled, taunting bots will stick to top hate
Goal = mob_tar->GetPosition();
if (taunting_adjust) {
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, true)) {
RunToGoalWithJitter(Goal);
return;
}
else { // Otherwise, stick to any other bots that are taunting
if (
mob_tar->IsBot() &&
mob_tar->CastToBot()->IsTaunting() &&
(Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))
) {
Goal = mob_tar->GetPosition();
}
}
else {
if (tar->IsEnraged() && !stop_melee_level && !IsBotRanged()) { // Move non-taunting melee bots behind target during enrage
bool enraged_adjust = !behind_mob || is_too_close || los_adjust;
if (enraged_adjust) {
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
RunToGoalWithJitter(Goal);
return;
}
}
}
else { // Regular adjustments
bool regular_adjust =
is_too_close ||
los_adjust ||
(!GetBehindMob() && !front_mob) ||
(GetBehindMob() && !behind_mob);
if (regular_adjust) {
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), !GetBehindMob())) {
RunToGoalWithJitter(Goal);
return;
@ -12099,8 +12077,6 @@ void Bot::DoCombatPositioning(
}
DoFaceCheckNoJitter(tar);
return;
}
bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) {
@ -12197,7 +12173,11 @@ bool Bot::RequiresLoSForPositioning() {
}
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
if (IsBotSpellTypeDetrimental(i) && !GetSpellTypeHold(i)) {
if (IsHealBotSpellType(i) || i == BotSpellTypes::PBAENuke) {
continue;
}
if (!GetSpellTypeHold(i)) {
return true;
}
}

View File

@ -235,7 +235,7 @@ static std::map<uint16, std::string> botSubType_names = {
struct CombatRangeInput {
Mob* target;
float target_distance;
uint8 stop_melee_level;
bool stop_melee_level;
const EQ::ItemInstance* p_item;
const EQ::ItemInstance* s_item;
};