local UIPrefabObject = require "app/bf/unity/uiprefab_object"
local BattleBaseUI = class("BattleBaseUI", BaseUI)

local GRID_CELL = "app/ui/battle/cell/grid_cell"
local GRID_CELL_PATH = "assets/prefabs/ui/battle/cell/grid_cell.prefab"
local SKILL_NODE_CELL = "app/ui/battle/cell/skill_node_cell"
local TINY_BUFF_CELL = "app/ui/battle/cell/tiny_buff_cell"
local BOSS_SKILL_CELL = "app/ui/battle/cell/boss_skill_cell"
local BG_PATH = "assets/arts/textures/background/battle/%s.png"
local BATTLE_COMMON_PATH = "assets/arts/textures/background/battle_common/%s.png"
local GRID_EDGE_CELL = "app/ui/battle/cell/grid_edge_cell"

local BOARD_SFX_ORDER = GConst.UI_EFFECT_ORDER.LEVEL4 + 1
local SIDE_ATK = GConst.BattleConst.SIDE_ATK
local SIDE_DEF = GConst.BattleConst.SIDE_DEF
local CacheVector2 = CS.UnityEngine.Vector2(0, 0)
local DEFAULT_X = 10000
local CACHE_SKILL_POS_1 = {x = 10, y = 360}
local CACHE_SKILL_POS_2 = {x = 10, y = 420}
local MAX_LASTSIBLING_TYPE = {
    [8] = true,
    [9] = true,
    [10] = true,
    [11] = true,
}

---------------------------------必须重写的方法----------------------------------
function BattleBaseUI:initBaseInfo()
    -- local uiMap = self.root:genAllChildren()
    -- self.uiMap = uiMap
    -- self.gridNode = ?
    -- self.boardCenterNode = ?
    -- self.boardNode = ?
    -- self.boardNode:setAnchoredPositionX(DEFAULT_X)
    -- self.boardMask2D = self.gridNode:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_RECT_MASK_2D)
    -- self.boardMask = ?
    -- self.boardMask:setVisible(false)
    -- self.boardCacheNode = ?
    -- self.boardCacheNode:setVisible(false)
    -- self.boardCacheBox = ?
    -- self.boardCacheBox:setAnchoredPositionX(DEFAULT_X)
    -- self.battleRoot = ?
    -- self.maxLayerNode = ?
    -- self.aniNode = ?
    -- self.gridEdgeNode = ?
    -- self.gridEdgeCacheCell = ?
end

function BattleBaseUI:initBg()
    -- self.bg = ?
    -- self.bg:setLocalScale(0, 0, 0)
    -- local width = self.bg:fastGetSizeDelta()
    -- self.bg:setAnchoredPositionX(width/4)
end

function BattleBaseUI:initSkill()
    local uiMap = self.root:genAllChildren()
    local atkNode = nil
    local defNode = nil
    local atkCellPrefix = nil
    local defCellPrefix = nil
    self:_initSkill(atkNode, atkCellPrefix, defNode, defCellPrefix)
end

function BattleBaseUI:initBuff()
    local atkBuffPrefix = nil
    local defBuffPrefix = nil
    local battleBuffTipsRoot = nil
    local battleBuffTipsMask = nil
    local battleBuffTipsBg = nil
    local battleBuffTipsBuff = nil
    self:_initBuff(atkBuffPrefix, defBuffPrefix, battleBuffTipsRoot, battleBuffTipsMask, battleBuffTipsBg, battleBuffTipsBuff)
end

function BattleBaseUI:initBossSkill()
	local bossSkillPrefix = nil
	self:_initBossSkill(bossSkillPrefix)
end

function BattleBaseUI:initBattlefield()
    self.battleNode = nil
end

function BattleBaseUI:initNumberNode()
    self.battleNumberNode = nil
    self.battleNumberRed = nil
    self.battleNumberGreen = nil
    self.battleNumberBlue = nil
    self.battleNumberWhite = nil
    self.battleNumberSpecial = nil
end

function BattleBaseUI:initComboNode()
    self.comboNode = nil
    self.comboBg = nil
    self.comboBg1 = nil
    self.comboTx1 = nil
    self.comboTx2 = nil
    self.comboFx1 = nil
    self.comboFx2 = nil
    self.comboFx3 = nil
    self:_initComboNode()
end

function BattleBaseUI:initHpNode()
    self.hpProgressLeft = self.uiMap[""]:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_SLIDER)
    self.hpProgressRight = self.uiMap[""]:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_SLIDER)
    self.hpTextLeft = self.uiMap[""]
    self.hpTextRight = self.uiMap[""]
    self.hpProgressYellowLeft = self.uiMap[""]:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_SLIDER)
    self.hpProgressYellowRight = self.uiMap[""]:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_SLIDER)
    self:_initHpNode()
end

function BattleBaseUI:initFxNode()
    self.fxNode = nil
end

function BattleBaseUI:hideGenerateSkillGridCells()
    local generateSkillCellPrefix = nil
    self:_hideGenerateSkillGridCells(generateSkillCellPrefix)
end

function BattleBaseUI:initSkillLineSfx()
    if not self.skillLineSfxs then
        self.skillLineSfxs = {}
        self.skillLineSfxs[13] = self.uiMap[""]
        self.skillLineSfxs[11] = self.uiMap[""]
        self.skillLineSfxs[15] = self.uiMap[""]
        self.skillLineSfxs[17] = self.uiMap[""]

        self.skillLineSfxs[23] = self.uiMap[""]
        self.skillLineSfxs[21] = self.uiMap[""]
        self.skillLineSfxs[25] = self.uiMap[""]
        self.skillLineSfxs[27] = self.uiMap[""]

        self.skillLineSfxs[33] = self.uiMap[""]
        self.skillLineSfxs[31] = self.uiMap[""]
        self.skillLineSfxs[35] = self.uiMap[""]
        self.skillLineSfxs[37] = self.uiMap[""]
    end

    if not self.root.skillLightSfxs then
        self.root.skillLightSfxs = {
            point = {
                isLoaded = true,
                -- obj = self.uiMap[""],
            }
        }
    end
    self:_initSkillLineSfx()
end

function BattleBaseUI:initGenerateSkillEffect()
    local generateSkillEffecPrefix = nil
    self:_initGenerateSkillEffect(generateSkillEffecPrefix)
end

function BattleBaseUI:initCounterAttack()
    self.counterAttackNode = nil
    self.counterTx = nil
    self.counterTxbgComp = nil
    self.counterTxTmp = self.counterTx:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO)
    self.counterAttackNode:setVisible(false)
end

--------------------------------end必须重写的方法--------------------------------
----------------------------------按需重写的方法 可以为nil，已容错----------------
function BattleBaseUI:initTutorialNode()
end

function BattleBaseUI:initCommonSkillDescTips()
    -- local uiMap = self.root:genAllChildren()
    -- self.skillDescTipsNode = ?
    -- self.skillDescTips = ?
    -- self.skillDescTipsNode:setVisible(false)
end

function BattleBaseUI:initSkillSelectCells()
    -- self.skillSelectCell = ?
end

function BattleBaseUI:initSelectSkillNode()
    -- self.selectSkillComp = ?
end

function BattleBaseUI:initBossEnterAni()
    -- local uiMap = self.root:genAllChildren()
    -- self.bossEnterNode = ?
    -- self.bossEnterImg = ?
    -- self.bossName = ?
    -- self.bossEnterNodeAnimator = self.bossEnterNode:getComponent(GConst.TYPEOF_UNITY_CLASS.ANIMATOR)
    -- self.bossEnterNodeAnimator.enabled = false
    -- self.bossEnterNodeCanvasGroup = self.bossEnterNode:getComponent(GConst.TYPEOF_UNITY_CLASS.CANVAS_GROUP)
    -- self.bossEnterNode:setVisible(false)
end

function BattleBaseUI:enterShowBoardAni(callback, skipInterval)
    self:clearEnterShowBoardSeq()

    if callback then
        callback()
    end
end

function BattleBaseUI:enterHideBoardAni(callback)
    self:clearEnterShowBoardSeq()

    if callback then
        callback()
    end
end

function BattleBaseUI:showTutorialFinger(posIdList)
end

function BattleBaseUI:moveBattlefield(time)
end

function BattleBaseUI:initTouchCancel()
end

function BattleBaseUI:showTouchCancel()
end

function BattleBaseUI:hideTouchCancel()
end

--------------------------------end按需重写的方法--------------------------------

function BattleBaseUI:getBGMId()
    return AudioManager.BGM_ID.BATTLE
end

function BattleBaseUI:ctor(parmas)
    self.battleController = parmas.battleController
    self.battleData = self.battleController.battleData
end

function BattleBaseUI:getPosInfo(posId)
    return ModuleManager.BattleManager:getPosInfo(posId, self.battleData:getRowCount())
end

function BattleBaseUI:onPressBackspace()
    if self.battleBuffTipsRoot then
        if self.battleBuffTipsRoot:getTransform().localScale.x > 0 then
            self:hideBuffTips()
            return
        end
    end

    ModuleManager.BattleManager:showPauseUI(self.battleController.battleType, self.battleController)
end

function BattleBaseUI:onClose()
	self:clear()
end

function BattleBaseUI:onLoadRootComplete()
    local uiMap = self.root:genAllChildren()
    self.uiMap = uiMap
    self:_display()
    self:_addListeners()
    self:_bind()
end

function BattleBaseUI:_display()
    self:initBaseInfo()
    self:initBg()
    self:initSkill()
    self:initBuff()
	self:initBossSkill()
    self:initBattlefield()
    self:initNumberNode()
    self:initComboNode()
    self:initHpNode()
    self:initFxNode()
    self:hideGenerateSkillGridCells()
    self:initTutorialNode()
    self:initUISfxs()
    self:initSkillSelectCells()
    self:hideAllSfxSmoke()
    self:initSkillLineSfx()
    self:initGenerateSkillEffect()
    self:initSelectSkillNode()
    self:initCommonSkillDescTips()
    self:initBossEnterAni()
    self:initCounterAttack()
    self:initTouchCancel()
    self:hideMonsterSkillGridCells()
end

function BattleBaseUI:_addListeners()
end

function BattleBaseUI:_bind()
end

function BattleBaseUI:_initComboNode()
    self.comboAnimator = self.comboNode:getComponent(GConst.TYPEOF_UNITY_CLASS.ANIMATOR)
    self.comboNodeVisible = false
    self.comboNode:setVisible(false)
    self.comboBg1:setVisible(false)
    self.comboTxComp1 = self.comboTx1:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT)
    self.comboTxComp2 = self.comboTx2:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT)
    self.comboFx1:setActive(false)
    self.comboFxVisible1 = false
    self.comboFx2:setActive(false)
    self.comboFxVisible2 = false
    self.comboFx3:setActive(false)
    self.comboFxVisible3 = false
end

function BattleBaseUI:_initHpNode()
    self.hpProgressLeft.value = 1
    self.hpProgressRight.value = 1
    self.hpProgressYellowLeft.value = 1
    self.hpProgressYellowRight.value = 1
    self.hpPercentLeft = 1
    self.hpPercentRight = 1
end

