/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "AzeriteItem.h" #include "AzeritePackets.h" #include "ConditionMgr.h" #include "DB2Stores.h" #include "DatabaseEnv.h" #include "GameObject.h" #include "GameTime.h" #include "Player.h" #include "UpdateData.h" #include #include AzeriteItem::AzeriteItem() : Item() { m_objectTypeId = TYPEID_AZERITE_ITEM; m_entityFragments.Add(WowCS::EntityFragment::Tag_AzeriteItem, false); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::DEBUGknowledgeWeek), -1); } bool AzeriteItem::Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext context, Player const* owner) { if (!Item::Create(guidlow, itemId, context, owner)) return false; SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Level), 1); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::KnowledgeLevel), GetCurrentKnowledgeLevel()); UnlockDefaultMilestones(); return true; } void AzeriteItem::SaveToDB(CharacterDatabaseTransaction trans) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE); stmt->setUInt64(0, GetGUID().GetCounter()); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER); stmt->setUInt64(0, GetGUID().GetCounter()); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE); stmt->setUInt64(0, GetGUID().GetCounter()); trans->Append(stmt); switch (GetState()) { case ITEM_NEW: case ITEM_CHANGED: { stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_AZERITE); stmt->setUInt64(0, GetGUID().GetCounter()); stmt->setUInt64(1, m_azeriteItemData->Xp); stmt->setUInt32(2, m_azeriteItemData->Level); stmt->setUInt32(3, m_azeriteItemData->KnowledgeLevel); std::size_t specIndex = 0; for (; specIndex < m_azeriteItemData->SelectedEssences.size(); ++specIndex) { stmt->setUInt32(4 + specIndex * 5, m_azeriteItemData->SelectedEssences[specIndex].SpecializationID); for (std::size_t j = 0; j < MAX_AZERITE_ESSENCE_SLOT; ++j) stmt->setUInt32(5 + specIndex * 5 + j, m_azeriteItemData->SelectedEssences[specIndex].AzeriteEssenceID[j]); } for (; specIndex < 4; ++specIndex) { stmt->setUInt32(4 + specIndex * 5, 0); for (std::size_t j = 0; j < MAX_AZERITE_ESSENCE_SLOT; ++j) stmt->setUInt32(5 + specIndex * 5 + j, 0); } trans->Append(stmt); for (uint32 azeriteItemMilestonePowerId : m_azeriteItemData->UnlockedEssenceMilestones) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_MILESTONE_POWER); stmt->setUInt64(0, GetGUID().GetCounter()); stmt->setUInt32(1, azeriteItemMilestonePowerId); trans->Append(stmt); } for (UF::UnlockedAzeriteEssence const& azeriteEssence : m_azeriteItemData->UnlockedEssences) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE); stmt->setUInt64(0, GetGUID().GetCounter()); stmt->setUInt32(1, azeriteEssence.AzeriteEssenceID); stmt->setUInt32(2, azeriteEssence.Rank); trans->Append(stmt); } break; } case ITEM_REMOVED: default: break; } Item::SaveToDB(trans); } void AzeriteItem::LoadAzeriteItemData(Player const* owner, AzeriteItemData& azeriteItemData) { bool needSave = false; if (!sAzeriteLevelInfoStore.LookupEntry(azeriteItemData.Level)) { azeriteItemData.Xp = 0; azeriteItemData.Level = 1; azeriteItemData.KnowledgeLevel = GetCurrentKnowledgeLevel(); needSave = true; } else if (azeriteItemData.Level > MAX_AZERITE_ITEM_LEVEL) { azeriteItemData.Xp = 0; azeriteItemData.Level = MAX_AZERITE_ITEM_LEVEL; needSave = true; } if (azeriteItemData.KnowledgeLevel != GetCurrentKnowledgeLevel()) { // rescale XP to maintain same progress % uint64 oldMax = CalcTotalXPToNextLevel(azeriteItemData.Level, azeriteItemData.KnowledgeLevel); azeriteItemData.KnowledgeLevel = GetCurrentKnowledgeLevel(); uint64 newMax = CalcTotalXPToNextLevel(azeriteItemData.Level, azeriteItemData.KnowledgeLevel); azeriteItemData.Xp = uint64(azeriteItemData.Xp / double(oldMax) * newMax); needSave = true; } else if (azeriteItemData.KnowledgeLevel > MAX_AZERITE_ITEM_KNOWLEDGE_LEVEL) { azeriteItemData.KnowledgeLevel = MAX_AZERITE_ITEM_KNOWLEDGE_LEVEL; needSave = true; } SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Xp), azeriteItemData.Xp); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Level), azeriteItemData.Level); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::KnowledgeLevel), azeriteItemData.KnowledgeLevel); for (uint32 azeriteItemMilestonePowerId : azeriteItemData.AzeriteItemMilestonePowers) AddUnlockedEssenceMilestone(azeriteItemMilestonePowerId); UnlockDefaultMilestones(); for (AzeriteEssencePowerEntry const* unlockedAzeriteEssence : azeriteItemData.UnlockedAzeriteEssences) SetEssenceRank(unlockedAzeriteEssence->AzeriteEssenceID, unlockedAzeriteEssence->Tier); for (AzeriteItemSelectedEssencesData const& selectedEssenceData : azeriteItemData.SelectedAzeriteEssences) { if (!selectedEssenceData.SpecializationId) continue; auto selectedEssences = AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences)); selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::SpecializationID).SetValue(selectedEssenceData.SpecializationId); for (uint32 i = 0; i < MAX_AZERITE_ESSENCE_SLOT; ++i) { // Check if essence was unlocked if (!GetEssenceRank(selectedEssenceData.AzeriteEssenceId[i])) continue; selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::AzeriteEssenceID, i).SetValue(selectedEssenceData.AzeriteEssenceId[i]); } if (owner && owner->GetPrimarySpecialization() == ChrSpecialization(selectedEssenceData.SpecializationId)) selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::Enabled).SetValue(true); } // add selected essences for current spec if (owner && !GetSelectedAzeriteEssences()) CreateSelectedAzeriteEssences(AsUnderlyingType(owner->GetPrimarySpecialization())); if (needSave) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_AZERITE_ON_LOAD); stmt->setUInt64(0, azeriteItemData.Xp); stmt->setUInt32(1, azeriteItemData.KnowledgeLevel); stmt->setUInt64(2, GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); } } void AzeriteItem::DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE); stmt->setUInt64(0, itemGuid); CharacterDatabase.ExecuteOrAppend(trans, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_MILESTONE_POWER); stmt->setUInt64(0, itemGuid); CharacterDatabase.ExecuteOrAppend(trans, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_AZERITE_UNLOCKED_ESSENCE); stmt->setUInt64(0, itemGuid); CharacterDatabase.ExecuteOrAppend(trans, stmt); } void AzeriteItem::DeleteFromDB(CharacterDatabaseTransaction trans) { AzeriteItem::DeleteFromDB(trans, GetGUID().GetCounter()); Item::DeleteFromDB(trans); } uint32 AzeriteItem::GetCurrentKnowledgeLevel() { // count weeks from 14.01.2020 boost::gregorian::date now = boost::posix_time::from_time_t(GameTime::GetGameTime()).date(); boost::gregorian::week_iterator itr(boost::gregorian::date(2020, boost::date_time::Jan, 14)); uint32 knowledge = 0; while (*itr < now && knowledge < MAX_AZERITE_ITEM_KNOWLEDGE_LEVEL) { ++knowledge; ++itr; } return knowledge; } uint64 AzeriteItem::CalcTotalXPToNextLevel(uint32 level, uint32 knowledgeLevel) { AzeriteLevelInfoEntry const* levelInfo = sAzeriteLevelInfoStore.AssertEntry(level); uint64 totalXp = levelInfo->BaseExperienceToNextLevel * sAzeriteKnowledgeMultiplierStore.AssertEntry(knowledgeLevel)->Multiplier; return std::max(totalXp, levelInfo->MinimumExperienceToNextLevel); } void AzeriteItem::GiveXP(uint64 xp) { Player* owner = GetOwner(); uint32 level = m_azeriteItemData->Level; if (level < MAX_AZERITE_ITEM_LEVEL) { uint64 currentXP = m_azeriteItemData->Xp; uint64 remainingXP = xp; do { uint64 totalXp = CalcTotalXPToNextLevel(level, m_azeriteItemData->KnowledgeLevel); if (currentXP + remainingXP >= totalXp) { // advance to next level ++level; remainingXP -= totalXp - currentXP; currentXP = 0; } else { currentXP += remainingXP; remainingXP = 0; } } while (remainingXP > 0 && level < MAX_AZERITE_ITEM_LEVEL); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Xp), currentXP); owner->UpdateCriteria(CriteriaType::EarnArtifactXPForAzeriteItem, xp); // changing azerite level changes item level, need to update stats if (m_azeriteItemData->Level != level) { if (IsEquipped()) owner->_ApplyItemBonuses(this, GetSlot(), false); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::Level), level); UnlockDefaultMilestones(); owner->UpdateCriteria(CriteriaType::AzeriteLevelReached, level); if (IsEquipped()) owner->_ApplyItemBonuses(this, GetSlot(), true); } SetState(ITEM_CHANGED, owner); } WorldPackets::Azerite::PlayerAzeriteItemGains xpGain; xpGain.ItemGUID = GetGUID(); xpGain.XP = xp; owner->SendDirectMessage(xpGain.Write()); } GameObject const* AzeriteItem::FindHeartForge(Player const* owner) { if (GameObject const* forge = owner->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_ITEM_FORGE, 40.0f)) if (forge->GetGOInfo()->itemForge.ForgeType == 2) return forge; return nullptr; } bool AzeriteItem::CanUseEssences() const { return ConditionMgr::IsPlayerMeetingCondition(GetOwner(), PLAYER_CONDITION_ID_UNLOCKED_AZERITE_ESSENCES); } bool AzeriteItem::HasUnlockedEssenceSlot(uint8 slot) const { AzeriteItemMilestonePowerEntry const* milestone = sDB2Manager.GetAzeriteItemMilestonePower(slot); return m_azeriteItemData->UnlockedEssenceMilestones.FindIndex(milestone->ID) != -1; } uint32 AzeriteItem::GetEssenceRank(uint32 azeriteEssenceId) const { int32 index = m_azeriteItemData->UnlockedEssences.FindIndexIf([azeriteEssenceId](UF::UnlockedAzeriteEssence const& essence) { return essence.AzeriteEssenceID == azeriteEssenceId; }); if (index < 0) return 0; return m_azeriteItemData->UnlockedEssences[index].Rank; } void AzeriteItem::SetEssenceRank(uint32 azeriteEssenceId, uint32 rank) { int32 index = m_azeriteItemData->UnlockedEssences.FindIndexIf([azeriteEssenceId](UF::UnlockedAzeriteEssence const& essence) { return essence.AzeriteEssenceID == azeriteEssenceId; }); if (!rank && index >= 0) { RemoveDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::UnlockedEssences), index); return; } if (!sDB2Manager.GetAzeriteEssencePower(azeriteEssenceId, rank)) return; if (index < 0) { UF::UnlockedAzeriteEssence& unlockedEssence = AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData) .ModifyValue(&UF::AzeriteItemData::UnlockedEssences)); unlockedEssence.AzeriteEssenceID = azeriteEssenceId; unlockedEssence.Rank = rank; } else SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::UnlockedEssences, index) .ModifyValue(&UF::UnlockedAzeriteEssence::Rank), rank); } UF::SelectedAzeriteEssences const* AzeriteItem::GetSelectedAzeriteEssences() const { for (UF::SelectedAzeriteEssences const& essences : m_azeriteItemData->SelectedEssences) if (essences.Enabled) return &essences; return nullptr; } void AzeriteItem::SetSelectedAzeriteEssences(uint32 specializationId) { int32 index = m_azeriteItemData->SelectedEssences.FindIndexIf([](UF::SelectedAzeriteEssences const& essences) { return essences.Enabled == 1; }); if (index >= 0) SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences, index) .ModifyValue(&UF::SelectedAzeriteEssences::Enabled), false); index = m_azeriteItemData->SelectedEssences.FindIndexIf([specializationId](UF::SelectedAzeriteEssences const& essences) { return essences.SpecializationID == specializationId; }); if (index >= 0) SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences, index) .ModifyValue(&UF::SelectedAzeriteEssences::Enabled), true); else CreateSelectedAzeriteEssences(specializationId); } void AzeriteItem::CreateSelectedAzeriteEssences(uint32 specializationId) { auto selectedEssences = AddDynamicUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences)); selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::SpecializationID).SetValue(specializationId); selectedEssences.ModifyValue(&UF::SelectedAzeriteEssences::Enabled).SetValue(true); } void AzeriteItem::SetSelectedAzeriteEssence(uint8 slot, uint32 azeriteEssenceId) { ASSERT(slot < MAX_AZERITE_ESSENCE_SLOT); int32 index = m_azeriteItemData->SelectedEssences.FindIndexIf([](UF::SelectedAzeriteEssences const& essences) { return essences.Enabled == 1; }); ASSERT(index >= 0); SetUpdateFieldValue(m_values.ModifyValue(&AzeriteItem::m_azeriteItemData).ModifyValue(&UF::AzeriteItemData::SelectedEssences, index) .ModifyValue(&UF::SelectedAzeriteEssences::AzeriteEssenceID, slot), azeriteEssenceId); } void AzeriteItem::BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { m_objectData->WriteCreate(*data, flags, this, target); m_itemData->WriteCreate(*data, flags, this, target); m_azeriteItemData->WriteCreate(*data, flags, this, target); } void AzeriteItem::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { *data << uint32(m_values.GetChangedObjectTypeMask()); if (m_values.HasChanged(TYPEID_OBJECT)) m_objectData->WriteUpdate(*data, flags, this, target); if (m_values.HasChanged(TYPEID_ITEM)) m_itemData->WriteUpdate(*data, flags, this, target); if (m_values.HasChanged(TYPEID_AZERITE_ITEM)) m_azeriteItemData->WriteUpdate(*data, flags, this, target); } void AzeriteItem::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { UpdateMask valuesMask; valuesMask.Set(TYPEID_ITEM); valuesMask.Set(TYPEID_AZERITE_ITEM); *data << uint32(valuesMask.GetBlock(0)); UF::ItemData::Mask mask; m_itemData->AppendAllowedFieldsMaskForFlag(mask, flags); m_itemData->WriteUpdate(*data, mask, true, this, target); UF::AzeriteItemData::Mask mask2; m_azeriteItemData->AppendAllowedFieldsMaskForFlag(mask2, flags); m_azeriteItemData->WriteUpdate(*data, mask2, true, this, target); } void AzeriteItem::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::ItemData::Mask const& requestedItemMask, UF::AzeriteItemData::Mask const& requestedAzeriteItemMask, Player const* target) const { UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); UF::ItemData::Mask itemMask = requestedItemMask; m_itemData->FilterDisallowedFieldsMaskForFlag(itemMask, flags); if (itemMask.IsAnySet()) valuesMask.Set(TYPEID_ITEM); UF::AzeriteItemData::Mask azeriteItemMask = requestedAzeriteItemMask; m_azeriteItemData->FilterDisallowedFieldsMaskForFlag(azeriteItemMask, flags); if (azeriteItemMask.IsAnySet()) valuesMask.Set(TYPEID_AZERITE_ITEM); ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) m_objectData->WriteUpdate(buffer, requestedObjectMask, true, this, target); if (valuesMask[TYPEID_ITEM]) m_itemData->WriteUpdate(buffer, itemMask, true, this, target); if (valuesMask[TYPEID_AZERITE_ITEM]) m_azeriteItemData->WriteUpdate(buffer, azeriteItemMask, true, this, target); buffer.put(sizePos, buffer.wpos() - sizePos - 4); data->AddUpdateBlock(); } void AzeriteItem::ValuesUpdateForPlayerWithMaskSender::operator()(Player const* player) const { UpdateData udata(player->GetMapId()); WorldPacket packet; Owner->BuildValuesUpdateForPlayerWithMask(&udata, ObjectMask.GetChangesMask(), ItemMask.GetChangesMask(), AzeriteItemMask.GetChangesMask(), player); udata.BuildPacket(&packet); player->SendDirectMessage(&packet); } void AzeriteItem::ClearUpdateMask(bool remove) { m_values.ClearChangesMask(&AzeriteItem::m_azeriteItemData); Item::ClearUpdateMask(remove); } void AzeriteItem::UnlockDefaultMilestones() { bool hasPreviousMilestone = true; for (AzeriteItemMilestonePowerEntry const* milestone : sDB2Manager.GetAzeriteItemMilestonePowers()) { if (!hasPreviousMilestone) break; if (milestone->RequiredLevel > int32(GetLevel())) break; if (HasUnlockedEssenceMilestone(milestone->ID)) continue; if (milestone->AutoUnlock) { AddUnlockedEssenceMilestone(milestone->ID); hasPreviousMilestone = true; } else hasPreviousMilestone = false; } }