Final Fantasy Wiki
Advertisement

Documentation for this module may be created at Module:XIVAPI/Sandbox/doc

-- <nowiki>
local p = {}
local getArgs = require("Dev:Arguments").getArgs
local disciplineIcons = mw.loadData("Module:Icon/data/FFXIV/Discipline")
local materiaDb = mw.loadData("Module:XIVAPI/Materia")

-- Locations of files on wiki

local DATA_DIRECTORY = "Module:XIVAPI/Item/"
local ICON_DATA_DIRECTORY = "Module:XIVAPI/Item Icon/"

-- Stat names.
local HQ_ICON = "[[File:FFXIV HQ Icon.png|HQ]]"

local BASE_STATS = {
    {"Block", "Block strength"},
    {"BlockRate", "Block rate"},
    {"DamagePhys", "Physical damage"},
    {"DamageMag", "Magic damage"},
    {"DefensePhys", "Physical defense"},
    {"DefenseMag", "Magic defense"},
    {"DelayMs", "Attack delay", "ms"},
    {"CooldownS", "Cooldown", "s"}
}

local PRIMARY_STATS = {
    "Strength",
    "Dexterity",
    "Vitality",
    "Intelligence",
    "Mind"
}

local SUB_STATS = {
    {"CriticalHit", "Critical Hit"},
    {"Determination", "Determination"},
    {"DirectHitRate", "Direct Hit"},
    {"SkillSpeed", "Skill Speed"},
    {"SpellSpeed", "Spell Speed"},
    {"Tenacity", "Tenacity"},
    {"Piety", "Piety"},
    {"Craftsmanship", "Craftsmanship"},
    {"Control", "Control"},
    {"Gathering", "Gathering"},
    {"Perception", "Perception"},
    {"CP", "CP"},
    {"GP", "GP"}
}

local BOOLS = {
    {"IsDyeable", "Is dyeable."},
    {"AetherialReduce", "Can be aetherially reduced."},
    {"Desynth", "Can be desynthesized."},
    {"EquipRestriction", "Gender/race locked."},
    {"ItemAction", "Can be used."},
    {"CanBeHq", "Can be " .. HQ_ICON .. "."},
    {"IsCollectable", "Collectable."},
    {"IsUnique", "Unique."},
    {"IsUntradable", "Untradable."},
    {"IsCrestWorthy", "Can be given FC crest."},
    {"IsIndisposable", "Cannot be discarded."},
    {"AlwaysCollectable", "Can only be crafted as collectable."}
}

local RECIPE_STATS = {
	{"ID", "Recipe ID"},
	{"Difficulty", "Difficulty"},
	{"Durability", "Durability"},
	{"Quality", "Quality"},
	{"AmountResult", "Amount produced"},
	{"SecretRecipeBook", "Recipe book"},
	{"RequiredCraftsmanship", "Required Craftsmanship"},
	{"RequiredControl", "Required Control"},
	{"SuggestedCraftsmanship", "Suggested Craftsmanship"},
	{"SuggestedControl", "Suggested Control"},
}

local RECIPE_BOOLS = {
	{"CanHq", "Can be made " .. HQ_ICON .. "."},
	{"CanQuickSynth", "Can be made using Quick Synthesis."},
	{"IsExpert", "Is an Expert recipe."},
	{"IsSecondary", "Is a secondary recipe."},
	{"IsSpecializationRequired", "Specialization required."},
}

local ROMAN_NUMERALS = {
	["I"] = 1,
	["II"] = 2,
	["III"] = 3,
	["IV"] = 4,
	["V"] = 5,
	["VI"] = 6,
	["VII"] = 7,
	["VIII"] = 8,
	["IX"] = 9,
	["X"] = 10,
	["XI"] = 11,
	["XII"] = 12,
	["XIII"] = 13,
	["XIV"] = 14
}

-- Placeholder stuff

local PLACEHOLDER_ICON = "Image Placeholder.png|128px"
local EMPTY_ITEM = {
    ["Name"] = "",
    ["ID"] = 0,
    ["IconID"] = 0,
    ["Link"] = "Final Fantasy XIV items"
}

