Difference between revisions of "Module:Infobox Item"

From Idlescape Wiki
Jump to navigation Jump to search
(Add quantity range for research loot)
(Fix correctly handle items with noScrapFromResearching, noDustFromResearching and researchesIntoDust. Disable Source field as findSource() doesn't work properly, would need a big overhaul.)
Line 49: Line 49:
  
 
local function sl()
 
local function sl()
     local s = "s" .. l{}
+
     local s = "s" .. l()
 
     return s
 
     return s
 
end
 
end
  
 
local function sd()
 
local function sd()
     local s = "s" .. d{}
+
     local s = "s" .. d()
 
     return s
 
     return s
 
end
 
end
Line 70: Line 70:
 
     local lname = name:lower()
 
     local lname = name:lower()
  
    --Remove leading and trailing spaces.
+
    --Remove leading and trailing spaces.
 
     lname = lname:gsub('^%s*(.-)%s*$', '%1')
 
     lname = lname:gsub('^%s*(.-)%s*$', '%1')
 
     for key, item in pairs(p.loadData("item")) do
 
     for key, item in pairs(p.loadData("item")) do
Line 141: Line 141:
 
     end
 
     end
 
     return icon(item.name, url, word)
 
     return icon(item.name, url, word)
 +
end
 +
 +
local function addSeparator(num)
 +
    return tostring(tonumber(num)):reverse():gsub("(%d%d%d)","%1,"):gsub(",(%-?)$","%1"):reverse()
 
end
 
end
  
Line 152: Line 156:
 
     end
 
     end
 
     return icon(loc.name, url, word)
 
     return icon(loc.name, url, word)
end
 
 
local function img(id)
 
    local url = ""
 
    if item.itemIcon then
 
        url = item.itemIcon
 
    else
 
        url = item.itemImage
 
    end
 
    return fullUrl(url)
 
end
 
 
local function addSeparator(num)
 
    return tostring(tonumber(num)):reverse():gsub("(%d%d%d)","%1,"):gsub(",(%-?)$","%1"):reverse()
 
 
end
 
end
  
Line 183: Line 173:
 
end
 
end
  
-- local function farmingSource(id)
+
local function farmingSource(id)
-- local s = ""
+
    local s = ""
 
+
    for key, item in pairs(p.loadData('item')) do
-- for key, item in pairs(p.loadData('item')) do
+
        if item.farmingStats then
-- if item.farmingStats then
+
            for key2, yield in pairs(item.farmingStats.yield) do
-- for key2, yield in pairs(item.farmingStats.yield) do
+
                if id == yield.id then
-- if id == yield.id then
+
                    s = s .. itemImage(item.id, true)
-- s = s .. itemImage(item.id, true)
+
                    s = s .. "<br>"
-- s = s .. "<br>"
+
                end
-- end
+
            end
-- end
+
        end
-- end
+
    end
-- end
+
    return s
-- return s
+
end
-- end
 
  
 
local function smithingSource(id)
 
local function smithingSource(id)
Line 306: Line 295:
 
---@param args table
 
---@param args table
 
---@param title string
 
---@param title string
---@param id string|number
+
---@param id string|number|false|nil # Primary loot id, skipped if falsy
 
---@param scrapping table|nil # data.chance, data.itemID (AugmentingLoot scrappingSuccess or scrappingFail)
 
---@param scrapping table|nil # data.chance, data.itemID (AugmentingLoot scrappingSuccess or scrappingFail)
 
local function addScrapping(args, title, id, scrapping)
 
local function addScrapping(args, title, id, scrapping)
     local text = itemImage(id, true) .. "<br>"
+
     local text = id and (itemImage(id, true) .. "<br>") or ""
 
     if scrapping then
 
     if scrapping then
 
         if scrapping.chance and scrapping.chance ~= 1 then
 
         if scrapping.chance and scrapping.chance ~= 1 then
Line 344: Line 333:
  
 
---@param args table
 
---@param args table
---@param id string|number
+
---@param item Item
---@param addHeader boolean
 
 
---@return boolean
 
---@return boolean
local function addResearch(args, id, addHeader)
+
local function addResearch(args, item)
    ---@type Item|nil
+
--local function addResearch(args, id, addHeader)
    local item = getItem(id)
+
     local craftAug = getCraftAug(item.id)
     local craftAug = getCraftAug(id)
+
     local augLoot = getAugLoot(tostring(item.id))
     local augLoot = getAugLoot(id)
 
 
     if item and craftAug then
 
     if item and craftAug then
         if addHeader then
+
         if not item.blockAugmenting then
 
             args[h()] = "Research"
 
             args[h()] = "Research"
 
         end
 
         end
Line 359: Line 346:
 
         local dustId = getDustId(tier)
 
         local dustId = getDustId(tier)
 
         local success = augLoot and augLoot.scrappingSuccess or nil
 
         local success = augLoot and augLoot.scrappingSuccess or nil
         addScrapping(args, "Success", dustId, success)
+
         addScrapping(args, "Success", (not item.noDustFromResearching and dustId), success)
         local scrapId = getScrapId(item.rarity)
+
         local scrapId = item.researchesIntoDust and dustId or getScrapId(item.rarity)
 
         local fail = augLoot and augLoot.scrappingFail or nil
 
         local fail = augLoot and augLoot.scrappingFail or nil
         addScrapping(args, "Fail", scrapId, fail)
+
         addScrapping(args, "Fail", (not item.noScrapFromResearching and scrapId), fail)
 
         local transforms = augLoot and augLoot.transforms or {}
 
         local transforms = augLoot and augLoot.transforms or {}
 
         addTransforms(args, false, transforms)
 
         addTransforms(args, false, transforms)
Line 395: Line 382:
 
     }
 
     }
  
