Module:Items

local TableTools = require("Module:TableTools") local Utils = require("Module:Utils") local Pagelist = require('Module:Pagelist') local mw = mw

local gsub = string.gsub local tinsert = table.insert

local pagelist = Pagelist._main local keysToList = TableTools.keysToList local shallowClone = TableTools.shallowClone local table_size = TableTools.size local sortedPairs = TableTools.sortedPairs local formatNum = Utils.formatNum local pageExists = Utils.pageExists local sprintf = Utils.sprintf local strlen = Utils.strlen local upper = Utils.upper

-- ----- MODULE FUNCTIONS --- -- local p = require("Module:BaseModule"):newModule local module_data = p:getModuleData("Module:Items/Data")

local main_args = { parentFirst = true, wrappers = { "Template:ItemTable", "Template:ItemHeader", "Template:ItemRecipeTable", "Template:ItemIngredientOf", "Template:EquipmentByAttribute" } }

p.makeItemTable = p:makeInvokeFunc("_makeItemTable", main_args) p.makeItemRecipeTable = p:makeInvokeFunc("_makeItemRecipeTable", main_args) p.getItemHeader = p:makeInvokeFunc("_getItemHeader", main_args) p.listItemIngredientOf = p:makeInvokeFunc("_listItemIngredientOf", main_args) p.tableEquipmentByAttribute = p:makeInvokeFunc("_tableEquipmentByAttribute", main_args)

-- -- PAGE FUNCTIONS --

-- Make Item Recipe table

function p._makeItemRecipeTable(args, frame) -- luacheck: ignore local item_name = args[1] local item_crafted_data = p:getModuleData("Module:Items/Data/Crafted", item_name) local item_equipment_data = p:getModuleData("Module:Items/Data/Equipment", item_name)

if item_crafted_data == nil then return sprintf("ERROR: %s doesn't exist in the Crafted data module!", item_name) end

local item_material_stats = item_equipment_data and item_equipment_data["Material Stats"] or {} local item_materials_needed = item_crafted_data["Materials Needed"] or {}

local num_material_stats = table_size(item_material_stats)

local quantity_crafted = item_crafted_data["Quantity Crafted"] or "??" local time_required = item_crafted_data["Time Required"] or "??"

local stations = shallowClone(item_crafted_data["Station"])

if #stations > 0 then stations["conjunction"] = " or " stations["nspace"] = "all" stations = gsub(pagelist(stations), "%[%[:Hand|Hand%]%]", "Hand") else stations = "UNKNOWN" end

local recipeWikitext = mw.html.create("p"):wikitext(sprintf("%s is crafted via %s. ", item_name, stations))

local craftingStatsTable = mw.html.create("table") :addClass("wikitable") :cssText("text-align: center") :tag("tr") :tag("th") :cssText("text-align: left; width: 130px") :wikitext("Quantity Crafted") :done :tag("td") :cssText("width: 20px") :wikitext(quantity_crafted) :done :done :tag("tr") :tag("th") :cssText("text-align: left; width: 130px") :wikitext("Time Required") :done :tag("td") :cssText("width: 20px") :wikitext(time_required) :done :done :done

local itemsNode local itemsNode_leftCellCss = "width:75%;" local itemsNode_rightCellCss = "width:25%;" local bonusStatsNode local stats local num_stats local count local name local row local itemTable_leftCellCss = "text-align: left; width: 100%" local itemTable_rightCellCss = "text-align: center" local itemTableWidth = "300px"

if num_material_stats > 0 then itemsNode_leftCellCss = "width:50%;" itemsNode_rightCellCss = "width:50%;"

itemTable_leftCellCss = "text-align: left; width: 65%" itemTableWidth = "550px" end

local itemTable = mw.html.create("table") :addClass("wikitable") :cssText(sprintf("width: %s", itemTableWidth)) :tag("caption") :addClass("game-item-table") :cssText("caption-side:bottom; font-size:90%; text-align:left; font-weight:normal; padding-left:5px") :wikitext("*One recipe per row") :done

