Module:Wares

local p = {} local h = {} local Data = mw.loadData("Module:Wares/Data")

local DataTable = require("Module:Data Table") local File = require("Module:File") local Franchise = require("Module:Franchise") local Term = require("Module:Term") local utilsArg = require("Module:UtilsArg") local utilsString = require("Module:UtilsString") local utilsTable = require("Module:UtilsTable") local utilsVar = require("Module:UtilsVar")

local CATEGORY_INVALID_ARGS = require("Module:Constants/category/invalidArgs") local VAR_ITEMS = "Module:Wares/items"

function h.warn(msg, ...) local utilsError = require("Module:UtilsError") msg = string.format(msg, ...) utilsError.warn(msg) end

function p.Main(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates["Wares"]) local categories = err and err.categoryText or "" if err and err.args and err.args.game then return "", categories end local items = utilsVar.get(VAR_ITEMS) utilsVar.set(VAR_ITEMS) -- clear variable for the next table local prices, categories2 = h.printWares(args, items) categories = categories..categories2 return prices, categories end

function p.Item(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates["Wares/Item"]) args.categories = err and err.categoryText or "" utilsVar.add(VAR_ITEMS, args) end

function h.printWares(args, items) local categories = ""

local hasLocations = #(utilsTable.filter(items, "location")) > 0 local itemsByLocation local locations if hasLocations then itemsByLocation = {} for i, item in ipairs(items) do			if not item.location then h.warn("Item  is missing the   parameter. If one item has a   parameter, then all items must have it.", item.item) categories = categories.."" end for i, location in ipairs(item.location or {}) do				local locationItems = itemsByLocation[location] or { location = location } table.insert(locationItems, utilsTable.merge({}, item, { location = location }))				itemsByLocation[location] = locationItems end end locations = utilsTable.toArray(itemsByLocation) utilsTable.sortBy(locations, "location") end

if hasLocations and args.locationTabs then local utilsLayout = require("Module:UtilsLayout") local tabData = {} for i, locationItems in ipairs(locations) do			local dataTable, categories2 = h.printWaresTable(args, locationItems) categories = categories..categories2 table.insert(tabData, {				label = locationItems.location,				content = dataTable			}) end local tabs = utilsLayout.tabs(tabData) return tabs, categories elseif hasLocations then items = utilsTable.concat(unpack(locations)) end local dataTable, categories2 = h.printWaresTable(args, items, itemsByLocation) categories = categories..categories2 return dataTable, categories end

local notes = {} local noteCount = 0 function h.printWaresTable(args, items, locations) local categories = "" local hasDescriptions = #(utilsTable.filter(items, "description")) > 0 local hasStock = #(utilsTable.filter(items, "stock")) > 0 local showQuantityColumn = args.showQuantityColumn local columns = {} if locations then table.insert(columns, "Location [Rowspan][Term]") end table.insert(columns, "Item") if showQuantityColumn then table.insert(columns, "Quantity") end if args.prices and #args.prices > 0 then table.insert(columns, string.format("Prices [Colspan:%d]", #args.prices)) columns = utilsTable.concat(columns, args.prices) else table.insert(columns, "Price") end if hasStock then table.insert(columns, "Stock") end if hasDescriptions then table.insert(columns, "Description [Description]") end local defaults = Data.defaults[Franchise.graphics(args.game)]

local seenLocations = {} local rows = {} for i, item in ipairs(items) do		local rowCells = {} if locations then local location = item.location if location and not seenLocations[location] then -- TODO: rowspan table.insert(rowCells, location) seenLocations[location] = true elseif location then table.insert(rowCells, location) else table.insert(rowCells, "N/A") end end local itemCell = h.itemCell(args, item) table.insert(rowCells, itemCell)

if showQuantityColumn then table.insert(rowCells, tostring(item.quantity or 1)) end

if args.prices and #args.prices > 0 then for j, priceName in ipairs(args.prices) do				local price = item.prices[j] and item.prices[j].price if price == nil then h.warn("Item  is missing the   parameter corresponding to the   price. If the item is not sold at this price, please set the parameter to  .", item.item, j, priceName) categories = categories.."" end table.insert(rowCells, h.price(args.game, price)) end else table.insert(rowCells, h.price(args.game, item.price)) if #item.prices > 0 then h.warn("Item  has an invalid parameter   parameter. Rename the parameter to   or enable multiple prices using the   parameter.", item.item) categories = categories.."" end end if hasStock then local stock = tostring(item.stock or "Unlimited") local restockInfo = h.getConfig("restocks", args, item) if restockInfo then local noteId = notes[restockInfo] if not noteId then noteCount = noteCount + 1 noteId = "Wares "..noteCount notes[restockInfo] = noteId end local restockNote = mw.getCurrentFrame:expandTemplate({					title = "Table Note",					args = {						name = noteId,						[1] = notes[restockInfo] and restockInfo					}				}) stock = stock.." "..restockNote end table.insert(rowCells, stock) end if hasDescriptions then table.insert(rowCells, item.description or "") end table.insert(rows, { cells = rowCells }) end local dataTable = DataTable.printTable(rows, {		game = args.game,		columns = columns,	}) if noteCount > 0 then dataTable = dataTable .. mw.getCurrentFrame:expandTemplate({ title = "Table Notes" }) end return dataTable, categories end

