Module:Sandbox/LootChest

Jump to navigation Jump to search
local getArgs = require('Module:Arguments').getArgs
local config = require('Module:Sandbox/LootChest/config')
local cov = config['json']['cov']
local sprite, spriteFile, alink
if not(config['customize']['itemLink'] and config['customize']['struLink'])
    then
    sprite = require('Module:Sprite').link
    spriteFile = require('Module:SpriteFile').link
end
if config['customize']['noUseAutoLink'] == true
    then
    alink = function(a) return a end
else
    alink = require('Module:Autolink').invlink
end

local p = {}

local function makeInvokeFunc(funcName)
    return function (frame)
        local args = getArgs(frame)
        return p[funcName](args)
    end
end

function struLink(name)
    -- 输入结构的ID,返回最后结果。

    local text = name
    if cov['structure']['text'][name]
        then
        text = cov['structure']['text'][name]
    end

    local id = ''
    if cov['structure']['sprite-id'][name]
        then
        id = cov['structure']['sprite-id'][name]
    end

    local dataType
    if cov['structure']['sprite-data-type'][name]
        then
        dataType = cov['structure']['sprite-data-type'][name]
    else
        dataType = cov['structure']['sprite-data-type']['default']
    end

    local link = 'none'
    if text:find('%|')
        then
        link = text:match('%[%[(.*)%|')
        text = text:match('%|(.*)%]%]')
    end

    return spriteFile({['name'] = dataType, ['id'] = id, ['link'] = link, ['text'] = text})

end

function ref(list)
    -- 输入物品的命名空间ID和itemLink最后的显示结果,对齐检索cov.json并添加注释。
    local ref = ""
    local generator = function(text, name)
        return mw.getCurrentFrame():callParserFunction('#tag:ref', {
        	text,
        	['name'] = name,
        	['group'] = '战利品',
        })
    end

    for _, entry in ipairs(list)
        do
        ref = ref..generator(entry[1], entry[2])
    end

    return ref
end


function itemLink(NsId, aux, functions, ref)
        -- 输入物品的命名空间ID,返回最终的显示结果。

    local enName, id, link, text, dataType
    local refText = {}
    enName = NsId:gsub('_', ' ')

        --- 如果在cov.json中存在转换规则,则进行转换,否则直接将命名空间ID作为英文名输入sprite
    local itemCov = {}
    if cov['item']['cov'][NsId]
        then
        itemCov = cov['item']['cov'][NsId]
        if aux and itemCov['aux']
            then
            itemCov = itemCov['aux'][tonumber(aux)] or itemCov
        end
        if itemCov['name'] then enName = itemCov['name'] end
        if itemCov['id'] then id = itemCov['id'] end
        if itemCov['link'] then link = itemCov['link'] end
        if itemCov['text'] then text = itemCov['text'] end
    end

    local Er, Sd, dataNum
    local funcCov
    for _, func in ipairs(functions) do
        funcCov = itemCov['functions']
        if funcCov and funcCov[func['function']] and funcCov[func['function']][tonumber(func['value']) or func['value']]
            then
            funcCov = funcCov[func['function']][tonumber(func['value']) or func['value']]
            if funcCov['name'] then enName = funcCov['name'] end
            if funcCov['id'] then id = funcCov['id'] end
            if funcCov['link'] then link = funcCov['link'] end
            if funcCov['text'] then text = funcCov['text'] end
        end
        Er = Er or func['function'] == 'enchant_randomly' or func['function'] == 'enchant_with_levels' or func['function'] == 'specific_enchants' or func['function'] == 'enchant_book_for_trading' or func['function'] == 'set_enchantments'
        Sd = Sd or func['function'] == 'set_damage'
    end
    text = text or alink(enName):gsub('.*%|', '')
    if Er and Sd and config['mark']['damaged enchanted']
        then
        text = config['mark']['damaged enchanted']:gsub('$s',text)
        else
        if Er then text = config['mark']['enchanted']:gsub('$s',text) end
        if Sd then text = config['mark']['damaged']:gsub('$s',text) end
    end
    dataType = cov['item']['sprite-data-type'][NsId] or cov['item']['sprite-data-type']['default']

    for _, entry in ipairs(ref)
        do
        table.insert(refText, entry[2])
    end
    table.sort(refText)

    return {['name'] = dataType, [1] = enName, ['id'] = id, ['link'] = link, ['text'] = text}, text .. table.concat(refText, ', ')

