Files
Endfield-Data/LuaScripts/UI/Panels/AchievementList/AchievementListCtrl.lua
2025-12-02 20:37:18 +07:00

737 lines
24 KiB
Lua

local uiCtrl = require_ex('UI/Panels/Base/UICtrl')
local PANEL_ID = PanelId.AchievementList
AchievementListCtrl = HL.Class('AchievementListCtrl', uiCtrl.UICtrl)
AchievementListCtrl.s_messages = HL.StaticField(HL.Table) << {
}
AchievementListCtrl.m_filterCellCache = HL.Field(HL.Forward("UIListCache"))
AchievementListCtrl.m_getCategoryCellFunc = HL.Field(HL.Function)
AchievementListCtrl.m_getAchievementCellFunc = HL.Field(HL.Function)
AchievementListCtrl.m_categoryDataSource = HL.Field(HL.Any) << nil
AchievementListCtrl.m_categoryFilteredData = HL.Field(HL.Any) << nil
AchievementListCtrl.m_filteredDataCount = HL.Field(HL.Number) << 0
AchievementListCtrl.m_filteredAchievementMap = HL.Field(HL.Any) << nil
AchievementListCtrl.m_selectCategoryIndex = HL.Field(HL.Number) << 1
AchievementListCtrl.m_selectGroupIndex = HL.Field(HL.Number) << 1
AchievementListCtrl.m_filterType = HL.Field(HL.Number) << 1
AchievementListCtrl.m_searchKey = HL.Field(HL.String) << ''
AchievementListCtrl.m_currentRedDot = HL.Field(HL.Any) << nil
AchievementListCtrl.m_viewedAchievements = HL.Field(HL.Any) << nil
AchievementListCtrl.m_isFold = HL.Field(HL.Boolean) << false
local ALL_FILTER_TYPE = 1
local OBTAIN_FILTER_TYPE = 2
local NOT_OBTAIN_FILTER_TYPE = 3
local FILTER_CONFIGS = {
[ALL_FILTER_TYPE] = {
icon = "achievement_tab_icon01",
text = "ui_achv_list_all",
filter = function(achievementInfo)
return true
end
},
[OBTAIN_FILTER_TYPE] = {
icon = "achievement_tab_icon02",
text = "ui_achv_list_obtained",
filter = function(achievementInfo)
if achievementInfo == nil or achievementInfo.achievementPlayerInfo == nil then
return false
end
return achievementInfo.achievementPlayerInfo.level > 0
end
},
[NOT_OBTAIN_FILTER_TYPE] = {
icon = "achievement_tab_icon03",
text = "ui_achv_list_not_obtained",
hideRedDot = true,
filter = function(achievementInfo)
if achievementInfo == nil or achievementInfo.achievementPlayerInfo == nil then
return true
end
return achievementInfo.achievementPlayerInfo.level <= 0
end
},
}
local SHOWING_RED_DOT = 1
AchievementListCtrl.OnCreate = HL.Override(HL.Any) << function(self, arg)
self:_InitViews()
self:_LoadData()
self:_RenderViews(true)
end
AchievementListCtrl.OnClose = HL.Override() << function(self)
self:_ClearShowedRedDot()
end
AchievementListCtrl.ShowAchievement = HL.StaticMethod(HL.Opt(HL.String)) << function(arg)
local ctrl = AchievementListCtrl.AutoOpen(PANEL_ID, nil, false)
if string.isEmpty(arg) then
logger.error("[Achievement] AchievementListCtrl: Invalid arg: " .. tostring(arg))
return
end
local focus = ctrl:_TryFocusAchievement(arg)
if not focus then
logger.error("[Achievement] AchievementListCtrl: Cannot Find Achievement To Focus " .. tostring(arg))
end
end
AchievementListCtrl._InitViews = HL.Method() << function(self)
self.view.controllerHintPlaceholder:InitControllerHintPlaceholder({ self.view.inputGroup.groupId })
self.view.helpBtn.onClick:AddListener(function()
Notify(MessageConst.SHOW_INTRO, "achievement")
end)
self.view.btnClose.onClick:AddListener(function()
self:PlayAnimationOutAndClose()
end)
self.m_filterCellCache = UIUtils.genCellCache(self.view.leftTabs.tabCell)
self.m_filterCellCache:Refresh(#FILTER_CONFIGS, function(cell, luaIndex)
local iconPath = UIConst.UI_SPRITE_ACHIEVEMENT
local iconName = FILTER_CONFIGS[luaIndex].icon
cell.toggle.onValueChanged:RemoveAllListeners()
cell.toggle.onValueChanged:AddListener(function(isOn)
if isOn and self.m_filterType ~= luaIndex then
self:_SetFilter(luaIndex)
end
end)
cell.selectedIcon:LoadSprite(iconPath, iconName)
cell.defaultIcon:LoadSprite(iconPath, iconName)
end)
self.m_filterCellCache:GetItem(self.m_filterType).toggle.isOn = true
self.m_getCategoryCellFunc = UIUtils.genCachedCellFunction(self.view.categoryList)
self.view.categoryList.onUpdateCell:RemoveAllListeners()
self.view.categoryList.onUpdateCell:AddListener(function(obj, csIndex)
self:_RenderCategory(self.m_getCategoryCellFunc(obj), LuaIndex(csIndex))
end)
self.view.categoryList.getCellSize = function(csIndex)
local luaIndex = LuaIndex(csIndex)
local categoryInfo = self.m_categoryFilteredData[luaIndex]
if categoryInfo == nil then
return 0
end
if categoryInfo.haveSub then
return self.view.config.CATEGORY_CELL_HEIGHT + self.view.config.CATEGORY_GROUP_CELL_HEIGHT * #categoryInfo.filteredGroups
end
return self.view.config.CATEGORY_CELL_HEIGHT
end
self.m_getAchievementCellFunc = UIUtils.genCachedCellFunction(self.view.achievementList)
self.view.achievementList.onUpdateCell:RemoveAllListeners()
self.view.achievementList.onUpdateCell:AddListener(function(obj, csIndex)
self:_RenderAchievement(self.m_getAchievementCellFunc(obj), LuaIndex(csIndex))
end)
self.view.clearBtn.gameObject:SetActive(false)
self:_ResetSearch()
InputManagerInst:ToggleGroup(self.view.textInputBindingGroup.groupId, true)
UIUtils.initSearchInput(self.view.inputField, {
clearBtn = self.view.clearBtn,
onInputValueChanged = function(input)
local trimedInput = string.trim(input)
if trimedInput ~= self.m_searchKey then
self:_SetSearchKey(trimedInput)
end
end,
onInputFocused = function()
if DeviceInfo.inputType ~= DeviceInfo.InputType.Controller then
return
end
Notify(MessageConst.SHOW_AS_CONTROLLER_SMALL_MENU, {
panelId = PANEL_ID,
isGroup = true,
id = self.view.textInputBindingGroup.groupId,
rectTransform = self.view.inputField.transform,
noHighlight = true,
hintPlaceholder = self.view.controllerHintPlaceholder,
})
end,
onInputEndEdit = function()
if DeviceInfo.inputType ~= DeviceInfo.InputType.Controller then
return
end
Notify(MessageConst.CLOSE_CONTROLLER_SMALL_MENU, self.view.textInputBindingGroup.groupId)
self.view.inputField:DeactivateInputField(true)
end,
onClearClick = function()
self:_ResetSearch()
end,
})
self.view.leftNaviGroup.onIsTopLayerChanged:RemoveAllListeners()
self.view.leftNaviGroup.onIsTopLayerChanged:AddListener(function(isTop)
if isTop then
self:_RefreshViews()
end
end)
self.view.rightNaviGroup.getDefaultSelectableFunc = function()
self.view.achievementList:ScrollToIndex(0, true)
local topCell = self.m_getAchievementCellFunc(1)
return topCell.button
end
end
AchievementListCtrl._LoadData = HL.Method() << function(self)
self.m_categoryDataSource = {}
self.m_categoryDataSource = AchievementUtils.loadAchievementData(true)
self:_LoadFilteredData()
self:_ResetSelectIndex()
end
AchievementListCtrl._LoadFilteredData = HL.Method() << function(self)
self.m_filteredDataCount = 0
self.m_categoryFilteredData, self.m_filteredAchievementMap = AchievementUtils.filterAchievementData(self.m_categoryDataSource, function(achievementInfo, filteredInfos, showNoObtain)
return self:_FilterAchievement(achievementInfo, filteredInfos, showNoObtain)
end)
end
AchievementListCtrl._FilterAchievement = HL.Method(HL.Any, HL.Any, HL.Boolean).Return(HL.Boolean) << function(self, achievementInfo, filteredInfos, showNoObtain)
local isObtained = achievementInfo.achievementPlayerInfo ~= nil
and achievementInfo.achievementPlayerInfo.level >= achievementInfo.achievementData.initLevel
if not showNoObtain and not isObtained then
return false
end
if not FILTER_CONFIGS[self.m_filterType].filter(achievementInfo) then
return false
end
local isSearch = not string.isEmpty(self.m_searchKey)
local isInclude, replaceName = self:_IsFilteredBySearchKey(achievementInfo.achievementData.name)
if isSearch and not isInclude then
return false
end
achievementInfo.showName = isSearch and replaceName or achievementInfo.achievementData.name
table.insert(filteredInfos, achievementInfo)
self.m_filteredDataCount = self.m_filteredDataCount + 1
return true
end
AchievementListCtrl._ResetSelectIndex = HL.Method() << function(self)
self.m_selectCategoryIndex = 1
self.m_selectGroupIndex = 1
self.m_isFold = false
end
AchievementListCtrl._IsFilteredBySearchKey = HL.Method(HL.String).Return(HL.Boolean, HL.String) << function(self, name)
if string.isEmpty(self.m_searchKey) then
return true, name
end
if string.isEmpty(name) then
return false, name
end
local rep = string.format(Language.LUA_ACHIEVEMENT_NAME_SEARCH_REPLACE, self.m_searchKey)
local nameStr, repCount = string.gsub(name, self.m_searchKey, rep)
if repCount > 0 then
return true, nameStr
end
return false, name
end
AchievementListCtrl._RenderViews = HL.Method(HL.Opt(HL.Boolean, HL.Number)) << function(self, isInit, focusIndex)
isInit = isInit == true
local achievementSystem = GameInstance.player.achievementSystem
self:_ClearShowedRedDot()
local isSearchMode = not string.isEmpty(self.m_searchKey)
self.view.filterTxt.text = I18nUtils.GetText(FILTER_CONFIGS[self.m_filterType].text)
self.view.searchTxt.text = I18nUtils.GetText("ui_achv_list_search_result") .. self.m_filteredDataCount
self.view.searchTxt.gameObject:SetActive(isSearchMode)
local filteredDataCount = #self.m_categoryFilteredData
local needFocus = focusIndex ~= nil
if isInit or needFocus then
if filteredDataCount ~= self.view.categoryList.count then
self.view.categoryList:UpdateCount(filteredDataCount, isInit, true)
elseif filteredDataCount ~= 0 and filteredDataCount == self.view.categoryList.count then
self.view.categoryList:UpdateShowingCells(function(csIndex, obj)
self:_RenderCategory(self.m_getCategoryCellFunc(obj), LuaIndex(csIndex))
end)
end
if filteredDataCount ~= 0 then
if DeviceInfo.usingController then
self.view.categoryList:FoldAll(true)
else
self.view.categoryList:FoldAll(false)
self.view.categoryList:ToggleByState(CSIndex(self.m_selectCategoryIndex), true, true)
end
end
if needFocus then
self.view.categoryList:ScrollToIndex(CSIndex(self.m_selectCategoryIndex), true)
self.view.categoryList:UpdateShowingCells(function(csIndex, obj)
self:_RenderCategory(self.m_getCategoryCellFunc(obj), LuaIndex(csIndex))
end)
end
self.m_isFold = false
else
self:_RefreshViews()
end
if self.m_categoryFilteredData and filteredDataCount > 0 then
local categoryInfo = self.m_categoryFilteredData[self.m_selectCategoryIndex]
if categoryInfo ~= nil and categoryInfo.haveSub then
local groupInfos = categoryInfo.filteredGroups
self.view.stateCtrl:SetState("HaveSub")
self.view.subTxt.text = groupInfos[self.m_selectGroupIndex].data.groupName
else
self.view.stateCtrl:SetState("NoSub")
end
else
self.view.stateCtrl:SetState("SearchNull")
end
local achievementCount = 0
self.m_currentRedDot = {}
local categoryInfo = self.m_categoryFilteredData[self.m_selectCategoryIndex]
if categoryInfo ~= nil then
local groupInfo = categoryInfo.filteredGroups[self.m_selectGroupIndex]
if groupInfo ~= nil then
achievementCount = #groupInfo.filteredInfos
for i = 1, achievementCount do
local achievementInfo = groupInfo.filteredInfos[i]
local achievementId = achievementInfo.achievementData.achieveId
if achievementSystem:IsAchievementUnread(achievementId) then
self.m_currentRedDot[achievementId] = SHOWING_RED_DOT
end
end
end
end
if needFocus then
self.view.achievementList:UpdateCount(achievementCount, focusIndex, false, false, true)
if DeviceInfo.usingController then
self.view.achievementList:UpdateShowingCells(function(csIndex, obj)
local luaIndex = LuaIndex(csIndex)
local cell = self.m_getAchievementCellFunc(obj)
self:_RenderAchievement(cell, luaIndex)
if luaIndex == focusIndex then
UIUtils.setAsNaviTarget(cell.button)
end
end)
end
else
self.view.achievementList:UpdateCount(achievementCount, true)
end
end
AchievementListCtrl._RefreshViews = HL.Method() << function(self)
self.view.categoryList:UpdateShowingCells(function(csIndex, obj)
self:_RenderCategory(self.m_getCategoryCellFunc(obj), LuaIndex(csIndex), true)
end)
end
AchievementListCtrl._RenderCategory = HL.Method(HL.Any, HL.Number, HL.Opt(HL.Boolean)) << function(self, cell, luaIndex, isRefresh)
local categoryInfo = self.m_categoryFilteredData[luaIndex]
if categoryInfo == nil then
return
end
isRefresh = isRefresh == true
local isSearchMode = not string.isEmpty(self.m_searchKey)
local hideRedDot = FILTER_CONFIGS[self.m_filterType].hideRedDot == true
cell:InitAchievementCategoryCell(categoryInfo, luaIndex, {
selectCategoryIndex = self.m_selectCategoryIndex,
selectGroupIndex = self.m_selectGroupIndex,
isSearchMode = isSearchMode,
needSetNavi = true,
isFold = self.m_isFold,
onCategoryClick = function(categoryIndex)
local selected = categoryIndex == self.m_selectCategoryIndex
if not DeviceInfo.usingController and categoryInfo.haveSub then
AudioAdapter.PostEvent((self.m_isFold or not selected) and "Au_UI_Toggle_AchieveDropDown_On" or "Au_UI_Toggle_AchieveDropDown_Off")
else
AudioAdapter.PostEvent("Au_UI_Toggle_Common_On")
end
if self.m_selectCategoryIndex == categoryIndex and not DeviceInfo.usingController then
self.view.categoryList:ToggleByState(CSIndex(categoryIndex), self.m_isFold)
self.m_isFold = not self.m_isFold
cell:UpdateArrow(self.m_isFold)
return
end
self:_SetSelectIndex(categoryIndex, 1)
end ,
onGroupClick = function(categoryIndex, groupIndex)
if categoryIndex == self.m_selectCategoryIndex and groupIndex == self.m_selectGroupIndex then
return
end
AudioAdapter.PostEvent("Au_UI_Toggle_Common_On")
self:_SetSelectIndex(categoryIndex, groupIndex)
end ,
onGroupCellRender = function(groupCell, groupIndex)
local groupInfo = categoryInfo.filteredGroups[groupIndex]
if groupInfo == nil then
return
end
groupCell.redDot:InitRedDot("AchievementGroup", groupInfo.data.groupId)
if isSearchMode or hideRedDot then
groupCell.redDotHolder.alpha = 0
else
groupCell.redDotHolder.alpha = 1
end
end,
})
cell.view.redDot:InitRedDot("AchievementCategory", categoryInfo.data.categoryId)
if isSearchMode or hideRedDot then
cell.view.redDotHolder.alpha = 0
else
cell.view.redDotHolder.alpha = 1
end
end
AchievementListCtrl._RenderAchievement = HL.Method(HL.Table, HL.Number) << function(self, cell, luaIndex)
local achievementSystem = GameInstance.player.achievementSystem
local categoryInfo = self.m_categoryFilteredData[self.m_selectCategoryIndex]
if categoryInfo == nil then
return
end
local groupInfo = categoryInfo.filteredGroups[self.m_selectGroupIndex]
if groupInfo == nil then
return
end
local achievementInfo = groupInfo.filteredInfos[luaIndex]
if achievementInfo == nil then
return
end
local achievementData = achievementInfo.achievementData
local achievementId = achievementData.achieveId
local maxLevel = 1
for i, levelInfo in pairs(achievementData.levelInfos) do
maxLevel = math.max(maxLevel, levelInfo.achieveLevel)
end
local playerInfo = achievementInfo.achievementPlayerInfo
local playerLevel = (playerInfo == nil) and 0 or playerInfo.level
local playerPlated = playerInfo ~= nil and playerInfo.isPlated
local playerObtainTs = (playerInfo == nil) and -1 or playerInfo.obtainTs
local timeInfo = achievementInfo.achievementTimeInfo
local curTs = DateTimeUtils.GetCurrentTimestampBySeconds()
local timeLimit = timeInfo ~= nil and curTs > timeInfo.openTime and curTs < timeInfo.closeTime
local isObtained = playerLevel >= achievementData.initLevel
local canUpgrade = isObtained and achievementData.canBeUpgraded and playerLevel < maxLevel
local canPlate = isObtained and achievementData.canBePlated and not playerPlated
local canRare = achievementData.applyRareEffect
local isRare = playerLevel >= Tables.achievementConst.levelDisplayEffect
local haveObtainLevel, obtainLevelInfo = achievementData.levelInfos:TryGetValue(achievementData.initLevel)
local isSearchMode = not string.isEmpty(self.m_searchKey)
if not isSearchMode and achievementSystem:IsAchievementUnread(achievementId) then
self:_CollectShowedRedDot(achievementId)
end
self:_UpdateBottomRedDot()
cell.name.text = achievementInfo.showName
cell.button.onClick:RemoveAllListeners()
cell.button.onClick:AddListener(function()
UIManager:Open(PanelId.AchievementDetailPopup, {achievementId = achievementId})
end)
cell.redDot:InitRedDot("AchievementItem", achievementId)
if isSearchMode then
cell.redDotHolder.alpha = 0
else
cell.redDotHolder.alpha = 1
end
if isObtained then
local medalBundle = {
achievementId = achievementData.achieveId,
level = playerLevel,
isPlated = playerPlated,
isRare = canRare
}
cell.medal:InitMedal(medalBundle)
end
cell.stateCtrl:SetState(isObtained and "Acquired" or "Unattained")
cell.stateCtrl:SetState(canUpgrade and "PromoteReforge" or (canPlate and "PromoteCladding" or "PromoteNull"))
cell.stateCtrl:SetState(canRare and (isRare and "QualifyPossess" or "QualifyNotPossess") or "QualifyNull")
cell.stateCtrl:SetState(timeLimit and "TimeLimit" or "TimeUnlimit")
if isObtained then
cell.obtainTimeTxt.text = Utils.timestampToDateYMD(playerObtainTs)
local currLevelText = ''
local haveCurrLevelInfo, currLevelInfo = achievementData.levelInfos:TryGetValue(playerLevel)
if haveCurrLevelInfo then
currLevelText = currLevelInfo.completeDesc
end
cell.descTxt.text = currLevelText
else
cell.descTxt.text = achievementData.desc
end
local conditionCount = 0
if haveObtainLevel and obtainLevelInfo ~= nil then
local condition = obtainLevelInfo.conditions[0]
conditionCount = #obtainLevelInfo.conditions
local conditionText = condition.desc
if not isObtained then
local conditionProgress, conditionTarget = self:_CalcAchievementCondition(obtainLevelInfo.conditions, playerInfo)
conditionText = conditionText .. string.format(Language.LUA_ACHIEVEMENT_CONDITION_TARGET, conditionProgress, conditionTarget)
end
cell.conditionTxt.text = conditionText
end
cell.moreCondition.gameObject:SetActive(not isObtained and (achievementData.canBeUpgraded or achievementData.canBePlated))
end
AchievementListCtrl._CalcAchievementCondition = HL.Method(HL.Any, HL.Any).Return(HL.Number, HL.Number) << function(self, conditions, playerInfo)
local progress = 0
local target = 0
for _, condition in pairs(conditions) do
if playerInfo ~= nil and playerInfo.condition ~= nil then
local suc, playerConditionVal = playerInfo.condition.conditionVals:TryGetValue(condition.conditionId)
if suc then
progress = progress + playerConditionVal
end
end
target = target + condition.progressToCompare
end
return progress, target
end
AchievementListCtrl._SetFilter = HL.Method(HL.Number) << function(self, filterType)
self.m_filterType = filterType
self:_LoadFilteredData()
self:_ResetSelectIndex()
self:_RenderViews(true)
end
AchievementListCtrl._SetSearchKey = HL.Method(HL.String) << function(self, searchKey)
if self.m_searchKey == searchKey then
return
end
self.m_searchKey = searchKey
self:_LoadFilteredData()
self:_ResetSelectIndex()
self:_RenderViews(true)
end
AchievementListCtrl._ResetSearch = HL.Method() << function(self)
self.view.inputField.text = ''
end
AchievementListCtrl._SetSelectIndex = HL.Method(HL.Number, HL.Number, HL.Opt(HL.Number)) << function(self, categoryIndex, groupIndex, achievementIndex)
if achievementIndex == nil and categoryIndex == self.m_selectCategoryIndex and groupIndex == self.m_selectGroupIndex then
return
end
local prevCategory = self.m_selectCategoryIndex
self.m_selectCategoryIndex = categoryIndex
self.m_selectGroupIndex = groupIndex
if achievementIndex ~= nil then
self:_RenderViews(false, achievementIndex)
else
self:_RenderViews(false)
end
if prevCategory ~= self.m_selectCategoryIndex and not DeviceInfo.usingController then
if not self.m_isFold then
self.view.categoryList:ToggleByState(CSIndex(prevCategory), false, true)
self.m_isFold = true
end
local categoryInfo = self.m_categoryFilteredData[self.m_selectCategoryIndex]
if categoryInfo.haveSub then
self.view.categoryList:ToggleByState(CSIndex(self.m_selectCategoryIndex), true)
self.m_isFold = false
end
end
end
AchievementListCtrl._TryFocusAchievement = HL.Method(HL.String).Return(HL.Boolean) << function(self, achievementId)
local needReset = false
if self.m_filterType ~= ALL_FILTER_TYPE then
self.m_filterType = ALL_FILTER_TYPE
needReset = true
end
if not string.isEmpty(self.m_searchKey) then
self:_ResetSearch()
needReset = true
end
if needReset then
self:_LoadFilteredData()
end
local achievementIndexInfo = self.m_filteredAchievementMap[achievementId]
if achievementIndexInfo == nil then
return false
end
self:_SetSelectIndex(achievementIndexInfo.categoryIndex, achievementIndexInfo.groupIndex, achievementIndexInfo.achievementIndex)
return true
end
AchievementListCtrl._CollectShowedRedDot = HL.Method(HL.String) << function(self, achievementId)
if self.m_viewedAchievements == nil then
self.m_viewedAchievements = {}
end
self.m_viewedAchievements[achievementId] = SHOWING_RED_DOT
end
AchievementListCtrl._ClearShowedRedDot = HL.Method() << function(self)
if self.m_viewedAchievements == nil then
return
end
local achievementSystem = GameInstance.player.achievementSystem
for id, flag in pairs(self.m_viewedAchievements) do
if flag == SHOWING_RED_DOT and achievementSystem:IsAchievementUnread(id) then
achievementSystem:ReadAchievement(id)
end
end
self.m_viewedAchievements = {}
end
AchievementListCtrl._UpdateBottomRedDot = HL.Method() << function(self)
if self.m_currentRedDot == nil or self.m_viewedAchievements == nil or not string.isEmpty(self.m_searchKey) then
self.view.redDot.gameObject:SetActive(false)
return
end
for id, flag in pairs(self.m_currentRedDot) do
if self.m_viewedAchievements[id] == nil then
self.view.redDot.gameObject:SetActive(true)
return
end
end
self.view.redDot.gameObject:SetActive(false)
end
HL.Commit(AchievementListCtrl)