Module:Armor

local p = {} local h = {}

local DataTable = require("Module:Data Table") local TermList = require("Module:Term List") local utilsArg = require("Module:UtilsArg") local utilsCargo = require("Module:UtilsCargo") local utilsString = require("Module:UtilsString") local utilsTable = require("Module:UtilsTable")

local CATEGORY_INVALID_ARGS = require("Module:Constants/category/invalidArgs") local ARMOR_COLUMNS = {"Set", "Body Part", "Armor", "Defense", "Effect(s)", "Sell Price", "Inventory Description"} local PAGE_ARMOR_UPGRADES = "User:PhantomCaleb/Sandbox/Armor Upgrade" -- TODO local PAGE_ARMOR = "User:PhantomCaleb/Sandbox/Armor" -- TODO local UPGRADE_COLUMNS = {"Armor", "Level", "Materials", "Defense", "Sell Price"}

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

function h.err(errMsg, warnMsg) local utilsError = require("Module:UtilsError") return utilsError.error(errMsg, warnMsg) end

function p.ArmorData(frame) return h.armorData(frame) end

function p.MaterialArmors(frame) return h.materialArmors(frame) end

function p.ArmorTable(frame) return DataTable.Main(frame, h.storeArmor, {		requiredColumns = ARMOR_COLUMNS	}) end

function p.UpgradeTable(frame) return DataTable.Main(frame, h.storeUpgrades, {		requiredColumns = UPGRADE_COLUMNS	}) end

function h.armorData(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates["Armor Data"]) if err then return "", err.categoryText end local armor = args.armor or mw.title.getCurrentTitle.subpageText local armorValue = utilsCargo.escape(armor) local armorRows = utilsCargo.query("Armor", "armor, armorSet, bodyPart, level, defense, materials, sellPrice", {		where = string.format("(_pageName = '%s' OR _pageName = '%s') AND (armor = '%s' OR armorSet = '%s')", PAGE_ARMOR, PAGE_ARMOR_UPGRADES, armorValue, armorValue)	}) local armorCount = #(utilsTable.uniqueBy(armorRows, "armor")) if #armorRows == 1 then h.warn("Non-upgradeable Armor does not have enough data to show in a table. The template should be removed from the page.") return "" elseif armorCount == 0 then h.warn("No data exists for Armor or Armor Set  on the %s page.", armor, PAGE_ARMOR) return "" elseif #armorRows == armorCount then return h.armorPieces(armorRows, args.game) -- simply list Armor pieces for non-upgradeable armor sets elseif armorCount > 1 then return h.setLevels(armorRows, args.game) else return h.armorLevels(armorRows, args.game) end end function h.armorLevels(armorRows, game) local rows = {} for i, armor in ipairs(armorRows) do		local row = {} row.cells = { armor.level or " ", armor.defense, armor.materials or "N/A", armor.sellPrice or "N/A" }		table.insert(rows, row) end local tableArgs = DataTable.formatTable(rows, {		game = game,		sortable = false,		columns = {"Level", string.format("Defense [Defense:%s:-]", game), "Materials [Amounts]", "Sell Price [Rupees]"}	}) local dataTable = DataTable.printTable(tableArgs) return dataTable end function h.setLevels(armorRows, game) armorRows = utilsTable.sortBy(armorRows, h.bodyPartSortKey) local rows = {} for i, armor in ipairs(armorRows) do		local row = {} row.cells = { armor.armor, armor.level or " ", armor.defense, armor.materials or "N/A", armor.sellPrice or "N/A" }		table.insert(rows, row) end local tableArgs = DataTable.formatTable(rows, {		game = game,		sortable = false,		columns = {"Armor [Rowspan:5][Term]", "Level", string.format("Defense [Defense:%s:-]", game), "Materials [Amounts]", "Sell Price [Rupees]"}	}) local dataTable = DataTable.printTable(tableArgs) return dataTable end function h.armorPieces(armorRows, game) local rows = {} for i, armor in ipairs(armorRows) do		local row = {} row.cells = { armor.armor, armor.defense, armor.sellPrice or "N/A" }		table.insert(rows, row) end local tableArgs = DataTable.formatTable(rows, {		game = game,		sortable = false,		columns = {"Armor [Term]", string.format("Defense [Defense:%s:-]", game), "Sell Price [Rupees]"}	}) local dataTable = DataTable.printTable(tableArgs) return dataTable end