function BattleBaseUI:_initSkill(atkNode, atkCellPrefix, defNode, defCellPrefix)
    if self.skillNodeCells then
        return
    end
    self.skillNodeCells = {
        [SIDE_ATK] = {},
        [SIDE_DEF] = {},
    }

    local initSKillCell = function(obj, elementType, sideSkillCells, side)
        sideSkillCells[elementType] = CellManager:addCellComp(obj, SKILL_NODE_CELL)
        local skillEntity = self.battleData:getSkillEntities(side)[elementType]
        if skillEntity then
            sideSkillCells[elementType]:refresh(skillEntity)
        end
        sideSkillCells[elementType]:getBaseObject():setActive(skillEntity ~= nil)
        if skillEntity then
            sideSkillCells[elementType]:addClickListener(function()
                ModuleManager.TipsManager:showBattleBoardSkillTips(self.battleData:getHeroEntity(elementType, side), self.battleController, side)
            end)
        end
    end

    for name, elementType in pairs(GConst.BattleConst.ELEMENT_TYPE) do
        if defCellPrefix then
            local obj = self.uiMap[defCellPrefix .. elementType]
            if obj then
                initSKillCell(obj, elementType, self.skillNodeCells[SIDE_DEF], SIDE_DEF)
            end
        end

        if atkCellPrefix then
            local obj = self.uiMap[atkCellPrefix .. elementType]
            if obj then
                initSKillCell(obj, elementType, self.skillNodeCells[SIDE_ATK], SIDE_ATK)
            end
        end
    end

    if defNode then
        defNode:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_HORIZONTAL_OR_VERTICAL_LAYOUT):RefreshLayout()
    end
    if atkNode then
        atkNode:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_HORIZONTAL_OR_VERTICAL_LAYOUT):RefreshLayout()
    end
end

function BattleBaseUI:_initBuff(atkBuffPrefix, defBuffPrefix, battleBuffTipsRoot, battleBuffTipsMask, battleBuffTipsBg, battleBuffTipsBuff)
    if self.tinyBuffCells then
        return
    end
    self.tinyBuffCells = {
        [SIDE_ATK] = {},
        [SIDE_DEF] = {},
    }
    for i = 1, 8 do
        if defBuffPrefix then
            local obj = self.uiMap[defBuffPrefix .. i]
            if obj then
                self.tinyBuffCells[SIDE_DEF][i] = CellManager:addCellComp(obj, TINY_BUFF_CELL)
                obj:addClickListener(function()
                    self.battleController:showBuffTips(SIDE_DEF)
                end)
            end
        end

        if atkBuffPrefix then
            local obj = self.uiMap[atkBuffPrefix .. i]
            if obj then
                self.tinyBuffCells[SIDE_ATK][i] = CellManager:addCellComp(obj, TINY_BUFF_CELL)
                obj:addClickListener(function()
                    self.battleController:showBuffTips(SIDE_ATK)
                end)
            end
        end
    end
    for side, cellList in pairs(self.tinyBuffCells) do
        for index, cell in ipairs(cellList) do
            cell:getBaseObject():setVisible(false)
        end
    end
    -- buff的tips
    self.battleBuffTipsRoot = battleBuffTipsRoot
    self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
    battleBuffTipsMask:addClickListener(function()
        if self.autoCloseBuffSid then
            self:unscheduleGlobal(self.autoCloseBuffSid)
            self.autoCloseBuffSid = nil
        end
        self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
    end)
    self.battleBuffTipsBg = battleBuffTipsBg
    self.battleBuffTipsBuff = battleBuffTipsBuff
    self.battleBuffTipsBuffList = {}
    local children = self.battleBuffTipsBg:getChildList()
    if children then
        for k, v in ipairs(children) do
            table.insert(self.battleBuffTipsBuffList, v)
        end
    end
end

function BattleBaseUI:_initBossSkill(bossSkillPrefix)
	if self.bossSkillCells then
		return
	end
	self.bossSkillCells = {}
	for i = 1, 6 do
		if bossSkillPrefix then
			local obj = self.uiMap[bossSkillPrefix .. i]
			if obj then
				self.bossSkillCells[i] = CellManager:addCellComp(obj, BOSS_SKILL_CELL)
				-- obj:addClickListener(function()
				-- 	-- self.battleController:showBuffTips(SIDE_DEF)
				-- end)
			end
		end
	end
	for index, cell in ipairs(self.bossSkillCells) do
		cell:setActive(false)
	end
	-- buff的tips
	-- self.battleBuffTipsRoot = battleBuffTipsRoot
	-- self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
	-- battleBuffTipsMask:addClickListener(function()
	-- 	if self.autoCloseBuffSid then
	-- 		self:unscheduleGlobal(self.autoCloseBuffSid)
	-- 		self.autoCloseBuffSid = nil
	-- 	end
	-- 	self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
	-- end)
	-- self.battleBuffTipsBg = battleBuffTipsBg
	-- self.battleBuffTipsBuff = battleBuffTipsBuff
	-- self.battleBuffTipsBuffList = {}
	-- local children = self.battleBuffTipsBg:getChildList()
	-- if children then
	-- 	for k, v in ipairs(children) do
	-- 		table.insert(self.battleBuffTipsBuffList, v)
	-- 	end
	-- end
end

function BattleBaseUI:refreshBuff(side, buffList)
    if not self.tinyBuffCells then
        return
    end
    local buffObj = nil
    local count = #buffList
    local buffCellCount = #self.tinyBuffCells[side]

    local index = 1
    for i = 1, count do
        buffObj = buffList[i]
        if buffObj and buffObj.buff:getIcon() and not buffObj.buff:getNotShowIcon() then
            local cell = self.tinyBuffCells[side][index]
            if cell then
                cell:getBaseObject():setVisible(true)
                cell:refresh(buffObj.buff:getIcon(), buffObj.round, buffObj.buff:getEffectNum(), buffObj.result)
            end
            index = index + 1
            if index > buffCellCount then
                break
            end
        end
    end
    for i = index, buffCellCount do
        local cell = self.tinyBuffCells[side][i]
        if cell then
            cell:getBaseObject():setVisible(false)
        end
    end
end

function BattleBaseUI:clearBuff(side)
    if not self.tinyBuffCells or not self.tinyBuffCells[side] then
        return
    end
    for index, cell in ipairs(self.tinyBuffCells[side]) do
        cell:getBaseObject():setVisible(false)
    end
end

function BattleBaseUI:refreshBossSkill(skillList)
	if not self.bossSkillCells then
		return
	end
	local skillCellCount = #self.bossSkillCells

	for i = 1, skillCellCount do
		if not skillList[i] then
			self.bossSkillCells[i]:setActive(false)
		else
			self.bossSkillCells[i]:setActive(true)
			self.bossSkillCells[i]:refresh(skillList[i])
		end
	end
end

function BattleBaseUI:clearBossSkill()
	if not self.bossSkillCells then
		return
	end
	for _, cell in ipairs(self.bossSkillCells) do
		cell:setActive(false)
	end
end

function BattleBaseUI:refreshChessBoard(boardBg)
    self.boardNode:setTexture(string.format(BATTLE_COMMON_PATH, boardBg), function()
        self.boardNode:setAnchoredPositionX(0)
    end)
end

function BattleBaseUI:loadBg(bgName)
    self.bg:setTexture(string.format(BG_PATH, bgName), function()
        self.bg:setLocalScale(1, 1, 1)
    end)
end

function BattleBaseUI:showLeftBuffTips(buffList, autoClose)
    self:showBuffTips(buffList, autoClose)
end

function BattleBaseUI:showRightBuffTips(buffList, autoClose)
    self:showBuffTips(buffList, autoClose)
end

function BattleBaseUI:getBattleNode()
    return self.battleNode
end

function BattleBaseUI:getNumberNode()
    return self.battleNumberNode
end

function BattleBaseUI:getBattleNumberRed()
    return self.battleNumberRed
end

function BattleBaseUI:getBattleNumberGreen()
    return self.battleNumberGreen
end

function BattleBaseUI:getBattleNumberBlue()
    return self.battleNumberBlue
end

function BattleBaseUI:getBattleNumberWhite()
    return self.battleNumberWhite
end

function BattleBaseUI:getBattleNumberSpecial()
    return self.battleNumberSpecial
end

function BattleBaseUI:getBattleBuffTipsObj(index)
    if self.battleBuffTipsBuffList[index] then
        return self.battleBuffTipsBuffList[index]
    end
    local prefab = CS.UnityEngine.Object.Instantiate(self.battleBuffTipsBuff:getGameObject())
    local prefabObject = UIPrefabObject:create()
    prefabObject:initWithPrefab(self.battleBuffTipsBuff:getAssetPath(), prefab)
    prefabObject:initPrefabHelper()
    prefabObject:setParent(self.battleBuffTipsBg, false)
    table.insert(self.battleBuffTipsBuffList, prefabObject)
    return prefabObject
end

function BattleBaseUI:showBuffTips(buffList, autoClose)
    if #buffList <= 0 then
        return 0
    end
    self.battleBuffTipsRoot:setLocalScale(1, 1, 1)
    local buffObj = nil
    local count = #buffList
    local index = 1
    local addY = 0
    for i = 1, count do
        buffObj = buffList[i]
        if buffObj and buffObj.buff:getIcon() and not buffObj.buff:getNotShowIcon() then
            local buffTipsObj = self:getBattleBuffTipsObj(index)
            buffTipsObj:setLocalScale(1, 1, 1)
            buffTipsObj:setAnchoredPositionY(-addY)
            local buffTipsObjMap = buffTipsObj:genAllChildren()
            buffTipsObjMap["buff.icon"]:setSprite(GConst.ATLAS_PATH.ICON_BUFF, buffObj.buff:getIcon())
            local round = buffObj.round
            if round <= 1 or round > 9 then
                buffTipsObjMap["buff.round"]:setText(GConst.EMPTY_STRING)
            else
                buffTipsObjMap["buff.round"]:setText(tostring(round))
            end
            local descTx = buffTipsObjMap["buff.desc"]
            descTx:setText(buffObj.buff:getDesc())
            descTx:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO):ForceMeshUpdate()
            local height = descTx:getComponent(GConst.TYPEOF_UNITY_CLASS.UI_TEXT_MESH_PRO).renderedHeight
            if height > 30 then
                addY = addY + 56 + height - 30
            else
                addY = addY + 56
            end
            index = index + 1
        end
    end
    if index <= 1 then -- 没有找到buff
        self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
        return
    end

    for i = index, #self.battleBuffTipsBuffList do
        self.battleBuffTipsBuffList[i]:setLocalScale(0, 0, 0)
    end
    self.battleBuffTipsBg:setSizeDeltaY(addY + 10)

    if self.autoCloseBuffSid then
        self:unscheduleGlobal(self.autoCloseBuffSid)
        self.autoCloseBuffSid = nil
    end

    if autoClose then
        self.autoCloseBuffSid = self:performWithDelayGlobal(function()
            self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
        end, 1.5)
    end

    return addY
end

function BattleBaseUI:hideBuffTips()
    if self.autoCloseBuffSid then
        self:unscheduleGlobal(self.autoCloseBuffSid)
        self.autoCloseBuffSid = nil
    end

    if not self.battleBuffTipsRoot then
        return
    end
    self.battleBuffTipsRoot:setLocalScale(0, 0, 0)
end

