Advent of Ascension Wiki

An alpha for 3.6 has been released. Download it here.

READ MORE

Advent of Ascension Wiki
Advertisement

This module implements Template:LootTable.


local p = {}

-- Background colours of pool blocks in template
local poolColours = {
    "#EEEEEE",
    "#FEEFCD",
    "#E8F3D8",
    "#EAE9E1",
    "#F9E6FF",
    "#F6FFCC",
    "#E6FFEE",
    "#FFE6E6",
    "#E6FCFF",
    "#F2F2F2"
}

-- Default table titles by loot table type for easy lookup
local tableTypeTitles = {
    advancement_entity = "Rewards",
    advancement_reward = "Rewards",
    command = "Rewards",
    barter = "Barter results",
    block = "Block drops",
    chest = "Chest loot",
    empty = "No loot",
    entity = "Entity loot",
    fishing = "Fishing loot",
    generic = "Unique drops",
    gift = "Gifts",
    hauling = "Hauling rewards",    
    selector = "Unique drops"
}

-- Parse loot entries from text to in-memory data.
local function parseLootEntries(args)
    local pools = {} -- Container for nested tables which represent each pool
    local poolWeights = {} -- Container for each pool's loot entry weights for calculation

    -- Default values for optional additional columns
    pools.hasQuantity = false
    pools.hasLooting = false
    pools.hasNotes = false

    -- Loop over pools to parse data, caps at 10 pools to prevent malicious wiki editing
    for poolIndex = 1, 10 do
        local poolData = args["pool" .. poolIndex]

        -- End pool parsing when reaching non-existent pool
        if not poolData then
            break
        else
            local pool = {}
            poolWeights[poolIndex] = 0

            -- Prep base pool data and variables
            pool.entries = {}
            pool.poolNumber = poolIndex
            pool.rolls = args["rolls" .. poolIndex]
            pool.bonusRolls = args["bonusrolls" .. poolIndex]
            pool.notes = args["notes" .. poolIndex]

            -- Append newline indicator to poolData so the following loop can identify lines properly
            poolData = poolData .. "\n"

            -- Loop over pool data line by line, delineating by newline markers
            for line in string.gmatch(poolData, "[^\r\n]+[\r\n]") do
                local entry = {}

                -- Split entry lines into data groups of approximate format key:value
                for key, value in string.gmatch(line, "([%a]+)%s*:%s*(.-)%s*[;\r\n]") do
                    if value ~= "" then
                        entry[key:lower()] = value
                    end
                end

                -- Ensure a weight value is present for calculation purposes in numeric format
                -- Update table-wide variables based on current entry
                entry.weight = tonumber(entry.weight or 1) or 1
                poolWeights[poolIndex] = poolWeights[poolIndex] + entry.weight
                pools.hasLooting = pools.hasLooting or entry.looting ~= nil
                pools.hasNotes = pools.hasNotes or entry.notes ~= nil
				
            	table.insert(pool.entries, entry)
            end

            table.insert(pools, pool)
        end
    end

    return pools, poolWeights
end

-- Format link and image
local function formatLink(entry, entryItem)
	-- Handle manually specified link text or image
	local linkText = entry.linktext or entryItem
    local image = entry.image or (entryItem .. ".png")
    local imageLink = ""
	
    -- Process image link with formatting
    if image:lower() ~= "none" then
        imageLink = "[[File:" .. image .. "|" .. (entry.imagesize or "32px") .. "|link=" .. entryItem .. "]]"
    end
	
    -- Compile item link with processed image link
    return imageLink .. "[[" .. entryItem .. "|" .. linkText .."]]"
end