function h.materialArmors(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates["Material Armors"]) if err then return "", err.categoryText end local material = args.material or mw.title.getCurrentTitle.subpageText material = utilsCargo.escape(material) local armorRows = utilsCargo.query("Armor", "armorSet, armor, bodyPart, level, materials", {		where = utilsCargo.allOf({ game = args.game, _pageName = PAGE_ARMOR_UPGRADES }, "materials HOLDS LIKE '%"..material.."%'")	})

if #armorRows == 0 then return h.err("Data not found", string.format("No upgrade data exists for material  on page %s for game  ", material, PAGE_ARMOR_UPGRADES, args.game)), "" end

local hasArmorSets = false armorRows = utilsTable.sortBy(armorRows, function(armor)		local armorSet = armor.armorSet		if armorSet and armorSet ~= "" then			hasArmorSets = true			local bodyPart = h.bodyPartSortKey(armor)			return "1"..armorSet..bodyPart..armor.level		else			return "0"..armor.armor..armor.level		end	end) local armorLevels = utilsTable.groupBy(armorRows, "armor") local armorBySet = utilsTable.groupBy(armorRows, "armorSet")

local tableRows = {} local seenArmors = {} local seenArmorSets = {} for i, armor in ipairs(armorRows) do		local armorLevelCount = #(armorLevels[armor.armor]) local spanning = not seenArmors[armor.armor] local cells = { {				rowspan = spanning and armorLevelCount or nil, raw = armor.armor, skip = not spanning, },			{				raw = armor.level },			{				raw = armor.materials }		}		if hasArmorSets then local spanning = not (seenArmorSets[armor.armorSet or ""] or seenArmors[armor.armor]) local armorSetLevelCount = #(armorBySet[armor.armorSet or ""] or armorLevels[armor.armor]) table.insert(cells, 1, {				rowspan = spanning and armorSetLevelCount or nil,				raw = armor.armorSet or "N/A",				skip = not spanning,			}) mw.logObject(cells[1]) end seenArmors[armor.armor] = true if armor.armorSet then seenArmorSets[armor.armorSet] = true end local row = { cells = cells } table.insert(tableRows, row) end

local columns = {"Armor [Term]", "Level", "Materials [Amounts]"} if hasArmorSets then table.insert(columns, 1, "Armor Set [Term]") end local tableArgs = DataTable.formatTable(tableRows, {		game = args.game,		sortable = false,		columns = columns,	}) local dataTable = DataTable.printTable(tableArgs) return dataTable end

function h.storeArmor(args) local categories = "" for i, row in ipairs(args.rows) do		local data, rowCategories = h.parseRow(ARMOR_COLUMNS, row.cells, i)		categories = categories..rowCategories mw.getCurrentFrame:expandTemplate({			title = "Armor/Store",			args = {				game = args.game,				armorSet = data["Set"],				armor = data["Armor"],				bodyPart = data["Body Part"],				level = nil,				defense = data["Defense"],				sellPrice = data["Sell Price"],				effects = data["Effect(s)"],				materials = nil,				description = data["Inventory Description"]			}		}) end return categories end

function h.storeUpgrades(args) local categories = "" local armorByName, armorBySet = h.getBaseArmorData(args.game) for i, row in ipairs(args.rows) do		local data, errCategories = h.parseRow(UPGRADE_COLUMNS, row.cells, i)		categories = categories..errCategories local armors, errCategories = h.addBaseData(data, armorBySet, armorByName, args.game) categories = categories..errCategories for i, armor in ipairs(armors) do			mw.getCurrentFrame:expandTemplate({				title = "Armor/Store",				args = armor,			}) end end return categories end function h.addBaseData(data, armorBySet, armorByName, game) local categories = "" local armors = {} local armorName = data["Armor"] local armorPieces = armorBySet[armorName] if armorPieces then for i, armorPiece in ipairs(armorPieces) do			local armor = h.mergeBaseData(data, armorByName[armorPiece]) armor.game = game table.insert(armors, armor) end elseif armorByName[armorName] then local armor = h.mergeBaseData(data, armorByName[armorName]) armor.game = game table.insert(armors, armor) else h.warn("Unknown Armor or Set '%s'", armorName) categories = categories.."" end return armors, categories end function h.mergeBaseData(upgrade, base) local description = base.description return { armor = base.armor, armorSet = base.armorSet, bodyPart = base.bodyPart, effects = base.effects, level = upgrade["Level"], defense = upgrade["Defense"], sellPrice = upgrade["Sell Price"], materials = upgrade["Materials"], description = upgrade["Description"], } end