function BattleBaseUI:playSkillLineSfx(posId, boradRangeList, randomPosList)
    if not boradRangeList or not boradRangeList[1] or not self.skillLineSfxs then
        return
    end

    local directionAndRange = {}
    for _, info in ipairs(boradRangeList) do
        if info.type == GConst.BattleConst.BOARD_RANGE_TYPE.RANDOM then
            directionAndRange[GConst.BattleConst.BOARD_RANGE_TYPE.RANDOM] = info.range
        elseif info.type == GConst.BattleConst.BOARD_RANGE_TYPE.UP or
                info.type == GConst.BattleConst.BOARD_RANGE_TYPE.DOWN then
            directionAndRange[GConst.BattleConst.BOARD_RANGE_TYPE.UP] = info.range
        elseif info.type == GConst.BattleConst.BOARD_RANGE_TYPE.LEFT or
                info.type == GConst.BattleConst.BOARD_RANGE_TYPE.RIGHT then
            directionAndRange[GConst.BattleConst.BOARD_RANGE_TYPE.LEFT] = info.range
        elseif info.type == GConst.BattleConst.BOARD_RANGE_TYPE.LEFT_UP or
                info.type == GConst.BattleConst.BOARD_RANGE_TYPE.LEFT_DOWN then
            directionAndRange[GConst.BattleConst.BOARD_RANGE_TYPE.LEFT_UP] = info.range
        elseif info.type == GConst.BattleConst.BOARD_RANGE_TYPE.RIGHT_UP or
                info.type == GConst.BattleConst.BOARD_RANGE_TYPE.RIGHT_DOWN then
            directionAndRange[GConst.BattleConst.BOARD_RANGE_TYPE.RIGHT_UP] = info.range
        end
    end

    local pos = self:getPosInfo(posId)
    for dir, range in pairs(directionAndRange) do
        local index = range * 10 + dir
        local obj = self.skillLineSfxs[index]
        if obj then
            obj:setAnchoredPosition(pos.x, pos.y)
            obj:setActive(true)
            obj:play()
        end
    end

    if randomPosList and randomPosList[1] then
        local count = #randomPosList
        self.root.skillLightSfxs.point.obj:setAnchoredPosition(pos.x, pos.y)
        self.root.skillLightSfxs.point.obj:setActive(true)
        self.root.skillLightSfxs.point.obj:play()
        for i = 1, count do
            local tartgetPos = randomPosList[i]
            tartgetPos = self:getPosInfo(tartgetPos)
            self:playSkillLightSfxs(tartgetPos, pos, 200 + i, true)
            self:playSkillLightSfxs(tartgetPos, pos, 100 + i, false)
        end
    end
end

function BattleBaseUI:playSkillLightSfxs(tartgetPos, pos, index, isPoint)
    local info = self.root.skillLightSfxs[index]
    local yOffset = tartgetPos.y - pos.y
    local xOffset = tartgetPos.x - pos.x
    local height = math.sqrt(xOffset * xOffset + yOffset * yOffset)
    if info then
        local obj = info.obj
        if obj then
            if isPoint then
                obj:setAnchoredPosition(tartgetPos.x, tartgetPos.y)
                obj:setActive(true)
                obj:play()
            else
                obj:setAnchoredPosition(pos.x, pos.y)
                obj:setLocalScaleX(0.3 * height / 94)
                obj:setEulerAngles(0, 0, - math.deg(math.atan(xOffset, yOffset)) + 90)
                obj:setActive(true)
                obj:play()
            end
        end
    else
        self.root.skillLightSfxs[index] = {
            isLoaded = true
        }
        local assetPath = GConst.BattleConst.SKILL_LIGHT_LINE_SFX
        if isPoint then
            assetPath = GConst.BattleConst.SKILL_LIGHT_POINT_SFX
        end
        EffectManager:loadUIEffectAsync(assetPath, self, self.aniNode, GConst.UI_EFFECT_ORDER.LEVEL5, function(obj)
            self.root.skillLightSfxs[index].obj = obj
            if isPoint then
                obj:setAnchoredPosition(tartgetPos.x, tartgetPos.y)
                obj:setActive(true)
                obj:play()
            else
                obj:setAnchoredPosition(pos.x, pos.y)
                obj:setLocalScaleX(0.3 * height / 94)
                obj:setEulerAngles(0, 0, - math.deg(math.atan(xOffset, yOffset)) + 90)
                obj:setActive(true)
                obj:play()
            end
        end)
    end
end

function BattleBaseUI:playChangeElementSfx(posId, index)
    if not self.root.changeElementSfxs then
        self.root.changeElementSfxs = {}
    end

    local pos = self:getPosInfo(posId)
    local info = self.root.changeElementSfxs[index]
    if info then
        local obj = info.obj
        if obj then
            obj:setAnchoredPosition(pos.x, pos.y)
            obj:setActive(true)
            obj:play()
        end
    else
        self.root.changeElementSfxs[index] = {
            isLoaded = true
        }
        EffectManager:loadUIEffectAsync(GConst.BattleConst.CHANGE_ELEMENT_SFX, self, self.gridNode, GConst.UI_EFFECT_ORDER.LEVEL5, function(obj)
            self.root.changeElementSfxs[index].obj = obj
            obj:setAnchoredPosition(pos.x, pos.y)
            obj:play()
        end)
    end
end

function BattleBaseUI:initUISfxs()
    if self.root.gridBreakSfxObjs then
        for breakSfxPath, map in pairs(self.root.gridBreakSfxObjs) do
            for index, info in pairs(map) do
                if info.obj then
                    info.obj:setActive(true)
                    info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
                    info.obj:setActive(false)
                end
            end
        end
    end

    if self.root.gridEdgeBreakSfxObjs then
        for breakSfxPath, map in pairs(self.root.gridEdgeBreakSfxObjs) do
            for index, info in pairs(map) do
                if info.obj then
                    info.obj:setActive(true)
                    info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
                    info.obj:setActive(false)
                end
            end
        end
    end

    if self.root.gridEffectSfx then
        for breakSfxPath, list in pairs(self.root.gridEffectSfx) do
            for index, info in pairs(list) do
                if info.obj then
                    info.obj:setActive(true)
                    info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
                    info.obj:setActive(false)
                end
            end
        end
    end

    if self.root.lineSfxObjs then
        for index, info in pairs(self.root.lineSfxObjs) do
            if info.obj then
                info.obj:setActive(true)
                info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), BOARD_SFX_ORDER)
                info.obj:setActive(false)
            end
        end
    end

    if self.root.smokeSfxObjs then
        for index, info in pairs(self.root.smokeSfxObjs) do
            if info.obj then
                info.obj:setActive(true)
                info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), BOARD_SFX_ORDER)
                info.obj:setActive(false)
            end
        end
    end

    if self.root.changeElementSfxs then
        for index, info in pairs(self.root.changeElementSfxs) do
            if info.obj then
                info.obj:setActive(true)
                info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
                info.obj:setActive(false)
            end
        end
    end
end

function BattleBaseUI:hideAllBoardSfxs()
    if self.generateSkillSfxs then
        for _, obj in pairs(self.generateSkillSfxs) do
           obj:setActive(false)
        end
    end

    if self.skillLineSfxs then
        for _, obj in pairs(self.skillLineSfxs) do
            obj:setActive(false)
        end
    end

    if self.root.skillLightSfxs then
        for index, info in pairs(self.root.skillLightSfxs) do
            if info.obj then
                info.obj:setActive(false)
            end
        end
    end

    if self.root.gridBreakSfxObjs then
        for breakSfxPath, map in pairs(self.root.gridBreakSfxObjs) do
            for index, info in pairs(map) do
                if info.obj then
                    info.obj:setActive(false)
                end
            end
        end
    end

    if self.root.lineSfxObjs then
        for index, info in pairs(self.root.lineSfxObjs) do
            if info.obj then
                info.obj:setActive(false)
            end
        end
    end

    if self.root.smokeSfxObjs then
        for index, info in pairs(self.root.smokeSfxObjs) do
            if info.obj then
                info.obj:setActive(false)
            end
        end
    end
end

function BattleBaseUI:hideCombo()
    if not self.comboNodeVisible then
        return
    end
    self.comboNodeVisible = false
    if self.comboSid then
        self:unscheduleGlobal(self.comboSid)
    end
    self.comboSid = self:performWithDelayGlobal(function()
        self.comboSid = nil
        self.comboNode:setVisible(false)
        if self.comboFxVisible1 then
            self.comboFxVisible1 = false
            self.comboFx1:setActive(false)
        end
        if self.comboFxVisible2 then
            self.comboFxVisible2 = false
            self.comboFx2:setActive(false)
        end
        if self.comboFxVisible3 then
            self.comboFxVisible3 = false
            self.comboFx3:setActive(false)
        end
    end, 0.5)
end

function BattleBaseUI:showCombo(count, side)
    if side == SIDE_ATK then
        self.comboNode:setAnchorMin(1, 0.5)
        self.comboNode:setAnchorMax(1, 0.5)
        self.comboBg:setLocalEulerAngles(0, 0, 0)
    else
        self.comboNode:setAnchorMin(0, 0.5)
        self.comboNode:setAnchorMax(0, 0.5)
        self.comboBg:setLocalEulerAngles(0, 180, 0)
    end

    if count > 99 then
        count = 99
    end
    self.comboNodeVisible = true
    self.comboNode:setVisible(true)
    if count <= 5 then
        if self.comboFxVisible1 then
            self.comboFxVisible1 = false
            self.comboFx1:setActive(false)
        end
        if self.comboFxVisible2 then
            self.comboFxVisible2 = false
            self.comboFx2:setActive(false)
        end
        if self.comboFxVisible3 then
            self.comboFxVisible3 = false
            self.comboFx3:setActive(false)
        end
        self.comboBg1:setVisible(false)
        self.comboTx1:setText(GConst.INT_TO_STRING[count], self.comboTxComp1)
        self.comboTx1:setAnchoredPositionX(0)
        self.comboTx2:setText(GConst.EMPTY_STRING, self.comboTxComp2)
        if side == SIDE_ATK then
            self.comboAnimator:Play(GConst.BattleConst.ANIMATOR_HASH_NAME_BATTLE_COMBO_1, -1, 0)
        else
            self.comboAnimator:Play(GConst.BattleConst.ANIMATOR_HASH_NAME_BATTLE_COMBO_1_l, -1, 0)
        end
    elseif count <= 10 then
        if self.comboFxVisible1 then
            self.comboFxVisible1 = false
            self.comboFx1:setActive(false)
        end
        if self.comboFxVisible2 then
            self.comboFxVisible2 = false
            self.comboFx2:setActive(false)
        end
        if not self.comboFxVisible3 then
            self.comboFxVisible3 = true
            self.comboFx3:setActive(true)
        end
        self.comboBg1:setVisible(true)
        self.comboTx1:setText(GConst.INT_TO_STRING[count], self.comboTxComp1)
        if count == 10 then
            self.comboTx1:setAnchoredPositionX(-26)
        else
            self.comboTx1:setAnchoredPositionX(0)
        end
        self.comboTx2:setText(GConst.EMPTY_STRING, self.comboTxComp2)
        if side == SIDE_ATK then
            self.comboAnimator:Play(GConst.BattleConst.ANIMATOR_HASH_NAME_BATTLE_COMBO_2, -1, 0)
        else
            self.comboAnimator:Play(GConst.BattleConst.ANIMATOR_HASH_NAME_BATTLE_COMBO_2_l, -1, 0)
        end
    else
        if not self.comboFxVisible1 then
            self.comboFxVisible1 = true
            self.comboFx1:setActive(true)
        end
        if not self.comboFxVisible2 then
            self.comboFxVisible2 = true
            self.comboFx2:setActive(true)
        end
        if self.comboFxVisible3 then
            self.comboFxVisible3 = false
            self.comboFx3:setActive(false)
        end
        self.comboBg1:setVisible(true)
        self.comboTx1:setText(GConst.EMPTY_STRING, self.comboTxComp1)
        self.comboTx2:setText(GConst.INT_TO_STRING[count], self.comboTxComp2)
        if side == SIDE_ATK then
            self.comboAnimator:Play(GConst.BattleConst.ANIMATOR_HASH_NAME_BATTLE_COMBO_3, -1, 0)
        else
            self.comboAnimator:Play(GConst.BattleConst.ANIMATOR_HASH_NAME_BATTLE_COMBO_3_l, -1, 0)
        end
    end