local affinities = {}
+
    local affinities = {}
for _, damageType in ipairs(allDamageTypes) do
+
    for _, damageType in ipairs(allDamageTypes) do
 
         local key = string.format("%s.%s", prefix, damageType)
 
         local key = string.format("%s.%s", prefix, damageType)
 
         local val = string.format("%s %s", uppercase and damageType:upper() or damageType, description)
 
         local val = string.format("%s %s", uppercase and damageType:upper() or damageType, description)
         affinities[key] = val:gsub("%s+$", "")
+
         local affinity = val:gsub("%s+$", "")
end
+
        affinities[key] = affinity
return affinities
+
    end
 +
    return affinities
 
end
 
end
  
Line 476: Line 464:
 
         text = ""
 
         text = ""
 
         for skill, level in pairs(item.requiredLevel) do
 
         for skill, level in pairs(item.requiredLevel) do
             text = text .. level .. " " .. skill:gsub("^%l", string.upper) .. "<br>"
+
             skill = skill:gsub("^%l", string.upper)
 +
            text = text .. level .. " " .. skill .. "<br>"
 
         end
 
         end
 
         text = text:gsub("<br>$", "")
 
         text = text:gsub("<br>$", "")
Line 483: Line 472:
 
     end
 
     end
  
     args[l()] = "Source"
+
     --TODO: findSource doesn't work so it's disabled for now. Does need a big rework
     args[d()] = findSource(item.id)
+
    --args[l()] = "Source"
 +
     --args[d()] = findSource(item.id)
  
 
     if item.heat then
 
     if item.heat then
Line 493: Line 483:
 
     local stats = item.equipmentStats
 
     local stats = item.equipmentStats
 
     if stats then
 
     if stats then
 +
        local slot = stats.slot:gsub("^%l", string.upper)
 
         args[l()] = "Slot"
 
         args[l()] = "Slot"
         args[d()] = stats.slot and stats.slot:gsub("^%l", string.upper)
+
         args[d()] = stats.slot and slot
  
 
         if item.enchantmentTier then
 
         if item.enchantmentTier then
Line 659: Line 650:
 
         stat = stats.itemSet
 
         stat = stats.itemSet
 
         if stat then
 
         if stat then
             for _i, enchId in pairs(stat) do
+
             for _, enchId in pairs(stat) do
 
                 local enchant = getEnchantment(enchId)
 
                 local enchant = getEnchantment(enchId)
 
                 text = ""
 
                 text = ""
 
                 if enchant.setRequirements then
 
                 if enchant.setRequirements then
                     for _i2, req in pairs(enchant.setRequirements) do
+
                     for _, req in pairs(enchant.setRequirements) do
 
                         if (req.strength > 0) then
 
                         if (req.strength > 0) then
 
                             text = text .. req.count .. ", "
 
                             text = text .. req.count .. ", "
Line 710: Line 701:
  
 
         text = ""
 
         text = ""