function h.parseRow(columns, cells, rowIndex) local categories = "" local row = {} local cellsByColumn = utilsTable.keyBy(cells, "columnHeader") for i, column in ipairs(columns) do		local cell = cellsByColumn[column] or {} if cell.raw == "" or cell.raw == nil then h.warn("The value for column  in row %d should not be empty.", column, rowIndex) categories = categories.."" cell = nil elseif string.find(cell.raw, "^N/A") then cell = nil else cell = TermList.separateAdditionalInfo(cell.raw) end row[column] = cell end return row, categories end

function h.getBaseArmorData local armors = utilsCargo.query("Armor", "armor, armorSet, bodyPart, effects", {		limit = 400,		_pageName = ARMOR_PAGE	}) local armorByName = utilsTable.keyBy(armors, "armor") local armorBySet = utilsTable.groupBy(armors, "armorSet") armorBySet = utilsTable.mapValues(armorBySet, utilsTable._map("armor")) return armorByName, armorBySet end

function h.bodyPartSortKey(armor) local sortKey = ({		["Head"] = 1,		["Chest"] = 2,		["Legs"] = 3,	})[armor.bodyPart or ""] return sortKey or 0 end

p.Templates = { ["Armor Data"] = { wip = true, description = "Displays Armor data", purpose = string.format("Displays Armor data retrieved from the Armor list and Armor upgrade list stored in the Armor Cargo table. For use on individual Armor and Armor Set articles.", PAGE_ARMOR, PAGE_ARMOR_UPGRADES), params = { [1] = {				name = "game", required = true, type = "string", enum = {"BotW"}, desc = "A game code.", },			[2] = {				name = "armor", type = "wiki-page-name", desc = "An article name referring to a particular piece of Armor. Defaults to the current page.", },		},		examples = { vertical= true, {"BotW", "Rubber Armor"}, {"BotW", "Rubber Armor (Set)"}, {"BotW", "Well-Worn Outfit"}, {				desc = "Displays nothing for non-upgradeable individual armor pieces, as there is not enough information to display in a table.", args = {"BotW", "Thunder Helm"}, },			{				desc = "Error: Non-existent Armor", args = {"BotW", "Not an Armor"}, },		},	},	["Armor/Store"] = { purpose = "Used by Template:Data Table/Armor to store armor data.", format = "block", storesData = true, paramOrder = {"game", "armorSet", "armor", "bodyPart", "level", "defense", "sellPrice", "effects", "materials", "description"}, boilerplate = { separateRequiredParams = false, },		params = { game = { type = "string", desc = "A game code from Data:Franchise", trim = true, nilIfEmpty = true, },			armorSet = { type = "wiki-page-name", desc = "If applicable, a wiki page name on the Armor Set that this Armor belongs to.", trim = true, },			armor = { required = true, type = "wiki-page-name", desc = "A wiki article name on a particular piece of Armor.", trim = true, nilIfEmpty = true, },			bodyPart = { required = true, type = "string", enum = {"Head", "Chest", "Legs"}, desc = "The slot that this Armor occupies on Link's body.", trim = true, nilIfEmpty = true, },			level = { type = "number", desc = "The level of the Armor upgrade, as an integer. Blank for base items.", trim = true, nilIfEmpty = true, },			defense = { required = true, type = "number", desc = "The defensive value for the Armor at the specified level.", trim = true, nilIfEmpty = true, },			sellPrice = { type = "number", desc = "An integer – the amount of Rupees that Link can sell the Armor for at the specified level.", trim = true, nilIfEmpty = true, },			effects = { type = "string", desc = "A comma-separated list of the Armor's Effects, if any.", trim = true, nilIfEmpty = true, },			materials = { required = true, type = "string", desc = "A list of the materials required to obtain specified upgrade level.", trim = true, nilIfEmpty = true, },			description = { required = true, type = "content", desc = "A transcript of the item's description on the Inventory screen.", trim = true, nilIfEmpty = true, },		},	},	["Data Table/Armor"] = {}, ["Data Table/Armor Upgrades"] = {}, ["Material Armors"] = { wip = true, description = "Lists Armor upgrades that require a particular Material.", purpose = string.format("Displays Armor Upgrades requiring a particular Material, for use on individual Material articles. The data is retrieved from the Armor Cargo table data stored on the %s page.", PAGE_ARMOR_UPGRADES), params = { [1] = {				name = "game", required = true, type = "string", enum = {"BotW"}, desc = "A game code.", },			[2] = {				name = "material", type = "wiki-page-name", desc = "An article name referring to a particular Material. Defaults to the current page.", },		},		examples = { vertical = true, {"BotW", "Amber"}, {"BotW", "Diamond"}, {"BotW", "Flint"}, {"BotW", "Not a Material"}, },	} }

return p