end

function BattleBaseUI:showCounterAttack(count, side)
    local x = 280
    local horizontal = 180
    if side == GConst.BattleConst.SIDE_ATK then
        x = -280
        horizontal = 0
        self.counterTxTmp.alignment = CS.TMPro.TextAlignmentOptions.MidlineLeft
    else
        self.counterTxTmp.alignment = CS.TMPro.TextAlignmentOptions.MidlineRight
    end
    self.counterTxbgComp:setEulerAngles(0, horizontal, 0)
    self.counterAttackNode:setAnchoredPositionX(x)
    self.counterTx:setText(I18N:getGlobalText(I18N.GlobalConst.COUNTER_ATTACK_DESC) .. "x" .. count)
    self.counterAttackNode:setVisible(true)
end

function BattleBaseUI:hideCounterAttack()
    self.counterAttackNode:setVisible(false)
end

function BattleBaseUI:getFxNode()
    return self.fxNode
end

function BattleBaseUI:setIsPauseHpProgress(value)
    if self.isPauseHpProgress == value then
        return
    end
    self.isPauseHpProgress = value
    if not value then
        local timeLeft = math.abs(self.hpProgressYellowLeft.value - self.hpPercentLeft)
        if timeLeft > 0.01 then
            local delayTime = math.abs(self.hpProgressLeft.value - self.hpPercentLeft)*2
            if delayTime > 0.05 then
                if self.hpProgressYellowLeftSid then
                    self:unscheduleGlobal(self.hpProgressYellowLeftSid)
                end
                self.hpProgressYellowLeftSid = self:performWithDelayGlobal(function()
                    self:playHpProgressYellowLeftTween(timeLeft)
                end, delayTime)
            else
                self:playHpProgressYellowLeftTween(timeLeft)
            end
        else
            if self.hpProgressYellowLeftTween then
                self.hpProgressYellowLeftTween:Pause()
            end
            self.hpProgressYellowLeft.value = self.hpPercentLeft
        end
        local timeRight = math.abs(self.hpProgressYellowRight.value - self.hpPercentRight)
        if timeRight > 0.01 then
            local delayTime = math.abs(self.hpProgressRight.value - self.hpPercentRight)*2
            if delayTime > 0.05 then
                if self.hpProgressYellowRightSid then
                    self:unscheduleGlobal(self.hpProgressYellowRightSid)
                end
                self.hpProgressYellowRightSid = self:performWithDelayGlobal(function()
                    self:playHpProgressYellowRightTween(timeRight)
                end, delayTime)
            else
                self:playHpProgressYellowRightTween(timeRight)
            end
        else
            if self.hpProgressYellowRightTween then
                self.hpProgressYellowRightTween:Pause()
            end
            self.hpProgressYellowRight.value = self.hpPercentRight
        end
    end
end

function BattleBaseUI:playHpProgressYellowLeftTween(time)
    if self.hpProgressYellowLeftTween == nil then
        self.hpProgressYellowLeftTween = GFunc.DOBFSliderValue(self.hpProgressYellowLeft, self.hpPercentLeft, time, false)
        self.hpProgressYellowLeftTween:SetIntId(GConst.DOTWEEN_IDS.BATTLE)
        self.hpProgressYellowLeftTween:SetAutoKill(false)
    else
        self.hpProgressYellowLeftTween:ChangeEndValue(self.hpPercentLeft, time, true)
        self.hpProgressYellowLeftTween:Restart()
    end
end

function BattleBaseUI:playHpProgressYellowRightTween(time)
    if self.hpProgressYellowRightTween == nil then
        self.hpProgressYellowRightTween = GFunc.DOBFSliderValue(self.hpProgressYellowRight, self.hpPercentRight, time, false)
        self.hpProgressYellowRightTween:SetIntId(GConst.DOTWEEN_IDS.BATTLE)
        self.hpProgressYellowRightTween:SetAutoKill(false)
    else
        self.hpProgressYellowRightTween:ChangeEndValue(self.hpPercentRight, time, true)
        self.hpProgressYellowRightTween:Restart()
    end
end

function BattleBaseUI:refreshAtkHp(num, percent)
    self.hpTextLeft:setText(GFunc.num2Str(num))
    if not self.isPauseHpProgress then
        if self.hpProgressLeftTween then
            self.hpProgressLeftTween:Pause()
        end
        if self.hpProgressYellowLeftTween then
            self.hpProgressYellowLeftTween:Pause()
        end
        if self.hpProgressYellowLeftSid then
            self:unscheduleGlobal(self.hpProgressYellowLeftSid)
            self.hpProgressYellowLeftSid = nil
        end
        self.hpProgressLeft.value = percent
        self.hpProgressYellowLeft.value = percent
        self.hpPercentLeft = percent
        return
    end
    if self.hpPercentLeft == percent then
        return
    end
    self.hpPercentLeft = percent
    local time = math.abs(self.hpProgressLeft.value - percent)*2
    if self.hpProgressLeftTween == nil then
        self.hpProgressLeftTween = GFunc.DOBFSliderValue(self.hpProgressLeft, percent, time, false)
        self.hpProgressLeftTween:SetIntId(GConst.DOTWEEN_IDS.BATTLE)
        self.hpProgressLeftTween:SetAutoKill(false)
    else
        self.hpProgressLeftTween:ChangeEndValue(percent, time, true)
        self.hpProgressLeftTween:Restart()
    end
end

function BattleBaseUI:refreshDefHp(num, percent)
    self.hpTextRight:setText(GFunc.num2Str(num))
    if not self.isPauseHpProgress then
        if self.hpProgressRightTween then
            self.hpProgressRightTween:Pause()
        end
        if self.hpProgressYellowRightTween then
            self.hpProgressYellowRightTween:Pause()
        end
        if self.hpProgressYellowRightSid then
            self:unscheduleGlobal(self.hpProgressYellowRightSid)
            self.hpProgressYellowRightSid = nil
        end
        self.hpProgressRight.value = percent
        self.hpProgressYellowRight.value = percent
        self.hpPercentRight = percent
        return
    end
    if self.hpPercentRight == percent then
        return
    end
    self.hpPercentRight = percent
    local time = math.abs(self.hpProgressRight.value - percent)*2
    if self.hpProgressRightTween == nil then
        self.hpProgressRightTween = GFunc.DOBFSliderValue(self.hpProgressRight, percent, time, false)
        self.hpProgressRightTween:SetIntId(GConst.DOTWEEN_IDS.BATTLE)
        self.hpProgressRightTween:SetAutoKill(false)
    else
        self.hpProgressRightTween:ChangeEndValue(percent, time, true)
        self.hpProgressRightTween:Restart()
    end
end

function BattleBaseUI:setAtkHp(num, percent)
    self.hpTextLeft:setText(GFunc.num2Str(num))
    self.hpProgressLeft.value = percent
end

function BattleBaseUI:setDefHp(num, percent)
    self.hpTextRight:setText(GFunc.num2Str(num))
    self.hpProgressRight.value = percent
end

function BattleBaseUI:refreshSkill(elementMap, showSfx, side)
    side = side or SIDE_ATK
    if not self.skillNodeCells or not self.skillNodeCells[side] then
        return
    end

    local skillCellMap = self.skillNodeCells[side]
    for elementType, skillEntity in pairs(self.battleData:getSkillEntities(side)) do
        local skillCell = skillCellMap[elementType]
        if skillCell then
            skillCell:refresh(skillEntity, elementMap, showSfx)
        end
    end
end


-- shakeType: 奇数是水平震动 偶数是垂直震动
function BattleBaseUI:shakeScreen(shakeType, duration)
	self.battleRoot:setLocalPosition(0, 0, 0)
	if self.shakeTween == nil then
		local length = ConfigManager:getConfig("const")["shake_level_" .. math.ceil(shakeType / 2)].value
		if shakeType % 2 == 0 then
            CacheVector2.x = 0
            CacheVector2.y = length
			self.shakeTween = self.battleRoot:getTransform():DOShakeAnchorPos(duration, CacheVector2, 100, 90, false, false)
		else
            CacheVector2.x = length
            CacheVector2.y = 0
			self.shakeTween = self.battleRoot:getTransform():DOShakeAnchorPos(duration, CacheVector2, 100, 90, false, false)
		end
		self.shakeTween:SetIntId(GConst.DOTWEEN_IDS.BATTLE)
		self.shakeTween:SetAutoKill(false)
		self.shakeType = shakeType
		self.shakeDuration = duration
	elseif self.shakeType == shakeType and self.shakeDuration == duration then
		self.shakeTween:Restart()
	else
		self.shakeTween:Kill()
		local length = ConfigManager:getConfig("const")["shake_level_" .. math.ceil(shakeType / 2)].value
		if shakeType % 2 == 0 then
            CacheVector2.x = 0
            CacheVector2.y = length
			self.shakeTween = self.battleRoot:getTransform():DOShakeAnchorPos(duration, CacheVector2, 100, 90, false, false)
		else
            CacheVector2.x = length
            CacheVector2.y = 0
			self.shakeTween = self.battleRoot:getTransform():DOShakeAnchorPos(duration, CacheVector2, 100, 90, false, false)
		end
		self.shakeTween:SetIntId(GConst.DOTWEEN_IDS.BATTLE)
		self.shakeTween:SetAutoKill(false)
		self.shakeType = shakeType
		self.shakeDuration = duration
	end
	self.shakeTween.timeScale = self.battleData:getTimeScale()
end

function BattleBaseUI:initGridCell(callback)
    self.onInitGridCellOverCallback = callback
    if self.root.gridCells then
        self.gridCells = self.root.gridCells
        self.gridInitOver = true
    end

    if self.gridCells and self.gridInitOver then
        self:onInitGridCellOver()
        return
    end

    self.gridCells = {}
    local gridEntities = self.battleData:getGridEnties()
    self.cellLoadRemianCount = table.nums(gridEntities)
    for posId, entity in pairs(gridEntities) do
        CellManager:loadCellAsync(GRID_CELL_PATH, GRID_CELL, self.gridNode, function(cell)
            self.gridCells[posId] = cell
            cell:getBaseObject():setAnchoredPositionX(DEFAULT_X) -- 初始化时放到屏幕外

            self.cellLoadRemianCount = self.cellLoadRemianCount - 1
            if self.cellLoadRemianCount <= 0 then
                self.gridInitOver = true
                self:onInitGridCellOver()
                self.root.gridCells = self.gridCells
            end
        end)
    end
end

