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

1521 lines
41 KiB
Lua

local InteractOptionIdentifier = CS.Beyond.Gameplay.Core.InteractOptionIdentifier
local InteractOptionType = CS.Beyond.Gameplay.Core.InteractOptionType
local uiCtrl = require_ex('UI/Panels/Base/UICtrl')
local PANEL_ID = PanelId.InteractOption
InteractOptionCtrl = HL.Class('InteractOptionCtrl', uiCtrl.UICtrl)
InteractOptionCtrl.s_messages = HL.StaticField(HL.Table) << {
[MessageConst.ADD_INTERACT_OPTION] = 'AddInteractOption',
[MessageConst.UPDATE_INTERACT_OPTION] = 'UpdateInteractOption',
[MessageConst.REMOVE_INTERACT_OPTION] = 'RemoveInteractOption',
[MessageConst.ADD_INTERACT_OPTIONS] = 'AddInteractOptions',
[MessageConst.UPDATE_INTERACT_OPTIONS] = 'UpdateInteractOptions',
[MessageConst.REMOVE_INTERACT_OPTIONS] = 'RemoveInteractOptions',
[MessageConst.ON_SCENE_LOAD_START] = 'ClearUIOptions',
[MessageConst.ON_MAINCHAR_ENTER_MUD] = 'RefreshActiveState',
[MessageConst.ON_MAINCHAR_LEAVE_MUD] = 'RefreshActiveState',
[MessageConst.ON_SQUAD_INFIGHT_CHANGED] = 'RefreshActiveState',
[MessageConst.CAMERA_HIDE_HUD] = 'CameraHideHud',
[MessageConst.CAMERA_END_HIDE_HUD] = 'CameraEndHideHud',
[MessageConst.ON_FAC_DESTROY_MODE_CHANGE] = 'OnFacDestroyModeChange',
[MessageConst.TOGGLE_HIDE_INTERACT_OPTION_LIST] = 'OnToggleHideInteractOptionList',
[MessageConst.FOCUS_ON_INTERACT_OPTION] = 'FocusOnInteractOption',
}
local MAX_DISPLAY_COUNT = 999
InteractOptionCtrl.m_optionInfoMap = HL.Field(HL.Table)
InteractOptionCtrl.m_curShowingOptInfoList = HL.Field(HL.Table)
InteractOptionCtrl.m_curShowingOptCount = HL.Field(HL.Number) << 0
InteractOptionCtrl.m_curSelectedOptIdentifier = HL.Field(HL.String) << ""
InteractOptionCtrl.m_cellObjCache = HL.Field(HL.Forward("CommonCache"))
InteractOptionCtrl.m_obj2CellMap = HL.Field(HL.Table)
InteractOptionCtrl.m_playingOutInfoTimers = HL.Field(HL.Table)
InteractOptionCtrl.m_viewGroupConfig = HL.Field(HL.Table)
InteractOptionCtrl.m_sortFunc = HL.Field(HL.Function)
InteractOptionCtrl.OnCreate = HL.Override(HL.Any) << function(self, arg)
UIUtils.bindInputPlayerAction("common_interact", function()
if self.m_curSelectedOptIdentifier ~= self.m_pressStartIdentifier and not InputManager.instance.guideUseActionIds:Contains("common_interact") then
return
end
self.m_pressStartIdentifier = nil
InputManagerInst:TryClickSkillButtonWhenControllerIndicatorJustRelease(1)
local info = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
if info and self.view.listNode.gameObject.activeSelf then
if not info.identifier.isPickable then
self:_OnClickOption(self.m_curSelectedOptIdentifier)
return
end
end
end, self.view.listNodeInputBindingGroupMonoTarget.groupId)
self.m_optionInfoMap = {}
self.m_curShowingOptInfoList = {}
self.m_hideListKeys = {}
self.m_sortFunc = Utils.genSortFunction({
"isItemOrder",
"typeOrder",
"sortId",
"seqNum",
"sourceId",
"subIndex"
}, true)
local CommonCache = require_ex("Common/Utils/CommonCache")
self.m_cellObjCache = CommonCache(function()
local obj = CSUtils.CreateObject(self.view.optionItem.gameObject, self.view.listContainer.transform)
return obj
end)
self.view.optionItem.gameObject:SetActive(false)
self:_CacheCell(self.view.optionItem)
self.m_obj2CellMap = {}
self.m_playingOutInfoTimers = {}
self.m_viewGroupConfig = InteractOptionConst.INTERACT_OPTION_VIEW_GROUP_CONFIG
self:BindInputPlayerAction("interact_option_next", function()
self:_OnScroll(-1)
end)
self:BindInputPlayerAction("interact_option_before", function()
self:_OnScroll(1)
end)
self:BindInputPlayerAction("common_interact_pickup_all_start", function()
InputManagerInst:TryClickSkillButtonWhenControllerIndicatorJustRelease(1)
self:_OnPressOptionStart(true)
end)
self:BindInputPlayerAction("common_interact_pickup_all_end", function()
self:_OnPressOptionEnd(true)
end)
self:_RefreshScrollHint()
self:RefreshActiveState()
self.view.btnHint.gameObject:SetActive(false)
end
InteractOptionCtrl.m_updateCor = HL.Field(HL.Thread)
InteractOptionCtrl.m_needUpdateList = HL.Field(HL.Boolean) << false
InteractOptionCtrl.m_nextUpdateNeedToTop = HL.Field(HL.Boolean) << true
InteractOptionCtrl._TryUpdateListTick = HL.Method() << function(self)
if self.m_needUpdateList then
self:_UpdateCurShowingList(self.m_nextUpdateNeedToTop)
self:_ScrollTo(self.m_curShowingOptCount)
end
self:_UpdateBtnHint()
end
InteractOptionCtrl.OnShow = HL.Override() << function(self)
self:_RefreshList()
self:_Register()
end
InteractOptionCtrl.OnHide = HL.Override() << function(self)
Notify(MessageConst.ON_TOGGLE_INTERACT_OPTION_SCROLL, false)
self:_ClearRegister()
self:_InterruptPickupAll()
end
InteractOptionCtrl.OnClose = HL.Override() << function(self)
Notify(MessageConst.ON_TOGGLE_INTERACT_OPTION_SCROLL, false)
self:_ClearRegister()
self:_InterruptPickupAll()
end
InteractOptionCtrl.m_onScroll = HL.Field(HL.Function)
InteractOptionCtrl._Register = HL.Method() << function(self)
local touchPanel = UIManager.commonTouchPanel
if not self.m_onScroll then
self.m_onScroll = function(delta)
self:_OnScroll(delta)
end
end
touchPanel.onScroll:AddListener(self.m_onScroll)
self.m_updateCor = self:_StartCoroutine(function()
while true do
coroutine.step()
self:_TryUpdateListTick()
end
end)
end
InteractOptionCtrl._ClearRegister = HL.Method() << function(self)
local touchPanel = UIManager.commonTouchPanel
touchPanel.onScroll:RemoveListener(self.m_onScroll)
self.m_updateCor = self:_ClearCoroutine(self.m_updateCor)
end
InteractOptionCtrl.OnFacDestroyModeChange = HL.Method(HL.Boolean) << function(self, inDestroyMode)
self:_ClearShowingUIOptions()
self:_UpdateCurShowingList(true)
end
InteractOptionCtrl.m_hideListKeys = HL.Field(HL.Table)
InteractOptionCtrl.OnToggleHideInteractOptionList = HL.Method(HL.Table) << function(self, args)
local key, hide = unpack(args)
self:ToggleHideInteractOptionList(key, hide)
end
InteractOptionCtrl.ToggleHideInteractOptionList = HL.Method(HL.String, HL.Boolean) << function(self, key, hide)
if hide then
self.m_hideListKeys[key] = true
else
self.m_hideListKeys[key] = nil
end
self:RefreshActiveState()
end
InteractOptionCtrl.RefreshActiveState = HL.Method(HL.Opt(HL.Any)) << function(self, _)
local shouldHide = next(self.m_hideListKeys) or
Utils.isInSettlementDefenseDefending()
self.view.listNode.gameObject:SetActive(not shouldHide)
if not shouldHide then
for i, info in pairs(self.m_optionInfoMap) do
self:_UpdateOptionSelected(info)
end
end
end
InteractOptionCtrl.CameraHideHud = HL.Method() << function(self)
self:ToggleHideInteractOptionList("CameraHideHud", true)
end
InteractOptionCtrl.CameraEndHideHud = HL.Method() << function(self)
self:ToggleHideInteractOptionList("CameraHideHud", false)
end
InteractOptionCtrl.AddInteractOption = HL.Method(HL.Table) << function(self, args)
local info = self:_GetSingleInteractOptionInfo(args)
local key = info.identifier.value
if self.m_optionInfoMap[key] then
self:UpdateInteractOption(args)
return
end
local oldInfo = self.m_optionInfoMap[key]
if oldInfo and oldInfo.identifier then
self.m_optionInfoMap[key].identifier:Recycle()
end
self.m_optionInfoMap[key] = info
self.m_needUpdateList = true
self.m_nextUpdateNeedToTop = true
end
InteractOptionCtrl.UpdateInteractOption = HL.Method(HL.Table) << function(self, args)
local newInfo = self:_GetSingleInteractOptionInfo(args)
if newInfo.identifier.subIndex < 0 then
logger.error("identifier.subIndex < 0", inspect(args))
newInfo.identifier:Recycle()
return
end
local key = newInfo.identifier.value
if not self.m_optionInfoMap[key] then
logger.error("没有该选项", inspect(args))
newInfo.identifier:Recycle()
return
end
local oldInfo = self.m_optionInfoMap[key]
if self.m_playingOutInfoTimers[oldInfo] then
self:_ClearTimer(self.m_playingOutInfoTimers[oldInfo])
self.m_playingOutInfoTimers[oldInfo] = nil
self:_PlayOptionAnimation(oldInfo.cell, true)
AudioAdapter.PostEvent("au_ui_btn_f_menubar_appear")
end
newInfo.cell = oldInfo.cell
newInfo.time = oldInfo.time
newInfo.overrideText = oldInfo.overrideText
if not newInfo.icon then
newInfo.icon = oldInfo.icon
newInfo.iconFolder = oldInfo.iconFolder
end
if not newInfo.action then
newInfo.action = oldInfo.action
end
self.m_optionInfoMap[key] = newInfo
if not newInfo.cell then
oldInfo.identifier:Recycle()
return
end
local index = lume.find(self.m_curShowingOptInfoList, oldInfo)
self.m_curShowingOptInfoList[index] = newInfo
self:_OnUpdateCell(newInfo)
if args.needReSort then
self:_SortInteractOptionList()
local newIndex = lume.find(self.m_curShowingOptInfoList, newInfo)
if newIndex == index then
oldInfo.identifier:Recycle()
return
end
newInfo.cell.gameObject.transform:SetSiblingIndex(CSIndex(newIndex))
self:_RefreshListViewGroup()
if args.setTopAsSelectedWhenSort then
self:_SetSelected(self.m_curShowingOptInfoList[1].identifier.value)
end
end
oldInfo.identifier:Recycle()
end
InteractOptionCtrl.RemoveInteractOption = HL.Method(HL.Table) << function(self, args)
local isList = lume.isarray(args)
local identifier
if isList then
identifier = InteractOptionIdentifier.CreateInstance(unpack(args))
elseif args.type ~= nil then
identifier = InteractOptionIdentifier.CreateInstance(args.type, args.sourceId or "0", args.subIndex or 0)
end
if not identifier then
logger.error(ELogChannel.UI, "[InteractOption] 移除交互物选项时 Identifier 为空,移除失败。")
return
end
local needDel = {}
for key, info in pairs(self.m_optionInfoMap) do
if identifier:Includes(info.identifier) then
local cell = info.cell
if cell then
self:_DeleteCell(cell, info)
else
info.identifier:Recycle()
table.insert(needDel, key)
end
end
end
identifier:Recycle()
for _, key in pairs(needDel) do
self.m_optionInfoMap[key] = nil
end
end
InteractOptionCtrl._OnOptOutAnimFinished = HL.Method(HL.Table) << function(self, info)
local cell = info.cell
self.m_playingOutInfoTimers[info] = nil
self:_CacheCell(cell)
self.m_optionInfoMap[info.identifier.value] = nil
self:_UpdateCurShowingList()
info.identifier:Recycle()
end
InteractOptionCtrl.m_nextOptSeqNum = HL.Field(HL.Number) << 1
InteractOptionCtrl._ParseOptionData = HL.Method(HL.Any).Return(HL.Table) << function(self, optionData)
local optionInfo = {
data = optionData
}
local identifier
local argsType = type(optionData)
if argsType == "table" then
if lume.isarray(optionData) then
identifier = optionData[1]
if identifier then
identifier = InteractOptionIdentifier.CreateInstance(identifier)
end
optionInfo.text = optionData[2]
optionInfo.action = optionData[3]
optionInfo.icon = optionData[4]
optionInfo.iconFolder = optionData[5]
optionInfo.sortId = optionData[6]
else
if optionData.type ~= nil then
identifier = InteractOptionIdentifier.CreateInstance(optionData.type, optionData.sourceId or "0", optionData.subIndex or 0)
setmetatable(optionInfo, { __index = optionData })
end
end
elseif argsType == "userdata" then
identifier = optionData.identifier
if identifier then
identifier = InteractOptionIdentifier.CreateInstance(identifier)
end
end
if identifier == nil then
logger.error("InteractOptionCtrl._ParseOptionData: No valid identifier")
return optionInfo
end
optionInfo.identifier = identifier
optionInfo.typeOrder = identifier.type:GetHashCode()
optionInfo.isItem = false
local optionType = identifier.type
local parseFunction = InteractOptionConst.INTERACT_OPTION_PARSE_OPTION_DATA_CONFIG[optionType]
if parseFunction ~= nil then
parseFunction(optionInfo, optionData)
end
optionInfo.isItemOrder = optionInfo.isItem and 0 or 1
optionInfo.sourceId = optionInfo.sourceId or optionInfo.identifier.sourceId
optionInfo.subIndex = optionInfo.identifier.subIndex
optionInfo.sortId = optionInfo.sortId or 0
return optionInfo
end
InteractOptionCtrl._GetSingleInteractOptionInfo = HL.Method(HL.Any).Return(HL.Table) << function(self, optionData)
local optionInfo = self:_ParseOptionData(optionData)
if optionInfo == nil then
return optionInfo
end
if optionInfo.identifier.ignoreSeqNum then
optionInfo.seqNum = 0
else
optionInfo.seqNum = self.m_nextOptSeqNum
self.m_nextOptSeqNum = self.m_nextOptSeqNum + 1
end
return optionInfo
end
InteractOptionCtrl._GetGroupInteractOptions = HL.Method(HL.Any).Return(HL.Table) << function(self, optionDataList)
local optionInfoList = {}
if optionDataList == nil then
return optionInfoList
end
local argsType = type(optionDataList)
if argsType == "table" then
for _, optionArg in pairs(optionDataList) do
local optionInfo = self:_ParseOptionData(optionArg)
if optionInfo ~= nil then
table.insert(optionInfoList, optionInfo)
end
end
elseif argsType == "userdata" then
for i = 0, optionDataList.Count - 1 do
local optionInfo = self:_ParseOptionData(optionDataList[i])
if optionInfo ~= nil then
table.insert(optionInfoList, optionInfo)
end
end
end
if #optionInfoList > 0 then
for _, optionInfo in pairs(optionInfoList) do
if optionInfo.identifier.ignoreSeqNum then
optionInfo.seqNum = 0
else
optionInfo.seqNum = self.m_nextOptSeqNum
end
end
end
self.m_nextOptSeqNum = self.m_nextOptSeqNum + 1
return optionInfoList
end
InteractOptionCtrl._OnScroll = HL.Method(HL.Number) << function(self, delta)
if self.m_curShowingOptCount <= 1 or self.m_isFocusingForGuide then
return
end
local oldInfo = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
local oldIndex = lume.find(self.m_curShowingOptInfoList, oldInfo)
local newIndex = lume.clamp(oldIndex + (delta < 0 and 1 or -1), 1, self.m_curShowingOptCount)
if newIndex == oldIndex then
return
end
self:_SelectOption(newIndex)
end
InteractOptionCtrl._SelectOption = HL.Method(HL.Number) << function(self, newIndex)
local newInfo = self.m_curShowingOptInfoList[newIndex]
self:_SetSelected(newInfo.identifier.value)
self:_ScrollTo(newIndex)
AudioAdapter.PostEvent("au_ui_btn_f_menubar_highlight")
end
InteractOptionCtrl._OnUpdateCell = HL.Method(HL.Table) << function(self, info)
local cell = info.cell
local key = info.identifier.value
local isItem = info.isItem
local isDel = info.isDel == true
cell.normalNode.gameObject:SetActiveIfNecessary(not isDel)
cell.delNode.gameObject:SetActiveIfNecessary(isDel)
local node = isDel and cell.delNode or cell.normalNode
if node.itemNode then
node.itemNode.gameObject:SetActive(isItem)
end
node.simpleNode.gameObject:SetActive(not isItem)
if isItem then
if not string.isEmpty(info.itemId) then
node.itemNode.numTxt.text = info.count <= MAX_DISPLAY_COUNT and UIUtils.getNumString(info.count) or Language.LUA_INTERACT_OPTION_MAX_TEXT
local itemData = Tables.itemTable:GetValue(info.itemId)
if info.useOverrideName then
node.nameTxt.text = info.text
else
node.nameTxt.text = itemData.name
end
node.itemNode.iconImg:LoadSprite(UIConst.UI_SPRITE_ITEM, itemData.iconId)
local isPickUp, _ = Tables.useItemTable:TryGetValue(info.itemId)
node.itemNode.pickUpNode.gameObject:SetActive(isPickUp)
UIUtils.setItemRarityImage(node.itemNode.rarityImg, itemData.rarity)
else
node.nameTxt.text = "ERROR: No ItemId"
logger.error("InteractOptionCtrl._OnUpdateCell: No itemId")
end
else
local icon = info.icon
if not info.icon then
icon = "btn_common_exchange_icon"
end
local iconFolder = info.iconFolder or UIConst.UI_SPRITE_INTERACT_OPTION_ICON
node.simpleIcon:LoadSprite(iconFolder, icon)
node.nameTxt.text = info.overrideText or info.text
end
cell.button.enabled = true
cell.button.onClick:RemoveAllListeners()
cell.button.onHoverChange:RemoveAllListeners()
cell.button.onPressStart:RemoveAllListeners()
cell.button.onPressEnd:RemoveAllListeners()
local isPickable = info.identifier.isPickable
if isPickable then
cell.button.onPressStart:AddListener(function(_)
self:_OnPressOptionStart()
end)
else
cell.button.onClick:AddListener(function()
info.clicked = true
self:_OnClickOption(key)
end)
end
cell.button.onHoverChange:AddListener(function(isHover)
if isHover then
self:_SetSelected(key)
end
end)
cell.button.onPressEnd:AddListener(function(eventData)
local newInfo = self.m_optionInfoMap[key]
self:_UpdateOptionSelected(newInfo)
if isPickable then
if not eventData or (eventData.position - eventData.pressPosition).sqrMagnitude < 1 then
self:_OnPressOptionEnd()
else
self:_InterruptPickupAll()
end
end
end)
self:_UpdateOptionSelected(info)
self:_UpdateCellObjectName(info)
end
InteractOptionCtrl._UpdateCellObjectName = HL.Method(HL.Table) << function(self, info)
local cell = info.cell
if info.gameObjectName then
if info.sourceId == "SubBuilding" and InteractOptionCtrl.s_subBuildingOptUseIndexNameForGuide then
cell.gameObject.name = info.gameObjectName .. "-" .. info.subBuildingIndex
else
cell.gameObject.name = info.gameObjectName
end
else
cell.gameObject.name = "OptionItem-" .. info.identifier.value
end
cell.button.gameObject.name = "Content-" .. cell.gameObject.name
CS.Beyond.Gameplay.Conditions.OnInteractOptionShow.Trigger(cell.gameObject.name)
end
InteractOptionCtrl._SetSelected = HL.Method(HL.String) << function(self, newIdentifier)
if self.m_isFocusingForGuide then
return
end
local oldInfo = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
local newInfo = self.m_optionInfoMap[newIdentifier]
self.m_curSelectedOptIdentifier = newIdentifier
self:_InterruptPickupAll()
self:_UpdateOptionSelected(oldInfo)
self:_UpdateOptionSelected(newInfo)
end
InteractOptionCtrl._RefreshScrollHint = HL.Method() << function(self)
local showScroll = self:IsShow() and self.m_curShowingOptCount > 1
self.view.scrollHint.gameObject:SetActive(showScroll)
Notify(MessageConst.ON_TOGGLE_INTERACT_OPTION_SCROLL, showScroll)
end
InteractOptionCtrl._UpdateOptionSelected = HL.Method(HL.Table) << function(self, info)
if info == nil then
return
end
local cell = info.cell
if not cell then
return
end
local isSelected
if DeviceInfo.usingTouch then
isSelected = not LuaSystemManager.factory.inDestroyMode
else
isSelected = info.identifier.value == self.m_curSelectedOptIdentifier
end
if isSelected then
cell.animator:ResetTrigger("Normal")
if cell.gameObject.activeInHierarchy then
cell.animator:Play("Highlighted")
end
else
cell.animator:ResetTrigger("Highlighted")
cell.animator:SetTrigger("Normal")
end
end
InteractOptionCtrl._UpdateBtnHint = HL.Method() << function(self)
local cell
if not string.isEmpty(self.m_curSelectedOptIdentifier) then
local info = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
if info then
cell = info.cell
else
logger.error("InteractOptionCtrl._UpdateBtnHint No m_curSelectedOptIdentifier", self.m_curSelectedOptIdentifier, self.m_optionInfoMap)
if self.m_curShowingOptCount > 0 then
self:_SelectOption(1)
else
self.m_curSelectedOptIdentifier = ""
end
self:_UpdateBtnHint()
return
end
end
local btnHint = self.view.btnHint
if cell then
if not btnHint.gameObject.activeSelf then
btnHint.gameObject:SetActive(true)
btnHint.animation:Play()
end
btnHint.transform.anchoredPosition = Vector2(27, cell.transform.anchoredPosition.y + cell.button.transform.anchoredPosition.y)
else
btnHint.gameObject:SetActive(false)
end
end
InteractOptionCtrl._SortInteractOptionList = HL.Method() << function(self)
if self.m_curShowingOptInfoList == nil or #self.m_curShowingOptInfoList <= 1 then
return
end
table.sort(self.m_curShowingOptInfoList, self.m_sortFunc)
end
InteractOptionCtrl._OnClickOption = HL.Method(HL.String) << function(self, identifier)
local info = self.m_optionInfoMap[identifier]
if not info then
return
end
if self.m_playingOutInfoTimers[info] then
return
end
local t = info.identifier.type
local audio = InteractOptionConst.INTERACT_OPTION_CLICK_AUDIO_CONFIG[t]
AudioManager.PostEvent(audio or "au_ui_g_click")
if not GameInstance.playerController.mainCharacter then
return
end
if GameWorld.battle.isSquadInFight then
if not UIConst.INT_BATTLE_ENABLED_OPT_TYPES[t] then
GameAction.ShowUIToast(Language.LUA_INTERACT_OPTION_IN_FIGHT)
return
end
end
if t == InteractOptionType.Factory or t == InteractOptionType.Crop then
local isForbid, forbidReason = Utils.isForbiddenWithReason(ForbidType.ForbidInteractFacBuilding)
if isForbid then
Notify(MessageConst.SHOW_RADIO, { GameAction.RadioRuntimeData(forbidReason.radioId, true, 0) })
return
end
end
info.action()
end
InteractOptionCtrl._ClearShowingUIOptions = HL.Method() << function(self)
for info, timer in pairs(self.m_playingOutInfoTimers) do
self:_ClearTimer(timer)
self:_CacheCell(info.cell)
self.m_optionInfoMap[info.identifier.value] = nil
end
self.m_playingOutInfoTimers = {}
end
InteractOptionCtrl.ClearUIOptions = HL.Method(HL.Opt(HL.Any)) << function(self, arg)
self:_ClearShowingUIOptions()
self.m_curShowingOptInfoList = {}
self:_UpdateCurShowingList(true)
end
InteractOptionCtrl._GetNewCell = HL.Method().Return(HL.Table) << function(self)
local obj = self.m_cellObjCache:Get()
local cell = self.m_obj2CellMap[obj]
if not cell then
obj.gameObject:SetActive(true)
cell = Utils.wrapLuaNode(obj)
self.m_obj2CellMap[obj] = cell
end
self:_PlayOptionAnimation(cell, true)
cell.layoutElement.ignoreLayout = false
cell.transform.localScale = Vector3.one
AudioAdapter.PostEvent("au_ui_btn_f_menubar_appear")
return cell
end
InteractOptionCtrl._CacheCell = HL.Method(HL.Table) << function(self, cell)
if cell == nil then
return
end
cell.layoutElement.ignoreLayout = true
cell.transform.localScale = Vector3.zero
cell.transform.anchoredPosition = Vector2(9999, 9999)
cell.gameObject.name = "CachedCell"
cell.button.gameObject.name = "Content"
cell.button.onClick:RemoveAllListeners()
cell.button.onHoverChange:RemoveAllListeners()
cell.button.onPressStart:RemoveAllListeners()
cell.button.onPressEnd:RemoveAllListeners()
self.m_cellObjCache:Cache(cell.gameObject)
end
InteractOptionCtrl._DeleteCell = HL.Method(HL.Table, HL.Table) << function(self, cell, optionInfo)
if cell == nil or optionInfo == nil then
return
end
if not self.m_playingOutInfoTimers[optionInfo] then
if self:IsShow() then
cell.button.enabled = false
self:_PlayOptionAnimation(cell, false)
AudioAdapter.PostEvent("au_ui_btn_f_menubar_dissappear")
cell.animator:Update(0)
self.m_playingOutInfoTimers[optionInfo] = self:_StartTimer(self.view.config.OPTION_CELL_OUT_ANIM_TIME, function()
self:_OnOptOutAnimFinished(optionInfo)
end)
else
self:_OnOptOutAnimFinished(optionInfo)
end
end
end
InteractOptionCtrl._UpdateCurShowingList = HL.Method(HL.Opt(HL.Boolean)) << function(self, toTop)
self.m_needUpdateList = false
local oldSelectedInfo, oldSelectedIndex
if not toTop and not string.isEmpty(self.m_curSelectedOptIdentifier) then
oldSelectedInfo = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
oldSelectedIndex = lume.find(self.m_curShowingOptInfoList, oldSelectedInfo)
end
self.m_curShowingOptInfoList = {}
self.m_curShowingOptCount = 0
for _, v in pairs(self.m_optionInfoMap) do
if not LuaSystemManager.factory.inDestroyMode or v.isDel then
table.insert(self.m_curShowingOptInfoList, v)
self.m_curShowingOptCount = self.m_curShowingOptCount + 1
else
if v.cell then
self:_CacheCell(v.cell)
v.cell = nil
end
end
end
self:_SortInteractOptionList()
local newSelectedInfo
if oldSelectedInfo then
local newSelectIndex = lume.find(self.m_curShowingOptInfoList, oldSelectedInfo)
if newSelectIndex then
newSelectedInfo = oldSelectedInfo
else
newSelectedInfo = self.m_curShowingOptInfoList[math.min(oldSelectedIndex, self.m_curShowingOptCount)]
end
else
newSelectedInfo = self.m_curShowingOptInfoList[1]
end
self.m_curSelectedOptIdentifier = newSelectedInfo and newSelectedInfo.identifier.value or ""
self:_InterruptPickupAll()
self:_RefreshList()
end
InteractOptionCtrl._RefreshList = HL.Method() << function(self)
self:_RefreshScrollHint()
for index, info in ipairs(self.m_curShowingOptInfoList) do
local cell = info.cell
if not cell then
cell = self:_GetNewCell()
info.cell = cell
end
self:_OnUpdateCell(info)
cell.gameObject.transform:SetSiblingIndex(CSIndex(index))
end
self:_RefreshListViewGroup()
local tooMuchCount = #self.m_curShowingOptInfoList > 1 and self.view.listContainer.rect.height > (self.view.optionList.transform.rect.height + 1)
self.view.optionListImage.raycastTarget = tooMuchCount
end
InteractOptionCtrl._ScrollTo = HL.Method(HL.Number) << function(self, index)
if index <= 0 or self.m_isFocusingForGuide then
return
end
local cell = self.m_curShowingOptInfoList[index].cell
self.view.optionList:ScrollToNaviTarget(cell.button)
end
InteractOptionCtrl._AddGroupInteractOptions = HL.Method(HL.Any) << function(self, optionDataList)
if optionDataList == nil then
return
end
local optionInfoList = self:_GetGroupInteractOptions(optionDataList)
if optionInfoList == nil or #optionInfoList == 0 then
return
end
local groupSourceId
for _, optionInfo in pairs(optionInfoList) do
local identifier = optionInfo.identifier
if groupSourceId == nil then
groupSourceId = identifier.sourceId
else
if groupSourceId ~= identifier.sourceId then
logger.error("InteractOptionCtrl.AddInteractOptions: Group options inconsistent source id")
for _, optionInfo in pairs(optionInfoList) do
if optionInfo and optionInfo.identifier then
optionInfo.identifier:Recycle()
end
end
return
end
end
end
for _, optionInfo in pairs(optionInfoList) do
local key = optionInfo.identifier.value
if self.m_optionInfoMap[key] and self.m_optionInfoMap[key].identifier then
self.m_optionInfoMap[key].identifier:Recycle()
end
self.m_optionInfoMap[key] = optionInfo
end
end
InteractOptionCtrl._RemoveGroupInteractOptions = HL.Method(HL.String) << function(self, groupSourceId)
if string.isEmpty(groupSourceId) then
return
end
local deleteList = {}
for key, optionInfo in pairs(self.m_optionInfoMap) do
local identifier = optionInfo.identifier
if identifier ~= nil and identifier.sourceId == groupSourceId then
local cell = optionInfo.cell
if cell then
self:_DeleteCell(cell, optionInfo)
else
identifier:Recycle()
end
table.insert(deleteList, key)
end
end
for _, deleteKey in ipairs(deleteList) do
self.m_optionInfoMap[deleteKey] = nil
end
end
InteractOptionCtrl.AddInteractOptions = HL.Method(HL.Any) << function(self, args)
if args == nil then
return
end
local optionDataList = args[1] or args.optionDataList
if optionDataList == nil then
return
end
self:_AddGroupInteractOptions(optionDataList)
self.m_needUpdateList = true
self.m_nextUpdateNeedToTop = true
end
InteractOptionCtrl.UpdateInteractOptions = HL.Method(HL.Any) << function(self, args)
if args == nil then
return
end
local groupSourceId, optionDataList
if lume.isarray(args) then
groupSourceId, optionDataList = unpack(args)
else
groupSourceId, optionDataList = args.groupSourceId, args.optionDataList
end
if string.isEmpty(groupSourceId) or optionDataList == nil then
return
end
self:_RemoveGroupInteractOptions(groupSourceId)
self:_AddGroupInteractOptions(optionDataList)
self:_UpdateCurShowingList(false)
end
InteractOptionCtrl.RemoveInteractOptions = HL.Method(HL.Any) << function(self, args)
if args == nil then
return
end
local groupSourceId = args[1] or args.groupSourceId
if string.isEmpty(groupSourceId) then
return
end
self:_RemoveGroupInteractOptions(groupSourceId)
self:_UpdateCurShowingList(false)
end
InteractOptionCtrl._RefreshListViewGroup = HL.Method() << function(self)
if self.m_curShowingOptInfoList == nil then
return
end
local findGroup = false
local groupDataList = {}
if self.m_curShowingOptCount > 0 then
local optIndex = 1
repeat
local optInfo = self.m_curShowingOptInfoList[optIndex]
local targetGroupValue = optInfo.viewGroupValue
local targetGroupType = optInfo.viewGroupType
local isValidGroup
if targetGroupValue ~= nil then
local i = optIndex
repeat
i = i + 1
until(i > self.m_curShowingOptCount or
self.m_curShowingOptInfoList[i].viewGroupValue == nil or
self.m_curShowingOptInfoList[i].viewGroupValue ~= targetGroupValue or
self.m_curShowingOptInfoList[i].viewGroupType ~= targetGroupType
)
if i - optIndex >= 2 then
local groupStartIndex, groupEndIndex = optIndex, i - 1
table.insert(groupDataList, {
groupStartIndex = groupStartIndex,
groupEndIndex = groupEndIndex,
groupType = optInfo.viewGroupType,
})
isValidGroup = true
if not findGroup then
findGroup = true
end
end
optIndex = i - 1
end
if not isValidGroup then
local cell = optInfo.cell
cell.groupTitle.gameObject:SetActive(false)
cell.groupEndSpace.gameObject:SetActive(false)
cell.normalNode.groupMask.gameObject:SetActive(false)
if not string.isEmpty(optInfo.overrideText) then
optInfo.overrideText = nil
self:_OnUpdateCell(optInfo)
end
end
optIndex = optIndex + 1
until(optIndex > self.m_curShowingOptCount)
end
if not findGroup then
return
end
for _, groupData in pairs(groupDataList) do
local groupStartIndex, groupEndIndex = groupData.groupStartIndex, groupData.groupEndIndex
local startOptInfo = self.m_curShowingOptInfoList[groupStartIndex]
local endOptInfo = self.m_curShowingOptInfoList[groupEndIndex]
local startOptCell, endOptCell = startOptInfo.cell, endOptInfo.cell
local groupConfigInfo = self.m_viewGroupConfig[groupData.groupType]
local mainOptionCheckFunction = groupConfigInfo.mainOptionCheckFunction
local groupTitle
if mainOptionCheckFunction ~= nil then
local mainOptInfo
for optInfoIndex = groupStartIndex, groupEndIndex do
local optInfo = self.m_curShowingOptInfoList[optInfoIndex]
if mainOptionCheckFunction(optInfo) == true then
mainOptInfo = optInfo
end
end
if mainOptInfo ~= nil then
local configGroupTitle = groupConfigInfo.groupTitle
if type(configGroupTitle) == "function" then
groupTitle = configGroupTitle(mainOptInfo)
self:_OnUpdateCell(mainOptInfo)
end
end
else
groupTitle = groupConfigInfo.groupTitle
end
local groupTitleNode = startOptCell.groupTitle
local groupEndSpaceNode = endOptCell.groupEndSpace
local size = groupTitleNode.bg.sizeDelta
size.y = self.view.config.BASE_HEIGHT + self.view.config.CELL_HEIGHT * (groupEndIndex - groupStartIndex)
groupTitleNode.bg.sizeDelta = size
endOptCell.line.transform.sizeDelta = Vector2(endOptCell.line.transform.sizeDelta.x, self.view.config.CELL_HEIGHT * (groupEndIndex - groupStartIndex + 1))
groupTitleNode.titleText.text = groupTitle
if not groupTitleNode.gameObject.activeSelf then
groupTitleNode.gameObject:SetActive(true)
groupTitleNode.animationWrapper:PlayInAnimation()
else
if groupTitleNode.animationWrapper.curState == CS.Beyond.UI.UIConst.AnimationState.Out then
groupTitleNode.animationWrapper:PlayInAnimation()
end
end
if not groupEndSpaceNode.gameObject.activeSelf then
groupEndSpaceNode.gameObject:SetActive(true)
groupEndSpaceNode:PlayInAnimation()
else
if groupEndSpaceNode.curState == CS.Beyond.UI.UIConst.AnimationState.Out then
groupEndSpaceNode:PlayInAnimation()
end
end
for optInfoIndex = groupStartIndex, groupEndIndex do
local optInfo = self.m_curShowingOptInfoList[optInfoIndex]
local cell = optInfo.cell
cell.normalNode.groupMask.gameObject:SetActive(true)
cell.gameObject.transform:SetSiblingIndex(CSIndex(optInfoIndex))
if optInfoIndex ~= groupStartIndex then
cell.groupTitle.gameObject:SetActive(false)
end
if optInfoIndex ~= groupEndIndex then
cell.groupEndSpace.gameObject:SetActive(false)
end
end
end
end
InteractOptionCtrl.m_pickupAllCoroutine = HL.Field(HL.Thread)
InteractOptionCtrl.m_pressStartIdentifier = HL.Field(HL.Any)
InteractOptionCtrl._OnPressOptionStart = HL.Method(HL.Opt(HL.Boolean)) << function(self, fromInputAction)
self.m_pressStartIdentifier = self.m_curSelectedOptIdentifier
local info = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
if not info then
return
end
if fromInputAction then
info.cell.animator:Play("Pressed")
end
if not info.identifier.isPickable then
return
end
self.m_pickupAllCoroutine = self:_ClearCoroutine(self.m_pickupAllCoroutine)
local node = self.view.btnHint
self.m_pickupAllCoroutine = self:_StartCoroutine(function()
local startTime = Time.unscaledTime
coroutine.wait(0.2)
node.pressHintNode.gameObject:SetActive(true)
node.pressArrow.gameObject:SetActive(not DeviceInfo.usingTouch)
while true do
local percent = (Time.unscaledTime - startTime) / 0.5
node.progressImg.fillAmount = percent
if percent >= 1 then
break
else
coroutine.step()
end
end
self:_OnTriggerPickupAll()
self:_InterruptPickupAll()
end)
end
InteractOptionCtrl._OnPressOptionEnd = HL.Method(HL.Opt(HL.Boolean)) << function(self, fromInputAction)
local info = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
if info and fromInputAction then
info.cell.animator:SetTrigger("Highlighted")
end
if self.m_pickupAllCoroutine then
self:_InterruptPickupAll()
if not info or not info.identifier.isPickable then
return
end
self:_OnClickOption(self.m_curSelectedOptIdentifier)
end
end
InteractOptionCtrl._InterruptPickupAll = HL.Method() << function(self)
self.m_pickupAllCoroutine = self:_ClearCoroutine(self.m_pickupAllCoroutine)
self.view.btnHint.pressHintNode.gameObject:SetActive(false)
self.view.btnHint.pressArrow.gameObject:SetActive(false)
end
InteractOptionCtrl._OnTriggerPickupAll = HL.Method() << function(self)
local list = {}
local curInfo = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
local startIndex = lume.find(self.m_curShowingOptInfoList, curInfo)
local count = #self.m_curShowingOptInfoList
for k = 0, count - 1 do
local index = (startIndex + k - 1) % count + 1
local info = self.m_curShowingOptInfoList[index]
if info.identifier.isPickable then
table.insert(list, info.identifier)
end
end
InteractOptionIdentifier.OnTriggerPickupAll(list)
end
InteractOptionCtrl.s_subBuildingOptUseIndexNameForGuide = HL.StaticField(HL.Boolean) << true
InteractOptionCtrl.FacToggleSubBuildingOptUseIndexNameForGuide = HL.StaticMethod(HL.Table) << function(arg)
local useIndexName = unpack(arg)
InteractOptionCtrl.s_subBuildingOptUseIndexNameForGuide = useIndexName
local _, self = UIManager:IsOpen(PANEL_ID)
if self then
for _, info in ipairs(self.m_curShowingOptInfoList) do
self:_UpdateCellObjectName(info)
end
end
end
InteractOptionCtrl.m_isFocusingForGuide = HL.Field(HL.Boolean) << false
InteractOptionCtrl.FocusOnInteractOption = HL.Method(HL.Table) << function(self, arg)
logger.info("InteractOptionCtrl.FocusOnInteractOption", arg)
local optName = unpack(arg)
self.m_isFocusingForGuide = false
if string.isEmpty(optName) then
if not string.isEmpty(self.m_curSelectedOptIdentifier) and self.m_curShowingOptCount > 0 then
local info = self.m_optionInfoMap[self.m_curSelectedOptIdentifier]
if not info then
self:_SelectOption(1)
end
end
return
end
for k, info in ipairs(self.m_curShowingOptInfoList) do
if info.cell.gameObject.name == optName then
self:_SelectOption(k)
self.m_isFocusingForGuide = true
return
end
end
logger.error("InteractOptionCtrl.FocusOnInteractOption: No Target OptName", optName)
end
InteractOptionCtrl._PlayOptionAnimation = HL.Method(HL.Table, HL.Boolean) << function(self, cell, isIn)
if isIn then
cell.animator:Play("In")
if cell.groupEndSpace.gameObject.activeInHierarchy then
cell.groupEndSpace:PlayInAnimation()
end
if cell.groupTitle.gameObject.activeInHierarchy then
cell.groupTitle.animationWrapper:PlayInAnimation()
end
else
cell.animator:Play("Out")
if cell.groupEndSpace.gameObject.activeInHierarchy then
cell.groupEndSpace:PlayOutAnimation()
end
if cell.groupTitle.gameObject.activeInHierarchy then
cell.groupTitle.animationWrapper:PlayOutAnimation()
end
end
end
HL.Commit(InteractOptionCtrl)