Core/LFG: Fixing and tunning LFG Followship bots
This commit is contained in:
@@ -1973,6 +1973,16 @@ void Group::AddFollowerModeBots(Player* player)
|
|||||||
player->CastSpell(player, SPELL_DUNGEON_ASSISTENCE);
|
player->CastSpell(player, SPELL_DUNGEON_ASSISTENCE);
|
||||||
|
|
||||||
std::vector<uint32> botsToSummon;
|
std::vector<uint32> botsToSummon;
|
||||||
|
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())
|
||||||
|
{
|
||||||
switch (player->GetRoleForGroup())
|
switch (player->GetRoleForGroup())
|
||||||
{
|
{
|
||||||
case ROLE_TANK:
|
case ROLE_TANK:
|
||||||
@@ -1985,6 +1995,7 @@ void Group::AddFollowerModeBots(Player* player)
|
|||||||
botsToSummon = { BOT_TANK, BOT_HEALER, BOT_DPS_2, BOT_DPS_3 };
|
botsToSummon = { BOT_TANK, BOT_HEALER, BOT_DPS_2, BOT_DPS_3 };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static float const offsetX[] = { 1.0f, 2.0f, 1.5f, 2.5f };
|
static float const offsetX[] = { 1.0f, 2.0f, 1.5f, 2.5f };
|
||||||
static float const offsetY[] = { 1.5f, 2.5f, 1.0f, 1.0f };
|
static float const offsetY[] = { 1.5f, 2.5f, 1.0f, 1.0f };
|
||||||
|
|||||||
@@ -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())))
|
if (TempSummon* summon = caster->SummonCreature(NPC_MIRROR_IMAGE, pos, TEMPSUMMON_TIMED_DESPAWN, Milliseconds(GetSpellInfo()->GetDuration())))
|
||||||
{
|
{
|
||||||
summon->SetOwnerGUID(caster->GetGUID());
|
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_CLONE_ME, TRIGGERED_FULL_MASK);
|
||||||
caster->CastSpell(summon, SPELL_MAGE_COPY_WEAPON, 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
|
struct npc_dungeon_follower_base : public ScriptedAI
|
||||||
{
|
{
|
||||||
npc_dungeon_follower_base(Creature* creature, float followDistance, float followAngle, bool isHealer = false)
|
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
|
void IsSummonedBy(WorldObject* summoner) override
|
||||||
{
|
{
|
||||||
@@ -554,22 +554,28 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Healer evasive movement: random side steps and jumps while in combat.
|
// Healer evasive movement: real side jumps and dodges only while threatened.
|
||||||
if (me->IsInCombat() && !me->HasUnitState(UNIT_STATE_CASTING))
|
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;
|
float const side = urand(0, 1) ? float(M_PI) / 2.0f : -float(M_PI) / 2.0f;
|
||||||
me->HandleEmoteCommand(EMOTE_ONESHOT_JUMPSTART);
|
Position jumpPos = me->GetFirstCollisionPosition(2.0f, me->GetOrientation() + side);
|
||||||
me->GetMotionMaster()->MoveFollow(owner, _followDistance, _followAngle + strafe, {}, MOTION_SLOT_ACTIVE);
|
me->GetMotionMaster()->Clear();
|
||||||
|
me->GetMotionMaster()->MoveJump(0, jumpPos, 5.0f, 1.0f);
|
||||||
|
_nextEvasiveMove = now + 1500;
|
||||||
return;
|
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->HandleEmoteCommand(EMOTE_ONESHOT_JUMPSTART);
|
||||||
me->GetMotionMaster()->MoveFollow(owner, _followDistance, _followAngle + side, {}, MOTION_SLOT_ACTIVE);
|
_nextEvasiveMove = now + 500;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -658,6 +664,7 @@ private:
|
|||||||
float _followDistance;
|
float _followDistance;
|
||||||
float _followAngle;
|
float _followAngle;
|
||||||
bool _isHealer;
|
bool _isHealer;
|
||||||
|
uint32 _nextEvasiveMove;
|
||||||
};
|
};
|
||||||
|
|
||||||
//209057 tank garrick
|
//209057 tank garrick
|
||||||
@@ -947,7 +954,8 @@ struct npc_Meredy_Huntswell_209059 : public npc_dungeon_follower_base
|
|||||||
|
|
||||||
void ExecuteEvent(uint32 eventId) override
|
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))
|
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim->IsNonMeleeSpellCast(true, false, true))
|
||||||
DoCastVictimMage(SPELL_MEREDY_COUNTERSPELL);
|
DoCastVictimMage(SPELL_MEREDY_COUNTERSPELL);
|
||||||
@@ -962,40 +970,40 @@ struct npc_Meredy_Huntswell_209059 : public npc_dungeon_follower_base
|
|||||||
DoCastForced(me, SPELL_MEREDY_BLAZING_BARRIER);
|
DoCastForced(me, SPELL_MEREDY_BLAZING_BARRIER);
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_FIREBALL:
|
case EVENT_MEREDY_FIREBALL:
|
||||||
if (!me->HasUnitState(UNIT_STATE_CASTING))
|
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
|
||||||
DoCastVictimMage(SPELL_MEREDY_FIREBALL);
|
DoCastVictimMage(SPELL_MEREDY_FIREBALL);
|
||||||
events.ScheduleEvent(EVENT_MEREDY_FIREBALL, 2500ms);
|
events.ScheduleEvent(EVENT_MEREDY_FIREBALL, 1500ms);
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_PYROBLAST:
|
case EVENT_MEREDY_PYROBLAST:
|
||||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||||
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 500ms);
|
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 500ms);
|
||||||
else
|
else if (victim)
|
||||||
{
|
{
|
||||||
DoCastVictimMage(SPELL_MEREDY_PYROBLAST);
|
DoCastVictimMage(SPELL_MEREDY_PYROBLAST);
|
||||||
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 7s);
|
events.ScheduleEvent(EVENT_MEREDY_PYROBLAST, 4s);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_FIRE_BLAST:
|
case EVENT_MEREDY_FIRE_BLAST:
|
||||||
if (!me->HasUnitState(UNIT_STATE_CASTING))
|
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
|
||||||
DoCastVictimMage(SPELL_MEREDY_FIRE_BLAST);
|
DoCastVictimMage(SPELL_MEREDY_FIRE_BLAST);
|
||||||
events.ScheduleEvent(EVENT_MEREDY_FIRE_BLAST, 5s);
|
events.ScheduleEvent(EVENT_MEREDY_FIRE_BLAST, 2s);
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_PHOENIX_FLAMES:
|
case EVENT_MEREDY_PHOENIX_FLAMES:
|
||||||
if (!me->HasUnitState(UNIT_STATE_CASTING))
|
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
|
||||||
{
|
{
|
||||||
DoCastVictimMage(SPELL_MEREDY_PHOENIX_FLAMES);
|
DoCastVictimMage(SPELL_MEREDY_PHOENIX_FLAMES);
|
||||||
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_2, 300ms);
|
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_2, 300ms);
|
||||||
}
|
}
|
||||||
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_FLAMES, 30s);
|
events.ScheduleEvent(EVENT_MEREDY_PHOENIX_FLAMES, 10s);
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_PHOENIX_2:
|
case EVENT_MEREDY_PHOENIX_2:
|
||||||
if (!me->HasUnitState(UNIT_STATE_CASTING))
|
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
|
||||||
DoCastVictimMage(SPELL_MEREDY_PHOENIX_FLAMES);
|
DoCastVictimMage(SPELL_MEREDY_PHOENIX_FLAMES);
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_METEOR:
|
case EVENT_MEREDY_METEOR:
|
||||||
if (!me->HasUnitState(UNIT_STATE_CASTING))
|
if (!me->HasUnitState(UNIT_STATE_CASTING) && victim)
|
||||||
DoCastVictimMage(SPELL_MEREDY_METEOR);
|
DoCastVictimMage(SPELL_MEREDY_METEOR);
|
||||||
events.ScheduleEvent(EVENT_MEREDY_METEOR, 55s);
|
events.ScheduleEvent(EVENT_MEREDY_METEOR, 20s);
|
||||||
break;
|
break;
|
||||||
case EVENT_MEREDY_BARRIER:
|
case EVENT_MEREDY_BARRIER:
|
||||||
if (!me->HasAura(SPELL_MEREDY_BLAZING_BARRIER))
|
if (!me->HasAura(SPELL_MEREDY_BLAZING_BARRIER))
|
||||||
@@ -1120,16 +1128,23 @@ struct npc_Crenna_Earth_Daughter_209072 : public npc_dungeon_follower_base
|
|||||||
|
|
||||||
Group* group = owner->GetGroup();
|
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)
|
if (group)
|
||||||
{
|
{
|
||||||
for (Group::MemberSlot const& slot : group->GetMemberSlots())
|
for (Group::MemberSlot const& slot : group->GetMemberSlots())
|
||||||
{
|
{
|
||||||
if (Unit* u = ObjectAccessor::GetUnit(*me, slot.guid))
|
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())
|
if (u->IsInCombat())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (owner->IsInCombat())
|
else if (owner->IsInCombat())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user