Module:Infobox Item
Jump to navigation
Jump to search
local p = {} local 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', cooking = 'Module:CookingList/data', ability = 'Module:Abilities/data', gameshop = 'Module:GameShopItems/data', itemsets = 'Module:Item sets', } local loaded_modules = {} local headerCount = 1 local labelCount = 1 local dataCount = 1 local cups = { 16000,16001,16002,16003,16004,16005,16006,16007, 16008,16009,16010,16011,16012,16013,16014,16015 } ---Checks if a table contains a specific value. ---@param t table ---@param value any ---@return boolean local function hasValue(t, value) for _, v in pairs(t) do if value == v then return true end end return false end ---Creates a deep copy of a tables and functions. ---Does not work with functions with upvalues and maybe userdata. ---@generic T ---@param t T The value to copy. ---@return T # A new value that is a deep copy of the original. local function copyTable(t) if type(t) ~= "table" then return t elseif type(t) == "function" then return loadstring(string.dump(t)) end local copy = {} for k, v in pairs(t) do copy[k] = copyTable(v) end return copy end ---Formats a number up to a given number of decimal places, removing trailing zeros. ---@param num number|string The number to format. ---@param digits number The number of decimal places to include (must be a non-negative integer). ---@return string|nil # The formatted number as a string or nil if the input is not a valid number. local function toFixed(num, digits) local n = tonumber(num) if not n then return nil end digits = math.max(0, math.floor(digits)) local formatted = string.format("%." .. digits .. "f", n):gsub("%.?0+$", "") return formatted end 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 ---@param data_type string ---@return table function p.loadData(data_type) local module_name = module_names[data_type] if loaded_modules[module_name] == nil then local status, module = pcall(mw.loadData, module_name) if not status then status, module = pcall(require, module_name) end if status then loaded_modules[module_name] = module else error("Failed to load module: " .. data_type .. " / " .. tostring(module_name) .. "\n" .. module) end end return loaded_modules[module_name] end local function getItem(id) return p.loadData("item")[tostring(id)] end local function getEnchant(id) return p.loadData("enchantment")[tostring(id)] end local function getEnchantName(id) local enchantment = getEnchant(id) return enchantment and enchantment.name end local function getModuleItemSet() return p.loadData("itemsets") 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 CookingItem|nil local function getCookingIngredient(id) return p.loadData("cooking")[tostring(id)] end ---@param id string|number|nil # Returns all if nil ---@return AugmentingLoot|table<string, AugmentingLoot>|nil local function getAugLoot(id) local augloot = p.loadData("augloot") return id == nil and augloot or augloot[tostring(id)] end ---@param id string|number ---@return Ability|nil local function getAbility(id) return p.loadData("ability")[tostring(id)] end ---@param id string|number ---@return {id: number, min: number, max: number, chance: number}[]|nil local function getYield(id) return p.loadData("farming")[tostring(id)] end ---@param itemId string|number # Item ID, is not the same as the `id` in the shop. ---@return nil local function getGeneralShopItem(itemId) local id = tonumber(itemId) if not id then return nil end local items = p.loadData("gameshop") for _, item in pairs(items) do if item.itemID == id then return item end end end ---@return Item|nil 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 end local function dottedTooltip(name, tooltip) return string.format( '<span class="rt-commentedText tooltip tooltip-dotted" title="%s">%s</span>', tooltip, name ) end ---Formats the given chance as a string presentation of percentage or 10k/100k fraction. ---@param chance number The chance to format. 1 equals 100%. ---@return string # The formatted chance as a string. local function formatChance(chance) local sign = chance < 0 and "-" or "" local abs = math.abs(chance) if abs < 0.001 then return sign .. toFixed(abs * 10000, 1) .. "/10k" elseif abs < 0.0001 then return sign .. toFixed(abs * 100000, 1) .. "/100k" end return toFixed(chance * 100, 3) .. "%" 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, size) size = size or 20 url = fullUrl(url) return string.format( '[[%s|<img src="%s" alt="%s" width="%s">%s]]', name, url, name, size, word and name or "" ) end ---@param id string|number # Item ID. ---@param word boolean # If true, show the name of the item after the icon. ---@return string # The icon wikitext. local function itemImage(id, word) local item = p.loadData("item")[tostring(id)] if not item then return "" end local url = "" if item.itemIcon then url = item.itemIcon else url = item.itemImage end return icon(item.name, url, word) end ---Creates an Wikitext link with an image inside a div. ---@param title string # The title of the link. ---@param imageAttributes table # The html attributes for the image. ---@param divCSS table # The CSS styles for the div. ---@return string # The Wikitext link with the image. local function createWikitextImage(title, imageAttributes, divCSS) divCSS = divCSS or {} divCSS.display = divCSS.display or "inline-block" --Makes the div same size as the image local e = mw.html.create("div") :css(divCSS) :tag("img") :attr(imageAttributes) :done() e = tostring(e):gsub("<img(.-) */>", "<img%1>") return string.format('[[%s|%s]]', title, e) end ---Creates a Wikitext containing links and images for the abilities. ---@param abilities number[]|{id: number}[]|nil Array of ability IDs. ---@param unique boolean|nil Skips duplicate abilities if truthy, otherwise creates an element for each ability in order. ---@return string # The Wikitext containing links and images for the abilities. local function createAbilityRotation(abilities, unique) if not abilities then return "" end local colors = { Melee = "red", Range = "green", Magic = "blue", } local seenIds = {} local images = {} for _, id in ipairs(abilities) do if type(id) == "table" then id = id.id end if not seenIds[id] then seenIds[id] = unique and true or false local ability = getAbility(id) if ability then -- Check if the ability page exists and add tooltip if not local exists = mw.title.new(ability.abilityName) exists = exists and exists.exists local attributes = { src = fullUrl(ability.abilityImage), alt = ability.abilityName .. ". " .. ability.description, width = 30, class = not exists and "tooltip" or nil, title = not exists and ability.abilityName .. ". " .. ability.description or nil, } local css = { ["box-shadow"] = "0 0 2px 1px " .. (colors[ability.damageType] or "white"), background = colors[ability.damageType] or "white", margin = "5px" } table.insert(images, createWikitextImage(ability.abilityName, attributes, css)) end end end return table.concat(images, "") 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 .. formatChance(scrapping.chance) .. " " 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 # infobox args table ---@param augTransform boolean # true for augmenting, false for researching ---@param transformsFrom table<number, ItemTransform>|nil # table of source ID as key, and list of transforms as value ---@param transformsTo ItemTransform[]|nil # list of transforms local function addTransforms(args, augTransform, transformsFrom, transformsTo) if not (transformsFrom or transformsTo) then return end ---@param transform ItemTransform ---@param id number|false|nil # uses this for item's image instead of transform.newItemID local function createTransform(transform, id) if (transform.augmentingTransform or false) == augTransform then local image = itemImage(id or transform.newItemID, true) if transform.augmentingTransform then return image .. " at level " .. (transform.augmentationLevel or 1) else return formatChance(transform.chance) .. " " .. image end end end ---@param transforms table<number, ItemTransform> ---@param isKeyId boolean|nil # wether the table keys are item IDs or indicies local function createTransformList(transforms, isKeyId) local list = {} for i, transform in pairs(transforms) do local text = createTransform(transform, isKeyId and i) table.insert(list, text) end return list end args[sbreak()] = "yes" local list if transformsFrom then list = createTransformList(transformsFrom, true) args[sl()] = "Transforms from" args[sd()] = table.concat(list, "<br>") end if transformsTo then list = createTransformList(transformsTo) args[sl()] = "Transforms to" args[sd()] = table.concat(list, "<br>") end end ---@param args table ---@param item Item ---@param transformsTo ItemTransform[]|nil # list of transforms ---@param transformsFrom table<number, ItemTransform>|nil # list of transforms ---@return boolean local function addResearch(args, item, transformsTo, transformsFrom) local isCup = hasValue(cups, item.id) local canResearch = not getGeneralShopItem(item.id) or isCup local craftAug = getCraftAug(item.id) if craftAug and canResearch then local augLoot = getAugLoot(tostring(item.id)) local tier = getItemTier(item) local dustId = not item.noDustFromResearching and getDustId(tier) or nil if not item.noScrapFromResearching then local success = augLoot and augLoot.scrappingSuccess or nil addScrapping(args, "Success", dustId, success) end -- General Shop items are hardcoded to never fail (CUPS atm) if not isCup then local scrapId = item.researchesIntoDust and dustId or getScrapId(item.rarity) local fail = augLoot and augLoot.scrappingFail or nil addScrapping(args, "Fail", scrapId, fail) end end addTransforms(args, false, transformsTo, transformsFrom) return craftAug and canResearch or transformsFrom ~= nil end ---@param prefix 'offensiveDamageAffinity'|'offensiveAccuracyAffinityRating'|'defensiveDamageAffinity' ---@param description string|nil ---@param uppercase boolean|nil ---@return table<string, string> local function createAffinityDictionary(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(createAffinityDictionary("offensiveDamageAffinity", "") or {}) do equipmentStatsToLabelMapping[k] = v end for k, v in pairs(createAffinityDictionary("offensiveAccuracyAffinityRating", "Accuracy") or {}) do equipmentStatsToLabelMapping[k] = v end for k, v in pairs(createAffinityDictionary("defensiveDamageAffinity", "") or {}) do equipmentStatsToLabelMapping[k] = v end return equipmentStatsToLabelMapping[bonusName] or bonusName end ---@param item Item local function createInfobox(item) local lang = mw.language.getContentLanguage() local args = {} args.autoheaders = "y" args.subbox = "no" args.bodystyle = " " args.title = item.name local url = item.itemIcon or item.itemImage args.image = '<img src="' .. fullUrl(url) .. '" width="150">' if item.value then args[l()] = "Vendor Value" args[d()] = lang:formatNum(item.value) .. " " .. icon('Gold', "/images/gold_coin.png", false, 13) end --TODO: add this back when the market is fixed --[[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.rarity then args[l()] = "Rarity" args[d()] = item.rarity:gsub("^%l", string.upper) end if item.requiredLevel then local levels = {} for skill, level in pairs(item.requiredLevel) do skill = skill:gsub("^%l", string.upper) table.insert(levels, level .. " " .. skill) end args[l()] = "Level" args[d()] = table.concat(levels, "<br>") 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, false) args[d()] = lang:formatNum(item.heat) end if item.forcedEnchant then args[l()] = "Enchantment" args[d()] = "[[" .. getEnchantName(item.forcedEnchant) .. "]]" args[l()] = "Enchantment Level" args[d()] = item.forcedEnchantAmount end if item.enchantmentTier then args[l()] = "Enchantment Slots" args[d()] = item.enchantmentTier end local stats = item.equipmentStats if stats then local slot = stats.slot and stats.slot:gsub("^%l", string.upper) args[l()] = "Slot" args[d()] = stats.slot and slot 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 .. "s" end if stats.toolBoost then args[h()] = "Tool Stats" for _, stat in ipairs(stats.toolBoost) do if stat.boost ~= 0 then local sign = stat.boost > 0 and "+" or "" local boost = sign .. stat.boost args[l()] = getLabelFromAugBonus("toolBoost." .. stat.skill) args[d()] = boost end end end args[h()] = "Offensive Stats" local stat = stats.offensiveCritical if stat then args[l()] = "Crit Chance" args[d()] = toFixed(stat.chance * 100, 3) .. "%" args[l()] = "Crit Multiplier" args[d()] = stat.damageMultiplier .. "x" 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 toFixed(stat.Melee * 100 - 100, 3) .. "%" args[sl()] = "Magic" args[sd()] = stat.Magic and toFixed(stat.Magic * 100 - 100, 3) .. "%" args[sl()] = "Range" args[sd()] = stat.Range and toFixed(stat.Range * 100 - 100, 3) .. "%" args[sbreak()] = "yes" args[sl()] = "Piercing" args[sd()] = stat.Piercing and toFixed(stat.Piercing * 100 - 100, 3) .. "%" args[sl()] = "Blunt" args[sd()] = stat.Blunt and toFixed(stat.Blunt * 100 - 100, 3) .. "%" args[sl()] = "Slashing" args[sd()] = stat.Slashing and toFixed(stat.Slashing * 100 - 100, 3) .. "%" args[sl()] = "Fire" args[sd()] = stat.Fire and toFixed(stat.Fire * 100 - 100, 3) .. "%" args[sl()] = "Ice" args[sd()] = stat.Ice and toFixed(stat.Ice * 100 - 100, 3) .. "%" args[sl()] = "Nature" args[sd()] = stat.Nature and toFixed(stat.Nature * 100 - 100, 3) .. "%" args[sl()] = "Chaos" args[sd()] = stat.Chaos and toFixed(stat.Chaos * 100 - 100, 3) .. "%" 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()] = toFixed(stat.chance * 100, 3) .. "%" 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 toFixed(stat.Melee * 100 - 100, 3) .. "%" args[sl()] = "Magic" args[sd()] = stat.Magic and toFixed(stat.Magic * 100 - 100, 3) .. "%" args[sl()] = "Range" args[sd()] = stat.Range and toFixed(stat.Range * 100 - 100, 3) .. "%" args[sbreak()] = "yes" args[sl()] = "Piercing" args[sd()] = stat.Piercing and toFixed(stat.Piercing * 100 - 100, 3) .. "%" args[sl()] = "Blunt" args[sd()] = stat.Blunt and toFixed(stat.Blunt * 100 - 100, 3) .. "%" args[sl()] = "Slashing" args[sd()] = stat.Slashing and toFixed(stat.Slashing * 100 - 100, 3) .. "%" args[sl()] = "Fire" args[sd()] = stat.Fire and toFixed(stat.Fire * 100 - 100, 3) .. "%" args[sl()] = "Ice" args[sd()] = stat.Ice and toFixed(stat.Ice * 100 - 100, 3) .. "%" args[sl()] = "Nature" args[sd()] = stat.Nature and toFixed(stat.Nature * 100 - 100, 3) .. "%" args[sl()] = "Chaos" args[sd()] = stat.Chaos and toFixed(stat.Chaos * 100 - 100, 3) .. "%" args[sbreak()] = "yes" end args[h()] = "Abilities" args[sd()] = createAbilityRotation(stats.grantedAbility, true) args[h()] = "Set Bonus" for id, desc in pairs(getModuleItemSet()._formatItemSets(stats.itemSet)) do local enchant = getEnchant(id) local counts = {} for _, req in ipairs(enchant.setRequirements) do if (req.strength > 0) then table.insert(counts, req.count) end end args[sl()] = "[[" .. enchant.name .. "]] [" .. table.concat(counts, ", ") .. "]" args[sd()] = desc args[sbreak()] = "yes" 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 ---@return table|nil local function getAugmentingByID(id) local craftAug = getCraftAug(id) if not craftAug then return nil end return craftAug and craftAug.augmenting; end ---@return table|nil local function getScrappingByID(id) local craftAug = getCraftAug(id) if not craftAug then return nil end return craftAug.scrapping or getAugmentingByID(id) end local function canAugment(id) return not item.blockAugmenting and getAugmentingByID(id) end local function canScrap(id) return not item.blockResearching and getScrappingByID(id) end --TODO: add transform source args[h()] = "Enchanting" if getScrappingByID(item.id) then if canAugment(item.id) or canScrap(item.id) then args[l()] = dottedTooltip("Base XP", "Experience is increased by 10% for every augmentation level.") args[d()] = lang:formatNum(20 * math.pow(getItemTier(item), 2)) end local craftAug = getCraftAug(item.id) local costs = {} for id, cost in pairs(craftAug.scrapping or {}) do table.insert(costs, {id = id, text = lang:formatNum(cost) .. " " .. itemImage(id, true)}) end table.sort(costs, function(a, b) return a.id < b.id end) for i = 1, #costs do costs[i] = costs[i].text end args[sl()] = "Cost" args[sd()] = table.concat(costs, "<br>") ---@type table<string, AugmentingLoot>|nil local augLoot = getAugLoot() ---@type AugmentingLoot|nil local augLootItem = augLoot and augLoot[tostring(item.id)] local transformsTo = augLootItem and augLootItem.transforms ---@type table<number, ItemTransform>|nil # the key is the id of the item that transforms local transformsFrom for id, keys in pairs(augLoot or {}) do for _, transform in ipairs(keys.transforms or {}) do if transform.newItemID == item.id then transformsFrom = transformsFrom or {} transformsFrom[tonumber(id)] = transform end end end args[h()] = "Augmentation" if canAugment(item.id) or transformsFrom then if item.equipmentStats then local bonuses = {} for _, bonus in pairs(item.equipmentStats.augmentationBonus or {}) do table.insert(bonuses, "+" .. bonus.value .. " " .. getLabelFromAugBonus(bonus.stat)) end args[sl()] = "Bonus Stats" args[sd()] = table.concat(bonuses, "<br>") end addTransforms(args, true, transformsFrom, transformsTo) end args[h()] = "Research" if canScrap(item.id) or transformsFrom then addResearch(args, item, transformsFrom, transformsTo) end end local crafting = item.craftingStats if crafting then args[h()] = "Crafting" args[l()] = "Category" args[d()] = crafting.category args[l()] = "Craftable" args[d()] = not crafting.craftable and "No" or "" args[l()] = "Level" args[d()] = crafting.level args[l()] = "Experience" args[d()] = crafting.experience and lang:formatNum(crafting.experience) or "" args[l()] = "Amount" args[d()] = crafting.multiplier args[sl()] = "Description" args[sd()] = crafting.description and '<p style="margin:auto;font-style:italic">' .. crafting.description .. '</p>' or "" end local ingredient = getCookingIngredient(item.id) if ingredient then args[h()] = "Cooking" args[l()] = "Level" args[d()] = ingredient.level args[l()] = "Difficulty" args[d()] = ingredient.difficulty args[l()] = "Size" args[d()] = ingredient.size args[l()] = "Alchemy Size" args[d()] = ingredient.alchemySize args[l()] = "Category" args[d()] = table.concat(ingredient.ingredientTags or {}, "<br>") args[l()] = "Buff" args[d()] = getEnchantName(ingredient.cookingEnchantment) or getEnchantName(ingredient.alchemyEnchantment) or "" end local farming = item.farmingStats if farming then local drops = {} local yield = copyTable(getYield(item.id) or {}) table.sort(yield, function(a, b) return a.chance > b.chance end) for _, drop in ipairs(yield) do table.insert(drops, string.format( "%s–%s %s %s%%", drop.min, drop.max, itemImage(drop.id, true), toFixed(drop.chance * 100, 2) )) end args[h()] = "Farming" args[l()] = "Level" args[d()] = farming.requiredLevel args[l()] = "Experience" args[d()] = lang:formatNum(farming.experience) args[l()] = "Plot Size" args[d()] = string.format( "%dx%d%s", farming.width, farming.height, farming.maxWidth and string.format(" – %dx%d", farming.maxWidth, farming.maxHeight) or "" ) args[l()] = "Harvest Time" args[d()] = farming.time .. " minutes" args[l()] = "Yield" args[d()] = table.concat(drops, "<br>") end if item.extraTooltipInfo then args[h()] = "Tooltip" args[d()] = '<p style="margin:auto;font-style:italic;font-size:1.2em">' .. item.extraTooltipInfo .. '</p>' end 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 = args.name or args.title or args[1] or mw.title.getCurrentTitle().text local item = findItem(name) if not item then return "<div style=\"color:red\"> No item named '" .. name .. "'</div>. The Module:Items/data maybe outdated." end return createInfobox(item) end return p