Module:ResourceListCSVParser
From Dune Awakening DB
Documentation for this module may be created at Module:ResourceListCSVParser/doc
-- Module:ResourceCSVParser
-- A Lua module that parses a MediaWiki table rather than CSV/TSV.
-- We assume each cell is on its own line, no nested tables, no multiline cells.
-- The user-provided table's lines define header vs. row data.
local p = {}
----------------------------------------------------------------------
-- 1) Helper function to load/parse the wiki table page and filter out magic-word lines
-- Then parse table markup: headers come before the first "|-" line.
-- Each subsequent "|-" signals a new row.
-- Cells are lines starting with '|'.
----------------------------------------------------------------------
local function loadResourceWikiTable()
local title = "Page:ResourceData.csv" -- Actually uses wiki table format, rename if needed
local content = mw.title.new(title):getContent()
if not content then
return nil, "Error: Wiki table page not found!"
end
local lines = mw.text.split(content, "\n")
-- Filter out magic words or blank lines
local filtered = {}
for _, line in ipairs(lines) do
line = mw.text.trim(line)
if line ~= "" and not line:match("^__") then
table.insert(filtered, line)
end
end
if #filtered < 2 then
return nil, "Error: No valid wiki table content found!"
end
local inTable = false
local headers = {} -- store the table headers
local colMap = {}
local data = {} -- array of row arrays
local currentRow = {}
local readingHeader = true -- before the first row separator ("|-"), treat cells as headers
for _, line in ipairs(filtered) do
if line:match("^{|") then
-- Start of table (line starts with "{|")
inTable = true
elseif line:match("^|%-$") then
-- Row separator ("|-")
if not readingHeader and #currentRow > 0 then
table.insert(data, currentRow)
end
currentRow = {}
readingHeader = false -- after the first row separator, subsequent cells are row cells
elseif line:match("^|%+") then
-- Table caption line ("|+"), skip it
elseif line:match("^|%}") then
-- End of table ("|}")
if not readingHeader and #currentRow > 0 then
table.insert(data, currentRow)
end
inTable = false
elseif inTable then
-- Process table cell lines (lines that start with "|")
if line:match("^|") then
-- Remove one or more leading '|' characters
local cell = line:gsub("^|+", "")
cell = mw.text.trim(cell)
if readingHeader then
table.insert(headers, cell)
else
table.insert(currentRow, cell)
end
end
end
end
-- Build colMap from headers (map header name to its column index)
for i, h in ipairs(headers) do
colMap[mw.text.trim(h)] = i
end
return { rows = data, colMap = colMap }, nil
end
----------------------------------------------------------------------
-- 2) Alphabetical bullet list (by Name)
----------------------------------------------------------------------
function p.alphabeticalList(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
-- We need at least the "Name" column
local nameCol = colMap["Name"]
if not nameCol then
return "Error: Wiki table missing 'Name' column."
end
-- Optional filter by name or class
local classCol = colMap["Resource Class"]
local filterName = mw.text.trim(frame.args.name or "")
local filterClass = mw.text.trim(frame.args.class or "")
local resourcesByLetter = {}
for b = string.byte("A"), string.byte("Z") do
resourcesByLetter[string.char(b)] = {}
end
local otherBucket = {}
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[nameCol] or "")
local rClass = ""
if classCol then
rClass = mw.text.trim(fields[classCol] or "")
end
if (filterName == "" or rName == filterName)
and (filterClass == "" or rClass == filterClass)
then
local letter = mw.ustring.upper(mw.ustring.sub(rName, 1, 1))
if resourcesByLetter[letter] then
table.insert(resourcesByLetter[letter], rName)
else
table.insert(otherBucket, rName)
end
end
end
local output = ""
for b = string.byte("A"), string.byte("Z") do
local letter = string.char(b)
output = output .. "* <big>" .. letter .. "</big>\n"
for _, resourceName in ipairs(resourcesByLetter[letter]) do
output = output .. "** [[" .. resourceName .. "]]\n"
end
end
if #otherBucket > 0 then
output = output .. "* <big>Other</big>\n"
for _, resourceName in ipairs(otherBucket) do
output = output .. "** [[" .. resourceName .. "]]\n"
end
end
return output
end
----------------------------------------------------------------------
-- A small helper that finds [[Resource Name]] patterns and inserts an icon
-- following the naming pattern "Resource_Name_-_Icon.png" (20px, frameless).
----------------------------------------------------------------------
local function addNameIcon(text)
local function rep(resource)
local fileName = resource:gsub("%s+", "_") .. "_-_Icon.png"
local fileTitle = mw.title.makeTitle("File", fileName)
if fileTitle.exists then
-- File actually exists, so we display the icon plus link
return string.format(
"[[File:%s|20px|frameless|link=%s]] [[%s]]",
fileName, resource, resource
)
else
-- If file doesn't exist, just return the plain link
return string.format("[[%s]]", resource)
end
end
-- Use gsub to replace all [[Resource]] with icon+link
return (text:gsub("%[%[([^%]]+)%]%]", rep))
end
----------------------------------------------------------------------
-- 3) A wikitable of resources, optional filtering by name/class
----------------------------------------------------------------------
function p.resourcesTable(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local rarityCol = colMap["Rarity"]
local weightCol = colMap["Weight"]
local xpCol = colMap["Xp Per Harvest"]
if not (classCol and nameCol and rarityCol and weightCol and xpCol) then
return "Error: Missing one of (Resource Class, Name, Rarity, Weight, Xp Per Harvest)."
end
local filterName = mw.text.trim(frame.args.name or "")
local filterClass = mw.text.trim(frame.args.class or "")
local output = '{| class="wikitable sortable"\n'
output = output .. "|-\n"
output = output .. "! Resource Class !! Name !! Rarity !! Weight !! XP/Harvest\n"
for _, fields in ipairs(rows) do
local rClass = mw.text.trim(fields[classCol] or "")
local rName = mw.text.trim(fields[nameCol] or "")
local rRarity = fields[rarityCol] or ""
local rWeight = fields[weightCol] or ""
local rXp = fields[xpCol] or ""
if (filterName == "" or rName == filterName)
and (filterClass == "" or rClass == filterClass)
then
-- Insert the icon in the Name cell
local nameWithIcon = addNameIcon("[["..rName.."]]")
output = output .. "|-\n"
output = output .. string.format("| %s || %s || %s || %s || %s\n",
rClass, nameWithIcon, rRarity, rWeight, rXp
)
end
end
output = output .. "|}"
return output
end
----------------------------------------------------------------------
-- 4) resourceDetail: Outputs the resource details for the main template.
-- - Displays the full image (if provided) using 400px with center alignment.
-- - Splits the "How to Obtain" field (delimited by semicolons) into a bullet list.
-- - Splits the "Locations to Harvest" field (delimited by semicolons) into a bullet list.
----------------------------------------------------------------------
function p.resourceDetail(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local filterName = mw.text.trim(frame.args.name or "")
local filterClass = mw.text.trim(frame.args.class or "")
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local rarityCol = colMap["Rarity"]
local descCol = colMap["Description"]
local weightCol = colMap["Weight"]
local xpCol = colMap["Xp Per Harvest"]
local howToCol = colMap["How to Obtain"]
local locCol = colMap["Locations to Harvest"]
local notesCol = colMap["Notes"]
local guideCol = colMap["Guide Link"]
local imgCol = colMap["Image Link"]
local cat1Col = colMap["Category 1"]
local cat2Col = colMap["Category 2"]
local catOtherCol = colMap["Other Categories"]
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[nameCol] or "")
local rClass = mw.text.trim(fields[classCol] or "")
if (filterName == "" or rName == filterName) and (filterClass == "" or rClass == filterClass) then
local rRarity = fields[rarityCol] or ""
local rDesc = fields[descCol] or ""
local rWeight = fields[weightCol] or ""
local rXp = fields[xpCol] or ""
local rHowTo = fields[howToCol] or ""
local rLoc = fields[locCol] or ""
local rNotes = fields[notesCol] or ""
local rGuide = fields[guideCol] or ""
local rImg = fields[imgCol] or ""
local rCat1 = cat1Col and (fields[cat1Col] or "") or ""
local rCat2 = cat2Col and (fields[cat2Col] or "") or ""
local rCatOther = catOtherCol and (fields[catOtherCol] or "") or ""
local output = ""
-- Display the full image if provided.
if rImg ~= "" then
-- Changed image dimensions from "400px" to "480x320" and ensured center alignment.
output = output .. string.format("[[File:%s|480x320|center]]\n\n", rImg)
end
output = output .. "; Class\n" .. rClass .. "\n\n"
output = output .. "; Rarity\n" .. rRarity .. "\n\n"
output = output .. "; Description\n" .. rDesc .. "\n\n"
output = output .. "; Weight\n" .. rWeight .. "\n\n"
output = output .. "; XP per Harvest\n" .. rXp .. "\n\n"
-- Process "How to Obtain": now output as inline bullets.
if rHowTo ~= "" then
local howToList = mw.text.split(rHowTo, ";")
output = output .. "; How to Obtain\n"
for _, method in ipairs(howToList) do
method = mw.text.trim(method)
if method ~= "" then
-- Instead of a newline bullet list, output inline using the bullet icon.
output = output .. "• " .. method .. " " -- two spaces after each bullet for separation
end
end
output = output .. "\n\n"
else
output = output .. "; How to Obtain\nComing Soon\n\n"
end
-- Process "Locations to Harvest": still output as a bullet list.
if rLoc ~= "" then
local locList = mw.text.split(rLoc, ";")
output = output .. "; Locations\n"
for _, location in ipairs(locList) do
location = mw.text.trim(location)
if location ~= "" then
output = output .. "* " .. location .. "\n"
end
end
output = output .. "\n"
else
output = output .. "; Locations\nComing Soon\n\n"
end
output = output .. "; Notes\n" .. rNotes .. "\n\n"
if rGuide ~= "" then
output = output .. "; Guide\n" .. rGuide .. "\n\n"
else
output = output .. "; Guide\nComing Soon\n\n"
end
output = output .. rCat1 .. rCat2 .. rCatOther
return output
end
end
return "Error: No resource matched name='" .. filterName .. "' class='" .. filterClass .. "'"
end
--------------------------------------------------------------------
-- resourceLocationsInColumns:
-- Splits "Locations to Harvest" into columns of 3 bullet points each,
-- using a single-row table with class="nostyle-table".
--------------------------------------------------------------------
function p.resourceLocationsInColumns(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local resourceName = mw.text.trim(frame.args.name or mw.title.getCurrentTitle().text)
local nameCol = colMap["Name"]
if not nameCol then
return "Error: Missing 'Name' column!"
end
-- Find row by name
local rowFields
for _, fields in ipairs(rows) do
if mw.text.trim(fields[nameCol] or "") == resourceName then
rowFields = fields
break
end
end
if not rowFields then
return "Error: Resource '" .. resourceName .. "' not found."
end
local locCol = colMap["Locations to Harvest"]
if not locCol then
return "Error: Missing 'Locations to Harvest' column!"
end
local rLoc = rowFields[locCol] or ""
local output = "=Where to Obtain=\n"
if rLoc == "" then
output = output .. "Coming Soon"
return output
end
-- Split on semicolon, trim
local allItems = {}
for _, item in ipairs(mw.text.split(rLoc, ";")) do
item = mw.text.trim(item)
if item ~= "" then
table.insert(allItems, item)
end
end
if #allItems == 0 then
output = output .. "Coming Soon"
return output
end
-- Break into chunks of 3
local function chunkList(items, chunkSize)
local results = {}
for i = 1, #items, chunkSize do
local sub = {}
for j = i, math.min(i + chunkSize - 1, #items) do
table.insert(sub, items[j])
end
table.insert(results, sub)
end
return results
end
local chunks = chunkList(allItems, 3)
-- Build single-row table with class="nostyle-table"
output = output .. "{| class=\"nostyle-table\"\n|-\n"
for _, chunk in ipairs(chunks) do
output = output .. "| <ul>\n"
for _, locItem in ipairs(chunk) do
output = output .. string.format(" <li>%s</li>\n", locItem)
end
output = output .. "</ul>\n"
end
output = output .. "|}"
return output
end
--------------------------------------------------------------------
-- resourceHowToInColumns:
-- Same approach for 'How to Obtain', with a single-row .nostyle-table
--------------------------------------------------------------------
function p.resourceHowToInColumns(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local resourceName = mw.text.trim(frame.args.name or mw.title.getCurrentTitle().text)
local nameCol = colMap["Name"]
if not nameCol then
return "Error: Missing 'Name' column!"
end
-- Find row
local rowFields
for _, fields in ipairs(rows) do
if mw.text.trim(fields[nameCol] or "") == resourceName then
rowFields = fields
break
end
end
if not rowFields then
return "Error: Resource '" .. resourceName .. "' not found."
end
local howCol = colMap["How to Obtain"]
if not howCol then
return "Error: Missing 'How to Obtain' column!"
end
local rHow = rowFields[howCol] or ""
local output = "=How to Obtain=\n"
if rHow == "" then
output = output .. "Coming Soon"
return output
end
local allItems = {}
for _, item in ipairs(mw.text.split(rHow, ";")) do
item = mw.text.trim(item)
if item ~= "" then
table.insert(allItems, item)
end
end
if #allItems == 0 then
output = output .. "Coming Soon"
return output
end
local function chunkList(items, chunkSize)
local results = {}
for i = 1, #items, chunkSize do
local sub = {}
for j = i, math.min(i + chunkSize - 1, #items) do
table.insert(sub, items[j])
end
table.insert(results, sub)
end
return results
end
local chunks = chunkList(allItems, 3)
output = output .. "{| class=\"nostyle-table\"\n|-\n"
for _, chunk in ipairs(chunks) do
output = output .. "| <ul>\n"
for _, method in ipairs(chunk) do
output = output .. string.format(" <li>%s</li>\n", method)
end
output = output .. "</ul>\n"
end
output = output .. "|}"
return output
end
----------------------------------------------------------------------
-- 5) Build a ResourcePage template call from wiki table data
----------------------------------------------------------------------
function p.getResourceData(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local rows = wikiData.rows
local colMap = wikiData.colMap
local resourceName = frame.args.name or ""
if resourceName == "" then
local currentTitle = mw.title.getCurrentTitle()
resourceName = currentTitle.text
end
for _, fields in ipairs(rows) do
if (fields[colMap["Name"]] or "") == resourceName then
local rDesc = fields[colMap["Description"]] or ""
local rClass = fields[colMap["Resource Class"]] or ""
local rWeight = fields[colMap["Weight"]] or ""
local rXp = fields[colMap["Xp Per Harvest"]] or ""
local rHowTo = fields[colMap["How to Obtain"]] or ""
local rRarity = fields[colMap["Rarity"]] or ""
local rImg = fields[colMap["Image Link"]] or ""
local rNotes = fields[colMap["Notes"]] or ""
local rCat1 = fields[colMap["Category 1"]] or ""
local rCat2 = fields[colMap["Category 2"]] or ""
local rCatO = fields[colMap["Other Categories"]] or ""
local templateCall = string.format(
"{{ResourcePage" ..
"|name=%s" ..
"|description=%s" ..
"|resource_class=%s" ..
"|weight=%s" ..
"|xp=%s" ..
"|howto=%s" ..
"|rarity=%s" ..
"|image_link=%s" ..
"|notes=%s" ..
"|cat1=%s" ..
"|cat2=%s" ..
"|othercat=%s" ..
"}}",
resourceName,
rDesc,
rClass,
rWeight,
rXp,
rHowTo,
rRarity,
rImg,
rNotes,
rCat1,
rCat2,
rCatO
)
return frame:preprocess(templateCall)
end
end
return "Error: Resource '" .. resourceName .. "' not found in wiki table."
end
----------------------------------------------------------------------
-- 6) resourcesTableByResourceClass
----------------------------------------------------------------------
function p.resourcesTableByResourceClass(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local rows = wikiData.rows
local colMap = wikiData.colMap
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local rarityCol = colMap["Rarity"]
local weightCol = colMap["Weight"]
local xpCol = colMap["Xp Per Harvest"]
if not (classCol and nameCol and rarityCol and weightCol and xpCol) then
return "Error: Missing one of (Resource Class, Name, Rarity, Weight, Xp Per Harvest)."
end
local resourceName = mw.text.trim(frame.args.name or "")
if resourceName == "" then
resourceName = mw.title.getCurrentTitle().text
end
local foundClass = nil
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[nameCol] or "")
if rName == resourceName then
foundClass = mw.text.trim(fields[classCol] or "")
break
end
end
if not foundClass or foundClass == "" then
return string.format("Error: Resource “%s” not found or has no class in wiki table.", resourceName)
end
local output = '{| class="wikitable sortable"\n'
output = output .. "|-\n"
output = output .. "! Resource Class !! Name !! Rarity !! Weight !! XP/Harvest\n"
for _, fields in ipairs(rows) do
local rClass = mw.text.trim(fields[classCol] or "")
local rName = mw.text.trim(fields[nameCol] or "")
local rRarity = fields[rarityCol] or ""
local rWeight = fields[weightCol] or ""
local rXp = fields[xpCol] or ""
if rClass == foundClass then
output = output .. "|-\n"
output = output .. string.format("| %s || [[%s]] || %s || %s || %s\n",
rClass, rName, rRarity, rWeight, rXp
)
end
end
output = output .. "|}"
return output
end
----------------------------------------------------------------------
-- resourcesTableByResourceClassTitle
-- filtering by the category
----------------------------------------------------------------------
function p.resourcesTableByResourceClassTitle(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local rows = wikiData.rows
local colMap = wikiData.colMap
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local rarityCol = colMap["Rarity"]
local weightCol = colMap["Weight"]
local xpCol = colMap["Xp Per Harvest"]
if not (classCol and nameCol and rarityCol and weightCol and xpCol) then
return "Error: Missing one of (Resource Class, Name, Rarity, Weight, Xp Per Harvest)."
end
-- Use the passed parameter 'name' if provided; otherwise, use the current page title.
local resourceClass = mw.text.trim(frame.args.name or mw.title.getCurrentTitle().text)
if resourceClass == "" then
return "Error: No resource class provided."
end
local output = '{| class="wikitable sortable"\n'
output = output .. "|-\n"
output = output .. "! Resource Class !! Name !! Rarity !! Weight !! XP/Harvest\n"
local foundAny = false
for _, fields in ipairs(rows) do
local rClass = mw.text.trim(fields[classCol] or "")
local rName = mw.text.trim(fields[nameCol] or "")
local rRarity = fields[rarityCol] or ""
local rWeight = fields[weightCol] or ""
local rXp = fields[xpCol] or ""
if rClass == resourceClass then
foundAny = true
local nameWithIcon = addNameIcon("[["..rName.."]]")
output = output .. "|-\n"
output = output .. string.format("| %s || %s || %s || %s || %s\n",
rClass, nameWithIcon, rRarity, rWeight, rXp
)
end
end
output = output .. "|}"
if not foundAny then
return string.format("Error: No resources found for resource class '%s'.", resourceClass)
end
return output
end
----------------------------------------------------------------------
-- 7) resourcesTableByPageTitle
----------------------------------------------------------------------
function p.resourcesTableByPageTitle(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local rarityCol = colMap["Rarity"]
local weightCol = colMap["Weight"]
local xpCol = colMap["Xp Per Harvest"]
if not (classCol and nameCol and rarityCol and weightCol and xpCol) then
return "Error: Missing one of (Resource Class, Name, Rarity, Weight, Xp Per Harvest)."
end
local filterClass = mw.text.trim(frame.args.class or "")
if filterClass == "" then
filterClass = mw.title.getCurrentTitle().text
end
local output = '{| class="wikitable sortable"\n'
output = output .. "|-\n"
output = output .. "! Resource Class !! Name !! Rarity !! Weight !! XP/Harvest\n"
for _, fields in ipairs(rows) do
local rClass = mw.text.trim(fields[classCol] or "")
local rName = mw.text.trim(fields[nameCol] or "")
local rRarity = fields[rarityCol] or ""
local rWeight = fields[weightCol] or ""
local rXp = fields[xpCol] or ""
if rClass == filterClass then
output = output .. "|-\n"
output = output .. string.format("| %s || [[%s]] || %s || %s || %s\n",
rClass, rName, rRarity, rWeight, rXp
)
end
end
output = output .. "|}"
return output
end
----------------------------------------------------------------------
-- 8) Refiner Data: three new functions
-- We do a second table from Page:RefinerData.csv
----------------------------------------------------------------------
local function loadRefinerWikiTable()
local title = "Page:RefinerData.csv" -- Updated to match your refiner data page name
local content = mw.title.new(title):getContent()
if not content then
return nil, "Error: Refiner wiki table page not found!"
end
local lines = mw.text.split(content, "\n")
local filtered = {}
for _, line in ipairs(lines) do
line = mw.text.trim(line)
if line ~= "" and not line:match("^__") then
table.insert(filtered, line)
end
end
if #filtered < 2 then
return nil, "Error: No valid refiner wiki table content found!"
end
local inTable = false
local headers = {}
local colMap = {}
local data = {}
local currentRow = {}
local readingHeader = true
for _, line in ipairs(filtered) do
if line:match("^%{%|") then
-- Matches the start of table "{|"
inTable = true
elseif line:match("^|%-$") then
-- Row separator ("|-")
if not readingHeader and #currentRow > 0 then
table.insert(data, currentRow)
end
currentRow = {}
readingHeader = false
elseif line:match("^|%+") then
-- Table caption line ("|+"), skip it
elseif line:match("^|%}") then
-- End of table ("|}")
if not readingHeader and #currentRow > 0 then
table.insert(data, currentRow)
end
inTable = false
elseif inTable and line:match("^|") then
local cell = line:gsub("^|+", "") -- remove leading '|' characters
cell = mw.text.trim(cell)
if readingHeader then
table.insert(headers, cell)
else
table.insert(currentRow, cell)
end
end
end
for i, h in ipairs(headers) do
colMap[mw.text.trim(h)] = i
end
return { rows = data, colMap = colMap }, nil
end
--------------------------------------------------------------------
-- refinerDetail: single row detail from Page:RefinerData.csv
--------------------------------------------------------------------
function p.refinerDetail(frame)
local wikiData, err = loadRefinerWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local resourceName = frame.args.name or ""
if resourceName == "" then
resourceName = mw.title.getCurrentTitle().text
end
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local timeCol = colMap["Time to Smelt"]
local ingrCol = colMap["Ingredients to Smelt"]
local recipeCol = colMap["Recipe to Smelt"]
local refinCol = colMap["Refiner Needed"]
if not (classCol and nameCol and timeCol and ingrCol and recipeCol and refinCol) then
return "Error: Wiki table missing one of the required Refiner columns."
end
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[nameCol] or "")
if rName == resourceName then
local rClass = fields[classCol] or ""
local rTime = fields[timeCol] or ""
local rIngr = fields[ingrCol] or ""
local rRec = fields[recipeCol] or ""
local rRefn = fields[refinCol] or ""
local output = "== Refiner Details ==\n"
output = output .. "; Resource Class\n" .. rClass .. "\n\n"
output = output .. "; Name\n" .. rName .. "\n\n"
output = output .. "; Time to Smelt\n" .. rTime .. "\n\n"
output = output .. "; Ingredients to Smelt\n" .. rIngr .. "\n\n"
output = output .. "; Recipe to Smelt\n" .. rRec .. "\n\n"
output = output .. "; Refiner Needed\n" .. rRefn .. "\n\n"
return output
end
end
return "Error: Resource '" .. resourceName .. "' not found in RefinerData."
end
--------------------------------------------------------------------
-- refinerTableByIngredients: shows rows that contain filterName in 'Ingredients to Smelt'
-- adds heading '=Refined Into=' if found, else returns empty.
-- Now, it adds icons (via naming convention) to the Name and Recipe to Smelt cells.
-- For any resource link ([[Resource Name]]), an icon is added:
-- File:<Resource_Name>_-_Icon.png (spaces become underscores)
-- In the Recipe cell, multiple ingredients (separated by ";")
-- are output on new lines.
--------------------------------------------------------------------
function p.refinerTableByIngredients(frame)
-- Load the refiner table data.
local wikiData, err = loadRefinerWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
-- Use the passed parameter 'name' as filter; if empty, use the current page title.
local filterName = frame.args.name or ""
if filterName == "" then
filterName = mw.title.getCurrentTitle().text
end
-- Get column indices for the refiner table.
local classCol = colMap["Resource Class"]
local nameCol = colMap["Name"]
local timeCol = colMap["Time to Smelt"]
local ingrCol = colMap["Ingredients to Smelt"]
local recipeCol = colMap["Recipe to Smelt"]
local refinCol = colMap["Refiner Needed"]
if not (classCol and nameCol and timeCol and ingrCol and recipeCol and refinCol) then
return "Error: Wiki table missing one of the required Refiner columns."
end
-- Helper function: For any text, find all occurrences of [[Resource Name]]
-- and replace each with an icon (using the naming convention) followed by the original link.
local function addIcons(text)
return (text:gsub("%[%[([^%]]+)%]%]", function(resource)
-- Replace one or more spaces with underscores
local resourceForFile = resource:gsub("%s+", "_")
local iconFile = resourceForFile .. "_-_Icon.png"
-- Return the icon image (20px, frameless, with a clickable link) followed by a nonbreaking space and the original link.
return string.format("[[File:%s|20px|frameless|link=%s]] [[%s]]", iconFile, resource, resource)
end))
end
local foundAny = false
local tableOutput = '{| class="wikitable sortable" style="width:100%"\n'
tableOutput = tableOutput .. "|-\n"
tableOutput = tableOutput .. "! Resource Class !! Name !! Time to Smelt !! Recipe to Smelt !! Refiner Needed\n"
for _, fields in ipairs(rows) do
local rIngr = fields[ingrCol] or ""
local rClass = fields[classCol] or ""
local rName = mw.text.trim(fields[nameCol] or "")
local rTime = fields[timeCol] or ""
local rRec = fields[recipeCol] or ""
local rRefn = fields[refinCol] or ""
-- Split the Ingredients cell on semicolon.
local ingredients = mw.text.split(rIngr, ";")
local matchFound = false
for _, ingrItem in ipairs(ingredients) do
local candidate = mw.text.trim(ingrItem)
-- If the ingredient is wrapped in [[ ]], extract the name.
local ingredientName = candidate:match("%[%[(.-)%]%]") or candidate
if ingredientName == filterName then
matchFound = true
break
end
end
if matchFound then
foundAny = true
-- Process the "Name" cell: apply addIcons.
local nameOutput = addIcons(rName)
-- Process the "Recipe to Smelt" cell:
-- Split by semicolon and process each part individually.
local recipeParts = mw.text.split(rRec, ";")
local recipeOutput = ""
for _, part in ipairs(recipeParts) do
local trimmedPart = mw.text.trim(part)
if trimmedPart ~= "" then
local partWithIcon = addIcons(trimmedPart)
if recipeOutput ~= "" then
recipeOutput = recipeOutput .. "<br/>" .. partWithIcon
else
recipeOutput = partWithIcon
end
end
end
tableOutput = tableOutput .. "|-\n"
tableOutput = tableOutput .. string.format("| %s || %s || %s || %s || %s\n",
rClass, nameOutput, rTime, recipeOutput, rRefn)
end
end
tableOutput = tableOutput .. "|}"
if foundAny then
return "=Refined Into=\n" .. tableOutput
else
return ""
end
end
--------------------------------------------------------------------
-- refinerData: example function that might build a template call, etc.
--------------------------------------------------------------------
function p.refinerData(frame)
local wikiData, err = loadRefinerWikiTable()
if not wikiData then
return err
end
local rows = wikiData.rows
local colMap = wikiData.colMap
local resourceName = frame.args.name or ""
if resourceName == "" then
local currentTitle = mw.title.getCurrentTitle()
resourceName = currentTitle.text
end
for _, fields in ipairs(rows) do
if (fields[colMap["Name"]] or "") == resourceName then
local rClass = fields[colMap["Resource Class"]] or ""
local rTime = fields[colMap["Time to Smelt"]] or ""
local rIngr = fields[colMap["Ingredients to Smelt"]] or ""
local rRec = fields[colMap["Recipe to Smelt"]] or ""
local rRefn = fields[colMap["Refiner Needed"]] or ""
local output = "== Refiner Info ==\n"
output = output .. "; Class\n" .. rClass .. "\n\n"
output = output .. "; Time\n" .. rTime .. "\n\n"
output = output .. "; Ingredients\n" .. rIngr .. "\n\n"
output = output .. "; Recipe\n" .. rRec .. "\n\n"
output = output .. "; Refiner Needed\n" .. rRefn .. "\n\n"
return output
end
end
return "Error: Resource '"..resourceName.."' not found in the refiner table."
end
--------------------------------------------------------------------
-- debugResourceDetail: testing data outputs
--------------------------------------------------------------------
function p.debugResourceDetail(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local filterName = mw.text.trim(frame.args.name or "")
local filterClass = mw.text.trim(frame.args.class or "")
local debugOutput = ""
for _, fields in ipairs(rows) do
local rClass = mw.text.trim(fields[colMap["Resource Class"]] or "")
local rName = mw.text.trim(fields[colMap["Name"]] or "")
-- Use the filters to select the appropriate row
if (filterName == "" or rName == filterName)
and (filterClass == "" or rClass == filterClass)
then
local rRarity = fields[colMap["Rarity"]] or ""
local rDesc = fields[colMap["Description"]] or ""
local rWeight = fields[colMap["Weight"]] or ""
local rXp = fields[colMap["Xp Per Harvest"]] or ""
local rHowTo = fields[colMap["How to Obtain"]] or ""
local rLoc = fields[colMap["Locations to Harvest"]] or ""
local rNotes = fields[colMap["Notes"]] or ""
local rGuide = mw.text.trim(fields[colMap["Guide Link"]] or "")
local rImg = fields[colMap["Image Link"]] or ""
local rCat1 = fields[colMap["Category 1"]] or ""
local rCat2 = fields[colMap["Category 2"]] or ""
local rCatOther = fields[colMap["Other Categories"]] or ""
debugOutput = debugOutput ..
"rClass: " .. rClass .. "\n" ..
"rName: " .. rName .. "\n" ..
"rRarity: " .. rRarity .. "\n" ..
"rDesc: " .. rDesc .. "\n" ..
"rWeight: " .. rWeight .. "\n" ..
"rXp: " .. rXp .. "\n" ..
"rHowTo: " .. rHowTo .. "\n" ..
"rLoc: " .. rLoc .. "\n" ..
"rNotes: " .. rNotes .. "\n" ..
"rGuide: " .. rGuide .. "\n" ..
"rImg: " .. rImg .. "\n" ..
"rCat1: " .. rCat1 .. "\n" ..
"rCat2: " .. rCat2 .. "\n" ..
"rCatOther: " .. rCatOther .. "\n"
return debugOutput
end
end
return "Error: No resource matched name='" .. filterName .. "' class='" .. filterClass .. "'"
end
----------------------------------------------------------------------
-- 9) resourceTableByResourceName
-- Output a wikitable for the resource filtered by the page title.
-- This function shows the icon (if provided) to the left of the resource name.
----------------------------------------------------------------------
function p.resourceTableByResourceName(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
-- Get the required column indices from the table
local nameCol = colMap["Name"]
local classCol = colMap["Resource Class"]
local rarityCol = colMap["Rarity"]
local weightCol = colMap["Weight"]
local xpCol = colMap["Xp Per Harvest"]
local iconCol = colMap["Icon Link"]
if not (nameCol and classCol and rarityCol and weightCol and xpCol) then
return "Error: Missing one of (Resource Class, Name, Rarity, Weight, Xp Per Harvest) columns."
end
-- Filter: if no parameter provided, use the current page title
local filterName = mw.text.trim(frame.args.name or "")
if filterName == "" then
filterName = mw.title.getCurrentTitle().text
end
local output = '{| class="wikitable" style="width:100%"\n'
output = output .. "! Item Class !! Item Name !! Rarity !! Weight !! XP/Harvest\n"
local found = false
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[nameCol] or "")
if rName == filterName then
found = true
local rClass = fields[classCol] or ""
local rRarity = fields[rarityCol] or ""
local rWeight = fields[weightCol] or ""
local rXp = fields[xpCol] or ""
local rIcon = fields[iconCol] or ""
-- Build the Item Name cell:
local itemNameOutput = ""
if rIcon ~= "" then
-- Use frameless to avoid the thumbnail border/background and display it inline.
itemNameOutput = string.format("[[File:%s|20px|frameless|link=%s]] '''[[%s]]'''", rIcon, rName, rName)
else
itemNameOutput = string.format("'''[[%s]]'''", rName)
end
output = output .. "|-\n"
output = output .. string.format("| [[%s]] || %s || %s || %s || %s\n",
rClass, itemNameOutput, rRarity, rWeight, rXp)
end
end
output = output .. "|}"
if not found then
return "Error: No resource matched name '" .. filterName .. "'."
end
return output
end
----------------------------------------------------------------------
-- 10) guideVideo: Returns the full <youtube>...</youtube> markup for a resource.
-- It looks up the resource by name (defaulting to the current page title)
-- and returns the Guide Link wrapped in <youtube> tags.
-- The output is processed by frame:preprocess(), and any stray </youtube>
-- is removed.
----------------------------------------------------------------------
function p.guideVideo(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
local resourceName = mw.text.trim(frame.args.name or "")
if resourceName == "" then
resourceName = mw.title.getCurrentTitle().text
end
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[colMap["Name"]] or "")
if rName == resourceName then
local rGuide = mw.text.trim(fields[colMap["Guide Link"]] or "")
if rGuide ~= "" then
-- Build the complete <youtube> markup using the guide link.
local ytMarkup = "<youtube>" .. rGuide .. "</youtube>"
-- Process the markup so that it is parsed as wikitext.
local processed = frame:preprocess(ytMarkup)
-- Remove any stray literal closing tag.
processed = processed:gsub("</youtube>", "")
return processed
else
return "Coming Soon"
end
end
end
return "Error: Resource '" .. resourceName .. "' not found in the resource table."
end
----------------------------------------------------------------------
-- 11) getGuideLink: Returns the full <youtube> markup for a resource,
-- with dimensions and alignment attributes.
-- It looks up the resource by name (defaulting to the current page title)
-- and then wraps the Guide Link (as stored in the resource table) in a
-- <youtube> tag with attributes.
----------------------------------------------------------------------
function p.getGuideLink(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
-- Use the passed parameter 'name' if provided; otherwise, use the current page title.
local resourceName = mw.text.trim(frame.args.name or mw.title.getCurrentTitle().text)
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[colMap["Name"]] or "")
if rName == resourceName then
local rGuide = mw.text.trim(fields[colMap["Guide Link"]] or "")
if rGuide ~= "" then
-- Get dimensions and alignment from parameters or use defaults.
local dims = frame.args.dimensions or "480x320"
local align = frame.args.alignment or "center"
-- Build the complete <youtube> markup.
local ytMarkup = string.format('<youtube dimensions="%s" alignment="%s">%s</youtube>', dims, align, rGuide)
-- Process the markup so that MediaWiki parses it as wikitext.
local processed = frame:preprocess(ytMarkup)
-- In case a stray closing tag appears, remove it.
processed = processed:gsub("</youtube>", "")
return processed
else
return "=Guide=\nComing Soon"
end
end
end
return "Error: Resource '" .. resourceName .. "' not found in the resource table."
end
----------------------------------------------------------------------
-- 12) breadcrumb: Builds a breadcrumb trail using Category 1, Category 2,
-- and the current resource name.
-- It extracts the plain text from the category fields (if they are in the form [[Category:...]])
-- and outputs the breadcrumb as: [[Category1]] > [[Category2]] > ResourceName
----------------------------------------------------------------------
function p.breadcrumb(frame)
local wikiData, err = loadResourceWikiTable()
if not wikiData then
return err
end
local colMap = wikiData.colMap
local rows = wikiData.rows
-- Use the passed parameter 'name' or the current page title.
local resourceName = mw.text.trim(frame.args.name or mw.title.getCurrentTitle().text)
for _, fields in ipairs(rows) do
local rName = mw.text.trim(fields[colMap["Name"]] or "")
if rName == resourceName then
-- Retrieve Category 1 and Category 2 values.
local cat1 = mw.text.trim(fields[colMap["Category 1"]] or "")
local cat2 = mw.text.trim(fields[colMap["Category 2"]] or "")
-- If the category value is wrapped in wiki markup like [[Category:Resources]],
-- extract only the inner text.
if cat1:find("%[%[Category:") then
cat1 = cat1:match("%[%[Category:%s*(.-)%s*%]%]")
end
if cat2:find("%[%[Category:") then
cat2 = cat2:match("%[%[Category:%s*(.-)%s*%]%]")
end
local breadcrumbParts = {}
if cat1 ~= "" then
table.insert(breadcrumbParts, "[[" .. cat1 .. "]]")
end
if cat2 ~= "" then
table.insert(breadcrumbParts, "[[" .. cat2 .. "]]")
end
-- Append the current resource name as bold text.
table.insert(breadcrumbParts, "'''" .. resourceName .. "'''")
return table.concat(breadcrumbParts, " > ")
end
end
return "Breadcrumb not available"
end
----------------------------------------------------------------------
-- 13) Notes and Gallery
-- Expands based on the amount of gallery images
----------------------------------------------------------------------
function p.notesAndGalleries(frame)
-- 1) Load the resource wiki table
local data, err = loadResourceWikiTable() -- or whatever your loader is named
if not data then
return err
end
local rows = data.rows
local colMap = data.colMap
-- 2) Identify the columns you need
local notesCol = colMap["Notes"]
local g1Col = colMap["Gallery 1"]
local g2Col = colMap["Gallery 2"]
local g3Col = colMap["Gallery 3"]
local g4Col = colMap["Gallery 4"]
-- If the table doesn't have these columns, fail gracefully
if not notesCol then
return "Error: Table missing 'Notes' column!"
end
-- It's okay if Gallery 1..4 might be nil; we handle that later.
-- 3) Figure out which resource row to look for
local resourceName = frame.args.name or mw.title.getCurrentTitle().text
-- 4) Find the row with name = resourceName
local nameCol = colMap["Name"]
if not nameCol then
return "Error: Table missing 'Name' column!"
end
local rowFields = nil
for _, fields in ipairs(rows) do
if mw.text.trim(fields[nameCol] or "") == resourceName then
rowFields = fields
break
end
end
if not rowFields then
return "Error: Resource '" .. resourceName .. "' not found in the table."
end
-- 5) Grab the notes and gallery fields
local notes = rowFields[notesCol] or ""
local g1 = g1Col and (rowFields[g1Col] or "") or ""
local g2 = g2Col and (rowFields[g2Col] or "") or ""
local g3 = g3Col and (rowFields[g3Col] or "") or ""
local g4 = g4Col and (rowFields[g4Col] or "") or ""
-- 6) Determine how many galleries are non-empty
local galleries = {}
for _, g in ipairs({g1, g2, g3, g4}) do
local trimmed = mw.text.trim(g)
if trimmed ~= "" then
table.insert(galleries, trimmed)
end
end
-- 7) If no galleries, just return the Notes text
if #galleries == 0 then
return notes
end
-- 8) Otherwise, build a single-row table with columns for notes + each gallery
local output = "{| class=\"wikitable\" style=\"width:100%\"\n|-\n"
-- The first cell is notes
output = output .. "| " .. notes
-- Then each gallery is a separate cell
for _, galleryFile in ipairs(galleries) do
local trimmedFile = mw.text.trim(galleryFile)
if trimmedFile ~= "" then
output = output .. " || [[File:"..trimmedFile.."|250px]]"
end
end
output = output .. "\n|}"
return output
end
return p