function BattleBaseUI:onInitGridCellOver()
    for posId, cell in pairs(self.gridCells) do
        local entity = self.battleData:getGridEntity(posId)
        if entity then
            cell:hideAni()
            cell:setOrder(self:getUIOrder())
            cell:refresh(entity)
            cell:addTouchListener(function(eventType)
                if self.battleController then
                    self.battleController:onTouchEvent(eventType, entity:getPosId())
                end
            end)
            cell:unBindAll()
            cell:bind(entity, "isDirty", function()
                local elementType = self.curElementType
                if not self.battleController:getPosIdInCurActionBoardRowRange(entity:getPosId()) then
                    elementType = nil
                end
                cell:refresh(entity, elementType, self.skillPosId)
            end)
            local pos = entity:getPos()
            cell:getBaseObject():setParent(self.gridNode, true)
            cell:getBaseObject():setAnchoredPosition(pos.x, pos.y)
            if entity:getIsIdle() then
                cell:getBaseObject():setAnchoredPositionX(DEFAULT_X)
            end
            entity:setCell(cell)
        end
    end

    -- 加载边缘
    if self.gridEdgeNode then
        local gridEdges = self.battleData:getGridEdgeEntities()
        for posId, infos in pairs(gridEdges) do
            for edgeType, entity in pairs(infos) do
                local cell = entity:getCell()
                if not cell then
                    local prefab = self:getGridEdgeObj()
                    local prefabObject = UIPrefabObject:create()
                    prefabObject:initWithPrefab(GConst.EMPTY_STRING, prefab)
                    prefabObject:initPrefabHelper()
                    prefabObject:setParent(self.gridEdgeNode, false)
                    prefabObject:setVisible(true)
                    cell = prefabObject:addLuaComponent(GRID_EDGE_CELL)
                    entity:setCell(cell)
                end

                cell:refresh(entity)
                cell:unBindAll()
                cell:bind(entity, "isDirty", function()
                    cell:refresh(entity)
                end)
            end
        end
    end

    local callback = self.onInitGridCellOverCallback
    self.onInitGridCellOverCallback = nil
    if callback then
        callback()
    end
end

function BattleBaseUI:switchBoard(downCallback, callback, isFirst)
    if isFirst then
        if downCallback then
            downCallback()
        end
        if callback then
            callback()
        end
        return
    end

    if downCallback then
        downCallback()
    end

    if callback then
        callback()
    end
end

function BattleBaseUI:showBoardMask(elementType, skillPosId)
    if not self.gridCells then
        return
    end
    if self.curElementType == elementType and self.skillPosId == skillPosId then
        return
    end

    self.curElementType = elementType
    self.skillPosId = skillPosId
    self:refreshBoard()
end

function BattleBaseUI:refreshBoard()
    local entities = self.battleData:getGridEnties()
    for posId, entity in pairs(entities) do
        local elementType = self.curElementType
        local skillPosId = self.skillPosId
        if not self.battleController:getPosIdInCurActionBoardRowRange(posId) then
            elementType = nil
            skillPosId = nil
        end
        if entity and entity:getCell() then
            entity:getCell():refresh(entity, elementType, skillPosId)
        end
    end
end

function BattleBaseUI:eliminationAni(aniSequence, effectGridMap, callback, side)
    self:showMask(true)
    if not aniSequence then
        if callback then
            callback()
        end
        return
    end

    self.boardMask:getTransform():SetAsLastSibling()
    if self.eliminationAniSeq then
        self.eliminationAniSeq:Kill()
        self.eliminationAniSeq = nil
    end

    self.eliminationAniSeq = self.root:createBindTweenSequence()
    self.eliminationAniSeq:AppendCallback(function()
        if self.boardMask2D then
            self.boardMask2D.enabled = false
        end
    end)

    if not self.posIdMap then
        self.posIdMap = {}
    end

    local breakSfxNameIndexMap = {}
    for index, info in ipairs(aniSequence) do
        if not self.posIdMap[info.posId] then
            local pos = self:getPosInfo(info.posId)
            self.posIdMap[info.posId] = true
            local entity = self.battleData:getGridEntity(info.posId)

            local time = info.timeIdx
            if entity and entity:getCell() then
                local countStr = entity:getCell():getCountStr()
                entity:getCell():refresh(entity)
                entity:getCell():setCountStr(countStr)

                if info.callback then
                    self.eliminationAniSeq:InsertCallback(time, function()
                        if info.callback then
                            info.callback()
                        end
                        entity:getCell():setCountStr(countStr)
                    end)
                end

                self:dealGridBreakSfx(time, info, breakSfxNameIndexMap)
                self:dealGridEffectSfx(time, info, breakSfxNameIndexMap, entity)

                time = time + (info.bftcTime or 0)
                if info.isIdle then
                    if entity:getSkillId() then
                        entity:getCell():hideSkillSfx()
                    end
                    local baseObject = entity:getCell():getBaseObject()
                    baseObject:getTransform():SetAsLastSibling()
                    local anitime = 0
                    if info.aniPosList then
                        local posList = {}
                        local count = 0
                        for _, posId in ipairs(info.aniPosList) do
                            table.insert(posList, self:getPosInfo(posId))
                            count = count + 1
                        end
                        anitime = count * GConst.BattleConst.GRID_BREAK_EFFECT_INTERVAL
                        self.eliminationAniSeq:Insert(time, baseObject:getTransform():DOLocalPath(posList, anitime):SetEase(CS.DG.Tweening.Ease.Linear))
                    else
                        if info.noAni then
                            self.eliminationAniSeq:InsertCallback(time, function()
                                baseObject:setAnchoredPositionX(DEFAULT_X)
                            end)
                        else
                            if info.rangeList then
                                self.eliminationAniSeq:InsertCallback(time, function()
                                    self:playSkillLineSfx(info.posId, info.rangeList, info.randomPosList)
                                end)
                            end
                            self.eliminationAniSeq:Insert(time, baseObject:getTransform():DOScale(1.3, 0.1))
                            self.eliminationAniSeq:InsertCallback(time + 0.2, function()
                                self:getSfxSmoke(index, function(obj)
                                    obj:setAnchoredPosition(pos.x, pos.y)
                                    obj:play()
                                end)
                            end)
                            local targetPos = self:getElementSkillPos(entity:getElementType(), side)
                            if info.breakFlyToCharacter then
                                targetPos = self.battleController.atkTeam:getMainUnit():getBaseObject():getTransform().position
                                local sPoint = UIManager:getUICameraComponent():WorldToScreenPoint(targetPos)
                                targetPos = CS.BF.Utils.RectTransformScreenPointToLocalPointInRectangle(self.boardCenterNode:getTransform(), sPoint.x, sPoint.y, UIManager:getUICameraComponent())
                            end
                            self.eliminationAniSeq:Insert(time + 0.2, baseObject:getTransform():DOAnchorPos(targetPos, 0.3))
                            self.eliminationAniSeq:Insert(time + 0.2, baseObject:getTransform():DOScale(0.5, 0.3))
                            anitime = 0.5
                        end
                    end
                    self.eliminationAniSeq:InsertCallback(time + anitime, function()
                        baseObject:setAnchoredPositionX(DEFAULT_X)
                        if info.overCallback then
                            info.overCallback()
                        end
                    end)
                end
            end
        end
    end

    local list
    for posId, _ in pairs(effectGridMap) do
        local entity = self.battleData:getGridEntity(posId)
        local baseObject = entity:getCell():getBaseObject()
        baseObject:getTransform():SetAsLastSibling()
        if MAX_LASTSIBLING_TYPE[entity:getGridType()] then
            if not list then
                list = {}
            end
            table.insert(list, baseObject)
        end
    end

    if list then
        for _, baseObject in ipairs(list) do
            baseObject:getTransform():SetAsLastSibling()
        end
    end

    self.eliminationAniSeq:AppendCallback(function()
        for posId, _ in pairs(self.posIdMap) do
            local entity = self.battleData:getGridEntity(posId)

            if entity and entity:getCell() then
                entity:getCell():hideAni()
            end
        end

        self.posIdMap = {}
        if self.boardMask2D then
            self.boardMask2D.enabled = true
        end

        if callback then
            callback()
        end

        self:refreshSkill(nil, nil, side)
        self:resetParentAllSfxGridBreak()
        self.eliminationAniSeq = nil
    end)
end

function BattleBaseUI:dealGridBreakSfx(time, info, breakSfxNameIndexMap)
    if info.breakSfxName then
        breakSfxNameIndexMap[info.breakSfxName] = (breakSfxNameIndexMap[info.breakSfxName] or 0) + 1
        local breakIndex = breakSfxNameIndexMap[info.breakSfxName]
        self.eliminationAniSeq:InsertCallback(time, function()
            self:getSfxGridBreak(info.breakSfxName, breakIndex, function(obj)
                local pos = self:getPosInfo(info.posId)
                obj:setLocalPosition(pos.x, pos.y, 0)
                obj:play()
            end)
        end)
    end
end

function BattleBaseUI:dealGridEffectSfx(time, info, breakSfxNameIndexMap, entity)
    if not self.eliminationAniSeq then
        return
    end

    self.flowEffects = table.clearOrCreate(self.flowEffects)
    if info.effectSfxName then
        breakSfxNameIndexMap[info.effectSfxName] = (breakSfxNameIndexMap[info.effectSfxName] or 0) + 1
        local breakIndex = breakSfxNameIndexMap[info.effectSfxName]
        self.eliminationAniSeq:InsertCallback(time, function()
            self:getSfxGridBreak(info.effectSfxName, breakIndex, function(obj)
                if not self.eliminationAniSeq then -- 动画已结束
                    obj:setActive(false)
                    return
                end
                if info.effectSfxFlow then
                    obj:setParent(entity:getCell():getBaseObject(), false)
                    obj:setLocalPosition(0, 0, 0)
                    obj:play()
                    table.insert(self.flowEffects, obj)
                else
                    local pos = self:getPosInfo(info.posId)
                    obj:setLocalPosition(pos.x, pos.y, 0)
                    obj:play()
                end
                if info.effectSfxDir == GConst.BattleConst.BOARD_RANGE_TYPE.UP then
                    obj:setLocalEulerAngles(0, 0, 0)
                elseif info.effectSfxDir == GConst.BattleConst.BOARD_RANGE_TYPE.DOWN then
                    obj:setLocalEulerAngles(0, 0, 180)
                elseif info.effectSfxDir == GConst.BattleConst.BOARD_RANGE_TYPE.LEFT then
                    obj:setLocalEulerAngles(0, 0, 90)
                elseif info.effectSfxDir == GConst.BattleConst.BOARD_RANGE_TYPE.RIGHT then
                    obj:setLocalEulerAngles(0, 0, -90)
                else
                    obj:setLocalEulerAngles(0, 0, 0)
                end
            end)
        end)
    end
end

function BattleBaseUI:showGridEffectSfx(posId, sfxName, callback, customTime, zEuler)
    self:getSfxGridEffect(sfxName,  function(info)
        info.isIdle = false
        local pos = self:getPosInfo(posId)
        customTime = customTime or info.obj:getDuration()
        info.obj:setLocalPosition(pos.x, pos.y, 0)
        info.obj:setLocalEulerAngles(0, 0, zEuler)
        info.obj:setActive(true)
        info.obj:playComplete(function()
            info.isIdle = true
            info.obj:setActive(false)
        end, customTime, function()
            if self:isClosed() then
                return
            end
            if callback then
                callback()
            end
        end)
    end)
end

