Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-02-06 17:51:47 +08:00
32 changed files with 1008 additions and 61 deletions

View File

@@ -2740,10 +2740,17 @@ Quests.IgnoreAutoAccept = 0
Quests.IgnoreAutoComplete = 0
#
# Rate.RewardQuestMoney
# Description: Allows to tweak the amount of money rewarded by quests (does not affect RewardBonusMoney).
# Default: 1
Rate.RewardQuestMoney = 1
#
# Rate.RewardBonusMoney
# Description: Allows to further tweak the amount of extra money rewarded by quests when the player
# is at MaxPlayerLevel (this amount is specified in quest_template.RewardBonusMoney).
# is at MaxPlayerLevel.
# Default: 1
Rate.RewardBonusMoney = 1

View File

@@ -771,6 +771,14 @@ struct ItemPosCount
};
typedef std::vector<ItemPosCount> ItemPosCountVec;
struct SavedItem
{
Item* item;
uint16 dstpos;
SavedItem(Item* _item, uint16 dstpos) : item(_item), dstpos(dstpos) {}
};
enum TransferAbortReason
{
TRANSFER_ABORT_NONE = 0x00,

View File

@@ -4413,27 +4413,27 @@ void ObjectMgr::LoadQuests()
"ID, QuestType, QuestLevel, MinLevel, QuestSortID, QuestInfoID, SuggestedGroupNum, TimeAllowed, AllowableRaces,"
// 9 10 11 12
"RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, "
// 13 14 15 16 17 18 19 20 21
"RewardNextQuest, RewardXPDifficulty, RewardMoney, RewardMoneyDifficulty, RewardBonusMoney, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
// 22 23 24 25 26 27
// 13 14 15 16 17 18 19 20
"RewardNextQuest, RewardXPDifficulty, RewardMoney, RewardMoneyDifficulty, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
// 21 22 23 24 25 26
"StartItem, Flags, RewardTitle, RequiredPlayerKills, RewardTalents, RewardArenaPoints, "
// 28 29 30 31 32 33 34 35
// 27 28 29 30 31 32 33 34
"RewardItem1, RewardAmount1, RewardItem2, RewardAmount2, RewardItem3, RewardAmount3, RewardItem4, RewardAmount4, "
// 36 37 38 39 40 41 42 43 44 45 46 47
// 35 36 37 38 39 40 41 42 43 44 45 46
"RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemID6, RewardChoiceItemQuantity6, "
// 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
// 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
"RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionID5, RewardFactionValue5, RewardFactionOverride5,"
// 62 64 65 66
// 61 63 64 65
"POIContinent, POIx, POIy, POIPriority, "
// 67 68 69 70 71
// 66 67 68 69 70
"LogTitle, LogDescription, QuestDescription, AreaDescription, QuestCompletionLog, "
// 72 73 74 75 76 77 78 79
// 71 72 73 74 75 76 77 78
"RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
// 80 81 82 83 84 85 86 87
// 79 80 81 82 83 84 85 86
"ItemDrop1, ItemDrop2, ItemDrop3, ItemDrop4, ItemDropQuantity1, ItemDropQuantity2, ItemDropQuantity3, ItemDropQuantity4, "
// 88 89 90 91 92 93 94 95 96 97 98 99
// 87 88 89 90 91 92 93 94 95 96 97 98
"RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
// 100 101 102 103 104
// 99 100 101 102 103
"Unknown0, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4"
" FROM quest_template");
if (!result)

View File

@@ -1799,6 +1799,8 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData)
{
LOG_DEBUG("network", "CMSG_EQUIPMENT_SET_USE");
std::vector<std::unique_ptr<SavedItem>> savedItems;
uint8 errorId = 0;
for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
ObjectGuid itemGuid;
@@ -1824,7 +1826,6 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData)
uint16 dstpos = i | (INVENTORY_SLOT_BAG_0 << 8);
InventoryResult msg;
Item* uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
if (uItem)
{
@@ -1844,11 +1845,19 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData)
msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sDest, uItem, false);
if (msg == EQUIP_ERR_OK)
{
savedItems.emplace_back(std::make_unique<SavedItem>(uItem, dstpos));
_player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
_player->StoreItem(sDest, uItem, true);
}
else
_player->SendEquipError(msg, uItem, nullptr);
{
errorId = 4;
for (uint8_t j = 0; j < savedItems.size(); ++j)
{
_player->SwapItem(savedItems[j].get()->item->GetPos(), savedItems[j].get()->dstpos);
}
break;
}
continue;
}
@@ -1875,7 +1884,7 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData)
}
WorldPacket data(SMSG_EQUIPMENT_SET_USE_RESULT, 1);
data << uint8(0); // 4 - equipment swap failed - inventory is full
data << uint8(errorId); // 4 - equipment swap failed - inventory is full
SendPacket(&data);
}