local function subdir(name)
    -- Return the three-character subdirectory for the item
    if #name >= 3 then
        return name:sub(1, 3):lower()
    else
        return "*"
    end
end

local function lookup_item(name)
    -- Return the data table for the item
    local b, v = pcall(mw.loadData, DATA_DIRECTORY .. subdir(name))
    return (b and v[name]) or EMPTY_ITEM
end

local function lookup_item_icon(id)
    -- Return the data table for the item icon
    if (id == 0) then
        return PLACEHOLDER_ICON
    end
    local b, v = pcall(mw.loadData, ICON_DATA_DIRECTORY .. subdir(tostring(id)))
    return (b and v[id]) or PLACEHOLDER_ICON
end

local function create_li(str)
    -- Return an <li> object with wikitext the string
    return mw.html.create("li"):wikitext(str)
end

local function create_class_icon(class)
    -- Return an image and link to a class
    -- Takes in the name of the class
    class = disciplineIcons[class]
    return "[[File:" .. class.file .. "|" .. class.name .. "|link=" .. class.link .. "|20px]]"
end

local function create_item_icon(item)
    -- Return an item icon, takes in an item table.
    item = item or EMPTY_ITEM
    return "[[File:" ..
        lookup_item_icon(item.IconID) .. "|" .. item.Name .. "|20px]] [[" .. item.Link .. "|" .. item.Name .. "]]"
end