end

function chanceCount(weightProportion, rolls)
        -- 概率计算函数

    if type(rolls) == "table"
        then
        local fp = 1 - weightProportion--failureProbability
        local sum = 0
        for i = rolls['min'], rolls['max'] do
            sum = sum + math.pow(fp, i)
        end
        return string.format("%.4f", 1 - (sum / (rolls['max'] - rolls['min'] + 1)))
        else
        return string.format("%.4f", 1 - math.pow((1 - weightProportion), rolls))
    end
end

function getItemStack(item, poolFuncs, edition)

        local functions = item['functions'] or {}
        for _,v in ipairs(poolFuncs) do table.insert(functions,v) end
        local covSingle, funcName, funcValue
        local id = (item['name'] or 'nothing'):gsub('minecraft:', '')
        local aux = 0
        local funcList = {}
        local count, averageCount = 1, 1
        local ref = {}
        local checkAssign = true

        ---遍历所有函数
        for _, func in pairs(functions) do
            funcName = func['function']:gsub('minecraft:','')
            funcValue = funcName
            if funcName == 'set_count'
                then
                count = func['count']
            elseif funcName =='set_data'
                then
                aux = func['data']
            elseif funcName =='exploration_map' and id == 'map'
                then
                id = 'buried_treasure_map'--此处使用英文名而非nsid以作区分
            else

                covSingle = config['function'][funcName]
                if covSingle
                    then

                    ---处理函数值
                    if covSingle[1]
                        then
                        if type(func[covSingle[1]]) == 'table'
                        	then
                        	if func[covSingle[1]]['min'] --For JE/BE enchant_with_levels
                            	then funcValue = tostring(func[covSingle[1]]['min']) .. '-'..tostring(func[covSingle[1]]['max'])
                        	elseif func[covSingle[1]][1] and type(func[covSingle[1]][1]) == 'table'
                        		then --For BE specific_enchants & BE enchant_book_for_trading & JE/BE set_stew_effect
                        			local last
                        			for _, v in ipairs(func[covSingle[1]]) do last = v end
                        			funcValue = tostring(last['id'] or last['type'] or last['name']):gsub('minecraft:','')
                        			checkAssign = false
                        	else
                        		if funcName == 'set_enchantments'
                        			then
                        				local geusage
                        				for _, v in pairs(func[covSingle[1]]) do geusage = _ end
                        				funcValue = tostring(geusage):gsub('minecraft:','')..'-'..tostring(func[covSingle[1]][geusage])  --For JE set_enchantments
                        				checkAssign = false
                        		else
                        			funcValue = tostring(func[covSingle[1]][1]):gsub('minecraft:','') --For JE enchant_randomly
                        		end
                        	end
                        else	
                            funcValue = tostring(func[covSingle[1]]):gsub('minecraft:','')
                        end
                    end
                    ---储存各个函数
                    table.insert(
                                    funcList,
                                    {['function'] = funcName,
                                     ['value'] = funcValue})

                    --处理注释
                    local funcRefList = cov['note']['functions']
                    if funcRefList[funcName] and funcRefList[funcName][tonumber(funcValue) or funcValue]
                    then
                        table.insert(ref, {funcRefList[funcName][tonumber(funcValue) or funcValue],
                        covSingle['name'] or funcName .. ':'..((config['function'][funcValue] or {})['name'] or funcValue)})
                    end
                end
            end
        end

        ---处理物品数量
        if type(count) == 'table'
            then
            if count['max'] ~= count['min']
                then
                averageCount = (count['min'] + count['max']) /2
                count = tostring(count['min']) ..
           '-'..tostring(count['max'])
                else
                averageCount = count['min']
                count = tostring(count['min'])
            end
            else
            averageCount = count
            count = tostring(count)
        end

        --此处使用硬编码以区分游戏中做特殊处理的物品
        if id == 'book' and funcList[1] and funcList[1]['function']:match('enchant')
            then id = 'enchanted_book'
        elseif id == 'map' and edition:find('java')
            then id = 'empty_map'--此处使用英文名而非nsid以作区分
        end

        --处理注释
        if cov['note']['assign'][id] and checkAssign
            then
            table.insert(ref, {cov['note']['assign'][id], id})
        end

        return {
            ['id'] = id,
            ["value"] = count,
            ["averageValue"] = averageCount,
            ["weight"] = item['weight'] or 1,
            ['functions'] = funcList,
            ["aux"] = tostring(aux),
            ['ref'] = ref
            }