View File

@@ -47,22 +47,21 @@ Quest::Quest(Field* questRecord)
RewardXPDifficulty = questRecord[14].Get<uint8>();
RewardMoney = questRecord[15].Get<int32>();
RewardMoneyDifficulty = questRecord[16].Get<uint32>();
RewardBonusMoney = questRecord[17].Get<uint32>();
RewardDisplaySpell = questRecord[18].Get<uint32>();
RewardSpell = questRecord[19].Get<int32>();
RewardHonor = questRecord[20].Get<uint32>();
RewardKillHonor = questRecord[21].Get<float>();
StartItem = questRecord[22].Get<uint32>();
Flags = questRecord[23].Get<uint32>();
RewardTitleId = questRecord[24].Get<uint8>();
RequiredPlayerKills = questRecord[25].Get<uint8>();
RewardTalents = questRecord[26].Get<uint8>();
RewardArenaPoints = questRecord[27].Get<uint16>();
RewardDisplaySpell = questRecord[17].Get<uint32>();
RewardSpell = questRecord[18].Get<int32>();
RewardHonor = questRecord[19].Get<uint32>();
RewardKillHonor = questRecord[20].Get<float>();
StartItem = questRecord[21].Get<uint32>();
Flags = questRecord[22].Get<uint32>();
RewardTitleId = questRecord[23].Get<uint8>();
RequiredPlayerKills = questRecord[24].Get<uint8>();
RewardTalents = questRecord[25].Get<uint8>();
RewardArenaPoints = questRecord[26].Get<uint16>();
for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
{
RewardItemId[i] = questRecord[28 + i * 2].Get<uint32>();
RewardItemIdCount[i] = questRecord[29 + i * 2].Get<uint16>();
RewardItemId[i] = questRecord[27 + i * 2].Get<uint32>();
RewardItemIdCount[i] = questRecord[28 + i * 2].Get<uint16>();
if (RewardItemId[i])
++_rewItemsCount;
@@ -70,8 +69,8 @@ Quest::Quest(Field* questRecord)
for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
{
RewardChoiceItemId[i] = questRecord[36 + i * 2].Get<uint32>();
RewardChoiceItemCount[i] = questRecord[37 + i * 2].Get<uint16>();
RewardChoiceItemId[i] = questRecord[35 + i * 2].Get<uint32>();
RewardChoiceItemCount[i] = questRecord[36 + i * 2].Get<uint16>();
if (RewardChoiceItemId[i])
++_rewChoiceItemsCount;
@@ -79,26 +78,26 @@ Quest::Quest(Field* questRecord)
for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
{
RewardFactionId[i] = questRecord[48 + i * 3].Get<uint16>();
RewardFactionValueId[i] = questRecord[49 + i * 3].Get<int32>();
RewardFactionValueIdOverride[i] = questRecord[50 + i * 3].Get<int32>();
RewardFactionId[i] = questRecord[47 + i * 3].Get<uint16>();
RewardFactionValueId[i] = questRecord[48 + i * 3].Get<int32>();
RewardFactionValueIdOverride[i] = questRecord[49 + i * 3].Get<int32>();
}
POIContinent = questRecord[63].Get<uint16>();
POIx = questRecord[64].Get<float>();
POIy = questRecord[65].Get<float>();
POIPriority = questRecord[66].Get<uint32>();
Title = questRecord[67].Get<std::string>();
Objectives = questRecord[68].Get<std::string>();
Details = questRecord[69].Get<std::string>();
AreaDescription = questRecord[70].Get<std::string>();
CompletedText = questRecord[71].Get<std::string>();
POIContinent = questRecord[62].Get<uint16>();
POIx = questRecord[63].Get<float>();
POIy = questRecord[64].Get<float>();
POIPriority = questRecord[65].Get<uint32>();
Title = questRecord[66].Get<std::string>();
Objectives = questRecord[67].Get<std::string>();
Details = questRecord[68].Get<std::string>();
AreaDescription = questRecord[69].Get<std::string>();
CompletedText = questRecord[70].Get<std::string>();
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
{
RequiredNpcOrGo[i] = questRecord[72 + i].Get<int32>();
RequiredNpcOrGoCount[i] = questRecord[76 + i].Get<uint16>();
ObjectiveText[i] = questRecord[101 + i].Get<std::string>();
RequiredNpcOrGo[i] = questRecord[71 + i].Get<int32>();
RequiredNpcOrGoCount[i] = questRecord[75 + i].Get<uint16>();
ObjectiveText[i] = questRecord[100 + i].Get<std::string>();
if (RequiredNpcOrGo[i])
++_reqCreatureOrGOcount;
@@ -106,14 +105,14 @@ Quest::Quest(Field* questRecord)
for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
{
ItemDrop[i] = questRecord[80 + i].Get<uint32>();
ItemDropQuantity[i] = questRecord[84 + i].Get<uint16>();
ItemDrop[i] = questRecord[79 + i].Get<uint32>();
ItemDropQuantity[i] = questRecord[83 + i].Get<uint16>();
}
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
RequiredItemId[i] = questRecord[88 + i].Get<uint32>();
RequiredItemCount[i] = questRecord[94 + i].Get<uint16>();
RequiredItemId[i] = questRecord[87 + i].Get<uint32>();
RequiredItemCount[i] = questRecord[93 + i].Get<uint16>();
if (RequiredItemId[i])
++_reqItemsCount;
@@ -252,15 +251,19 @@ int32 Quest::GetRewOrReqMoney(uint8 playerLevel) const
}
}
return static_cast<int32>(rewardedMoney * sWorld->getRate(RATE_REWARD_BONUS_MONEY));
return static_cast<int32>(rewardedMoney * sWorld->getRate(RATE_REWARD_QUEST_MONEY));
}
uint32 Quest::GetRewMoneyMaxLevel() const
{
if (HasFlag(QUEST_FLAGS_NO_MONEY_FROM_XP))
return 0;
uint32 rewMoney = 0;
return static_cast<int32>(RewardBonusMoney * sWorld->getRate(RATE_REWARD_BONUS_MONEY));
if (HasFlag(QUEST_FLAGS_NO_MONEY_FROM_XP))
return rewMoney;
rewMoney = (XPValue(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) * (6 * COPPER));
// https://wowpedia.fandom.com/wiki/Quest?oldid=1035002 Formula is XP gained * 6c
return static_cast<int32>(rewMoney * sWorld->getRate(RATE_REWARD_BONUS_MONEY));
}
bool Quest::IsAutoAccept() const

