麻将牌堆渲染(Lua)

麻将牌堆渲染(Lua):

用法:

lua 复制代码
    -- 牌堆
    local nTotalRemaining = tData.nRemainingCount or 0
    local tDice = tData.tDice or { 0, 0 }
    local tWall = MahjongUI_CalcWallTiles(tData.nRemainingCount, tData.tDice)
    for nDir = 1, 4 do
        MahjongUI_UpdateMaPai(nDir, tWall[nDir], 26)
    end

实现:

lua 复制代码
local MAHJONG_TOTAL_TILES   = 108  -- 总牌数(万条筒各1-9×4)
local MAHJONG_TILES_PER_WALL = 27  -- 每方墙 108/4 = 27 张

--- 根据骰子点数和剩余张数计算4方墙各显示多少张
--- 牌墙是连续一整条,从切牌位顺时针顺序消耗,剩余显示在对应墙
--- @param nRemainingCount number 剩余总张数
--- @param tDice table|nil 骰子数组 {d1, d2},nil时均分
--- @return table tWallTiles[1..4] 每方墙张数
function MahjongUI_CalcWallTiles(nRemainingCount, tDice)
    local tWall = { 0, 0, 0, 0 }
    local R = nRemainingCount or 0
    if R <= 0 then return tWall end

    local nCutWall = 1
    local nDeadCards = 0
    if tDice and tDice[1] and tDice[2] and not (tDice[1] == 0 and tDice[2] == 0) then
        local nSum = tDice[1] + tDice[2]
        nCutWall = ((nSum - 1) % 4) + 1
        nDeadCards = (nSum - 1) * 2
    end

    -- 1. 切牌墙先放死牌
    local nDead = math.min(nDeadCards, R)
    tWall[nCutWall] = nDead
    R = R - nDead
    if R <= 0 then return tWall end

    -- 2. 顺时针分配(下家->对家->上家)
    local MAX_PER_WALL = 26
    local ORDER = { [1]=2, [2]=3, [3]=4, [4]=1 }
    local nIdx = nCutWall
    while R > 0 do
        nIdx = ORDER[nIdx]
        local nCanAdd = MAX_PER_WALL - tWall[nIdx]
        local nAdd = math.min(nCanAdd, R)
        tWall[nIdx] = tWall[nIdx] + nAdd
        R = R - nAdd
        if nCanAdd <= 0 then break end
    end
    if R > 0 then
        tWall[nCutWall] = tWall[nCutWall] + R
    end

    if MAHJONG_DEBUG then
        module_mahjong.MJLOGT("CalcWallTiles: result=%s", toString(tWall))
    end
    return tWall
end

--- 根据骰子计算每方墙初始牌数(发牌后剩余56张)
--- @param tDice table 骰子点数
--- @return table[4] 每方墙初始牌数
function MahjongUI_GetInitWallTiles(tDice)
    return MahjongUI_CalcWallTiles(56, tDice)
end

-- === _UpdateMaPai_Unified ===
-- 统一牌墙渲染(带详细日志)
local function _UpdateMaPai_Unified(nDir, wndMaPai, nTiles, nCutSeat, nDiceSum)
    local szTemplate = MAPAI_TEMPLATES[nDir]
    local MAX_PILES = 13
    local bIsCutWall = (nDir == nCutSeat)
    local nDeadPiles = bIsCutWall and (nDiceSum - 1) or 0
    local nDeadCards = nDeadPiles * 2
    local nEffMaxPiles = MAX_PILES - nDeadPiles

    local nEffectiveTiles = math.max(0, nTiles - nDeadCards)
    local nRemainingPiles = math.ceil(nEffectiveTiles / 2)
    local bPartial = (nEffectiveTiles > 0) and (nEffectiveTiles % 2 == 1)
    local nConsumed = nEffMaxPiles - nRemainingPiles

    -- 入口方向:仅本家入口在索引13,其他三家入口在索引1
    local bEntryAtHigh = (nDir == 1 or nDir == 4)

    if MAHJONG_DEBUG then
        module_mahjong.MJLOGT("_UpdateMaPai_Unified: dir=%d, tiles=%d, isCut=%s, deadPiles=%d, effTiles=%d, remainPiles=%d, consumed=%d, partial=%s",
            nDir, nTiles, tostring(bIsCutWall), nDeadPiles, nEffectiveTiles, nRemainingPiles, nConsumed, tostring(bPartial))
    end

    local tCutWalls = {}
    for i = 1, MAX_PILES do
        local nIdx = i   -- 控件从左到右创建
        -- 计算该位置在消耗方向上的逻辑序号(1 = 入口端)
        local nPos
        if bEntryAtHigh then
            nPos = MAX_PILES - i + 1   -- 本家:索引13对应位置1(入口)
        else
            nPos = i                    -- 其他:索引1对应位置1(入口)
        end

        local bPH, bIsPartial
        if nPos <= nDeadPiles then
            bPH, bIsPartial = false, false
        elseif nPos <= nDeadPiles + nConsumed then
            bPH, bIsPartial = true, false
        elseif nPos == nDeadPiles + nConsumed + 1 and bPartial then
            bPH, bIsPartial = false, true
        else
            bPH, bIsPartial = false, false
        end

        local wndCard = wndMaPai:CreateChildWindowFromTemplate(szTemplate)
        if not wndCard then break end
        wndCard:SetName("mapai_" .. tostring(nDir) .. "_" .. tostring(nIdx))
        wndCard:Show(true)

        local card1 = wndCard:FindEx("card1")
        local card2 = wndCard:FindEx("card2")
        card1:Show(true)
        card2:Show(true)

        if bPH then
            card1:SetAlpha(0.0)
            card2:SetAlpha(0.0)
        elseif bIsPartial then
            card1:SetAlpha(1.0)
            card2:SetAlpha(0.0)
        elseif bIsCutWall then
            table_insert(tCutWalls, wndCard)
        else
            card1:SetAlpha(1.0)
            card2:SetAlpha(1.0)
        end
    end

    -- 切牌墙专用二次修正
    if bIsCutWall and #tCutWalls > 0 then
        local totalCutPiles = nDiceSum - 1
        local totalCutCards = totalCutPiles * 2
        local nDrawn = math.max(0, totalCutCards - nTiles)
        local nHideFull = math.floor(nDrawn / 2)
        local bCutPartial = (nDrawn % 2 == 1)

        -- 根据方向决定遍历顺序:本家/左家倒序(从右到左),右家/对家正序(从左到右)
        local bReverse = bEntryAtHigh   -- 高位入口的方向采用倒序
        local nCount = #tCutWalls

        for seq = 1, nCount do
            local wndCard
            if bReverse then
                wndCard = tCutWalls[nCount - seq + 1]   -- 倒序
            else
                wndCard = tCutWalls[seq]                -- 正序
            end

            local card1 = wndCard:FindEx("card1")
            local card2 = wndCard:FindEx("card2")

            if seq <= nHideFull then
                card1:SetAlpha(0.0)
                card2:SetAlpha(0.0)
            elseif seq == nHideFull + 1 and bCutPartial then
                card1:SetAlpha(1.0)
                card2:SetAlpha(0.0)
            else
                card1:SetAlpha(1.0)
                card2:SetAlpha(1.0)
            end
        end
    end