local itemTableHeaderRow = mw.html.create("tr") :tag("th") :cssText(itemTable_leftCellCss) :wikitext("Recipes") :done

if num_material_stats > 0 then itemTableHeaderRow :tag("th") :cssText(itemTable_rightCellCss) :wikitext("Bonus Stats") :done end

itemTable:node(itemTableHeaderRow:done)

for idx, recipe in ipairs(item_materials_needed) do       -- Create wikitext for recipe items and bonus stats itemsNode = mw.html.create("table"):cssText("width: 100%") bonusStatsNode = mw.html.create("div"):cssText("text-align: center")

for _, item in ipairs(recipe) do           name = item["name"]

itemsNode:tag("tr") :tag("td") :cssText(sprintf("text-align: left; background-color:inherit; border:none;%s", itemsNode_leftCellCss)) :wikitext(sprintf("%s", name)) :done :tag("td") :cssText(sprintf("text-align: center; background-color:inherit; border:none;%s", itemsNode_rightCellCss)) :tag("div") :cssText("display: inline-block; vertical-align: middle; margin-right: 4px") :wikitext(item["quantity"]) :done :tag("div") :cssText("display: inline-block") :wikitext(sprintf("", item["image"], name)) :done :done :done end

-- Get material stats stats = item_material_stats[idx] or {} num_stats = table_size(stats) count = 1

for k, v in pairs(stats) do           k = pageExists(k) and sprintf("%s", k) or k

bonusStatsNode:tag("div") :cssText("display: inline-block; vertical-align: middle; margin-right: 4px") :wikitext(k) :done

bonusStatsNode:tag("div") :cssText("display: inline-block") :wikitext(v) :done

if count < num_stats then bonusStatsNode:wikitext(" ") end

count = count + 1 end

itemsNode:done bonusStatsNode:done

row = mw.html.create("tr") :tag("td") :cssText("vertical-align:middle") :node(itemsNode) :done

if num_material_stats > 0 then row:tag("td") :cssText("vertical-align:middle") :node(bonusStatsNode) :done end

itemTable:node(row:done) end

recipeWikitext :node(craftingStatsTable) :wikitext(" ") :node(itemTable:done)

return sprintf("%s", tostring(recipeWikitext:allDone)) end

-- Make Item Table

function p._makeItemTable(args, frame) -- luacheck: ignore local item_name = args[1] local item_data = module_data[item_name] or {} local item_image = item_data["image"]

if item_data == nil or strlen(item_data) == 0 then return sprintf("%s DOES NOT EXIST!", item_name) end

if item_image == nil or strlen(item_image) == 0 or not pageExists(sprintf("File:%s", item_image)) then item_image = "Icon Unknown.jpg" end

-- Need to modify values for formatting consistency item_data = shallowClone(item_data)

-- CSS is set below after the local functions local itemTable = mw.html.create("table")

-- CSS for left and right table columns local leftCellCss = "text-align:center; width:50%" local rightCellCss = leftCellCss

-- Each section can order its own keys local data_key_order

-- Adds a section header to table row local function add_section_header(name) itemTable :tag("tr") :tag("th") :attr("class", "section-header") :attr("colspan", "2") :attr("style", "background-color:DimGrey; padding: 2px 0") :cssText("text-align: center;") :wikitext(sprintf("%s", name)) :done :done end

-- Add a common table row to table local function add_basic_row(left_data, right_data) if pageExists(left_data) then left_data = sprintf("%s", left_data) end

itemTable :tag("tr") :tag("th") :cssText(leftCellCss) :wikitext(left_data) :done :tag("td") :cssText(rightCellCss) :wikitext(right_data) :done :done end

-- Add several common table rows to table local function add_basic_rows(key_order, row_data) local right_data for _, left_data in ipairs(key_order) do           right_data = row_data[left_data]