View File

@@ -371,7 +371,6 @@ protected:
float RewardKillHonor;
int32 RewardMoney;
uint32 RewardMoneyDifficulty;
uint32 RewardBonusMoney;
uint32 RewardDisplaySpell;
int32 RewardSpell;
uint32 POIContinent;

View File

@@ -4699,7 +4699,7 @@ void Spell::SendSpellStart()
uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY;
if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count && !m_spellInfo->IsChanneled())
if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count && !(m_spellInfo->IsChanneled() || m_spellInfo->CastTimeEntry->CastTime > 0))
castFlags |= CAST_FLAG_PENDING;
if (m_spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
@@ -4781,7 +4781,7 @@ void Spell::SendSpellGo()
uint32 castFlags = CAST_FLAG_UNKNOWN_9;
// triggered spells with spell visual != 0
if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count && !m_spellInfo->IsChanneled())
if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count && !(m_spellInfo->IsChanneled() || m_spellInfo->CastTimeEntry->CastTime > 0))
castFlags |= CAST_FLAG_PENDING;
if (m_spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))

View File

@@ -460,6 +460,7 @@ enum Rates
RATE_BUYVALUE_ITEM_ARTIFACT,
RATE_BUYVALUE_ITEM_HEIRLOOM,
RATE_DROP_MONEY,
RATE_REWARD_QUEST_MONEY,
RATE_REWARD_BONUS_MONEY,
RATE_XP_KILL,
RATE_XP_BG_KILL_AV,

View File

@@ -495,6 +495,7 @@ void World::LoadConfigSettings(bool reload)
_rate_values[RATE_DROP_ITEM_GROUP_AMOUNT] = sConfigMgr->GetOption<float>("Rate.Drop.Item.GroupAmount", 1.0f);
_rate_values[RATE_DROP_MONEY] = sConfigMgr->GetOption<float>("Rate.Drop.Money", 1.0f);
_rate_values[RATE_REWARD_QUEST_MONEY] = sConfigMgr->GetOption<float>("Rate.RewardQuestMoney", 1.0f);
_rate_values[RATE_REWARD_BONUS_MONEY] = sConfigMgr->GetOption<float>("Rate.RewardBonusMoney", 1.0f);
_rate_values[RATE_XP_KILL] = sConfigMgr->GetOption<float>("Rate.XP.Kill", 1.0f);
_rate_values[RATE_XP_BG_KILL_AV] = sConfigMgr->GetOption<float>("Rate.XP.BattlegroundKillAV", 1.0f);