function h.itemCell(args, item) local text, image = h.itemDisplay(args, item) local html = mw.html.create("div") :addClass("zw-wares__item") if image then html:tag("div") :addClass("zw-wares__item-image") :wikitext(image) end html:tag("div") :addClass("zw-wares__item-text") :wikitext(text) return tostring(html) end function h.itemDisplay(args, item) local containsLink = string.find(item.item, "%[%[") local formatArgs if not containsLink then local singular = Term.fetchTerm(item.item, args.game) local plural = Term.fetchTerm(item.item, args.game, { 			plural = true,			allowSingular = true,		}) formatArgs = { game = args.game, singular = singular or item.item, plural = plural or item.item, quantity = tostring(item.quantity or "") }	else formatArgs = {} end

local fileFormat = h.getConfig("fileFormat", args, item) local file = item.image if not file and not containsLink and fileFormat ~= "" then file = utilsString.interpolate(fileFormat, formatArgs) end local fileSize = h.getConfig("fileSize", args, item) local fileScale = h.getConfig("fileScale", args, item) local image = file and File.image(file, {		size = fileSize,		scale = fileScale,	})

local text = item.display if not text and containsLink then text = item.item elseif not text and item.quantity and item.quantity > 1 then local quantityFormat = h.getConfig("quantityFormat", args, item) text = utilsString.interpolate(quantityFormat, formatArgs) elseif not text then text = Term.link(item.item, args.game) end return text, image end

function h.price(game, price) local html = mw.html.create("div") :addClass("zw-wares__price") local text if not price or price == "" then html:wikitext("") elseif price == "Varies" then html:addClass('zw-wares__price--variable') :wikitext("Varies") elseif string.match(price, "^[%d,]+$") then -- is number local text = mw.getCurrentFrame:expandTemplate({			title = "Rupee",			args = {game, price}		}) html:wikitext(text) elseif not string.find(price, ", ") and not string.find(price, "^%d") then -- assume it is a single, non-numbered item local text = mw.getCurrentFrame:expandTemplate({			title = "Icon List",			args = {game, price}		}) html:wikitext(text) else -- assume it is a list of item amounts local text = mw.getCurrentFrame:expandTemplate({			title = "Amounts",			args = {game, price}		}) html:wikitext(text) end return tostring(html) end

function h.getConfig(prop, args, item) if prop == "fileFormat" and args.fileType then return string.format("File:${game} ${singular} %s.png", args.fileType) end

local gameData = Data[args.game] local defaultProp = Data.defaults[Franchise.graphics(args.game)][prop] local gameProp = gameData and gameData[prop] local itemProp = gameData and gameData[item.item] and gameData[item.item][prop] return itemProp or gameProp or defaultProp end

p.Templates = { ["Wares"] = { description = "Lists items sold by merchants or in shops", format = "block", paramOrder = {"game", "prices", "locationTabs", "showQuantityColumn", "fileType", "..."}, params = { game = { required = true, desc = "A game code.", type = "string", enum = Franchise.enum, },			prices = { desc = "", type = "string", split = true, trim = true, nilIfEmpty = true, },			locationTabs = { desc = "", type = "boolean", default = false, trim = true, nilIfEmpty = true, },			showQuantityColumn = { desc = "", type = "boolean", default = false, trim = true, nilIfEmpty = true, },			fileType = { desc = "", type = "string", trim = true, nilIfEmpty = true, },			["..."] = {				name = "items", type = "content", },		},	},	["Wares/Item"] = { repeatedGroup = { name = "prices", params = {"price"}, allowSingle = true, },		paramOrder = {"item", "price", "image", "display", "quantity", "stock", "description", "location", "weather"}, params = { item = { desc = "", required = true, type = "wiki-page-name", trim = true, nilIfEmpty = true, },			price = { desc = "", required = true, type = "string", trim = true, nilIfEmpty = true, },			image = { desc = "", type = "string", trim = true, nilIfEmpty = true, },			display = { desc = "", type = "content", trim = true, nilIfEmpty = true, },			quantity = { desc = "", type = "number", default = 1, trim = true, nilIfEmpty = true, },			stock = { desc = "", type = "number", trim = true, nilIfEmpty = true, },			description = { desc = "", type = "content", trim = true, nilIfEmpty = true, },			location = { desc = "", type = "string", split = true, trim = true, nilIfEmpty = true, },			weather = { desc = "", type = "string", split = true, trim = true, nilIfEmpty = true, },		}	}, }

return p