local Character_Model = {} local private = {} local EventSystem = require('Event/EventSystem') local portrait_pb = require("Proto/portrait_pb") local frame_pb = require("Proto/frame_pb") Character_Model.OwnedType = { Forever = 1, -- 永久有效 TimeLimit = 2, -- 限时生效 Overdue = 3, -- 已过期(之前拥有过) UnOwned = 4, -- 尚未拥有 } Character_Model.GetType = { InitialHead = 1, -- 初始头像就是获取方式为注册是下发的头像 ItemActive = 2, -- 道具激活 HeroActive = 3, -- 武将激活 AwakenActive = 4, -- 武将觉醒激活 } ---------- start 辅助相关 start ---------- local function SortPortraitsOrFramesList(a, b) if a.OwnedType <= Character_Model.OwnedType.TimeLimit and b.OwnedType > Character_Model.OwnedType.TimeLimit then return true elseif b.OwnedType <= Character_Model.OwnedType.TimeLimit and a.OwnedType > Character_Model.OwnedType.TimeLimit then return false else if a.Default and not b.Default then return true elseif b.Default and not a.Default then return false else return a.ID < b.ID end end end ---------- end 辅助相关 end ---------- function Character_Model.OnStartup() private.curWearPortrait = -1 -- 玩家当前佩戴的头像 private.portraitsList = {} -- 头像信息List private.curWearFrame = -1 -- 玩家当前佩戴的头像框 private.framesList = {} -- 头像框信息List private.timerHandle = nil -- 头像、头像框过期计时器 private.overdueData = nil -- 即将过期的头像、头像框数据 -- 初始化头像信息 private.InitPortraitsList() -- 初始化头像框信息 private.InitFramesList() NetManager.RegisterMsgCB(MessageEnums.MessagePortraitInvalid, private.PortraitInvalidResponse) NetManager.RegisterMsgCB(MessageEnums.MessagePortraitChange, private.PortraitChangeResponse) NetManager.RegisterMsgCB(MessageEnums.MessagePortraitUseItem, private.PortraitUseItemResponse) NetManager.RegisterMsgCB(MessageEnums.MessagePortraitBrowse, private.PortraitBrowseResponse) NetManager.RegisterMsgCB(MessageEnums.MessageFrameInvalid, private.FrameInvalidResponse) NetManager.RegisterMsgCB(MessageEnums.MessageFrameChange, private.FrameChangeResponse) NetManager.RegisterMsgCB(MessageEnums.MessageFrameUseItem, private.FrameUseItemResponse) NetManager.RegisterMsgCB(MessageEnums.MessageFrameBrowse, private.FrameBrowseResponse) end ---------- start 协议相关 start ---------- -- #region 头像失效 function Character_Model.PortraitInvalidRequest(portraitID) local msg = portrait_pb.PortraitInvalidReq() msg.PortraitID = portraitID NetManager.SendMessage(MessageEnums.MessagePortraitInvalid, msg) end function private.PortraitInvalidResponse(msgResp) if msgResp.Resp.Code == 0 then private.SetTimerHandle() end end -- #endregion -- #region 头像更换 -- 如果摘除头像不佩戴则PortraitID==0 -- 会提示穿戴成功,如果不想提示则notHint传true,不传默认会提示 function Character_Model.PortraitChangeRequest(portraitID, notHint) local msg = portrait_pb.PortraitChangeReq() msg.PortraitID = portraitID if notHint then private.portraitChangeNotHint = true else private.portraitChangeNotHint = false end NetManager.SendMessage(MessageEnums.MessagePortraitChange, msg) end function private.PortraitChangeResponse(msgResp) if msgResp.Resp.Code == 0 and not private.portraitChangeNotHint then CTL.Tips.AddTipsMsg(Const_IDS.IDS_HeadPortraitAndBox_Success) CTL.Sound.PlayGameSound("UI_xingxiang") end end -- #endregion -- #region 使用头像道具获取头像 function Character_Model.PortraitUseItemRequest(itemID) local portraitData = Character_Model.GetPortraitDataByItemId(itemID) -- 如果头像已经拥有,并且是永久生效,下次不弹tips if portraitData.OwnedType == Character_Model.OwnedType.Forever then private.lastItemForeverActived = true else private.lastItemForeverActived = false end local msg = portrait_pb.PortraitUseItemReq() msg.ItemID = itemID private.usePortraitItemID = itemID NetManager.SendMessage(MessageEnums.MessagePortraitUseItem, msg) end function private.PortraitUseItemResponse(msgResp) if msgResp.Resp.Code == 0 then if private.usePortraitItemID then local portraitData = Character_Model.GetPortraitDataByItemId(private.usePortraitItemID) if portraitData.Duration == 0 then if not private.lastItemForeverActived then local name = UIUtils.GetLocalizedTxt(Const_IDS[portraitData.Name]) local overdue = UIUtils.GetLocalizedTxt(Const_IDS.IDS_HeadPortraitAndBox_GotForever) CTL.Tips.AddTipsMsg(string.FormatByIdsId(Const_IDS.IDS_HeadPortraitAndBox_Get, name, overdue)) end else local name = UIUtils.GetLocalizedTxt(Const_IDS[portraitData.Name]) local overdue if portraitData.InvalidTimeStamp > 0 then overdue = UTL.Time.FormatTimeForDayOrClock(portraitData.InvalidTimeStamp - UTL.Time.GetServerTimestamp()) else overdue = UTL.Time.FormatTimeForTitle(portraitData.Duration) end CTL.Tips.AddTipsMsg(string.FormatByIdsId(Const_IDS.IDS_HeadPortraitAndBox_Get, name, overdue)) end private.usePortraitItemID = nil end end end -- #endregion -- #region 头像浏览消除红点 function Character_Model.PortraitBrowseRequest(portraitID) local msg = portrait_pb.PortraitBrowseReq() msg.PortraitID = portraitID NetManager.SendMessage(MessageEnums.MessagePortraitBrowse, msg) end function private.PortraitBrowseResponse(msgResp) end -- #endregion -- #region 头像框失效 function Character_Model.FrameInvalidRequest(frameID) local msg = frame_pb.FrameInvalidReq() msg.FrameID = frameID NetManager.SendMessage(MessageEnums.MessageFrameInvalid, msg) end function private.FrameInvalidResponse(msgResp) if msgResp.Resp.Code == 0 then private.SetTimerHandle() end end -- #endregion -- #region 头像框更换 -- 如果摘除头像不佩戴则FrameID==0 -- 会提示穿戴成功,如果不想提示则notHint传true,不传默认会提示 function Character_Model.FrameChangeRequest(frameID, notHint) local msg = frame_pb.FrameChangeReq() msg.FrameID = frameID if notHint then private.frameChangeNotHint = true else private.frameChangeNotHint = false end NetManager.SendMessage(MessageEnums.MessageFrameChange, msg) end function private.FrameChangeResponse(msgResp) if msgResp.Resp.Code == 0 and not private.frameChangeNotHint then CTL.Tips.AddTipsMsg(Const_IDS.IDS_HeadPortraitAndBox_Success) CTL.Sound.PlayGameSound("UI_xingxiang") end end -- #endregion -- #region 使用头像道具获得头像框 function Character_Model.FrameUseItemRequest(itemID) local frameData = Character_Model.GetFrameDataByItemId(itemID) -- 如果头像框已经拥有,并且是永久生效,下次不弹tips if frameData.OwnedType == Character_Model.OwnedType.Forever then private.lastItemForeverActived = true else private.lastItemForeverActived = false end local msg = frame_pb.FrameUseItemReq() msg.ItemID = itemID private.useFrameItemID = itemID NetManager.SendMessage(MessageEnums.MessageFrameUseItem, msg) end function private.FrameUseItemResponse(msgResp) if msgResp.Resp.Code == 0 then if private.useFrameItemID then local frameData = Character_Model.GetFrameDataByItemId(private.useFrameItemID) if frameData then if frameData.Duration == 0 then if not private.lastItemForeverActived then local name = UIUtils.GetLocalizedTxt(Const_IDS[frameData.Name]) local overdue = UIUtils.GetLocalizedTxt(Const_IDS.IDS_HeadPortraitAndBox_GotForever) CTL.Tips.AddTipsMsg(string.FormatByIdsId(Const_IDS.IDS_HeadPortraitAndBox_Get, name, overdue)) end else local name = UIUtils.GetLocalizedTxt(Const_IDS[frameData.Name]) local overdue if frameData.InvalidTimeStamp > 0 then overdue = UTL.Time.FormatTimeForDayOrClock(frameData.InvalidTimeStamp - UTL.Time.GetServerTimestamp()) else overdue = UTL.Time.FormatTimeForTitle(frameData.Duration) end CTL.Tips.AddTipsMsg(string.FormatByIdsId(Const_IDS.IDS_HeadPortraitAndBox_Get, name, overdue)) end end private.useFrameItemID = nil end end end -- #endregion -- #region 头像框消除红点 function Character_Model.FrameBrowseRequest(frameID) local msg = frame_pb.FrameBrowseReq() msg.FrameID = frameID NetManager.SendMessage(MessageEnums.MessageFrameBrowse, msg) end function private.FrameBrowseResponse(msgResp) end -- #endregion -- #region -- 同步头像 function Character_Model.SyncPortraitsList(portraitInfos) -- 同步当前佩戴头像 if private.curWearPortrait ~= portraitInfos.CurWearPortrait then private.curWearPortrait = portraitInfos.CurWearPortrait EventSystem.Dispatch(EventID.UI_PlayerHeadPortraitAndBox_RefreshCur) end -- 同步当前拥有头像 if IsServerDataNotNil(portraitInfos.Portraits) then local curTime = UTL.Time.GetServerTimestamp() local hasNew = false for _, v in ipairs(portraitInfos.Portraits) do local portrait = nil for _, info in ipairs(private.portraitsList) do if v.PortraitID == info.ID then portrait = info break end end if not portrait then logging.error(LTAG.ERROR, "头像表配置错误 : 没有找到 ID = ", v.PortraitID, "这一条数据") return end portrait.InvalidTimeStamp = v.InvalidTimeStamp portrait.IsNew = v.IsNewPortrait if not hasNew then hasNew = v.IsNewPortrait end portrait.IsWear = private.curWearPortrait == v.PortraitID if v.InvalidTimeStamp == 0 then portrait.OwnedType = Character_Model.OwnedType.Forever elseif v.InvalidTimeStamp > curTime then portrait.OwnedType = Character_Model.OwnedType.TimeLimit elseif v.InvalidTimeStamp < 0 then portrait.OwnedType = Character_Model.OwnedType.Overdue end end table.sort(private.portraitsList, SortPortraitsOrFramesList) private.SetTimerHandle() if hasNew then CTL.RedPoint.AddRedPoint(CTL.RedPoint.RedPointKey.TownHead_Team_Head_Portrait) else CTL.RedPoint.RemoveRedPoint(CTL.RedPoint.RedPointKey.TownHead_Team_Head_Portrait) end end EventSystem.Dispatch(EventID.UI_PlayerHeadPortraitAndBox_Refresh, true) end -- 同步头像框 function Character_Model.SyncFramesList(frameInfos) -- 同步当前佩戴头像框 if private.curWearFrame ~= frameInfos.CurWearFrame then private.curWearFrame = frameInfos.CurWearFrame EventSystem.Dispatch(EventID.UI_PlayerHeadPortraitAndBox_RefreshCur) end -- 同步当前拥有头像框 if IsServerDataNotNil(frameInfos.Frames) then local curTime = UTL.Time.GetServerTimestamp() local hasNew = false for _, v in ipairs(frameInfos.Frames) do local frame = nil for _, info in ipairs(private.framesList) do if v.FrameID == info.ID then frame = info break end end if not frame then logging.error(LTAG.ERROR, "头像框表配置错误 : 没有找到 ID = ", v.FrameID, "这一条数据") return end frame.InvalidTimeStamp = v.InvalidTimeStamp frame.IsNew = v.IsNewFrame if not hasNew then hasNew = v.IsNewFrame end frame.IsWear = private.curWearFrame == v.FrameID if v.InvalidTimeStamp == 0 then frame.OwnedType = Character_Model.OwnedType.Forever elseif v.InvalidTimeStamp > curTime then frame.OwnedType = Character_Model.OwnedType.TimeLimit elseif v.InvalidTimeStamp < 0 then frame.OwnedType = Character_Model.OwnedType.Overdue end end table.sort(private.framesList, SortPortraitsOrFramesList) private.SetTimerHandle() if hasNew then CTL.RedPoint.AddRedPoint(CTL.RedPoint.RedPointKey.TownHead_Team_Head_Frame) else CTL.RedPoint.RemoveRedPoint(CTL.RedPoint.RedPointKey.TownHead_Team_Head_Frame) end end EventSystem.Dispatch(EventID.UI_PlayerHeadPortraitAndBox_Refresh, true) end -- #endregion ---------- end 协议相关 end ---------- ---------- start 数据获取相关 start ---------- -- 初始化头像信息 function private.InitPortraitsList() private.portraitsList = {} for i, v in DataSheetManager.HeadPortrait:GetEnumerator() do private.portraitsList[i] = { ID = v.ID, Name = v.Name, -- 名称 HeadResource = v.HeadResource, -- 资源路径 ItemID = v.ItemID, -- 激活道具(可能是0,只有获取条件是道具激活时才有用) HeroID = v.HeroID, -- 对应武将(可能是0,只有获取条件是武将激活时才有用) Describe = v.Describe, -- 获取条件描述 Duration = v.Duration, -- 持续时间(持续时间为0,表示永久有效) Gender = v.Gender, -- 头像性别 Default = v.Default == 1, -- 是否默认 GetType = v.GetType, -- 获取方式 InvalidTimeStamp = 0, -- 服务器返回到期时间戳(为0是永久) IsNew = false, -- 是否是新获得 IsWear = false, -- 是否是穿戴中 OwnedType = Character_Model.OwnedType.UnOwned -- 拥有状态 } end end -- 获取当前穿戴头像 function Character_Model.GetCurWearPortrait() return private.curWearPortrait end -- 获取头像列表迭代器 function Character_Model.GetPortraitsListIterator() return ipairs(private.portraitsList) end -- 根据heroid获取头像数据 function Character_Model.GetFullPortraitDataByHeroID(heroID) for i, v in DataSheetManager.HeadPortrait:GetEnumerator() do if v.HeroID == heroID then return v end end return nil end -- 根据itemid获取头像数据 function Character_Model.GetPortraitDataByItemId(itemId) for _, data in ipairs(private.portraitsList) do if data.ItemID == itemId then return data end end return nil end -- 根据ID判断头像是否已经激活 function Character_Model.IsPortraitActivedInBagByID(ID) for _, data in ipairs(private.portraitsList) do if data.ID == ID then return Character_Model.IsActiveByInfo(data) end end return false end -- 背包中的道具是否有红点 function Character_Model.IsPortraitHasRedInBag(itemId) for _, data in ipairs(private.portraitsList) do if data.ItemID == itemId then if data.OwnedType == Character_Model.OwnedType.TimeLimit then return true else return not Character_Model.IsActiveByInfo(data) end end end return false end -- 设置第一个符合的默认头像 function Character_Model.SetDefaultPortrait() local selfGender = MDL.Player.GetSelfGender() for _, info in ipairs(private.portraitsList) do if info.Default and info.Gender == selfGender then Character_Model.PortraitChangeRequest(info.ID) return end end end -- 初始化头像框信息 function private.InitFramesList() private.framesList = {} for i, v in DataSheetManager.HeadBox:GetEnumerator() do private.framesList[i] = { ID = v.ID, Name = v.Name, -- 名称 HeadResource = v.HeadResource, -- 资源路径 ItemID = v.ItemID, -- 激活道具(可能是0,需要道具激活时才有用) Describe = v.Describe, -- 获取条件描述 Duration = v.Duration, -- 持续时间(持续时间为0,表示永久有效) Effect = v.Effect, -- 头像框对应特效 Default = v.Default == 1, -- 是否默认 InvalidTimeStamp = 0, -- 到期时间戳(为0是永久) IsNew = false, -- 是否是新获得 IsWear = false, -- 是否是穿戴中 OwnedType = Character_Model.OwnedType.UnOwned -- 拥有状态 } end end -- 获取当前穿戴头像框 function Character_Model.GetCurWearFrame() return private.curWearFrame end -- 获取头像框迭代器 function Character_Model.GetFramesListIterator() return ipairs(private.framesList) end -- 根据itemid获取头像框数据 function Character_Model.GetFrameDataByItemId(itemId) for _, data in ipairs(private.framesList) do if data.ItemID == itemId then return data end end return nil end -- 根据itemid判断头像框是否已经激活 function Character_Model.IsFrameHasRedInBag(itemId) for _, data in ipairs(private.framesList) do if data.ItemID == itemId then if data.OwnedType == Character_Model.OwnedType.TimeLimit then return true else return not Character_Model.IsActiveByInfo(data) end end end return false end -- 根据ID判断头像框是否已经激活 function Character_Model.IsFrameActivedInBagByID(ID) for _, data in ipairs(private.framesList) do if data.ID == ID then return Character_Model.IsActiveByInfo(data) end end return false end -- 设置第一个符合的默认头像框 -- 会提示穿戴成功,如果不想提示则notHint传true,不传默认会提示 function Character_Model.SetDefaultFrame(notHint) for _, info in ipairs(private.framesList) do if info.Default then Character_Model.FrameChangeRequest(info.ID, notHint) return end end end -- 根据info(头像、头像框都可以)判断是否已经激活 function Character_Model.IsActiveByInfo(info) return info.OwnedType == Character_Model.OwnedType.Forever or info.OwnedType == Character_Model.OwnedType.TimeLimit end ---------- end 数据获取相关 end ---------- ---------- start 计时器相关 start ---------- -- 开启头像、头像框过期计时器 function private.SetTimerHandle() if private.timerHandle then TimerUtil.Remove(private.timerHandle) private.timerHandle = nil end local minTime = 0 local overdueData = nil local isPortrait = true local curTime = UTL.Time.GetServerTimestamp() for _, info in ipairs(private.portraitsList) do if info.OwnedType == Character_Model.OwnedType.TimeLimit then local offset = info.InvalidTimeStamp - curTime if minTime == 0 or minTime > offset then minTime = offset overdueData = info isPortrait = true end end end for _, info in ipairs(private.framesList) do if info.OwnedType == Character_Model.OwnedType.TimeLimit then local offset = info.InvalidTimeStamp - curTime if minTime == 0 or minTime > offset then minTime = offset overdueData = info isPortrait = false end end end if minTime > 0 then private.overdueData = overdueData private.timerHandle = TimerUtil.Interval(isPortrait and private.PortraitTimeToEnd or private.FrameTimeToEnd, minTime, 1) end end -- 头像过期 function private.PortraitTimeToEnd() if private.overdueData then if private.overdueData.ID == private.curWearPortrait then Character_Model.SetDefaultPortrait() end Character_Model.PortraitInvalidRequest(private.overdueData.ID) CTL.Tips.AddTipsMsg(string.FormatByIdsId(Const_IDS.IDS_HeadPortraitAndBox_overdue1, UIUtils.GetLocalizedTxt(Const_IDS[private.overdueData.Name]))) private.overdueData = nil end end -- 头像框过期 function private.FrameTimeToEnd() if private.overdueData then if private.overdueData.ID == private.curWearFrame then Character_Model.SetDefaultFrame() end Character_Model.FrameInvalidRequest(private.overdueData.ID) CTL.Tips.AddTipsMsg(string.FormatByIdsId(Const_IDS.IDS_HeadPortraitAndBox_Delete, UIUtils.GetLocalizedTxt(Const_IDS[private.overdueData.Name]))) private.overdueData = nil end end -- #endregion ---------- end 计时器相关 end ---------- return Character_Model