if right_data ~= nil then add_basic_row(left_data, right_data) end end end

-- Name header, Image itemTable :attr("class", "game-item-table") :cssText("float: right; background-color: black; border: 2px solid dimgrey; border-collapse: separate; border-radius: 25px; width: 350px;") :done :tag("tr") :tag("th") :attr("colspan", "2") :cssText("text-align: center; font-size: 18px; font-weight: bold; font-style: italic; width: 110%") :wikitext(sprintf("%s", item_name)) :done :done :tag("tr") :tag("td") :attr("colspan", "2") :wikitext(sprintf("", item_image)) :done :done

-- Some editors may use a comma and others a dot for number decimals -- Replace comma with dot to avoid tonumber and lang.formatNum issues if item_data["Weight"] ~= nil then item_data["Weight"] = sprintf("%s kg", formatNum(item_data["Weight"])) end

-- Enforce order of keys for base data data_key_order = {"Source", "Max Stack", "Weight"} add_basic_rows(data_key_order, item_data)

-- Guarantee the order these appear in the table for consistency local item_edible_data = p:getModuleData("Module:Items/Data/Edible", item_name) local item_reagent_data = p:getModuleData("Module:Items/Data/Reagents", item_name) local item_equipment_data = p:getModuleData("Module:Items/Data/Equipment", item_name)

-- Process Edible data if item_edible_data ~= nil then add_section_header("Edible") for k, v in pairs(item_edible_data) do           add_basic_row(k, v)        end end

-- Process Enchanting Reagent data if item_reagent_data ~= nil then add_section_header("Enchanting Reagent")

for _, name in ipairs(keysToList(item_reagent_data)) do           for _, ele in ipairs(item_reagent_data[name]) do                add_basic_row(name, ele) end end end

-- Process Equipment data if item_equipment_data ~= nil then add_section_header("Stats")

data_key_order = { "Armor Type", "Weapon Type", "Durability", "Slash Armor", "Pierce Armor", "Crush Armor", "Magic Resistance", "Ice Resistance", "Poison Resistance", "Damage Type", "Versatile", "Damage Low", "Damage High", "Attack Speed", "Accuracy", "Evasion", "Weapon Damage", "Spell Damage", "Critical Chance", "Critical Damage", "Luck", "Willpower", "Fortitude", "Health", "Strength", "Intelligence", "Dexterity", "Enchantments", "Quality" }       add_basic_rows(data_key_order, item_equipment_data) end return sprintf("%s", tostring(itemTable:allDone)) end

-- Get Item Header

function p._getItemHeader(args, frame) -- luacheck: ignore local item_name = args[1] local item_data = module_data[item_name]

if item_data == nil then return sprintf(" %s HAS NO HEADER!", item_name) end

local res = "" local description = item_data["description"] local image = item_data["envImage"] if image ~= nil then if strlen(image) > 0 and pageExists(sprintf("File:%s", image)) then res = res .. sprintf('\n', image) else res = res .. sprintf('\n') end end

if description ~= nil and strlen(description) > 0 then res = res .. sprintf('%s\n', description) else res = res .. sprintf("%s HAS NO DESCRIPTION", item_name) end

return res end

-- Return items where args is an ingredient

function p._listItemIngredientOf(args, frame) -- luacheck: ignore local item_name = args[1] local item_crafted_data = p:getModuleData("Module:Items/Data/Crafted") local list = {} local i = 1 local res = ""

for name, _ in pairs(item_crafted_data) do       local item_data = item_crafted_data[name] local recipe_list = item_data["Materials Needed"] for _, materials_info in ipairs(recipe_list) do           for _, item_info in ipairs(materials_info) do                if item_info["name"] == item_name then list[i] = name i = i + 1 end end end end

if #list ~= 0 then res = res .. item_name .. " is used in the following recipes:\n" for _, v in pairs(TableTools.removeDuplicates(Utils.tableSort(list))) do           res = res .. sprintf('* %s\n',v) end else res = res .. "There are no known recipes, which this item is part of." end