local costs = {}
+
        local costs = {}
 
         for key, cost in pairs(craftAug.scrapping) do
 
         for key, cost in pairs(craftAug.scrapping) do
table.insert(costs, key)
+
            table.insert(costs, key)
 
         end
 
         end
-- Lua tables are unsorted, order by itemID
+
        -- Lua tables are unsorted, order by itemID
table.sort(costs)
+
        table.sort(costs)
for _, key in ipairs(costs) do
+
        for _, key in ipairs(costs) do
 
             text = text .. craftAug.scrapping[key] .. " "
 
             text = text .. craftAug.scrapping[key] .. " "
 
             text = text .. itemImage(key, true) .. "<br>"
 
             text = text .. itemImage(key, true) .. "<br>"
end
+
        end
  
 
         text = text:gsub("<br>$", "")
 
         text = text:gsub("<br>$", "")
Line 728: Line 719:
 
         local transforms = augLoot and augLoot.transforms or {}
 
         local transforms = augLoot and augLoot.transforms or {}
 
         addTransforms(args, true, transforms) --only add transforms if the item transforms by augmenting
 
         addTransforms(args, true, transforms) --only add transforms if the item transforms by augmenting
         addResearch(args, item.id, not item.blockAugmenting)
+
         addResearch(args, item)
 
     end
 
     end
  

Revision as of 18:26, 30 April 2025


local p = {}

local data_module_names = {
    item = 'Module:Items/data',
    enchantment = 'Module:Enchantment/data',
    location = 'Module:Location/data',
    craftaug = 'Module:CraftingAugmenting/data',
    farming = 'Module:Farming/data',
    augloot = 'Module:Augmenting loot/data',
}

local loaded_data_modules = {}

local headerCount = 1
local labelCount = 1
local dataCount = 1

local function h()
    local s = "header" .. headerCount
    headerCount = headerCount + 1
    labelCount = headerCount
    dataCount = headerCount
    return s
end

local function sbreak()
    local s = "sbreak" .. headerCount
    headerCount = headerCount + 1
    labelCount = headerCount
    dataCount = headerCount
    return s
end

local function l()
    local s = "label" .. labelCount
    dataCount = labelCount
    labelCount = labelCount + 1
    headerCount = labelCount
    return s
end

local function d()
    local s = "data" .. dataCount
    dataCount = dataCount + 1
    headerCount = dataCount
    labelCount = dataCount
    return s
end

local function sl()
    local s = "s" .. l()
    return s
end

local function sd()
    local s = "s" .. d()
    return s
end

function p.loadData(data_type)
    local module_name = data_module_names[data_type]
    if loaded_data_modules[module_name] == nil then
        loaded_data_modules[module_name] = mw.loadData(module_name)
    end

    return loaded_data_modules[module_name]
end

local function findItem(name)
    local lname = name:lower()

    --Remove leading and trailing spaces.
    lname = lname:gsub('^%s*(.-)%s*$', '%1')
    for key, item in pairs(p.loadData("item")) do
        if lname == item.name:lower() then
            return item
        end
    end
    return 0
end

local function getItem(id)
    return p.loadData("item")[tostring(id)]
end

local function getEnchantmentName(id)
    return p.loadData("enchantment")[tostring(id)].name
end

local function getEnchantment(id)
    return p.loadData("enchantment")[tostring(id)]
end

local function getItemName(id)
    return p.loadData("item")[tostring(id)].name
end

local function getCraftAug(id)
    return p.loadData("craftaug")[tostring(id)]
end


---@param id string|number
---@return AugmentingLoot|nil
local function getAugLoot(id)
    return p.loadData("augloot")[tostring(id)]
end

local function fullUrl(url)
    local newUrl = url
    if url:sub(1,5) == "https" then
        return newUrl
    end

    if url:sub(1,1) ~= "/" then
        newUrl = "/" .. newUrl
    end

    newUrl = "https://www.play.idlescape.com" .. newUrl
    return newUrl
end

local function icon(name, url, word)
    local s = fullUrl(url)
    s = "[[" .. name .. "|<img src=\"" .. s
    s = s .. "\" alt=\""  .. name .. "\" width=\"20\">"
    if word then
        s = s .. name
    end
    s = s .. "]]"
    return s
end

local function itemImage(id, word)
    local item = p.loadData("item")[tostring(id)]
    local url = ""
    if item.itemIcon then
        url = item.itemIcon
    else
        url = item.itemImage
    end
    return icon(item.name, url, word)
