Module:Sandbox/LootTable

Jump to navigation Jump to search

Trying to make a Loot Table parser that can be used instead of the current LootChest module, but with support for other loot types and the ease of use of just needing to paste the JSON loot tables when new versions comes out (very early version, pretty much nothing is working).

Usage[edit source]

[view] [edit] [history] [refresh]The above documentation is transcluded from Module:Sandbox/LootTable/doc.
local java_data = mw.text.jsonDecode(require('Module:Sandbox/LootTable/Java.json'))

local p = {
	calc_average_amount_this_item_per_pool = function(
			min_stacksize, max_stacksize,
			min_pool_rolls, max_pool_rolls,
			item_weight, pool_total_item_weight )

		local avg_stacksize = ( min_stacksize + max_stacksize ) / 2
		local avg_rolls = ( min_pool_rolls + max_pool_rolls ) / 2

		return avg_stacksize * avg_rolls * item_weight / pool_total_item_weight

	end,

	calc_chance_any_of_this_item_per_pool = function(
			min_pool_rolls, max_pool_rolls,
			item_weight, pool_total_item_weight )

		local inverse_result = 0 -- 1 - inverse_result = return value
		local inverse_item_weight = pool_total_item_weight - item_weight

		-- will be used for the division in the for loop to avoid the slightly
		-- less performant math.pow(). The divisor already includes the probability
		-- of picking any specific number of rolls.
		local cur_dividend = pool_total_item_weight
		local cur_divisor = pool_total_item_weight * (max_pool_rolls - min_pool_rolls + 1)
		for i = 0, max_pool_rolls do
			if i >= min_pool_rolls then
				inverse_result = inverse_result + cur_dividend / cur_divisor
			end
			cur_dividend = cur_dividend * inverse_item_weight -- simulate pow
			cur_divisor = cur_divisor * pool_total_item_weight -- simulate pow
		end

		return 1 - inverse_result
	end,

	get_tables_and_items = function(data)
        local items = {}
        local loottables = {}

        for table_id, details in pairs(java_data) do
            loottables[table_id] = {
                category = details.category,
                sub_category = details.sub_category,
                type = details.type,
                pools = {},
            }
            for pool_i, pool in ipairs(details["loot_table"]["pools"]) do
                loottables[table_id].pools[pool_i] = {
                    min_rolls = 1,
                    max_rolls = 1,
                    total_weight = 0
                }
                if type(pool.rolls) == "number" then
                    loottables[table_id].pools[pool_i].min_rolls = pool.rolls
                    loottables[table_id].pools[pool_i].max_rolls = pool.rolls
                else
                    loottables[table_id].pools[pool_i].min_rolls = pool.rolls.min
                    loottables[table_id].pools[pool_i].max_rolls = pool.rolls.max
                end

                -- TODO parse conditions

                for i, entry in ipairs(pool["entries"]) do
                    if entry["type"] == "minecraft:item" then
                        local item = {
                            id = entry["name"],
                            min_stacksize = 1,
                            max_stacksize = 1,
                            item_weight = entry["weight"] or 1,
                            pool = pool_i,
                            loot_table = table_id,
                        }
                        if entry["functions"] then
                            for i, func in ipairs(entry["functions"]) do
                                if func["function"] == "minecraft:set_count" then
                                    item.min_stacksize = func["count"]["min"] or 1
                                    item.max_stacksize = func["count"]["max"] or 1
                                    -- TODO parse other functions
                                end
                            end
                        end

                        loottables[table_id].pools[pool_i].total_weight = loottables[table_id].pools[pool_i].total_weight + item.item_weight

                        if items[item.id] then
                            table.insert(items[item.id], item)
                        else
                            items[item.id] = {item}
                        end
                    end
                end
            end
        end
        return items, loottables
    end,

	current_frame = nil
}

p.loot_tables = function( ... )
    local items, loottables = p.get_tables_and_items(java_data)
    local output = '{| class="wikitable sortable"\n|-\n! Item !! Pool !! Chance per pool !! Average amount per pool'
    for item_id, item_data in pairs(items) do
        for _, item in ipairs(item_data) do
            local chance = p.calc_chance_any_of_this_item_per_pool(
                loottables[item.loot_table].pools[item.pool].min_rolls,
                loottables[item.loot_table].pools[item.pool].max_rolls,
                item.item_weight,
                loottables[item.loot_table].pools[item.pool].total_weight
            )
            local avg_amount = p.calc_average_amount_this_item_per_pool(
                item.min_stacksize,
                item.max_stacksize,
                loottables[item.loot_table].pools[item.pool].min_rolls,
                loottables[item.loot_table].pools[item.pool].max_rolls,
                item.item_weight,
                loottables[item.loot_table].pools[item.pool].total_weight
            )
            output = output .. string.format('\n|-\n| %s || %d || %f || %f', item_id, item.pool, chance, avg_amount)
        end
    end
    output = output .. '\n|}'
    return output
end

return p