function BattleBaseUI:moveGridCells(gridEntityList, callback)
    if not gridEntityList then
        if callback then
            callback()
        end
    end

    if self.moveGridCellsSeq then
        self.moveGridCellsSeq:Kill()
        self.moveGridCellsSeq = nil
    end

    self.moveGridCellsSeq = self.root:createBindTweenSequence()
    for _, entity in pairs(gridEntityList) do
        local posId = entity:getPosId()
        local baseObject = entity:getCell():getBaseObject()
        local pos = self:getPosInfo(posId)
        if entity:getEffectType() then
            baseObject:getTransform():SetAsLastSibling()
        end
        self.moveGridCellsSeq:Insert(0, baseObject:getTransform():DOLocalMove(pos, 1))
    end
    self.moveGridCellsSeq:AppendCallback(function()
        if callback then
            callback()
        end
    end)
end

function BattleBaseUI:generateSkillAni(map, callback, side)
    if table.nums(map) <= 0 or table.nums(self.generateSkillGridEntities) <= 0 then
        if callback then
            callback()
        end
        return
    end

    self:hideGenerateSkillGridCells()
    if self.generateSkillAniSeq then
        self.generateSkillAniSeq:Kill()
        self.generateSkillAniSeq = nil
    end

    self.generateSkillAniSeq = self.root:createBindTweenSequence()
    local count = 0
    for elementType, info in pairs(map) do
        local entity = self.generateSkillGridEntities[elementType]
        if entity and entity:getCell() then
            count = count + 1
            entity:setSkilId(info.skillId, false, side)
            local cell = entity:getCell()
            cell:refresh(entity)
            self.generateSkillAniSeq:AppendCallback(function()
                local pos = self:getElementSkillPos(elementType, side)
                cell:getBaseObject():setAnchoredPosition(pos.x, pos.y)
            end)
            local pos = self:getPosInfo(info.posId)
            self.generateSkillAniSeq:Append(cell:getBaseObject():getTransform():DOAnchorPos(pos, 0.5))
            if self.generateSkillSfxs and self.generateSkillSfxs[count] then
                self.generateSkillSfxs[count]:setParent(self.gridNode, true)
                self.generateSkillSfxs[count]:setAnchoredPosition(pos.x, pos.y)
                self:setGenerateSkillSfxsParent(info.posId, self.generateSkillSfxs[count])
                self.generateSkillAniSeq:AppendCallback(function()
                    self.generateSkillSfxs[count]:setActive(true)
                    self.generateSkillSfxs[count]:play()
                end)
            end
        end
    end
    self.generateSkillAniSeq:AppendCallback(function()
        if callback then
            callback()
        end
        self:hideGenerateSkillGridCells()
    end)
end

function BattleBaseUI:setGenerateSkillSfxsParent(posId, obj)
end

function BattleBaseUI:_hideGenerateSkillGridCells(generateSkillCellPrefix)
    if not self.generateSkillGridEntities then
        local uiMap = self.root:genAllChildren()
        self.generateSkillGridEntities = {}
        if generateSkillCellPrefix then
            for name, elementType in pairs(GConst.BattleConst.ELEMENT_TYPE) do
                local obj = uiMap[generateSkillCellPrefix .. elementType]
                if obj then
                    local cell = CellManager:addCellComp(obj, GRID_CELL)
                    local entity = self.battleData:getNewGridEntity()
                    entity:setCell(cell)
                    self.generateSkillGridEntities[elementType] = entity
                end
            end
        end
    end

    for _, entity in pairs(self.generateSkillGridEntities) do
        if entity:getCell() then
            entity:getCell():getBaseObject():setAnchoredPositionX(DEFAULT_X)
        end
    end
end

function BattleBaseUI:_initGenerateSkillEffect(generateSkillEffecPrefix)
    if not self.generateSkillSfxs then
        local uiMap = self.root:genAllChildren()
        self.generateSkillSfxs = {}
        if generateSkillEffecPrefix then
            for i = 1, 5 do
                self.generateSkillSfxs[i] = uiMap[generateSkillEffecPrefix .. i]
            end
        end
    end

    for _, obj in pairs(self.generateSkillSfxs) do
        obj:setActive(true)
        obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
        obj:setActive(false)
    end
end

function BattleBaseUI:_initSkillLineSfx()
    for _, obj in pairs(self.skillLineSfxs) do
        obj:setActive(true)
        obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
        obj:setActive(false)
    end

    for _, info in pairs(self.root.skillLightSfxs) do
        if info.obj then
            info.obj:setActive(true)
            info.obj:getComponent(GConst.TYPEOF_UNITY_CLASS.BF_EFFECT_HELPER):SetSortingOrder(self:getUIOrder(), GConst.UI_EFFECT_ORDER.LEVEL5)
            info.obj:setActive(false)
        end
    end
end

function BattleBaseUI:showMonsterSkillAni(map, monsterPos, callback)
    if table.nums(map) <= 0 or table.nums(self.monsterSkillGridEntities) <= 0 then
        if callback then
            callback()
        end
        return
    end

    self:hideMonsterSkillGridCells()
    if self.monsterSkillAniSeq then
        self.monsterSkillAniSeq:Kill()
        self.monsterSkillAniSeq = nil
    end

    local sPoint = UIManager:getUICameraComponent():WorldToScreenPoint(monsterPos)
    monsterPos = CS.BF.Utils.RectTransformScreenPointToLocalPointInRectangle(self.boardCenterNode:getTransform(), sPoint.x, sPoint.y, UIManager:getUICameraComponent())

    self.monsterSkillAniSeq = self.root:createBindTweenSequence()
    local count = 1
    for posId, info in pairs(map) do
        local entity = self.monsterSkillGridEntities[count]
        if entity and entity:getCell() then
            entity:setGridType(info.gridType)
            entity:setElementType(GConst.BattleConst.ELEMENT_TYPE.EMPTY)
            count = count + 1
            local cell = entity:getCell()
            cell:refresh(entity)
            cell:getBaseObject():setAnchoredPosition(monsterPos.x, monsterPos.y)
            cell:getBaseObject():setLocalScale(0.3, 0.3, 0.3)
            local pos = self:getPosInfo(posId)
            if count == 1 then
                self.monsterSkillAniSeq:Append(cell:getBaseObject():getTransform():DOAnchorPos(pos, 0.3))
            else
                self.monsterSkillAniSeq:Join(cell:getBaseObject():getTransform():DOAnchorPos(pos, 0.3))
            end
            self.monsterSkillAniSeq:Join(cell:getBaseObject():getTransform():DOScale(1, 0.3))
        end
    end
    self.monsterSkillAniSeq:AppendCallback(function()
        if callback then
            callback()
        end
        self:hideMonsterSkillGridCells()
    end)
end

function BattleBaseUI:hideMonsterSkillGridCells()
    if not self.monsterSkillGridEntities then
        local uiMap = self.root:genAllChildren()
        self.monsterSkillGridEntities = {}
        -- for name, elementType in pairs(GConst.BattleConst.ELEMENT_TYPE) do
        --     local obj = uiMap["battle_ui.bg_2.ani_node.grid_cell_m" .. elementType]
        --     if obj then
        --         local cell = CellManager:addCellComp(obj, GRID_CELL)
        --         local entity = self.battleData:getNewGridEntity()
        --         entity:setCell(cell)
        --         table.insert(self.monsterSkillGridEntities, entity)
        --     end
        -- end
    end

    for _, entity in ipairs(self.monsterSkillGridEntities) do
        if entity:getCell() then
            entity:getCell():getBaseObject():setAnchoredPositionX(DEFAULT_X)
        end
    end
end

function BattleBaseUI:gotOneSkillAni(skillId, elementType, callback, startPos, side)
    if not skillId or not self.skillSelectCell then
        if callback then
            callback()
        end
        return
    end

    self.skillSelectCell:refresh(skillId)
    self.skillSelectCell:getBaseObject():setAnchoredPosition(startPos.x, startPos.y)
    self.skillSelectCell:getBaseObject():setVisible(true)
    if self.gotOneSkillAniSeq then
        self.gotOneSkillAniSeq:Kill()
        self.gotOneSkillAniSeq = nil
    end

    self.gotOneSkillAniSeq = self.root:createBindTweenSequence()
    local pos = self:getElementSkillPos(elementType, side)
    self.gotOneSkillAniSeq:Append(self.skillSelectCell:getBaseObject():getTransform():DOAnchorPos(pos, 0.7))
    self.gotOneSkillAniSeq:Join(self.skillSelectCell:getBaseObject():getTransform():DOScale(0.76, 0.7))
    self.gotOneSkillAniSeq:AppendCallback(function()
        self.skillSelectCell:getBaseObject():setAnchoredPositionX(DEFAULT_X)
        if callback then
            callback()
        end
    end)
end

function BattleBaseUI:shuffleBoard(changeInfo, callback)
    if self.shuffleBoardSeq then
        self.shuffleBoardSeq:Kill()
        self.shuffleBoardSeq = nil
    end

    self.shuffleBoardSeq = self.root:createBindTweenSequence()
    for posId, tartgetPos in pairs(changeInfo) do
        local entity = self.battleData:getGridEntity(posId)
        local cell = entity:getCell()
        local posId = entity:getPosId()
        if cell then
            local pos = self:getPosInfo(posId)
            self.shuffleBoardSeq:Insert(0, cell:getBaseObject():getTransform():DOAnchorPos(pos, 1))
        end
    end
    self.shuffleBoardSeq:AppendCallback(function()
        if callback then
            callback()
        end
    end)
end

function BattleBaseUI:removeGridOutOfScreen(posIdList)
    for _, posId in ipairs(posIdList) do
        self:removeOneGridOutOfScreen(posId)
    end
end

function BattleBaseUI:removeOneGridOutOfScreen(posId)
    local entity = self.battleData:getGridEntity(posId)
    local cell = entity:getCell()
    cell:getBaseObject():setAnchoredPositionX(DEFAULT_X)
end

function BattleBaseUI:fallGrid(listInfo, isRoundBeginCheck, callback)
    if isRoundBeginCheck then
        self:showMask(false)
    else
        self.boardMask:getTransform():SetAsLastSibling()
    end
    self.fallAniCount = 0
    for posId, info in pairs(listInfo) do
        local entity = self.battleData:getGridEntity(posId)
        local cell = entity:getCell()
        if cell then
            self.fallAniCount = self.fallAniCount + 1
            if cell.fallSeq then
                cell.fallSeq:Kill()
                cell.fallSeq = nil
            end
            local baseObject = cell:getBaseObject()
            cell.fallSeq = baseObject:createBindTweenSequence()
            baseObject:setAnchoredPosition(info[1].x, info[1].y)
            local count = #info
            local time = GConst.BattleConst.ONE_STEP_TIME * count
            cell.fallSeq:Append(baseObject:getTransform():DOLocalPath(info, time):SetEase(CS.DG.Tweening.Ease.InQuad))
            cell.fallSeq:AppendCallback(function()
                self.fallAniCount = self.fallAniCount - 1
                if self.fallAniCount == 0 then
                    if callback then
                        callback()
                    end
                end
            end)
            cell.fallSeq:InsertCallback(time + math.random() * 0.1 - 0.2, function()
                cell:showAni()
            end)
        end
    end
    if self.fallAniCount == 0 and callback then
        callback()
    end
end