end

function dataParsing(args, edition, genre)
    -- 获取数据table,同时用于all模式和item模式。
    
    local loot = config['json'][edition]
    local struList, listNum = {}, 1
    local poorsValue, stru, itemNum, rItemNum, poolsList, poolNum
    local poorsWeight, rolls
    -- 预定义循环内变量,分别为函数体内使用,第一层for使用,第二层for使用

    if genre == "all" then loopPrimer = loot else loopPrimer = args end

    for q, w in pairs(loopPrimer) do
        --结构循环
        itemNum, rItemNum = 1, 1

        poolsList, poorsValue = {}, {}

        if genre == "all" then stru = q else stru = w end

        if loot[stru] ~= nil
            then

            for o, pool in ipairs(loot[stru]['pools']) do
                --奖池循环

                poorsWeight = 0
                
                
                rolls = pool['rolls']


                if type(rolls) == 'table'
                    then
                    poorsValue[o] = rolls['min'] .. '-'..rolls['max']
                else
                    poorsValue[o] = tostring(rolls)
                end

                for _, itemStack in ipairs(pool['entries']) do
                    --物品循环
                    poolsList[itemNum] = getItemStack(itemStack, pool['functions'] or {}, edition)
                    poolsList[itemNum]['pools'] = o
                    poorsWeight = poorsWeight + poolsList[itemNum]['weight']
                    itemNum = itemNum + 1
                end
                --到这里为止,poolsList包含了一个奖池的全部信息,然后在下一个循环进行概率等内容的计算,之后在最外层进行排序。

                for n in ipairs(pool['entries']) do
                    --物品循环
                    poolsList[rItemNum]['weightProportion'] = tonumber(poolsList[rItemNum]['weight']) / poorsWeight
                    poolsList[rItemNum]['weightProportionOut'] = '<sup>'..tostring(poolsList[rItemNum]['weight']) .. '</sup>/<sub>'..tostring(poorsWeight) .. '</sub>'
                    poolsList[rItemNum]['chance'] = chanceCount(poolsList[rItemNum]['weightProportion'], rolls)
                    poolsList[rItemNum]['chanceOut'] = tonumber(poolsList[rItemNum]['chance'] * 100) .. '%'
                    poolsList[rItemNum]['itemNum'] = string.format("%.2f", poolsList[rItemNum]['averageValue'] * poolsList[rItemNum]['chance'])
                    poolsList[rItemNum]['boxNum'] = string.format("%.1f", 1 / poolsList[rItemNum]['chance'])
                    rItemNum = rItemNum + 1
                end

                table.sort(poolsList, function(a, b) return a['chance'] > b['chance'] end)
                
                poolNum = o

            end
            struList[listNum] = {}
            struList[listNum]['name'] = stru
            struList[listNum]['value'] = poolsList
            struList[listNum]['poolsValue'] = poorsValue
            struList[listNum]['poolNum'] = poolNum
            struList[listNum]['itemNum'] = itemNum - 1
            listNum = listNum + 1
        else
            struList[listNum] = {}
            struList[listNum]['poolNum'] = 0
            struList[listNum]['itemNum'] = 0
            listNum = listNum + 1
        end

    end

    table.sort(struList, function(a, b) return a['itemNum'] > b['itemNum'] end)

    return struList
end

