diff --git a/Data/Camera.lua b/Data/Camera.lua index ee21610..2580e66 100644 --- a/Data/Camera.lua +++ b/Data/Camera.lua @@ -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 }, -- 套装详情 diff --git a/ItemCollection/SM_ItemCollectionUI.lua b/ItemCollection/SM_ItemCollectionUI.lua index 2fff3e6..35d02c8 100644 --- a/ItemCollection/SM_ItemCollectionUI.lua +++ b/ItemCollection/SM_ItemCollectionUI.lua @@ -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 -- 清除所有按钮 diff --git a/SM_CollectionSystem.lua b/SM_CollectionSystem.lua index 672b538..0bc0885 100644 --- a/SM_CollectionSystem.lua +++ b/SM_CollectionSystem.lua @@ -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 diff --git a/SM_Events.lua b/SM_Events.lua index b07852e..e3ea8be 100644 --- a/SM_Events.lua +++ b/SM_Events.lua @@ -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 diff --git a/SM_UI_SplitPanel.lua b/SM_UI_SplitPanel.lua index 247337d..d0e4332 100644 --- a/SM_UI_SplitPanel.lua +++ b/SM_UI_SplitPanel.lua @@ -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 -- 注册事件处理 diff --git a/mod-Collection/include/CollectionMgr.h b/mod-Collection/include/CollectionMgr.h index d380826..471b708 100644 --- a/mod-Collection/include/CollectionMgr.h +++ b/mod-Collection/include/CollectionMgr.h @@ -6,7 +6,8 @@ #include "Common.h" // 基础类型定义(uint32, uint8等) #include "DatabaseEnv.h" // 数据库操作相关 #include // 哈希表容器,用于快速查找 -#include // 集合容器,用于存储不重复的收藏项 +#include // 集合容器,用于存储不重复的收藏项 +#include // 前向声明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 如果为0,则随机召唤;否则召唤指定ID的坐骑 + * @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 _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); }; // 定义全局访问宏,方便在其他地方调用收藏管理器 diff --git a/mod-Collection/src/CollectionMgr.cpp b/mod-Collection/src/CollectionMgr.cpp index fe2c3af..df6672d 100644 --- a/mod-Collection/src/CollectionMgr.cpp +++ b/mod-Collection/src/CollectionMgr.cpp @@ -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(category)); + LOG_INFO("server.loading", "Player {} ({}) added item {} to collection category {}", player->GetName().c_str(), playerGuid, itemId, static_cast(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(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(category)); + + return true; +} + +/** + * 为玩家应用或移除收藏系统的 Aura 效果 + * @param player 目标玩家 + * @param auraType Aura 类型 + * @param value 属性值 + * @param misc MiscValue 参数 + * @param miscB MiscValueB 参数 + * @param apply true=应用Aura,false=移除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(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(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(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(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(); 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 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(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(castResult)); + LOG_ERROR("collection", "玩家 {} {}召唤坐骑 {} 失败,施法结果: {}", player->GetName(), summonType, mountSpellIdToSummon, static_cast(castResult)); + } + + return castResult == SPELL_CAST_OK; +} diff --git a/mod-Collection/src/CollectionScript.cpp b/mod-Collection/src/CollectionScript.cpp index 41b8280..c9683cd 100644 --- a/mod-Collection/src/CollectionScript.cpp +++ b/mod-Collection/src/CollectionScript.cpp @@ -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