diff options
Diffstat (limited to 'src')
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, ¶m1, ¶m2); @@ -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, ¶m1, ¶m2); 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) |