function getTableAll(data, args)
    --获取all函数的结果文本。
    local wikiTable, poolNum, wikiTablePart = '', '', {}

    for i, k in ipairs(data) do
        if k['poolNum'] ~= 0
            then
            poolNum = k['poolNum']
            --表头部分
            wikiTable = wikiTable..'<div>\n{| class="wikitable sortable jquery-tablesorter" style="'..config['style']['allTableStyle'] .. '"\n!colspan="' .. (poolNum * 2 + 4)

			if args['header'] then
				wikiTable = wikiTable..'"|'.. args['header'] .. '\n|-\n'
            elseif config['customize']['struLink'] then
                wikiTable = wikiTable..'"|'..config['struLink'](k['name']) .. '\n|-\n'
            else
                wikiTable = wikiTable..'"|'..struLink(k['name']) .. '\n|-\n'
            end
            --此处

            wikiTable = wikiTable..
            '! rowspan=2 | ' .. config['header']['item'] .. '\n' ..
            '! colspan='..poolNum..' | ' .. config['header']['stack'] .. '\n' ..
            '! colspan='..poolNum..' | ' .. config['header']['weight'] .. '\n' ..
            '! rowspan=2 | ' .. config['header']['probability'] .. '\n' ..
            '! rowspan=2 | ' .. config['header']['itemNumber'] .. '\n' ..
            '! rowspan=2 | ' .. config['header']['boxNumber'] .. '\n |- \n'

            --表中部分
            for h = 1, 2 do
                for y = 1, poolNum do
                    wikiTable = wikiTable..'! '..config['header']['weightNoteH']..k['poolsValue'][y]..config['header']['weightNoteF']..k['poolsValue'][y] .. '×</abbr> \n'
                end
            end

            wikiTable = wikiTable..'|- \n'

            for o, p in ipairs(data[i]['value']) do
                --值循环
                if config['customize']['itemLink'] then
                    wikiTablePart[o] = '|'..config['itemLink'](p['id'], p['aux'], p['functions'], config)..ref(p["ref"])
                else
                    wikiTablePart[o] = '|'..spriteFile(itemLink(p['id'], p['aux'], p['functions'], p['ref']))..ref(p["ref"])
                end

                for m = 1, poolNum do
                    if p['pools'] == m
                        then
                        wikiTablePart[o] = wikiTablePart[o] .. '||'..p['value']
                    else
                        wikiTablePart[o] = wikiTablePart[o] .. '||' .. ' '
                    end
                end

                for m = 1, poolNum do
                    if p['pools'] == m
                        then
                        wikiTablePart[o] = wikiTablePart[o] .. '||'..p['weightProportionOut']
                    else
                        wikiTablePart[o] = wikiTablePart[o] .. '||' .. ' '
                    end
                end

                wikiTablePart[o] = wikiTablePart[o] .. '||'..p['chanceOut'] .. '||'..p['itemNum'] .. '||'..p['boxNum'] .. '\n|-\n'
                wikiTable = wikiTable..wikiTablePart[o]
            end
            wikiTable = wikiTable..'|}\n</div>'
        end
    end
    return wikiTable
end

function itemDataCov(args, edition)
    local itemList = {}
    local link,key,aux,name
    local data = dataParsing({}, edition, 'all')
 
    for _, arg in ipairs(args) do
        name = arg:gsub('minecraft:', ''):gsub(':.*','')
        aux = arg:gsub('minecraft:', ''):match(':(.*)')
        for _, struInf in ipairs(data) do

            for _, itemInf in ipairs(struInf['value']) do

                if itemInf['id'] == name and (aux==nil or itemInf['aux'] == aux)
                    then
                    link, key = itemLink(itemInf['id'], itemInf['aux'], itemInf['functions'], itemInf['ref'])
                    itemList[key] = itemList[key] or {}                   
                    itemList[key]['ref'] = itemList[key]['ref'] or ref(itemInf["ref"])
                    itemList[key]['link'] = itemList[key]['link'] or spriteFile(link)
                    itemList[key]['list'] = itemList[key]['list'] or {}
                    itemInf['stru'] = struInf['name']
                    table.insert(itemList[key]['list'], itemInf) 
                end

            end

        end

    end
    return itemList
end

