Merge remote-tracking branch 'acore/master' into test-staging
# Conflicts: # .github/workflows/windows_build.yml
This commit is contained in:
@@ -1241,6 +1241,23 @@ void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, ui
|
||||
|
||||
void SmartAI::StopFollow(bool complete)
|
||||
{
|
||||
if (complete)
|
||||
{
|
||||
Player* player = ObjectAccessor::GetPlayer(*me, mFollowGuid);
|
||||
if (player && mFollowCredit > 0)
|
||||
{
|
||||
if (!mFollowCreditType)
|
||||
player->RewardPlayerAndGroupAtEvent(mFollowCredit, me);
|
||||
else
|
||||
player->GroupEventHappens(mFollowCredit, me);
|
||||
}
|
||||
|
||||
SetDespawnTime(5000);
|
||||
StartDespawn();
|
||||
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
|
||||
}
|
||||
|
||||
mFollowGuid.Clear();
|
||||
mFollowDist = 0;
|
||||
mFollowAngle = 0;
|
||||
@@ -1252,23 +1269,6 @@ void SmartAI::StopFollow(bool complete)
|
||||
me->GetMotionMaster()->Clear(false);
|
||||
me->StopMoving();
|
||||
me->GetMotionMaster()->MoveIdle();
|
||||
|
||||
if (!complete)
|
||||
return;
|
||||
|
||||
Player* player = ObjectAccessor::GetPlayer(*me, mFollowGuid);
|
||||
if (player)
|
||||
{
|
||||
if (!mFollowCreditType)
|
||||
player->RewardPlayerAndGroupAtEvent(mFollowCredit, me);
|
||||
else
|
||||
player->GroupEventHappens(mFollowCredit, me);
|
||||
}
|
||||
|
||||
SetDespawnTime(5000);
|
||||
StartDespawn();
|
||||
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
|
||||
}
|
||||
|
||||
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker)
|
||||
|
||||
@@ -676,6 +676,8 @@ enum RBACPermissions
|
||||
RBAC_PERM_COMMAND_CHARACTER_CHECK_PROFESSION = 911,
|
||||
RBAC_PERM_COMMAND_GOBJECT_LOAD = 912,
|
||||
RBAC_PERM_COMMAND_BF_QUEUE = 913,
|
||||
RBAC_PERM_COMMAND_PET_LIST = 914,
|
||||
RBAC_PERM_COMMAND_PET_DELETE = 915,
|
||||
// custom permissions 1000+
|
||||
RBAC_PERM_MAX
|
||||
};
|
||||
|
||||
@@ -2718,7 +2718,8 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
|
||||
|
||||
float x, y, z;
|
||||
x = y = z = 0.0f;
|
||||
if (GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)->GetResetPosition(x, y, z))
|
||||
MovementGenerator* idleSlot = GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE);
|
||||
if (idleSlot && idleSlot->GetResetPosition(x, y, z))
|
||||
return IsInDist2d(x, y, dist);
|
||||
else
|
||||
return IsInDist2d(&m_homePosition, dist);
|
||||
|
||||
@@ -14351,7 +14351,7 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const
|
||||
|
||||
SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(c->GetEntry());
|
||||
if (clickPair.first == clickPair.second)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
for (SpellClickInfoContainer::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
|
||||
{
|
||||
|
||||
@@ -8387,8 +8387,10 @@ void Unit::EnergizeBySpell(Unit* victim, uint32 spellID, uint32 damage, Powers p
|
||||
{
|
||||
victim->ModifyPower(powerType, damage, false);
|
||||
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID))
|
||||
victim->GetThreatMgr().ForwardThreatForAssistingMe(this, float(damage) / 2.0f, spellInfo, true);
|
||||
// Happiness is internal hunter pet state, not combat assistance — energizing it must not generate threat
|
||||
if (powerType != POWER_HAPPINESS)
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID))
|
||||
victim->GetThreatMgr().ForwardThreatForAssistingMe(this, float(damage) / 2.0f, spellInfo, true);
|
||||
|
||||
SendEnergizeSpellLog(victim, spellID, damage, powerType);
|
||||
}
|
||||
|
||||
@@ -459,14 +459,35 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
return;
|
||||
}
|
||||
|
||||
// log traded items before moving (pointers become invalid after moveItems)
|
||||
// cache traded item info before moving (pointers become invalid after moveItems if items merge into existing stacks)
|
||||
struct TradedItemInfo
|
||||
{
|
||||
bool present = false;
|
||||
std::string name;
|
||||
uint32 entry = 0;
|
||||
uint32 count = 0;
|
||||
};
|
||||
TradedItemInfo myItemsInfo[TRADE_SLOT_TRADED_COUNT];
|
||||
TradedItemInfo hisItemsInfo[TRADE_SLOT_TRADED_COUNT];
|
||||
std::string myItemsStr, hisItemsStr;
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (myItems[i])
|
||||
myItemsStr += Acore::StringFormat("{} (Entry:{}) x{}, ", myItems[i]->GetTemplate()->Name1, myItems[i]->GetEntry(), myItems[i]->GetCount());
|
||||
{
|
||||
myItemsInfo[i].present = true;
|
||||
myItemsInfo[i].name = myItems[i]->GetTemplate()->Name1;
|
||||
myItemsInfo[i].entry = myItems[i]->GetEntry();
|
||||
myItemsInfo[i].count = myItems[i]->GetCount();
|
||||
myItemsStr += Acore::StringFormat("{} (Entry:{}) x{}, ", myItemsInfo[i].name, myItemsInfo[i].entry, myItemsInfo[i].count);
|
||||
}
|
||||
if (hisItems[i])
|
||||
hisItemsStr += Acore::StringFormat("{} (Entry:{}) x{}, ", hisItems[i]->GetTemplate()->Name1, hisItems[i]->GetEntry(), hisItems[i]->GetCount());
|
||||
{
|
||||
hisItemsInfo[i].present = true;
|
||||
hisItemsInfo[i].name = hisItems[i]->GetTemplate()->Name1;
|
||||
hisItemsInfo[i].entry = hisItems[i]->GetEntry();
|
||||
hisItemsInfo[i].count = hisItems[i]->GetCount();
|
||||
hisItemsStr += Acore::StringFormat("{} (Entry:{}) x{}, ", hisItemsInfo[i].name, hisItemsInfo[i].entry, hisItemsInfo[i].count);
|
||||
}
|
||||
}
|
||||
|
||||
// execute trade: 1. remove
|
||||
@@ -509,10 +530,10 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (myItems[i])
|
||||
if (myItemsInfo[i].present)
|
||||
LOG_GM(GetAccountId(), "GM {} (Account: {}) traded item: {} (Entry: {} Count: {}) to {}",
|
||||
_player->GetName(), GetAccountId(),
|
||||
myItems[i]->GetTemplate()->Name1, myItems[i]->GetEntry(), myItems[i]->GetCount(),
|
||||
myItemsInfo[i].name, myItemsInfo[i].entry, myItemsInfo[i].count,
|
||||
trader->GetName());
|
||||
}
|
||||
if (my_trade->GetMoney() > 0)
|
||||
@@ -523,10 +544,10 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
{
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
|
||||
{
|
||||
if (hisItems[i])
|
||||
if (hisItemsInfo[i].present)
|
||||
LOG_GM(trader->GetSession()->GetAccountId(), "GM {} (Account: {}) traded item: {} (Entry: {} Count: {}) to {}",
|
||||
trader->GetName(), trader->GetSession()->GetAccountId(),
|
||||
hisItems[i]->GetTemplate()->Name1, hisItems[i]->GetEntry(), hisItems[i]->GetCount(),
|
||||
hisItemsInfo[i].name, hisItemsInfo[i].entry, hisItemsInfo[i].count,
|
||||
_player->GetName());
|
||||
}
|
||||
if (his_trade->GetMoney() > 0)
|
||||
|
||||
@@ -1478,6 +1478,15 @@ enum AcoreStrings
|
||||
LANG_POOL_LOOKUP_IN_POOL = 35431,
|
||||
LANG_POOL_LOOKUP_NOT_IN_POOL = 35432,
|
||||
LANG_POOL_LOOKUP_USE_INFO = 35433,
|
||||
LANG_POOL_LOOKUP_NOTARGET = 35434
|
||||
LANG_POOL_LOOKUP_NOTARGET = 35434,
|
||||
|
||||
// Pet list command
|
||||
LANG_PET_LIST_HEADER = 35435,
|
||||
LANG_PET_LIST_ENTRY = 35436,
|
||||
LANG_PET_LIST_EMPTY = 35437,
|
||||
|
||||
// Pet delete command
|
||||
LANG_PET_DELETE_NOT_FOUND = 35438,
|
||||
LANG_PET_DELETE_SUCCESS = 35439
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -5390,10 +5390,10 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
|
||||
{
|
||||
if (target->isDead() && GetBase() && target->IsCreature() && target->GetEntry() == 24601)
|
||||
{
|
||||
auto caster2 = GetBase()->GetCaster();
|
||||
if (caster2 && caster2->IsPlayer())
|
||||
if (Unit* caster2 = GetBase()->GetCaster())
|
||||
{
|
||||
caster2->ToPlayer()->KilledMonsterCredit(25987);
|
||||
if (Player* player = caster2->GetCharmerOrOwnerPlayerOrPlayerItself())
|
||||
player->KilledMonsterCredit(25987);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "Chat.h"
|
||||
#include "CommandScript.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include <algorithm>
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
@@ -37,9 +39,11 @@ public:
|
||||
{
|
||||
static ChatCommandTable petCommandTable =
|
||||
{
|
||||
{ "create", HandlePetCreateCommand, rbac::RBAC_PERM_COMMAND_PET_CREATE, Console::No },
|
||||
{ "learn", HandlePetLearnCommand, rbac::RBAC_PERM_COMMAND_PET_LEARN, Console::No },
|
||||
{ "unlearn", HandlePetUnlearnCommand, rbac::RBAC_PERM_COMMAND_PET_UNLEARN, Console::No }
|
||||
{ "create", HandlePetCreateCommand, rbac::RBAC_PERM_COMMAND_PET_CREATE, Console::No },
|
||||
{ "delete", HandlePetDeleteCommand, rbac::RBAC_PERM_COMMAND_PET_DELETE, Console::Yes },
|
||||
{ "learn", HandlePetLearnCommand, rbac::RBAC_PERM_COMMAND_PET_LEARN, Console::No },
|
||||
{ "list", HandlePetListCommand, rbac::RBAC_PERM_COMMAND_PET_LIST, Console::Yes },
|
||||
{ "unlearn", HandlePetUnlearnCommand, rbac::RBAC_PERM_COMMAND_PET_UNLEARN, Console::No }
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
@@ -126,6 +130,103 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandlePetDeleteCommand(ChatHandler* handler, PlayerIdentifier owner, uint32 petNumber)
|
||||
{
|
||||
ObjectGuid ownerGuid = owner.GetGUID();
|
||||
|
||||
QueryResult result = CharacterDatabase.Query(
|
||||
"SELECT entry, name FROM character_pet WHERE id = {} AND owner = {}",
|
||||
petNumber, ownerGuid.GetCounter());
|
||||
|
||||
if (!result)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_PET_DELETE_NOT_FOUND, petNumber, owner.GetName(),
|
||||
ownerGuid.GetCounter());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Player* online = owner.GetConnectedPlayer())
|
||||
{
|
||||
if (Pet* activePet = online->GetPet())
|
||||
{
|
||||
if (activePet->GetCharmInfo() &&
|
||||
activePet->GetCharmInfo()->GetPetNumber() == petNumber)
|
||||
{
|
||||
online->RemovePet(activePet, PET_SAVE_AS_DELETED);
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the pet from the in-memory PetStable so the stable UI / call-pet
|
||||
// logic does not reference a row that no longer exists in the database.
|
||||
if (PetStable* stable = online->GetPetStable())
|
||||
{
|
||||
if (stable->CurrentPet && stable->CurrentPet->PetNumber == petNumber)
|
||||
stable->CurrentPet.reset();
|
||||
|
||||
for (Optional<PetStable::PetInfo>& stabled : stable->StabledPets)
|
||||
if (stabled && stabled->PetNumber == petNumber)
|
||||
stabled.reset();
|
||||
|
||||
stable->UnslottedPets.erase(
|
||||
std::remove_if(stable->UnslottedPets.begin(), stable->UnslottedPets.end(),
|
||||
[petNumber](PetStable::PetInfo const& p) { return p.PetNumber == petNumber; }),
|
||||
stable->UnslottedPets.end());
|
||||
}
|
||||
}
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
uint32 entry = fields[0].Get<uint32>();
|
||||
std::string name = fields[1].Get<std::string>();
|
||||
|
||||
std::string creatureName = "<unknown>";
|
||||
if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(entry))
|
||||
creatureName = cInfo->Name;
|
||||
|
||||
Pet::DeleteFromDB(petNumber);
|
||||
|
||||
handler->PSendSysMessage(LANG_PET_DELETE_SUCCESS, petNumber, name, entry, creatureName,
|
||||
owner.GetName(), ownerGuid.GetCounter());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandlePetListCommand(ChatHandler* handler, PlayerIdentifier owner)
|
||||
{
|
||||
ObjectGuid ownerGuid = owner.GetGUID();
|
||||
|
||||
QueryResult result = CharacterDatabase.Query(
|
||||
"SELECT id, entry, level, slot, name, PetType FROM character_pet "
|
||||
"WHERE owner = {} ORDER BY slot, id",
|
||||
ownerGuid.GetCounter());
|
||||
|
||||
if (!result)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_PET_LIST_EMPTY, owner.GetName(), ownerGuid.GetCounter());
|
||||
return true;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_PET_LIST_HEADER, owner.GetName(), ownerGuid.GetCounter());
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 petNumber = fields[0].Get<uint32>();
|
||||
uint32 entry = fields[1].Get<uint32>();
|
||||
uint8 level = fields[2].Get<uint8>();
|
||||
uint8 slot = fields[3].Get<uint8>();
|
||||
std::string name = fields[4].Get<std::string>();
|
||||
uint8 petType = fields[5].Get<uint8>();
|
||||
|
||||
std::string creatureName = "<unknown>";
|
||||
if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(entry))
|
||||
creatureName = cInfo->Name;
|
||||
|
||||
handler->PSendSysMessage(LANG_PET_LIST_ENTRY, petNumber, uint32(slot), entry,
|
||||
creatureName, name, uint32(level), uint32(petType));
|
||||
} while (result->NextRow());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandlePetUnlearnCommand(ChatHandler* handler, SpellInfo const* spell)
|
||||
{
|
||||
if (!spell)
|
||||
|
||||
@@ -189,17 +189,31 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleSendMoneyCommand(ChatHandler* handler, Optional<PlayerIdentifier> target, QuotedString subject, QuotedString text, uint32 money)
|
||||
static bool HandleSendMoneyCommand(ChatHandler* handler, Optional<PlayerIdentifier> target, QuotedString subject, QuotedString text, Tail money)
|
||||
{
|
||||
if (!target)
|
||||
{
|
||||
target = PlayerIdentifier::FromTargetOrSelf(handler);
|
||||
}
|
||||
|
||||
if (!target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto IsExistWord = [](std::string_view line, std::initializer_list<std::string_view> words)
|
||||
{
|
||||
for (auto const& word : words)
|
||||
{
|
||||
if (line.find(word) != std::string_view::npos)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Optional<int32> moneyToAddO = IsExistWord(money, { "g", "s", "c" }) ? MoneyStringToMoney(money) : Acore::StringTo<int32>(money);
|
||||
|
||||
if (!moneyToAddO)
|
||||
return false;
|
||||
|
||||
int32 moneyToAdd = *moneyToAddO;
|
||||
|
||||
// from console show not existed sender
|
||||
MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUID().GetCounter() : 0, MAIL_STATIONERY_GM);
|
||||
@@ -207,7 +221,7 @@ public:
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
MailDraft(subject, text)
|
||||
.AddMoney(money)
|
||||
.AddMoney(moneyToAdd)
|
||||
.SendMailTo(trans, MailReceiver(target->GetConnectedPlayer(), target->GetGUID().GetCounter()), sender);
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
+4
-4
@@ -21,10 +21,10 @@
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_SHADOWWORDPAIN = 10894,
|
||||
SPELL_MANABURN = 10876,
|
||||
SPELL_PSYCHICSCREAM = 8122,
|
||||
SPELL_SHADOWSHIELD = 22417
|
||||
SPELL_SHADOWWORDPAIN = 14032,
|
||||
SPELL_MANABURN = 14033,
|
||||
SPELL_PSYCHICSCREAM = 13704,
|
||||
SPELL_SHADOWSHIELD = 12040
|
||||
};
|
||||
|
||||
struct boss_high_interrogator_gerstahn : public ScriptedAI
|
||||
|
||||
@@ -416,6 +416,9 @@ struct boss_sartharion : public BossAI
|
||||
for (uint32 i : dragons)
|
||||
if (Creature* boss = instance->GetCreature(i))
|
||||
boss->DespawnOrUnsummon();
|
||||
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESPERON);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION);
|
||||
}
|
||||
|
||||
void SetData(uint32 type, uint32 data) override
|
||||
@@ -838,6 +841,8 @@ struct boss_sartharion_dragonAI : public BossAI
|
||||
{
|
||||
Talk(SAY_VESPERON_DEATH);
|
||||
instance->DoAction(ACTION_CLEAR_PORTAL);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESPERON);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION);
|
||||
if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS)
|
||||
instance->SetBossState(DATA_VESPERON, DONE);
|
||||
break;
|
||||
|
||||
@@ -215,12 +215,10 @@ public:
|
||||
instance = me->GetInstanceScript();
|
||||
if (!instance)
|
||||
me->IsAIEnabled = false;
|
||||
first = (instance && !instance->GetPersistentData(PERSISTENT_DATA_INTRO));
|
||||
}
|
||||
|
||||
InstanceScript* instance;
|
||||
EventMap events;
|
||||
bool first;
|
||||
bool shortver;
|
||||
|
||||
void Reset() override
|
||||
@@ -228,9 +226,8 @@ public:
|
||||
shortver = false;
|
||||
events.Reset();
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||
if (first)
|
||||
if (instance && !instance->GetPersistentData(PERSISTENT_DATA_INTRO))
|
||||
{
|
||||
first = false;
|
||||
events.ScheduleEvent(EVENT_PRE_INTRO_1, 10s);
|
||||
events.ScheduleEvent(EVENT_PRE_INTRO_2, 11s);
|
||||
events.ScheduleEvent(EVENT_EMOTE_LEADER_1, 16s);
|
||||
|
||||
@@ -493,51 +493,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class npc_icefang : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_icefang() : CreatureScript("npc_icefang") { }
|
||||
|
||||
struct npc_icefangAI : public npc_escortAI
|
||||
{
|
||||
npc_icefangAI(Creature* creature) : npc_escortAI(creature) { }
|
||||
|
||||
void AttackStart(Unit* /*who*/) override { }
|
||||
void JustEngagedWith(Unit* /*who*/) override { }
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override { }
|
||||
|
||||
void PassengerBoarded(Unit* who, int8 /*seatId*/, bool apply) override
|
||||
{
|
||||
if (who->IsPlayer())
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
me->SetWalk(false);
|
||||
Start(false, who->GetGUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using CreatureAI::WaypointReached;
|
||||
void WaypointReached(uint32 /*waypointId*/) override { }
|
||||
void JustDied(Unit* /*killer*/) override { }
|
||||
void OnCharmed(bool /*apply*/) override { }
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
npc_escortAI::UpdateAI(diff);
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new npc_icefangAI(creature);
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_hyldsmeet_protodrake : public CreatureAI
|
||||
{
|
||||
explicit npc_hyldsmeet_protodrake(Creature* creature) : CreatureAI(creature), _accessoryInstalled(false), _accessoryRespawnTimer(0)
|
||||
@@ -1344,7 +1299,6 @@ void AddSC_storm_peaks()
|
||||
RegisterSpellScript(spell_q13007_iron_colossus);
|
||||
new npc_brunnhildar_prisoner();
|
||||
new npc_freed_protodrake();
|
||||
new npc_icefang();
|
||||
RegisterCreatureAI(npc_hyldsmeet_protodrake);
|
||||
RegisterSpellScript(spell_close_rift_aura);
|
||||
new npc_vehicle_d16_propelled_delivery();
|
||||
|
||||
@@ -209,6 +209,16 @@ struct npc_pet_dk_ebon_gargoyle : ScriptedAI
|
||||
_decisionTimer -= diff;
|
||||
if (!UpdateVictimWithGaze())
|
||||
{
|
||||
// Re-engage if we still have a valid victim but lost engagement
|
||||
// (e.g., PvP combat reference expired during CC like Cyclone)
|
||||
if (Unit* victim = me->GetVictim())
|
||||
{
|
||||
if (me->IsValidAttackTarget(victim))
|
||||
{
|
||||
me->EngageWithTarget(victim);
|
||||
return;
|
||||
}
|
||||
}
|
||||
MySelectNextTarget();
|
||||
return;
|
||||
}
|
||||
@@ -356,6 +366,26 @@ struct npc_pet_dk_army_of_the_dead : public AggressorAI
|
||||
if (me->IsValidAttackTarget(attacker))
|
||||
AttackStart(attacker);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
{
|
||||
// Re-engage if we still have a valid victim but lost engagement
|
||||
// (e.g., combat reference expired during CC like knockback)
|
||||
if (Unit* victim = me->GetVictim())
|
||||
{
|
||||
if (me->IsValidAttackTarget(victim))
|
||||
{
|
||||
me->EngageWithTarget(victim);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_pet_dk_dancing_rune_weapon : public NullCreatureAI
|
||||
|
||||
Reference in New Issue
Block a user