Core/LFG: Fixing and tunning LFG Followship bots
This commit is contained in:
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user