end

local function addSeparator(num)
    return tostring(tonumber(num)):reverse():gsub("(%d%d%d)","%1,"):gsub(",(%-?)$","%1"):reverse()
end

local function locationImage(id, word)
    local loc = p.loadData("location")[tostring(id)]
    local url = ""
    if loc.locationImage then
        url = loc.locationImage
    else
        return "[[" .. loc.name .. "]]"
    end
    return icon(loc.name, url, word)
end

local function gatheringSource(id)
    local s = ""
    for key, loc in pairs(p.loadData('location')) do
        if loc.loot then
            for key2, loot in pairs(loc.loot) do
                if id == loot.id then
                    s = s .. locationImage(loc.locID, true)
                    s = s .. "<br>"
                end
            end
        end
    end
    return s
end

local function farmingSource(id)
    local s = ""
    for key, item in pairs(p.loadData('item')) do
        if item.farmingStats then
            for key2, yield in pairs(item.farmingStats.yield) do
                if id == yield.id then
                    s = s .. itemImage(item.id, true)
                    s = s .. "<br>"
                end
            end
        end
    end
    return s
end

local function smithingSource(id)
    local s = ""
    local item = getItem(id)
    if item.skill == "smithing" and item.name ~= 'Ichor' then
        s = '[[Smithing]]<br>'
    end
    return s
end

local function cookingSource(id)
    local s = ""
    local item = getItem(id)
    if (item.class == "cooking-ingredient" and not item.ingredientTags) or item.class == 'cookedFish' or item.name=='Ashes' then
        s = '[[Cooking]]<br>'
    end
    return s
end

local function runecraftingSource(id)
    local s = ""
    local item = getItem(id)
    if item.class == "cloth" or (item.class == 'rune' and item.requiredResources) then
        s = '[[Runecrafting]]<br>'
    end
    return s
end

local function scrollcraftingSource(id)
    local s = ""
    local item = getItem(id)
    if item.class == "enchanted-scroll" and item.level and item.level < 100 then
        s = '[[Scrollcrafting]]<br>'
    end
    return s
end

local function craftingSource(id)
    local s = ""
    local item = getItem(id)
    if item.craftable then
        s = '[[Crafting]]<br>'
    end
    return s
end

local function findSource(id)
    local s = ""
    s = s .. gatheringSource(id)
    -- s = s .. farmingSource(id)
    s = s .. scrollcraftingSource(id)
    s = s .. runecraftingSource(id)
    s = s .. smithingSource(id)
    s = s .. craftingSource(id)
    s = s .. cookingSource(id)
    s = s:gsub("<br>$", "")
    return s
end

---@param item Item
---@return integer
local function getItemTier(item)
    local maxRequiredLevel
    if item.requiredLevel then
        for _, level in pairs(item.requiredLevel) do
            if maxRequiredLevel == nil or maxRequiredLevel < level then
                maxRequiredLevel = level
            end
        end
        maxRequiredLevel = math.floor(math.floor(maxRequiredLevel / 10))
    end
    return item.overrideItemTier or maxRequiredLevel or item.enchantmentTier or 1
end

local function getDustId(tier)
    local AFFIX_DUST_PER_ITEM_TIER = {
        550,
        550,
        550,
        551,
        552,
        553,
        554,
        554,
        554,
    }
    tier = (tonumber(tier) or 0) + 1 -- +1 for lua indexing
    tier = math.floor(tier)
    tier = math.min(math.max(tier, 1), 9)
    return AFFIX_DUST_PER_ITEM_TIER[tier]
end

---@param rarity string|nil
---@return integer
local function getScrapId(rarity)
    rarity = rarity or "common"
    local scrapIds = {
        common = 555,
        uncommon = 556,
        rare = 557,
        epic = 558,
        legendary = 559
    }
    return scrapIds[rarity]
end

---@param args table
---@param title string
---@param id string|number|false|nil # Primary loot id, skipped if falsy
---@param scrapping table|nil # data.chance, data.itemID (AugmentingLoot scrappingSuccess or scrappingFail)
local function addScrapping(args, title, id, scrapping)
    local text = id and (itemImage(id, true) .. "<br>") or ""
    if scrapping then
        if scrapping.chance and scrapping.chance ~= 1 then
            text = text .. scrapping.chance * 100 .. "% "
        end
        if scrapping.minimum and scrapping.maximum then
            text = text .. scrapping.minimum .. "–" .. scrapping.maximum .. " "
        end
        text = text .. itemImage(scrapping.itemID, true)
    end
    args[sl()] = title
    args[sd()] = text
