aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Entities')
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp29
-rw-r--r--src/server/game/Entities/Item/Item.cpp350
-rw-r--r--src/server/game/Entities/Item/Item.h26
-rw-r--r--src/server/game/Entities/Item/ItemEnchantmentMgr.cpp1
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp158
-rw-r--r--src/server/game/Entities/Player/Player.h17
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp6
-rw-r--r--src/server/game/Entities/Unit/Unit.h3
9 files changed, 568 insertions, 23 deletions
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index eb7b3ec9d85..56e4ca80c59 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -25,6 +25,7 @@
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "GroupMgr.h"
+#include "ArtifactPackets.h"
#include "MiscPackets.h"
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
@@ -1816,6 +1817,34 @@ void GameObject::Use(Unit* user)
player->SetStandState(UnitStandStateType(UNIT_STAND_STATE_SIT_LOW_CHAIR + info->barberChair.chairheight), info->barberChair.SitAnimKit);
return;
}
+ case GAMEOBJECT_TYPE_ARTIFACT_FORGE:
+ {
+ GameObjectTemplate const* info = GetGOInfo();
+ if (!info)
+ return;
+
+ if (user->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* player = user->ToPlayer();
+ if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(info->artifactForge.conditionID1))
+ if (!sConditionMgr->IsPlayerMeetingCondition(player, playerCondition))
+ return;
+
+ Aura const* artifactAura = player->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE);
+ Item const* item = artifactAura ? player->GetItemByGuid(artifactAura->GetCastItemGUID()) : nullptr;
+ if (!item)
+ {
+ player->SendDirectMessage(WorldPackets::Misc::DisplayGameError(GameError::ERR_MUST_EQUIP_ARTIFACT).Write());
+ return;
+ }
+
+ WorldPackets::Artifact::ArtifactForgeOpened artifactForgeOpened;
+ artifactForgeOpened.ArtifactGUID = item->GetGUID();
+ artifactForgeOpened.ForgeGUID = GetGUID();
+ player->SendDirectMessage(artifactForgeOpened.Write());
+ return;
+ }
default:
if (GetGoType() >= MAX_GAMEOBJECT_TYPE)
TC_LOG_ERROR("misc", "GameObject::Use(): unit (type: %u, %s, name: %s) tries to use object (%s, name: %s) of unknown type (%u)",
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index b01508c3933..d79bf16ac07 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -33,6 +33,7 @@
#include "TradeData.h"
#include "GameTables.h"
#include "CollectionMgr.h"
+#include "ArtifactPackets.h"
void AddItemsSetItem(Player* player, Item* item)
{
@@ -308,11 +309,40 @@ bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owne
SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
- for (uint8 i = 0; i < itemProto->Effects.size() && i < 5; ++i)
- SetSpellCharges(i, itemProto->Effects[i]->Charges);
+ for (std::size_t i = 0; i < itemProto->Effects.size(); ++i)
+ {
+ if (i < 5)
+ SetSpellCharges(i, itemProto->Effects[i]->Charges);
+ if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemProto->Effects[i]->SpellID))
+ if (spellInfo->HasEffect(SPELL_EFFECT_GIVE_ARTIFACT_POWER))
+ if (uint32 artifactKnowledgeLevel = owner->GetCurrency(CURRENCY_TYPE_ARTIFACT_KNOWLEDGE))
+ SetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL, artifactKnowledgeLevel + 1);
+ }
SetUInt32Value(ITEM_FIELD_DURATION, itemProto->GetDuration());
SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, 0);
+
+ if (itemProto->GetArtifactID())
+ {
+ InitArtifactPowers(itemProto->GetArtifactID());
+ for (ArtifactAppearanceEntry const* artifactAppearance : sArtifactAppearanceStore)
+ {
+ if (ArtifactAppearanceSetEntry const* artifactAppearanceSet = sArtifactAppearanceSetStore.LookupEntry(artifactAppearance->ArtifactAppearanceSetID))
+ {
+ if (itemProto->GetArtifactID() != artifactAppearanceSet->ArtifactID)
+ continue;
+
+ if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(artifactAppearance->PlayerConditionID))
+ if (!sConditionMgr->IsPlayerMeetingCondition(owner, playerCondition))
+ continue;
+
+ SetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID, artifactAppearance->ID);
+ SetAppearanceModId(artifactAppearance->AppearanceModID);
+ break;
+ }
+ }
+ }
+
return true;
}
@@ -481,6 +511,51 @@ void Item::SaveToDB(SQLTransaction& trans)
trans->Append(stmt);
}
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT_POWERS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ if (GetTemplate()->GetArtifactID())
+ {
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_ARTIFACT);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ stmt->setUInt32(1, GetUInt32Value(ITEM_FIELD_ARTIFACT_XP));
+ stmt->setUInt32(2, GetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID));
+ trans->Append(stmt);
+
+ for (ItemDynamicFieldArtifactPowers const& artifactPower : GetArtifactPowers())
+ {
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_ARTIFACT_POWERS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ stmt->setUInt32(1, artifactPower.ArtifactPowerId);
+ stmt->setUInt8(2, artifactPower.PurchasedRank);
+ trans->Append(stmt);
+ }
+ }
+
+ static ItemModifier const modifiersTable[] =
+ {
+ ITEM_MODIFIER_SCALING_STAT_DISTRIBUTION_FIXED_LEVEL,
+ ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL
+ };
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ if (std::find_if(std::begin(modifiersTable), std::end(modifiersTable), [this](ItemModifier modifier) { return GetModifier(modifier) != 0; }) != std::end(modifiersTable))
+ {
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_MODIFIERS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ stmt->setUInt32(1, GetModifier(ITEM_MODIFIER_SCALING_STAT_DISTRIBUTION_FIXED_LEVEL));
+ stmt->setUInt32(2, GetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL));
+ trans->Append(stmt);
+ }
+
break;
}
case ITEM_REMOVED:
@@ -497,6 +572,18 @@ void Item::SaveToDB(SQLTransaction& trans)
stmt->setUInt64(0, GetGUID().GetCounter());
trans->Append(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT_POWERS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
if (HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
@@ -534,8 +621,8 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie
// itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4,
// 24 25 26 27 28
// spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4,
- // 29 30 31 32 33 34 35 36 37
- // gemItemId1, gemBonuses1, gemContext1, gemItemId2, gemBonuses2, gemContext2, gemItemId3, gemBonuses3, gemContext3 FROM item_instance
+ // 29 30 31 32 33 34 35 36 37 38 39
+ // gemItemId1, gemBonuses1, gemContext1, gemItemId2, gemBonuses2, gemContext2, gemItemId3, gemBonuses3, gemContext3, fixedScalingLevel, artifactKnowledgeLevel FROM item_instance
// create item before any checks for store correct guid
// and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB
@@ -665,6 +752,9 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie
SetGem(i, &gemData[i]);
}
+ SetModifier(ITEM_MODIFIER_SCALING_STAT_DISTRIBUTION_FIXED_LEVEL, fields[38].GetUInt32());
+ SetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL, fields[39].GetUInt32());
+
if (need_save) // normal item changed state set not work at loading
{
uint8 index = 0;
@@ -680,6 +770,58 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie
return true;
}
+void Item::LoadArtifactData(uint32 xp, uint32 artifactAppearanceId, std::vector<ItemDynamicFieldArtifactPowers>& powers)
+{
+ InitArtifactPowers(GetTemplate()->GetArtifactID());
+ SetUInt32Value(ITEM_FIELD_ARTIFACT_XP, xp);
+ SetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID, artifactAppearanceId);
+ if (ArtifactAppearanceEntry const* artifactAppearance = sArtifactAppearanceStore.LookupEntry(artifactAppearanceId))
+ SetAppearanceModId(artifactAppearance->AppearanceModID);
+
+ uint8 totalPurchasedRanks = 0;
+ for (ItemDynamicFieldArtifactPowers& power : powers)
+ {
+ power.CurrentRankWithBonus += power.PurchasedRank;
+ totalPurchasedRanks += power.PurchasedRank;
+
+ ArtifactPowerEntry const* artifactPower = sArtifactPowerStore.AssertEntry(power.ArtifactPowerId);
+ for (uint32 e = SOCK_ENCHANTMENT_SLOT; e <= SOCK_ENCHANTMENT_SLOT_3; ++e)
+ {
+ if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(GetEnchantmentId(EnchantmentSlot(e))))
+ {
+ for (uint32 i = 0; i < MAX_ITEM_ENCHANTMENT_EFFECTS; ++i)
+ {
+ switch (enchant->Effect[i])
+ {
+ case ITEM_ENCHANTMENT_TYPE_ARTIFACT_POWER_BONUS_RANK_BY_TYPE:
+ if (artifactPower->RelicType == enchant->EffectSpellID[i])
+ power.CurrentRankWithBonus += enchant->EffectPointsMin[i];
+ break;
+ case ITEM_ENCHANTMENT_TYPE_ARTIFACT_POWER_BONUS_RANK_BY_ID:
+ if (artifactPower->ID == enchant->EffectSpellID[i])
+ power.CurrentRankWithBonus += enchant->EffectPointsMin[i];
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ SetArtifactPower(&power);
+ }
+
+ for (ItemDynamicFieldArtifactPowers& power : powers)
+ {
+ ArtifactPowerEntry const* scaledArtifactPowerEntry = sArtifactPowerStore.AssertEntry(power.ArtifactPowerId);
+ if (!(scaledArtifactPowerEntry->Flags & ARTIFACT_POWER_FLAG_SCALES_WITH_NUM_POWERS))
+ continue;
+
+ power.CurrentRankWithBonus = totalPurchasedRanks;
+ SetArtifactPower(&power);
+ }
+}
+
/*static*/
void Item::DeleteFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid)
{
@@ -1049,6 +1191,9 @@ void Item::SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint
owner->GetSession()->SendEnchantmentLog(GetOwnerGUID(), caster, GetEntry(), id);
}
+ ApplyArtifactPowerEnchantmentBonuses(GetEnchantmentId(slot), false, owner);
+ ApplyArtifactPowerEnchantmentBonuses(id, true, owner);
+
SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot * MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET, id);
SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot * MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET, duration);
SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot * MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET, charges);
@@ -1098,6 +1243,49 @@ ItemDynamicFieldGems const* Item::GetGem(uint16 slot) const
void Item::SetGem(uint16 slot, ItemDynamicFieldGems const* gem)
{
ASSERT(slot < MAX_GEM_SOCKETS);
+ _bonusData.GemItemLevelBonus[slot] = 0;
+ if (ItemTemplate const* gemTemplate = sObjectMgr->GetItemTemplate(gem->ItemId))
+ {
+ if (GemPropertiesEntry const* gemProperties = sGemPropertiesStore.LookupEntry(gemTemplate->GetGemProperties()))
+ {
+ if (SpellItemEnchantmentEntry const* gemEnchant = sSpellItemEnchantmentStore.LookupEntry(gemProperties->EnchantID))
+ {
+ BonusData gemBonus;
+ gemBonus.Initialize(gemTemplate);
+ for (uint16 bonusListId : gem->BonusListIDs)
+ if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListId))
+ for (ItemBonusEntry const* itemBonus : *bonuses)
+ gemBonus.AddBonus(itemBonus->Type, itemBonus->Value);
+
+ for (uint32 i = 0; i < MAX_ITEM_ENCHANTMENT_EFFECTS; ++i)
+ {
+ switch (gemEnchant->Effect[i])
+ {
+ case ITEM_ENCHANTMENT_TYPE_BONUS_LIST_ID:
+ {
+ if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(gemEnchant->EffectSpellID[i]))
+ for (ItemBonusEntry const* itemBonus : *bonuses)
+ if (itemBonus->Type == ITEM_BONUS_ITEM_LEVEL)
+ _bonusData.GemItemLevelBonus[slot] += itemBonus->Value[0];
+ break;
+ }
+ case ITEM_ENCHANTMENT_TYPE_BONUS_LIST_CURVE:
+ {
+ if (uint32 bonusListId = sDB2Manager.GetItemBonusListForItemLevelDelta(int16(sDB2Manager.GetCurveValueAt(CURVE_ID_ARTIFACT_RELIC_ITEM_LEVEL_BONUS, gemTemplate->GetBaseItemLevel() + gemBonus.ItemLevel))))
+ if (DB2Manager::ItemBonusList const* bonuses = sDB2Manager.GetItemBonusList(bonusListId))
+ for (ItemBonusEntry const* itemBonus : *bonuses)
+ if (itemBonus->Type == ITEM_BONUS_ITEM_LEVEL)
+ _bonusData.GemItemLevelBonus[slot] += itemBonus->Value[0];
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
SetDynamicStructuredValue(ITEM_DYNAMIC_FIELD_GEMS, slot, gem);
}
@@ -1978,6 +2166,9 @@ uint32 Item::GetItemLevel(Player const* owner) const
if (ItemUpgradeEntry const* upgrade = sItemUpgradeStore.LookupEntry(GetModifier(ITEM_MODIFIER_UPGRADE_ID)))
itemLevel += upgrade->ItemLevelBonus;
+ for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
+ itemLevel += _bonusData.GemItemLevelBonus[i];
+
return std::min(std::max(itemLevel + _bonusData.ItemLevel, uint32(MIN_ITEM_LEVEL)), uint32(MAX_ITEM_LEVEL));
}
@@ -2081,6 +2272,154 @@ void Item::AddBonuses(uint32 bonusListID)
}
}
+DynamicFieldStructuredView<ItemDynamicFieldArtifactPowers> Item::GetArtifactPowers() const
+{
+ return GetDynamicStructuredValues<ItemDynamicFieldArtifactPowers>(ITEM_DYNAMIC_FIELD_ARTIFACT_POWERS);
+}
+
+ItemDynamicFieldArtifactPowers const* Item::GetArtifactPower(uint32 artifactPowerId) const
+{
+ auto indexItr = m_artifactPowerIdToIndex.find(artifactPowerId);
+ if (indexItr != m_artifactPowerIdToIndex.end())
+ return GetDynamicStructuredValue<ItemDynamicFieldArtifactPowers>(ITEM_DYNAMIC_FIELD_ARTIFACT_POWERS, indexItr->second);
+
+ return nullptr;
+}
+
+void Item::SetArtifactPower(ItemDynamicFieldArtifactPowers const* artifactPower, bool createIfMissing /*= false*/)
+{
+ auto indexItr = m_artifactPowerIdToIndex.find(artifactPower->ArtifactPowerId);
+ uint16 index;
+ if (indexItr != m_artifactPowerIdToIndex.end())
+ index = indexItr->second;
+ else
+ {
+ if (!createIfMissing)
+ return;
+
+ index = uint16(m_artifactPowerIdToIndex.size());
+ m_artifactPowerIdToIndex[artifactPower->ArtifactPowerId] = index;
+ }
+
+ SetDynamicStructuredValue(ITEM_DYNAMIC_FIELD_ARTIFACT_POWERS, index, artifactPower);
+}
+
+void Item::InitArtifactPowers(uint8 artifactId)
+{
+ for (ArtifactPowerEntry const* artifactPower : sDB2Manager.GetArtifactPowers(artifactId))
+ {
+ if (m_artifactPowerIdToIndex.find(artifactPower->ID) != m_artifactPowerIdToIndex.end())
+ continue;
+
+ ItemDynamicFieldArtifactPowers powerData;
+ memset(&powerData, 0, sizeof(powerData));
+ powerData.ArtifactPowerId = artifactPower->ID;
+ powerData.PurchasedRank = 0;
+ powerData.CurrentRankWithBonus = (artifactPower->Flags & ARTIFACT_POWER_FLAG_FIRST) ? 1 : 0;
+ SetArtifactPower(&powerData, true);
+ }
+}
+
+uint32 Item::GetTotalPurchasedArtifactPowers() const
+{
+ uint32 purchasedRanks = 0;
+ for (ItemDynamicFieldArtifactPowers const& power : GetArtifactPowers())
+ purchasedRanks += power.PurchasedRank;
+
+ return purchasedRanks;
+}
+
+void Item::ApplyArtifactPowerEnchantmentBonuses(uint32 enchantId, bool apply, Player* owner)
+{
+ if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId))
+ {
+ for (uint32 i = 0; i < MAX_ITEM_ENCHANTMENT_EFFECTS; ++i)
+ {
+ switch (enchant->Effect[i])
+ {
+ case ITEM_ENCHANTMENT_TYPE_ARTIFACT_POWER_BONUS_RANK_BY_TYPE:
+ for (ItemDynamicFieldArtifactPowers const& artifactPower : GetArtifactPowers())
+ {
+ if (sArtifactPowerStore.AssertEntry(artifactPower.ArtifactPowerId)->RelicType == enchant->EffectSpellID[i])
+ {
+ ItemDynamicFieldArtifactPowers newPower = artifactPower;
+ if (apply)
+ newPower.CurrentRankWithBonus += enchant->EffectPointsMin[i];
+ else
+ newPower.CurrentRankWithBonus -= enchant->EffectPointsMin[i];
+
+ if (IsEquipped())
+ if (ArtifactPowerRankEntry const* artifactPowerRank = sDB2Manager.GetArtifactPowerRank(artifactPower.ArtifactPowerId, newPower.CurrentRankWithBonus ? newPower.CurrentRankWithBonus - 1 : 0))
+ owner->ApplyArtifactPowerRank(this, artifactPowerRank, newPower.CurrentRankWithBonus != 0);
+
+ SetArtifactPower(&newPower);
+ }
+ }
+ break;
+ case ITEM_ENCHANTMENT_TYPE_ARTIFACT_POWER_BONUS_RANK_BY_ID:
+ if (ItemDynamicFieldArtifactPowers const* artifactPower = GetArtifactPower(enchant->EffectSpellID[i]))
+ {
+ ItemDynamicFieldArtifactPowers newPower = *artifactPower;
+ if (apply)
+ newPower.CurrentRankWithBonus += enchant->EffectPointsMin[i];
+ else
+ newPower.CurrentRankWithBonus -= enchant->EffectPointsMin[i];
+
+ if (IsEquipped())
+ if (ArtifactPowerRankEntry const* artifactPowerRank = sDB2Manager.GetArtifactPowerRank(artifactPower->ArtifactPowerId, newPower.CurrentRankWithBonus ? newPower.CurrentRankWithBonus - 1 : 0))
+ owner->ApplyArtifactPowerRank(this, artifactPowerRank, newPower.CurrentRankWithBonus != 0);
+
+ SetArtifactPower(&newPower);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void Item::CopyArtifactDataFromParent(Item* parent)
+{
+ memcpy(_bonusData.GemItemLevelBonus, parent->GetBonus()->GemItemLevelBonus, sizeof(_bonusData.GemItemLevelBonus));
+ SetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID, parent->GetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID));
+ SetAppearanceModId(parent->GetAppearanceModId());
+}
+
+void Item::GiveArtifactXp(int32 amount, Item* sourceItem, uint32 artifactCategoryId)
+{
+ Player const* owner = GetOwner();
+ if (!owner)
+ return;
+
+ if (artifactCategoryId)
+ {
+ if (ArtifactCategoryEntry const* artifactCategory = sArtifactCategoryStore.LookupEntry(artifactCategoryId))
+ {
+ uint32 artifactKnowledgeLevel = 0;
+ if (sourceItem && sourceItem->GetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL))
+ artifactKnowledgeLevel = sourceItem->GetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL) - 1;
+ else
+ artifactKnowledgeLevel = owner->GetCurrency(artifactCategory->ArtifactKnowledgeCurrencyID);
+
+ amount = int32(amount * sDB2Manager.GetCurveValueAt(artifactCategory->ArtifactKnowledgeMultiplierCurveID, artifactKnowledgeLevel));
+ if (amount >= 5000)
+ amount = 50 * (amount / 50);
+ else if (amount >= 1000)
+ amount = 25 * (amount / 25);
+ else if (amount >= 50)
+ amount = 5 * (amount / 5);
+ }
+ }
+
+ ApplyModInt32Value(ITEM_FIELD_ARTIFACT_XP, amount, true);
+
+ WorldPackets::Artifact::ArtifactXpGain artifactXpGain;
+ artifactXpGain.ArtifactGUID = GetGUID();
+ artifactXpGain.Amount = amount;
+ owner->SendDirectMessage(artifactXpGain.Write());
+}
+
void BonusData::Initialize(ItemTemplate const* proto)
{
Quality = proto->GetQuality();
@@ -2099,7 +2438,10 @@ void BonusData::Initialize(ItemTemplate const* proto)
ItemStatSocketCostMultiplier[i] = proto->GetItemStatSocketCostMultiplier(i);
for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
+ {
SocketColor[i] = proto->GetSocketColor(i);
+ GemItemLevelBonus[i] = 0;
+ }
AppearanceModID = 0;
if (ItemModifiedAppearanceEntry const* defaultAppearance = sDB2Manager.GetDefaultItemModifiedAppearance(proto->GetId()))
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index fe86591ccc3..204eb8e44d3 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -249,6 +249,7 @@ enum ItemModifier : uint16
ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_2 = 20,
ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_3 = 21,
ITEM_MODIFIER_CHALLENGE_KEYSTONE_IS_CHARGED = 22,
+ ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL = 23,
MAX_ITEM_MODIFIERS
};
@@ -273,12 +274,22 @@ struct BonusData
float RepairCostMultiplier;
uint32 ScalingStatDistribution;
+ uint32 GemItemLevelBonus[MAX_ITEM_PROTO_SOCKETS];
+
void Initialize(ItemTemplate const* proto);
void Initialize(WorldPackets::Item::ItemInstance const& itemInstance);
void AddBonus(uint32 type, int32 const (&values)[2]);
};
#pragma pack(push, 1)
+struct ItemDynamicFieldArtifactPowers
+{
+ uint32 ArtifactPowerId;
+ uint8 PurchasedRank;
+ uint8 CurrentRankWithBonus;
+ uint16 Padding;
+};
+
struct ItemDynamicFieldGems
{
uint32 ItemId;
@@ -313,6 +324,7 @@ class TC_GAME_API Item : public Object
bool IsBoundByEnchant() const;
virtual void SaveToDB(SQLTransaction& trans);
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fields, uint32 entry);
+ void LoadArtifactData(uint32 xp, uint32 artifactAppearanceId, std::vector<ItemDynamicFieldArtifactPowers>& powers); // must be called after LoadFromDB to have gems (relics) initialized
void AddBonuses(uint32 bonusListID);
@@ -426,7 +438,8 @@ class TC_GAME_API Item : public Object
int32 GetItemStatType(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_STATS); return _bonusData.ItemStatType[index]; }
int32 GetItemStatValue(uint32 index, Player const* owner) const;
SocketColor GetSocketColor(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_SOCKETS); return SocketColor(_bonusData.SocketColor[index]); }
- uint32 GetAppearanceModId() const { return _bonusData.AppearanceModID; }
+ uint32 GetAppearanceModId() const { return GetUInt32Value(ITEM_FIELD_APPEARANCE_MOD_ID); }
+ void SetAppearanceModId(uint32 appearanceModId) { SetUInt32Value(ITEM_FIELD_APPEARANCE_MOD_ID, appearanceModId); }
uint32 GetArmor(Player const* owner) const { return GetTemplate()->GetArmor(GetItemLevel(owner)); }
void GetDamage(Player const* owner, float& minDamage, float& maxDamage) const { GetTemplate()->GetDamage(GetItemLevel(owner), minDamage, maxDamage); }
uint32 GetDisplayId(Player const* owner) const;
@@ -480,6 +493,16 @@ class TC_GAME_API Item : public Object
ObjectGuid GetChildItem() const { return m_childItem; }
void SetChildItem(ObjectGuid childItem) { m_childItem = childItem; }
+ DynamicFieldStructuredView<ItemDynamicFieldArtifactPowers> GetArtifactPowers() const;
+ ItemDynamicFieldArtifactPowers const* GetArtifactPower(uint32 artifactPowerId) const;
+ void SetArtifactPower(ItemDynamicFieldArtifactPowers const* artifactPower, bool createIfMissing = false);
+
+ void InitArtifactPowers(uint8 artifactId);
+ uint32 GetTotalPurchasedArtifactPowers() const;
+ void ApplyArtifactPowerEnchantmentBonuses(uint32 enchantId, bool apply, Player* owner);
+ void CopyArtifactDataFromParent(Item* parent);
+
+ void GiveArtifactXp(int32 amount, Item* sourceItem, uint32 artifactCategoryId);
protected:
BonusData _bonusData;
@@ -496,5 +519,6 @@ class TC_GAME_API Item : public Object
uint32 m_paidExtendedCost;
GuidSet allowedGUIDs;
ObjectGuid m_childItem;
+ std::unordered_map<uint32, uint16> m_artifactPowerIdToIndex;
};
#endif
diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp
index a36a4a4c246..ac9000ded5e 100644
--- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp
+++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp
@@ -190,6 +190,7 @@ TC_GAME_API uint32 GetRandomPropertyPoints(uint32 itemLevel, uint32 quality, uin
return randPropPointsEntry->RarePropertiesPoints[propIndex];
case ITEM_QUALITY_EPIC:
case ITEM_QUALITY_LEGENDARY:
+ case ITEM_QUALITY_ARTIFACT:
return randPropPointsEntry->EpicPropertiesPoints[propIndex];
}
diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h
index bbf89d18dbf..4d2ec62621c 100644
--- a/src/server/game/Entities/Item/ItemTemplate.h
+++ b/src/server/game/Entities/Item/ItemTemplate.h
@@ -757,6 +757,7 @@ struct TC_GAME_API ItemTemplate
uint32 GetItemLimitCategory() const { return ExtendedData->ItemLimitCategory; }
HolidayIds GetHolidayID() const { return HolidayIds(ExtendedData->HolidayID); }
float GetStatScalingFactor() const { return ExtendedData->StatScalingFactor; }
+ uint8 GetArtifactID() const { return ExtendedData->ArtifactID; }
uint32 GetBaseArmor() const { return GetArmor(ExtendedData->ItemLevel); }
void GetBaseDamage(float& minDamage, float& maxDamage) const { GetDamage(ExtendedData->ItemLevel, minDamage, maxDamage); }
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index e1d5c92b80e..849aab0af79 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -4012,6 +4012,18 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
trans->Append(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT_BY_OWNER);
+ stmt->setUInt64(0, guid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT_POWERS_BY_OWNER);
+ stmt->setUInt64(0, guid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS_BY_OWNER);
+ stmt->setUInt64(0, guid);
+ trans->Append(stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER);
stmt->setUInt64(0, guid);
trans->Append(stmt);
@@ -7212,6 +7224,7 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply)
_ApplyItemBonuses(item, slot, apply);
ApplyItemEquipSpell(item, apply);
+ ApplyArtifactPowers(item, apply);
ApplyEnchantment(item, apply);
TC_LOG_DEBUG("entities.player.items", "Player::_ApplyItemMods: completed");
@@ -7683,6 +7696,80 @@ void Player::UpdateItemSetAuras(bool formChange /*= false*/)
}
}
+void Player::ApplyArtifactPowers(Item* item, bool apply)
+{
+ for (ItemDynamicFieldArtifactPowers const& artifactPower : item->GetArtifactPowers())
+ {
+ uint8 rank = artifactPower.CurrentRankWithBonus;
+ if (!rank)
+ continue;
+
+ ArtifactPowerRankEntry const* artifactPowerRank = sDB2Manager.GetArtifactPowerRank(artifactPower.ArtifactPowerId, rank - 1);
+ if (!artifactPowerRank)
+ continue;
+
+ ApplyArtifactPowerRank(item, artifactPowerRank, apply);
+ }
+}
+
+void Player::ApplyArtifactPowerRank(Item* artifact, ArtifactPowerRankEntry const* artifactPowerRank, bool apply)
+{
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(artifactPowerRank->SpellID);
+ if (!spellInfo)
+ return;
+
+ if (spellInfo->IsPassive())
+ {
+ AuraApplication* powerAura = GetAuraApplication(artifactPowerRank->SpellID, ObjectGuid::Empty, artifact->GetGUID());
+ if (powerAura)
+ {
+ if (apply)
+ {
+ for (AuraEffect* auraEffect : powerAura->GetBase()->GetAuraEffects())
+ {
+ if (!auraEffect)
+ continue;
+
+ if (powerAura->HasEffect(auraEffect->GetEffIndex()))
+ auraEffect->ChangeAmount(artifactPowerRank->Value ? artifactPowerRank->Value : auraEffect->GetSpellEffectInfo()->CalcValue());
+ }
+ }
+ else
+ RemoveAura(powerAura);
+ }
+ else if (apply)
+ {
+ CustomSpellValues csv;
+ if (artifactPowerRank->Value)
+ for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (spellInfo->GetEffect(i))
+ csv.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), artifactPowerRank->Value);
+
+ CastCustomSpell(artifactPowerRank->SpellID, csv, this, TRIGGERED_FULL_MASK, artifact);
+ }
+ }
+ else
+ {
+ if (apply && !HasSpell(artifactPowerRank->SpellID))
+ {
+ AddTemporarySpell(artifactPowerRank->SpellID);
+ WorldPackets::Spells::LearnedSpells learnedSpells;
+ learnedSpells.SuppressMessaging = true;
+ learnedSpells.SpellID.push_back(artifactPowerRank->SpellID);
+ SendDirectMessage(learnedSpells.Write());
+ }
+ else if (!apply)
+ {
+ RemoveTemporarySpell(artifactPowerRank->SpellID);
+ WorldPackets::Spells::UnlearnedSpells unlearnedSpells;
+ unlearnedSpells.SuppressMessaging = true;
+ unlearnedSpells.SpellID.push_back(artifactPowerRank->SpellID);
+ SendDirectMessage(unlearnedSpells.Write());
+ }
+ }
+
+}
+
void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx)
{
if (!target || !target->IsAlive() || target == this)
@@ -7944,6 +8031,7 @@ void Player::_RemoveAllItemMods()
ApplyItemEquipSpell(m_items[i], false);
ApplyEnchantment(m_items[i], false);
+ ApplyArtifactPowers(m_items[i], false);
}
}
@@ -8000,6 +8088,7 @@ void Player::_ApplyAllItemMods()
continue;
ApplyItemEquipSpell(m_items[i], true);
+ ApplyArtifactPowers(m_items[i], true);
ApplyEnchantment(m_items[i], true);
}
}
@@ -11387,6 +11476,10 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const
if (HasSpell(proto->Effects[1]->SpellID))
return EQUIP_ERR_INTERNAL_BAG_ERROR;
+ if (ArtifactEntry const* artifact = sArtifactStore.LookupEntry(proto->GetArtifactID()))
+ if (artifact->SpecID != GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID))
+ return EQUIP_ERR_CANT_USE_ITEM;
+
return EQUIP_ERR_OK;
}
@@ -11515,10 +11608,12 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat
{
childItem->SetGuidValue(ITEM_FIELD_CREATOR, item->GetGUID());
childItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD);
+ item->SetChildItem(childItem->GetGUID());
}
}
}
}
+
return item;
}
@@ -17608,7 +17703,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
// must be before inventory (some items required reputation check)
m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
- _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
+ _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ARTIFACTS), time_diff);
if (IsVoidStorageUnlocked())
_LoadVoidStorage(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_VOID_STORAGE));
@@ -17969,7 +18064,7 @@ void Player::LoadCorpse(PreparedQueryResult result)
RemoveAtLoginFlag(AT_LOGIN_RESURRECT);
}
-void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
+void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, uint32 timeDiff)
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12
// SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text,
@@ -17979,13 +18074,40 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
// itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4,
// 24 25 26 27 28
// spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4,
- // 29 30 31 32 33 34 35 36 37 38 39
+ // 29 30 31 32 33 34 35 36 37 40 41
// gemItemId1, gemBonuses1, gemContext1, gemItemId2, gemBonuses2, gemContext2, gemItemId3, gemBonuses3, gemContext3, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot
//NOTE: the "order by `bag`" is important because it makes sure
//the bagMap is filled before items in the bags are loaded
//NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
//expected to be equipped before offhand items (@todo fixme)
+ // 0 1 2 3 4
+ // SELECT a.itemGuid, a.xp, a.artifactAppearanceId, ap.artifactPowerId, ap.purchasedRank FROM item_instance_artifact_powers ap LEFT JOIN item_instance_artifact a ON ap.itemGuid = a.itemGuid INNER JOIN character_inventory ci ON ci.item = ap.guid WHERE ci.guid = ?
+ std::unordered_map<ObjectGuid, std::tuple<uint32, uint32, std::vector<ItemDynamicFieldArtifactPowers>>> artifactData;
+ if (artifactsResult)
+ {
+ do
+ {
+ Field* fields = artifactsResult->Fetch();
+ auto& artifactDataEntry = artifactData[ObjectGuid::Create<HighGuid::Item>(fields[0].GetUInt64())];
+ std::get<0>(artifactDataEntry) = fields[1].GetUInt32();
+ std::get<1>(artifactDataEntry) = fields[2].GetUInt32();
+ ItemDynamicFieldArtifactPowers artifactPowerData;
+ artifactPowerData.ArtifactPowerId = fields[3].GetUInt32();
+ artifactPowerData.PurchasedRank = fields[4].GetUInt8();
+ if (ArtifactPowerEntry const* artifactPower = sArtifactPowerStore.LookupEntry(artifactPowerData.ArtifactPowerId))
+ {
+ if (artifactPowerData.PurchasedRank > artifactPower->MaxRank)
+ artifactPowerData.PurchasedRank = artifactPower->MaxRank;
+
+ artifactPowerData.CurrentRankWithBonus = (artifactPower->Flags & ARTIFACT_POWER_FLAG_FIRST) ? 1 : 0;
+
+ std::get<2>(artifactDataEntry).push_back(artifactPowerData);
+ }
+
+ } while (artifactsResult->NextRow());
+ }
+
if (result)
{
uint32 zoneId = GetZoneId();
@@ -18003,8 +18125,12 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
Field* fields = result->Fetch();
if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields))
{
- ObjectGuid bagGuid = fields[38].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[38].GetUInt64()) : ObjectGuid::Empty;
- uint8 slot = fields[39].GetUInt8();
+ auto artifactDataItr = artifactData.find(item->GetGUID());
+ if (item->GetTemplate()->GetArtifactID() && artifactDataItr != artifactData.end())
+ item->LoadArtifactData(std::get<0>(artifactDataItr->second), std::get<1>(artifactDataItr->second), std::get<2>(artifactDataItr->second));
+
+ ObjectGuid bagGuid = fields[40].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[40].GetUInt64()) : ObjectGuid::Empty;
+ uint8 slot = fields[41].GetUInt8();
GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item);
GetSession()->GetCollectionMgr()->AddItemAppearance(item);
@@ -18079,7 +18205,6 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
}
-
// Item's state may have changed after storing
if (err == EQUIP_ERR_OK)
{
@@ -18116,7 +18241,12 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
for (Item* childItem : childItems)
{
if (Item* parent = GetItemByGuid(childItem->GetGuidValue(ITEM_FIELD_CREATOR)))
+ {
parent->SetChildItem(childItem->GetGUID());
+ childItem->CopyArtifactDataFromParent(parent);
+ if (childItem->IsEquipped())
+ SetVisibleItemSlot(childItem->GetSlot(), childItem);
+ }
else
childItem->SetState(ITEM_REMOVED, this);
}
@@ -18134,7 +18264,7 @@ void Player::_LoadVoidStorage(PreparedQueryResult result)
do
{
- // SELECT itemId, itemEntry, slot, creatorGuid, randomProperty, suffixFactor, upgradeId, bonusListIDs FROM character_void_storage WHERE playerGuid = ?
+ // SELECT itemId, itemEntry, slot, creatorGuid, randomProperty, suffixFactor, upgradeId, fixedScalingLevel, artifactKnowledgeLevel, bonusListIDs FROM character_void_storage WHERE playerGuid = ?
Field* fields = result->Fetch();
uint64 itemId = fields[0].GetUInt64();
@@ -18144,8 +18274,10 @@ void Player::_LoadVoidStorage(PreparedQueryResult result)
uint32 randomProperty = fields[4].GetUInt32();
uint32 suffixFactor = fields[5].GetUInt32();
uint32 upgradeId = fields[6].GetUInt32();
+ uint32 fixedScalingLevel = fields[7].GetUInt32();
+ uint32 artifactKnowledgeLevel = fields[8].GetUInt32();
std::vector<uint32> bonusListIDs;
- Tokenizer bonusListIdTokens(fields[7].GetString(), ' ');
+ Tokenizer bonusListIdTokens(fields[9].GetString(), ' ');
for (char const* token : bonusListIdTokens)
bonusListIDs.push_back(atoul(token));
@@ -18170,7 +18302,7 @@ void Player::_LoadVoidStorage(PreparedQueryResult result)
continue;
}
- _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid, randomProperty, suffixFactor, upgradeId, bonusListIDs);
+ _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid, randomProperty, suffixFactor, upgradeId, fixedScalingLevel, artifactKnowledgeLevel, bonusListIDs);
WorldPackets::Item::ItemInstance voidInstance;
voidInstance.Initialize(_voidStorageItems[slot]);
@@ -18346,7 +18478,7 @@ void Player::_LoadMailedItems(Mail* mail)
Item* item = NewItemOrBag(proto);
- ObjectGuid ownerGuid = fields[38].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[38].GetUInt64()) : ObjectGuid::Empty;
+ ObjectGuid ownerGuid = fields[40].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[40].GetUInt64()) : ObjectGuid::Empty;
if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry))
{
TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: " UI64FMTD ") in mail (%u) doesn't exist, deleted from mail.", itemGuid, mail->messageID);
@@ -19875,7 +20007,7 @@ void Player::_SaveVoidStorage(SQLTransaction& trans)
}
else
{
- // REPLACE INTO character_inventory (itemId, playerGuid, itemEntry, slot, creatorGuid, randomProperty, suffixFactor, upgradeId, bonusListIDs) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ // REPLACE INTO character_void_storage (itemId, playerGuid, itemEntry, slot, creatorGuid, randomProperty, suffixFactor, upgradeId, fixedScalingLevel, artifactKnowledgeLevel, bonusListIDs) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_VOID_STORAGE_ITEM);
stmt->setUInt64(0, _voidStorageItems[i]->ItemId);
stmt->setUInt64(1, GetGUID().GetCounter());
@@ -19885,10 +20017,12 @@ void Player::_SaveVoidStorage(SQLTransaction& trans)
stmt->setUInt32(5, _voidStorageItems[i]->ItemRandomPropertyId);
stmt->setUInt32(6, _voidStorageItems[i]->ItemSuffixFactor);
stmt->setUInt32(7, _voidStorageItems[i]->ItemUpgradeId);
+ stmt->setUInt32(8, _voidStorageItems[i]->FixedScalingLevel);
+ stmt->setUInt32(9, _voidStorageItems[i]->ArtifactKnowledgeLevel);
std::ostringstream bonusListIDs;
for (int32 bonusListID : _voidStorageItems[i]->BonusListIDs)
bonusListIDs << bonusListID << ' ';
- stmt->setString(8, bonusListIDs.str());
+ stmt->setString(10, bonusListIDs.str());
}
trans->Append(stmt);
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index f25e5cbb3dc..421a15729dc 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -956,6 +956,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS,
PLAYER_LOGIN_QUERY_LOAD_REPUTATION,
PLAYER_LOGIN_QUERY_LOAD_INVENTORY,
+ PLAYER_LOGIN_QUERY_LOAD_ARTIFACTS,
PLAYER_LOGIN_QUERY_LOAD_ACTIONS,
PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT,
PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE,
@@ -1123,15 +1124,17 @@ struct BGData
struct VoidStorageItem
{
- VoidStorageItem() : ItemId(0), ItemEntry(0), ItemRandomPropertyId(0), ItemSuffixFactor(0), ItemUpgradeId(0) { }
- VoidStorageItem(uint64 id, uint32 entry, ObjectGuid const& creator, uint32 randomPropertyId, uint32 suffixFactor, uint32 upgradeId, std::vector<uint32> const& bonuses)
+ VoidStorageItem() : ItemId(0), ItemEntry(0), ItemRandomPropertyId(0), ItemSuffixFactor(0), ItemUpgradeId(0), FixedScalingLevel(0), ArtifactKnowledgeLevel(0) { }
+ VoidStorageItem(uint64 id, uint32 entry, ObjectGuid const& creator, uint32 randomPropertyId, uint32 suffixFactor,
+ uint32 upgradeId, uint32 fixedScalingLevel, uint32 artifactKnowledgeLevel, std::vector<uint32> const& bonuses)
: ItemId(id), ItemEntry(entry), CreatorGuid(creator), ItemRandomPropertyId(randomPropertyId),
- ItemSuffixFactor(suffixFactor), ItemUpgradeId(upgradeId)
+ ItemSuffixFactor(suffixFactor), ItemUpgradeId(upgradeId), FixedScalingLevel(fixedScalingLevel), ArtifactKnowledgeLevel(artifactKnowledgeLevel)
{
BonusListIDs.insert(BonusListIDs.end(), bonuses.begin(), bonuses.end());
}
VoidStorageItem(VoidStorageItem&& vsi) : ItemId(vsi.ItemId), ItemEntry(vsi.ItemEntry), CreatorGuid(vsi.CreatorGuid), ItemRandomPropertyId(vsi.ItemRandomPropertyId),
- ItemSuffixFactor(vsi.ItemSuffixFactor), ItemUpgradeId(vsi.ItemUpgradeId), BonusListIDs(std::move(vsi.BonusListIDs)) { }
+ ItemSuffixFactor(vsi.ItemSuffixFactor), ItemUpgradeId(vsi.ItemUpgradeId), FixedScalingLevel(vsi.FixedScalingLevel),
+ ArtifactKnowledgeLevel(vsi.ArtifactKnowledgeLevel), BonusListIDs(std::move(vsi.BonusListIDs)) { }
uint64 ItemId;
uint32 ItemEntry;
@@ -1139,6 +1142,8 @@ struct VoidStorageItem
uint32 ItemRandomPropertyId;
uint32 ItemSuffixFactor;
uint32 ItemUpgradeId;
+ uint32 FixedScalingLevel;
+ uint32 ArtifactKnowledgeLevel;
std::vector<int32> BonusListIDs;
};
@@ -2152,6 +2157,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void ApplyEquipSpell(SpellInfo const* spellInfo, Item* item, bool apply, bool formChange = false);
void UpdateEquipSpellsAtFormChange();
void UpdateItemSetAuras(bool formChange = false);
+ void ApplyArtifactPowers(Item* item, bool apply);
+ void ApplyArtifactPowerRank(Item* artifact, ArtifactPowerRankEntry const* artifactPowerRank, bool apply);
void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx);
void CastItemUseSpell(Item* item, SpellCastTargets const& targets, ObjectGuid castCount, int32* misc);
@@ -2518,7 +2525,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadActions(PreparedQueryResult result);
void _LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effectResult, uint32 timediff);
void _LoadBoundInstances(PreparedQueryResult result);
- void _LoadInventory(PreparedQueryResult result, uint32 timeDiff);
+ void _LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, uint32 timeDiff);
void _LoadVoidStorage(PreparedQueryResult result);
void _LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery);
void _LoadMail();
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index c8cd3645721..d665225edc1 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -14214,6 +14214,12 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
{
if (GetTypeId() == TYPEID_PLAYER)
{
+ if (Aura* artifactAura = GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE))
+ if (Item* artifact = ToPlayer()->GetItemByGuid(artifactAura->GetCastItemGUID()))
+ if (ArtifactAppearanceEntry const* artifactAppearance = sArtifactAppearanceStore.LookupEntry(artifact->GetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID)))
+ if (ShapeshiftForm(artifactAppearance->ModifiesShapeshiftFormDisplay) == form)
+ return artifactAppearance->ShapeshiftDisplayID;
+
switch (form)
{
case FORM_CAT_FORM:
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 246a8a09818..4847723fb9c 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -31,6 +31,7 @@
#include <boost/container/flat_set.hpp>
#define WORLD_TRIGGER 12999
+#define ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE 197886
enum SpellInterruptFlags
{
@@ -773,7 +774,7 @@ enum NPCFlags : uint64
UNIT_NPC_FLAG_SPELLCLICK = 0x0001000000, // cause client to send 1015 opcode (spell click)
UNIT_NPC_FLAG_PLAYER_VEHICLE = 0x0002000000, // players with mounts that have vehicle data should have it set
UNIT_NPC_FLAG_MAILBOX = 0x0004000000, // mailbox
- UNIT_NPC_FLAG_REFORGER = 0x0008000000, // reforging
+ UNIT_NPC_FLAG_ARTIFACT_POWER_RESPEC = 0x0008000000, // artifact powers reset
UNIT_NPC_FLAG_TRANSMOGRIFIER = 0x0010000000, // transmogrification
UNIT_NPC_FLAG_VAULTKEEPER = 0x0020000000, // void storage
UNIT_NPC_FLAG_BLACK_MARKET = 0x0080000000, // black market