mNo edit summary |
m (Added class for styling) |
||
(47 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
local p = {} |
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", |
||
+ | 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.hasNotes = false; |
||
+ | pools.hasLooting = 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.items = {} |
||
+ | pool.poolNumber = poolIndex |
||
+ | pool.rolls = args["rolls" .. poolIndex] or "1" |
||
+ | 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.items, entry) |
||
+ | end |
||
+ | |||
+ | table.insert(pools, pool) |
||
+ | end |
||
+ | end |
||
+ | |||
+ | return pools, poolWeights |
||
+ | end |
||
+ | |||
+ | 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.items) do |
||
+ | -- Prep base variables for entry |
||
+ | local itemLink = "Nothing" |
||
+ | local quantity = entry.quantity or "-" |
||
+ | local looting = "" |
||
+ | local notes = "" |
||
+ | local chance = "100%" |
||
+ | |||
+ | -- Skip empty entries |
||
+ | if entry.item ~= nil and string.lower(entry.item) ~= "nothing" then |
||
+ | local image = entry.image or (entry.item .. ".png") |
||
+ | local imageLink = "" |
||
+ | |||
+ | -- Process image link with formatting |
||
+ | if image:lower() ~= "none" then |
||
+ | imageLink = "[[File:" .. image .. "|" .. (entry.imagesize or "32px") .. "|link=" .. entry.item .. "]]" |
||
+ | end |
||
+ | |||
+ | -- Compile item link with processed image link |
||
+ | itemLink = imageLink .. "[[" .. entry.item .. "]]" |
||
+ | 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, "| " .. itemLink .. " || " .. quantity .. looting .. " || " .. chance .. notes) -- Insert formatted data for entry |
||
+ | end |
||
+ | |||
+ | local poolDescription = "| colspan=" .. columnCount .. " | " -- Begin bottom row text |
||
+ | |||
+ | -- Add background colour to pool if applicable |
||
+ | if backgroundColour ~= "" then |
||
+ | table.insert(output, "|- style=\"" .. backgroundColour .. "\"") |
||
+ | else |
||
+ | table.insert(output, "|-") |
||
+ | end |
||
+ | |||
+ | -- 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 |
||
+ | |||
+ | -- Add notes if present in pool data |
||
+ | if pool.notes then |
||
+ | poolDescription = poolDescription .. "<br/>" .. pool.notes |
||
+ | end |
||
+ | |||
+ | table.insert(output, poolDescription) |
||
+ | end |
||
+ | |||
+ | return table.concat(output, "\n") |
||
+ | end |
||
function p.lootTable(frame) |
function p.lootTable(frame) |
||
+ | local data = frame |
||
− | -- pass args into the module |
||
+ | |||
− | args = frame:getParent().args |
||
+ | -- 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 = 3 |
||
+ | local linesOut = {} -- Container for per-line text for output |
||
+ | local pools, poolWeights = parseLootEntries(data) |
||
+ | local title = "" |
||
+ | local columnHeader = "|-\n! Item !! Quantity !! " -- Start column header line with base columns |
||
+ | |||
+ | -- 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] |
||
+ | -- Add looting column if required |
||
− | local tableColors = { |
||
+ | if pools.hasLooting then |
||
− | '#EEEEEE', |
||
+ | columnHeader = columnHeader .. frame:expandTemplate{title = "tooltip", args = {"Looting", "The amount of extra items obtainable with the Looting enchantment."}} .. " !! " |
||
− | '#FCCC5D', |
||
+ | columnCount = columnCount + 1 |
||
− | '#A8D16E', |
||
+ | end |
||
− | '#918C67', |
||
− | '#EDB5FF', |
||
− | '#F2FFB5', |
||
− | '#B5FFCE', |
||
− | '#FFB5B6', |
||
− | '#B5F7FF', |
||
− | '#CCCCCC' |
||
− | } |
||
− | local output = {} -- list of strings to be concatenated into final output |
||
− | local poolWeights = {} -- stores weights for each pool separately |
||
− | local itemsTable = {} -- stores each item as a nested table |
||
− | |||
− | table.insert(output, '{| class = "wikitable"\n|-') |
||
+ | -- Insert chance column |
||
− | local title = '' |
||
+ | columnHeader = columnHeader .. "Chance" |
||
− | local centerColumn = '' |
||
− | |||
− | if (args.type == 'chest') then |
||
− | title = 'Chest loot' |
||
− | centerColumn = 'Luck' |
||
− | elseif (args.type == 'custom') then |
||
− | centerColumn = args.otherlooting |
||
− | else |
||
− | title = 'Unique drops' |
||
− | centerColumn = frame:expandTemplate{title = 'tooltip', args = {'Looting', 'The amount of extra items obtainable with the Loot enchantment.'}} |
||
− | end |
||
− | |||
− | if args.title then |
||
− | title = args.title |
||
− | end |
||
+ | -- Add notes column if required |
||
− | -- should colspan be changed to a variable instead of hardcoded number? |
||
+ | if pools.hasNotes then |
||
− | table.insert(output, '! colspan = 5 | ' .. title) |
||
+ | columnHeader = columnHeader .. " !! Notes " |
||
− | table.insert(output, '|-\n! Item !! Quantity !! ' .. centerColumn .. ' !! Chance !! Notes\n|-') |
||
+ | columnCount = columnCount + 1 |
||
+ | end |
||
+ | -- Append newline and next line starter after columns header line compiled |
||
− | |||
+ | columnHeader = columnHeader .. "\n|-" |
||
− | -- parse actual loot info; currently supports up to 10 "pool=" arguments |
||
− | for poolNumber=1, 10 do |
||
− | local poolArg = args['pool'..poolNumber] |
||
− | if poolArg then |
||
− | -- TODO |
||
− | poolArg = poolArg .. '\n' -- allow last line to be matched like the rest |
||
− | |||
− | -- parse input of pool parameter |
||
− | for line in mw.ustring.gmatch(poolArg, '[^\r\n]+[\r\n]') do -- split on newline |
||
− | --local splitLine = {} |
||
− | local currentItem = {} |
||
− | for key, value in mw.ustring.gmatch(line, '([%a]+)%s*:%s*(.-)%s*[;\r\n]') do |
||
− | if value ~= '' then |
||
− | currentItem[key:lower()] = value |
||
− | end |
||
− | end |
||
− | |||
− | if not currentItem.quantity then |
||
− | currentItem.quantity = '0' |
||
− | end |
||
− | |||
− | if not currentItem.looting then |
||
− | currentItem.looting = '-' |
||
− | end |
||
− | |||
− | local weight |
||
− | if currentItem.weight then |
||
− | weight = tonumber(currentItem.weight) |
||
− | currentItem.weight = weight |
||
− | end |
||
− | |||
− | if not currentItem.notes then |
||
− | currentItem.notes = '' |
||
− | end |
||
− | |||
− | if not poolWeights[poolNumber] then |
||
− | poolWeights[poolNumber] = weight |
||
− | else |
||
− | poolWeights[poolNumber] = poolWeights[poolNumber] + weight |
||
− | end |
||
− | |||
− | currentItem.poolNumber = poolNumber |
||
− | |||
− | table.insert(itemsTable, currentItem) |
||
− | end |
||
− | end |
||
− | end |
||
+ | -- Compile final output |
||
− | -- create output from parsed input |
||
+ | table.insert(linesOut, "{| class = \"wikitable loottable\"\n|-") |
||
− | for _, currentItem in ipairs(itemsTable) do |
||
+ | table.insert(linesOut, "! colspan = " .. columnCount .. " | " .. title) |
||
− | local itemLink = '' |
||
+ | table.insert(linesOut, columnHeader) |
||
+ | table.insert(linesOut, generateLootLines(pools, poolWeights, columnCount)) |
||
+ | table.insert(linesOut, "|}") |
||
+ | return table.concat(linesOut, "\n") |
||
− | if currentItem.item == '' or not currentItem.item or mw.ustring.lower(currentItem.item) == 'nothing' then |
||
− | itemLink = 'Nothing' |
||
− | else |
||
− | local image = currentItem.item .. '.png' |
||
− | local imagesize = '32px' |
||
− | if currentItem.image then |
||
− | image = currentItem.image |
||
− | end |
||
− | itemLink = '[[File:' .. image .. '|' .. imagesize .. ']] [[' .. currentItem.item .. ']]' |
||
− | end |
||
− | |||
− | local chance = '0%' |
||
− | -- quantity, looting and group stay as is |
||
− | if currentItem.weight then -- weight exists and is not 0 |
||
− | chance = string.format("%.1f", (currentItem.weight / poolWeights[currentItem.poolNumber]) * 100) .. '%' |
||
− | end |
||
− | |||
− | local color = tableColors[currentItem.poolNumber] |
||
− | if not color then |
||
− | color = '' |
||
− | else |
||
− | color = ' style="background-color:' .. color .. ';"' |
||
− | end |
||
− | table.insert(output, '|-' .. color) |
||
− | table.insert(output, '| ' .. itemLink .. ' || ' .. currentItem.quantity .. ' || ' |
||
− | .. currentItem.looting .. ' || ' .. chance .. ' || ' .. currentItem.notes) |
||
− | end |
||
− | -- footer |
||
− | table.insert(output, '|}') |
||
− | |||
− | return table.concat(output, '\n') |
||
end |
end |
||
− | return p |
+ | return p |
Revision as of 14:41, 22 July 2021
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",
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.hasNotes = false;
pools.hasLooting = 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.items = {}
pool.poolNumber = poolIndex
pool.rolls = args["rolls" .. poolIndex] or "1"
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.items, entry)
end
table.insert(pools, pool)
end
end
return pools, poolWeights
end
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.items) do
-- Prep base variables for entry
local itemLink = "Nothing"
local quantity = entry.quantity or "-"
local looting = ""
local notes = ""
local chance = "100%"
-- Skip empty entries
if entry.item ~= nil and string.lower(entry.item) ~= "nothing" then
local image = entry.image or (entry.item .. ".png")
local imageLink = ""
-- Process image link with formatting
if image:lower() ~= "none" then
imageLink = "[[File:" .. image .. "|" .. (entry.imagesize or "32px") .. "|link=" .. entry.item .. "]]"
end
-- Compile item link with processed image link
itemLink = imageLink .. "[[" .. entry.item .. "]]"
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, "| " .. itemLink .. " || " .. quantity .. looting .. " || " .. chance .. notes) -- Insert formatted data for entry
end
local poolDescription = "| colspan=" .. columnCount .. " | " -- Begin bottom row text
-- Add background colour to pool if applicable
if backgroundColour ~= "" then
table.insert(output, "|- style=\"" .. backgroundColour .. "\"")
else
table.insert(output, "|-")
end
-- 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
-- Add notes if present in pool data
if pool.notes then
poolDescription = poolDescription .. "<br/>" .. pool.notes
end
table.insert(output, poolDescription)
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 = 3
local linesOut = {} -- Container for per-line text for output
local pools, poolWeights = parseLootEntries(data)
local title = ""
local columnHeader = "|-\n! Item !! Quantity !! " -- Start column header line with base columns
-- 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]
-- 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 next line starter 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))
table.insert(linesOut, "|}")
return table.concat(linesOut, "\n")
end
return p