end

---@param args table
---@param transforms ItemTransform[]
local function addTransforms(args, augTransform, transforms)
    local text = ""
    for _, transform in ipairs(transforms) do
        if (transform.augmentingTransform or false) == augTransform then
            if transform.augmentingTransform then
                text = text .. "At level " .. (transform.augmentationLevel or 1) .. " to "
            else
                text = text .. transform.chance * 100 .. "% "
            end
            text = text .. itemImage(transform.newItemID, true) .. "<br>"
        end
    end
    text = text:gsub("<br>$", "")
    args[sbreak()] = "yes"
    args[sl()] = "Transforms"
    args[sd()] = text
end

---@param args table
---@param item Item
---@return boolean
local function addResearch(args, item)
--local function addResearch(args, id, addHeader)
    local craftAug = getCraftAug(item.id)
    local augLoot = getAugLoot(tostring(item.id))
    if item and craftAug then
        if not item.blockAugmenting then
            args[h()] = "Research"
        end
        local tier = getItemTier(item)
        local dustId = getDustId(tier)
        local success = augLoot and augLoot.scrappingSuccess or nil
        addScrapping(args, "Success", (not item.noDustFromResearching and dustId), success)
        local scrapId = item.researchesIntoDust and dustId or getScrapId(item.rarity)
        local fail = augLoot and augLoot.scrappingFail or nil
        addScrapping(args, "Fail", (not item.noScrapFromResearching and scrapId), fail)
        local transforms = augLoot and augLoot.transforms or {}
        addTransforms(args, false, transforms)
        return true
    end
    return false
end

---@param prefix 'offensiveDamageAffinity'|'offensiveAccuracyAffinityRating'|'defensiveDamageAffinity'
---@param description string|nil
---@param uppercase boolean|nil
---@return table<string, string>
local function generateAffinityDictionary(prefix, description, uppercase)
    description = description or ""
    uppercase = uppercase or false

    local allDamageTypes = {
        "Melee",
        "Magic",
        "Range",
        "Piercing",
        "Blunt",
        "Slashing",
        "Chaos",
        "Nature",
        "Fire",
        "Ice",
        "Lightning",
        "Poison",
        "Typeless",
        "Heal"
    }

    local affinities = {}
    for _, damageType in ipairs(allDamageTypes) do
        local key = string.format("%s.%s", prefix, damageType)
        local val = string.format("%s %s", uppercase and damageType:upper() or damageType, description)
        local affinity = val:gsub("%s+$", "")
        affinities[key] = affinity
    end
    return affinities
end

local function getLabelFromAugBonus(bonusName)
    local equipmentStatsToLabelMapping = {
        ["weaponBonus.strength"] = "Strength",
        ["weaponBonus.intellect"] = "Intellect",
        ["weaponBonus.dexterity"] = "Dexterity",
        ["offensiveCritical.chance"] = "Crit Chance",
        ["offensiveCritical.damageMultiplier"] = "Crit Mult",
        ["armorBonus.protection"] = "Protection",
        ["armorBonus.resistance"] = "Resistance",
        ["armorBonus.agility"] = "Agility",
        ["armorBonus.stamina"] = "Stamina",
        ["defensiveCritical.chance"] = "Crit Avoidance",
        ["defensiveCritical.damageMultiplier"] = "Crit Reduction",
        ["toolBoost.fishing"] = "Fishing",
        ["toolBoost.fishingBaitPower"] = "Bait Power",
        ["toolBoost.fishingReelPower"] = "Reel Power",
        ["toolBoost.fishingRarityPower"] = "Bonus Rarity",
        ["toolBoost.mining"] = "Mining",
        ["toolBoost.foraging"] = "Foraging",
        ["toolBoost.farming"] = "Farming",
        ["toolBoost.cooking"] = "Cooking",
        ["toolBoost.smithing"] = "Smithing",
        ["toolBoost.enchanting"] = "Enchanting",
        ["toolBoost.runecrafting"] = "Runecrafting"
    }
    for k, v in pairs(generateAffinityDictionary("offensiveDamageAffinity", "") or {}) do
        equipmentStatsToLabelMapping[k] = v
    end
    for k, v in pairs(generateAffinityDictionary("offensiveAccuracyAffinityRating", "Accuracy") or {}) do
        equipmentStatsToLabelMapping[k] = v
    end
    for k, v in pairs(generateAffinityDictionary("defensiveDamageAffinity", "") or {}) do
        equipmentStatsToLabelMapping[k] = v
    end

    return equipmentStatsToLabelMapping[bonusName] or bonusName
