local Achievement_Model = {} local private = {} local EventSystem = require('Event/EventSystem') local achievement = require 'Proto.achievement_pb' local AchievementLimitedCount = DataSheetManager.ConstData:GetDataByID("AchievementRecentlyItemNum").IntValue local SortType = { None = 0, RecentlyDoing = 1, RecentlyComplete = 2 } local FirstMainType = 1 local SummaryChildType = 2 local RecentlyCompleteChildType = 3 local RecentlyDoingChildType = 4 -------------------------- 系统函数 --------------------------- function Achievement_Model.OnStartup() NetManager.RegisterMsgCB(MessageEnums.MessageAchievementLevelReward, private.ResponseClaimAchievementLevelReward) NetManager.RegisterMsgCB(MessageEnums.MessageAchievementReward, private.ResponseClaimAchievementReward) NetManager.RegisterMsgCB(MessageEnums.MessageAchievementChange, nil) NetManager.RegisterMsgCB(MessageEnums.MessageAllAchievementReward, private.ResponseClaimAchievementReward) private.achievementShowTypes = {} -- 特殊页签 local specialTypeChildNodes = { private.CreateChildNode(SummaryChildType, "IDS_Achievement_Show", FirstMainType), private.CreateChildNode(RecentlyCompleteChildType, "IDS_Achievement_RecentlyReached", FirstMainType), private.CreateChildNode(RecentlyDoingChildType, "IDS_Achievement_ComingSoon", FirstMainType) } table.insert(private.achievementShowTypes, private.CreateParentNode(FirstMainType, "IDS_Achievement_Summary", specialTypeChildNodes)) local typeData = nil for i, v in DataSheetManager.AchievementType:GetEnumerator() do typeData = nil for _, data in ipairs(private.achievementShowTypes) do if v.MainType == data.typeID then typeData = data break end end if not typeData then table.insert(private.achievementShowTypes, private.CreateParentNode(v.MainType, v.TypeName, {})) else table.insert(typeData.childNodes, private.CreateChildNode(v.ChildType, v.TypeName, typeData.typeID)) end end private.curAchievementLevel = 0 private.curAchievementScore = 0 private.completeAchievementCount = 0 private.claimAchievementCount = 0 private.amountAchievementCount = DataSheetManager.AchievementItem:GetDataCount() private.claimLevelRewardDict = LuaExt.CreateTable(DataSheetManager.AchievementData:GetDataCount()) private.achievementDict = LuaExt.CreateTable(0, DataSheetManager.AchievementItem:GetDataCount()) private.sortedAchievenments = LuaExt.CreateTable(0, #private.achievementShowTypes - 1) private.recentlyDoingAchievements = LuaExt.CreateTable(AchievementLimitedCount) private.recentlyCompleteAchievements = LuaExt.CreateTable(AchievementLimitedCount) end function Achievement_Model.OnShutdown() private.achievementShowTypes = nil private.claimLevelRewardDict = nil private.achievementDict = nil private.sortedAchievenments = nil end --------------------------------------------------------------- -- 处理协议结构 function Achievement_Model.SetAchievementInfos(achievementSystemInfo, isInit) if not IsServerDataNotNil(achievementSystemInfo) then return end -- logging.error(LTAG.ERROR, "成就推送", achievementSystemInfo) private.SetCurAchievementLevel(achievementSystemInfo.level) private.SetClaimLevelRewardDict(achievementSystemInfo.rewardLevels) if achievementSystemInfo.infos and #achievementSystemInfo.infos > 0 then for _, info in ipairs (achievementSystemInfo.infos) do -- 新成就 if not private.achievementDict[info.achievementId] then private.InitAchievement(info) else private.SetAchievementInfo(private.achievementDict[info.achievementId], info) end end if isInit then -- 子类排序 for _, mainAchievement in pairs (private.sortedAchievenments) do for _, childAchievement in pairs (mainAchievement.childTypeDict) do if #childAchievement.achievementDatas > 0 then table.sort(childAchievement.achievementDatas, private.SortSortedAchievements) end end end end -- 重置最近分类 private.CheckRecentlyAchievements() EventSystem.Dispatch(EventID.UI_AchievementInfo_UpdateItems) end EventSystem.Dispatch(EventID.UI_Achievement_RefreshRedPoint) end function Achievement_Model.RequestClaimAchievementLevelReward(level) local msg = NetManager.GetPB("achievement_oper").AchievementLevelRewardReq() msg.level = level NetManager.SendMessage(MessageEnums.MessageAchievementLevelReward, msg) end function private.ResponseClaimAchievementLevelReward(msgResp) if IsServerDataNotNil(msgResp.Resp.PlayerChange.achievementSystemInfo) then local achievementSystemInfo = msgResp.Resp.PlayerChange.achievementSystemInfo -- logging.error(LTAG.ERROR, "主成就奖励", achievementSystemInfo) private.SetClaimLevelRewardDict(achievementSystemInfo.rewardLevels) -- 有新奖励被领取 if achievementSystemInfo.rewardLevels and #achievementSystemInfo.rewardLevels > 0 then EventSystem.Dispatch(EventID.UI_AchievementReward_RefreshReward) EventSystem.Dispatch(EventID.UI_Achievement_RefreshRedPoint) end end end function Achievement_Model.RequestClaimAchievementReward(id) local msg = NetManager.GetPB("achievement_oper").AchievementRewardReq() msg.id = id NetManager.SendMessage(MessageEnums.MessageAchievementReward, msg) end function private.ResponseClaimAchievementReward(msgResp) if IsServerDataNotNil(msgResp.Resp.PlayerChange.achievementSystemInfo) then local achievementSystemInfo = msgResp.Resp.PlayerChange.achievementSystemInfo -- logging.error(LTAG.ERROR, "成就奖励", achievementSystemInfo) if achievementSystemInfo.infos and #achievementSystemInfo.infos > 0 then for _, info in ipairs (achievementSystemInfo.infos) do private.SetAchievementInfo(private.achievementDict[info.achievementId], info) end -- 重置最近分类 private.CheckRecentlyAchievements() EventSystem.Dispatch(EventID.UI_AchievementInfo_UpdateItems) end end EventSystem.Dispatch(EventID.UI_Achievement_RefreshRedPoint) end -- -- 对外接口 function Achievement_Model.GetAchievementItemByID(id) if not id then return nil end return private.achievementDict[id] end -- 查询当前成就在当前分类的下标位置 function Achievement_Model.FindIndexInSortedAchievements(achievementData) if not achievementData then return -1 end local childAchievement = Achievement_Model.GetChildAchievement(achievementData.configData.MainType, achievementData.configData.ChildType) if not childAchievement or #childAchievement.achievementDatas == 0 then return -1 end local amountCount = #childAchievement.achievementDatas if amountCount == 1 then return childAchievement.achievementDatas[1] == achievementData and 1 or -1 end local l, r = 1, amountCount if achievementData.state == achievement.Doing then l, r = private.GetDoingAchievementItemIndexs(achievementData.configData.MainType, achievementData.configData.ChildType) elseif achievementData.state == achievement.Achieve then l, r = private.GetUnclaimAchievementItemIndexs(achievementData.configData.MainType, achievementData.configData.ChildType) elseif achievementData.state == achievement.Done then l, r = private.GetClaimAchievementItemIndexs(achievementData.configData.MainType, achievementData.configData.ChildType) end while (l <= r) do if childAchievement.achievementDatas[l] == achievementData then return l end if childAchievement.achievementDatas[r] == achievementData then return r end local mid = l + math.floor((r - l) / 2) if childAchievement.achievementDatas[mid] == achievementData then return mid end if private.SortSortedAchievements(childAchievement.achievementDatas[mid], achievementData) then -- 中值比当前要排序靠前 l = mid + 1 else -- 中值比当前要排序靠后 r = mid - 1 end end return -1 end -- 当前分类显示是否是总览 function Achievement_Model.IsSummaryChildType(childType) if not childType then return false end return childType == SummaryChildType end function Achievement_Model.IsSpecialChildType(childType) if not childType then return false end return childType == SummaryChildType or childType == RecentlyDoingChildType or childType == RecentlyCompleteChildType end function Achievement_Model.GetChildAchievement(mainType, childType) if not mainType or not childType then return nil end if Achievement_Model.IsSpecialChildType(childType) then return nil end local mainAchievement = private.sortedAchievenments[mainType] if not mainAchievement then return nil end return mainAchievement.childTypeDict[childType] end function Achievement_Model.GetAchievementItems(mainType, childType) if not mainType or not childType then return nil end if childType == RecentlyDoingChildType then return private.recentlyDoingAchievements elseif childType == RecentlyCompleteChildType then return private.recentlyCompleteAchievements end local childAchievement = Achievement_Model.GetChildAchievement(mainType, childType) if not childAchievement then return nil end return childAchievement.achievementDatas end function Achievement_Model.IsShowEnterRedPoint() for _, mainAchievement in pairs (private.sortedAchievenments) do if mainAchievement.completeCount > 0 and mainAchievement.completeCount > mainAchievement.claimCount then return true end end return Achievement_Model.IsShowAchievementLevelRewardRedPoint() end function Achievement_Model.IsShowAchievementLevelRewardRedPoint() for i, v in DataSheetManager.AchievementData:GetEnumerator() do if v.ID ~= 0 then if v.ID <= private.curAchievementLevel and not private.claimLevelRewardDict[v.ID] then return true end end end return false end function Achievement_Model.IsShowMainTypeRedPoint(mainType) if mainType == FirstMainType then return Achievement_Model.IsShowChildTypeRedPoint(mainType, RecentlyCompleteChildType) or Achievement_Model.IsShowAchievementLevelRewardRedPoint() end local mainAchievement = MDL.Achievement.GetMainAchievement(mainType) if mainAchievement and mainAchievement.completeCount > 0 and mainAchievement.completeCount > mainAchievement.claimCount then return true end return false end function Achievement_Model.IsShowChildTypeRedPoint(mainType, childType) if MDL.Achievement.IsSpecialChildType(childType) then if childType == RecentlyCompleteChildType then local items = MDL.Achievement.GetAchievementItems(mainType, childType) if items and #items > 0 then for _, item in ipairs (items) do if item.state == achievement.Achieve then return true end end end elseif childType == SummaryChildType then return Achievement_Model.IsShowAchievementLevelRewardRedPoint() end return false end local childAchievement = MDL.Achievement.GetChildAchievement(mainType, childType) if childAchievement and childAchievement.completeCount > 0 and childAchievement.completeCount > childAchievement.claimCount then return true end return false end function Achievement_Model.FindFirstUnclaimIndexAndUnachieveIndex() local firstUnclaimIndex = -1 local firstUnachieveIndex = -1 for i, v in DataSheetManager.AchievementData:GetEnumerator() do if firstUnclaimIndex ~= -1 and firstUnachieveIndex ~= -1 then break end if v.ID ~= 0 then if firstUnclaimIndex == -1 and v.ID <= private.curAchievementLevel and not private.claimLevelRewardDict[v.ID] then firstUnclaimIndex = i - 1 elseif firstUnachieveIndex == -1 and v.ID > private.curAchievementLevel then firstUnachieveIndex = i - 1 end end end return firstUnclaimIndex, firstUnachieveIndex end -- -- -- 缓存信息 function Achievement_Model.GetCurAchievementScore() return private.curAchievementScore end function Achievement_Model.GetMainAchievement(mainType) if not mainType then return nil end return private.sortedAchievenments[mainType] end function Achievement_Model.GetAchievementShowTypes() return private.achievementShowTypes end function private.SetCurAchievementLevel(level) if level == 0 then return end private.curAchievementLevel = level EventSystem.Dispatch(EventID.UI_AchievementPandect_Refresh) EventSystem.Dispatch(EventID.UI_AchievementReward_RefreshLevel) end function Achievement_Model.GetCurAchievementLevel() return private.curAchievementLevel end function private.SetClaimLevelRewardDict(rewardLevels) if rewardLevels and #rewardLevels > 0 then for _, level in ipairs (rewardLevels) do private.claimLevelRewardDict[level] = true end end end function Achievement_Model.IsClaimLevelReward(level) if not level or not private.claimLevelRewardDict then return false end return private.claimLevelRewardDict[level] end -- -- 私有方法加工缓存数据 function private.CreateParentNode(typeID, IDS, childNodes) return private.CreateTabNode(typeID, IDS, typeID, childNodes) end function private.CreateChildNode(typeID, IDS, parentTypeID) return private.CreateTabNode(typeID, IDS, parentTypeID) end function private.CreateTabNode(typeID, IDS, parentTypeID, childNodes) local node = { typeID = typeID, showNameIDS = Const_IDS[IDS], parentTypeID = parentTypeID, childNodes = childNodes, isParent = childNodes ~= nil, isExtend = false } return node end function private.InitAchievement(info) local configData = DataSheetManager.AchievementItem:GetDataByID(info.achievementId) if not configData then logging.error(LTAG.ERROR, "不存在成就ID :", info.achievementId) return end local itemConfigData = DataSheetManager.AchievementReward:GetDataByID(configData.ChildReward) if not itemConfigData then logging.error(LTAG.ERROR, "不存在成就ID :", info.achievementId, "的奖励ID :", configData.ChildReward) return end local amountProgress = 0 if configData.ChildProgress == 0 then local triggerData = DataSheetManager.TriggerID:GetDataByID(configData.ChildTriggerID) if not triggerData then logging.error(LTAG.ERROR, "不存在成就ID :", info.achievementId, "的TriggerID :", configData.ChildTriggerID) return end local unlockTable = triggerData.Unlock_Table[1] amountProgress = tonumber(unlockTable.UnlockConditionParamA) elseif configData.ChildProgress == 1 then amountProgress = 1 end local achievementData = setmetatable({configData = configData, itemTable = itemConfigData.Item_Table, amountProgress = amountProgress}, {__index = info}) private.achievementDict[info.achievementId] = achievementData private.SetAchievementInfo(achievementData) end function private.SortSortedAchievements(a, b) if a.state == b.state then if a.state == achievement.Doing then local aPercent = a.progress / a.amountProgress local bPercent = b.progress / b.amountProgress if aPercent ~= bPercent then return aPercent > bPercent end else if a.achieveTime ~= b.achieveTime then return a.achieveTime > b.achieveTime end end if a.configData.ChildWeight ~= b.configData.ChildWeight then return a.configData.ChildWeight > b.configData.ChildWeight end return a.achievementId < b.achievementId else return a.state == achievement.Achieve or (a.state == achievement.Doing and b.state == achievement.Done) end return true end function private.CheckRecentlyAchievements() -- 刷新最近进行和最近达成 table.clearArray(private.recentlyDoingAchievements) table.clearArray(private.recentlyCompleteAchievements) for mainType, mainAchievement in pairs (private.sortedAchievenments) do if mainAchievement.amountCount > mainAchievement.completeCount or mainAchievement.completeCount > 0 then for childType, childAchievement in pairs (mainAchievement.childTypeDict) do local doingStartIndex, doingEndIndex = private.GetDoingAchievementItemIndexs(mainType, childType) local unclaimStartIndex, unclaimEndIndex = private.GetUnclaimAchievementItemIndexs(mainType, childType) local claimStartIndex, claimEndIndex = private.GetClaimAchievementItemIndexs(mainType, childType) while (doingStartIndex <= doingEndIndex or unclaimStartIndex <= unclaimEndIndex or claimStartIndex <= claimEndIndex) do local item = nil if doingStartIndex <= doingEndIndex then -- 最近执行中 item = childAchievement.achievementDatas[doingStartIndex] if item.progress == 0 then doingStartIndex = doingEndIndex + 1 end if not private.InsertRecentlyAchievements(item, private.recentlyDoingAchievements, private.SortRecentlyDoingAchievememts) then doingStartIndex = doingEndIndex + 1 end end doingStartIndex = doingStartIndex + 1 if unclaimStartIndex <= unclaimEndIndex then -- 最近完成 item = childAchievement.achievementDatas[unclaimStartIndex] if not private.InsertRecentlyAchievements(item, private.recentlyCompleteAchievements, private.SortRecentlyCompleteAchievememts) then unclaimStartIndex = unclaimEndIndex + 1 end unclaimStartIndex = unclaimStartIndex + 1 elseif claimStartIndex <= claimEndIndex then -- 最近完成 item = childAchievement.achievementDatas[claimStartIndex] if not private.InsertRecentlyAchievements(item, private.recentlyCompleteAchievements, private.SortRecentlyCompleteAchievememts) then unclaimStartIndex = unclaimEndIndex + 1 end claimStartIndex = claimStartIndex + 1 end end end end end end function private.CheckProgress(achievementData) if achievementData then local triggerData = DataSheetManager.TriggerID:GetDataByID(achievementData.configData.ChildTriggerID) if not triggerData then return end -- 一雪前耻 服务器进度发过来的是时间 需要客户端转换进度 if triggerData.Unlock_Table[1].UnlockCondition == 60104 then achievementData.progress = (achievementData.progress > 0 and achievementData.progress <= tonumber(triggerData.Unlock_Table[1].UnlockConditionParamA)) and 1 or 0 end end end function private.SetAchievementInfo(achievementData, info) local isComplete = achievementData.state == achievement.Achieve or achievementData.state == achievement.Done private.CheckProgress(achievementData) if not info then if not private.sortedAchievenments[achievementData.configData.MainType] then private.sortedAchievenments[achievementData.configData.MainType] = { curScore = 0, amountScore = 0, amountCount = 0, completeCount = 0, claimCount = 0, childTypeDict = {} } end local mainAchievement = private.sortedAchievenments[achievementData.configData.MainType] if not mainAchievement.childTypeDict[achievementData.configData.ChildType] then mainAchievement.childTypeDict[achievementData.configData.ChildType] = { curScore = 0, amountScore = 0, amountCount = 0, completeCount = 0, claimCount = 0, achievementDatas = {} } end local childAchievement = mainAchievement.childTypeDict[achievementData.configData.ChildType] table.insert(childAchievement.achievementDatas, achievementData) -- 计算总分 mainAchievement.amountScore = mainAchievement.amountScore + achievementData.configData.ChildScore childAchievement.amountScore = childAchievement.amountScore + achievementData.configData.ChildScore -- 计算成就总数 mainAchievement.amountCount = mainAchievement.amountCount + 1 childAchievement.amountCount = childAchievement.amountCount + 1 if isComplete then -- 计算完成数 private.completeAchievementCount = private.completeAchievementCount + 1 mainAchievement.completeCount = mainAchievement.completeCount + 1 childAchievement.completeCount = childAchievement.completeCount + 1 -- 计算当前分数 private.curAchievementScore = private.curAchievementScore + achievementData.configData.ChildScore mainAchievement.curScore = mainAchievement.curScore + achievementData.configData.ChildScore childAchievement.curScore = childAchievement.curScore + achievementData.configData.ChildScore if achievementData.state == achievement.Done then -- 计算领取奖励数 private.claimAchievementCount = private.claimAchievementCount + 1 mainAchievement.claimCount = mainAchievement.claimCount + 1 childAchievement.claimCount = childAchievement.claimCount + 1 end end else local mainAchievement = private.sortedAchievenments[achievementData.configData.MainType] if not mainAchievement then logging.error(LTAG.ERROR, "不存在成就缓存主类型为", achievementData.configData.MainType) return end local childAchievement = mainAchievement.childTypeDict[achievementData.configData.ChildType] if not childAchievement then logging.error(LTAG.ERROR, "不存在成就缓存子类型为", achievementData.configData.ChildType) return end local achievementState = achievementData.state local originalIndex = Achievement_Model.FindIndexInSortedAchievements(achievementData) -- 替换数据 local mt = getmetatable(achievementData) mt.__index = info -- 重新排序 private.SortChildAchievementDatas(childAchievement.achievementDatas, originalIndex) -- 判断状态 local curIsComplete = info.state == achievement.Achieve or achievementData.state == achievement.Done if curIsComplete and not isComplete then -- 添加完成数 private.completeAchievementCount = private.completeAchievementCount + 1 mainAchievement.completeCount = mainAchievement.completeCount + 1 childAchievement.completeCount = childAchievement.completeCount + 1 -- 添加当前分数 private.curAchievementScore = private.curAchievementScore + achievementData.configData.ChildScore mainAchievement.curScore = mainAchievement.curScore + achievementData.configData.ChildScore childAchievement.curScore = childAchievement.curScore + achievementData.configData.ChildScore EventSystem.Dispatch(EventID.UI_AchievementPandect_Refresh, achievementData.configData.MainType) CTL.Achievement.OnCompleteAchievement(achievementData) end -- 判断是否领取奖励 if info.state == achievement.Done and achievementState ~= achievement.Done then -- 添加领取数 private.claimAchievementCount = private.claimAchievementCount + 1 mainAchievement.claimCount = mainAchievement.claimCount + 1 childAchievement.claimCount = childAchievement.claimCount + 1 end end end function private.SortChildAchievementDatas(achievementDatas, originalIndex) local curData = achievementDatas[originalIndex] local formerData = originalIndex - 1 >= 1 and achievementDatas[originalIndex - 1] or nil local nextData = originalIndex + 1 <= #achievementDatas and achievementDatas[originalIndex + 1] or nil -- 确定是否需要排序 if (not formerData or private.SortSortedAchievements(formerData, curData)) and (not nextData or private.SortSortedAchievements(curData, nextData)) then return end -- 相邻替换 if formerData and not private.SortSortedAchievements(formerData, curData) then local index = originalIndex - 1 while (index >= 1) do if private.SortSortedAchievements(achievementDatas[index + 1], achievementDatas[index]) then local temp = achievementDatas[index + 1] achievementDatas[index + 1] = achievementDatas[index] achievementDatas[index] = temp else EventSystem.Dispatch(EventID.UI_AchievementInfo_UpdateItems, curData) break end index = index - 1 end elseif nextData and not private.SortSortedAchievements(curData, nextData) then local index = originalIndex + 1 while (index <= #achievementDatas) do if private.SortSortedAchievements(achievementDatas[index], achievementDatas[index - 1]) then local temp = achievementDatas[index] achievementDatas[index] = achievementDatas[index - 1] achievementDatas[index - 1] = temp else break end index = index + 1 end end end function private.GetDoingAchievementItemIndexs(mainType, childType) local childAchievement = Achievement_Model.GetChildAchievement(mainType, childType) if not childAchievement then return 0, -1 end if childAchievement.amountCount == childAchievement.completeCount then return 0, -1 end local index = 1 + childAchievement.completeCount - childAchievement.claimCount return index, index + childAchievement.amountCount - childAchievement.completeCount - 1 end function private.GetUnclaimAchievementItemIndexs(mainType, childType) local childAchievement = Achievement_Model.GetChildAchievement(mainType, childType) if not childAchievement then return 0, -1 end if childAchievement.completeCount == childAchievement.claimCount then return 0, -1 end return 1, childAchievement.completeCount - childAchievement.claimCount end function private.GetClaimAchievementItemIndexs(mainType, childType) local childAchievement = Achievement_Model.GetChildAchievement(mainType, childType) if not childAchievement then return 0, -1 end if childAchievement.claimCount == 0 then return 0, -1 end return childAchievement.amountCount - childAchievement.claimCount + 1, childAchievement.amountCount end function private.SortRecentlyDoingAchievememts(a, b) local aPercent = a.progress / a.amountProgress local bPercent = b.progress / b.amountProgress if aPercent == bPercent then return a.achievementId < b.achievementId end return aPercent > bPercent end function private.SortRecentlyCompleteAchievememts(a, b) if a.state == b.state then if a.achieveTime == b.achieveTime then return a.achievementId < b.achievementId end return a.achieveTime > b.achieveTime end return a.state < b.state end function private.InsertRecentlyAchievements(achievementItem, achievementList, compareFuc) if #achievementList == AchievementLimitedCount then local lastItem = achievementList[AchievementLimitedCount] if compareFuc(lastItem, achievementItem) then return false else achievementList[AchievementLimitedCount] = achievementItem end else table.insert(achievementList, achievementItem) end if #achievementList > 1 then -- 相邻替换 for i = #achievementList, 1, -1 do if i - 1 < 1 or compareFuc(achievementList[i - 1], achievementList[i]) then break end local tmp = achievementList[i] achievementList[i] = achievementList[i - 1] achievementList[i - 1] = tmp end end return true end --优化,之前是有成就变化就推送,现在变成只有达成某一项的时候才会推送 function Achievement_Model.ReqAllAchievenmentData() local msg = NetManager.GetPB("achievement_oper").AchievementChangeReq() NetManager.SendMessage(MessageEnums.MessageAchievementChange, msg) end function Achievement_Model.ReqAllAchievementReward() local msg = NetManager.GetPB("achievement_oper").AllAchievementRewardReq() NetManager.SendMessage(MessageEnums.MessageAllAchievementReward, msg) end return Achievement_Model