aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp5
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h3
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp3
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h3
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp14
-rw-r--r--src/server/game/DataStores/DB2Stores.h4
-rw-r--r--src/server/game/DataStores/DB2Structure.h13
-rw-r--r--src/server/game/DataStores/DB2fmt.h1
-rw-r--r--src/server/game/Entities/Player/CollectionMgr.cpp185
-rw-r--r--src/server/game/Entities/Player/CollectionMgr.h36
-rw-r--r--src/server/game/Entities/Player/Player.cpp54
-rw-r--r--src/server/game/Entities/Player/Player.h1
-rw-r--r--src/server/game/Server/Packets/MiscPackets.cpp20
-rw-r--r--src/server/game/Server/Packets/MiscPackets.h13
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/game/Server/WorldSession.cpp6
-rw-r--r--src/server/game/Spells/Spell.h4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp38
18 files changed, 397 insertions, 8 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp
index 104b5e2bbc7..b9127642234 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -188,6 +188,11 @@ void HotfixDatabaseConnection::DoPrepareStatements()
// GuildPerkSpells.db2
PrepareStatement(HOTFIX_SEL_GUILD_PERK_SPELLS, "SELECT ID, GuildLevel, SpellID FROM guild_perk_spells ORDER BY ID DESC", CONNECTION_SYNCH);
+ // Heirloom.db2
+ PrepareStatement(HOTFIX_SEL_HEIRLOOM, "SELECT ID, ItemID, Flags, SourceText, Source, OldItem1, OldItem2, NextDifficultyItemID, UpgradeItemID1, UpgradeItemID2, "
+ "ItemBonusListID1, ItemBonusListID2 FROM heirloom ORDER BY ID DESC", CONNECTION_SYNCH);
+ PREPARE_LOCALE_STMT(HOTFIX_SEL_HEIRLOOM, "SELECT ID, SourceText_lang FROM heirloom_locale WHERE locale = ?", CONNECTION_SYNCH);
+
// Holidays.db2
PrepareStatement(HOTFIX_SEL_HOLIDAYS, "SELECT ID, Duration1, Duration2, Duration3, Duration4, Duration5, Duration6, Duration7, Duration8, "
"Duration9, Duration10, Date1, Date2, Date3, Date4, Date5, Date6, Date7, Date8, Date9, Date10, Date11, Date12, Date13, Date14, Date15, "
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h
index 196d7602da7..6a513633575 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -118,6 +118,9 @@ enum HotfixDatabaseStatements
HOTFIX_SEL_GUILD_PERK_SPELLS,
+ HOTFIX_SEL_HEIRLOOM,
+ HOTFIX_SEL_HEIRLOOM_LOCALE,
+
HOTFIX_SEL_HOLIDAYS,
HOTFIX_SEL_HOLIDAYS_LOCALE,
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index 9526e012fa2..7e199659177 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -153,4 +153,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_BATTLE_PET_SLOTS, "SELECT id, battlePetGuid, locked FROM battle_pet_slots WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_BATTLE_PET_SLOTS, "INSERT INTO battle_pet_slots (id, battlenetAccountId, battlePetGuid, locked) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_BATTLE_PET_SLOTS, "DELETE FROM battle_pet_slots WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(LOGIN_SEL_ACCOUNT_HEIRLOOMS, "SELECT itemId, flags FROM battlenet_account_heirlooms WHERE accountId = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_REP_ACCOUNT_HEIRLOOMS, "REPLACE INTO battlenet_account_heirlooms (accountId, itemId, flags) VALUES (?, ?, ?)", CONNECTION_ASYNC);
}
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index 9ef214a3120..5225561a539 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -141,6 +141,9 @@ enum LoginDatabaseStatements
LOGIN_INS_BATTLE_PET_SLOTS,
LOGIN_DEL_BATTLE_PET_SLOTS,
+ LOGIN_SEL_ACCOUNT_HEIRLOOMS,
+ LOGIN_REP_ACCOUNT_HEIRLOOMS,
+
MAX_LOGINDATABASE_STATEMENTS
};
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 61b5da47b7f..ddb99876152 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -60,6 +60,7 @@ DB2Storage<GarrSiteLevelEntry> sGarrSiteLevelStore("GarrSiteLev
DB2Storage<GarrSiteLevelPlotInstEntry> sGarrSiteLevelPlotInstStore("GarrSiteLevelPlotInst.db2", GarrSiteLevelPlotInstFormat, HOTFIX_SEL_GARR_SITE_LEVEL_PLOT_INST);
DB2Storage<GlyphSlotEntry> sGlyphSlotStore("GlyphSlot.db2", GlyphSlotFormat, HOTFIX_SEL_GLYPH_SLOT);
DB2Storage<GuildPerkSpellsEntry> sGuildPerkSpellsStore("GuildPerkSpells.db2", GuildPerkSpellsFormat, HOTFIX_SEL_GUILD_PERK_SPELLS);
+DB2Storage<HeirloomEntry> sHeirloomStore("Heirloom.db2", HeirloomFormat, HOTFIX_SEL_HEIRLOOM);
DB2Storage<HolidaysEntry> sHolidaysStore("Holidays.db2", HolidaysEntryFormat, HOTFIX_SEL_HOLIDAYS);
DB2Storage<ImportPriceArmorEntry> sImportPriceArmorStore("ImportPriceArmor.db2", ImportPriceArmorFormat, HOTFIX_SEL_IMPORT_PRICE_ARMOR);
DB2Storage<ImportPriceQualityEntry> sImportPriceQualityStore("ImportPriceQuality.db2", ImportPriceQualityFormat, HOTFIX_SEL_IMPORT_PRICE_QUALITY);
@@ -233,6 +234,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sGarrSiteLevelStore);
LOAD_DB2(sGlyphSlotStore);
LOAD_DB2(sGuildPerkSpellsStore);
+ LOAD_DB2(sHeirloomStore);
LOAD_DB2(sHolidaysStore);
LOAD_DB2(sImportPriceArmorStore);
LOAD_DB2(sImportPriceQualityStore);
@@ -489,6 +491,9 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
for (ToyEntry const* toy : sToyStore)
_toys.push_back(toy->ItemID);
+ for (HeirloomEntry const* heirloom : sHeirloomStore)
+ _heirlooms[heirloom->ItemID] = heirloom;
+
// error checks
if (bad_db2_files.size() >= DB2FilesCount)
{
@@ -846,3 +851,12 @@ bool DB2Manager::GetToyItemIdMatch(uint32 toy) const
{
return std::find(_toys.begin(), _toys.end(), toy) != _toys.end();
}
+
+HeirloomEntry const* DB2Manager::GetHeirloomByItemId(uint32 itemId) const
+{
+ auto itr = _heirlooms.find(itemId);
+ if (itr != _heirlooms.end())
+ return itr->second;
+
+ return nullptr;
+}
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index 0769e6e9739..80a14bc2778 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -56,6 +56,7 @@ extern DB2Storage<GarrSiteLevelEntry> sGarrSiteLevelStore;
extern DB2Storage<GarrSiteLevelPlotInstEntry> sGarrSiteLevelPlotInstStore;
extern DB2Storage<GlyphSlotEntry> sGlyphSlotStore;
extern DB2Storage<GuildPerkSpellsEntry> sGuildPerkSpellsStore;
+extern DB2Storage<HeirloomEntry> sHeirloomStore;
extern DB2Storage<HolidaysEntry> sHolidaysStore;
extern DB2Storage<ImportPriceArmorEntry> sImportPriceArmorStore;
extern DB2Storage<ImportPriceQualityEntry> sImportPriceQualityStore;
@@ -158,6 +159,7 @@ public:
typedef std::unordered_map<uint32, std::vector<SpellPowerEntry const*>> SpellPowerContainer;
typedef std::unordered_map<uint32, std::unordered_map<uint32, std::vector<SpellPowerEntry const*>>> SpellPowerDifficultyContainer;
typedef std::vector<uint32> ToyItemIdsContainer;
+ typedef std::unordered_map<uint32, HeirloomEntry const*> HeirloomItemsContainer;
static DB2Manager& Instance()
{
@@ -193,6 +195,7 @@ public:
std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const;
std::vector<SpellPowerEntry const*> GetSpellPowers(uint32 spellId, Difficulty difficulty = DIFFICULTY_NONE, bool* hasDifficultyPowers = nullptr) const;
bool GetToyItemIdMatch(uint32 toy) const;
+ HeirloomEntry const* GetHeirloomByItemId(uint32 itemId) const;
private:
StorageMap _stores;
@@ -218,6 +221,7 @@ private:
SpellPowerContainer _spellPowers;
SpellPowerDifficultyContainer _spellPowerDifficulties;
ToyItemIdsContainer _toys;
+ HeirloomItemsContainer _heirlooms;
};
#define sDB2Manager DB2Manager::Instance()
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 0544ce82cea..5dbe3c0aa04 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -588,6 +588,19 @@ struct GuildPerkSpellsEntry
uint32 SpellID; // 2
};
+struct HeirloomEntry
+{
+ uint32 ID; // 0
+ uint32 ItemID; // 1
+ uint32 Flags; // 2
+ LocalizedString* SourceText; // 3
+ uint32 Source; // 4
+ uint32 OldItem[2]; // 5-6
+ uint32 NextDifficultyItemID; // 7
+ uint32 UpgradeItemID[2]; // 8-9
+ uint32 ItemBonusListID[2]; // 10-11
+};
+
#define MAX_HOLIDAY_DURATIONS 10
#define MAX_HOLIDAY_DATES 16
#define MAX_HOLIDAY_FLAGS 10
diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h
index ab9e2e0ae4e..599ada6df8c 100644
--- a/src/server/game/DataStores/DB2fmt.h
+++ b/src/server/game/DataStores/DB2fmt.h
@@ -53,6 +53,7 @@ char const GarrPlotInstanceFormat[] = "nis";
char const GarrSiteLevelFormat[] = "niiiiffiiii";
char const GarrSiteLevelPlotInstFormat[] = "niiffi";
char const GlyphSlotFormat[] = "nii";
+char const HeirloomFormat[] = "niisiiiiiiii";
char const GuildPerkSpellsFormat[] = "nii";
char const HolidaysEntryFormat[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiisiii";
char const ImportPriceArmorFormat[] = "nffff";
diff --git a/src/server/game/Entities/Player/CollectionMgr.cpp b/src/server/game/Entities/Player/CollectionMgr.cpp
index 34e19fabc1a..9929c6362ef 100644
--- a/src/server/game/Entities/Player/CollectionMgr.cpp
+++ b/src/server/game/Entities/Player/CollectionMgr.cpp
@@ -52,7 +52,7 @@ void CollectionMgr::LoadAccountToys(PreparedQueryResult result)
void CollectionMgr::SaveAccountToys(SQLTransaction& trans)
{
- PreparedStatement* stmt = NULL;
+ PreparedStatement* stmt = nullptr;
for (ToyBoxContainer::const_iterator itr = _toys.begin(); itr != _toys.end(); ++itr)
{
stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_ACCOUNT_TOYS);
@@ -76,3 +76,186 @@ void CollectionMgr::ToySetFavorite(uint32 itemId, bool favorite)
itr->second = favorite;
}
+
+void CollectionMgr::LoadAccountHeirlooms(PreparedQueryResult result)
+{
+ if (!result)
+ return;
+
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 itemId = fields[0].GetUInt32();
+ uint32 flags = fields[1].GetUInt32();
+
+ HeirloomEntry const* heirloom = sDB2Manager.GetHeirloomByItemId(itemId);
+ if (!heirloom)
+ continue;
+
+ uint32 bonusId = 0;
+
+ if (flags & HEIRLOOM_FLAG_BONUS_LEVEL_90)
+ bonusId = heirloom->ItemBonusListID[0];
+ if (flags & HEIRLOOM_FLAG_BONUS_LEVEL_100)
+ bonusId = heirloom->ItemBonusListID[1];
+
+ _heirlooms[itemId] = HeirloomData(flags, bonusId);
+ } while (result->NextRow());
+}
+
+void CollectionMgr::SaveAccountHeirlooms(SQLTransaction& trans)
+{
+ PreparedStatement* stmt = nullptr;
+ for (auto const& heirloom : _heirlooms)
+ {
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_ACCOUNT_HEIRLOOMS);
+ stmt->setUInt32(0, _owner->GetBattlenetAccountId());
+ stmt->setUInt32(1, heirloom.first);
+ stmt->setUInt32(2, heirloom.second.flags);
+ trans->Append(stmt);
+ }
+}
+
+bool CollectionMgr::UpdateAccountHeirlooms(uint32 itemId, uint32 flags)
+{
+ return _heirlooms.insert(HeirloomContainer::value_type(itemId, HeirloomData(flags, 0))).second;
+}
+
+uint32 CollectionMgr::GetHeirloomBonus(uint32 itemId) const
+{
+ HeirloomContainer::const_iterator itr = _heirlooms.find(itemId);
+ if (itr != _heirlooms.end())
+ return itr->second.bonusId;
+
+ return 0;
+}
+
+void CollectionMgr::LoadHeirlooms()
+{
+ for (auto const& item : _heirlooms)
+ {
+ _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOMS, item.first);
+ _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOM_FLAGS, item.second.flags);
+ }
+}
+
+void CollectionMgr::AddHeirloom(uint32 itemId, uint32 flags)
+{
+ if (UpdateAccountHeirlooms(itemId, flags))
+ {
+ _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOMS, itemId);
+ _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOM_FLAGS, flags);
+ }
+}
+
+void CollectionMgr::UpgradeHeirloom(uint32 itemId, uint32 castItem)
+{
+ Player* player = _owner->GetPlayer();
+ if (!player)
+ return;
+
+ HeirloomEntry const* heirloom = sDB2Manager.GetHeirloomByItemId(itemId);
+ if (!heirloom)
+ return;
+
+ HeirloomContainer::iterator itr = _heirlooms.find(itemId);
+ if (itr == _heirlooms.end())
+ return;
+
+ uint32 flags = itr->second.flags;
+ uint32 bonusId = 0;
+
+ if (heirloom->UpgradeItemID[0] == castItem)
+ {
+ flags |= HEIRLOOM_FLAG_BONUS_LEVEL_90;
+ bonusId = heirloom->ItemBonusListID[0];
+ }
+ if (heirloom->UpgradeItemID[1] == castItem)
+ {
+ flags |= HEIRLOOM_FLAG_BONUS_LEVEL_100;
+ bonusId = heirloom->ItemBonusListID[1];
+ }
+
+ for (Item* item : player->GetItemListByEntry(itemId, true))
+ item->AddBonuses(bonusId);
+
+ // Get heirloom offset to update only one part of dynamic field
+ std::vector<uint32> const& fields = player->GetDynamicValues(PLAYER_DYNAMIC_FIELD_HEIRLOOMS);
+ uint8 offset = std::find(fields.begin(), fields.end(), itemId) - fields.begin();
+
+ player->SetDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOM_FLAGS, offset, flags);
+ itr->second.flags = flags;
+ itr->second.bonusId = bonusId;
+}
+
+void CollectionMgr::CheckHeirloomUpgrades(Item* item)
+{
+ Player* player = _owner->GetPlayer();
+ if (!player)
+ return;
+
+ // Check already owned heirloom for upgrade kits
+ if (HeirloomEntry const* heirloom = sDB2Manager.GetHeirloomByItemId(item->GetEntry()))
+ {
+ HeirloomContainer::iterator itr = _heirlooms.find(item->GetEntry());
+ if (itr == _heirlooms.end())
+ return;
+
+ // Check for heirloom pairs (normal - heroic, heroic - mythic)
+ uint32 heirloomItemId = heirloom->NextDifficultyItemID;
+ uint32 newItemId = 0;
+ while (HeirloomEntry const* heirloomDiff = sDB2Manager.GetHeirloomByItemId(heirloomItemId))
+ {
+ if (player->GetItemByEntry(heirloomDiff->ItemID))
+ newItemId = heirloomDiff->ItemID;
+
+ if (HeirloomEntry const* heirloomSub = sDB2Manager.GetHeirloomByItemId(heirloomDiff->NextDifficultyItemID))
+ {
+ heirloomItemId = heirloomSub->ItemID;
+ continue;
+ }
+
+ break;
+ }
+
+ if (newItemId)
+ {
+ std::vector<uint32> const& fields = player->GetDynamicValues(PLAYER_DYNAMIC_FIELD_HEIRLOOMS);
+ uint8 offset = std::find(fields.begin(), fields.end(), itr->first) - fields.begin();
+
+ player->SetDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOMS, offset, newItemId);
+ player->SetDynamicValue(PLAYER_DYNAMIC_FIELD_HEIRLOOM_FLAGS, offset, 0);
+
+ _heirlooms.erase(itr);
+ _heirlooms[newItemId] = 0;
+
+ return;
+ }
+
+ std::vector<uint32> const& fields = item->GetDynamicValues(ITEM_DYNAMIC_FIELD_BONUSLIST_IDS);
+
+ for (uint32 bonusId : fields)
+ if (bonusId != itr->second.bonusId)
+ item->ClearDynamicValue(ITEM_DYNAMIC_FIELD_BONUSLIST_IDS);
+
+ if (std::find(fields.begin(), fields.end(), itr->second.bonusId) == fields.end())
+ item->AddBonuses(itr->second.bonusId);
+ }
+}
+
+bool CollectionMgr::CanApplyHeirloomXpBonus(uint32 itemId, uint32 level)
+{
+ if (!sDB2Manager.GetHeirloomByItemId(itemId))
+ return false;
+
+ HeirloomContainer::iterator itr = _heirlooms.find(itemId);
+ if (itr == _heirlooms.end())
+ return false;
+
+ if (itr->second.flags & HEIRLOOM_FLAG_BONUS_LEVEL_100)
+ return level <= 100;
+ if (itr->second.flags & HEIRLOOM_FLAG_BONUS_LEVEL_90)
+ return level <= 90;
+
+ return level <= 60;
+}
diff --git a/src/server/game/Entities/Player/CollectionMgr.h b/src/server/game/Entities/Player/CollectionMgr.h
index 1920447be9c..e023f093dd7 100644
--- a/src/server/game/Entities/Player/CollectionMgr.h
+++ b/src/server/game/Entities/Player/CollectionMgr.h
@@ -20,7 +20,30 @@
#include "WorldSession.h"
+enum HeirloomPlayerFlags
+{
+ HEIRLOOM_FLAG_NONE = 0x00,
+ HEIRLOOM_FLAG_BONUS_LEVEL_90 = 0x01,
+ HEIRLOOM_FLAG_BONUS_LEVEL_100 = 0x02
+};
+
+enum HeirloomItemFlags
+{
+ HEIRLOOM_ITEM_FLAG_NONE = 0x00,
+ HEIRLOOM_ITEM_FLAG_SHOW_ONLY_IF_KNOWN = 0x01,
+ HEIRLOOM_ITEM_FLAG_PVP = 0x02
+};
+
+struct HeirloomData
+{
+ HeirloomData(uint32 _flags = 0, uint32 _bonusId = 0) : flags(_flags), bonusId(_bonusId) { }
+
+ uint32 flags;
+ uint32 bonusId;
+};
+
typedef std::map<uint32, bool> ToyBoxContainer;
+typedef std::map<uint32, HeirloomData> HeirloomContainer;
class CollectionMgr
{
@@ -41,12 +64,25 @@ public:
ToyBoxContainer const& GetAccountToys() const { return _toys; }
// Account-wide heirlooms
+ void LoadHeirlooms();
+ void LoadAccountHeirlooms(PreparedQueryResult result);
+ void SaveAccountHeirlooms(SQLTransaction& trans);
+ void AddHeirloom(uint32 itemId, uint32 flags);
+ void UpgradeHeirloom(uint32 itemId, uint32 castItem);
+ void CheckHeirloomUpgrades(Item* item);
+
+ bool UpdateAccountHeirlooms(uint32 itemId, uint32 flags);
+ bool CanApplyHeirloomXpBonus(uint32 itemId, uint32 level);
+ uint32 GetHeirloomBonus(uint32 itemId) const;
+ HeirloomContainer const& GetAccountHeirlooms() const { return _heirlooms; }
+
// Account-wide mounts
private:
WorldSession* _owner;
ToyBoxContainer _toys;
+ HeirloomContainer _heirlooms;
};
#endif // CollectionMgr_h__
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index b079ce95eab..9d9ff325dc1 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -7905,6 +7905,11 @@ void Player::ApplyItemEquipSpell(Item* item, bool apply, bool formChange /*= fal
if (!spellproto)
continue;
+ if (spellproto->HasAura(GetMap()->GetDifficultyID(), SPELL_AURA_MOD_XP_PCT)
+ && !GetSession()->GetCollectionMgr()->CanApplyHeirloomXpBonus(item->GetEntry(), getLevel())
+ && sDB2Manager.GetHeirloomByItemId(item->GetEntry()))
+ continue;
+
ApplyEquipSpell(spellproto, item, apply, formChange);
}
}
@@ -8301,6 +8306,7 @@ void Player::_ApplyAllLevelScaleItemMods(bool apply)
continue;
_ApplyItemBonuses(m_items[i], i, apply);
+ ApplyItemEquipSpell(m_items[i], apply);
}
}
}
@@ -10869,7 +10875,7 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool
ScalingStatDistributionEntry const* ssd = pProto->GetScalingStatDistribution() ? sScalingStatDistributionStore.LookupEntry(pProto->GetScalingStatDistribution()) : 0;
// check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items)
- if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel())
+ if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel() && !sDB2Manager.GetHeirloomByItemId(pProto->GetId()))
return EQUIP_ERR_NOT_EQUIPPABLE;
uint8 eslot = FindEquipSlot(pProto, slot, swap);
@@ -11398,6 +11404,10 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat
ItemAddedQuestCheck(itemId, count);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, itemId, count);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, itemId, 1);
+
+ if (sDB2Manager.GetHeirloomByItemId(itemId))
+ GetSession()->GetCollectionMgr()->AddHeirloom(itemId, 0);
+
if (randomPropertyId)
item->SetItemRandomProperties(randomPropertyId);
@@ -12206,6 +12216,38 @@ Item* Player::GetItemByEntry(uint32 entry) const
return NULL;
}
+std::vector<Item*> Player::GetItemListByEntry(uint32 entry, bool inBankAlso) const
+{
+ std::vector<Item*> itemList = std::vector<Item*>();
+
+ for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
+ if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ if (item->GetEntry() == entry)
+ itemList.push_back(item);
+
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ if (Bag* bag = GetBagByPos(i))
+ for (uint32 j = 0; j < bag->GetBagSize(); ++j)
+ if (Item* item = bag->GetItemByPos(j))
+ if (item->GetEntry() == entry)
+ itemList.push_back(item);
+
+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ if (item->GetEntry() == entry)
+ itemList.push_back(item);
+
+ if (inBankAlso)
+ {
+ for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
+ if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ if (item->GetEntry() == entry)
+ itemList.push_back(item);
+ }
+
+ return itemList;
+}
+
void Player::DestroyItemCount(Item* pItem, uint32 &count, bool update)
{
if (!pItem)
@@ -17045,6 +17087,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
GetSession()->GetCollectionMgr()->LoadToys();
+ GetSession()->GetCollectionMgr()->LoadHeirlooms();
LearnSpecializationSpells();
@@ -17496,6 +17539,8 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
ObjectGuid bagGuid = fields[21].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[21].GetUInt64()) : ObjectGuid::Empty;
uint8 slot = fields[22].GetUInt8();
+ GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item);
+
uint8 err = EQUIP_ERR_OK;
// Item is not in bag
if (!bagGuid)
@@ -18992,6 +19037,7 @@ void Player::SaveToDB(bool create /*=false*/)
trans = LoginDatabase.BeginTransaction();
GetSession()->GetCollectionMgr()->SaveAccountToys(trans);
GetSession()->GetBattlePetMgr()->SaveToDB(trans);
+ GetSession()->GetCollectionMgr()->SaveAccountHeirlooms(trans);
LoginDatabase.CommitTransaction(trans);
// save pet (hunter pet level and experience and all type pets health/mana).
@@ -22447,6 +22493,12 @@ void Player::SendInitialPacketsBeforeAddToMap()
toysUpdate.Toys = &GetSession()->GetCollectionMgr()->GetAccountToys();
SendDirectMessage(toysUpdate.Write());
+ // SMSG_ACCOUNT_HEIRLOOM_UPDATE
+ WorldPackets::Misc::AccountHeirloomUpdate heirloomUpdate;
+ heirloomUpdate.IsFullUpdate = true;
+ heirloomUpdate.Heirlooms = &GetSession()->GetCollectionMgr()->GetAccountHeirlooms();
+ SendDirectMessage(heirloomUpdate.Write());
+
WorldPackets::Character::InitialSetup initialSetup;
initialSetup.ServerExpansionLevel = sWorld->getIntConfig(CONFIG_EXPANSION);
SendDirectMessage(initialSetup.Write());
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index ab894574f9f..60447757035 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1404,6 +1404,7 @@ class Player : public Unit, public GridObject<Player>
uint32 GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem = NULL) const;
Item* GetItemByGuid(ObjectGuid guid) const;
Item* GetItemByEntry(uint32 entry) const;
+ std::vector<Item*> GetItemListByEntry(uint32 entry, bool inBankAlso = false) const;
Item* GetItemByPos(uint16 pos) const;
Item* GetItemByPos(uint8 bag, uint8 slot) const;
Item* GetUseableItemByPos(uint8 bag, uint8 slot) const;
diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp
index 8617e56a841..142530464f3 100644
--- a/src/server/game/Server/Packets/MiscPackets.cpp
+++ b/src/server/game/Server/Packets/MiscPackets.cpp
@@ -569,3 +569,23 @@ void WorldPackets::Misc::WorldTeleport::Read()
_worldPacket >> Pos;
_worldPacket >> Facing;
}
+
+WorldPacket const* WorldPackets::Misc::AccountHeirloomUpdate::Write()
+{
+ _worldPacket.WriteBit(IsFullUpdate);
+ _worldPacket.FlushBits();
+
+ _worldPacket << int32(Unk);
+
+ // both lists have to have the same size
+ _worldPacket << int32(Heirlooms->size());
+ _worldPacket << int32(Heirlooms->size());
+
+ for (auto const& item : *Heirlooms)
+ _worldPacket << uint32(item.first);
+
+ for (auto const& flags : *Heirlooms)
+ _worldPacket << uint32(flags.second.flags);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h
index 423a6ccbc8d..b3c8955dba9 100644
--- a/src/server/game/Server/Packets/MiscPackets.h
+++ b/src/server/game/Server/Packets/MiscPackets.h
@@ -26,6 +26,7 @@
#include "Unit.h"
#include "Player.h"
#include "Weather.h"
+#include "CollectionMgr.h"
namespace WorldPackets
{
@@ -747,6 +748,18 @@ namespace WorldPackets
G3D::Vector3 Pos;
float Facing = 0.0f;
};
+
+ class AccountHeirloomUpdate final : public ServerPacket
+ {
+ public:
+ AccountHeirloomUpdate() : ServerPacket(SMSG_ACCOUNT_HEIRLOOM_UPDATE) { }
+
+ WorldPacket const* Write() override;
+
+ bool IsFullUpdate = false;
+ HeirloomContainer const* Heirlooms = nullptr;
+ int32 Unk = 0;
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 4637093c8a9..c59a4bb3175 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -841,7 +841,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ABORT_NEW_WORLD, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_CRITERIA_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_DATA_TIMES, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_HEIRLOOM_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_HEIRLOOM_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_MOUNT_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TOYS_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_DELETED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 1874403eb19..2189fbffeff 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -1160,6 +1160,7 @@ public:
GLOBAL_ACCOUNT_TOYS = 0,
BATTLE_PETS,
BATTLE_PET_SLOTS,
+ GLOBAL_ACCOUNT_HEIRLOOMS,
MAX_QUERIES
};
@@ -1182,6 +1183,10 @@ public:
stmt->setUInt32(0, battlenetAccountId);
ok = SetPreparedQuery(BATTLE_PET_SLOTS, stmt) && ok;
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_HEIRLOOMS);
+ stmt->setUInt32(0, battlenetAccountId);
+ ok = SetPreparedQuery(GLOBAL_ACCOUNT_HEIRLOOMS, stmt) && ok;
+
return ok;
}
};
@@ -1214,6 +1219,7 @@ void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQue
LoadAccountData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::GLOBAL_ACCOUNT_DATA), GLOBAL_CACHE_MASK);
LoadTutorialsData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::TUTORIALS));
_collectionMgr->LoadAccountToys(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_TOYS));
+ _collectionMgr->LoadAccountHeirlooms(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_HEIRLOOMS));
if (!m_inQueue)
SendAuthResponse(AUTH_OK, false);
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index d5ed0a3c0a8..0728ee46e8c 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -437,6 +437,8 @@ class Spell
void EffectHealBattlePetPct(SpellEffIndex effIndex);
void EffectEnableBattlePets(SpellEffIndex effIndex);
void EffectUncageBattlePet(SpellEffIndex effIndex);
+ void EffectCreateHeirloomItem(SpellEffIndex effIndex);
+ void EffectUpgradeHeirloom(SpellEffIndex effIndex);
typedef std::set<Aura*> UsedSpellMods;
@@ -507,7 +509,7 @@ class Spell
uint32 getState() const { return m_spellState; }
void setState(uint32 state) { m_spellState = state; }
- void DoCreateItem(uint32 i, uint32 itemtype);
+ void DoCreateItem(uint32 i, uint32 itemtype, std::vector<int32> const& bonusListIDs = std::vector<int32>());
bool CheckEffectTarget(Unit const* target, SpellEffectInfo const* effect, Position const* losPosition) const;
bool CheckEffectTarget(GameObject const* target, SpellEffectInfo const* effect) const;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 2bea3a78137..77f3d2b905e 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -287,7 +287,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //219 SPELL_EFFECT_219
&Spell::EffectAddGarrisonFollower, //220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER
&Spell::EffectNULL, //221 SPELL_EFFECT_221
- &Spell::EffectNULL, //222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM
+ &Spell::EffectCreateHeirloomItem, //222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM
&Spell::EffectNULL, //223 SPELL_EFFECT_CHANGE_ITEM_BONUSES
&Spell::EffectActivateGarrisonBuilding, //224 SPELL_EFFECT_ACTIVATE_GARRISON_BUILDING
&Spell::EffectNULL, //225 SPELL_EFFECT_GRANT_BATTLEPET_LEVEL
@@ -310,7 +310,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //242 SPELL_EFFECT_242
&Spell::EffectNULL, //243 SPELL_EFFECT_APPLY_ENCHANT_ILLUSION
&Spell::EffectNULL, //244 SPELL_EFFECT_LEARN_FOLLOWER_ABILITY
- &Spell::EffectNULL, //245 SPELL_EFFECT_UPGRADE_HEIRLOOM
+ &Spell::EffectUpgradeHeirloom, //245 SPELL_EFFECT_UPGRADE_HEIRLOOM
&Spell::EffectNULL, //246 SPELL_EFFECT_FINISH_GARRISON_MISSION
&Spell::EffectNULL, //247 SPELL_EFFECT_ADD_GARRISON_MISSION
&Spell::EffectNULL, //248 SPELL_EFFECT_FINISH_SHIPMENT
@@ -1414,7 +1414,7 @@ void Spell::EffectHealthLeech(SpellEffIndex /*effIndex*/)
}
}
-void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype)
+void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype, std::vector<int32> const& bonusListIDs)
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
@@ -1493,7 +1493,7 @@ void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype)
if (num_to_add)
{
// create the new item and store it
- Item* pItem = player->StoreNewItem(dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid));
+ Item* pItem = player->StoreNewItem(dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid), GuidSet(), bonusListIDs);
// was it successful? return error if not
if (!pItem)
@@ -5867,6 +5867,26 @@ void Spell::EffectAddGarrisonFollower(SpellEffIndex effIndex)
garrison->AddFollower(GetEffect(effIndex)->MiscValue);
}
+void Spell::EffectCreateHeirloomItem(SpellEffIndex effIndex)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
+ return;
+
+ Player* player = m_caster->ToPlayer();
+ if (!player)
+ return;
+
+ CollectionMgr* collectionMgr = player->GetSession()->GetCollectionMgr();
+ if (!collectionMgr)
+ return;
+
+ std::vector<int32> bonusList;
+ bonusList.push_back(collectionMgr->GetHeirloomBonus(m_misc.Raw.Data[0]));
+
+ DoCreateItem(effIndex, m_misc.Raw.Data[0], bonusList);
+ ExecuteLogEffectCreateItem(effIndex, m_misc.Raw.Data[0]);
+}
+
void Spell::EffectActivateGarrisonBuilding(SpellEffIndex effIndex)
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
@@ -5960,3 +5980,13 @@ void Spell::EffectUncageBattlePet(SpellEffIndex /*effIndex*/)
plr->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
m_CastItem = nullptr;
}
+
+void Spell::EffectUpgradeHeirloom(SpellEffIndex /*effIndex*/)
+{
+ if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
+ return;
+
+ if (Player* player = m_caster->ToPlayer())
+ if (CollectionMgr* collectionMgr = player->GetSession()->GetCollectionMgr())
+ collectionMgr->UpgradeHeirloom(m_misc.Raw.Data[0], m_castItemEntry);
+}