-- Generate body of table
local function generateLootLines(pools, poolWeights, columnCount)
    local output = {}

    -- Loop over pools in pool collection for individual formatting
    for _, pool in ipairs(pools) do
        local backgroundColour = poolColours[pool.poolNumber] or ""

        if backgroundColour ~= "" then
            backgroundColour = "background-color:" .. backgroundColour .. ";"
        end

        -- Loop over entries in current pool for individual formatting
        for _, entry in ipairs(pool.entries) do
            -- Prep base variables for entry
            local entryItem = entry.item or entry.entity
            local entryLink = "Nothing"
            local quantity = ""
            local looting = ""
            local notes = ""
            local chance = " || 100%"

            -- Skip image/link formatting for empty entries
            if entryItem ~= nil and string.lower(entryItem) ~= "nothing" then
            	entryLink = formatLink(entry, entryItem)
            end
			
			if hasQuantity then
				quantity = " || " .. (entry.quantity or "-")
			end
			
            -- Format looting column value if applicable
            if entry.looting then
                looting = " || +" .. entry.looting .. " per level"
            elseif pools.hasLooting then
                looting = " || -"
            end

            -- Format chance column value if applicable, calculated by entry's weight over the total pool weight
            if entry.weight and poolWeights[pool.poolNumber] then
                chance = " || " .. string.format("%.1f", (entry.weight / poolWeights[pool.poolNumber]) * 100) .. "%"
            end

            -- Add notes if applicable or empty column value if required
            if entry.notes then
                notes = " || style=\"text-align:left;\" | " .. entry.notes
            elseif pools.hasNotes then
                notes = " || "
            end

            table.insert(output, "|- style=\"text-align:center;" .. backgroundColour .. "\"") -- Add colour to entry row
            table.insert(output, "| " .. entryLink .. quantity .. looting .. chance .. notes) -- Insert formatted data for entry
        end
        
        if pool.rolls or pool.notes then
	        -- Add background colour to pool description if applicable
	        if backgroundColour ~= "" then
	            table.insert(output, "|- style=\"" .. backgroundColour .. "\"")
	        else
	            table.insert(output, "|-")
	        end
	        
			local poolDescription = "| colspan=" .. columnCount .. " | " -- Begin bottom row text
			
			if pool.rolls then
		        -- Begin adding pool roll count to description
		        poolDescription = poolDescription .. "The above pool is rolled "
		
		        if pool.rolls == "1" then
		            poolDescription = poolDescription .. "1 time"
		        else
		            poolDescription = poolDescription .. pool.rolls .. " times"
		        end
		        
		        -- Add bonus rolls text to pool description if applicable
		        if pool.bonusRolls then
		            poolDescription = poolDescription .. ", with an additional "
		
		            if pool.bonusRolls == "1" then
		                poolDescription = poolDescription .. "roll per point of [[mcw:Luck|Luck]]"
		            else
		                poolDescription = poolDescription .. pool.bonusRolls .. " rolls per point of [[mcw:Luck|Luck]]"
		            end
		        end
	        end
	
	        -- Add notes if present in pool data
	        if pool.notes then
	        	if pool.rolls then
	            	poolDescription = poolDescription .. "<br/>" .. pool.notes
	        	else
	        		poolDescription = poolDescription .. pool.notes
	    		end
	        end
	
	        table.insert(output, poolDescription)
        end
    end

    return table.concat(output, "\n")
end

function p.lootTable(frame)
    local data = frame

    -- Process arguments from template block on page
    if frame == mw.getCurrentFrame() then
        data = require("Module:ProcessArgs").merge(true)
    else
        frame = mw.getCurrentFrame()
    end

    -- Prep base data for final printout
    local columnCount = 2
    local linesOut = {} -- Container for per-line text for output
    local pools, poolWeights = parseLootEntries(data)
    local title = ""
    local columnHeader = "|-\n" -- Start column header line

    -- Set default value to table type titles. This acts as a safety if we somehow have an invalid table type
    setmetatable(tableTypeTitles, {__index = function() return "Unique drops" end})

    -- Set table title based on specified title in template or default for loot table type
    title = data.title or tableTypeTitles[data.type]
	
	-- Set first column name and add quantity column if required
	if data.type == "hauling" then
		hasQuantity = false
		columnHeader = columnHeader .. "! Catch"
	else
		hasQuantity = true
		columnHeader = columnHeader .. "! Item !! Quantity"
		columnCount = columnCount + 1
	end
	
    -- Add looting column if required
    if pools.hasLooting then
        columnHeader = columnHeader .. " !! " .. frame:expandTemplate{title = "tooltip", args = {"Looting", "The amount of extra items obtainable with the Looting enchantment."}}
        columnCount = columnCount + 1
    end

    -- Insert chance column
    columnHeader = columnHeader .. " !! Chance"

    -- Add notes column if required
    if pools.hasNotes then
        columnHeader = columnHeader .. " !! Notes"
        columnCount = columnCount + 1
    end

    -- Append newline and separator after columns header line compiled
    columnHeader = columnHeader .. "\n|-"

    -- Compile final output
    table.insert(linesOut, "{| class = \"wikitable loottable\"\n|-")
    table.insert(linesOut, "! colspan = " .. columnCount .. " | " .. title)
    table.insert(linesOut, columnHeader)
    table.insert(linesOut, generateLootLines(pools, poolWeights, columnCount))
    
    if data.notes then
    	table.insert(linesOut, "|- style=\"background-color:" .. poolColours[1] .. ";\"")
    	table.insert(linesOut, "| colspan=" .. columnCount .. " | " .. data.notes)
	end
    
    table.insert(linesOut, "|}")

    return table.concat(linesOut, "\n")
end

return p
Advertisement