return res end

-- Return items that go in a given character slot (i.e. Head, Chest, Hands)

local function getEquipmentInSlot(slot) local item_equipment_data = p:getModuleData("Module:Items/Data/Equipment") local data

if not slot then data = item_equipment_data else data = {}

for item_name, item_data in pairs(item_equipment_data) do           if upper(slot) == upper(item_data["Slot"]) then data[item_name] = item_data end end end

return data end

-- Return items where args is a property (i.e. Evasion, Melee Damage)

function p._tableEquipmentByAttribute(args, frame) -- luacheck: ignore local item_attribute = args[1] local item_equipment_data = getEquipmentInSlot(args[2]) local item_crafted_data = p:getModuleData("Module:Items/Data/Crafted") local found_items = false

local itemTable = mw.html.create("table") :addClass("wikitable sortable") :cssText("text-align: center; width: 50%")

itemTable :tag("tr") :tag("th"):wikitext("Item Name"):done :tag("th"):wikitext("Slot"):done :tag("th"):wikitext(item_attribute):done :tag("th"):wikitext(sprintf("%s from Recipes", item_attribute)):done :done

local slot local material_stats_data local material_stats_value local item_crafted local materials_needed_data local armor_types = {"Slash Armor", "Pierce Armor", "Crush Armor"} local slash_armor_value local pierce_armor_value local crush_armor_value

for item_name, item_attributes in sortedPairs(item_equipment_data) do       local attribute_value = "" local materials_needed_names = {} slot = item_attributes["Slot"] material_stats_data = item_attributes["Material Stats"] item_crafted = item_crafted_data[item_name] slash_armor_value = item_attributes["Slash Armor"] pierce_armor_value = item_attributes["Pierce Armor"] crush_armor_value = item_attributes["Crush Armor"]

-- Special case for Armor to get all types if item_attribute == "Armor" and (slash_armor_value or pierce_armor_value or crush_armor_value) then local armor_type_values = { slash_armor_value or "", pierce_armor_value or "", crush_armor_value or "" }

for idx, armor_type_value in ipairs(armor_type_values) do               if #armor_type_value > 0 then attribute_value = sprintf("%s %s: %s", attribute_value, armor_types[idx], armor_type_value) end end

-- remove leading and trailing line breaks attribute_value = gsub(attribute_value, "^ ", "") attribute_value = gsub(attribute_value, " $", "") else attribute_value = item_attributes[item_attribute] end

-- Check if any recipes offer the attribute we're looking for if material_stats_data ~= nil then for idx, stats in ipairs(material_stats_data) do               for k, v in pairs(stats) do                    if k == item_attribute then -- Get the value for the attribute material_stats_value = v

-- Find what materials are needed to acquire the attribute on the item materials_needed_data = item_crafted and item_crafted["Materials Needed"]

if materials_needed_data ~= nil then local materials_needed_idx = materials_needed_data[idx] or {}

for _, craft_items in ipairs(materials_needed_idx) do                               tinsert(materials_needed_names, craft_items["name"]) end end end end end end

-- Check if the item itself has the attribute if attribute_value ~= nil or #materials_needed_names > 0 then local recipe_str = ""

if material_stats_value ~= nil and #materials_needed_names > 0 then recipe_str = sprintf("%s if made with %s", material_stats_value, pagelist(materials_needed_names)) end

itemTable :tag("tr") :tag("td"):wikitext(sprintf("%s", item_name)):done :tag("td"):wikitext(slot or ""):done :tag("td"):wikitext(attribute_value or ""):done :tag("td"):wikitext(recipe_str):done :done

found_items = true end

end

if found_items == false then itemTable :tag("tr") :tag("td") :attr("colspan", "4") :wikitext(sprintf("No items with %s found", item_attribute)) :done :done end

return sprintf("%s", tostring(itemTable:allDone)) end

return p