Difference between revisions of "Module:Infobox Item"

From Idlescape Wiki
Jump to navigation Jump to search
(Feature add research success and failure outputs and item transforms to both research and augmentation)
(Order aug cost)
Line 641: Line 641:
 
         end
 
         end
  
            text = ""
+
        text = ""
 +
local costs = {}
 
         for key, cost in pairs(craftAug.scrapping) do
 
         for key, cost in pairs(craftAug.scrapping) do
             text = text .. cost .. " "
+
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>"
 
             text = text .. itemImage(key, true) .. "<br>"
        end
+
end
 +
 
 
         text = text:sub(1,text:len()-4)
 
         text = text:sub(1,text:len()-4)
 
         args[sl()] = "Cost"
 
         args[sl()] = "Cost"

Revision as of 09:38, 29 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 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 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

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)
    if s:len() > 4 then
        s = s:sub(1,s:len()-4)
    end
    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
---@param scrapping table|nil # data.chance, data.itemID (AugmentingLoot scrappingSuccess or scrappingFail)
local function addScrapping(args, title, id, scrapping)
    local text = itemImage(id, true) .. "<br>"
    if scrapping then
        if scrapping.chance and scrapping.chance ~= 1 then
            text = text .. scrapping.chance * 100 .. "% "
        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 = ""
    local added = false
    for _, transform in ipairs(transforms) do
        if (transform.augmentingTransform or false) == augTransform then
            added = true
            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
    if added then
        text = text:sub(1, text:len()-4)
    end
    if text:len() > 0 then
        args[sbreak()] = "yes"
        args[sl()] = "Transforms"
        args[sd()] = text
    end
end

---@param args table
---@param id string|number
---@param addHeader boolean
---@return boolean
local function addResearch(args, id, addHeader)
    ---@type Item|nil
    local item = getItem(id)
    local craftAug = getCraftAug(id)
    local augLoot = getAugLoot(id)
    if item and craftAug then
        if addHeader then
            args[h()] = "Research"
        end
        local tier = getItemTier(item)
        local dustId = getDustId(tier)
        local success = augLoot and augLoot.scrappingSuccess or nil
        addScrapping(args, "Success", dustId, success)
        local scrapId = getScrapId(item.rarity)
        local fail = augLoot and augLoot.scrappingFail or nil
        addScrapping(args, "Fail", scrapId, fail)
        local transforms = augLoot and augLoot.transforms or {}
        addTransforms(args, false, transforms)
        return true
    end
    return false
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
            text = text .. level .. " " .. skill .. "<br>"
        end
        text = text:sub(1,text:len()-4)
        args[l()] = "Level Required"
        args[d()] = text
    end

    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
        args[l()] = "Slot"
        args[d()] = stats.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
                text = text .. stat.boost .. " "
                text = text .. stat.skill .. "<br>"
            end
            text = text:sub(1,text:len()-4)
            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 _i, enchId in pairs(stat) do
                local enchant = getEnchantment(enchId)
                text = ""
                if enchant.setRequirements then
                    for _i2, 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 .. bonus.stat:sub(bonus.stat:find('%.')+1,bonus.stat:len()) .. "<br>"
            end
            text = text:sub(1,text:len()-4)
            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:sub(1,text:len()-4)
        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.id, not item.blockAugmenting)
    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:sub(1,text:len()-4)
        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:sub(1,text:len()-4)
            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