local function create_trs(item, showTypeRow)
    -- Return a list of <tr> objects for an item
    local tr = {}

    tr[1] = mw.html.create("tr")
    if showTypeRow then
	    tr[2] = mw.html.create("tr")
	end

    -- Left cell
    local name_cell_wt = (item.Name or "")
    if not (item.IconID == 0) then
        name_cell_wt = name_cell_wt .. "[[File:" .. lookup_item_icon(item.IconID) .. "]]"
    end

    -- Level row
    local level_cell = mw.html.create("ul")
    if item.LevelEquip or item.ClassJobCategory then
        -- item.ClassJobCategory should be formatted as {'arcanist', etc.}
        local s = "Equip to: "
        for _, class in ipairs(item.ClassJobCategory or {}) do
            s = s .. create_class_icon(class)
        end
        s = s .. " " .. tostring(item.LevelEquip or 0)
        level_cell:node(create_li(s))
    end
    level_cell:node(create_li("Item level: " .. tostring(item.LevelItem or 0)))
    level_cell:node(create_li("Stack size: " .. tostring(item.StackSize or 0)))
    level_cell:node(create_li("ID: " .. tostring(item.ID or 0)))

    -- Stats row
    local stats_cell = mw.html.create("ul")
    for _, s in ipairs(BASE_STATS) do
        local code, name, unit = unpack(s)
        unit = unit or ""
        local li = name
        if item[code] then
            li = li .. ": " .. tostring(item[code]) .. unit
        end
        if item.CanBeHq and item["Hq" .. code] then
            -- TODO: Make the xivapi parser generate Hq$baseStat$
            if li == name then
                li = li .. ": 0" .. unit
            end
            li = li .. " (" .. HQ_ICON .. " " .. tostring(item["Hq" .. code]) .. unit .. ")"
        end
        if not (li == name) then
            stats_cell:node(create_li(li))
        end
    end
    if item.Stats then
        for _, s in ipairs(PRIMARY_STATS) do
            if item.Stats[s] then
                local li = s .. ": " .. tostring(item.Stats[s].NQ)
                if item.canBeHq and item.Stats[s].HQ then
                    li = li .. " (" .. HQ_ICON .. " " .. tostring(item.Stats[s].HQ) .. " )"
                end
                stats_cell:node(create_li(li))
            end
        end
        for _, s in ipairs(SUB_STATS) do
            local code, name = unpack(s)
            if item.Stats[code] then
                local li = name .. ": " .. tostring(item.Stats[code].NQ)
                if item.canBeHq and item.Stats[code].HQ then
                    li = li .. " (" .. HQ_ICON .. " " .. tostring(item.Stats[code].HQ) .. " )"
                end
                stats_cell:node(create_li(li))
            end
        end
    end
    if not (tostring(stats_cell) == "<ul></ul>") then
        table.insert(
        	tr,
        	mw.html.create("tr"):
        		tag("td"):
        		tag("div"):
        		attr("class", "columns"):
        		node(stats_cell):
        		allDone()
        	)
    end

    -- Repair row
    local repair_cell = mw.html.create("ul")
    if item.ClassJobRepair then
        repair_cell:node(
            create_li(
                "Repair job: " ..
                    create_class_icon(item.ClassJobRepair) .. " " .. tostring(math.max((item.LevelEquip or 0) - 10, 1))
            )
        )
    end
    if item.ItemRepair then
        repair_cell:node(create_li("Catalyst: " .. create_item_icon(lookup_item(item.ItemRepair))))
    end
    if item.MateriaSlotCount then
        local s = "Materia slots: " .. tostring(item.MateriaSlotCount)
        if item.IsAdvancedMeldingPermitted then
            s = s .. " (can overmeld)"
        end
        repair_cell:node(create_li(s))
    end

    if not (tostring(repair_cell) == "<ul></ul>") then
        table.insert(
        	tr,
        	mw.html.create("tr"):
        		tag("td"):
        		tag("div"):
        		attr("class", "columns"):
        		node(repair_cell):
        		allDone()
        )
    end

    -- Materia row
    -- Materia information isn't any easier to recover from XIVAPI than it is from the item's name,
    -- so we just determine if the item is a materia from its name.
    -- See Module:XIVAPI/Materia for data
    local materiaStart, materiaEnd = string.find(item.Name, " Materia ")
    if materiaStart then
    	local materiaType = materiaDb[string.sub(item.Name, 1, materiaStart - 1)]
    	local strength = ROMAN_NUMERALS[string.sub(item.Name, materiaEnd + 1)]
    	if materiaType and strength then
        	table.insert(
            	tr,
            	mw.html.create("tr"):
            		tag("td"):
            		wikitext(
                		"This materia increases " .. materiaType[1] .. " by " .. tostring(materiaType[2][strength]) .. "."
            		):allDone()
        	)
        end
    end

    -- Recipe rows
    for i, recipe in ipairs(item.Recipes) do
    	local stats_ul = mw.html.create("ul")
    	stats_ul:node(create_li("Crafted by: " .. create_class_icon(recipe.ClassJob) .. " " .. tostring(recipe.ClassJobLevel or 0)))
    	for _, stat in ipairs(RECIPE_STATS) do
    		local code, str = unpack(stat)
    		if recipe[code] then
    			stats_ul:node(create_li(str .. ": " .. tostring(recipe[code])))
    		end
    	end
    	if recipe.Stars then
    		stats_ul:node(create_li(string.rep("[[File:MFF 1-Star Icon.png|star|16px]]", recipe.Stars) .. " recipe"))
    	end
    	
    	local bool_output = "<center>Properties:</center><br/>"
    	for _, bool in ipairs(RECIPE_BOOLS) do
    		local code, str = unpack(bool)
    		if recipe[code] then
    			bool_output = bool_output .. str .. " "
    		end
    	end
    	if bool_output == "<center>Properties:</center><br/>" then
    		bool_output = ""
    	end
    	
    	local ingredients_ul = mw.html.create("ul")
    	for _, ingredient in ipairs(recipe.Ingredients) do
    		local name, amount = unpack(ingredient)
    		ingredients_ul:node(create_li(create_item_icon(lookup_item(item.ItemRepair)) .. " x" .. tostring(amount)))
    	end
    	
    	local recipe_cell = mw.html.create("td"):
    		wikitext("<center>Recipe " .. tostring(i) .. ":</center>"):
    		tag("div"):
    		attr("class", "columns"):
    		node(stats_ul):
    		allDone():
    		wikitext("<br><center>Ingredients</center>:"):
    		tag("div"):
    		attr("class", "columns"):
    		node(ingredients_ul):
    		allDone():
    		wikitext(bool_output)
    	table.insert(
    		tr,
    		mw.html.create("tr"):
    			node(recipe_cell):
    			allDone()
    		)
    end
    
    -- TODO: Used to make

    -- Description row
    local descr_string = (item.Description and (item.Description .. "<br/>")) or ""
    for _, bool in ipairs(BOOLS) do
        local bool_name, bool_descr = unpack(bool)
        if item[bool_name] then
            descr_string = descr_string .. bool_descr .. " "
        end
    end
    if item.ItemSeries then
        descr_string = descr_string .. "Part of the " .. item.ItemSeries .. " collection. "
    end
    if item.GrandCompany then
        descr_string = descr_string .. item.GrandCompany .. " uniform."
    end
    if not (descr_string == "") then
        table.insert(
        	tr,
        	mw.html.create("tr"):
        		tag("td"):
        		wikitext(descr_string):
        		done()
        	)
    end

    -- Generate left and top cells
    if showTypeRow then
    	tr[1]:
    		tag("th"):
    		wikitext(name_cell_wt):
    		attr("rowspan", #tr):
    		done():
    		tag("th"):
    		wikitext((item.ItemKind or "&mdash;") .. " (" .. (item.ItemUICategory or "&mdash;") .. ")")
    	tr[2]:
    		tag("td"):
    		tag("div"):
    		attr("class", "columns"):
    		node(level_cell)
    else
    	tr[1]:
	    	tag("th"):
	    	wikitext(name_cell_wt):
	    	attr("rowspan", #tr):
	    	done():
	    	tag("td"):
	    	tag("div"):
	    	attr("class", "columns"):
	    	node(level_cell)
	end

    return tr
end

local function table_header()
    -- Return a basic table header
    return mw.html.create("table"):attr("class", "full-width article-table ffxiv-item-table"):tag("tr"):tag("th"):attr(
        "style",
        "width:128px;"
    ):wikitext("Item"):done():tag("th"):wikitext("Description"):allDone()
end

function p.Test()
    item = {}
    item.Name = "Savage Aim Materia VI"
    item.ID = 12345
    item.Link = "Project:Sandsea"
    item.ClassJobCategory = {"arcanist", "scholar"}
    item.LevelEquip = 26
    item.LevelItem = 45
    item.ItemRepair = "grade 5 dark matter"
    item.IsIndisposable = 1
    item.IconID = 10000
    item.ClassJobRepair = "weaver"
    item.Description = "A big weapon."
    item.IsCrestWorthy = 1
    item.StackSize = 16
    item.BlockRate = 100
    item.HqBlockRate = 120
    item.HqCooldownS = 19
    item.MateriaSlotCount = 2
    item.ItemKind = "Medicines & Meals"
    item.ItemUICategory = "Gladiator's Arm"
    item.IsAdvancedMeldingPermitted = 1
    item.AlwaysCollectable = 1
    item.CanBeHq = true
    item.DamageMag = 15
    item.Stats = {
        ["Strength"] = {["NQ"] = 16, ["HQ"] = 26},
        ["DirectHitRate"] = {["NQ"] = 19}
    }
    item.Recipes = {
    	{
	    	["CanHq"] = true,
    		["ID"] = 199,
    		["ClassJob"] = "blacksmith",
    		["ClassJobLevel"] = 100,
    		["IsExpert"] = false,
    		["AmountResult"] = 15,
    		["Stars"] = 3,
    		["SuggestedControl"] = 1,
    		["RequiredControl"] = 2,
    		["Ingredients"] = {
    			{"Grade 5 Dark Matter", 15},
    		}
    	},
    }
    
    local t = table_header()
    local trs = create_trs(item, false)
    for i, tr in ipairs(trs) do
        t:node(tr)
    end
    return t
end

return p
Advertisement