function BattleBaseUI:cacheSkillAni(skillInfo, isPop, callback)
    local skillInfoCount = #skillInfo
    if skillInfoCount <= 0 then
        if callback then
            callback()
        end
        return
    end

    self:disableUITouch()

    if not self.root.skillAniGridEntities then
        self.root.skillAniGridEntities = {}
    end

    for _, entity in ipairs(self.root.skillAniGridEntities) do
        local cell = entity:getCell()
        if cell then
            cell:getBaseObject():setAnchoredPositionX(DEFAULT_X) -- 放到屏幕外
        end
    end

    local gridEntityCount = #self.root.skillAniGridEntities
    if gridEntityCount < skillInfoCount then
        for i = gridEntityCount, skillInfoCount do
            CellManager:loadCellAsync(GRID_CELL_PATH, GRID_CELL, self.boardCacheNode, function(cell)
                cell:getBaseObject():setAnchoredPositionX(DEFAULT_X) -- 初始化时放到屏幕外
                local entity = self.battleData:getNewGridEntity()
                entity:setCell(cell)
                cell:hideAni()
                table.insert(self.root.skillAniGridEntities, entity)
                if i == skillInfoCount then
                    if isPop then
                        self:doCachePopAni(skillInfo, callback)
                    else
                        self:doCacheAni(skillInfo, callback)
                    end
                end
            end)
        end
    else
        if isPop then
            self:doCachePopAni(skillInfo, callback)
        else
            self:doCacheAni(skillInfo, callback)
        end
    end
end

function BattleBaseUI:doCacheAni(skillInfo, callback)
    if self.cacheSkillAniSeq then
        self.cacheSkillAniSeq:Kill()
        self.cacheSkillAniSeq = nil
    end

    if not self.root.skillAniGridEntities then
        if callback then
            callback()
        end
        return
    end

    self.boardCacheNode:setVisible(true)

    local w, h = GFunc.getUIExpandScreenSize()
    local w = w / 2 + 100
    self.cacheSkillAniSeq = self.root:createBindTweenSequence()
    self.cacheSkillAniSeq:AppendCallback(function()
        self.boardCacheBox:setAnchoredPositionX(- w)
    end)

    for index, info in ipairs(skillInfo) do
        local entity = self.root.skillAniGridEntities[index]
        if entity then
            entity:setSkilId(info.skillId, false, self.battleController:getCurActionSide())
            local pos = self:getPosInfo(info.posId)
            local cell = entity:getCell()
            if cell then
                cell:refresh(entity)
                local obj = cell:getBaseObject()
                self.cacheSkillAniSeq:InsertCallback(0.5 * (index - 1), function()
                    obj:setAnchoredPosition(pos.x, pos.y)
                    local gridEntity = self.battleData:getGridEntity(info.posId)
                    if gridEntity and gridEntity:getCell() then
                        gridEntity:getCell():getBaseObject():setAnchoredPositionX(DEFAULT_X) -- 放到屏幕外
                    end
                end)

                self.cacheSkillAniSeq:Insert(0.5 * (index - 1) + 0.02, obj:getTransform():DOAnchorPos(CACHE_SKILL_POS_2, 0.7))
            end
        end
    end

    self.cacheSkillAniSeq:Append(self.boardCacheBox:getTransform():DOAnchorPosX(0, 0.5))
    for index, info in ipairs(skillInfo) do
        local entity = self.root.skillAniGridEntities[index]
        if entity then
            local cell = entity:getCell()
            if cell then
                local obj = cell:getBaseObject()
                if index == 1 then
                    self.cacheSkillAniSeq:Append(obj:getTransform():DOAnchorPos(CACHE_SKILL_POS_1, 0.3))
                else
                    self.cacheSkillAniSeq:Join(obj:getTransform():DOAnchorPos(CACHE_SKILL_POS_1, 0.3))
                end
            end
        end
    end
    self.cacheSkillAniSeq:AppendCallback(function()
        for index, entity in ipairs(self.root.skillAniGridEntities) do
            if entity and entity:getCell() then
                entity:getCell():getBaseObject():setAnchoredPositionX(DEFAULT_X)
            end
        end
    end)
    self.cacheSkillAniSeq:Append(self.boardCacheBox:getTransform():DOAnchorPosX(w, 0.5))
    self.cacheSkillAniSeq:AppendCallback(function()
        self.boardCacheNode:setVisible(false)
        self:enableUITouch()
        if callback then
            callback()
        end
    end)
end

function BattleBaseUI:doCachePopAni(skillInfo, callback)
    if self.cacheSkillAniSeq then
        self.cacheSkillAniSeq:Kill()
        self.cacheSkillAniSeq = nil
    end

    if not self.root.skillAniGridEntities then
        if callback then
            callback()
        end
        return
    end

    self.boardCacheNode:setVisible(true)
    local w, h = GFunc.getUIExpandScreenSize()
    local w = w / 2 + 100
    self.cacheSkillAniSeq = self.root:createBindTweenSequence()
    self.cacheSkillAniSeq:AppendCallback(function()
        self.boardCacheBox:setAnchoredPositionX(- w)
    end)
    self.cacheSkillAniSeq:Append(self.boardCacheBox:getTransform():DOAnchorPosX(0, 0.5))
    for index, info in ipairs(skillInfo) do
        local entity = self.root.skillAniGridEntities[index]
        if entity then
            entity:setSkilId(info.skillId, false, self.battleController:getCurActionSide())
            local cell = entity:getCell()
            if cell then
                cell:refresh(entity)
                local obj = cell:getBaseObject()
                self.cacheSkillAniSeq:InsertCallback(0, function()
                    obj:setAnchoredPositionX(DEFAULT_X)
                end)
                self.cacheSkillAniSeq:InsertCallback(0.5 + 0.5 * (index - 1), function()
                    obj:setAnchoredPosition(CACHE_SKILL_POS_1.x, CACHE_SKILL_POS_1. y)
                end)

                self.cacheSkillAniSeq:Insert(0.5 + 0.5 * (index - 1) + 0.02, obj:getTransform():DOAnchorPos(CACHE_SKILL_POS_2, 0.3))
            end
        end
    end
    self.cacheSkillAniSeq:Append(self.boardCacheBox:getTransform():DOAnchorPosX(w, 0.5))

    for index, info in ipairs(skillInfo) do
        local entity = self.root.skillAniGridEntities[index]
        if entity then
            local pos = self:getPosInfo(info.posId)
            local cell = entity:getCell()
            if cell then
                local obj = cell:getBaseObject()
                if index == 1 then
                    self.cacheSkillAniSeq:Append(obj:getTransform():DOAnchorPos(pos, 0.7))
                else
                    self.cacheSkillAniSeq:Join(obj:getTransform():DOAnchorPos(pos, 0.7))
                end
            end
        end
    end

    self.cacheSkillAniSeq:AppendCallback(function()
        self.boardCacheNode:setVisible(false)
        self:enableUITouch()
        if callback then
            callback()
        end
    end)
end

function BattleBaseUI:showMask(show)
    if not self.boardMask then
        return
    end
    self.boardMask:setVisible(show)
end

function BattleBaseUI:getElementSkillPos(elementType, side)
    side = side or SIDE_ATK
    if not self.skillPoss then
        self.skillPoss = {
            [SIDE_ATK] = {},
            [SIDE_DEF] = {},
        }
    end

    if not self.skillPoss[side][elementType] then
        local cell = self.skillNodeCells[side][elementType]
        if not cell then
            self.skillPoss[side][elementType] = BF.Vector2(0, 0)
        else
            local targetPos = cell:getBaseObject():getTransform().position
            local sPoint = UIManager:getUICameraComponent():WorldToScreenPoint(targetPos)
            targetPos = CS.BF.Utils.RectTransformScreenPointToLocalPointInRectangle(self.boardCenterNode:getTransform(), sPoint.x, sPoint.y, UIManager:getUICameraComponent())
            self.skillPoss[side][elementType] = targetPos
        end
    end

    return self.skillPoss[side][elementType]
end

function BattleBaseUI:refreshWave(wave, iconAtlas, iconName)
end

function BattleBaseUI:getSfxLine(index, func)
    self.hidingAllSfxLine = false

    if not self.root.lineSfxObjs then
        self.root.lineSfxObjs = {}
    end
    if self.root.lineSfxObjs[index] then
        if self.root.lineSfxObjs[index].obj and func then
            local obj = self.root.lineSfxObjs[index].obj
            obj:setActive(true)
            func(obj)
        end
    else
        self.root.lineSfxObjs[index] = {
            isLoaded = true
        }
        EffectManager:loadUIEffectAsync(GConst.BattleConst.LINE_SFX, self, self.gridNode, BOARD_SFX_ORDER, function(obj)
            self.root.lineSfxObjs[index].obj = obj
            if self.hidingAllSfxLine then
                obj:setActive(false)
            else
                if func then
                    func(obj)
                end
            end
        end)
    end
end

function BattleBaseUI:hideAllSfxLine()
    self.hidingAllSfxLine = true
    if not self.root.lineSfxObjs then
        return
    end
    for inde, info in pairs(self.root.lineSfxObjs) do
        if info.obj then
            info.obj:setActive(false)
        end
    end
end

function BattleBaseUI:resetParentAllSfxGridBreak()
    if not self.flowEffects then
        return
    end

    for _, obj in ipairs(self.flowEffects) do
        obj:setActive(false)
        obj:setParent(self.gridNode, true)
    end
end

function BattleBaseUI:getSfxGridBreak(breakSfxPath, index, func)
    if not breakSfxPath then
        return
    end

    self.hidingAllSfxGridBreak = false

    if not self.root.gridBreakSfxObjs then
        self.root.gridBreakSfxObjs = {}
    end
    if not self.root.gridBreakSfxObjs[breakSfxPath] then
        self.root.gridBreakSfxObjs[breakSfxPath] = {}
    end
    if self.root.gridBreakSfxObjs[breakSfxPath][index] then
        if self.root.gridBreakSfxObjs[breakSfxPath][index].obj and func then
            local obj = self.root.gridBreakSfxObjs[breakSfxPath][index].obj
            obj:setActive(true)
            func(obj)
        end
    else
        self.root.gridBreakSfxObjs[breakSfxPath][index] = {
            isLoaded = true
        }
        EffectManager:loadUIEffectAsync(breakSfxPath, self, self.gridNode, GConst.UI_EFFECT_ORDER.LEVEL5, function(obj)
            self.root.gridBreakSfxObjs[breakSfxPath][index].obj = obj
            if self.hidingAllSfxGridBreak then
                obj:setActive(false)
            else
                if func then
                    func(obj)
                end
            end
        end)
    end
end

function BattleBaseUI:hideAllSfxGridBreak()
    self.hidingAllSfxLine = false

    if self.root.gridBreakSfxObjs then
        for breakSfxPath, map in pairs(self.root.gridBreakSfxObjs) do
            for index, info in pairs(map) do
                if info.obj then
                    info.obj:setParent(self.gridNode, false)
                    info.obj:setActive(false)
                end
            end
        end
    end

    if self.root.gridEdgeBreakSfxObjs then
        for index, info in pairs(self.root.gridEdgeBreakSfxObjs) do
            if info.obj then
                info.obj:setParent(self.gridNode, false)
                info.obj:setActive(false)
            end
        end
    end
end