View File

@@ -485,6 +485,27 @@ struct npc_midsummer_torch_target : public ScriptedAI
// SPELLS
///////////////////////////////
class spell_fire_festival_fortitude : public SpellScript
{
PrepareSpellScript(spell_fire_festival_fortitude)
void SelectTargets(std::list<WorldObject*>& targets)
{
targets.clear();
GetCaster()->GetMap()->DoForAllPlayers([&](Player* p)
{
if (p->GetZoneId() == GetCaster()->GetZoneId())
targets.push_back(p);
});
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_fire_festival_fortitude::SelectTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
}
};
class spell_bonfires_blessing : public AuraScript
{
PrepareAuraScript(spell_bonfires_blessing)
@@ -1249,6 +1270,7 @@ void AddSC_event_midsummer_scripts()
RegisterCreatureAI(npc_midsummer_ribbon_pole_target);
// Spells
RegisterSpellScript(spell_fire_festival_fortitude);
RegisterSpellScript(spell_bonfires_blessing);
RegisterSpellScript(spell_gen_crab_disguise);
RegisterSpellScript(spell_midsummer_ribbon_pole_firework);

View File

@@ -17,6 +17,7 @@
#include "CreatureScript.h"
#include "Player.h"
#include "MoveSplineInit.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "World.h"
@@ -795,6 +796,61 @@ public:
};
};
enum ToyPlane
{
NPC_DND_DALARAN_TOY_STORE_PLANE_STRING_HOOK = 29807,
SPELL_TOY_PLANE_CABLE = 55281,
};
struct npc_cosmetic_toy_plane : public ScriptedAI
{
npc_cosmetic_toy_plane(Creature* creature) : ScriptedAI(creature)
{
}
void Reset() override
{
Movement::MoveSplineInit init(me);
init.MovebyPath(_movementArray);
init.SetFly();
init.SetCyclic();
// one full loop is 10.76 seconds (sniffed value)
// loop diameter is approx. 9.225f (calculated from waypoints below)
// with a circumference of approx. 28.98f
// this results in flying speed of approx. 2.7f
init.SetVelocity(2.7f);
init.Launch();
scheduler.Schedule(420ms, [this](TaskContext context)
{
if (Creature* cr = me->FindNearestCreature(NPC_DND_DALARAN_TOY_STORE_PLANE_STRING_HOOK, 42.0f))
DoCast(cr, SPELL_TOY_PLANE_CABLE, true);
else
context.Repeat();
});
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
}
private:
Movement::PointsArray const _movementArray = {
// cyclic movementspine unfortunately includes spawn into loop
// which results in an imperfect loop right now
// CO1 SPAWN(5809.888, 683.5779, 653.6859)
G3D::Vector3(5813.709, 682.51855, 653.6033),
G3D::Vector3(5816.815, 684.8459, 653.5755),
G3D::Vector3(5817.1997, 688.83527, 653.631),
G3D::Vector3(5814.235, 691.6307, 653.6587),
G3D::Vector3(5809.9287, 690.98224, 653.7697),
G3D::Vector3(5808.225, 687.1498, 653.6322),
G3D::Vector3(5809.8423, 683.6158, 653.6862),
};
};
void AddSC_dalaran()
{
// our
@@ -804,6 +860,7 @@ void AddSC_dalaran()
new npc_archmage_landalock();
new npc_dalaran_mage();
new npc_dalaran_warrior();
RegisterCreatureAI(npc_cosmetic_toy_plane);
// theirs
new npc_mageguard_dalaran();

View File

@@ -86,6 +86,7 @@ struct boss_lady_vashj : public BossAI
_count = 0;
_recentlySpoken = false;
_batTimer = 20s;
_playerAngle = 0.0f;
BossAI::Reset();
ScheduleHealthCheckEvent(70, [&]{
@@ -180,7 +181,12 @@ struct boss_lady_vashj : public BossAI
scheduler.CancelAll();
scheduler.Schedule(2400ms, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_FORKED_LIGHTNING);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
_playerAngle = me->GetAngle(target);
me->SetOrientation(_playerAngle);
DoCast(target, SPELL_FORKED_LIGHTNING);
}
context.Repeat(2400ms, 12450ms);
}).Schedule(0s, [this](TaskContext context)
{
@@ -258,6 +264,7 @@ struct boss_lady_vashj : public BossAI
}
private:
float _playerAngle;
bool _recentlySpoken;
bool _intro;
int32 _count;

View File

@@ -45,7 +45,12 @@ enum Yells
SAY_THALADRED_AGGRO = 0,
SAY_SANGUINAR_AGGRO = 0,
SAY_CAPERNIAN_AGGRO = 0,
SAY_TELONICUS_AGGRO = 0
SAY_TELONICUS_AGGRO = 0,
SAY_THALADRED_DEATH = 1,
SAY_SANGUINAR_DEATH = 1,
SAY_CAPERNIAN_DEATH = 1,
SAY_TELONICUS_DEATH = 1,
EMOTE_THALADRED_FIXATE = 2
};
enum Spells
@@ -103,7 +108,28 @@ enum Spells
SPELL_NETHER_BEAM = 35869,
SPELL_NETHER_BEAM_DAMAGE = 35873,
SPELL_REMOTE_TOY_STUN = 37029
SPELL_REMOTE_TOY_STUN = 37029,
// Advisors
// Universal
SPELL_KAEL_PHASE_TWO = 36709,
// Sanguinar
SPELL_BELLOWING_ROAR = 44863,
// Capernian
SPELL_CAPERNIAN_FIREBALL = 36971,
SPELL_CONFLAGRATION = 37018,
SPELL_ARCANE_BURST = 36970,
// Telonicus
SPELL_BOMB = 37036,
SPELL_REMOTE_TOY = 37027,
// Thaladred
SPELL_PSYCHIC_BLOW = 36966,
SPELL_REND = 36965,
SPELL_SILENCE = 30225
};
enum Misc
@@ -721,6 +747,204 @@ public:
}
};
struct npc_lord_sanguinar : public ScriptedAI
{
npc_lord_sanguinar(Creature* creature) : ScriptedAI(creature) {
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(0s, [&]{
DoCastSelf(SPELL_BELLOWING_ROAR);
}, 15s);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_SANGUINAR_DEATH);
DoCastSelf(SPELL_KAEL_PHASE_TWO, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
};
struct npc_capernian : public ScriptedAI
{
npc_capernian(Creature* creature) : ScriptedAI(creature) {
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(0ms, [&]{
DoCastVictim(SPELL_CAPERNIAN_FIREBALL);
}, 2500ms);
ScheduleTimedEvent(7000ms, 10000ms, [&]{
DoCastRandomTarget(SPELL_CONFLAGRATION, 0, 30.0f);
}, 18500ms, 20500ms);
ScheduleTimedEvent(3s, [&]{
DoCastRandomTarget(SPELL_ARCANE_BURST, 0, 8.0f);
}, 6s);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_CAPERNIAN_DEATH);
DoCastSelf(SPELL_KAEL_PHASE_TWO, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
};
struct npc_telonicus : public ScriptedAI
{
npc_telonicus(Creature* creature) : ScriptedAI(creature) {
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(0ms, [&]{
DoCastVictim(SPELL_BOMB);
}, 3600ms, 7100ms);
ScheduleTimedEvent(13250ms, [&]{
DoCastRandomTarget(SPELL_CONFLAGRATION, 0, 100.0f);
}, 15750ms);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_TELONICUS_DEATH);
DoCastSelf(SPELL_KAEL_PHASE_TWO, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
};
struct npc_thaladred : public ScriptedAI
{
npc_thaladred(Creature* creature) : ScriptedAI(creature) {
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
me->SetWalk(true);
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(100ms, [&]
{
DoResetThreatList();
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->AddThreat(target, 10000000.0f);
Talk(EMOTE_THALADRED_FIXATE, target);
}
}, 10000ms);
ScheduleTimedEvent(4000ms, 19350ms, [&]
{
DoCastVictim(SPELL_PSYCHIC_BLOW);
}, 15700ms, 48900ms);
ScheduleTimedEvent(3000ms, 6050ms, [&]
{
DoCastVictim(SPELL_REND);
}, 15700ms, 48900ms);
ScheduleTimedEvent(3000ms,6050ms, [&]
{
if (Unit* victim = me->GetVictim())
{
if (victim->IsNonMeleeSpellCast(false, false, true))
{
DoCastVictim(SPELL_SILENCE);
}
}
}, 3600ms, 15200ms);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_THALADRED_DEATH);
DoCastSelf(SPELL_KAEL_PHASE_TWO, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
};
class spell_kaelthas_kael_phase_two : public SpellScriptLoader
{
public:
@@ -1052,6 +1276,10 @@ public:
void AddSC_boss_kaelthas()
{
new boss_kaelthas();
RegisterTheEyeAI(npc_lord_sanguinar);
RegisterTheEyeAI(npc_capernian);
RegisterTheEyeAI(npc_telonicus);
RegisterTheEyeAI(npc_thaladred);
new spell_kaelthas_kael_phase_two();
new spell_kaelthas_remote_toy();
new spell_kaelthas_summon_weapons();