function getTableItem(data)
    local wikiTable = ''
    if next(data) ~= nil then

    wikiTable = '\n{| class="wikitable sortable jquery-tablesorter" style="'..config['style']['itemTableStyle'] .. '\n' ..
            '! ' .. config['header']['item'] .. '\n' ..
            '! ' .. config['header']['source'] .. '\n' ..
            '! ' .. config['header']['itemNumber'] .. '\n' ..
            '! ' .. config['header']['quantity'] .. '\n' ..
            '! ' .. config['header']['probability'] .. '\n' ..
            '! ' .. config['header']['boxNumber'] .. '\n' ..
            '|-\n'

    for _, itemInf in pairs(data) do
        if itemInf['list'][1]
            then
            wikiTable = wikiTable..'| rowspan='.. #itemInf['list'] .. '|'.. itemInf['link'] .. itemInf['ref'] .. '\n'

            for _, lineInf in pairs(itemInf['list']) do
                wikiTable = wikiTable
                .. '|' ..struLink(lineInf['stru']) .. '\n'
                .. '|' ..lineInf['itemNum'] .. '\n'
                .. '|' ..lineInf['value'] .. '\n'
                .. '|' ..lineInf['chanceOut'] .. '\n'
                .. '|' ..lineInf['boxNum'] .. '\n'
                .. '|-\n'
            end
        end
    end

    wikiTable = wikiTable..'|}\n'

    end
    return wikiTable
end

function getTableItemAll(edition)
    local data = dataParsing({}, edition, 'all')
    local itemList, resultList = {}, {}
    for struNum, struInf in pairs(data) do

        for itemNum, itemInf in ipairs(struInf['value']) do
            itemList[itemInf['id']] = true 
        end

    end

    for id in pairs(itemList) do
        table.insert(resultList, id)
    end
    resultList['version'] = edition
    return p._item(resultList)
end


function result(args, getData, getTable)
    local genre
    if args['type'] == 'all' or args[1] == 'all'
        then
        genre = 'all'
    else
        genre = 'arg'
    end

    local toText = function (dt)
    	local text = mw.html.create("div"):addClass('loot-chest-container'):wikitext(dt)
    	-- return require( 'Module:TSLoader' ).call( 'User:Star00/LootChest/styles.css' ) .. tostring(text)
    	return tostring(text)
    end

    if args['version']
        then return toText(getTable(getData(args, args['version'], genre), args))
    end

    local cout = ''
    local dataJava, dataBedrock, dataJavaDev, dataBedrockBeta
    dataJava = getTable(getData(args, 'java', genre), args)
    dataBedrock = getTable(getData(args, 'bedrock', genre), args)

    if config['debug']['onlyJava'] or args['only'] == 'java'
        then 
        cout = toText(dataJava)
    elseif config['debug']['onlyBedrock'] or args['only'] == 'bedrock'
        then 
        cout = toText(dataBedrock)
    elseif mw.text.killMarkers(dataJava) == mw.text.killMarkers(dataBedrock)
        then
        cout = config['label']['javaAndBedrock'] .. '\n'..toText(dataJava)
    else
        if dataJava ~= ''
            then
            cout = config['label']['java'] .. '\n'..toText(dataJava)
        end
        if dataBedrock ~= ''
            then
            cout = cout .. config['label']['bedrock'] .. '\n'..toText(dataBedrock)
        end
    end
    
    if config['switch']['javaDev'] and args['only'] ~= 'bedrock'
        then
        dataJavaDev = getTable(getData(args, 'javaDev', genre), args)
        if mw.text.killMarkers(dataJava) ~= mw.text.killMarkers(dataJavaDev) and dataJavaDev ~= ''
            then
            cout = cout .. config['label']['javaDev'] .. '\n'..toText(dataJavaDev)
        end
    end
    if config['switch']['bedrockBeta'] and args['only'] ~= 'java'
        then
        dataBedrockBeta = getTable(getData(args, 'bedrockBeta', genre), args)
        if mw.text.killMarkers(dataBedrock) ~= mw.text.killMarkers(dataBedrockBeta) and dataBedrockBeta ~= ''
            then
            cout = cout .. config['label']['bedrockBeta'] .. '\n'..toText(dataBedrockBeta)
        end
    end

    return cout
end

p.all = makeInvokeFunc('_all')

function p._all(args)
    return result(args, dataParsing, getTableAll)
end

p.item = makeInvokeFunc('_item')

function p._item(args)
    if args['type'] == 'all' or args[1] == 'all'
        then
        return result(args, function(a,b) return b end, getTableItemAll)
        else
        return result(args, itemDataCov, getTableItem)
    end
end

return p