麻将牌堆渲染(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
相关推荐
Wonderful U44 分钟前
基于Python+Django的在线题库与智能阅卷系统:从痛点分析到完整实现
开发语言·python·django
码语智行44 分钟前
拦截器、接口限流、过滤器、防重发/幂等性功能说明
开发语言·网络·python
我是一颗柠檬1 小时前
【Redis】事务与Lua脚本Day7(2026年)
数据库·redis·后端·lua·database
雨落在了我的手上1 小时前
初始java(十七):常⽤⼯具类介绍
java·开发语言
凤凰院凶涛QAQ1 小时前
《Java版数据结构 & 集合类剖析》集合框架的封装设计与顺序表:“从 Iterable 到 ArrayList:集合框架的‘职业树“
java·开发语言·数据结构
孟华苏1 小时前
怎么快速排查内存泄漏问题
java·开发语言·python
zz34572981131 小时前
C语言中字符串常量存储位置
c语言·开发语言·算法·青少年编程
noipp1 小时前
推荐题目:洛谷 P16510 [GKS 2015 #C] gRanks
java·c语言·开发语言·c++·python·算法
flyinmind1 小时前
Java环境与Android环境中使用QuickJS
java·开发语言·javascript·quickjs