diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 35bcbbd9d1c47..ee2d369cebf4b 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -3749,7 +3749,8 @@ INSERT INTO `updates` VALUES ('2024_08_26_00_characters.sql','68EEBE1D639D59B24F5121008C2D103CA67FFC9A','ARCHIVED','2024-08-26 00:49:08',0), ('2024_09_03_00_characters.sql','71ECC73A3F324EB64DA19B0CC4DF72A85E022BDC','ARCHIVED','2024-09-03 00:47:42',0), ('2024_09_23_00_characters.sql','D8491BCEE728F40D55D47E3A4BC5A5F083EBD02E','ARCHIVED','2024-09-23 22:48:10',0), -('2024_10_03_00_characters.sql','408249A6992999A36EB94089D184972E8E0767A3','RELEASED','2024-10-03 11:10:18',0); +('2024_10_03_00_characters.sql','408249A6992999A36EB94089D184972E8E0767A3','RELEASED','2024-10-03 11:10:18',0), +('2024_11_04_00_characters.sql','F7980E0CEE728FF866703693690F76F932E7C764','RELEASED','2024-11-04 17:14:03',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/master/2024_11_04_00_characters.sql b/sql/updates/characters/master/2024_11_04_00_characters.sql new file mode 100644 index 0000000000000..b056282ea7718 --- /dev/null +++ b/sql/updates/characters/master/2024_11_04_00_characters.sql @@ -0,0 +1 @@ +DELETE FROM `character_spell` WHERE `disabled`=1; diff --git a/sql/updates/world/master/2024_11_03_00_world.sql b/sql/updates/world/master/2024_11_03_00_world.sql new file mode 100644 index 0000000000000..64334de063606 --- /dev/null +++ b/sql/updates/world/master/2024_11_03_00_world.sql @@ -0,0 +1,3 @@ +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_warl_demonbolt'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(264178, 'spell_warl_demonbolt'); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 09de6c3b0ef81..3aa347b745761 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -627,8 +627,7 @@ void AuctionHouseMgr::LoadAuctions() void AuctionHouseMgr::AddAItem(Item* item) { ASSERT(item); - ASSERT(_itemsByGuid.count(item->GetGUID()) == 0); - _itemsByGuid[item->GetGUID()] = item; + ASSERT_WITH_SIDE_EFFECTS(_itemsByGuid.emplace(item->GetGUID(), item).second); } bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteItem /*= false*/, CharacterDatabaseTransaction* trans /*= nullptr*/) @@ -928,12 +927,10 @@ void AuctionHouseObject::AddAuction(CharacterDatabaseTransaction trans, AuctionP if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = auction.Items[0]->GetItemModifiedAppearance()) { - auto itr = std::find_if(bucket->ItemModifiedAppearanceId.begin(), bucket->ItemModifiedAppearanceId.end(), - [itemModifiedAppearance](std::pair const& appearance) { return appearance.first == itemModifiedAppearance->ID; }); + auto itr = std::ranges::find(bucket->ItemModifiedAppearanceId, itemModifiedAppearance->ID, &std::pair::first); if (itr == bucket->ItemModifiedAppearanceId.end()) - itr = std::find_if(bucket->ItemModifiedAppearanceId.begin(), bucket->ItemModifiedAppearanceId.end(), - [](std::pair const& appearance) { return appearance.first == 0; }); + itr = std::ranges::find(bucket->ItemModifiedAppearanceId, 0u, &std::pair::first); if (itr != bucket->ItemModifiedAppearanceId.end()) { @@ -964,7 +961,7 @@ void AuctionHouseObject::AddAuction(CharacterDatabaseTransaction trans, AuctionP } } - bucket->QualityMask |= static_cast(1 << (quality + 4)); + bucket->QualityMask |= static_cast(AsUnderlyingType(AuctionHouseFilterMask::PoorQuality) << quality); ++bucket->QualityCounts[quality]; if (trans) @@ -1004,16 +1001,17 @@ void AuctionHouseObject::AddAuction(CharacterDatabaseTransaction trans, AuctionP WorldPackets::AuctionHouse::AuctionSortDef priceSort{ AuctionHouseSortOrder::Price, false }; AuctionPosting::Sorter insertSorter(LOCALE_enUS, std::span(&priceSort, 1)); - bucket->Auctions.insert(std::lower_bound(bucket->Auctions.begin(), bucket->Auctions.end(), addedAuction, std::cref(insertSorter)), addedAuction); + bucket->Auctions.insert(std::ranges::lower_bound(bucket->Auctions, addedAuction, std::cref(insertSorter)), addedAuction); sScriptMgr->OnAuctionAdd(this, addedAuction); } -void AuctionHouseObject::RemoveAuction(CharacterDatabaseTransaction trans, AuctionPosting* auction, std::map::iterator* auctionItr /*= nullptr*/) +std::map::node_type AuctionHouseObject::RemoveAuction(CharacterDatabaseTransaction trans, AuctionPosting* auction, + std::map::iterator* auctionItr /*= nullptr*/) { AuctionsBucketData* bucket = auction->Bucket; - bucket->Auctions.erase(std::remove(bucket->Auctions.begin(), bucket->Auctions.end(), auction), bucket->Auctions.end()); + std::erase(bucket->Auctions, auction); if (!bucket->Auctions.empty()) { // update cache fields @@ -1065,10 +1063,13 @@ void AuctionHouseObject::RemoveAuction(CharacterDatabaseTransaction trans, Aucti } if (!--bucket->QualityCounts[quality]) - bucket->QualityMask &= static_cast(~(1 << (quality + 4))); + bucket->QualityMask &= static_cast(AsUnderlyingType(AuctionHouseFilterMask::PoorQuality) << quality); } else + { + auction->Bucket = nullptr; _buckets.erase(bucket->Key); + } CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION); stmt->setUInt32(0, auction->Id); @@ -1084,9 +1085,9 @@ void AuctionHouseObject::RemoveAuction(CharacterDatabaseTransaction trans, Aucti Trinity::Containers::MultimapErasePair(_playerBidderAuctions, bidder, auction->Id); if (auctionItr) - *auctionItr = _itemsByAuctionId.erase(*auctionItr); + return _itemsByAuctionId.extract((*auctionItr)++); else - _itemsByAuctionId.erase(auction->Id); + return _itemsByAuctionId.extract(auction->Id); } void AuctionHouseObject::Update() @@ -1127,29 +1128,24 @@ void AuctionHouseObject::Update() continue; } + std::map::node_type removedAuctionNode = RemoveAuction(trans, auction, &it); + auction = &removedAuctionNode.mapped(); + ///- Either cancel the auction if there was no bidder if (auction->Bidder.IsEmpty()) { - SendAuctionExpired(auction, trans); sScriptMgr->OnAuctionExpire(this, auction); - - RemoveAuction(trans, auction, &it); + SendAuctionExpired(auction, trans); } ///- Or perform the transaction else { - // Copy data before freeing AuctionPosting in auctionHouse->RemoveAuction - // Because auctionHouse->SendAuctionWon can unload items if bidder is offline - // we need to RemoveAuction before sending mails - AuctionPosting copy = *auction; - RemoveAuction(trans, auction, &it); - + sScriptMgr->OnAuctionSuccessful(this, auction); //we should send an "item sold" message if the seller is online //we send the item to the winner //we send the money to the seller - SendAuctionSold(©, nullptr, trans); - SendAuctionWon(©, nullptr, trans); - sScriptMgr->OnAuctionSuccessful(this, auction); + SendAuctionSold(auction, nullptr, trans); + SendAuctionWon(auction, nullptr, trans); } } @@ -1227,7 +1223,7 @@ void AuctionHouseObject::BuildListBuckets(WorldPackets::AuctionHouse::AuctionLis { if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(bucketAppearance.first)) { - if (knownAppearanceIds.find(itemModifiedAppearance->ItemAppearanceID) == knownAppearanceIds.end()) + if (!knownAppearanceIds.contains(itemModifiedAppearance->ItemAppearanceID)) { hasAll = false; break; @@ -1281,6 +1277,13 @@ void AuctionHouseObject::BuildListBuckets(WorldPackets::AuctionHouse::AuctionLis continue; } + if (filters.HasFlag(AuctionHouseFilterMask::CurrentExpansionOnly)) + { + ItemTemplate const* itemTemplate = ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(bucket.first.ItemId)); + if (itemTemplate->GetRequiredExpansion() != sWorld->getIntConfig(CONFIG_EXPANSION)) + continue; + } + // TODO: this one needs to access loot history to know highest item level for every inventory type //if (filters.HasFlag(AuctionHouseFilterMask::UpgradesOnly)) //{ @@ -1313,7 +1316,7 @@ void AuctionHouseObject::BuildListBuckets(WorldPackets::AuctionHouse::AuctionLis } AuctionsBucketData::Sorter sorter(player->GetSession()->GetSessionDbcLocale(), sorts); - std::sort(buckets.begin(), buckets.end(), std::cref(sorter)); + std::ranges::sort(buckets, std::cref(sorter)); for (AuctionsBucketData const* resultBucket : buckets) { @@ -1335,7 +1338,7 @@ void AuctionHouseObject::BuildListBiddedItems(WorldPackets::AuctionHouse::Auctio auctions.push_back(auction); AuctionPosting::Sorter sorter(player->GetSession()->GetSessionDbcLocale(), sorts); - std::sort(auctions.begin(), auctions.end(), std::cref(sorter)); + std::ranges::sort(auctions, std::cref(sorter)); for (AuctionPosting const* resultAuction : auctions) { @@ -1414,7 +1417,7 @@ void AuctionHouseObject::BuildListOwnedItems(WorldPackets::AuctionHouse::Auction auctions.push_back(auction); AuctionPosting::Sorter sorter(player->GetSession()->GetSessionDbcLocale(), sorts); - std::sort(auctions.begin(), auctions.end(), std::cref(sorter)); + std::ranges::sort(auctions, std::cref(sorter)); for (AuctionPosting const* resultAuction : auctions) { @@ -1486,6 +1489,9 @@ CommodityQuote const* AuctionHouseObject::CreateCommodityQuote(Player const* pla uint32 remainingQuantity = quantity; for (AuctionPosting const* auction : bucketItr->second.Auctions) { + if (auction->Owner == player->GetGUID() || auction->OwnerAccount == player->GetSession()->GetAccountGUID()) + continue; + for (Item* auctionItem : auction->Items) { if (auctionItem->GetCount() >= remainingQuantity) @@ -1545,6 +1551,9 @@ bool AuctionHouseObject::BuyCommodity(CharacterDatabaseTransaction trans, Player for (auto auctionItr = bucketItr->second.Auctions.begin(); auctionItr != bucketItr->second.Auctions.end();) { AuctionPosting* auction = *auctionItr++; + if (auction->Owner == player->GetGUID() || auction->OwnerAccount == player->GetSession()->GetAccountGUID()) + continue; + auctions.push_back(auction); for (Item* auctionItem : auction->Items) { @@ -1611,6 +1620,9 @@ bool AuctionHouseObject::BuyCommodity(CharacterDatabaseTransaction trans, Player for (auto auctionItr = bucketItr->second.Auctions.begin(); auctionItr != bucketItr->second.Auctions.end();) { AuctionPosting* auction = *auctionItr++; + if (auction->Owner == player->GetGUID() || auction->OwnerAccount == player->GetSession()->GetAccountGUID()) + continue; + if (!uniqueSeller) uniqueSeller = auction->Owner; else if (*uniqueSeller != auction->Owner) @@ -1823,7 +1835,10 @@ void AuctionHouseObject::SendAuctionWon(AuctionPosting const* auction, Player* b { // bidder doesn't exist, delete the item for (Item* item : auction->Items) - sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); + { + item->FSetState(ITEM_REMOVED); + item->SaveToDB(trans); + } } } @@ -1879,7 +1894,10 @@ void AuctionHouseObject::SendAuctionExpired(AuctionPosting const* auction, Chara { // owner doesn't exist, delete the item for (Item* item : auction->Items) - sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); + { + item->FSetState(ITEM_REMOVED); + item->SaveToDB(trans); + } } } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index f63540600220b..2d84fa3f5e835 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -97,19 +97,20 @@ constexpr std::size_t MAX_FAVORITE_AUCTIONS = 100; enum class AuctionHouseFilterMask : uint32 { - None = 0x0, - UncollectedOnly = 0x1, - UsableOnly = 0x2, - UpgradesOnly = 0x4, - ExactMatch = 0x8, - PoorQuality = 0x10, - CommonQuality = 0x20, - UncommonQuality = 0x40, - RareQuality = 0x80, - EpicQuality = 0x100, - LegendaryQuality = 0x200, - ArtifactQuality = 0x400, - LegendaryCraftedItemOnly = 0x800, + None = 0x0000, + UncollectedOnly = 0x0002, + UsableOnly = 0x0004, + CurrentExpansionOnly = 0x0008, + UpgradesOnly = 0x0010, + ExactMatch = 0x0020, + PoorQuality = 0x0040, + CommonQuality = 0x0080, + UncommonQuality = 0x0100, + RareQuality = 0x0200, + EpicQuality = 0x0400, + LegendaryQuality = 0x0800, + ArtifactQuality = 0x1000, + LegendaryCraftedItemOnly = 0x2000, }; DEFINE_ENUM_FLAG(AuctionHouseFilterMask); @@ -291,7 +292,8 @@ class TC_GAME_API AuctionHouseObject void AddAuction(CharacterDatabaseTransaction trans, AuctionPosting auction); - void RemoveAuction(CharacterDatabaseTransaction trans, AuctionPosting* auction, std::map::iterator* auctionItr = nullptr); + std::map::node_type RemoveAuction(CharacterDatabaseTransaction trans, AuctionPosting* auction, + std::map::iterator* auctionItr = nullptr); void Update(); diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp index 95e98c53a6f25..de91078aa5d8f 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp @@ -415,12 +415,12 @@ void AuctionBotBuyer::BuyEntry(AuctionPosting* auction, AuctionHouseObject* auct // Copy data before freeing AuctionPosting in auctionHouse->RemoveAuction // Because auctionHouse->SendAuctionWon can unload items if bidder is offline // we need to RemoveAuction before sending mails - AuctionPosting copy = *auction; - auctionHouse->RemoveAuction(trans, auction); + std::map::node_type removedAuctionNode = auctionHouse->RemoveAuction(trans, auction); + auction = &removedAuctionNode.mapped(); // Mails must be under transaction control too to prevent data loss - auctionHouse->SendAuctionSold(©, nullptr, trans); - auctionHouse->SendAuctionWon(©, nullptr, trans); + auctionHouse->SendAuctionSold(auction, nullptr, trans); + auctionHouse->SendAuctionWon(auction, nullptr, trans); // Run SQLs CharacterDatabase.CommitTransaction(trans); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index fa45b862b940b..ff05a3c2efe98 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2714,12 +2714,12 @@ void Player::RemoveTalent(TalentEntry const* talent) if (!spellInfo) return; - RemoveSpell(talent->SpellID, true); + RemoveSpell(talent->SpellID); // search for spells that the talent teaches and unlearn them for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0) - RemoveSpell(spellEffectInfo.TriggerSpell, true); + RemoveSpell(spellEffectInfo.TriggerSpell); if (talent->OverridesSpellID) RemoveOverrideSpell(talent->OverridesSpellID, talent->SpellID); @@ -27214,7 +27214,7 @@ void Player::RemovePvpTalent(PvpTalentEntry const* talent, uint8 activeTalentGro if (!spellInfo) return; - RemoveSpell(talent->SpellID, true); + RemoveSpell(talent->SpellID); // Move this to toggle ? if (talent->OverridesSpellID) @@ -27235,7 +27235,7 @@ void Player::TogglePvpTalents(bool enable) { if (enable) { - LearnSpell(pvpTalentInfo->SpellID, false); + LearnSpell(pvpTalentInfo->SpellID, true); if (pvpTalentInfo->OverridesSpellID) AddOverrideSpell(pvpTalentInfo->OverridesSpellID, pvpTalentInfo->SpellID); } @@ -27243,7 +27243,7 @@ void Player::TogglePvpTalents(bool enable) { if (pvpTalentInfo->OverridesSpellID) RemoveOverrideSpell(pvpTalentInfo->OverridesSpellID, pvpTalentInfo->SpellID); - RemoveSpell(pvpTalentInfo->SpellID, true); + RemoveSpell(pvpTalentInfo->SpellID); } } } @@ -28226,12 +28226,12 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) if (!spellInfo) continue; - RemoveSpell(talentInfo->SpellID, true); + RemoveSpell(talentInfo->SpellID); // search for spells that the talent teaches and unlearn them for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0) - RemoveSpell(spellEffectInfo.TriggerSpell, true); + RemoveSpell(spellEffectInfo.TriggerSpell); if (talentInfo->OverridesSpellID) RemoveOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID); @@ -28247,12 +28247,12 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) if (!spellInfo) continue; - RemoveSpell(talentInfo->SpellID, true); + RemoveSpell(talentInfo->SpellID); // search for spells that the talent teaches and unlearn them for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) if (spellEffectInfo.IsEffect(SPELL_EFFECT_LEARN_SPELL) && spellEffectInfo.TriggerSpell > 0) - RemoveSpell(spellEffectInfo.TriggerSpell, true); + RemoveSpell(spellEffectInfo.TriggerSpell); if (talentInfo->OverridesSpellID) RemoveOverrideSpell(talentInfo->OverridesSpellID, talentInfo->SpellID); @@ -30078,7 +30078,7 @@ void Player::RemoveSpecializationSpells() for (size_t j = 0; j < specSpells->size(); ++j) { SpecializationSpellsEntry const* specSpell = (*specSpells)[j]; - RemoveSpell(specSpell->SpellID, true); + RemoveSpell(specSpell->SpellID); if (specSpell->OverridesSpellID) RemoveOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID); } diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index f87e58366b82d..3c9d56ef87e27 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -438,10 +438,10 @@ void WorldSession::HandleAuctionPlaceBid(WorldPackets::AuctionHouse::AuctionPlac if (canBuyout && placeBid.BidAmount == auction->BuyoutOrUnitPrice) { // buyout - auctionHouse->SendAuctionSold(auction, nullptr, trans); - auctionHouse->SendAuctionWon(auction, player, trans); + std::map::node_type removedAuctionNode = auctionHouse->RemoveAuction(trans, auction); - auctionHouse->RemoveAuction(trans, auction); + auctionHouse->SendAuctionSold(&removedAuctionNode.mapped(), nullptr, trans); + auctionHouse->SendAuctionWon(&removedAuctionNode.mapped(), player, trans); } else { diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index fc810d573e693..cffbb7999c486 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -49,6 +49,7 @@ enum WarlockSpells SPELL_WARLOCK_CREATE_HEALTHSTONE = 23517, SPELL_WARLOCK_CURSE_OF_EXHAUSTION = 334275, SPELL_WARLOCK_DEATHS_EMBRACE = 453189, + SPELL_WARLOCK_DEMONBOLT_ENERGIZE = 280127, SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST = 62388, SPELL_WARLOCK_DEMONIC_CIRCLE_SUMMON = 48018, SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT = 48020, @@ -457,6 +458,28 @@ class spell_warl_deaths_embrace_drain_life : public AuraScript } }; +// 264178 - Demonbolt +class spell_warl_demonbolt : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo ({ SPELL_WARLOCK_DEMONBOLT_ENERGIZE }); + } + + void HandleAfterCast() const + { + GetCaster()->CastSpell(GetCaster(), SPELL_WARLOCK_DEMONBOLT_ENERGIZE, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell() + }); + } + + void Register() override + { + AfterCast += SpellCastFn(spell_warl_demonbolt::HandleAfterCast); + } +}; + // 48018 - Demonic Circle: Summon class spell_warl_demonic_circle_summon : public AuraScript { @@ -1482,6 +1505,7 @@ void AddSC_warlock_spell_scripts() RegisterSpellScript(spell_warl_deaths_embrace); RegisterSpellScript(spell_warl_deaths_embrace_dots); RegisterSpellScript(spell_warl_deaths_embrace_drain_life); + RegisterSpellScript(spell_warl_demonbolt); RegisterSpellScript(spell_warl_demonic_circle_summon); RegisterSpellScript(spell_warl_demonic_circle_teleport); RegisterSpellScript(spell_warl_devour_magic);