mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
[Bots] Positioning rewrite (#4856)
* [Bots] Positioning rewrite - Fixes positioning issues where bots could run back and forth or stay at the target's feet. - Adds checks to ensure bots don't spam positioning updates and complete their initial positioning. - Cleans up logic and makes it easier to read. * Cleanup
This commit is contained in:
parent
08bb9de437
commit
ff71cfbd5b
@ -858,12 +858,6 @@ RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery A
|
||||
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
||||
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
||||
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
||||
RULE_REAL(Bots, LowerMeleeDistanceMultiplier, 0.35, "Closest % of the hit box a melee bot will get to the target. Default 0.35")
|
||||
RULE_REAL(Bots, LowerTauntingMeleeDistanceMultiplier, 0.25, "Closest % of the hit box a taunting melee bot will get to the target. Default 0.25")
|
||||
RULE_REAL(Bots, LowerMaxMeleeRangeDistanceMultiplier, 0.80, "Closest % of the hit box a max melee range melee bot will get to the target. Default 0.80")
|
||||
RULE_REAL(Bots, UpperMeleeDistanceMultiplier, 0.55, "Furthest % of the hit box a melee bot will get from the target. Default 0.55")
|
||||
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_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.")
|
||||
|
||||
350
zone/bot.cpp
350
zone/bot.cpp
@ -245,6 +245,8 @@ Bot::Bot(
|
||||
|
||||
EquipBot();
|
||||
|
||||
m_combat_jitter_timer.Start();
|
||||
|
||||
if (GetClass() == Class::Rogue) {
|
||||
m_rogue_evade_timer.Start();
|
||||
}
|
||||
@ -2186,8 +2188,7 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
if (HOLDING || (raid && r_group == RAID_GROUPLESS)) {
|
||||
glm::vec3 Goal(0, 0, 0);
|
||||
TryNonCombatMovementChecks(bot_owner, follow_mob, Goal);
|
||||
TryNonCombatMovementChecks(bot_owner, follow_mob);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -2217,8 +2218,6 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
//ALT COMBAT (ACQUIRE HATE)
|
||||
glm::vec3 Goal(0, 0, 0);
|
||||
|
||||
// We have aggro to choose from
|
||||
if (IsEngaged()) {
|
||||
if (rest_timer.Enabled()) {
|
||||
@ -2293,15 +2292,15 @@ void Bot::AI_Process()
|
||||
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
|
||||
const EQ::ItemInstance* s_item = GetBotItem(EQ::invslot::slotSecondary);
|
||||
|
||||
CombatRangeInput input = {
|
||||
.target = tar,
|
||||
.target_distance = tar_distance,
|
||||
.stop_melee_level = stop_melee_level,
|
||||
.p_item = p_item,
|
||||
.s_item = s_item
|
||||
CombatRangeInput i = {
|
||||
.target = tar,
|
||||
.target_distance = tar_distance,
|
||||
.stop_melee_level = stop_melee_level,
|
||||
.p_item = p_item,
|
||||
.s_item = s_item
|
||||
};
|
||||
|
||||
CombatRangeOutput o = EvaluateCombatRange(input);
|
||||
CombatRangeOutput o = EvaluateCombatRange(i);
|
||||
|
||||
// Combat range variables
|
||||
bool at_combat_range = o.at_combat_range;
|
||||
@ -2362,7 +2361,7 @@ void Bot::AI_Process()
|
||||
}
|
||||
}
|
||||
|
||||
TryPursueTarget(leash_distance, Goal);
|
||||
TryPursueTarget(leash_distance);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -2385,52 +2384,30 @@ void Bot::AI_Process()
|
||||
(bot_owner->GetBotPulling() && NOT_RETURNING_BOT);
|
||||
|
||||
if (!other_bot_pulling && at_combat_range) {
|
||||
bool jitter_cooldown = false;
|
||||
|
||||
if (m_combat_jitter_timer.GetRemainingTime() > 1 && m_combat_jitter_timer.Enabled()) {
|
||||
jitter_cooldown = true;
|
||||
}
|
||||
|
||||
if (
|
||||
IsMoving() ||
|
||||
GetCombatJitterFlag() ||
|
||||
GetCombatOutOfRangeJitterFlag()
|
||||
) {
|
||||
if (
|
||||
!GetCombatJitterFlag() ||
|
||||
!IsMoving() ||
|
||||
GetCombatOutOfRangeJitterFlag()
|
||||
) {
|
||||
StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY()));
|
||||
}
|
||||
CombatPositioningInput cpi {
|
||||
.tar = tar,
|
||||
.stop_melee_level = stop_melee_level,
|
||||
.tar_distance = tar_distance,
|
||||
.melee_distance_min = melee_distance_min,
|
||||
.melee_distance = melee_distance,
|
||||
.melee_distance_max = melee_distance_max,
|
||||
.behind_mob = behind_mob,
|
||||
.front_mob = front_mob
|
||||
};
|
||||
|
||||
if (DoCombatPositioning(cpi) && IsMoving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!jitter_cooldown &&
|
||||
AI_movement_timer->Check() &&
|
||||
(!spellend_timer.Enabled() || GetClass() == Class::Bard)
|
||||
) {
|
||||
DoCombatPositioning(tar, Goal, stop_melee_level, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behind_mob, front_mob);
|
||||
if (!IsSitting() && !IsFacingMob(tar)) {
|
||||
FaceTarget(tar);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!IsSitting() && !IsFacingMob(tar)) {
|
||||
FaceTarget(tar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsBotNonSpellFighter() && AI_HasSpells() && AI_EngagedCastCheck()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMoving()) {
|
||||
StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) &&
|
||||
IsBotRanged() &&
|
||||
@ -2479,7 +2456,7 @@ void Bot::AI_Process()
|
||||
|
||||
// ENGAGED NOT AT COMBAT RANGE
|
||||
|
||||
else if (!other_bot_pulling && !TryPursueTarget(leash_distance, Goal)) {
|
||||
else if (!other_bot_pulling && !TryPursueTarget(leash_distance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2492,7 +2469,7 @@ void Bot::AI_Process()
|
||||
TryMeditate();
|
||||
}
|
||||
else { // Out-of-combat behavior
|
||||
DoOutOfCombatChecks(bot_owner, follow_mob, Goal, leash_distance, fm_distance);
|
||||
DoOutOfCombatChecks(bot_owner, follow_mob, leash_distance, fm_distance);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2509,8 +2486,10 @@ bool Bot::TryBardMovementCasts() {// Basically, bard bots get a chance to cast i
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal) {// Non-engaged movement checks
|
||||
bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob) {// Non-engaged movement checks
|
||||
if (AI_movement_timer->Check() && (!IsCasting() || GetClass() == Class::Bard)) {
|
||||
glm::vec3 Goal(0, 0, 0);
|
||||
|
||||
if (GUARDING) {
|
||||
Goal = GetGuardPoint();
|
||||
}
|
||||
@ -2564,7 +2543,7 @@ bool Bot::TryIdleChecks(float fm_distance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, glm::vec3& Goal, float leash_distance, float fm_distance) {
|
||||
void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_distance, float fm_distance) {
|
||||
SetAttackFlag(false);
|
||||
SetCombatRoundForAlerts(false);
|
||||
SetAttackingFlag(false);
|
||||
@ -2597,7 +2576,7 @@ void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, glm::vec3& Goa
|
||||
}
|
||||
|
||||
// Ok to idle
|
||||
if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) {
|
||||
if (TryNonCombatMovementChecks(bot_owner, follow_mob)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2808,15 +2787,14 @@ bool Bot::TryMeditate() {
|
||||
}
|
||||
|
||||
// This code actually gets processed when we are too far away from target and have not engaged yet
|
||||
bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) {
|
||||
bool Bot::TryPursueTarget(float leash_distance) {
|
||||
if (AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) {
|
||||
if (GetTarget() && !IsRooted()) {
|
||||
LogAIDetail("Pursuing [{}] while engaged", GetTarget()->GetCleanName());
|
||||
Goal = GetTarget()->GetPosition();
|
||||
glm::vec3 Goal = GetTarget()->GetPosition();
|
||||
|
||||
if (DistanceSquared(m_Position, Goal) <= leash_distance) {
|
||||
RunTo(Goal.x, Goal.y, Goal.z);
|
||||
SetCombatOutOfRangeJitter();
|
||||
} else {
|
||||
WipeHateList();
|
||||
SetTarget(nullptr);
|
||||
@ -3130,8 +3108,8 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
||||
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
||||
|
||||
if (IsTaunting()) { // Taunting bots
|
||||
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerTauntingMeleeDistanceMultiplier);
|
||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperTauntingMeleeDistanceMultiplier);
|
||||
o.melee_distance_min = o.melee_distance_max * 0.25f;
|
||||
o.melee_distance = o.melee_distance_max * 0.45f;
|
||||
}
|
||||
else if (IsBotRanged()) { // Archers/Throwers
|
||||
float min_distance = RuleI(Combat, MinRangedAttackDist);
|
||||
@ -3139,22 +3117,22 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
||||
float desired_range = GetBotDistanceRanged();
|
||||
|
||||
max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged even if items/ammo aren't correct
|
||||
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
||||
o.melee_distance_min = std::max(min_distance, (desired_range * 0.75f));
|
||||
o.melee_distance = std::min(max_distance, desired_range);
|
||||
}
|
||||
else if (input.stop_melee_level) { // Casters
|
||||
float desired_range = GetBotDistanceRanged();
|
||||
|
||||
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range / 2));
|
||||
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range * 0.75f));
|
||||
o.melee_distance = std::max((o.melee_distance_max * 1.25f), desired_range);
|
||||
}
|
||||
else if (GetMaxMeleeRange()) { // Melee bots set to max melee range
|
||||
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerMaxMeleeRangeDistanceMultiplier);
|
||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperMaxMeleeRangeDistanceMultiplier);
|
||||
o.melee_distance_min = o.melee_distance_max * 0.80f;
|
||||
o.melee_distance = o.melee_distance_max * 0.95f;
|
||||
}
|
||||
else { // Regular melee
|
||||
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerMeleeDistanceMultiplier);
|
||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperMeleeDistanceMultiplier);
|
||||
o.melee_distance_min = o.melee_distance_max * 0.30f;
|
||||
o.melee_distance = o.melee_distance_max * 0.65f;
|
||||
}
|
||||
|
||||
o.at_combat_range = (input.target_distance <= o.melee_distance);
|
||||
@ -11966,193 +11944,171 @@ void Bot::SetCastedSpellType(uint16 spell_type) {
|
||||
_castedSpellType = spell_type;
|
||||
}
|
||||
|
||||
void Bot::DoFaceCheckWithJitter(Mob* tar) {
|
||||
if (!tar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMoving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetCombatJitter();
|
||||
if (!IsFacingMob(tar)) {
|
||||
FaceTarget(tar);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Bot::DoFaceCheckNoJitter(Mob* tar) {
|
||||
if (!tar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMoving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsFacingMob(tar)) {
|
||||
FaceTarget(tar);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Bot::RunToGoalWithJitter(glm::vec3 Goal) {
|
||||
RunTo(Goal.x, Goal.y, Goal.z);
|
||||
SetCombatJitter();
|
||||
}
|
||||
|
||||
void Bot::SetCombatOutOfRangeJitter() {
|
||||
SetCombatOutOfRangeJitterFlag();
|
||||
|
||||
if (RuleI(Bots, MaxJitterTimer) > 0) {
|
||||
m_combat_jitter_timer.Start(zone->random.Int(RuleI(Bots, MinJitterTimer), RuleI(Bots, MaxJitterTimer)), true);
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::SetCombatJitter() {
|
||||
SetCombatJitterFlag();
|
||||
|
||||
if (RuleI(Bots, MaxJitterTimer) > 0) {
|
||||
m_combat_jitter_timer.Start(zone->random.Int(RuleI(Bots, MinJitterTimer), RuleI(Bots, MaxJitterTimer)), true);
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::DoCombatPositioning(
|
||||
Mob* tar,
|
||||
glm::vec3 Goal,
|
||||
bool stop_melee_level,
|
||||
float tar_distance,
|
||||
float melee_distance_min,
|
||||
float melee_distance,
|
||||
float melee_distance_max,
|
||||
bool behind_mob,
|
||||
bool front_mob
|
||||
) {
|
||||
if (!tar->IsFeared()) {
|
||||
bool is_too_close = tar_distance < melee_distance_min;
|
||||
bool los_adjust = !HasRequiredLoSForPositioning(tar);
|
||||
bool Bot::DoCombatPositioning(const CombatPositioningInput& input)
|
||||
{
|
||||
bool adjustment_needed = false;
|
||||
bool is_too_close = input.tar_distance < input.melee_distance_min;
|
||||
bool los_adjust = !HasRequiredLoSForPositioning(input.tar);
|
||||
bool behind_mob_set = !input.stop_melee_level &&
|
||||
!IsBotRanged() &&
|
||||
GetBehindMob(); // Don't want casters or ranged to find positions behind the target.
|
||||
bool adjustment_allowed = !IsMoving() &&
|
||||
m_combat_jitter_timer.Check() &&
|
||||
(!spellend_timer.Enabled() || GetClass() == Class::Bard);
|
||||
|
||||
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;
|
||||
}
|
||||
if (!IsMoving() && !IsSitting() && !IsFacingMob(input.tar)) {
|
||||
FaceTarget(input.tar);
|
||||
}
|
||||
|
||||
FindPositionInput find_position_input = {
|
||||
.tar = input.tar,
|
||||
.distance_min = input.melee_distance_min,
|
||||
.distance_max = input.melee_distance_max,
|
||||
.behind_only = behind_mob_set,
|
||||
.front_only = IsTaunting(),
|
||||
.bypass_los = false,
|
||||
};
|
||||
|
||||
bool is_melee = (!input.stop_melee_level && !IsBotRanged());
|
||||
|
||||
if (input.tar->IsRooted() && !IsTaunting()) { // Move non-taunting melee out of range
|
||||
adjustment_needed =
|
||||
(input.tar_distance <= input.melee_distance_max) &&
|
||||
HasTargetReflection();
|
||||
|
||||
if (adjustment_needed && adjustment_allowed) {
|
||||
find_position_input.distance_min = input.melee_distance_max + 1;
|
||||
find_position_input.distance_max = input.melee_distance_max * 1.25f;
|
||||
|
||||
PlotBotPositionAroundTarget(find_position_input);
|
||||
}
|
||||
} else {
|
||||
if (input.tar->IsFeared()) {
|
||||
adjustment_needed = los_adjust;
|
||||
|
||||
if (adjustment_needed && adjustment_allowed) {
|
||||
find_position_input.distance_min = input.melee_distance_min;
|
||||
find_position_input.distance_max = input.melee_distance;
|
||||
find_position_input.behind_only = false;
|
||||
find_position_input.front_only = false;
|
||||
|
||||
PlotBotPositionAroundTarget(find_position_input);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (IsTaunting()) { // Taunting adjustments
|
||||
bool taunting_adjust = (!front_mob || is_too_close || los_adjust);
|
||||
else if (IsTaunting() || HasTargetReflection()) { // Taunting/Aggro adjustments
|
||||
adjustment_needed =
|
||||
is_too_close ||
|
||||
los_adjust ||
|
||||
(is_melee && !input.front_mob);
|
||||
|
||||
if (taunting_adjust) {
|
||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, true)) {
|
||||
RunToGoalWithJitter(Goal);
|
||||
if (adjustment_needed && adjustment_allowed) {
|
||||
find_position_input.distance_min = input.melee_distance_min;
|
||||
find_position_input.distance_max = input.melee_distance;
|
||||
find_position_input.behind_only = false;
|
||||
find_position_input.front_only = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
PlotBotPositionAroundTarget(find_position_input);
|
||||
}
|
||||
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;
|
||||
} else {
|
||||
if (input.tar->IsEnraged() && is_melee) { // Move non-taunting melee bots behind target during enrage
|
||||
adjustment_needed =
|
||||
!behind_mob_set ||
|
||||
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);
|
||||
if (adjustment_needed && adjustment_allowed) {
|
||||
find_position_input.distance_min = input.melee_distance_min;
|
||||
find_position_input.distance_max = input.melee_distance;
|
||||
find_position_input.behind_only = true;
|
||||
find_position_input.front_only = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
PlotBotPositionAroundTarget(find_position_input);
|
||||
}
|
||||
else { // Regular adjustments
|
||||
bool regular_adjust =
|
||||
is_too_close ||
|
||||
los_adjust ||
|
||||
(!GetBehindMob() && !front_mob) ||
|
||||
(GetBehindMob() && !behind_mob);
|
||||
} else { // Regular adjustments
|
||||
adjustment_needed =
|
||||
is_too_close ||
|
||||
los_adjust ||
|
||||
(behind_mob_set && !input.behind_mob);
|
||||
|
||||
if (regular_adjust) {
|
||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), !GetBehindMob())) {
|
||||
RunToGoalWithJitter(Goal);
|
||||
if (adjustment_needed && adjustment_allowed) {
|
||||
find_position_input.distance_min = input.melee_distance_min;
|
||||
find_position_input.distance_max = input.melee_distance;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
PlotBotPositionAroundTarget(find_position_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoFaceCheckNoJitter(tar);
|
||||
if (!adjustment_needed && IsMoving()) {
|
||||
StopMoving();
|
||||
}
|
||||
|
||||
return adjustment_needed;
|
||||
}
|
||||
|
||||
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) {
|
||||
bool Bot::PlotBotPositionAroundTarget(const FindPositionInput& input) {
|
||||
bool Result = false;
|
||||
|
||||
if (target) {
|
||||
float look_heading = 0;
|
||||
|
||||
min_distance = min_distance;
|
||||
max_distance = max_distance;
|
||||
float temp_x = 0;
|
||||
float temp_y = 0;
|
||||
float temp_z = target->GetZ();
|
||||
float best_z = 0;
|
||||
auto offset = GetZOffset();
|
||||
const float tar_x = target->GetX();
|
||||
const float tar_y = target->GetY();
|
||||
float tar_distance = 0;
|
||||
|
||||
glm::vec3 temp_z_Position;
|
||||
glm::vec4 temp_m_Position;
|
||||
|
||||
const uint16 max_iterations_allowed = 50;
|
||||
uint16 counter = 0;
|
||||
if (input.tar) {
|
||||
glm::vec3 temp_goal(0, 0, input.tar->GetZ());
|
||||
glm::vec3 tar_position(input.tar->GetX(), input.tar->GetY(), input.tar->GetZ());
|
||||
float look_heading = 0;
|
||||
float best_z = 0;
|
||||
float tar_distance = 0;
|
||||
float desired_angle = 0;
|
||||
const float offset = GetZOffset();
|
||||
const uint16 max_iterations_allowed = 50;
|
||||
uint16 counter = 0;
|
||||
|
||||
while (counter < max_iterations_allowed) {
|
||||
temp_x = tar_x + zone->random.Real(-max_distance, max_distance);
|
||||
temp_y = tar_y + zone->random.Real(-max_distance, max_distance);
|
||||
|
||||
temp_z_Position.x = temp_x;
|
||||
temp_z_Position.y = temp_y;
|
||||
temp_z_Position.z = temp_z;
|
||||
best_z = GetFixedZ(temp_z_Position);
|
||||
temp_goal.x = tar_position.x + zone->random.Real(-input.distance_max, input.distance_max);
|
||||
temp_goal.y = tar_position.y + zone->random.Real(-input.distance_max, input.distance_max);
|
||||
best_z = GetFixedZ(temp_goal);
|
||||
|
||||
if (best_z != BEST_Z_INVALID) {
|
||||
temp_z = best_z;
|
||||
temp_goal.z = best_z;
|
||||
}
|
||||
else {
|
||||
counter++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
temp_m_Position.x = temp_x;
|
||||
temp_m_Position.y = temp_y;
|
||||
temp_m_Position.z = temp_z;
|
||||
tar_distance = Distance(input.tar->GetPosition(), temp_goal);
|
||||
|
||||
tar_distance = Distance(target->GetPosition(), temp_m_Position);
|
||||
|
||||
if (tar_distance > max_distance || tar_distance < min_distance) {
|
||||
if (tar_distance > input.distance_max || tar_distance < std::max(input.distance_min, (input.distance_max * 0.75f))) {
|
||||
counter++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (front_only && !InFrontMob(target, temp_x, temp_y)) {
|
||||
if (input.front_only && !InFrontMob(input.tar, temp_goal.x, temp_goal.y)) {
|
||||
counter++;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (behind_only && !BehindMob(target, temp_x, temp_y)) {
|
||||
else if (input.behind_only && !BehindMob(input.tar, temp_goal.x, temp_goal.y)) {
|
||||
counter++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, temp_x, temp_y, temp_z)) {
|
||||
if (!input.bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(input.tar, temp_goal.x, temp_goal.y, temp_goal.z)) {
|
||||
counter++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -12161,9 +12117,7 @@ bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest,
|
||||
}
|
||||
|
||||
if (Result) {
|
||||
x_dest = temp_x;
|
||||
y_dest = temp_y;
|
||||
z_dest = temp_z;
|
||||
RunToGoalWithJitter(temp_goal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
37
zone/bot.h
37
zone/bot.h
@ -39,9 +39,6 @@
|
||||
|
||||
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds
|
||||
constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 3000; // 3 seconds
|
||||
|
||||
constexpr uint32 MAG_EPIC_1_0 = 28034;
|
||||
|
||||
extern WorldServer worldserver;
|
||||
@ -232,21 +229,6 @@ static std::map<uint16, std::string> botSubType_names = {
|
||||
{ CommandedSubTypes::Selo, "Selo" }
|
||||
};
|
||||
|
||||
struct CombatRangeInput {
|
||||
Mob* target;
|
||||
float target_distance;
|
||||
bool stop_melee_level;
|
||||
const EQ::ItemInstance* p_item;
|
||||
const EQ::ItemInstance* s_item;
|
||||
};
|
||||
|
||||
struct CombatRangeOutput {
|
||||
bool at_combat_range = false;
|
||||
float melee_distance_min = 0.0f;
|
||||
float melee_distance = 0.0f;
|
||||
float melee_distance_max = 0.0f;
|
||||
};
|
||||
|
||||
class Bot : public NPC {
|
||||
friend class Mob;
|
||||
public:
|
||||
@ -577,7 +559,7 @@ public:
|
||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||
|
||||
// Movement checks
|
||||
bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false);
|
||||
bool PlotBotPositionAroundTarget(const FindPositionInput& input);
|
||||
std::vector<Mob*> GetSpellTargetList(bool entire_raid = false);
|
||||
void SetSpellTargetList(std::vector<Mob*> spell_target_list) { _spell_target_list = spell_target_list; }
|
||||
std::vector<Mob*> GetGroupSpellTargetList() { return _group_spell_target_list; }
|
||||
@ -1102,15 +1084,8 @@ public:
|
||||
bool CheckIfCasting(float fm_distance);
|
||||
void HealRotationChecks();
|
||||
|
||||
bool GetCombatJitterFlag() { return m_combat_jitter_flag; }
|
||||
void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; }
|
||||
bool GetCombatOutOfRangeJitterFlag() { return m_combat_out_of_range_jitter_flag; }
|
||||
void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; }
|
||||
void SetCombatJitter();
|
||||
void SetCombatOutOfRangeJitter();
|
||||
void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stop_melee_level, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behind_mob, bool front_mob);
|
||||
void DoFaceCheckWithJitter(Mob* tar);
|
||||
void DoFaceCheckNoJitter(Mob* tar);
|
||||
bool DoCombatPositioning(const CombatPositioningInput& input);
|
||||
void RunToGoalWithJitter(glm::vec3 Goal);
|
||||
bool RequiresLoSForPositioning();
|
||||
bool HasRequiredLoSForPositioning(Mob* tar);
|
||||
@ -1118,12 +1093,12 @@ public:
|
||||
// Try Combat Methods
|
||||
bool TryEvade(Mob* tar);
|
||||
bool TryFacingTarget(Mob* tar);
|
||||
bool TryPursueTarget(float leash_distance, glm::vec3& Goal);
|
||||
bool TryPursueTarget(float leash_distance);
|
||||
bool TryMeditate();
|
||||
bool TryAutoDefend(Client* bot_owner, float leash_distance);
|
||||
bool TryIdleChecks(float fm_distance);
|
||||
bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal);
|
||||
void DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, glm::vec3& Goal, float leash_distance, float fm_distance);
|
||||
bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob);
|
||||
void DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_distance, float fm_distance);
|
||||
bool TryBardMovementCasts();
|
||||
bool BotRangedAttack(Mob* other, bool can_double_attack = false);
|
||||
bool CheckDoubleRangedAttack();
|
||||
@ -1189,8 +1164,6 @@ private:
|
||||
Timer m_auto_save_timer;
|
||||
|
||||
Timer m_combat_jitter_timer;
|
||||
bool m_combat_jitter_flag;
|
||||
bool m_combat_out_of_range_jitter_flag;
|
||||
|
||||
bool m_dirtyautohaters;
|
||||
bool m_guard_flag;
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../common/timer.h"
|
||||
#include "mob.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@ -151,4 +152,39 @@ struct BotSpellTypesByClass {
|
||||
std::string description;
|
||||
};
|
||||
|
||||
struct CombatRangeInput {
|
||||
Mob* target;
|
||||
float target_distance;
|
||||
bool stop_melee_level;
|
||||
const EQ::ItemInstance* p_item;
|
||||
const EQ::ItemInstance* s_item;
|
||||
};
|
||||
|
||||
struct CombatRangeOutput {
|
||||
bool at_combat_range = false;
|
||||
float melee_distance_min = 0.0f;
|
||||
float melee_distance = 0.0f;
|
||||
float melee_distance_max = 0.0f;
|
||||
};
|
||||
|
||||
struct CombatPositioningInput {
|
||||
Mob* tar;
|
||||
bool stop_melee_level;
|
||||
float tar_distance;
|
||||
float melee_distance_min;
|
||||
float melee_distance;
|
||||
float melee_distance_max;
|
||||
bool behind_mob;
|
||||
bool front_mob;
|
||||
};
|
||||
|
||||
struct FindPositionInput {
|
||||
Mob* tar;
|
||||
float distance_min;
|
||||
float distance_max;
|
||||
bool behind_only;
|
||||
bool front_only;
|
||||
bool bypass_los;
|
||||
};
|
||||
|
||||
#endif // BOT_STRUCTS
|
||||
|
||||
@ -1698,13 +1698,6 @@ void Mob::StopMoving()
|
||||
|
||||
void Mob::StopMoving(float new_heading)
|
||||
{
|
||||
if (IsBot()) {
|
||||
auto bot = CastToBot();
|
||||
|
||||
bot->SetCombatJitterFlag(false);
|
||||
bot->SetCombatOutOfRangeJitterFlag(false);
|
||||
}
|
||||
|
||||
StopNavigation();
|
||||
RotateTo(new_heading);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user