diff --git a/common/ruletypes.h b/common/ruletypes.h index 8bc73df36..9ed9fa5a2 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -165,6 +165,7 @@ RULE_INT(Mercs, AggroRadius, 100) // Determines the distance from which a merc RULE_INT(Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) RULE_INT(Mercs, ResurrectRadius, 50) // Determines the distance from which a healer merc will attempt to resurrect a group member's corpse RULE_INT(Mercs, ScaleRate, 100) +RULE_BOOL(Mercs, MercsUsePathing, true) // Mercs will use node pathing when moving RULE_BOOL(Mercs, AllowMercSuspendInCombat, true) RULE_CATEGORY_END() diff --git a/utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql b/utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql new file mode 100644 index 000000000..7393facdd --- /dev/null +++ b/utils/sql/git/optional/2017_03_14_mercs_use_pathing_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:MercsUsePathing', 'false', 'Mercs will use node pathing when moving'); diff --git a/zone/bot.h b/zone/bot.h index ca5a7e9ad..6c1dfc53f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -737,7 +737,7 @@ private: bool _hasBeenSummoned; glm::vec3 m_PreSummonLocation; - Timer evade_timer; + Timer evade_timer; // can be moved to pTimers at some point BotCastingRoles m_CastingRoles; diff --git a/zone/merc.cpp b/zone/merc.cpp index 435d048b8..2b3368da6 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -69,6 +69,9 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) SetStance(MercStanceBalanced); rest_timer.Disable(); + if (GetClass() == ROGUE) + evade_timer.Start(); + int r; for (r = 0; r <= EQEmu::skills::HIGHEST_SKILL; r++) { skills[r] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)r, GetLevel()); @@ -1459,7 +1462,7 @@ void Merc::AI_Process() { // Let's check if we have a los with our target. // If we don't, our hate_list is wiped. // Else, it was causing the merc to aggro behind wall etc... causing massive trains. - if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { + if(GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { WipeHateList(); if(IsMoving()) { @@ -1474,6 +1477,26 @@ void Merc::AI_Process() { return; } + else if (!CheckLosFN(GetTarget())) { + if (RuleB(Mercs, MercsUsePathing) && zone->pathing) { + bool WaypointChanged, NodeReached; + + glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), + GetRunspeed(), WaypointChanged, NodeReached); + + if (WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); + } + else { + Mob* follow = entity_list.GetMob(GetFollowID()); + if (follow) + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), GetRunspeed()); + } + + return; + } if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) SendAddPlayerState(PlayerState::Aggressive); @@ -1508,32 +1531,60 @@ void Merc::AI_Process() { } } - if(AI_movement_timer->Check()) - { - if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) - { - // Move the rogue to behind the mob - float newX = 0; - float newY = 0; - float newZ = 0; + if(AI_movement_timer->Check()) { + if (!IsMoving()) { + if (GetClass() == ROGUE) { + if (HasTargetReflection() && !GetTarget()->IsFeared() && !GetTarget()->IsStunned()) { + // Hate redux actions + if (evade_timer.Check(false)) { + // Attempt to evade + int timer_duration = (HideReuseTime - GetSkillReuseTime(EQEmu::skills::SkillHide)) * 1000; + if (timer_duration < 0) + timer_duration = 0; + evade_timer.Start(timer_duration); - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) - { - CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); - return; + if (zone->random.Int(0, 260) < (int)GetSkill(EQEmu::skills::SkillHide)) + RogueEvade(GetTarget()); + + return; + } + else if (GetTarget()->IsRooted()) { + // Move rogue back from rooted mob - out of combat range, if necessary + float melee_distance = GetMaxMeleeRangeToTarget(GetTarget()); + float current_distance = DistanceSquared(static_cast(m_Position), static_cast(GetTarget()->GetPosition())); + + if (current_distance <= melee_distance) { + float newX = 0; + float newY = 0; + float newZ = 0; + FaceTarget(GetTarget()); + if (PlotPositionAroundTarget(this, newX, newY, newZ)) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } + } + } + else if (!BehindMob(GetTarget(), GetX(), GetY())) { + // Move the rogue to behind the mob + float newX = 0; + float newY = 0; + float newZ = 0; + if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } } - } - else if(!IsMoving() && GetClass() != ROGUE && (DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) < GetTarget()->GetSize())) - { - // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up - float newX = 0; - float newY = 0; - float newZ = 0; - - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) - { - CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); - return; + else if (GetClass() != ROGUE && (DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) < GetTarget()->GetSize())) { + // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up + float newX = 0; + float newY = 0; + float newZ = 0; + if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } } } @@ -1707,34 +1758,42 @@ void Merc::AI_Process() { } } - if(AI_movement_timer->Check()) - { - if(GetFollowID()) - { + if(AI_movement_timer->Check()) { + if(GetFollowID()) { Mob* follow = entity_list.GetMob(GetFollowID()); - if(follow) - { + if (follow) { float dist = DistanceSquared(m_Position, follow->GetPosition()); int speed = GetRunspeed(); - if(dist < GetFollowDistance() + 1000) + if (dist < GetFollowDistance() + 1000) speed = GetWalkspeed(); SetRunAnimSpeed(0); - if(dist > GetFollowDistance()) { - CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); - if(rest_timer.Enabled()) + if (dist > GetFollowDistance()) { + if (RuleB(Mercs, MercsUsePathing) && zone->pathing) { + bool WaypointChanged, NodeReached; + + glm::vec3 Goal = UpdatePath(follow->GetX(), follow->GetY(), follow->GetZ(), + speed, WaypointChanged, NodeReached); + + if (WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, speed); + } + else { + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); + } + + if (rest_timer.Enabled()) rest_timer.Disable(); - return; } - else - { - if(moved) - { - SetCurrentSpeed(0); - moved = false; + else { + if (moved) { + moved = false; + SetCurrentSpeed(0); } } } diff --git a/zone/merc.h b/zone/merc.h index 18256faf8..abd7e65d6 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -290,6 +290,8 @@ protected: std::vector merc_spells; std::map timers; + Timer evade_timer; // can be moved to pTimers at some point + uint16 skills[EQEmu::skills::HIGHEST_SKILL + 1]; uint32 equipment[EQEmu::legacy::EQUIPMENT_SIZE]; //this is an array of item IDs uint16 d_melee_texture1; //this is an item Material value