end

local function createInfobox(item)
    local args = {}
    local url = ""
    local text = ""
    args.autoheaders = "y"
    args.subbox = "no"
    args.bodystyle = " "
    args.title = item.name

    if item.itemIcon then
        url = item.itemIcon
    else
        url = item.itemImage
    end
    args.image = "<img src=\"" .. fullUrl(url) .. "\" width=\"150\">"

    if item.value then
        args[l()] = icon('Gold', "/images/gold_coin.png")
        args[d()] = addSeparator(item.value)
    end

    if item.tradeable then
        args[l()] = icon('Market', "/images/ui/marketplace_icon.png")
        local market = require("Module:Market")["_price"]({item.name, 1, 1})
        if market then
            args[d()] = addSeparator(market)
        else
            args[d()] = "Yes"
        end
    end

    if item.requiredLevel then
        text = ""
        for skill, level in pairs(item.requiredLevel) do
            skill = skill:gsub("^%l", string.upper)
            text = text .. level .. " " .. skill .. "<br>"
        end
        text = text:gsub("<br>$", "")
        args[l()] = "Level Required"
        args[d()] = text
    end

    --TODO: findSource doesn't work so it's disabled for now. Does need a big rework
    --args[l()] = "Source"
    --args[d()] = findSource(item.id)

    if item.heat then
        args[l()] = itemImage(2)
        args[d()] = addSeparator(item.heat)
    end

    local stats = item.equipmentStats
    if stats then
        local slot = stats.slot:gsub("^%l", string.upper)
        args[l()] = "Slot"
        args[d()] = stats.slot and slot

        if item.enchantmentTier then
            args[l()] = "Enchantment Slots"
            args[d()] = item.enchantmentTier
        end

        if item.forcedEnchant then
            args[l()] = "Enchantments"
            args[d()] = "[[" .. getEnchantmentName(item.forcedEnchant) .. "]]"
        end

        if stats.toolBoost then
            text = ""
            for key, stat in pairs(stats.toolBoost) do
                if stat.boost ~= 0 then
                    text = text .. stat.boost .. " "
                    text = text .. getLabelFromAugBonus("toolBoost." .. stat.skill) .. "<br>"
                end
            end
            text = text:gsub("<br>$", "")
            args[l()] = "Stats"
            args[d()] = text
        end

        if stats.oneHanded == false then
            args[l()] = "Two-handed"
            args[d()] = "Yes"
        end

        if stats.attackSpeed then
            args[l()] = "Attack Speed"
            args[d()] = stats.attackSpeed
        end


        args[h()] = "Offensive Stats"

        stat = stats.offensiveCritical
        if stat then
            args[l()] = "Crit Chance"
            args[d()] = stat.chance * 100 .. "%"
            args[l()] = "Crit Multiplier"
            args[d()] = stat.damageMultiplier
        end

        stat = stats.weaponBonus
        if stat then
            args[sl()] = "Str"
            args[sd()] = stat.strength
            args[sl()] = "Int"
            args[sd()] = stat.intellect
            args[sl()] = "Dex"
            args[sd()] = stat.dexterity
        end

        args[h()] = "Offensive Affinity"
        stat = stats.offensiveDamageAffinity
        if stat then
            args[sl()] = "Melee"
            args[sd()] = stat.Melee and stat.Melee * 100 - 100 .. "%"
            args[sl()] = "Magic"
            args[sd()] = stat.Magic and stat.Magic * 100 - 100 .. "%"
            args[sl()] = "Range"
            args[sd()] = stat.Range and stat.Range * 100 - 100 .. "%"
            args[sbreak()] = "yes"

            args[sl()] = "Piercing"
            args[sd()] = stat.Piercing and stat.Piercing * 100 - 100 .. "%"
            args[sl()] = "Blunt"
            args[sd()] = stat.Blunt and stat.Blunt * 100 - 100 .. "%"
            args[sl()] = "Slashing"
            args[sd()] = stat.Slashing and stat.Slashing * 100 - 100 .. "%"
            args[sl()] = "Fire"
            args[sd()] = stat.Fire and stat.Fire * 100 - 100 .. "%"
            args[sl()] = "Ice"
            args[sd()] = stat.Ice and stat.Ice * 100 - 100 .. "%"
            args[sl()] = "Nature"
            args[sd()] = stat.Nature and stat.Nature * 100 - 100 .. "%"
            args[sl()] = "Chaos"
            args[sd()] = stat.Chaos and stat.Chaos * 100 - 100 .. "%"
            args[sbreak()] = "yes"
        end

        args[h()] = "Accuracy"
        stat = stats.offensiveAccuracyAffinityRating
        if stat then
            args[sl()] = "Melee"
            args[sd()] = stat.Melee
            args[sl()] = "Magic"
            args[sd()] = stat.Magic
            args[sl()] = "Range"
            args[sd()] = stat.Range
            args[sbreak()] = "yes"

            args[sl()] = "Piercing"
            args[sd()] = stat.Piercing
            args[sl()] = "Blunt"
            args[sd()] = stat.Blunt
            args[sl()] = "Slashing"
            args[sd()] = stat.Slashing
            args[sl()] = "Fire"
            args[sd()] = stat.Fire
            args[sl()] = "Ice"
            args[sd()] = stat.Ice
            args[sl()] = "Nature"
            args[sd()] = stat.Nature
            args[sl()] = "Chaos"
            args[sd()] = stat.Chaos
            args[sbreak()] = "yes"
        end

        args[h()] = "Defensive Stats"

        stat = stats.defensiveCritical
        if stat then
            args[l()] = "Crit Avoidance"
            args[d()] = stat.chance * 100 .. "%"
            args[l()] = "Crit Reduction"
            args[d()] = stat.damageMultiplier
        end

        stat = stats.armorBonus
        if stat then
            args[sl()] = "Protection"
            args[sd()] = stat.protection
            args[sl()] = "Resistance"
            args[sd()] = stat.resistance
            args[sl()] = "Agility"
            args[sd()] = stat.agility
            args[sl()] = "Stamina"
            args[sd()] = stat.stamina
        end

        args[h()] = "Defensive Affinity"
        stat = stats.defensiveDamageAffinity
        if stat then
            args[sl()] = "Melee"
            args[sd()] = stat.Melee and stat.Melee * 100 - 100 .. "%"
            args[sl()] = "Magic"
            args[sd()] = stat.Magic and stat.Magic * 100 - 100 .. "%"
            args[sl()] = "Range"
            args[sd()] = stat.Range and stat.Range * 100 - 100 .. "%"
            args[sbreak()] = "yes"

            args[sl()] = "Piercing"
            args[sd()] = stat.Piercing and stat.Piercing * 100 - 100 .. "%"
            args[sl()] = "Blunt"
            args[sd()] = stat.Blunt and stat.Blunt * 100 - 100 .. "%"
            args[sl()] = "Slashing"
            args[sd()] = stat.Slashing and stat.Slashing * 100 - 100 .. "%"
            args[sl()] = "Fire"
            args[sd()] = stat.Fire and stat.Fire * 100 - 100 .. "%"
            args[sl()] = "Ice"
            args[sd()] = stat.Ice and stat.Ice * 100 - 100 .. "%"
            args[sl()] = "Nature"
            args[sd()] = stat.Nature and stat.Nature * 100 - 100 .. "%"
            args[sl()] = "Chaos"
            args[sd()] = stat.Chaos and stat.Chaos * 100 - 100 .. "%"
            args[sbreak()] = "yes"
        end

        args[h()] = "Set Bonus"
        stat = stats.itemSet
        if stat then
            for _, enchId in pairs(stat) do
                local enchant = getEnchantment(enchId)
                text = ""
                if enchant.setRequirements then
                    for _, req in pairs(enchant.setRequirements) do
                        if (req.strength > 0) then
                            text = text .. req.count .. ", "
                        end
                    end
                    text = text:sub(1, -3)
                    text = "[" .. text .. "]"
                    args[sl()] = "[[" .. enchant.name .. "]] " .. text
                    args[sd()] = enchant.desc
                    args[sbreak()] = "yes"
                end
            end
        end
    end

    local ammoStat = item.ammunitionMults

    if ammoStat then
        args[h()] = "Ammo Stats"
        args[sl()] = 'Type'
        args[sd()] = ammoStat.style
        args[sl()] = 'Damage'
        args[sd()] = ammoStat.damageMult .. 'x'
        args[sl()] = 'Accuracy'
        args[sd()] = ammoStat.accuracyMult .. 'x'
    end

    if item.blockAugmenting then
        args[h()] = "Research"
    else
        args[h()] = "Augmentation"
    end
    local craftAug = getCraftAug(item.id)

    if craftAug and craftAug.scrapping then
        if item.equipmentStats then
            text = ""
            for key, bonus in pairs(item.equipmentStats.augmentationBonus) do
                text = text .. "+" .. bonus.value .. " "
                text = text .. getLabelFromAugBonus(bonus.stat) .. "<br>"
            end
            text = text:gsub("<br>$", "")
            args[sl()] = "Aug Bonus"
            args[sd()] = text
        end

        text = ""
        local costs = {}
        for key, cost in pairs(craftAug.scrapping) do
            table.insert(costs, key)
        end
        -- Lua tables are unsorted, order by itemID
        table.sort(costs)
        for _, key in ipairs(costs) do
            text = text .. craftAug.scrapping[key] .. " "
            text = text .. itemImage(key, true) .. "<br>"
        end

        text = text:gsub("<br>$", "")
        args[sl()] = "Cost"
        args[sd()] = text

        local augLoot = getAugLoot(item.id)
        local transforms = augLoot and augLoot.transforms or {}
        addTransforms(args, true, transforms) --only add transforms if the item transforms by augmenting
        addResearch(args, item)
    end

    args[h()] = "Cooking"

    if item.size then
        args[l()] = "Size"
        args[d()] = item.size
    end

    if item.difficulty then
        args[l()] = "Difficulty"
        args[d()] = item.difficulty
    end

    if item.ingredientTags then
        text = ""
        for key, tag in pairs(item.ingredientTags) do
            text = text .. tag .. "<br>"
        end
        text = text:gsub("<br>$", "")
        args[l()] = "Category"
        args[d()] = text
    end

    if item.cookingEnchantment then
        args[l()] = "Buff"
        args[d()] = getEnchantmentName(item.cookingEnchantment)
    end

    args[h()] = "Seeds"

    local farming = item.farmingStats
    if farming then
        local farmingData = p.loadData('farming')
        local seedData = farmingData[item.id]
        if seedData then
            text = ""
            for key, yield in pairs(seedData) do
                text = text .. yield.min .. "-" .. yield.max .. " "
                text = text .. itemImage(yield.id, true)
                if yield.chance ~= 1 then
                    text = text .. " " .. tonumber(string.format('%.2f', yield.chance * 100)) .. "%"
                end
                text = text .. "<br>"
            end
            text = text:gsub("<br>$", "")
            args[l()] = "Level Required"
            args[d()] = farming.requiredLevel
            args[l()] = "Experience"
            args[d()] = addSeparator(farming.experience)
            args[l()] = "Plot Size"
            args[d()] = farming.height .. "x" .. farming.width
            args[l()] = "Harvest Time"
            args[d()] = farming.time .. " minutes"
            args[l()] = "Yield"
            args[d()] = text
        end
    end

    local args2 = {}

    args2.subbox = "yes"
    args2.bodystyle = "padding: 0.5em; margin:auto; font-style:italic; font-size:110%; text-align: center"
    args2.data1 = item.extraTooltipInfo
    args[h()] = "Tooltip"
    args[d()] = require('Module:Infobox').infobox(args2)

    for key, data in pairs(args) do
        if string.find(key, "data") then
            args[key] = tostring(data)
        end
    end

    return require('Module:Infobox').infobox(args)
end

function p.item(frame)
    local args = frame:getParent().args
    return p._item(args)
end

function p._item(args)
    local name = ""
    local item = 0
    local infobox = ""

    if args[1] then
        name = args[1]
    else
        name = mw.title.getCurrentTitle().text
    end

    item = findItem(name)

    if item == 0 then
        return "<div style=\"color:red\"> No item named '" .. name .. "'</div>. The Module:Items/data maybe outdated."
    end

    infobox = createInfobox(item)
    return infobox
end

return p