Core/LFG: Fixing and tunning LFG Followship bots

This commit is contained in:
SistemasCPU
2026-09-14 01:32:51 +02:00
parent 8c7078a1d5
commit eb6ae62cf7
3 changed files with 60 additions and 34 deletions
+21 -10
View File
@@ -1973,17 +1973,28 @@ void Group::AddFollowerModeBots(Player* player)
player->CastSpell(player, SPELL_DUNGEON_ASSISTENCE);
std::vector<uint32> botsToSummon;
switch (player->GetRoleForGroup())
uint8 lfgRoles = GetLfgRoles(player->GetGUID());
if (lfgRoles & lfg::PLAYER_ROLE_TANK)
botsToSummon = { BOT_HEALER, BOT_DPS_1, BOT_DPS_2, BOT_DPS_3 };
else if (lfgRoles & lfg::PLAYER_ROLE_HEALER)
botsToSummon = { BOT_TANK, BOT_DPS_1, BOT_DPS_2, BOT_DPS_3 };
else
botsToSummon = { BOT_TANK, BOT_HEALER, BOT_DPS_2, BOT_DPS_3 };
if (botsToSummon.empty())
{
case ROLE_TANK:
botsToSummon = { BOT_HEALER, BOT_DPS_1, BOT_DPS_2, BOT_DPS_3 };
break;
case ROLE_HEALER:
botsToSummon = { BOT_TANK, BOT_DPS_1, BOT_DPS_2, BOT_DPS_3 };
break;
default:
botsToSummon = { BOT_TANK, BOT_HEALER, BOT_DPS_2, BOT_DPS_3 };
break;
switch (player->GetRoleForGroup())
{
case ROLE_TANK:
botsToSummon = { BOT_HEALER, BOT_DPS_1, BOT_DPS_2, BOT_DPS_3 };
break;
case ROLE_HEALER:
botsToSummon = { BOT_TANK, BOT_DPS_1, BOT_DPS_2, BOT_DPS_3 };
break;
default:
botsToSummon = { BOT_TANK, BOT_HEALER, BOT_DPS_2, BOT_DPS_3 };
break;
}
}
static float const offsetX[] = { 1.0f, 2.0f, 1.5f, 2.5f };
+1 -1
View File
@@ -3107,7 +3107,7 @@ class spell_mage_mirror_image : public SpellScript
if (TempSummon* summon = caster->SummonCreature(NPC_MIRROR_IMAGE, pos, TEMPSUMMON_TIMED_DESPAWN, Milliseconds(GetSpellInfo()->GetDuration())))
{
summon->SetOwnerGUID(caster->GetGUID());
summon->SetFaction(caster->getFaction());
summon->SetFaction(caster->GetFaction());
caster->CastSpell(summon, SPELL_MAGE_CLONE_ME, TRIGGERED_FULL_MASK);
caster->CastSpell(summon, SPELL_MAGE_COPY_WEAPON, TRIGGERED_FULL_MASK);
+38 -23
View File
@@ -161,7 +161,7 @@ namespace FollowerFormation
struct npc_dungeon_follower_base : public ScriptedAI
{
npc_dungeon_follower_base(Creature* creature, float followDistance, float followAngle, bool isHealer = false)
: ScriptedAI(creature), _followDistance(followDistance), _followAngle(followAngle), _isHealer(isHealer) { }
: ScriptedAI(creature), _followDistance(followDistance), _followAngle(followAngle), _isHealer(isHealer), _nextEvasiveMove(0) { }
void IsSummonedBy(WorldObject* summoner) override
{
@@ -554,22 +554,28 @@ protected:
}
}
// Healer evasive movement: random side steps and jumps while in combat.
if (me->IsInCombat() && !me->HasUnitState(UNIT_STATE_CASTING))
// Healer evasive movement: real side jumps and dodges only while threatened.
if (me->IsInCombat() && me->getAttackerForHelper() && !me->HasUnitState(UNIT_STATE_CASTING)
&& GameTime::GetGameTimeMS() >= _nextEvasiveMove)
{
if (urand(0, 8) == 0)
uint32 now = GameTime::GetGameTimeMS();
// 25% chance to jump sideways, away from the current attacker.
if (urand(0, 3) == 0)
{
float const strafe = urand(0, 1) ? 0.5f : -0.5f;
me->HandleEmoteCommand(EMOTE_ONESHOT_JUMPSTART);
me->GetMotionMaster()->MoveFollow(owner, _followDistance, _followAngle + strafe, {}, MOTION_SLOT_ACTIVE);
float const side = urand(0, 1) ? float(M_PI) / 2.0f : -float(M_PI) / 2.0f;
Position jumpPos = me->GetFirstCollisionPosition(2.0f, me->GetOrientation() + side);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveJump(0, jumpPos, 5.0f, 1.0f);
_nextEvasiveMove = now + 1500;
return;
}
if (urand(0, 25) == 0)
// Occasional hop in place, as if trying not to touch the ground.
if (urand(0, 8) == 0)
{
float const side = urand(0, 1) ? 0.9f : -0.9f;
me->HandleEmoteCommand(EMOTE_ONESHOT_JUMPSTART);
me->GetMotionMaster()->MoveFollow(owner, _followDistance, _followAngle + side, {}, MOTION_SLOT_ACTIVE);
_nextEvasiveMove = now + 500;
return;
}
}
@@ -658,6 +664,7 @@ private:
float _followDistance;
float _followAngle;
bool _isHealer;
uint32 _nextEvasiveMove;
};
//209057 tank garrick
@@ -947,7 +954,8 @@ struct npc_Meredy_Huntswell_209059 : public npc_dungeon_follower_base
void ExecuteEvent(uint32 eventId) override
{
if (Unit* victim = me->GetVictim())
Unit* victim = me->GetVictim();
if (victim)
{
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim->IsNonMeleeSpellCast(true, false, true))
DoCastVictimMage(SPELL_MEREDY_COUNTERSPELL);
@@ -962,40 +970,40 @@ struct npc_Meredy_Huntswell_209059 : public npc_dungeon_follower_base
DoCastForced(me, SPELL_MEREDY_BLAZING_BARRIER);
break;
case EVENT_MEREDY_FIREBALL:
if (!me->HasUnitState(UNIT_STATE_CASTING))
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
DoCastVictimMage(SPELL_MEREDY_FIREBALL);
events.ScheduleEvent(EVENT_MEREDY_FIREBALL, 2500ms);
events.ScheduleEvent(EVENT_MEREDY_FIREBALL, 1500ms);
break;
case EVENT_MEREDY_PYROBLAST:
if (me->HasUnitState(UNIT_STATE_CASTING))
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 500ms);
else
else if (victim)
{
DoCastVictimMage(SPELL_MEREDY_PYROBLAST);
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 7s);
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 4s);
}
break;
case EVENT_MEREDY_FIRE_BLAST:
if (!me->HasUnitState(UNIT_STATE_CASTING))
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
DoCastVictimMage(SPELL_MEREDY_FIRE_BLAST);
events.ScheduleEvent(EVENT_MEREDY_FIRE_BLAST, 5s);
events.ScheduleEvent(EVENT_MEREDY_FIRE_BLAST, 2s);
break;
case EVENT_MEREDY_PHOENIX_FLAMES:
if (!me->HasUnitState(UNIT_STATE_CASTING))
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
{
DoCastVictimMage(SPELL_MEREDY_PHOENIX_FLAMES);
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_2, 300ms);
}
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_FLAMES, 30s);
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_FLAMES, 10s);
break;
case EVENT_MEREDY_PHOENIX_2:
if (!me->HasUnitState(UNIT_STATE_CASTING))
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
DoCastVictimMage(SPELL_MEREDY_PHOENIX_FLAMES);
break;
case EVENT_MEREDY_METEOR:
if (!me->HasUnitState(UNIT_STATE_CASTING))
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
DoCastVictimMage(SPELL_MEREDY_METEOR);
events.ScheduleEvent(EVENT_MEREDY_METEOR, 55s);
events.ScheduleEvent(EVENT_MEREDY_METEOR, 20s);
break;
case EVENT_MEREDY_BARRIER:
if (!me->HasAura(SPELL_MEREDY_BLAZING_BARRIER))
@@ -1120,14 +1128,21 @@ struct npc_Crenna_Earth_Daughter_209072 : public npc_dungeon_follower_base
Group* group = owner->GetGroup();
// Do not resurrect while any group member is still in combat.
// Do not resurrect while any living group member is still in combat.
if (group)
{
for (Group::MemberSlot const& slot : group->GetMemberSlots())
{
if (Unit* u = ObjectAccessor::GetUnit(*me, slot.guid))
{
if (u == me)
continue;
// Dead units may still be flagged in combat; ignore them.
if (!u->IsAlive())
continue;
if (u->IsInCombat())
return false;
}
}
}
else if (owner->IsInCombat())