end

-- === MahjongUI_UpdateMaPai ===
-- 牌墙渲染入口:根据切牌位置分派到固定13墩(Fixed13)或动态 SC 模板。
-- 销毁并重新创建所有牌墙实例。
-- 阶段:所有阶段(初始发牌和游戏中)。
-- @param nDir number 方向 (1-4)
-- @param nRemainingTiles number 该墙当前剩余牌数
-- @param nInitTiles number 该墙初始牌数
-- @return nil
function MahjongUI_UpdateMaPai(nDir, nRemainingTiles, nInitTiles)
    local frame = FindWindow("mahjong_main_panel")
    if not frame then return end
    local wndMaPai = MahjongUI_FindWindow(frame, "wnd_table", "list_mapai" .. tostring(nDir))
    if not wndMaPai then return end

    local nTiles = nRemainingTiles or 0
    local nInit = nInitTiles or 0
    if nInit <= 0 then
        if nDir ~= 1 and nDir ~= 2 and nDir ~= 3 and nDir ~= 4 then return end
    end

    wndMaPai:DestroyAllChildWindow()
    local tDice = module_mahjong.mahjong_client_core.Client_GetData().tDice
    local nDiceSum = (tDice and tDice[1] and tDice[2]) and (tDice[1] + tDice[2]) or 0
    local nCutSeatAbs = (nDiceSum > 0) and ((nDiceSum - 1) % 4 + 1) or 0
    local nMySeat = MahjongUI_GetMySeat()
    local nCutSeat = (nCutSeatAbs > 0 and nMySeat > 0) and MahjongUI_GetRelativeSeat(nMySeat, nCutSeatAbs) or 0

    if MAHJONG_DEBUG then
        module_mahjong.MJLOGT("UpdateMaPai: nDir=%d nCutSeat(abs=%d→rel=%d) nMySeat=%d nTiles=%d",
            nDir, nCutSeatAbs, nCutSeat, nMySeat, nTiles)
    end

    -- 统一渲染(正式使用)
    _UpdateMaPai_Unified(nDir, wndMaPai, nTiles, nCutSeat, nDiceSum)
end
相关推荐
大数据张老师1 分钟前
Typora 导出 Word 模版操作手册
开发语言·c#·word·typora
略略略咯咯5 分钟前
stream流浅拷贝
开发语言·python
long3167 分钟前
Java 团队入门到精通学习资料
java·开发语言
城管不管19 分钟前
rabbitmq如何保证消息不丢失?解决方案又是什么?
开发语言·ai·面试·职场和发展·rabbitmq·php·agent
程序员良辰20 分钟前
【TongWeb7】启动接近两分钟问题排查
java·开发语言·中间件
深色風信子22 分钟前
Kotlin Bytedeco OpenCV 图像图像58 无缝克隆
开发语言·opencv·kotlin·bytedeco
朝阳3934 分钟前
react19【系列实用教程】实用组件封装
开发语言·前端·javascript
我是苏苏41 分钟前
C#基础:使用System.Speech.Synthesis离线播放/朗读文字内容
开发语言·c#
Patrick在香港1 小时前
Python依赖管理从踩坑到选型:pip/poetry/uv四种方案全面实测
开发语言·python·数据分析·scikit-learn·ai编程·pip·uv
我命由我123451 小时前
Kotlin 面向对象 - 枚举排序
android·java·开发语言·java-ee·kotlin·android studio·android-studio