aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/Utilities/StringFormat.h2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp14
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp1
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h8
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBot.cpp3
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBot.h2
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp8
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp6
-rw-r--r--src/server/game/Entities/Player/Player.cpp135
-rw-r--r--src/server/game/Entities/Player/Player.h23
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp11
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp46
-rwxr-xr-xsrc/server/game/Entities/Vehicle/Vehicle.cpp1
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp19
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp5
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp2
-rw-r--r--src/server/game/Handlers/PetHandler.cpp17
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp4
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp26
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h2
-rw-r--r--src/server/game/Spells/Spell.cpp131
-rw-r--r--src/server/game/Spells/SpellInfo.cpp49
-rw-r--r--src/server/game/Spells/SpellInfo.h4
-rw-r--r--src/server/game/Spells/SpellMgr.cpp30
-rw-r--r--src/server/scripts/Events/fireworks_show.cpp7
-rw-r--r--src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp47
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp35
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp27
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp1
-rw-r--r--src/server/scripts/Outland/BlackTemple/black_temple.cpp42
-rw-r--r--src/server/scripts/Outland/BlackTemple/black_temple.h17
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp2
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp16
-rw-r--r--src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp29
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp17
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp15
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp37
-rw-r--r--src/server/worldserver/Main.cpp17
-rw-r--r--src/server/worldserver/worldserver.conf.dist9
40 files changed, 542 insertions, 327 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 0428738f2dd..6855ac871da 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -63,7 +63,7 @@ target_include_directories(common
target_link_libraries(common
PUBLIC
boost
- cppformat
+ fmt
g3dlib
Detour
sfmt
diff --git a/src/common/Utilities/StringFormat.h b/src/common/Utilities/StringFormat.h
index e21b1024e87..6f101a78ef1 100644
--- a/src/common/Utilities/StringFormat.h
+++ b/src/common/Utilities/StringFormat.h
@@ -19,7 +19,7 @@
#ifndef TRINITYCORE_STRING_FORMAT_H
#define TRINITYCORE_STRING_FORMAT_H
-#include "cppformat/format.h"
+#include "fmt/format.h"
namespace Trinity
{
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 6f0a71007c4..2c35bea6b0f 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -2057,6 +2057,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
delete targets;
break;
}
+ case SMART_ACTION_GO_SET_GO_STATE:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsGameObject(*itr))
+ (*itr)->ToGameObject()->SetGoState((GOState)e.action.goState.state);
+
+ delete targets;
+ break;
+ }
case SMART_ACTION_SEND_TARGET_TO_TARGET:
{
ObjectList* targets = GetTargets(e, unit);
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 0d54bfad0f7..3c4995d101a 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -1223,6 +1223,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_JUMP_TO_POS:
case SMART_ACTION_SEND_GOSSIP_MENU:
case SMART_ACTION_GO_SET_LOOT_STATE:
+ case SMART_ACTION_GO_SET_GO_STATE:
case SMART_ACTION_SEND_TARGET_TO_TARGET:
case SMART_ACTION_SET_HOME_POS:
case SMART_ACTION_SET_HEALTH_REGEN:
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 0a13f31f2ac..58cb6c1bd95 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -551,8 +551,9 @@ enum SMART_ACTION
SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf
SMART_ACTION_SET_CORPSE_DELAY = 116, // timer
SMART_ACTION_DISABLE_EVADE = 117, // 0/1 (1 = disabled, 0 = enabled)
+ SMART_ACTION_GO_SET_GO_STATE = 118, // state
- SMART_ACTION_END = 118
+ SMART_ACTION_END = 119
};
struct SmartAction
@@ -1014,6 +1015,11 @@ struct SmartAction
struct
{
+ uint32 state;
+ } goState;
+
+ struct
+ {
uint32 group;
uint32 attackInvoker;
} creatureGroup;
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp
index 4601495a70b..9cd273f4326 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.cpp
@@ -293,6 +293,9 @@ void AuctionBotConfig::GetConfigFromFile()
SetConfig(CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_KEY, "AuctionHouseBot.Class.RandomStackRatio.Key", 100);
SetConfig(CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_MISC, "AuctionHouseBot.Class.RandomStackRatio.Misc", 100);
SetConfig(CONFIG_AHBOT_CLASS_RANDOMSTACKRATIO_GLYPH, "AuctionHouseBot.Class.RandomStackRatio.Glyph", 0);
+
+ SetConfig(CONFIG_AHBOT_BIDPRICE_MIN, "AuctionHouseBot.BidPrice.Min", 0.6f);
+ SetConfig(CONFIG_AHBOT_BIDPRICE_MAX, "AuctionHouseBot.BidPrice.Max", 0.9f);
}
char const* AuctionBotConfig::GetHouseTypeName(AuctionHouseType houseType)
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.h b/src/server/game/AuctionHouseBot/AuctionHouseBot.h
index 4f68a172255..663dd871294 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBot.h
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.h
@@ -193,6 +193,8 @@ enum AuctionBotConfigBoolValues
enum AuctionBotConfigFloatValues
{
CONFIG_AHBOT_BUYER_CHANCE_FACTOR,
+ CONFIG_AHBOT_BIDPRICE_MIN,
+ CONFIG_AHBOT_BIDPRICE_MAX,
CONFIG_AHBOT_FLOAT_COUNT
};
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
index 34127f0c59f..2e96d2a4847 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
@@ -732,9 +732,11 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
buyp = static_cast<uint32>(frand(basePriceFloat - range, basePriceFloat + range) + 0.5f);
if (buyp == 0)
buyp = 1;
- uint32 basePrice = buyp * .5;
- range = buyp * .4;
- bidp = urand(static_cast<uint32>(basePrice - range + 0.5f), static_cast<uint32>(basePrice + range + 0.5f)) + 1;
+
+ float bidPercentage = frand(sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BIDPRICE_MIN), sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BIDPRICE_MAX));
+ bidp = static_cast<uint32>(bidPercentage * buyp);
+ if (bidp == 0)
+ bidp = 1;
}
// Determines the stack size to use for the item
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 397fb6a7375..8704734d45f 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -880,8 +880,10 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
}
// Resistance
- for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
- SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i]));
+ // Hunters pet should not inherit resistances from creature_template, they have separate auras for that
+ if (!IsHunterPet())
+ for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i]));
//health, mana, armor and resistance
PetLevelInfo const* pInfo = sObjectMgr->GetPetLevelInfo(creature_ID, petlevel);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 21d1f25bf42..b50c6603c59 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -4742,6 +4742,9 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
// update visibility
UpdateObjectVisibility();
+ // recast lost by death auras of any items held in the inventory
+ CastAllObtainSpells();
+
if (!applySickness)
return;
@@ -7754,6 +7757,46 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
UpdateDamagePhysical(attType);
}
+void Player::CastAllObtainSpells()
+{
+ for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
+ if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
+ ApplyItemObtainSpells(item, true);
+
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ {
+ Bag* bag = GetBagByPos(i);
+ if (!bag)
+ continue;
+
+ for (uint32 slot = 0; slot < bag->GetBagSize(); ++slot)
+ if (Item* item = bag->GetItemByPos(slot))
+ ApplyItemObtainSpells(item, true);
+ }
+}
+
+void Player::ApplyItemObtainSpells(Item* item, bool apply)
+{
+ ItemTemplate const* itemTemplate = item->GetTemplate();
+ for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
+ {
+ if (itemTemplate->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger
+ continue;
+
+ int32 const spellId = itemTemplate->Spells[i].SpellId;
+ if (spellId <= 0)
+ continue;
+
+ if (apply)
+ {
+ if (!HasAura(spellId))
+ CastSpell(this, spellId, true, item);
+ }
+ else
+ RemoveAurasDueToSpell(spellId);
+ }
+}
+
void Player::ApplyItemDependentAuras(Item* item, bool apply)
{
if (apply)
@@ -11889,12 +11932,8 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
AddEnchantmentDurations(pItem);
AddItemDurations(pItem);
- const ItemTemplate* proto = pItem->GetTemplate();
- for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
- if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
- if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
- if (!HasAura(proto->Spells[i].SpellId))
- CastSpell(this, proto->Spells[i].SpellId, true, pItem);
+ if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
+ ApplyItemObtainSpells(pItem, true);
return pItem;
}
@@ -11932,12 +11971,8 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
pItem2->SetState(ITEM_CHANGED, this);
- const ItemTemplate* proto = pItem2->GetTemplate();
- for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
- if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
- if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
- if (!HasAura(proto->Spells[i].SpellId))
- CastSpell(this, proto->Spells[i].SpellId, true, pItem2);
+ if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
+ ApplyItemObtainSpells(pItem2, true);
return pItem2;
}
@@ -12289,10 +12324,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
pItem->ClearSoulboundTradeable(this);
RemoveTradeableItem(pItem);
- const ItemTemplate* proto = pItem->GetTemplate();
- for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
- if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
- RemoveAurasDueToSpell(proto->Spells[i].SpellId);
+ ApplyItemObtainSpells(pItem, false);
ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
sScriptMgr->OnItemRemove(this, pItem);
@@ -13063,9 +13095,6 @@ void Player::SwapItem(uint16 src, uint16 dst)
RemoveItem(dstbag, dstslot, false);
RemoveItem(srcbag, srcslot, false);
- if (srcbag == INVENTORY_SLOT_BAG_0 && srcslot < INVENTORY_SLOT_BAG_END)
- ApplyItemDependentAuras(pSrcItem, false);
-
// add to dest
if (IsInventoryPos(dst))
StoreItem(sDest, pSrcItem, true);
@@ -13074,9 +13103,6 @@ void Player::SwapItem(uint16 src, uint16 dst)
else if (IsEquipmentPos(dst))
EquipItem(eDest, pSrcItem, true);
- if (dstbag == INVENTORY_SLOT_BAG_0 && dstslot < INVENTORY_SLOT_BAG_END)
- ApplyItemDependentAuras(pDstItem, false);
-
// add to src
if (IsInventoryPos(src))
StoreItem(sDest2, pDstItem, true);
@@ -13085,6 +13111,11 @@ void Player::SwapItem(uint16 src, uint16 dst)
else if (IsEquipmentPos(src))
EquipItem(eDest2, pDstItem, true);
+ // if inventory item was moved, check if we can remove dependent auras, because they were not removed in Player::RemoveItem (update was set to false)
+ // do this after swaps are done, we pass nullptr because both weapons could be swapped and none of them should be ignored
+ if ((srcbag == INVENTORY_SLOT_BAG_0 && srcslot < INVENTORY_SLOT_BAG_END) || (dstbag == INVENTORY_SLOT_BAG_0 && dstslot < INVENTORY_SLOT_BAG_END))
+ ApplyItemDependentAuras((Item*)nullptr, false);
+
// if player is moving bags and is looting an item inside this bag
// release the loot
if (GetLootGUID())
@@ -20846,59 +20877,12 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
m_spellMods[mod->op].erase(mod);
}
-// Restore spellmods in case of failed cast
-void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/)
+bool Player::HasSpellModApplied(SpellModifier* mod, Spell* spell)
{
- if (!spell || spell->m_appliedMods.empty())
- return;
-
- std::list<Aura*> aurasQueue;
- for (uint8 i = 0; i < MAX_SPELLMOD; ++i)
- {
- for (auto itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr)
- {
- SpellModifier* mod = *itr;
-
- // Spellmods without charged aura cannot be charged
- if (!mod->ownerAura->IsUsingCharges())
- continue;
-
- // Restore only specific owner aura mods
- if (ownerAuraId && mod->spellId != ownerAuraId)
- continue;
-
- if (aura && mod->ownerAura != aura)
- continue;
-
- // Check if mod affected this spell
- // First, check if the mod aura applied at least one spellmod to this spell
- Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
- if (iterMod == spell->m_appliedMods.end())
- continue;
- // Second, check if the current mod is one of those applied by the mod aura
- if (!(mod->mask & spell->m_spellInfo->SpellFamilyFlags))
- continue;
-
- // remove from list - This will be done after all mods have been gone through
- // to ensure we iterate over all mods of an aura before removing said aura
- // from applied mods (Else, an aura with two mods on the current spell would
- // only see the first of its modifier restored)
- aurasQueue.push_back(mod->ownerAura);
-
- // add charges back to aura
- mod->ownerAura->ModCharges(1);
- }
- }
-
- for (Aura* aura : aurasQueue)
- spell->m_appliedMods.erase(aura);
-}
+ if (!spell)
+ return false;
-void Player::RestoreAllSpellMods(uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/)
-{
- for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
- if (Spell* spell = m_currentSpells[i])
- RestoreSpellMods(spell, ownerAuraId, aura);
+ return spell->m_appliedMods.count(mod->ownerAura) != 0;
}
void Player::ApplyModToSpell(SpellModifier* mod, Spell* spell)
@@ -25455,6 +25439,7 @@ void Player::SendEquipmentSetList()
{
if (itr->second.state == EQUIPMENT_SET_DELETED)
continue;
+
data.appendPackGUID(itr->second.Guid);
data << uint32(itr->first);
data << itr->second.Name;
@@ -25464,8 +25449,10 @@ void Player::SendEquipmentSetList()
// ignored slots stored in IgnoreMask, client wants "1" as raw GUID, so no HighGuid::Item
if (itr->second.IgnoreMask & (1 << i))
data.appendPackGUID(uint64(1));
- else
+ else if (itr->second.Items[i] > 0) // send proper data (do not append 0 with high guid)
data << ObjectGuid(HighGuid::Item, 0, itr->second.Items[i]).WriteAsPacked();
+ else
+ data.appendPackGUID(uint64(0));
}
++count; // client have limit but it checked at loading and set
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index dcaa2199496..156f9340951 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1607,9 +1607,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
static bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr);
template <SpellModOp op, class T>
void ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell = nullptr) const;
- void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr);
- void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr);
static void ApplyModToSpell(SpellModifier* mod, Spell* spell);
+ static bool HasSpellModApplied(SpellModifier* mod, Spell* spell);
void SetSpellModTakingSpell(Spell* spell, bool apply);
void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false);
@@ -1958,6 +1957,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void ResetAllPowers();
+ void CastAllObtainSpells();
+ void ApplyItemObtainSpells(Item* item, bool apply);
void ApplyItemDependentAuras(Item* item, bool apply);
void _ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemAuras = true);
@@ -2634,7 +2635,7 @@ void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullpt
case SPELLMOD_CASTING_TIME:
{
SpellModifier* modInstantSpell = nullptr;
- for (SpellModifier* mod : m_spellMods[SPELLMOD_CASTING_TIME])
+ for (SpellModifier* mod : m_spellMods[op])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -2658,7 +2659,7 @@ void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullpt
case SPELLMOD_CRITICAL_CHANCE:
{
SpellModifier* modCritical = nullptr;
- for (SpellModifier* mod : m_spellMods[SPELLMOD_CRITICAL_CHANCE])
+ for (SpellModifier* mod : m_spellMods[op])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -2694,14 +2695,22 @@ void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullpt
break;
case SPELLMOD_PCT:
{
- // skip percent mods for null basevalue (most important for spell mods with charges)
+ // skip percent mods with null basevalue (most important for spell mods with charges)
if (basevalue == T(0))
continue;
// special case (skip > 10sec spell casts for instant cast setting)
- if (op == SPELLMOD_CASTING_TIME)
+ if (op == SPELLMOD_CASTING_TIME && mod->value <= -100 && basevalue >= T(10000))
+ continue;
+ else if (!Player::HasSpellModApplied(mod, spell))
{
- if (mod->value <= -100 && basevalue >= T(10000))
+ // special case for Surge of Light, don't apply critical chance reduction if other mods not applied (ie procs while casting another spell)
+ // (Surge of Light is the only PCT_MOD on critical chance)
+ if (op == SPELLMOD_CRITICAL_CHANCE)
+ continue;
+ // special case for Backdraft, dont' apply GCD reduction if cast time reduction wasn't applied (ie when Backlash is consumed first)
+ // (Backdraft is the only PCT_MOD on global cooldown)
+ else if (op == SPELLMOD_GLOBAL_COOLDOWN)
continue;
}
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index c4a1f90b9f2..c01b7ae3e6b 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -836,17 +836,10 @@ void Player::UpdateExpertise(WeaponAttackType attack)
int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
- Item* weapon = GetWeaponForAttack(attack, true);
+ Item const* weapon = GetWeaponForAttack(attack, true);
expertise += GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE, [weapon](AuraEffect const* aurEff) -> bool
{
- // item neutral spell
- if (aurEff->GetSpellInfo()->EquippedItemClass == -1)
- return true;
- // item dependent spell
- else if (weapon && weapon->IsFitToSpellRequirements(aurEff->GetSpellInfo()))
- return true;
-
- return false;
+ return aurEff->GetSpellInfo()->IsItemFitToSpellRequirements(weapon);
});
if (expertise < 0)
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 53c778fccd6..da829b882b0 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -1623,17 +1623,10 @@ uint32 Unit::CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo
{
float arpPct = ToPlayer()->GetRatingBonusValue(CR_ARMOR_PENETRATION);
- Item* weapon = ToPlayer()->GetWeaponForAttack(attackType, true);
+ Item const* weapon = ToPlayer()->GetWeaponForAttack(attackType, true);
arpPct += GetTotalAuraModifier(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT, [weapon](AuraEffect const* aurEff) -> bool
{
- // item neutral spell
- if (aurEff->GetSpellInfo()->EquippedItemClass == -1)
- return true;
- // item dependent spell
- else if (weapon && weapon->IsFitToSpellRequirements(aurEff->GetSpellInfo()))
- return true;
-
- return false;
+ return aurEff->GetSpellInfo()->IsItemFitToSpellRequirements(weapon);
});
// no more than 100%
@@ -7216,7 +7209,34 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto
crit_chance = 0.0f;
// For other schools
else if (GetTypeId() == TYPEID_PLAYER)
+ {
crit_chance = GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + GetFirstSchoolInMask(schoolMask));
+
+ // register aura mod, this is needed for Arcane Potency
+ if (Spell* spell = ToPlayer()->m_spellModTakingSpell)
+ {
+ std::vector<Aura*> affectingAuras;
+ (void)GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE, [&affectingAuras](AuraEffect const* aurEff) -> bool
+ {
+ affectingAuras.push_back(aurEff->GetBase());
+ return true;
+ });
+
+ (void)GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, [&affectingAuras, schoolMask](AuraEffect const* aurEff) -> bool
+ {
+ if ((aurEff->GetMiscValue() & schoolMask) != 0)
+ {
+ affectingAuras.push_back(aurEff->GetBase());
+ return true;
+ }
+
+ return false;
+ });
+
+ for (Aura* aura : affectingAuras)
+ spell->m_appliedMods.insert(aura);
+ }
+ }
else
{
crit_chance = (float)m_baseSpellCritChance;
@@ -11567,6 +11587,10 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
if (isRewardAllowed && creature && creature->GetLootRecipient())
player = creature->GetLootRecipient();
+ // Exploit fix
+ if (creature && creature->IsPet() && creature->GetOwnerGUID().IsPlayer())
+ isRewardAllowed = false;
+
// Reward player, his pets, and group/raid members
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
if (isRewardAllowed && player && player != victim)
@@ -11966,8 +11990,8 @@ void Unit::SetStunned(bool apply)
SetTarget(EnsureVictim()->GetGUID());
// don't remove UNIT_FLAG_STUNNED for pet when owner is mounted (disabled pet's interface)
- Unit* owner = GetOwner();
- if (!owner || (owner->GetTypeId() == TYPEID_PLAYER && !owner->ToPlayer()->IsMounted()))
+ Unit* owner = GetCharmerOrOwner();
+ if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !owner->ToPlayer()->IsMounted())
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
if (!HasUnitState(UNIT_STATE_ROOT)) // prevent moving if it also has root effect
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index e930d20c9fd..54e61be7a34 100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -181,6 +181,7 @@ void Vehicle::ApplyAllImmunities()
_me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_SCHOOL_IMMUNITY, true);
_me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_UNATTACKABLE, true);
_me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_SCHOOL_ABSORB, true);
+ _me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_BANISH, true);
_me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_SHIELD, true);
_me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_IMMUNE_SHIELD, true);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 46cab0f63ad..1d92f1d2c0b 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -1488,6 +1488,13 @@ void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData)
ObjectGuid itemGuid;
recvData >> itemGuid.ReadAsPacked();
+ // if client sends 0, it means empty slot
+ if (itemGuid.IsEmpty())
+ {
+ eqSet.Items[i] = 0;
+ continue;
+ }
+
// equipment manager sends "1" (as raw GUID) for slots set to "ignore" (don't touch slot at equip set)
if (itemGuid.GetRawValue() == 1)
{
@@ -1496,13 +1503,13 @@ void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData)
continue;
}
+ // some cheating checks
Item* item = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
-
- if (!item && itemGuid) // cheating check 1
- return;
-
- if (item && item->GetGUID() != itemGuid) // cheating check 2
- return;
+ if (!item || item->GetGUID() != itemGuid)
+ {
+ eqSet.Items[i] = 0;
+ continue;
+ }
eqSet.Items[i] = itemGuid.GetCounter();
}
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 8f9a3ba6ffc..285edf4f143 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -253,6 +253,11 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData)
_player->EquipItem(eSrc, pDstItem, true);
_player->AutoUnequipOffhandIfNeed();
+
+ // if inventory item was moved, check if we can remove dependent auras, because they were not removed in Player::RemoveItem (update was set to false)
+ // do this after swaps are done, we pass nullptr because both weapons could be swapped and none of them should be ignored
+ if ((srcbag == INVENTORY_SLOT_BAG_0 && srcslot < INVENTORY_SLOT_BAG_END) || (dstbag == INVENTORY_SLOT_BAG_0 && dstslot < INVENTORY_SLOT_BAG_END))
+ _player->ApplyItemDependentAuras((Item*)nullptr, false);
}
}
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index b5a21bbcc89..d7baddb9b50 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -400,7 +400,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
// player can be alive if GM/etc
// change the death state to CORPSE to prevent the death timer from
// starting in the next player update
- if (!plrMover->IsAlive())
+ if (plrMover->IsAlive())
plrMover->KillPlayer();
}
}
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 6be8389a485..348f6048064 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -676,20 +676,15 @@ void WorldSession::HandlePetAbandon(WorldPacket& recvData)
// pet/charmed
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
- if (pet)
+ if (pet && pet->ToPet() && pet->ToPet()->getPetType() == HUNTER_PET)
{
- if (pet->IsPet())
+ if (pet->GetGUID() == _player->GetPetGUID())
{
- if (pet->GetGUID() == _player->GetPetGUID())
- {
- uint32 feelty = pet->GetPower(POWER_HAPPINESS);
- pet->SetPower(POWER_HAPPINESS, feelty > 50000 ? (feelty-50000) : 0);
- }
-
- _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
+ uint32 feelty = pet->GetPower(POWER_HAPPINESS);
+ pet->SetPower(POWER_HAPPINESS, feelty > 50000 ? (feelty-50000) : 0);
}
- else if (pet->GetGUID() == _player->GetCharmGUID())
- _player->StopCastingCharm();
+
+ _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
}
}
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index a64d02af59a..ff75c471111 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -5858,6 +5858,10 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
caster->DealDamage(caster, funnelDamage, &cleanDamage, NODAMAGE, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true);
}
+ // %-based heal - does not proc auras
+ if (GetAuraType() == SPELL_AURA_OBS_MOD_HEALTH)
+ return;
+
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL;
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 67ca679e249..232d96a62aa 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -902,7 +902,6 @@ bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode /*= AURA_REMOVE_B
if (refresh)
{
- RefreshSpellMods();
RefreshTimers(resetPeriodicTimer);
// reset charges
@@ -913,13 +912,6 @@ bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode /*= AURA_REMOVE_B
return false;
}
-void Aura::RefreshSpellMods()
-{
- for (Aura::ApplicationMap::const_iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter)
- if (Player* player = appIter->second->GetTarget()->ToPlayer())
- player->RestoreAllSpellMods(0, this);
-}
-
bool Aura::HasMoreThanOneEffectForType(AuraType auraType) const
{
uint32 count = 0;
@@ -1266,24 +1258,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
}
switch (GetId())
{
- case 12536: // Clearcasting
- case 12043: // Presence of Mind
- // Arcane Potency
- if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_MAGE, 2120, 0))
- {
- uint32 spellId = 0;
-
- switch (aurEff->GetId())
- {
- case 31571: spellId = 57529; break;
- case 31572: spellId = 57531; break;
- default:
- TC_LOG_ERROR("spells", "Aura::HandleAuraSpecificMods: Unknown rank of Arcane Potency (%d) found", aurEff->GetId());
- }
- if (spellId)
- caster->CastSpell(caster, spellId, true);
- }
- break;
case 44544: // Fingers of Frost
// Refresh or add visual aura
target->CastCustomSpell(74396, SPELLVALUE_AURA_STACK, sSpellMgr->AssertSpellInfo(74396)->StackAmount, (Unit*)nullptr, true);
diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h
index 9cdf84e82cf..f37f7a96a44 100644
--- a/src/server/game/Spells/Auras/SpellAuras.h
+++ b/src/server/game/Spells/Auras/SpellAuras.h
@@ -148,8 +148,6 @@ class TC_GAME_API Aura
void SetStackAmount(uint8 num);
bool ModStackAmount(int32 num, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT, bool resetPeriodicTimer = true);
- void RefreshSpellMods();
-
uint8 GetCasterLevel() const { return m_casterLevel; }
bool HasMoreThanOneEffectForType(AuraType auraType) const;
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 9d062898ec6..3e9702a9589 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -529,25 +529,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO
memset(m_damageMultipliers, 0, sizeof(m_damageMultipliers));
// Get data for type of attack
- switch (m_spellInfo->DmgClass)
- {
- case SPELL_DAMAGE_CLASS_MELEE:
- if (m_spellInfo->HasAttribute(SPELL_ATTR3_REQ_OFFHAND))
- m_attackType = OFF_ATTACK;
- else
- m_attackType = BASE_ATTACK;
- break;
- case SPELL_DAMAGE_CLASS_RANGED:
- m_attackType = m_spellInfo->IsRangedWeaponSpell() ? RANGED_ATTACK : BASE_ATTACK;
- break;
- default:
- // Wands
- if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG))
- m_attackType = RANGED_ATTACK;
- else
- m_attackType = BASE_ATTACK;
- break;
- }
+ m_attackType = info->GetAttackType();
m_spellSchoolMask = info->GetSchoolMask(); // Can be override for some spell (wand shoot for example)
@@ -2979,13 +2961,10 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
triggeredByAura->GetBase()->SetDuration(0);
}
+ // cleanup after mod system
+ // triggered spell pointer can be not removed in some cases
if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- m_caster->ToPlayer()->RestoreSpellMods(this);
- // cleanup after mod system
- // triggered spell pointer can be not removed in some cases
m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
- }
if (param1 || param2)
SendCastResult(result, &param1, &param2);
@@ -3069,7 +3048,11 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
TriggerGlobalCooldown();
//item: first cast may destroy item and second cast causes crash
- if (!m_casttime && !m_spellInfo->StartRecoveryTime && !m_castItemGUID && GetCurrentContainer() == CURRENT_GENERIC_SPELL)
+ // commented out !m_spellInfo->StartRecoveryTime, it forces instant spells with global cooldown to be processed in spell::update
+ // as a result a spell that passed CheckCast and should be processed instantly may suffer from this delayed process
+ // the easiest bug to observe is LoS check in AddUnitTarget, even if spell passed the CheckCast LoS check the situation can change in spell::update
+ // because target could be relocated in the meantime, making the spell fly to the air (no targets can be registered, so no effects processed, nothing in combat log)
+ if (!m_casttime && /*!m_spellInfo->StartRecoveryTime && */!m_castItemGUID && GetCurrentContainer() == CURRENT_GENERIC_SPELL)
cast(true);
}
}
@@ -3087,8 +3070,6 @@ void Spell::cancel()
{
case SPELL_STATE_PREPARING:
CancelGlobalCooldown();
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- m_caster->ToPlayer()->RestoreSpellMods(this);
// no break
case SPELL_STATE_DELAYED:
SendInterrupted(0);
@@ -3183,14 +3164,12 @@ void Spell::cast(bool skipCheck)
{
SendCastResult(castResult, &param1, &param2);
SendInterrupted(0);
- //restore spell mods
+
+ // cleanup after mod system
+ // triggered spell pointer can be not removed in some cases
if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- m_caster->ToPlayer()->RestoreSpellMods(this);
- // cleanup after mod system
- // triggered spell pointer can be not removed in some cases
m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
- }
+
finish(false);
SetExecutedCurrently(false);
return;
@@ -3210,10 +3189,11 @@ void Spell::cast(bool skipCheck)
my_trade->SetSpell(m_spellInfo->Id, m_CastItem);
SendCastResult(SPELL_FAILED_DONT_REPORT);
SendInterrupted(0);
- m_caster->ToPlayer()->RestoreSpellMods(this);
+
// cleanup after mod system
// triggered spell pointer can be not removed in some cases
m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
+
finish(false);
SetExecutedCurrently(false);
return;
@@ -3236,14 +3216,12 @@ void Spell::cast(bool skipCheck)
if (m_spellState == SPELL_STATE_FINISHED)
{
SendInterrupted(0);
- //restore spell mods
+
+ // cleanup after mod system
+ // triggered spell pointer can be not removed in some cases
if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- m_caster->ToPlayer()->RestoreSpellMods(this);
- // cleanup after mod system
- // triggered spell pointer can be not removed in some cases
m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
- }
+
finish(false);
SetExecutedCurrently(false);
return;
@@ -5764,14 +5742,54 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const
// Get unit state
uint32 const unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS);
+
+ // this check should only be done when player does cast directly
+ // (ie not when it's called from a script) Breaks for example PlayerAI when charmed
+ /*
if (m_caster->GetCharmerGUID())
{
if (Unit* charmer = m_caster->GetCharmer())
if (charmer->GetUnitBeingMoved() != m_caster && !CheckSpellCancelsCharm(param1))
result = SPELL_FAILED_CHARMED;
}
- else if (unitflag & UNIT_FLAG_STUNNED && !usableWhileStunned && !CheckSpellCancelsStun(param1))
- result = SPELL_FAILED_STUNNED;
+ */
+
+ if (unitflag & UNIT_FLAG_STUNNED)
+ {
+ // spell is usable while stunned, check if caster has allowed stun auras, another stun types must prevent cast spell
+ if (usableWhileStunned)
+ {
+ static uint32 const allowedStunMask =
+ 1 << MECHANIC_STUN
+ | 1 << MECHANIC_SLEEP;
+
+ bool foundNotStun = false;
+ Unit::AuraEffectList const& stunAuras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_STUN);
+ for (AuraEffect const* stunEff : stunAuras)
+ {
+ uint32 const stunMechanicMask = stunEff->GetSpellInfo()->GetAllEffectsMechanicMask();
+ if (stunMechanicMask && !(stunMechanicMask & allowedStunMask))
+ {
+ foundNotStun = true;
+
+ // fill up aura mechanic info to send client proper error message
+ if (param1)
+ {
+ *param1 = stunEff->GetSpellInfo()->Effects[stunEff->GetEffIndex()].Mechanic;
+ if (!*param1)
+ *param1 = stunEff->GetSpellInfo()->Mechanic;
+ }
+ break;
+ }
+ }
+
+ if (foundNotStun)
+ result = SPELL_FAILED_STUNNED;
+ }
+ // Not usable while stunned, however spell might provide some immunity that allows to cast it anyway
+ else if (!CheckSpellCancelsStun(param1))
+ result = SPELL_FAILED_STUNNED;
+ }
else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && !CheckSpellCancelsSilence(param1))
result = SPELL_FAILED_SILENCED;
else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && !CheckSpellCancelsPacify(param1))
@@ -6531,12 +6549,11 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
}
// check weapon presence in slots for main/offhand weapons
- if (!(_triggeredCastFlags & TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT) && m_spellInfo->EquippedItemClass >=0)
+ if (!(_triggeredCastFlags & TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT) && m_spellInfo->EquippedItemClass >= 0)
{
- // main hand weapon required
- if (m_spellInfo->HasAttribute(SPELL_ATTR3_MAIN_HAND))
+ auto weaponCheck = [this](WeaponAttackType attackType) -> SpellCastResult
{
- Item* item = m_caster->ToPlayer()->GetWeaponForAttack(BASE_ATTACK);
+ Item const* item = m_caster->ToPlayer()->GetWeaponForAttack(attackType);
// skip spell if no weapon in slot or broken
if (!item || item->IsBroken())
@@ -6545,20 +6562,22 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /
// skip spell if weapon not fit to triggered spell
if (!item->IsFitToSpellRequirements(m_spellInfo))
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+
+ return SPELL_CAST_OK;
+ };
+
+ if (m_spellInfo->HasAttribute(SPELL_ATTR3_MAIN_HAND))
+ {
+ SpellCastResult mainHandResult = weaponCheck(BASE_ATTACK);
+ if (mainHandResult != SPELL_CAST_OK)
+ return mainHandResult;
}
- // offhand hand weapon required
if (m_spellInfo->HasAttribute(SPELL_ATTR3_REQ_OFFHAND))
{
- Item* item = m_caster->ToPlayer()->GetWeaponForAttack(OFF_ATTACK);
-
- // skip spell if no weapon in slot or broken
- if (!item || item->IsBroken())
- return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
-
- // skip spell if weapon not fit to triggered spell
- if (!item->IsFitToSpellRequirements(m_spellInfo))
- return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+ SpellCastResult offHandResult = weaponCheck(OFF_ATTACK);
+ if (offHandResult != SPELL_CAST_OK)
+ return offHandResult;
}
}
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 8394ca71103..dbf9bdc5a40 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -561,7 +561,7 @@ uint32 SpellEffectInfo::GetProvidedTargetMask() const
uint32 SpellEffectInfo::GetMissingTargetMask(bool srcSet /*= false*/, bool dstSet /*= false*/, uint32 mask /*=0*/) const
{
uint32 effImplicitTargetMask = GetTargetFlagMask(GetUsedTargetObjectType());
- uint32 providedTargetMask = GetTargetFlagMask(TargetA.GetObjectType()) | GetTargetFlagMask(TargetB.GetObjectType()) | mask;
+ uint32 providedTargetMask = GetProvidedTargetMask() | mask;
// remove all flags covered by effect target mask
if (providedTargetMask & TARGET_FLAG_UNIT_MASK)
@@ -1245,6 +1245,45 @@ bool SpellInfo::HasInitialAggro() const
return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO));
}
+WeaponAttackType SpellInfo::GetAttackType() const
+{
+ WeaponAttackType result;
+ switch (DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MELEE:
+ if (HasAttribute(SPELL_ATTR3_REQ_OFFHAND))
+ result = OFF_ATTACK;
+ else
+ result = BASE_ATTACK;
+ break;
+ case SPELL_DAMAGE_CLASS_RANGED:
+ result = IsRangedWeaponSpell() ? RANGED_ATTACK : BASE_ATTACK;
+ break;
+ default:
+ // Wands
+ if (IsAutoRepeatRangedSpell())
+ result = RANGED_ATTACK;
+ else
+ result = BASE_ATTACK;
+ break;
+ }
+
+ return result;
+}
+
+bool SpellInfo::IsItemFitToSpellRequirements(Item const* item) const
+{
+ // item neutral spell
+ if (EquippedItemClass == -1)
+ return true;
+
+ // item dependent spell
+ if (item && item->IsFitToSpellRequirements(this))
+ return true;
+
+ return false;
+}
+
bool SpellInfo::IsAffected(uint32 familyName, flag96 const& familyFlags) const
{
if (!familyName)
@@ -3093,13 +3132,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c
if (SpellShapeshiftEntry const* ss = sSpellShapeshiftStore.LookupEntry(caster->GetShapeshiftForm()))
speed = ss->attackSpeed;
else
- {
- WeaponAttackType slot = BASE_ATTACK;
- if (HasAttribute(SPELL_ATTR3_REQ_OFFHAND))
- slot = OFF_ATTACK;
-
- speed = caster->GetAttackTime(slot);
- }
+ speed = caster->GetAttackTime(GetAttackType());
powerCost += speed / 100;
}
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 302c5e2676b..472bea8a1a7 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -452,6 +452,10 @@ class TC_GAME_API SpellInfo
bool IsAutoRepeatRangedSpell() const;
bool HasInitialAggro() const;
+ WeaponAttackType GetAttackType() const;
+
+ bool IsItemFitToSpellRequirements(Item const* item) const;
+
bool IsAffected(uint32 familyName, flag96 const& familyFlags) const;
bool IsAffectedBySpellMods() const;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 02ff0382f98..22798beeaaa 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -2669,6 +2669,16 @@ void SpellMgr::LoadSpellInfoCorrections()
// Entries were not updated after spell effect change, we have to do that manually :/
spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED;
break;
+ case 51627: // Turn the Tables (Rank 1)
+ case 51628: // Turn the Tables (Rank 2)
+ case 51629: // Turn the Tables (Rank 3)
+ spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS;
+ break;
+ case 52910: // Turn the Tables
+ case 52914: // Turn the Tables
+ case 52915: // Turn the Tables
+ spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER);
+ break;
case 29441: // Magic Absorption (Rank 1)
case 29444: // Magic Absorption (Rank 2)
// Caused off by 1 calculation (ie 79 resistance at level 80)
@@ -2747,10 +2757,23 @@ void SpellMgr::LoadSpellInfoCorrections()
case 53385: // Divine Storm (Damage)
spellInfo->MaxAffectedTargets = 4;
break;
+ case 47977: // Magic Broom
+ case 48025: // Headless Horseman's Mount
+ case 54729: // Winged Steed of the Ebon Blade
+ case 71342: // Big Love Rocket
+ case 72286: // Invincible
+ case 74856: // Blazing Hippogryph
+ case 75614: // Celestial Steed
+ case 75973: // X-53 Touring Rocket
+ // First two effects apply auras, which shouldn't be there
+ // due to NO_TARGET applying aura on current caster (core bug)
+ // Just wipe effect data, to mimic blizz-behavior
+ spellInfo->Effects[EFFECT_0].Effect = 0;
+ spellInfo->Effects[EFFECT_1].Effect = 0;
+ break;
case 56342: // Lock and Load (Rank 1)
- // @workaround: Delete dummy effect from rank 1,
- // effect apply aura has TargetA == TargetB == 0 but core still applies it to caster
- // core bug?
+ // @workaround: Delete dummy effect from rank 1
+ // effect apply aura has NO_TARGET but core still applies it to caster (same as above)
spellInfo->Effects[EFFECT_2].Effect = 0;
break;
case 53480: // Roar of Sacrifice
@@ -2889,6 +2912,7 @@ void SpellMgr::LoadSpellInfoCorrections()
case 49064: // Explosive Trap Effect (Rank 5)
case 49065: // Explosive Trap Effect (Rank 6)
case 43446: // Explosive Trap Effect (Hexlord Malacrass)
+ case 50661: // Weakened Resolve
case 68979: // Unleashed Souls
spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(13);
break;
diff --git a/src/server/scripts/Events/fireworks_show.cpp b/src/server/scripts/Events/fireworks_show.cpp
index 4e996f8fe29..ac8d6ed51e2 100644
--- a/src/server/scripts/Events/fireworks_show.cpp
+++ b/src/server/scripts/Events/fireworks_show.cpp
@@ -797,8 +797,7 @@ public:
localtime_r(&time, &localTm);
// Start
- if (((localTm.tm_min == 0 && localTm.tm_sec == 0) && !_started && IsHolidayActive(HOLIDAY_FIREWORKS_SPECTACULAR)) ||
- ((localTm.tm_hour == 0 && localTm.tm_min == 0 && localTm.tm_sec == 0) && !_started && IsEventActive(GAME_EVENT_NEW_YEAR)))
+ if ((localTm.tm_min == 0 && localTm.tm_sec == 0) && !_started && (IsHolidayActive(HOLIDAY_FIREWORKS_SPECTACULAR) || IsEventActive(GAME_EVENT_NEW_YEAR)))
{
_events.ScheduleEvent(EVENT_CHEER, Seconds(1));
_events.ScheduleEvent(EVENT_FIRE, Seconds(1));
@@ -806,14 +805,14 @@ public:
}
// Event is active
- if ((localTm.tm_min >= 0 && localTm.tm_sec >= 1 && localTm.tm_min <= 9 && localTm.tm_sec <= 59) && !_started && IsHolidayActive(HOLIDAY_FIREWORKS_SPECTACULAR))
+ if ((localTm.tm_min >= 0 && localTm.tm_sec >= 1 && localTm.tm_min <= 9 && localTm.tm_sec <= 59 && !_started) && (IsHolidayActive(HOLIDAY_FIREWORKS_SPECTACULAR) || IsEventActive(GAME_EVENT_NEW_YEAR)))
{
_events.ScheduleEvent(EVENT_FIRE, Seconds(1));
_started = true;
}
// Stop
- if ((localTm.tm_min == 10 && localTm.tm_sec == 0) || (localTm.tm_min == 10 && localTm.tm_sec == 0 && localTm.tm_hour == 0 && _started == true))
+ if ((localTm.tm_min == 10 && localTm.tm_sec == 0) && _started == true)
{
_started = false;
_events.ScheduleEvent(EVENT_CHEER, Seconds(1));
diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp
index de425fbfce1..328e2aced43 100644
--- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp
+++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp
@@ -34,8 +34,10 @@ enum Spells
SPELL_ARCANE_EXPLOSION = 26192,
SPELL_EARTH_SHOCK = 26194,
SPELL_TRUE_FULFILLMENT = 785,
+ SPELL_TRUE_FULFILLMENT_2 = 2313,
SPELL_INITIALIZE_IMAGE = 3730,
- SPELL_SUMMON_IMAGES = 747
+ SPELL_SUMMON_IMAGES = 747,
+ SPELL_GENERIC_DISMOUNT = 61286
};
enum Events
@@ -158,9 +160,8 @@ class boss_skeram : public CreatureScript
events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, urand(8000, 18000));
break;
case EVENT_FULLFILMENT:
- /// @todo For some weird reason boss does not cast this
- // Spell actually works, tested in duel
- DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true), SPELL_TRUE_FULFILLMENT, true);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 45.0f, true))
+ DoCast(target, SPELL_TRUE_FULFILLMENT);
events.ScheduleEvent(EVENT_FULLFILMENT, urand(20000, 30000));
break;
case EVENT_BLINK:
@@ -218,6 +219,7 @@ class PlayerOrPetCheck
}
};
+// 26192 - Arcane Explosion
class spell_skeram_arcane_explosion : public SpellScriptLoader
{
public:
@@ -244,8 +246,45 @@ class spell_skeram_arcane_explosion : public SpellScriptLoader
}
};
+// 785 - True Fulfillment
+class spell_skeram_true_fulfillment : public SpellScriptLoader
+{
+public:
+ spell_skeram_true_fulfillment() : SpellScriptLoader("spell_skeram_true_fulfillment") { }
+
+ class spell_skeram_true_fulfillment_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_skeram_true_fulfillment_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_TRUE_FULFILLMENT_2)
+ || !sSpellMgr->GetSpellInfo(SPELL_GENERIC_DISMOUNT))
+ return false;
+ return true;
+ }
+
+ void HandleEffect(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_GENERIC_DISMOUNT, true);
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_TRUE_FULFILLMENT_2, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_skeram_true_fulfillment_SpellScript::HandleEffect, EFFECT_0, SPELL_AURA_MOD_CHARM);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_skeram_true_fulfillment_SpellScript();
+ }
+};
+
void AddSC_boss_skeram()
{
new boss_skeram();
new spell_skeram_arcane_explosion();
+ new spell_skeram_true_fulfillment();
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index d6682e272fd..53dbe095155 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -48,7 +48,7 @@ enum Texts
EMOTE_CORPOREALITY_TIT = 3, // Your companions' efforts force %s further into the twilight realm!
EMOTE_CORPOREALITY_TOT = 4, // Your efforts force %s further out of the twilight realm!
- EMOTE_WARN_LASER = 0, // The orbiting spheres pulse with dark energy!
+ EMOTE_WARN_LASER = 0 // The orbiting spheres pulse with dark energy!
};
enum Spells
@@ -138,7 +138,8 @@ enum Events
EVENT_SHADOW_PULSARS_SHOOT = 14,
EVENT_TRIGGER_BERSERK = 15,
EVENT_TWILIGHT_MENDING = 16,
- EVENT_ACTIVATE_EMBERS = 17
+ EVENT_ACTIVATE_EMBERS = 17,
+ EVENT_EVADE_CHECK = 18
};
enum Actions
@@ -171,7 +172,7 @@ enum Misc
DATA_MATERIAL_DAMAGE_TAKEN = 2,
DATA_STACKS_DISPELLED = 3,
DATA_FIGHT_PHASE = 4,
- DATA_SPAWNED_FLAMES = 5,
+ DATA_SPAWNED_FLAMES = 5
};
enum OrbCarrierSeats
@@ -201,7 +202,8 @@ struct CorporealityEntry
uint32 materialRealmSpell;
};
-CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] = {
+CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] =
+{
{74836, 74831},
{74835, 74830},
{74834, 74829},
@@ -561,7 +563,7 @@ class npc_halion_controller : public CreatureScript
void JustRespawned() override
{
- if (_instance->GetGuidData(DATA_HALION))
+ if (_instance->GetGuidData(DATA_HALION) || _instance->GetBossState(DATA_GENERAL_ZARITHRIAN) != DONE)
return;
Reset();
@@ -596,6 +598,7 @@ class npc_halion_controller : public CreatureScript
_materialDamageTaken = 0;
_events.ScheduleEvent(EVENT_TRIGGER_BERSERK, Minutes(8));
+ _events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(5));
}
void EnterEvadeMode(EvadeReason /*why*/) override
@@ -744,12 +747,27 @@ class npc_halion_controller : public CreatureScript
case EVENT_ACTIVATE_EMBERS:
_summons.DoZoneInCombat(NPC_LIVING_EMBER);
break;
+ case EVENT_EVADE_CHECK:
+ DoCheckEvade();
+ _events.Repeat(Seconds(5));
+ break;
default:
break;
}
}
}
+ void DoCheckEvade()
+ {
+ Map::PlayerList const &players = me->GetMap()->GetPlayers();
+ for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
+ if (Player* player = i->GetSource())
+ if (player->IsAlive() && CheckBoundary(player) && !player->IsGameMaster())
+ return;
+
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
+ }
+
void SetData(uint32 id, uint32 value) override
{
switch (id)
@@ -1765,6 +1783,13 @@ class spell_halion_twilight_phasing : public SpellScriptLoader
{
PrepareSpellScript(spell_halion_twilight_phasing_SpellScript);
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_TWILIGHT_PORTAL))
+ return false;
+ return true;
+ }
+
void Phase()
{
Unit* caster = GetCaster();
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
index c77d21035d0..edc73bca889 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
@@ -209,6 +209,12 @@ class boss_xt002 : public CreatureScript
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MUST_DECONSTRUCT_FASTER);
}
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ summons.DespawnAll();
+ _DespawnAtEvade();
+ }
+
void EnterCombat(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
@@ -444,24 +450,17 @@ class npc_xt002_heart : public CreatureScript
public:
npc_xt002_heart() : CreatureScript("npc_xt002_heart") { }
- struct npc_xt002_heartAI : public ScriptedAI
+ struct npc_xt002_heartAI : public NullCreatureAI
{
- npc_xt002_heartAI(Creature* creature) : ScriptedAI(creature),
- _instance(creature->GetInstanceScript())
- {
- SetCombatMovement(false);
- }
-
- void UpdateAI(uint32 /*diff*/) override { }
+ npc_xt002_heartAI(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { }
void JustDied(Unit* /*killer*/) override
{
- Creature* xt002 = _instance ? ObjectAccessor::GetCreature(*me, _instance->GetGuidData(BOSS_XT002)) : nullptr;
- if (!xt002 || !xt002->AI())
- return;
-
- xt002->AI()->SetData(DATA_TRANSFERED_HEALTH, me->GetHealth());
- xt002->AI()->DoAction(ACTION_ENTER_HARD_MODE);
+ if (Creature* xt002 = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(BOSS_XT002)))
+ {
+ xt002->AI()->SetData(DATA_TRANSFERED_HEALTH, me->GetHealth());
+ xt002->AI()->DoAction(ACTION_ENTER_HARD_MODE);
+ }
}
private:
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
index 3dec0c60991..808fcf1e71f 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
@@ -784,7 +784,6 @@ public:
nextBoss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_IMMUNE_TO_PC);
nextBoss->SetStandState(UNIT_STAND_STATE_STAND);
nextBoss->SetInCombatWithZone();
- nextBoss->Attack(nextBoss->SelectNearestTarget(100), true);
}
currentPhase = PHASE_NONE;
diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.cpp b/src/server/scripts/Outland/BlackTemple/black_temple.cpp
index d9a87fd8494..a48bba44efa 100644
--- a/src/server/scripts/Outland/BlackTemple/black_temple.cpp
+++ b/src/server/scripts/Outland/BlackTemple/black_temple.cpp
@@ -30,7 +30,10 @@ enum Spells
// Angered Soul Fragment
SPELL_GREATER_INVISIBILITY = 41253,
- SPELL_ANGER = 41986
+ SPELL_ANGER = 41986,
+
+ // Illidari Nightlord
+ SPELL_SHADOW_INFERNO_DAMAGE = 39646
};
enum Creatures
@@ -283,9 +286,46 @@ class spell_soul_fragment_anger : public SpellScriptLoader
}
};
+// 39645 - Shadow Inferno
+class spell_illidari_nightlord_shadow_inferno : public SpellScriptLoader
+{
+ public:
+ spell_illidari_nightlord_shadow_inferno() : SpellScriptLoader("spell_illidari_nightlord_shadow_inferno") { }
+
+ class spell_illidari_nightlord_shadow_inferno_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_illidari_nightlord_shadow_inferno_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHADOW_INFERNO_DAMAGE))
+ return false;
+ return true;
+ }
+
+ void OnPeriodic(AuraEffect const* aurEffect)
+ {
+ PreventDefaultAction();
+ int32 bp = aurEffect->GetTickNumber() * aurEffect->GetAmount();
+ GetUnitOwner()->CastCustomSpell(SPELL_SHADOW_INFERNO_DAMAGE, SPELLVALUE_BASE_POINT0, bp, GetUnitOwner(), true);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_nightlord_shadow_inferno_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_illidari_nightlord_shadow_inferno_AuraScript();
+ }
+};
+
void AddSC_black_temple()
{
new npc_wrathbone_flayer();
new npc_angered_soul_fragment();
new spell_soul_fragment_anger();
+ new spell_illidari_nightlord_shadow_inferno();
}
diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.h b/src/server/scripts/Outland/BlackTemple/black_temple.h
index 6a504e48965..9641f3c2d59 100644
--- a/src/server/scripts/Outland/BlackTemple/black_temple.h
+++ b/src/server/scripts/Outland/BlackTemple/black_temple.h
@@ -25,7 +25,7 @@ uint32 const EncounterCount = 9;
enum DataTypes
{
- // Encounter States/Boss GUIDs
+ // Encounter States
DATA_HIGH_WARLORD_NAJENTUS = 0,
DATA_SUPREMUS = 1,
DATA_SHADE_OF_AKAMA = 2,
@@ -86,7 +86,14 @@ enum CreatureIds
NPC_SUPREMUS_VOLCANO = 23085,
NPC_BLACK_TEMPLE_TRIGGER = 22984,
NPC_RELIQUARY_WORLD_TRIGGER = 23472,
- NPC_ENSLAVED_SOUL = 23469
+ NPC_ENSLAVED_SOUL = 23469,
+ NPC_ASHTONGUE_STALKER = 23374,
+ NPC_ASHTONGUE_BATTLELORD = 22844,
+ NPC_ASHTONGUE_MYSTIC = 22845,
+ NPC_ASHTONGUE_PRIMALIST = 22847,
+ NPC_ASHTONGUE_STORMCALLER = 22846,
+ NPC_ASHTONGUE_FERAL_SPIRIT = 22849,
+ NPC_STORM_FURY = 22848
};
enum GameObjectIds
@@ -107,6 +114,12 @@ enum GameObjectIds
GO_ILLIDAN_DOOR_L = 186262
};
+enum BlackTempleFactions
+{
+ ASHTONGUE_FACTION_FRIEND = 1820,
+ AKAMA_FACTION_COMBAT = 1868
+};
+
template<class AI>
AI* GetBlackTempleAI(Creature* creature)
{
diff --git a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp
index 37090656d74..4d518cfb9ff 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp
@@ -122,7 +122,7 @@ Position const DespawnPoint = { 497.4939f, 183.2081f, 94.53341f };
class EnslavedSoulEvent : public BasicEvent
{
- public: explicit EnslavedSoulEvent(Creature* owner) : _owner(owner) { }
+ public: EnslavedSoulEvent(Creature* owner) : _owner(owner) { }
bool Execute(uint64 /*time*/, uint32 /*diff*/) override
{
diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
index 720aa676bfc..48a896d4a43 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
@@ -82,12 +82,6 @@ enum Creatures
NPC_CREATURE_SPAWNER_AKAMA = 23210
};
-enum Factions
-{
- FACTION_FRIENDLY = 1820,
- FACTION_COMBAT = 1868
-};
-
enum Actions
{
ACTION_START_SPAWNING = 0,
@@ -385,7 +379,7 @@ public:
void Reset() override
{
Initialize();
- me->setFaction(FACTION_FRIENDLY);
+ me->setFaction(ASHTONGUE_FACTION_FRIEND);
DoCastSelf(SPELL_STEALTH);
if (_instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE)
@@ -431,7 +425,7 @@ public:
{
_isInCombat = false;
me->CombatStop(true);
- me->setFaction(FACTION_FRIENDLY);
+ me->setFaction(ASHTONGUE_FACTION_FRIEND);
me->SetWalk(true);
_events.Reset();
me->GetMotionMaster()->MovePoint(AKAMA_INTRO_WAYPOINT, AkamaWP[1]);
@@ -485,7 +479,7 @@ public:
case EVENT_SHADE_CHANNEL:
me->SetFacingTo(FACE_THE_PLATFORM);
DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL);
- me->setFaction(FACTION_COMBAT);
+ me->setFaction(AKAMA_FACTION_COMBAT);
_events.ScheduleEvent(EVENT_FIXATE, Seconds(5));
break;
case EVENT_FIXATE:
@@ -533,7 +527,7 @@ public:
}
}
- if (me->getFaction() == FACTION_COMBAT)
+ if (me->getFaction() == AKAMA_FACTION_COMBAT)
{
if (!UpdateVictim())
return;
@@ -1171,7 +1165,7 @@ public:
Talk(SAY_BROKEN_SPECIAL);
break;
case ACTION_BROKEN_HAIL:
- me->setFaction(FACTION_FRIENDLY);
+ me->setFaction(ASHTONGUE_FACTION_FRIEND);
Talk(SAY_BROKEN_HAIL);
break;
case ACTION_BROKEN_EMOTE:
diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp
index a1ccf156958..cfa3dc34ccf 100644
--- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp
+++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp
@@ -105,6 +105,28 @@ class instance_black_temple : public InstanceMapScript
HandleGameObject(ObjectGuid::Empty, true, go);
}
+ void OnCreatureCreate(Creature* creature) override
+ {
+ InstanceScript::OnCreatureCreate(creature);
+
+ switch (creature->GetEntry())
+ {
+ case NPC_ASHTONGUE_STALKER:
+ case NPC_ASHTONGUE_BATTLELORD:
+ case NPC_ASHTONGUE_MYSTIC:
+ case NPC_ASHTONGUE_PRIMALIST:
+ case NPC_ASHTONGUE_STORMCALLER:
+ case NPC_ASHTONGUE_FERAL_SPIRIT:
+ case NPC_STORM_FURY:
+ AshtongueGUIDs.emplace_back(creature->GetGUID());
+ if (GetBossState(DATA_SHADE_OF_AKAMA) == DONE)
+ creature->setFaction(ASHTONGUE_FACTION_FRIEND);
+ break;
+ default:
+ break;
+ }
+ }
+
bool SetBossState(uint32 type, EncounterState state) override
{
if (!InstanceScript::SetBossState(type, state))
@@ -118,6 +140,11 @@ class instance_black_temple : public InstanceMapScript
trigger->AI()->Talk(EMOTE_HIGH_WARLORD_NAJENTUS_DIED);
break;
case DATA_SHADE_OF_AKAMA:
+ if (state == DONE)
+ for (ObjectGuid ashtongueGuid : AshtongueGUIDs)
+ if (Creature* ashtongue = instance->GetCreature(ashtongueGuid))
+ ashtongue->setFaction(ASHTONGUE_FACTION_FRIEND);
+ // no break
case DATA_TERON_GOREFIEND:
case DATA_GURTOGG_BLOODBOIL:
case DATA_RELIQUARY_OF_SOULS:
@@ -144,6 +171,8 @@ class instance_black_temple : public InstanceMapScript
return false;
return true;
}
+ protected:
+ GuidVector AshtongueGUIDs;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 6c534b8f1e5..cc62e69132a 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -1944,14 +1944,10 @@ class spell_gen_mount : public SpellScriptLoader
target->RemoveAurasByType(SPELL_AURA_MOUNTED, ObjectGuid::Empty, GetHitAura());
// Triggered spell id dependent on riding skill and zone
- bool canFly = false;
- uint32 map = GetVirtualMapForMapAndZone(target->GetMapId(), target->GetZoneId());
- if (map == 530 || (map == 571 && target->HasSpell(SPELL_COLD_WEATHER_FLYING)))
- canFly = true;
-
- AreaTableEntry const* area = sAreaTableStore.LookupEntry(target->GetAreaId());
- if (!area || (canFly && (area->flags & AREA_FLAG_NO_FLY_ZONE)))
- canFly = false;
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_mount150);
+ uint32 zoneid, areaid;
+ target->GetZoneAndAreaId(zoneid, areaid);
+ bool const canFly = spellInfo && (spellInfo->CheckLocation(target->GetMapId(), zoneid, areaid, target) == SPELL_CAST_OK);
uint32 mount = 0;
switch (target->GetBaseSkillValue(SKILL_RIDING))
@@ -1987,16 +1983,13 @@ class spell_gen_mount : public SpellScriptLoader
}
if (mount)
- {
- PreventHitAura();
target->CastSpell(target, mount, true);
- }
}
}
void Register() override
{
- OnEffectHitTarget += SpellEffectFn(spell_gen_mount_SpellScript::HandleMount, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT);
+ OnEffectHitTarget += SpellEffectFn(spell_gen_mount_SpellScript::HandleMount, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT);
}
private:
diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp
index 5c496024599..88eb2663520 100644
--- a/src/server/scripts/Spells/spell_mage.cpp
+++ b/src/server/scripts/Spells/spell_mage.cpp
@@ -68,7 +68,9 @@ enum MageSpells
enum MageSpellIcons
{
- SPELL_ICON_MAGE_SHATTERED_BARRIER = 2945
+ SPELL_ICON_MAGE_SHATTERED_BARRIER = 2945,
+ SPELL_ICON_MAGE_PRESENCE_OF_MIND = 139,
+ SPELL_ICON_MAGE_CLEARCASTING = 212
};
// Incanter's Absorbtion
@@ -114,6 +116,16 @@ class spell_mage_arcane_potency : public SpellScriptLoader
return true;
}
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ // due to family mask sharing with brain freeze/missile barrage proc, we need to filter out by icon id
+ SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
+ if (!spellInfo || (spellInfo->SpellIconID != SPELL_ICON_MAGE_CLEARCASTING && spellInfo->SpellIconID != SPELL_ICON_MAGE_PRESENCE_OF_MIND))
+ return false;
+
+ return true;
+ }
+
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
{
static uint32 const triggerSpell[2] = { SPELL_MAGE_ARCANE_POTENCY_RANK_1, SPELL_MAGE_ARCANE_POTENCY_RANK_2 };
@@ -126,6 +138,7 @@ class spell_mage_arcane_potency : public SpellScriptLoader
void Register() override
{
+ DoCheckProc += AuraCheckProcFn(spell_mage_arcane_potency_AuraScript::CheckProc);
OnEffectProc += AuraEffectProcFn(spell_mage_arcane_potency_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
}
};
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index f3d30af1f84..78c84048aec 100644
--- a/src/server/scripts/Spells/spell_rogue.cpp
+++ b/src/server/scripts/Spells/spell_rogue.cpp
@@ -1102,8 +1102,7 @@ class spell_rog_turn_the_tables : public SpellScriptLoader
if (!caster)
return;
- Unit* target = GetTarget();
- target->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true, nullptr, aurEff, caster->GetGUID());
+ caster->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true, nullptr, aurEff);
}
void Register() override
@@ -1118,39 +1117,6 @@ class spell_rog_turn_the_tables : public SpellScriptLoader
}
};
-// 52910,52914,52915 - Turn the Tables proc
-class spell_rog_turn_the_tables_proc : public SpellScriptLoader
-{
- public:
- spell_rog_turn_the_tables_proc() : SpellScriptLoader("spell_rog_turn_the_tables_proc") { }
-
- class spell_rog_turn_the_tables_proc_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_rog_turn_the_tables_proc_SpellScript);
-
- void FilterTargets(std::list<WorldObject*>& targets)
- {
- targets.clear();
-
- Unit* target = GetOriginalCaster();
- if (!target)
- return;
-
- targets.push_back(target);
- }
-
- void Register() override
- {
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_rog_turn_the_tables_proc_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID);
- }
- };
-
- SpellScript* GetSpellScript() const override
- {
- return new spell_rog_turn_the_tables_proc_SpellScript();
- }
-};
-
void AddSC_rogue_spell_scripts()
{
new spell_rog_blade_flurry();
@@ -1173,5 +1139,4 @@ void AddSC_rogue_spell_scripts()
new spell_rog_honor_among_thieves();
new spell_rog_honor_among_thieves_proc();
new spell_rog_turn_the_tables();
- new spell_rog_turn_the_tables_proc();
}
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index 0241221a2ac..3ac0d88e3f9 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -396,8 +396,6 @@ void WorldUpdateLoop()
uint32 realCurrTime = 0;
uint32 realPrevTime = getMSTime();
- uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
-
///- While we have not World::m_stopEvent, update the world
while (!World::IsStopped())
{
@@ -409,18 +407,11 @@ void WorldUpdateLoop()
sWorld->Update(diff);
realPrevTime = realCurrTime;
- // diff (D0) include time of previous sleep (d0) + tick time (t0)
- // we want that next d1 + t1 == WORLD_SLEEP_CONST
- // we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement
- // d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0
- if (diff <= WORLD_SLEEP_CONST + prevSleepTime)
- {
- prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff;
+ uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime());
- std::this_thread::sleep_for(std::chrono::milliseconds(prevSleepTime));
- }
- else
- prevSleepTime = 0;
+ // we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time
+ if (executionTimeDiff < WORLD_SLEEP_CONST)
+ std::this_thread::sleep_for(std::chrono::milliseconds(WORLD_SLEEP_CONST - executionTimeDiff));
#ifdef _WIN32
if (m_ServiceStatus == 0)
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 1d173057e6f..6bb8b400230 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -3224,6 +3224,15 @@ AuctionHouseBot.ItemsPerCycle.Normal = 20
AuctionHouseBot.BuyPrice.Seller = 1
#
+# AuctionHouseBot.BidPrice.*
+# Description: These values determine the range that the Bid Price will fall into, as a percentage of the Buy Price
+# Default: 0.6 - (Min)
+# 0.9 - (Max)
+
+AuctionHouseBot.BidPrice.Min = 0.6
+AuctionHouseBot.BidPrice.Max = 0.9
+
+#
# AuctionHouseBot.Alliance.Price.Ratio
# Description: Percentage by which the price of items selled on Alliance Auction House is incremented / decreased
# Default: 100 - (Not modify)