function BattleBaseUI:getSfxGridEdgeBreak(breakSfxPath, index, func)
    if not breakSfxPath then
        return
    end

    self.hidingAllSfxGridBreak = false

    if not self.root.gridEdgeBreakSfxObjs then
        self.root.gridEdgeBreakSfxObjs = {}
    end
    if not self.root.gridEdgeBreakSfxObjs[breakSfxPath] then
        self.root.gridEdgeBreakSfxObjs[breakSfxPath] = {}
    end
    if self.root.gridEdgeBreakSfxObjs[breakSfxPath][index] then
        if self.root.gridEdgeBreakSfxObjs[breakSfxPath][index].obj then
            local obj = self.root.gridEdgeBreakSfxObjs[breakSfxPath][index].obj
            obj:setActive(true)
            if func then
                func(obj)
            end
        end
    else
        self.root.gridEdgeBreakSfxObjs[breakSfxPath][index] = {
            isLoaded = true
        }
        EffectManager:loadUIEffectAsync(breakSfxPath, self, self.gridNode, GConst.UI_EFFECT_ORDER.LEVEL5, function(obj)
            self.root.gridEdgeBreakSfxObjs[breakSfxPath][index].obj = obj
            if self.hidingAllSfxGridBreak then
                obj:setActive(false)
            else
                if func then
                    func(obj)
                end
            end
        end)
    end
end

function BattleBaseUI:getSfxGridEffect(effectSfxPath, func)
    if not effectSfxPath then
        return
    end

    if not self.root.gridEffectSfx then
        self.root.gridEffectSfx = {}
    end
    if not self.root.gridEffectSfx[effectSfxPath] then
        self.root.gridEffectSfx[effectSfxPath] = {}
    end
    local count = 0
    for index, info in pairs(self.root.gridEffectSfx[effectSfxPath]) do
        if info.isIdle then
            func(self.root.gridEffectSfx[effectSfxPath][index])
            return
        end
        count = count + 1
    end

    local index = count + 1
    self.root.gridEffectSfx[effectSfxPath][index] = {
        isLoaded = true,
    }
    EffectManager:loadUIEffectAsync(effectSfxPath, self, self.gridNode, GConst.UI_EFFECT_ORDER.LEVEL5, function(obj)
        self.root.gridEffectSfx[effectSfxPath][index].obj = obj
        self.root.gridEffectSfx[effectSfxPath][index].isIdle = true
        if func then
            func(self.root.gridEffectSfx[effectSfxPath][index])
        end
    end)
end

function BattleBaseUI:getSfxSmoke(index, func)
    self.hidingAllSfxSmoke = false

    if not self.root.smokeSfxObjs then
        self.root.smokeSfxObjs = {}
    end
    if self.root.smokeSfxObjs[index] then
        if self.root.smokeSfxObjs[index].obj and func then
            local obj = self.root.smokeSfxObjs[index].obj
            obj:setActive(true)
            func(obj)
        end
    else
        self.root.smokeSfxObjs[index] = {
            isLoaded = true
        }
        EffectManager:loadUIEffectAsync(GConst.BattleConst.LINK_SMOKE, self, self.gridNode, BOARD_SFX_ORDER, function(obj)
            self.root.smokeSfxObjs[index].obj = obj
            obj:setLocalScale(1.5, 1.5, 1.5)
            if self.hidingAllSfxSmoke then
                obj:setActive(false)
            else
                if func then
                    func(obj)
                end
            end
        end)
    end
end

function BattleBaseUI:hideAllSfxSmoke()
    self.hidingAllSfxSmoke = true
    if not self.root.smokeSfxObjs then
        return
    end
    for inde, info in pairs(self.root.smokeSfxObjs) do
        if info.obj then
            info.obj:setActive(false)
        end
    end
end

function BattleBaseUI:showSelectSkillComp(skillList, isCommon)
    if not self.selectSkillComp then
        return
    end
    self.selectSkillComp:refresh(skillList, isCommon)
end

function BattleBaseUI:showCommonSkillTips(skillId)
    if not self.skillDescTipsNode then
        return
    end
    -- 需要合并显示
    local rogueCfg = ConfigManager:getConfig("skill_rogue")
    local curCfg = rogueCfg[skillId]
    if not curCfg or curCfg.universal ~= 1 then
        return
    end

    local value = 0
    local selectSkillMap = self.battleData:getSelectSkillMap(self.battleController:getCurActionSide())
    if selectSkillMap[skillId] then
        value = selectSkillMap[skillId].value
    end

    if curCfg.toast_mark then
        for id, info in pairs(rogueCfg) do
            if selectSkillMap[id] and id ~= skillId and info.toast_mark == curCfg.toast_mark then
                value = value + selectSkillMap[id].value
            end
        end
    end

    local valueStr
    if skillId == 24 then -- 特殊处理
        if ModuleManager.BattleManager.battleController then
            valueStr = ModuleManager.BattleManager.battleController.battleData:getSkillCount(skillId)
            valueStr = I18N:getText("skill_rogue", skillId, "desc", valueStr)
        end
    else
        valueStr = ModuleManager.HeroManager:getSkillRogueDesc(skillId, value)
    end


    if self.showCommonSkillTipsSid then
        self:unscheduleGlobal(self.showCommonSkillTipsSid)
        self.showCommonSkillTipsSid = nil
    end
    self.skillDescTipsNode:setVisible(true)
    self.skillDescTips:setText(valueStr)
    self.showCommonSkillTipsSid = self:performWithDelayGlobal(function()
        self.skillDescTipsNode:setVisible(false)
    end, 1.5)
end

function BattleBaseUI:showBossEnterAni(bornTime, bossName, monsterComp, callback)
    if not self.bossEnterNode then
        if callback then
            callback()
        end
        self.battleController:showBuffTips(SIDE_DEF, true)
        return
    end
    AudioManager:playEffect(AudioManager.EFFECT_ID.BOSS_WARNING)
    self.bossEnterNode:setVisible(true)
    self.bossEnterImg:setVisible(false)
    self.bossName:setText(bossName)
    self.bossName:setAnchoredPositionX(-756)
    self.bossEnterNodeCanvasGroup.alpha = 0
    monsterComp:getBaseObject():setLocalPosition(DEFAULT_X, 0, 0)
    if self.bossEnterAniSeq then
        self.bossEnterAniSeq:Kill()
        self.bossEnterAniSeq = nil
    end
    self.bossEnterAniSeq = self.root:createBindTweenSequence()
    self.bossEnterAniSeq:Append(self.bossEnterNodeCanvasGroup:DOFade(1, 0.2))
    self.bossEnterAniSeq:AppendCallback(function()
        self.bossEnterImg:setVisible(true)
        self.bossEnterImg:playAnim("idle", false, true)
        -- CS.UnityEngine.Animator.StringToHash("born") 结果是1155742626
        self.bossEnterNodeAnimator.enabled = true
        self.bossEnterNodeAnimator:Play(1155742626, -1, 0)
        monsterComp:initPosition()
        monsterComp:getBaseObject():setParent(self.maxLayerNode, false)
        if callback then
            callback()
        end
    end)
    self.bossEnterAniSeq:AppendInterval(2)
    self.bossEnterAniSeq:Append(self.bossEnterNodeCanvasGroup:DOFade(0, 0.2))
    self.bossEnterAniSeq:AppendCallback(function()
        monsterComp:getBaseObject():setParent(self:getBattleNode(), false)
        self.bossEnterNode:setVisible(false)
        self.battleController:showBuffTips(SIDE_DEF, true)
        self.bossEnterNodeAnimator.enabled = false
    end)
end

function BattleBaseUI:clearEnterShowBoardSeq()
    if self.enterShowBoardSeq then
        self.enterShowBoardSeq:Kill()
        self.enterShowBoardSeq = nil
    end
end

function BattleBaseUI:getGridEdgeObj()
    local prefab = CS.UnityEngine.Object.Instantiate(self.gridEdgeCacheCell:getGameObject())
    return prefab
end

function BattleBaseUI:clear()
	if self.alreadyClear then
		return
	end
	self.alreadyClear = true
    if self.battleNode then
        self.battleNode:removeAllChildren()
    end
    if self.maxLayerNode then
        self.maxLayerNode:removeAllChildren()
    end
    if self.fxNode then
        self.fxNode:removeAllChildren()
    end
    if self.battleNumberNode then
        self.battleNumberNode:removeAllChildren()
    end
    if self.gridEdgeNode then
        self.gridEdgeNode:removeAllChildren()
    end
    if self.hpProgressYellowLeftSid then
        self:unscheduleGlobal(self.hpProgressYellowLeftSid)
        self.hpProgressYellowLeftSid = nil
    end
    if self.hpProgressYellowRightSid then
        self:unscheduleGlobal(self.hpProgressYellowRightSid)
        self.hpProgressYellowRightSid = nil
    end
    if self.hpProgressLeftTween then
        self.hpProgressLeftTween:Kill()
        self.hpProgressLeftTween = nil
    end
    if self.hpProgressYellowLeftTween then
        self.hpProgressYellowLeftTween:Kill()
        self.hpProgressYellowLeftTween = nil
    end
    if self.hpProgressRightTween then
        self.hpProgressRightTween:Kill()
        self.hpProgressRightTween = nil
    end
    if self.hpProgressYellowRightTween then
        self.hpProgressYellowRightTween:Kill()
        self.hpProgressYellowRightTween = nil
    end
    if self.bgMoveTween then
        self.bgMoveTween:Kill()
        self.bgMoveTween = nil
    end
    if self.shakeTween then
        self.shakeTween:Kill()
        self.shakeTween = nil
    end

    if self.showTutorialFingerSeq then
        self.showTutorialFingerSeq:Kill()
        self.showTutorialFingerSeq = nil
    end

    if self.switchBoardSeq then
        self.switchBoardSeq:Kill()
        self.switchBoardSeq = nil
    end

    if self.eliminationAniSeq then
        self.eliminationAniSeq:Kill()
        self.eliminationAniSeq = nil
    end

    if self.generateSkillAniSeq then
        self.generateSkillAniSeq:Kill()
        self.generateSkillAniSeq = nil
    end

    if self.gotOneSkillAniSeq then
        self.gotOneSkillAniSeq:Kill()
        self.gotOneSkillAniSeq = nil
    end

    if self.shuffleBoardSeq then
        self.shuffleBoardSeq:Kill()
        self.shuffleBoardSeq = nil
    end

    if self.gridCells then
        for _, cell in pairs(self.gridCells) do
            if cell.fallSeq then
                cell.fallSeq:Kill()
                cell.fallSeq = nil
            end
        end
    end

    if self.cacheSkillAniSeq then
        self.cacheSkillAniSeq:Kill()
        self.cacheSkillAniSeq = nil
    end

    if self.monsterSkillAniSeq then
        self.monsterSkillAniSeq:Kill()
        self.monsterSkillAniSeq = nil
    end

    if self.showCommonSkillTipsSid then
        self:unscheduleGlobal(self.showCommonSkillTipsSid)
        self.showCommonSkillTipsSid = nil
    end

    if self.autoCloseBuffSid then
        self:unscheduleGlobal(self.autoCloseBuffSid)
        self.autoCloseBuffSid = nil
    end

    if self.bossEnterAniSeq then
        self.bossEnterAniSeq:Kill()
        self.bossEnterAniSeq = nil
    end

    if self.moveGridCellsSeq then
        self.moveGridCellsSeq:Kill()
        self.moveGridCellsSeq = nil
    end

    self:clearEnterShowBoardSeq()
end

return BattleBaseUI