获取新物品显示改变纹理、设置new图标,进行排序

This commit is contained in:
尚美 2025-06-17 00:21:49 +08:00
parent c5f085f175
commit e69e2e76f2
8 changed files with 1543 additions and 152 deletions

View File

@ -613,7 +613,7 @@ SM_CollectionData.Cameras =
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_TABARD)] = { 2.15, 0.00, -1.80, 0.10 }, -- 战袍
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_WRIST)] = { 3.85, -0.22, -2.75, -0.80 }, -- 护腕
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_HAND)] = { 3.85, -0.22, -2.75, -0.80 }, -- 手套
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_WAIST)] = { 4, 0.3, -2.80, 0.10 }, -- 腰带
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_WAIST)] = { 5, 0.4, -4.2, 0 }, -- 腰带
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_LEGS)] = { 2.00, -0.10, -0.20, 0.10 }, -- 腿甲
[Chr(O.Classic, R.BloodElf, S.Male, I.INVTYPE_FEET)] = { 2.45, -0.10, 0.47, -0.70 }, -- 靴子
[Chr(O.Classic, R.BloodElf, S.Male, I.SetsDetails)] = { 0.00, 0.00, -0.33, 0.00 }, -- 套装详情

View File

@ -23,6 +23,83 @@ SM_ItemCollectionUI.pageButtons = {}
SM_ItemCollectionUI.lastUpdateTime = 0 -- 上次更新时间
SM_ItemCollectionUI.CACHE_EXPIRE_TIME = 3600 -- 缓存过期时间1小时
-- 注册物品获取事件
function SM_ItemCollectionUI:RegisterItemEvents()
-- 创建事件处理框架
if not self.eventFrame then
self.eventFrame = CreateFrame("Frame")
self.eventFrame:SetScript("OnEvent", function(self, event, ...)
if event == "CHAT_MSG_LOOT" then
SM_ItemCollectionUI:OnLootReceived(...)
elseif event == "BAG_UPDATE" then
-- 使用背包更新事件替代ITEM_PUSH
SM_ItemCollectionUI:OnBagUpdate()
end
end)
end
-- 注册相关事件
self.eventFrame:RegisterEvent("CHAT_MSG_LOOT")
self.eventFrame:RegisterEvent("BAG_UPDATE")
print("[事件] 物品获取事件已注册")
end
-- 处理物品获取事件
function SM_ItemCollectionUI:OnLootReceived(msg)
-- 解析聊天消息中的物品ID
local itemLink = string.match(msg, "|H(item:[^|]+)|h")
if itemLink then
local itemID = string.match(itemLink, "item:(%d+)")
if itemID then
print("[事件] 检测到物品获取:", itemID)
-- 延迟一点执行更新,确保服务器数据已同步
local itemToUpdate = itemID
local updateFrame = CreateFrame("Frame")
updateFrame:SetScript("OnUpdate", function(self, elapsed)
self.elapsed = (self.elapsed or 0) + elapsed
if self.elapsed >= 0.5 then
SM_ItemCollectionUI:UpdateItemObtainedStatus(itemToUpdate, true)
self:SetScript("OnUpdate", nil)
end
end)
end
end
end
-- 处理背包更新事件
function SM_ItemCollectionUI:OnBagUpdate()
-- 检查所有背包中的物品
for bag = 0, 4 do
local slots = GetContainerNumSlots(bag)
for slot = 1, slots do
local itemLink = GetContainerItemLink(bag, slot)
if itemLink then
local itemID = string.match(itemLink, "item:(%d+)")
if itemID and not self.processedItems then
self.processedItems = {}
end
-- 避免重复处理同一物品
if itemID and not self.processedItems[itemID] then
self.processedItems[itemID] = true
print("[事件] 检测到背包物品:", itemID)
-- 延迟一点执行更新,确保服务器数据已同步
local itemToUpdate = itemID
local updateFrame = CreateFrame("Frame")
updateFrame:SetScript("OnUpdate", function(self, elapsed)
self.elapsed = (self.elapsed or 0) + elapsed
if self.elapsed >= 0.5 then
SM_ItemCollectionUI:UpdateItemObtainedStatus(itemToUpdate, true)
self:SetScript("OnUpdate", nil)
end
end)
end
end
end
end
end
-- 初始化物品收藏界面
function SM_ItemCollectionUI:Init()
-- 只做一次全局声明绝不重置SM_CollectionsDB或itemCache
@ -32,9 +109,45 @@ function SM_ItemCollectionUI:Init()
end
-- 初始化物品槽位数据
self:InitSlotData()
-- 注册物品获取事件
self:RegisterItemEvents()
self.initialized = true
end
-- 添加处理NEW图标的统一函数
function SM_ItemCollectionUI:HandleNewItemIcon(model, itemID)
if model and itemID and SM_Collections and SM_Collections.NewItems and SM_Collections.NewItems.item and SM_Collections.NewItems.item[tonumber(itemID)] then
-- 添加"NEW"标记,如果这是新获得的物品
if model.itemData and model.itemData.obtained and SM_Collections.NewItems and SM_Collections.NewItems.item and SM_Collections.NewItems.item[tonumber(itemID)] then
print("[收藏系统] 检测到新物品准备添加NEW图标:", itemID)
-- 创建NEW图标使用OVERLAY层的最高级别(7)确保显示在最顶层
if not model.newTexture then
local newTexture = model:CreateTexture(nil, "OVERLAY", nil, 7)
newTexture:SetSize(32, 28)
newTexture:SetPoint("TOPRIGHT", model, "TOPRIGHT", 10, 10)
newTexture:SetTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\New_Icon")
-- 确保纹理在最顶层
newTexture:SetDrawLayer("OVERLAY", 7)
model.newTexture = newTexture
print("[收藏系统] 成功创建NEW图标:", itemID)
else
model.newTexture:Show()
-- 确保纹理在最顶层
model.newTexture:SetDrawLayer("OVERLAY", 7)
print("[收藏系统] 显示已有NEW图标:", itemID)
end
else
-- 如果不是新物品但存在NEW图标隐藏它
if model and model.newTexture then
model.newTexture:Hide()
end
end
end
end
-- 初始化物品槽位数据
function SM_ItemCollectionUI:InitSlotData()
-- 获取物品分类数据
@ -265,6 +378,14 @@ function SM_ItemCollectionUI:UpdateAttributes(itemID, itemType)
-- 重置数组
rightSidePanel.attributeButtons = {}
rightSidePanel.attributeTexts = {}
-- 创建属性容器用于RefreshCollectionStatus函数统一更新颜色
if not rightSidePanel.attributesContainer then
rightSidePanel.attributesContainer = CreateFrame("Frame", nil, rightSidePanel.AttributeBonusFrame)
rightSidePanel.attributesContainer:SetSize(180, 200)
rightSidePanel.attributesContainer:SetPoint("TOP", rightSidePanel.AttributeBonusFrame, "TOP", 0, -30)
end
rightSidePanel.attributesContainer:Show()
-- 模拟不同物品的不同属性加成
local attributes = {}
@ -299,10 +420,10 @@ function SM_ItemCollectionUI:UpdateAttributes(itemID, itemType)
for i, attribute in ipairs(attributes) do
-- 创建带背景纹理的按钮
local button = CreateFrame("Button", "SM_ItemCollectionAttributeButton" .. i, rightSidePanel.AttributeBonusFrame)
local button = CreateFrame("Button", "SM_ItemCollectionAttributeButton" .. i, rightSidePanel.attributesContainer)
button:SetSize(buttonH, buttonW)
button:SetNormalTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\AttributeuttonTexture")
button:SetPoint("TOPLEFT", 0, -30 - (i - 1) * buttonW)
button:SetPoint("TOPLEFT", 0, -(i - 1) * buttonW)
-- 属性名称文本
local nameText = button:CreateFontString(nil, "OVERLAY")
@ -355,11 +476,49 @@ function SM_ItemCollectionUI:HandleButtonClick(slot)
end
end
self.currentPage = 1
-- 确保按获得状态对物品排序
self:SortSlotItems(slot)
-- 显示物品预览
self:ShowItemPreview(slot, self.currentPanel)
end
-- 强制清理所有物品模型
function SM_ItemCollectionUI:ForceCleanupModels(panel)
print("[清理] 强制清理所有物品模型")
-- 清理预览框架
if panel and panel.previewFrame then
local children = { panel.previewFrame:GetChildren() }
for _, child in ipairs(children) do
-- 清理所有事件处理器
child:SetScript("OnEnter", nil)
child:SetScript("OnLeave", nil)
child:SetScript("OnMouseDown", nil)
child:SetScript("OnMouseUp", nil)
child:SetScript("OnMouseWheel", nil)
child:SetScript("OnUpdate", nil)
-- 清理所有纹理
if child.newTexture then child.newTexture:Hide() end
if child.TransmogStateTexture then child.TransmogStateTexture:Hide() end
if child.Border then child.Border:Hide() end
-- 隐藏并移除
child:Hide()
child:SetParent(nil)
child:ClearAllPoints()
end
end
-- 清理全局缓存
collectgarbage("collect")
end
-- 显示物品预览
function SM_ItemCollectionUI:ShowItemPreview(slot, panel)
-- 确保物品已排序
self:SortSlotItems(slot)
-- 清除当前选中的模型
self:ClearModelSelection()
@ -482,12 +641,46 @@ function SM_ItemCollectionUI:ShowItemPreview(slot, panel)
end
end
model.itemData = itemData -- 保存物品数据以便后续使用
model.itemData = itemData or {id = itemID, obtained = false} -- 保存物品数据以便后续使用
-- 添加"NEW"标记,如果这是新获得的物品
if itemData and itemData.obtained and SM_Collections.NewItems and SM_Collections.NewItems.item and SM_Collections.NewItems.item[tonumber(itemID)] then
print("[收藏系统] 检测到新物品准备添加NEW图标:", itemID)
-- 创建NEW图标使用OVERLAY层的最高级别(7)确保显示在最顶层
if not model.newTexture then
local newTexture = model:CreateTexture(nil, "OVERLAY", nil, 7)
newTexture:SetSize(32, 28)
newTexture:SetPoint("TOPRIGHT", model, "TOPRIGHT", 10, 10)
newTexture:SetTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\New_Icon")
-- 确保纹理在最顶层
newTexture:SetDrawLayer("OVERLAY", 7)
model.newTexture = newTexture
print("[收藏系统] 成功创建NEW图标:", itemID)
else
model.newTexture:Show()
-- 确保纹理在最顶层
model.newTexture:SetDrawLayer("OVERLAY", 7)
print("[收藏系统] 显示已有NEW图标:", itemID)
end
end
if model.TransmogStateTexture then model.TransmogStateTexture:Hide() end
table.insert(modelFrames, model)
model:Show()
model:EnableMouse(true)
model:SetScript("OnEnter", function(self)
-- 处理NEW图标
if self.newTexture and self.newTexture:IsShown() then
print("[收藏系统] 清除NEW图标:", itemID)
self.newTexture:Hide()
-- 从NewItems表中移除
if SM_Collections and SM_Collections.NewItems and SM_Collections.NewItems.item then
SM_Collections.NewItems.item[tonumber(itemID)] = nil
print("[收藏系统] 已清除NEW标记:", itemID)
end
end
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetHyperlink("item:" .. itemID)
GameTooltip:Show()
@ -574,12 +767,45 @@ function SM_ItemCollectionUI:ShowItemPreview(slot, panel)
end
end
model.itemData = itemData -- 保存物品数据以便后续使用
model.itemData = itemData or {id = itemID, obtained = false} -- 保存物品数据以便后续使用
-- 添加"NEW"标记,如果这是新获得的物品
if itemData and itemData.obtained and SM_Collections.NewItems and SM_Collections.NewItems.item and SM_Collections.NewItems.item[tonumber(itemID)] then
print("[收藏系统] 检测到新物品准备添加NEW图标(3D):", itemID)
-- 创建NEW图标使用OVERLAY层的最高级别(7)确保显示在最顶层
if not model.newTexture then
local newTexture = model:CreateTexture(nil, "OVERLAY", nil, 7)
newTexture:SetSize(32, 28)
newTexture:SetPoint("TOPRIGHT", model, "TOPRIGHT", 10, 10)
newTexture:SetTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\New_Icon")
-- 确保纹理在最顶层
newTexture:SetDrawLayer("OVERLAY", 7)
model.newTexture = newTexture
print("[收藏系统] 成功创建NEW图标(3D):", itemID)
else
model.newTexture:Show()
-- 确保纹理在最顶层
model.newTexture:SetDrawLayer("OVERLAY", 7)
print("[收藏系统] 显示已有NEW图标(3D):", itemID)
end
end
table.insert(modelFrames, model)
model:Show()
model:EnableMouse(true)
model:SetScript("OnEnter", function(self)
-- 处理NEW图标
if self.newTexture and self.newTexture:IsShown() then
print("[收藏系统] 清除NEW图标:", itemID)
self.newTexture:Hide()
-- 从NewItems表中移除
if SM_Collections and SM_Collections.NewItems and SM_Collections.NewItems.item then
SM_Collections.NewItems.item[tonumber(itemID)] = nil
print("[收藏系统] 已清除NEW标记:", itemID)
end
end
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetHyperlink("item:" .. itemID)
GameTooltip:Show()
@ -774,11 +1000,20 @@ function SM_ItemCollectionUI:CreatePageButtons(panel, slot)
-- 上一页按钮
local prevBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
prevBtn:SetSize(60, 24)
prevBtn:SetPoint("BOTTOM", panel, "BOTTOM", 0, 30)
prevBtn:SetPoint("BOTTOM", panel, "BOTTOM", 0, 35)
prevBtn:SetText("上一页")
prevBtn:SetScript("OnClick", function()
if self.currentPage > 1 then
print("[分页] 切换到上一页:", self.currentPage - 1)
self.currentPage = self.currentPage - 1
-- 确保物品始终保持排序
self:SortSlotItems(slot)
-- 强制清理旧的预览内容
if panel.previewFrame then
panel.previewFrame:Hide()
panel.previewFrame:SetParent(nil)
panel.previewFrame = nil
end
self:ShowItemPreview(slot, panel)
end
end)
@ -793,11 +1028,20 @@ function SM_ItemCollectionUI:CreatePageButtons(panel, slot)
-- 下一页按钮
local nextBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
nextBtn:SetSize(60, 24)
nextBtn:SetPoint("BOTTOM", panel, "BOTTOM", 160, 30)
nextBtn:SetPoint("BOTTOM", panel, "BOTTOM", 160, 35)
nextBtn:SetText("下一页")
nextBtn:SetScript("OnClick", function()
if self.currentPage < self.totalPages then
print("[分页] 切换到下一页:", self.currentPage + 1)
self.currentPage = self.currentPage + 1
-- 确保物品始终保持排序
self:SortSlotItems(slot)
-- 强制清理旧的预览内容
if panel.previewFrame then
panel.previewFrame:Hide()
panel.previewFrame:SetParent(nil)
panel.previewFrame = nil
end
self:ShowItemPreview(slot, panel)
end
end)
@ -811,11 +1055,138 @@ function SM_ItemCollectionUI:CreatePageButtons(panel, slot)
-- 页码文本
local pageText = panel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
pageText:SetPoint("BOTTOM", panel, "BOTTOM", 80, 35)
pageText:SetText(string.format("第 %d / %d 页", self.currentPage, self.totalPages))
pageText:SetDrawLayer("OVERLAY", 7) -- 使用子层级7确保在OVERLAY层的最顶部
pageText:Show()
self.pageButtons.text = pageText
pageText:SetPoint("BOTTOM", panel, "BOTTOM", 80, 40)
pageText:SetText(string.format("第 %d / %d 页", self.currentPage, self.totalPages))
pageText:SetDrawLayer("OVERLAY", 7) -- 使用子层级7确保在OVERLAY层的最顶部
pageText:Show()
self.pageButtons.text = pageText
end
-- 新增函数:对槽位中的物品按获得状态排序
function SM_ItemCollectionUI:SortSlotItems(slot)
if not slot or not slot.items or #slot.items == 0 then
print("[排序] 槽位", slot and slot.invType or "未知", "为空或没有物品,无需排序")
return
end
print("[排序] 开始对槽位", slot.invType, "中的", #slot.items, "个物品排序")
-- 对物品按照已获得状态进行排序,已获得的排在前面
local sortedItems = {}
-- 首先创建一个带状态的物品列表
for _, itemID in ipairs(slot.items) do
local isObtained = false
if SM_CollectionsDB and SM_CollectionsDB.itemCache and SM_CollectionsDB.itemCache.items then
for _, item in ipairs(SM_CollectionsDB.itemCache.items) do
if tonumber(item.id) == tonumber(itemID) then
isObtained = item.obtained
break
end
end
end
table.insert(sortedItems, {id = itemID, obtained = isObtained})
end
-- 按照获得状态进行排序
table.sort(sortedItems, function(a, b)
if a.obtained and not b.obtained then
return true -- a排在b前面
elseif not a.obtained and b.obtained then
return false -- b排在a前面
else
-- 如果获得状态相同按ID排序
return a.id < b.id
end
end)
-- 打印排序前后的状态
local beforeIDs = {}
for i, id in ipairs(slot.items) do
beforeIDs[i] = id
end
local afterIDs = {}
for i, item in ipairs(sortedItems) do
afterIDs[i] = item.id
end
print("[排序] 排序前:", table.concat(beforeIDs, ", "))
print("[排序] 排序后:", table.concat(afterIDs, ", "))
-- 更新排序后的物品ID列表
local newItems = {}
for _, item in ipairs(sortedItems) do
table.insert(newItems, item.id)
end
slot.items = newItems
print("[排序] 槽位", slot.invType, "物品排序完成")
return true -- 返回true表示排序成功
end
-- 新增函数:更新物品获得状态并重新排序显示
function SM_ItemCollectionUI:UpdateItemObtainedStatus(itemID, isObtained)
if not itemID then return end
print("[更新] 更新物品", itemID, "获得状态为", isObtained and "已获得" or "未获得")
-- 更新物品缓存中的状态
local itemUpdated = false
if SM_CollectionsDB and SM_CollectionsDB.itemCache and SM_CollectionsDB.itemCache.items then
for _, item in ipairs(SM_CollectionsDB.itemCache.items) do
if tonumber(item.id) == tonumber(itemID) then
item.obtained = isObtained
itemUpdated = true
break
end
end
end
if not itemUpdated then
print("[更新] 警告:物品", itemID, "未在缓存中找到")
return false
end
-- 如果物品是新获得的添加到NewItems表中
if isObtained then
if not SM_Collections.NewItems then
SM_Collections.NewItems = {}
end
if not SM_Collections.NewItems.item then
SM_Collections.NewItems.item = {}
end
SM_Collections.NewItems.item[tonumber(itemID)] = true
print("[更新] 物品", itemID, "标记为新获得")
end
-- 找到当前物品所在的槽位
local currentSlot = nil
for _, slot in ipairs(self.ALL_SLOTS) do
for _, slotItemID in ipairs(slot.items) do
if tonumber(slotItemID) == tonumber(itemID) then
currentSlot = slot
break
end
end
if currentSlot then break end
end
if not currentSlot then
print("[更新] 警告:物品", itemID, "未在任何槽位中找到")
return false
end
-- 重新排序当前槽位的物品
self:SortSlotItems(currentSlot)
-- 如果当前正在显示该槽位,则刷新显示
if self.currentPanel and self.currentSelectedButton and self.currentSelectedButton.slotData == currentSlot then
print("[更新] 刷新当前显示的槽位", currentSlot.name)
self:ShowItemPreview(currentSlot, self.currentPanel)
end
return true
end
-- 清除所有按钮

View File

@ -38,3 +38,57 @@ SM_Collections.NewItems = {
card = {}, -- 新获得的卡牌ID
item = {} -- 新获得的物品ID
}
-- 添加ADDON_LOADED事件处理以初始化数据库
local InitFrame = CreateFrame("Frame")
InitFrame:RegisterEvent("ADDON_LOADED")
InitFrame:SetScript("OnEvent", function(self, event, addonName)
if addonName == "SM_CollectionSystem" then
print("收藏系统插件加载:", addonName)
-- 确保SM_CollectionsDB存在
if not SM_CollectionsDB then
SM_CollectionsDB = {
mountCache = { mounts = {} },
companionCache = { companions = {} },
cardCache = { cards = {} },
itemCache = { items = {} }
}
end
-- 确保缓存子表存在
if not SM_CollectionsDB.mountCache then
SM_CollectionsDB.mountCache = { mounts = {} }
end
if not SM_CollectionsDB.companionCache then
SM_CollectionsDB.companionCache = { companions = {} }
end
if not SM_CollectionsDB.cardCache then
SM_CollectionsDB.cardCache = { cards = {} }
end
if not SM_CollectionsDB.itemCache then
SM_CollectionsDB.itemCache = { items = {} }
end
-- 确保items子表存在
if not SM_CollectionsDB.itemCache.items then
SM_CollectionsDB.itemCache.items = {}
end
-- 注册插件消息在3.3.5中不需要显式注册前缀)
print("收藏系统消息通道已准备就绪")
-- 设置清理数据库的函数,在每次登录时调用
self:RegisterEvent("PLAYER_LOGIN")
elseif event == "PLAYER_LOGIN" then
-- 在登录时清理过期的缓存条目
SM_Collections.CleanupExpiredCacheEntries()
print("收藏系统玩家登录初始化完成")
end
end)
-- 清理过期的缓存条目
function SM_Collections.CleanupExpiredCacheEntries()
-- 这里可以添加逻辑来删除很久未更新的缓存条目
-- 或者根据服务器版本检测不再有效的条目
end

View File

@ -360,15 +360,82 @@ end
-- 处理收藏状态更新
function SM_Collections:HandleCollectionUpdate(msg)
print("[收藏更新] 收到消息:", msg)
local data = self:MessageSplit(msg, "|")
if #data >= 3 then
local itemType = data[1]
local itemID = tonumber(data[2])
local isObtained = (data[3] == "1")
print("[收藏更新] 解析消息:", itemType, itemID, isObtained and "已获得" or "未获得")
-- 确保数据结构存在
if not SM_CollectionsDB then
SM_CollectionsDB = {
mountCache = { mounts = {} },
companionCache = { companions = {} },
cardCache = { cards = {} },
itemCache = { items = {} }
}
end
-- 同步更新SM_CollectionsDB数据确保立即生效
if itemType == "mount" and SM_CollectionsDB.mountCache then
local found = false
for i, mount in ipairs(SM_CollectionsDB.mountCache.mounts or {}) do
if mount.id == itemID then
mount.obtained = isObtained
found = true
break
end
end
if not found and isObtained then
table.insert(SM_CollectionsDB.mountCache.mounts, {id = itemID, obtained = true})
end
elseif itemType == "companion" and SM_CollectionsDB.companionCache then
local found = false
for i, companion in ipairs(SM_CollectionsDB.companionCache.companions or {}) do
if companion.id == itemID then
companion.obtained = isObtained
found = true
break
end
end
if not found and isObtained then
table.insert(SM_CollectionsDB.companionCache.companions, {id = itemID, obtained = true})
end
elseif itemType == "card" and SM_CollectionsDB.cardCache then
local found = false
for i, card in ipairs(SM_CollectionsDB.cardCache.cards or {}) do
if card.id == itemID then
card.obtained = isObtained
found = true
break
end
end
if not found and isObtained then
table.insert(SM_CollectionsDB.cardCache.cards, {id = itemID, obtained = true})
end
elseif itemType == "item" and SM_CollectionsDB.itemCache then
local found = false
for i, item in ipairs(SM_CollectionsDB.itemCache.items or {}) do
if tonumber(item.id) == itemID then
item.obtained = isObtained
found = true
break
end
end
if not found and isObtained then
table.insert(SM_CollectionsDB.itemCache.items, {id = itemID, obtained = true})
end
-- 物品收藏更新后,立即重新排序当前显示的物品
print("[收藏更新] 物品获取:", itemID, isObtained and "已获得" or "未获得")
-- 立即执行物品排序,不等待延迟
self:ForceItemReordering(itemID)
end
if itemID and itemType then
-- 更新本地数据
@ -402,12 +469,10 @@ function SM_Collections:HandleCollectionUpdate(msg)
-- 如果是新获得的物品记录到NewItems表中
if isObtained then
self.NewItems.item[itemID] = true
print("[收藏系统] 新获得物品:", itemID)
end
end
-- 检查当前是否显示了对应类型的面板
local isCurrentPanel = false
if self.MainFrame then
@ -418,11 +483,6 @@ function SM_Collections:HandleCollectionUpdate(msg)
if self.CurrentPanel.scrollChild and self.CurrentPanel.scrollChild.buttons and #self.CurrentPanel.scrollChild.buttons > 0 then
local firstButton = self.CurrentPanel.scrollChild.buttons[1]
if firstButton then
end
if firstButton and firstButton.itemType then
if firstButton.itemType == "mount" then
inferredTabID = 1
@ -451,9 +511,6 @@ function SM_Collections:HandleCollectionUpdate(msg)
end
end
-- 无论当前面板是什么都尝试刷新UI
-- 如果不是当前面板RefreshCollectionStatus函数内部会自己处理
self:RefreshCollectionStatus(itemType, itemID, isObtained)
@ -510,14 +567,112 @@ function SM_Collections:HandleCollectionUpdate(msg)
end)
companionReloadFrame:Show()
end
-- 如果是物品且获得了新物品,强制重新加载物品面板
if itemType == "item" and isObtained then
-- 使用 3.3.5 支持的延迟执行方法
local itemReloadFrame = CreateFrame("Frame")
itemReloadFrame:SetScript("OnUpdate", function(self, elapsed)
self.elapsed = (self.elapsed or 0) + elapsed
if self.elapsed >= 0.1 then
self:SetScript("OnUpdate", nil)
self:Hide()
-- 如果物品页面当前打开,刷新显示
if SM_Collections.MainFrame and SM_Collections.MainFrame.tabID == 4 then
-- 如果当前正在使用物品收藏UI
if SM_ItemCollectionUI and SM_ItemCollectionUI.currentPanel then
-- 如果有当前选中的按钮和对应的槽位数据
if SM_ItemCollectionUI.currentSelectedButton and SM_ItemCollectionUI.currentSelectedButton.slotData then
-- 对当前槽位的物品重新排序并刷新显示
local slot = SM_ItemCollectionUI.currentSelectedButton.slotData
SM_ItemCollectionUI:SortSlotItems(slot)
SM_ItemCollectionUI:ShowItemPreview(slot, SM_ItemCollectionUI.currentPanel)
end
end
-- 这里调用现有的显示物品函数
SM_Collections:ShowItems()
end
end
end)
itemReloadFrame:Show()
end
end
else
print("[收藏更新] 消息格式不正确:", msg)
end
end
-- 强制物品重排序,立即执行而不等待延迟
function SM_Collections:ForceItemReordering(updatedItemID)
print("[排序] 强制重排序:", updatedItemID)
-- 确保物品收藏UI存在
if not SM_ItemCollectionUI then
print("[排序] 物品收藏UI不存在无法排序")
return
end
-- 确保物品槽位数据存在
if not SM_ItemCollectionUI.ALL_SLOTS then
print("[排序] 物品槽位数据不存在,无法排序")
return
end
-- 是否当前显示的是物品面板
local isItemPanelVisible = (SM_Collections.MainFrame and SM_Collections.MainFrame.tabID == 4)
print("[排序] 物品面板是否可见:", isItemPanelVisible)
-- 对所有槽位进行排序
local allSlots = SM_ItemCollectionUI.ALL_SLOTS
local targetSlot = nil
-- 找到包含更新物品的槽位
for _, slot in ipairs(allSlots) do
local containsItem = false
for _, itemID in ipairs(slot.items or {}) do
if tonumber(itemID) == tonumber(updatedItemID) then
targetSlot = slot
containsItem = true
print("[排序] 在槽位", slot.invType, "中找到物品", updatedItemID)
break
end
end
-- 无论是否包含物品,都对该槽位进行排序
if #(slot.items or {}) > 0 then
print("[排序] 对槽位", slot.invType, "进行排序")
SM_ItemCollectionUI:SortSlotItems(slot)
end
end
-- 如果物品面板可见,且找到了包含更新物品的槽位
if isItemPanelVisible and targetSlot then
print("[排序] 物品面板可见,立即刷新")
-- 如果当前显示的正是包含更新物品的槽位
if SM_ItemCollectionUI.currentSelectedButton and
SM_ItemCollectionUI.currentSelectedButton.slotData and
SM_ItemCollectionUI.currentSelectedButton.slotData.invType == targetSlot.invType then
print("[排序] 刷新当前槽位:", targetSlot.invType)
-- 直接重新显示物品预览
SM_ItemCollectionUI:ShowItemPreview(targetSlot, SM_ItemCollectionUI.currentPanel)
-- 强制立即重新加载整个物品面板
SM_Collections:ShowItems()
else
-- 当前显示的不是包含更新物品的槽位,但我们仍然刷新当前显示
print("[排序] 当前显示的不是目标槽位,刷新当前槽位")
local currentSlot = SM_ItemCollectionUI.currentSelectedButton.slotData
SM_ItemCollectionUI:ShowItemPreview(currentSlot, SM_ItemCollectionUI.currentPanel)
end
end
end
-- 消息处理函数
function SM_Collections_MessageEvent(self, event, prefix, msg, type, sender)
print("收到消息: 事件=", event, "前缀=", prefix, "类型=", type, "发送者=", sender)
if event == 'CHAT_MSG_WHISPER' or event == 'CHAT_MSG_ADDON' then
if prefix == 'SM_S_COLLECTIONS_MOUNTS' then
SM_Collections:HandleMountsData(msg) -- 坐骑
@ -533,13 +688,36 @@ function SM_Collections_MessageEvent(self, event, prefix, msg, type, sender)
SM_Collections:HandleItemAttributesData(msg) -- 物品属性
elseif prefix == "SM_S_COLLECTION_UPDATE" then
SM_Collections:HandleCollectionUpdate(msg) --404行
print("收到收藏更新消息:", msg)
SM_Collections:HandleCollectionUpdate(msg)
end
end
end
-- 添加测试更新命令
SLASH_SM_COLLECTIONS_TEST1 = "/smtest"
SLASH_SM_COLLECTIONS_TEST2 = "/收藏测试"
SlashCmdList["SM_COLLECTIONS_TEST"] = function(msg)
-- 解析命令参数 /smtest item 12345 1
local args = {}
for word in msg:gmatch("%S+") do
table.insert(args, word)
end
local itemType = args[1] or "item"
local itemID = tonumber(args[2] or "12345")
local status = args[3] or "1"
print("执行测试更新命令: 类型=", itemType, "ID=", itemID, "状态=", status)
-- 构造测试消息
local testMsg = itemType .. "|" .. itemID .. "|" .. status
-- 直接调用处理函数
SM_Collections:HandleCollectionUpdate(testMsg)
end
-- 事件监听框架
local SM_Collections_EventFrame = CreateFrame('Frame', 'SM_Collections_EventFrame')
SM_Collections_EventFrame:RegisterEvent('CHAT_MSG_WHISPER')
@ -549,8 +727,8 @@ SM_Collections_EventFrame:SetScript('OnEvent', function(self, event, ...)
if event == 'ADDON_LOADED' then
local addonName = ...
if addonName == "SM_CollectionSystem" then
print("收藏系统插件加载完成")
-- 在3.3.5中不需要显式注册前缀
end
elseif event == 'CHAT_MSG_ADDON' then
SM_Collections_MessageEvent(self, event, ...)
@ -572,7 +750,9 @@ SM_Collections_EventFrame:SetScript('OnEvent', function(self, event, ...)
prefix == "SM_S_COLLECTIONS_CARDS" or
prefix == "SM_S_COLLECTIONS_ITEMS" or
prefix == "SM_S_COLLECTIONS_ATTRIBUTES" or
prefix == "SM_S_COLLECTIONS_ITEM_ATTRIBUTES") then
prefix == "SM_S_COLLECTIONS_ITEM_ATTRIBUTES" or
prefix == "SM_S_COLLECTION_UPDATE") then
print("收到系统私聊消息:", prefix, content:sub(1, 50))
SM_Collections_MessageEvent(self, "CHAT_MSG_WHISPER", prefix, content)
end
end

View File

@ -374,22 +374,54 @@ function SM_Collections:CreateListItems(parent, data, itemType, model, nameText,
-- 添加"NEW"标记,如果这个项目是新获得的
if item.obtained and self.NewItems and self.NewItems[itemType] and self.NewItems[itemType][item.id] then
local newTexture = button:CreateTexture(nil, "OVERLAY")
newTexture:SetSize(32, 28)
newTexture:SetPoint("TOPRIGHT", button, "TOPRIGHT", -5, -5)
newTexture:SetTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\New_Icon")
button.newTexture = newTexture
-- 检查是否已经有NEW标记
if not button.newTexture then
local newTexture = button:CreateTexture(nil, "OVERLAY")
newTexture:SetSize(32, 28)
newTexture:SetPoint("TOPRIGHT", button, "TOPRIGHT", 10, 10)
newTexture:SetTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\New_Icon")
button.newTexture = newTexture
-- 在鼠标悬停时隐藏"NEW"标记
button:HookScript("OnEnter", function(self)
if self.newTexture then
self.newTexture:Hide()
-- 从NewItems表中移除
if SM_Collections.NewItems and SM_Collections.NewItems[itemType] then
SM_Collections.NewItems[itemType][item.id] = nil
-- 先清除现有的鼠标处理函数,防止多次注册
button:SetScript("OnEnter", nil)
-- 使用SetScript而不是HookScript确保只有一个处理函数
button:SetScript("OnEnter", function(self)
print("[收藏系统] 按钮鼠标悬停:", itemType, item.id)
if self.newTexture and self.newTexture:IsShown() then
self.newTexture:Hide()
-- 从NewItems表中移除
if SM_Collections.NewItems and SM_Collections.NewItems[itemType] then
SM_Collections.NewItems[itemType][item.id] = nil
print("[收藏系统] 清除NEW标记:", itemType, item.id)
end
end
end
end)
-- 显示提示信息
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
if self.itemData then
GameTooltip:SetText(self.itemData.name or "未知物品")
else
GameTooltip:SetText("收藏项目")
end
GameTooltip:Show()
-- 显示高亮
if self.Highlight then
self.Highlight:Show()
end
end)
button:SetScript("OnLeave", function(self)
GameTooltip:Hide()
if self.Highlight then
self.Highlight:Hide()
end
end)
else
-- 如果已经有NEW标记确保它是可见的
button.newTexture:Show()
end
end
local ButtonIcon = nil
@ -861,7 +893,6 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
for _, button in ipairs(buttons) do
if button.itemData and button.itemData.id == itemID then
isCorrectPanel = true
break
end
end
@ -873,7 +904,6 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
for _, button in ipairs(buttons) do
if button.itemData and button.itemData.id == itemID then
isCorrectPanel = true
break
end
end
@ -895,18 +925,20 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
for _, button in ipairs(buttons) do
if button.itemData and button.itemData.id == itemID then
isCorrectPanel = true
break
end
end
end
if not isCorrectPanel then
-- 如果是物品类型且确实需要刷新
if itemType == "item" then
-- 尝试更新物品模型边框
self:RefreshItemModelBorders(itemID, isObtained)
end
return
end
-- 查找对应的按钮并更新状态
local updatedButton = nil
local buttonFound = false
@ -915,7 +947,6 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
if button.itemData and button.itemData.id == itemID then
buttonFound = true
-- 更新获取状态
button.itemData.obtained = isObtained
@ -928,8 +959,6 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
local text = button:GetRegions()
if text and text:GetObjectType() == "FontString" then
text:SetTextColor(isObtained and 1 or 0.5, isObtained and 1 or 0.5, isObtained and 1 or 0.5)
else
end
end
@ -969,19 +998,45 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
if not button.newTexture then
local newTexture = button:CreateTexture(nil, "OVERLAY")
newTexture:SetSize(32, 28)
newTexture:SetPoint("TOPRIGHT", button, "TOPRIGHT", -5, -5)
newTexture:SetPoint("TOPRIGHT", button, "TOPRIGHT", 10, 10)
newTexture:SetTexture("Interface\\AddOns\\SM_CollectionSystem\\Textures\\New_Icon")
button.newTexture = newTexture
-- 在鼠标悬停时隐藏"NEW"标记
button:HookScript("OnEnter", function(self)
if self.newTexture then
-- 先清除现有的鼠标处理函数,防止多次注册
button:SetScript("OnEnter", nil)
-- 使用SetScript而不是HookScript确保只有一个处理函数
button:SetScript("OnEnter", function(self)
print("[收藏系统] 按钮鼠标悬停:", itemType, itemID)
if self.newTexture and self.newTexture:IsShown() then
self.newTexture:Hide()
-- 从NewItems表中移除
if SM_Collections.NewItems and SM_Collections.NewItems[itemType] then
SM_Collections.NewItems[itemType][itemID] = nil
print("[收藏系统] 清除NEW标记:", itemType, itemID)
end
end
-- 显示提示信息
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
if self.itemData then
GameTooltip:SetText(self.itemData.name or "未知物品")
else
GameTooltip:SetText("收藏项目")
end
GameTooltip:Show()
-- 显示高亮
if self.Highlight then
self.Highlight:Show()
end
end)
button:SetScript("OnLeave", function(self)
GameTooltip:Hide()
if self.Highlight then
self.Highlight:Hide()
end
end)
else
-- 如果已经有NEW标记确保它是可见的
@ -1013,7 +1068,6 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
for i, btn in ipairs(obtainedButtons) do
if btn == button then
table.remove(obtainedButtons, i)
break
end
end
@ -1027,15 +1081,22 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
end
end
if not buttonFound then
end
-- 如果更新的是当前选中的项目,刷新右侧面板
if updatedButton then
local panel = self.CurrentPanel
OnItemClick(updatedButton, updatedButton.itemData, itemType, panel)
-- 如果我们需要更新右侧面板
if updatedButton and updatedButton.isSelected and self.MainFrame and self.MainFrame.RightSidePanel then
-- 更新状态文本
if self.MainFrame.RightSidePanel.statusText then
local statusText = isObtained and "|cFF00FF00已获得|r" or "|cFFFF0000未获得|r"
self.MainFrame.RightSidePanel.statusText:SetText(statusText)
end
-- 更新属性文本颜色
if self.MainFrame.RightSidePanel.attributesContainer then
for _, child in ipairs({self.MainFrame.RightSidePanel.attributesContainer:GetChildren()}) do
if child:IsObjectType("FontString") then
child:SetTextColor(isObtained and 1 or 0.5, isObtained and 1 or 0.5, isObtained and 1 or 0.5)
end
end
end
end
-- 通知玩家
@ -1064,6 +1125,89 @@ function SM_Collections:RefreshCollectionStatus(itemType, itemID, isObtained)
end
end
-- 新增函数:更新物品模型边框颜色
function SM_Collections:RefreshItemModelBorders(itemID, isObtained)
if not SM_ItemCollectionUI then return end
-- 遍历所有显示中的物品模型
local previewFrame = SM_ItemCollectionUI.previewFrame
if not previewFrame then return end
for _, child in ipairs({previewFrame:GetChildren()}) do
-- 检查是DressUpModel类型3D模型还是Frame类型图标
local isModelFrame = child:IsObjectType("DressUpModel")
local isIconFrame = child:IsObjectType("Frame") and not child:IsObjectType("DressUpModel")
if (isModelFrame or isIconFrame) and child.itemData and child.itemData.id == itemID then
-- 更新物品数据
child.itemData.obtained = isObtained
-- 更新边框颜色
if child.Border then
if isObtained then
child.Border:SetVertexColor(0, 1, 0, 1) -- 绿色边框
else
child.Border:SetVertexColor(1, 0, 0, 1) -- 红色边框
end
end
-- 如果是新获得的物品且已获得,添加"NEW"标记
if isObtained and self.NewItems and self.NewItems.item and self.NewItems.item[itemID] then
-- 使用SM_ItemCollectionUI的统一处理函数
if SM_ItemCollectionUI and SM_ItemCollectionUI.HandleNewItemIcon then
SM_ItemCollectionUI:HandleNewItemIcon(child, itemID)
end
end
-- 对于图标类物品(颈部、戒指、饰品),更新图标灰度
if isIconFrame then
for _, texture in ipairs({child:GetRegions()}) do
if texture:IsObjectType("Texture") and texture:GetDrawLayer() == "ARTWORK" then
-- 这可能是物品图标
if isObtained then
texture:SetDesaturated(false)
texture:SetVertexColor(1, 1, 1)
else
texture:SetDesaturated(true)
texture:SetVertexColor(0.7, 0.7, 0.7)
end
end
end
end
-- 更新高光颜色
local highlightTextures = { child:GetRegions() }
for _, region in ipairs(highlightTextures) do
if region:GetDrawLayer() == "HIGHLIGHT" then
if isObtained then
region:SetVertexColor(0, 1, 0, 0.7) -- 绿色高光
else
region:SetVertexColor(1, 0, 0, 0.7) -- 红色高光
end
end
end
-- 如果此模型当前被选中,更新右侧面板
if child.isSelected and SM_Collections.MainFrame and SM_Collections.MainFrame.RightSidePanel then
-- 更新状态文本
if SM_Collections.MainFrame.RightSidePanel.statusText then
local statusText = isObtained and "|cFF00FF00已获得|r" or "|cFFFF0000未获得|r"
SM_Collections.MainFrame.RightSidePanel.statusText:SetText(statusText)
end
-- 更新属性文本颜色
if SM_Collections.MainFrame.RightSidePanel.attributesContainer then
for _, child in ipairs({SM_Collections.MainFrame.RightSidePanel.attributesContainer:GetChildren()}) do
if child:IsObjectType("FontString") then
child:SetTextColor(isObtained and 1 or 0.5, isObtained and 1 or 0.5, isObtained and 1 or 0.5)
end
end
end
end
end
end
end
-- 重新排序收藏按钮
function SM_Collections:ResortCollectionButtons(scrollChild, itemType)
if not scrollChild or not scrollChild.buttons then
@ -1072,16 +1216,23 @@ function SM_Collections:ResortCollectionButtons(scrollChild, itemType)
local buttons = scrollChild.buttons
-- 按照已获得状态排序
-- 按照已获得状态排序:已获得的排在前面
table.sort(buttons, function(a, b)
if a.itemData.obtained and not b.itemData.obtained then
return true
elseif not a.itemData.obtained and b.itemData.obtained then
return false
else
-- 如果获得状态相同按ID排序
return a.itemData.id < b.itemData.id
if a.itemData and b.itemData then
if a.itemData.obtained and not b.itemData.obtained then
return true -- a排在b前面
elseif not a.itemData.obtained and b.itemData.obtained then
return false -- b排在a前面
else
-- 如果获得状态相同按名称或ID排序
if a.itemData.name and b.itemData.name then
return a.itemData.name < b.itemData.name
else
return a.itemData.id < b.itemData.id
end
end
end
return false
end)
-- 重新设置位置
@ -1099,6 +1250,11 @@ function SM_Collections:ResortCollectionButtons(scrollChild, itemType)
button:SetPoint("TOPLEFT", 30, -(i - 1) * (buttonHeight + spacing))
end
end
-- 强制滚动到顶部显示已获得的物品
if scrollChild:GetParent() and scrollChild:GetParent().ScrollBar then
scrollChild:GetParent().ScrollBar:SetValue(0)
end
end
-- 注册事件处理

View File

@ -6,7 +6,8 @@
#include "Common.h" // 基础类型定义uint32, uint8等
#include "DatabaseEnv.h" // 数据库操作相关
#include <unordered_map> // 哈希表容器,用于快速查找
#include <set> // 集合容器,用于存储不重复的收藏项
#include <set> // 集合容器,用于存储不重复的收藏项
#include <SpellAuraDefines.h>
// 前向声明Player类避免循环包含
class Player;
@ -34,58 +35,73 @@ enum CollectionCategory : uint8
enum CollectionBonusType : uint8
{
// 基础类型
BONUS_NONE = 0, // 空 - 无加成
BONUS_NONE = 0, // 空 - 无加成
// 生命值相关
BONUS_HEALTH = 1, // 生命值
BONUS_HEALTH = 1, // 生命值
// 基础属性相关
BONUS_ALL_STATS = 2, // 全属性
BONUS_STRENGTH = 3, // 力量
BONUS_AGILITY = 4, // 敏捷
BONUS_STAMINA = 5, // 耐力
BONUS_INTELLECT = 6, // 智力
BONUS_SPIRIT = 7, // 精神
BONUS_ALL_STATS = 2, // 全属性
BONUS_STRENGTH = 3, // 力量
BONUS_AGILITY = 4, // 敏捷
BONUS_STAMINA = 5, // 耐力
BONUS_INTELLECT = 6, // 智力
BONUS_SPIRIT = 7, // 精神
// 攻击强度相关
BONUS_MELEE_ATTACK_POWER = 8, // 近战攻击强度
BONUS_RANGED_ATTACK_POWER = 9, // 远程攻击强度
BONUS_MELEE_ATTACK_POWER = 8, // 近战攻击强度
BONUS_RANGED_ATTACK_POWER = 9, // 远程攻击强度
// 攻击速度相关
BONUS_CASTING_SPEED = 10, // 施法速度
BONUS_MELEE_HASTE = 11, // 近战攻击速度
BONUS_RANGED_HASTE = 12, // 远程攻击速度
BONUS_CASTING_SPEED = 10, // 施法速度
BONUS_MELEE_HASTE = 11, // 近战攻击速度
BONUS_RANGED_HASTE = 12, // 远程攻击速度
// 法术强度相关
BONUS_SPELL_POWER = 13, // 法术强度(通用)
BONUS_ARCANE_SPELL_POWER = 14, // 奥术法术强度
BONUS_FIRE_SPELL_POWER = 15, // 火焰法术强度
BONUS_FROST_SPELL_POWER = 16, // 冰霜法术强度
BONUS_HOLY_SPELL_POWER = 17, // 神圣法术强度
BONUS_SHADOW_SPELL_POWER = 18, // 暗影法术强度
BONUS_NATURE_SPELL_POWER = 19, // 自然法术强度
BONUS_SPELL_POWER = 13, // 法术强度(通用)
BONUS_ARCANE_SPELL_POWER = 14, // 奥术法术强度
BONUS_FIRE_SPELL_POWER = 15, // 火焰法术强度
BONUS_FROST_SPELL_POWER = 16, // 冰霜法术强度
BONUS_HOLY_SPELL_POWER = 17, // 神圣法术强度
BONUS_SHADOW_SPELL_POWER = 18, // 暗影法术强度
BONUS_NATURE_SPELL_POWER = 19, // 自然法术强度
// 治疗效果
BONUS_HEALING = 20, // 治疗效果
BONUS_HEALING = 20, // 治疗效果
// 造成伤害相关
BONUS_DAMAGE_ALL = 21, // 造成所有伤害
BONUS_DAMAGE_PHYSICAL = 22, // 造成物理伤害
BONUS_DAMAGE_MAGIC = 23, // 造成法术伤害
BONUS_DAMAGE_ARCANE = 24, // 造成奥术伤害
BONUS_DAMAGE_FIRE = 25, // 造成火焰伤害
BONUS_DAMAGE_FROST = 26, // 造成冰霜伤害
BONUS_DAMAGE_HOLY = 27, // 造成神圣伤害
BONUS_DAMAGE_SHADOW = 28, // 造成暗影伤害
BONUS_DAMAGE_NATURE = 29, // 造成自然伤害
BONUS_DAMAGE_ALL = 21, // 造成所有伤害
BONUS_DAMAGE_PHYSICAL = 22, // 造成物理伤害
BONUS_DAMAGE_MAGIC = 23, // 造成法术伤害
BONUS_DAMAGE_ARCANE = 24, // 造成奥术伤害
BONUS_DAMAGE_FIRE = 25, // 造成火焰伤害
BONUS_DAMAGE_FROST = 26, // 造成冰霜伤害
BONUS_DAMAGE_HOLY = 27, // 造成神圣伤害
BONUS_DAMAGE_SHADOW = 28, // 造成暗影伤害
BONUS_DAMAGE_NATURE = 29, // 造成自然伤害
// 受到伤害相关
BONUS_DAMAGE_TAKEN_ALL = 30, // 受到所有伤害(减伤)
BONUS_DAMAGE_TAKEN_ALL = 30, // 受到所有伤害(减伤)
// 暴击相关
BONUS_CRIT_DAMAGE = 31, // 造成暴击伤害
BONUS_CRIT_DAMAGE = 31, // 造成暴击伤害
MAX_BONUS_TYPE // 最大值标记
// 添加缺失的能量类型
BONUS_MANA = 32, // 法力值上限
BONUS_RAGE = 33, // 怒气值上限
BONUS_FOCUS = 34, // 集中值上限
BONUS_ENERGY = 35, // 能量值上限
// 添加抗性类型
BONUS_ARMOR = 36, // 护甲值
BONUS_HOLY_RESISTANCE = 37, // 神圣抗性
BONUS_FIRE_RESISTANCE = 38, // 火焰抗性
BONUS_NATURE_RESISTANCE = 39, // 自然抗性
BONUS_FROST_RESISTANCE = 40, // 冰霜抗性
BONUS_SHADOW_RESISTANCE = 41, // 暗影抗性
BONUS_ARCANE_RESISTANCE = 42, // 奥术抗性
MAX_BONUS_TYPE // 最大值标记
};
/**
@ -269,6 +285,23 @@ public:
*/
void ApplyCollectionBonuses(Player* player);
/**
*
* @param player
* @param config
* @param apply true=false=
*/
void ApplyCollectionBonus(Player* player, CollectionConfig const* config, bool apply = true);
/**
*
* @param player
* @param category
* @param itemId ID
* @return
*/
bool RemoveFromCollection(Player* player, CollectionCategory category, uint32 itemId);
/**
*
* @param sender
@ -398,6 +431,13 @@ public:
*/
bool IsCardCreature(uint32 creatureId) const;
/**
*
* @param itemId EntryD
* @return
*/
bool IsCollectibleItem(uint32 itemId) const;
/**
*
* @param player
@ -409,20 +449,21 @@ public:
*/
void Initialize();
/**
*
* @param player
* @param mountId 0ID的坐骑
* @return
*/
bool SummonMountLogic(Player* player, uint32 mountId = 0);
private:
/**
*
* @param category
* @return
*/
std::string GetCategoryString(CollectionCategory category) const;
/**
*
* @param player
* @param config
*/
void ApplyCollectionBonus(Player* player, CollectionConfig const* config);
std::string GetCategoryString(CollectionCategory category) const;
// 存储所有收藏配置key是(分类+物品ID)的组合value是配置信息
std::unordered_map<uint64, CollectionConfig> _collectionConfigs;
@ -478,6 +519,29 @@ private:
* @return
*/
std::string GetItemTypeString(CollectionItemType itemType) const;
/**
* Aura
* @param player
* @param auraType Aura
* @param value
* @param misc MiscValue
* @param miscB MiscValueB
*/
void ApplyCollectionAura(Player* player, AuraType auraType, float value, int32 misc, int32 miscB, bool apply);
/**
* Aura
* @param player
*/
void RemoveAllCollectionAuras(Player* player);
public:
/**
*
* @param player
*/
void ReapplyAllCollectionBonuses(Player* player);
};
// 定义全局访问宏,方便在其他地方调用收藏管理器

View File

@ -95,10 +95,10 @@ void CollectionMgr::LoadCollectionConfigs()
_collectionConfigs.clear();
// 先加载属性加成表
// 先加载属性加成表
LoadAttributeBonuses();
// 再加载各个收藏配置表
// 再加载各个收藏配置表
LoadMountConfigs();
LoadPetConfigs();
LoadCardConfigs();
@ -270,11 +270,11 @@ bool CollectionMgr::AddToCollection(Player* player, CollectionCategory category,
// 添加收藏
_playerCollections[playerGuid].collections.insert(std::make_pair(category, itemId));
// 应用属性加成
auto configItr = _collectionConfigs.find(key);
if (configItr != _collectionConfigs.end())
// 应用属性加成 - 使用默认的 apply=true
CollectionConfig const* config = GetCollectionConfig(category, itemId);
if (config)
{
ApplyCollectionBonus(player, &configItr->second);
ApplyCollectionBonus(player, config, true); // 明确指定 apply=true
}
// 发送通知给客户端
@ -303,7 +303,7 @@ bool CollectionMgr::AddToCollection(Player* player, CollectionCategory category,
SendAddonMessageToPlayer(player, "SM_S_COLLECTION_UPDATE", message.str(), CHAT_MSG_WHISPER);
// 记录日志
LOG_INFO("server.loading", "Player {} ({}) added item {} to collection category %u", player->GetName().c_str(), playerGuid, itemId, static_cast<uint32>(category));
LOG_INFO("server.loading", "Player {} ({}) added item {} to collection category {}", player->GetName().c_str(), playerGuid, itemId, static_cast<uint32>(category));
return true;
}
@ -331,7 +331,7 @@ void CollectionMgr::SavePlayerCollections(Player* player)
if (itr == _playerCollections.end())
return;
// 删除现有数据
// 删除现有数据
std::string deleteQuery = "DELETE FROM 玩家收藏数据 WHERE 玩家GUID = {}";
CharacterDatabase.Execute(deleteQuery.c_str(), playerGuid);
@ -344,47 +344,426 @@ void CollectionMgr::SavePlayerCollections(Player* player)
}
}
void CollectionMgr::ApplyCollectionBonus(Player* player, CollectionConfig const* config)
/**
*
* @param player -
* @param config
* @param apply true=false=
*/
void CollectionMgr::ApplyCollectionBonus(Player* player, CollectionConfig const* config, bool apply)
{
// 确保只对玩家应用加成
if (!player || !player->IsPlayer())
return;
// 遍历配置中的所有属性加成最多5个
for (uint8 i = 0; i < 5; ++i)
{
CollectionBonus const& bonus = config->bonuses[i];
// 跳过无效的属性加成
if (bonus.type == BONUS_NONE || bonus.value == 0.0f)
continue;
// 根据属性类型应用不同的修改器
// 根据数值类型确定修饰符类型:百分比或固定值
UnitModifierType modType = bonus.valueType == VALUE_PERCENTAGE ? BASE_PCT : BASE_VALUE;
bool isPercentage = bonus.valueType == VALUE_PERCENTAGE;
switch (bonus.type)
{
// ========== 基础属性(通过 UnitMods 处理)==========
case BONUS_HEALTH:
if (bonus.valueType == VALUE_PERCENTAGE)
player->HandleStatModifier(UNIT_MOD_HEALTH, BASE_PCT, bonus.value, true);
else
player->HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, bonus.value, true);
// 生命值加成
player->HandleStatModifier(UNIT_MOD_HEALTH, modType, bonus.value, apply);
break;
case BONUS_ALL_STATS:
// 全属性加成 - 对所有基础属性(力量、敏捷、耐力、智力、精神)生效
for (int32 stat = STAT_STRENGTH; stat < MAX_STATS; ++stat)
{
if (bonus.valueType == VALUE_PERCENTAGE)
player->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + stat), BASE_PCT, bonus.value, true);
else
player->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + stat), BASE_VALUE, bonus.value, true);
player->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + stat), modType, bonus.value, apply);
player->ApplyStatBuffMod(Stats(stat), bonus.value, apply);
}
break;
case BONUS_STRENGTH:
if (bonus.valueType == VALUE_PERCENTAGE)
player->HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_PCT, bonus.value, true);
else
player->HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, bonus.value, true);
// 力量属性加成
player->HandleStatModifier(UNIT_MOD_STAT_STRENGTH, modType, bonus.value, apply);
player->ApplyStatBuffMod(STAT_STRENGTH, bonus.value, apply);
break;
// 继续为其他属性类型添加实现...
case BONUS_AGILITY:
// 敏捷属性加成
player->HandleStatModifier(UNIT_MOD_STAT_AGILITY, modType, bonus.value, apply);
player->ApplyStatBuffMod(STAT_AGILITY, bonus.value, apply);
break;
case BONUS_STAMINA:
// 耐力属性加成
player->HandleStatModifier(UNIT_MOD_STAT_STAMINA, modType, bonus.value, apply);
player->ApplyStatBuffMod(STAT_STAMINA, bonus.value, apply);
break;
case BONUS_INTELLECT:
// 智力属性加成
player->HandleStatModifier(UNIT_MOD_STAT_INTELLECT, modType, bonus.value, apply);
player->ApplyStatBuffMod(STAT_INTELLECT, bonus.value, apply);
break;
case BONUS_SPIRIT:
// 精神属性加成
player->HandleStatModifier(UNIT_MOD_STAT_SPIRIT, modType, bonus.value, apply);
player->ApplyStatBuffMod(STAT_SPIRIT, bonus.value, apply);
break;
case BONUS_MELEE_ATTACK_POWER:
// 近战攻击强度加成
player->HandleStatModifier(UNIT_MOD_ATTACK_POWER, modType, bonus.value, apply);
break;
case BONUS_RANGED_ATTACK_POWER:
// 远程攻击强度加成
player->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, modType, bonus.value, apply);
break;
// ========== 通过 Aura 系统处理的复杂属性 ==========
case BONUS_CASTING_SPEED:
// 施法速度加成 - 通过 Aura 系统
ApplyCollectionAura(player, SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK, bonus.value, 0, 0, apply);
break;
case BONUS_MELEE_HASTE:
// 近战攻击速度加成 - 通过 Aura 系统
ApplyCollectionAura(player, SPELL_AURA_MOD_MELEE_HASTE, bonus.value, 0, 0, apply);
break;
case BONUS_RANGED_HASTE:
// 远程攻击速度加成 - 通过 Aura 系统
ApplyCollectionAura(player, SPELL_AURA_MOD_RANGED_HASTE, bonus.value, 0, 0, apply);
break;
case BONUS_SPELL_POWER:
// 法术强度加成 - 通过 Aura 系统
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_MAGIC, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_MAGIC, 0, apply);
break;
case BONUS_ARCANE_SPELL_POWER:
// 奥术法术强度加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_ARCANE, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_ARCANE, 0, apply);
break;
case BONUS_FIRE_SPELL_POWER:
// 火焰法术强度加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_FIRE, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_FIRE, 0, apply);
break;
case BONUS_FROST_SPELL_POWER:
// 冰霜法术强度加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_FROST, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_FROST, 0, apply);
break;
case BONUS_HOLY_SPELL_POWER:
// 神圣法术强度加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_HOLY, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_HOLY, 0, apply);
break;
case BONUS_SHADOW_SPELL_POWER:
// 暗影法术强度加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_SHADOW, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_SHADOW, 0, apply);
break;
case BONUS_NATURE_SPELL_POWER:
// 自然法术强度加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_NATURE, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_NATURE, 0, apply);
break;
case BONUS_HEALING:
// 治疗效果加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT, bonus.value, SPELL_SCHOOL_MASK_MAGIC, -1000, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_HEALING, bonus.value, SPELL_SCHOOL_MASK_MAGIC, 0, apply);
break;
case BONUS_DAMAGE_ALL:
// 造成所有伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_ALL, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_ALL, 0, apply);
break;
case BONUS_DAMAGE_PHYSICAL:
// 造成物理伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_NORMAL, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_NORMAL, 0, apply);
break;
case BONUS_DAMAGE_MAGIC:
// 造成法术伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_MAGIC, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_MAGIC, 0, apply);
break;
case BONUS_DAMAGE_ARCANE:
// 造成奥术伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_ARCANE, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_ARCANE, 0, apply);
break;
case BONUS_DAMAGE_FIRE:
// 造成火焰伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_FIRE, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_FIRE, 0, apply);
break;
case BONUS_DAMAGE_FROST:
// 造成冰霜伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_FROST, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_FROST, 0, apply);
break;
case BONUS_DAMAGE_HOLY:
// 造成神圣伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_HOLY, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_HOLY, 0, apply);
break;
case BONUS_DAMAGE_SHADOW:
// 造成暗影伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_SHADOW, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_SHADOW, 0, apply);
break;
case BONUS_DAMAGE_NATURE:
// 造成自然伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, bonus.value, SPELL_SCHOOL_MASK_NATURE, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_DONE, bonus.value, SPELL_SCHOOL_MASK_NATURE, 0, apply);
break;
case BONUS_DAMAGE_TAKEN_ALL:
// 受到所有伤害加成
if (isPercentage)
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, bonus.value, SPELL_SCHOOL_MASK_ALL, 0, apply);
else
ApplyCollectionAura(player, SPELL_AURA_MOD_DAMAGE_TAKEN, bonus.value, SPELL_SCHOOL_MASK_ALL, 0, apply);
break;
case BONUS_CRIT_DAMAGE:
// 造成暴击伤害加成
ApplyCollectionAura(player, SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, bonus.value, SPELL_SCHOOL_MASK_ALL, 0, apply);
break;
default:
LOG_WARN("collection", "玩家 {} 遇到未知的属性加成类型: {}",
player->GetName(), static_cast<uint32>(bonus.type));
break;
}
}
// 应用所有属性加成后,更新玩家的统计信息
player->UpdateAllStats();
player->UpdateSpellDamageAndHealingBonus();
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO,
"已为玩家 {} {} 收藏属性加成", player->GetName(), apply ? "应用" : "移除");
}
/**
*
* @param player
* @param category
* @param itemId ID
* @return
*/
bool CollectionMgr::RemoveFromCollection(Player* player, CollectionCategory category, uint32 itemId)
{
if (!player)
return false;
uint32 playerGuid = player->GetGUID().GetCounter();
// 检查玩家数据是否存在
auto playerItr = _playerCollections.find(playerGuid);
if (playerItr == _playerCollections.end())
return false;
// 检查是否在收藏中
auto collectionPair = std::make_pair(category, itemId);
auto collectionItr = playerItr->second.collections.find(collectionPair);
if (collectionItr == playerItr->second.collections.end())
return false;
// 从收藏中移除
playerItr->second.collections.erase(collectionItr);
// 发送通知给客户端
std::string itemType;
switch (category)
{
case COLLECTION_MOUNT:
itemType = "mount";
break;
case COLLECTION_PET:
itemType = "companion";
break;
case COLLECTION_CARD:
itemType = "card";
break;
case COLLECTION_ITEM:
itemType = "item";
break;
default:
itemType = "unknown";
}
std::ostringstream message;
message << itemType << "|" << itemId << "|0"; // 0表示移除
SendAddonMessageToPlayer(player, "SM_S_COLLECTION_UPDATE", message.str(), CHAT_MSG_WHISPER);
// 记录日志
LOG_INFO("server.loading", "Player {} ({}) removed item {} from collection category {}",
player->GetName().c_str(), playerGuid, itemId, static_cast<uint32>(category));
return true;
}
/**
* Aura
* @param player
* @param auraType Aura
* @param value
* @param misc MiscValue
* @param miscB MiscValueB
* @param apply true=Aurafalse=Aura
*/
void CollectionMgr::ApplyCollectionAura(Player* player, AuraType auraType, float value, int32 misc, int32 miscB, bool apply)
{
if (!player || !player->IsPlayer())
return;
// 创建一个特殊的收藏系统法术ID
uint32 collectionSpellId = 999999; // 使用一个不会与现有法术冲突的ID
if (apply)
{
// 获取或创建SpellInfo
SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->GetSpellInfo(collectionSpellId));
if (!spellInfo)
{
LOG_WARN("server.loading", "收藏系统Aura法术 {} 不存在,需要动态创建", collectionSpellId);
return;
}
// 修改SpellInfo的效果来匹配我们需要的Aura
spellInfo->Effects[0].Effect = SPELL_EFFECT_APPLY_AURA;
spellInfo->Effects[0].ApplyAuraName = auraType;
spellInfo->Effects[0].BasePoints = static_cast<int32>(value);
spellInfo->Effects[0].MiscValue = misc;
spellInfo->Effects[0].MiscValueB = miscB;
// 应用Aura到玩家
if (Aura* aura = Aura::TryRefreshStackOrCreate(spellInfo, MAX_EFFECT_MASK, player, player))
{
// 在这里设置持续时间
aura->SetDuration(-1); // 永久持续
aura->SetMaxDuration(-1);
aura->ApplyForTargets();
LOG_DEBUG("server.loading", "为玩家 {} 应用收藏Aura: 类型={}, 数值={}",
player->GetName(), static_cast<uint32>(auraType), value);
}
}
else // 移除 Aura
{
// 移除特定类型的收藏Aura
// 遍历玩家身上所有应用的光环
for (Unit::AuraApplicationMap::iterator itr = player->GetAppliedAuras().begin(); itr != player->GetAppliedAuras().end(); ++itr)
{
AuraApplication* aurApp = itr->second;
Aura* aura = aurApp->GetBase();
// 检查是否是收藏系统创建的Aura并且Aura类型匹配
if (aura->GetId() == collectionSpellId && aura->GetSpellInfo()->Effects[0].ApplyAuraName == auraType)
{
player->RemoveAura(aurApp);
LOG_DEBUG("server.loading", "从玩家 {} 移除收藏Aura: 类型={}, 数值={}",
player->GetName(), static_cast<uint32>(auraType), value);
return; // 假设每个类型只有一个Aura找到并移除后即可返回
}
}
}
}
/**
* Aura
* @param player
*/
void CollectionMgr::RemoveAllCollectionAuras(Player* player)
{
if (!player || !player->IsPlayer())
return;
// 移除所有收藏系统相关的Aura
uint32 collectionSpellId = 999999;
player->RemoveAurasDueToSpell(collectionSpellId);
LOG_DEBUG("server.loading", "已移除玩家 {} 的所有收藏系统Aura", player->GetName());
}
/**
*
*
* @param player
*/
void CollectionMgr::ReapplyAllCollectionBonuses(Player* player)
{
if (!player || !player->IsPlayer())
return;
// 先移除所有现有的收藏Aura
RemoveAllCollectionAuras(player);
// 重新应用所有收藏的属性加成
ApplyCollectionBonuses(player);
LOG_DEBUG("server.loading", "已重新应用玩家 {} 的所有收藏属性加成", player->GetName());
}
void CollectionMgr::LoadPlayerCollections(Player* player)
@ -409,11 +788,11 @@ void CollectionMgr::LoadPlayerCollections(Player* player)
uint32 itemId = fields[1].Get<uint32>();
if (category >= MAX_COLLECTION_CATEGORY)
{
LOG_ERROR("sql.sql", "Player {} has invalid collection category, skipping.", playerGuid);
LOG_ERROR("server.loading", "Player {} has invalid collection category, skipping.", playerGuid);
continue;
}
// 检查配置是否存在(使用新的配置结构)
// 检查配置是否存在(使用新的配置结构)
bool configExists = false;
switch (category)
{
@ -433,7 +812,7 @@ void CollectionMgr::LoadPlayerCollections(Player* player)
if (!configExists)
{
LOG_WARN("sql.sql", "Player {} has collection item {} in category {} but no config exists, skipping.", playerGuid, itemId, uint32(category));
LOG_WARN("server.loading", "Player {} has collection item {} in category {} but no config exists, skipping.", playerGuid, itemId, uint32(category));
continue;
}
_playerCollections[playerGuid].collections.insert(std::make_pair(category, itemId));
@ -549,7 +928,7 @@ CollectionBonusType CollectionMgr::GetBonusTypeFromString(std::string const& bon
CollectionValueType CollectionMgr::GetValueTypeFromString(std::string const& valueStr) const
{
if (valueStr == "固定值") return VALUE_FIXED;
if (valueStr == "百分比(加法叠加)") return VALUE_PERCENTAGE;
if (valueStr == "百分比") return VALUE_PERCENTAGE;
return VALUE_FIXED;
}
@ -643,7 +1022,7 @@ void CollectionMgr::HandleAddonRequest(Player* player, const std::string& prefix
}
else if (command == "GET_ITEMS")
{
SendItemsData(player);
SendItemsData(player); // 不要在插件中发送 改为玩家登录发送数据
}
else if (command == "GET_ATTRIBUTES")
{
@ -792,10 +1171,17 @@ void CollectionMgr::SendItemsData(Player* player)
{
if (!player)
return;
// 临时召唤 NPC 2334持续 0.1 秒 (100 毫秒)
if (TempSummon* summon = player->SummonCreature(2334, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 100))
{
// 召唤成功的处理逻辑(如果需要)
}
std::ostringstream data;
uint32 count = 0;
// 遍历物品配置
// 遍历物品配置
for (auto const& pair : _itemConfigs)
{
uint32 itemId = pair.first;
@ -813,15 +1199,14 @@ void CollectionMgr::SendItemsData(Player* player)
data << (hasCollected ? "1" : "0") << "|";
data << itemTypeStr;
count++;
if (count % 10 == 0)
{
SendAddonMessageToPlayer(player, "SM_S_COLLECTIONS_ITEMS", data.str(), CHAT_MSG_WHISPER);
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO, "SM_S_COLLECTIONS_ITEMS1{} ", data.str());
SendAddonMessageToPlayer(player, "SM_S_COLLECTIONS_ITEMS", data.str(), CHAT_MSG_WHISPER);
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO, "SM_S_COLLECTIONS_ITEMS1{} ", data.str());
data.str("");
data.clear();
data.str("");
data.clear();
}
}
if (!data.str().empty())
{
@ -1164,6 +1549,12 @@ bool CollectionMgr::IsCardCreature(uint32 creatureId) const
return _cardConfigs.find(creatureId) != _cardConfigs.end();
}
//检查是否是物品收藏中的物品
bool CollectionMgr::IsCollectibleItem(uint32 itemId) const
{
return _itemConfigs.find(itemId) != _itemConfigs.end();
}
// 检查并更新所有收藏
void CollectionMgr::CheckAndUpdateAllCollections(Player* player)
{
@ -1201,3 +1592,92 @@ void CollectionMgr::CheckAndUpdateAllCollections(Player* player)
// 在CollectionMgr的初始化函数中调用钩子注册
void CollectionMgr::Initialize(){ }
bool CollectionMgr::SummonMountLogic(Player* player, uint32 mountId)
{
if (!player)
return false;
uint32 mountSpellIdToSummon = 0;
std::string summonType = "";
if (mountId == 0) // 随机召唤
{
summonType = "随机";
std::vector<uint32> collectedMounts;
// 遍历所有坐骑配置
for (auto const& pair : _mountConfigs)
{
uint32 mountSpellId = pair.first;
// 检查玩家是否已收集该坐骑
if (HasInCollection(player, COLLECTION_MOUNT, mountSpellId))
{
collectedMounts.push_back(mountSpellId);
}
}
if (collectedMounts.empty())
{
ChatHandler(player->GetSession()).PSendSysMessage("您还没有收集任何坐骑!");
LOG_INFO("collection", "玩家 {} 尝试随机召唤坐骑,但没有已收集的坐骑。", player->GetName());
return false;
}
// 随机选择一个已收集的坐骑
uint32 randomIndex = urand(0, collectedMounts.size() - 1);
mountSpellIdToSummon = collectedMounts[randomIndex];
}
else // 召唤指定坐骑
{
summonType = "指定";
mountSpellIdToSummon = mountId;
// 检查玩家是否已收集该坐骑
if (!HasInCollection(player, COLLECTION_MOUNT, mountSpellIdToSummon))
{
ChatHandler(player->GetSession()).PSendSysMessage("您尚未收集该坐骑!");
LOG_INFO("collection", "玩家 {} 尝试召唤坐骑 {},但未收集。", player->GetName(), mountSpellIdToSummon);
return false;
}
}
// 获取坐骑的SpellInfo
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(mountSpellIdToSummon);
if (!spellInfo)
{
LOG_ERROR("collection", "{}召唤坐骑失败找不到法术ID {} 的SpellInfo", summonType, mountSpellIdToSummon);
ChatHandler(player->GetSession()).PSendSysMessage("召唤坐骑失败请联系GM。");
return false;
}
// 检查玩家当前是否已骑乘,如果已骑乘则先下马
if (player->IsMounted())
{
player->Dismount();
LOG_INFO("collection", "玩家 {} 已下马,准备{}召唤坐骑。", player->GetName(), summonType);
}
// 移除所有现有的SPELL_AURA_MOUNTED光环
// 这确保了旧的坐骑buff图标被清除并且不会干扰新的坐骑光环应用
player->RemoveAurasByType(SPELL_AURA_MOUNTED);
// 召唤坐骑
// 将 triggered 参数设置为 true表示由服务器触发忽略施法时间、冷却等限制。
SpellCastResult castResult = player->CastSpell(player, mountSpellIdToSummon, false);
// 记录施法结果
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO, "玩家 {} 尝试{}召唤坐骑 {},施法结果: {}",
player->GetName(), summonType, mountSpellIdToSummon, static_cast<uint32>(castResult));
if (castResult == SPELL_CAST_OK)
{
ChatHandler(player->GetSession()).PSendSysMessage("您{}召唤了坐骑: {}", summonType, spellInfo->SpellName[0]);
LOG_INFO("collection", "玩家 {} 成功{}召唤了坐骑 {}", player->GetName(), summonType, mountSpellIdToSummon);
}
else
{
ChatHandler(player->GetSession()).PSendSysMessage("{}召唤坐骑失败,施法结果: {}", summonType, static_cast<uint32>(castResult));
LOG_ERROR("collection", "玩家 {} {}召唤坐骑 {} 失败,施法结果: {}", player->GetName(), summonType, mountSpellIdToSummon, static_cast<uint32>(castResult));
}
return castResult == SPELL_CAST_OK;
}

View File

@ -24,6 +24,9 @@ public:
// 直接更新所有收藏类型,不使用延迟
sCollectionMgr->CheckAndUpdateAllCollections(player);
// 发送物品数据
sCollectionMgr->SendItemsData(player);
// 告知玩家收藏系统已加载
// player->WhisperToSelf("收藏系统: 已加载您的收藏数据");
}
@ -67,6 +70,54 @@ public:
}
}
// 忘却技能时移除坐骑和小伙伴收藏的属性加成
void OnPlayerForgotSpell(Player* player, uint32 spellId) override
{
if (!player || !player->IsInWorld())
return;
// 检查是否是坐骑技能
if (sCollectionMgr->IsMountSpell(spellId))
{
// 获取配置并移除属性加成
CollectionConfig const* config = sCollectionMgr->GetCollectionConfig(COLLECTION_MOUNT, spellId);
if (config)
{
// 移除属性加成
sCollectionMgr->ApplyCollectionBonus(player, config, false);
}
// 从收藏数据中移除
sCollectionMgr->RemoveFromCollection(player, COLLECTION_MOUNT, spellId);
// 发送更新数据到客户端
sCollectionMgr->SendMountsData(player);
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO,
"玩家 {} 忘却坐骑法术 {},已移除收藏和属性加成", player->GetName(), spellId);
}
// 检查是否是小伙伴技能
else if (sCollectionMgr->IsCompanionSpell(spellId))
{
// 获取配置并移除属性加成
CollectionConfig const* config = sCollectionMgr->GetCollectionConfig(COLLECTION_PET, spellId);
if (config)
{
// 移除属性加成
sCollectionMgr->ApplyCollectionBonus(player, config, false);
}
// 从收藏数据中移除
sCollectionMgr->RemoveFromCollection(player, COLLECTION_PET, spellId);
// 发送更新数据到客户端
sCollectionMgr->SendCompanionsData(player);
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO,
"玩家 {} 忘却小伙伴法术 {},已移除收藏和属性加成", player->GetName(), spellId);
}
}
// Called when a player kills a creature
void OnPlayerCreatureKill(Player* killer, Creature* killed) override
{
@ -104,6 +155,41 @@ public:
}
}
}
// 玩家获取物品时检查物品收藏
void OnPlayerStoreNewItem(Player* player, Item* item, uint32 count) override
{
if (!player || !player->IsInWorld() || !item)
return;
uint32 itemId = item->GetEntry();
// 检查是否是物品收藏
if (sCollectionMgr->IsCollectibleItem(itemId)) // 您需要实现 CollectionMgr::IsCollectibleItem 方法
{
// 检查是否已经收藏
bool alreadyCollected = sCollectionMgr->HasInCollection(player, COLLECTION_ITEM, itemId);
bool added = sCollectionMgr->AddToCollection(player, COLLECTION_ITEM, itemId);
if (added && !alreadyCollected)
{
sLog->outMessage("server", LogLevel::LOG_LEVEL_INFO, "玩家 {} 获得新物品 {},已添加到物品收藏。", player->GetName(), itemId);
// 发送实时更新通知
std::ostringstream updateMsg;
updateMsg << "item|" << itemId << "|1";
sCollectionMgr->SendAddonMessageToPlayer(player, "SM_S_COLLECTION_UPDATE", updateMsg.str(), CHAT_MSG_WHISPER);
// 通知玩家
std::string itemName = item->GetTemplate()->Name1;
std::string message = "您已收集到新物品: " + itemName;
ChatHandler(player->GetSession()).SendNotification(message.c_str());
// 发送更新数据到客户端
sCollectionMgr->SendItemsData(player);
}
}
}
};
class CollectionSystem_WorldScript : public WorldScript