Module:Item/recipes

From Path of Exile 2 Wiki
Revision as of 19:57, 25 May 2021 by Vinifera7 (talk | contribs) (Fated unique item prophecies have either 'Fated' or 'AddsSpecificMod' in their prophecy_id field)
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]


Lua logo

This module depends on the following other modules:

This submodule of Module:Item contains configuration and functions for item recipes.

-------------------------------------------------------------------------------
-- 
-- Upgrade paths for Module:Item2
-- 
-------------------------------------------------------------------------------

local m_cargo = require('Module:Cargo')
local m_util = require('Module:Util')

-- Lazy loading
local f_modifier_link -- require('Module:Modifier table').modifier_link

local m_game = mw.loadData('Module:Game')

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = mw.loadData('Module:Item2/config')

local i18n = cfg.i18n.upgrade_paths

-- ----------------------------------------------------------------------------
-- Helper functions 
-- ----------------------------------------------------------------------------

local h = {}

-- Lazy loading for Module:Modifier table
function h.modifier_link(args)
    if not f_modifier_link then
        f_modifier_link = require('Module:Modifier table').modifier_link
    end
    return f_modifier_link(args)
end

h.conditions = {}
h.conditions.factory = {}

function h.conditions.factory.arg(args)
    -- Required:
    --  arg: The argument to check against
    --  One must be specified
    --   value: check whether the argument equals this value
    --   values: check whether the argument is in this list of values
    --   values_assoc: check whether the argument is in this associative table
    --
    -- Optional:
    --  negate: negates the check against the value, i.e. whether the value is not equal or not in the list/table.
    if args.negate == nil then
        args.negate = false
    end
    
    -- Inner type of function depending on whether to check a single value, a list of values or an associative list of values
    local inner
    if args.value ~= nil then
        inner = function (tpl)
            return tpl == args.value
        end
    elseif args.values ~= nil then
        inner = function (tpl)
            for _, value in ipairs(args.values) do
                if tpl == value then
                    return true
                end
            end
            return false
        end
    elseif args.values_assoc ~= nil then
        inner = function(tpl) 
            return args.values_assoc[tpl] ~= nil
        end
    else
        error(string.format('Missing inner comparision function. Args: %s', mw.dumpObject(args)))
    end
    
    -- Outer type of function depending on whether to check a single value or against a table
    return function (tpl_args, frame)
        local tpl_value = tpl_args[args.arg]
        local rtr
        if type(tpl_value) == 'table' then
            rtr = false
            for key, value in pairs(tpl_value) do
                if type(key) == 'number' then
                    rtr = rtr or inner(value)
                else
                    rtr = rtr or inner(key)
                end
            end
            
         else
            rtr = inner(tpl_value)
         end
         
         if args.negate then
            rtr = not rtr
         end
         
         return rtr
     end
end

function h.conditions.factory.league(args)
    return function (tpl_args, frame)
        for _, league in ipairs(tpl_args.drop_leagues or {}) do
            if league == args.league then
                return true
            end
        end
        return false
    end
end

h.conditions.normal = h.conditions.factory.arg{arg='rarity_id', value='normal'}
h.conditions.unique = h.conditions.factory.arg{arg='rarity_id', value='unique'}

-- ----------------------------------------------------------------------------
-- Additional configuration
-- ----------------------------------------------------------------------------

local c = {}

-- Default for all entries, but can be disabled by specific ones.
c.automatic_upgraded_from_defaults = {
    is_drop_restricted = h.conditions.factory.arg{arg='is_drop_restricted', value=false},
    is_corrupted = h.conditions.factory.arg{arg='is_corrupted', value=false},
}
-- Order matters!
-- Put most specific outcome at the top and the least specific at the bottom.
c.automatic_upgraded_from = {
--[[
    {
        defaults = {
            arg_key = function (tpl_args, frame) end,
        },
        condition = {
            function (tpl_args, frame) end,
        },
        text = '',
        groups = {
            {
                name = '',
                item_id = '',
                amount = 0,
                notes = '',
            },
        },
    },
    ]]
    --
    -- Item base specific
    --
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Amulets/Amulet9'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.agate_amulet))
        end,
        groups = {
            {
                -- Lysah's Respite
                item_id = "Metadata/Items/DivinationCards/DivinationCardLysahsRespite",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Rings/Ring15'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.unset_ring))
        end,
        groups = {
            {
                -- The Penitent
                item_id = "Metadata/Items/DivinationCards/DivinationCardThePenitent",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Rings/Ring4'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.gold_ring))
        end,
        groups = {
            {
                -- Glimmer of Hope
                item_id = "Metadata/Items/DivinationCards/DivinationCardGlimmerOfHope",
                amount = 8,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Rings/Ring8'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.prismatic_ring))
        end,
        groups = {
            {
                -- Hope
                item_id = "Metadata/Items/DivinationCards/DivinationCardHope",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='metadata_id', values={'Metadata/Items/Rings/Ring12', 'Metadata/Items/Rings/Ring13', 'Metadata/Items/Rings/Ring14'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('rare', i18n.two_stone_ring))
        end,
        groups = {
            {
                -- Lantador's Lost Love
                item_id = "Metadata/Items/DivinationCards/DivinationCardLantadorsLostLove",
                amount = 7,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', values={'Metadata/Items/Rings/Ring12', 'Metadata/Items/Rings/Ring13', 'Metadata/Items/Rings/Ring14'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.two_stone_ring))
        end,
        groups = {
            {
                -- Heterochromia
                item_id = "Metadata/Items/DivinationCards/DivinationCardHeterochromia",
                amount = 2,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Weapons/OneHandWeapons/OneHandMaces/Sceptre11'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.crystal_sceptre))
        end,
        groups = {
            {
                -- Light and Truth (Свет и правда)
                item_id = "Metadata/Items/DivinationCards/DivinationCardLightAndTruth",
                amount = 2,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Flasks/FlaskUtility5'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.granite_flask))
        end,
        groups = {
            {
                -- Earth Drinker (Пьющий землю)
                item_id = "Metadata/Items/DivinationCards/DivinationCardEarthDrinker",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Armours/Helmets/HelmetStrDex10'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.nightmare_bascinet))
        end,
        groups = {
            {
                -- The Gladiator (Гладиатор)
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheGladiator",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Belts/Belt1'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.rustic_sash))
        end,
        groups = {
            {
                -- The Standoff (Противоборство)
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheStandoff",
                amount = 3,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Jewels/JewelTimeless'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.timeless_jewel))
        end,
        groups = {
            {
                -- Peaceful Moments (Безмятежные минуты)
                item_id = "Metadata/Items/DivinationCards/DivinationCardPeacefulMoments",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_drop_restricted = false,
        },
        condition = {
            h.conditions.factory.arg{arg='base_item_id', value='Metadata/Items/Currency/CurrencyItemisedProphecy'},
            function (tpl_args, frame)
                local patterns = {'Fated', 'AddsSpecificMod'}
                for _, p in ipairs(patterns) do
                    if string.find(tpl_args.prophecy_id, p, 1, true) then
                        return true
                    end
                end
                return false
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('prophecy', i18n.fated_unique_item_prophecy))
        end,
        groups = {
            {
                -- Akil's Prophecy
                item_id = "Metadata/Items/DivinationCards/DivinationCardAkilsProphecy",
                amount = 3,
            },
        },
    },
    -- 
    -- Item name
    -- 
    {
        defaults = {
            is_drop_restricted = false,
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='name', value=i18n.precursors_emblem},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.precursors_emblem))
        end,
        groups = {
            {
                -- Remembrance (Поминовение)
                item_id = "Metadata/Items/DivinationCards/DivinationCardRemembrance",
                amount = 8,
            },
        },
    },
    -- 
    -- Item name like
    -- 
    {
        condition = {
            h.conditions.unique,
            function (tpl_args, frame)
                return string.find(tpl_args.name, i18n.atziri, 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, i18n.atziri)))
        end,
        groups = {
            {
                -- The Admirer
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheAdmirer",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            function (tpl_args, frame)
                return string.find(tpl_args.name, i18n.doedre, 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, i18n.doedre)))
        end,
        groups = {
            {
                -- Doedre's Madness
                item_id = "Metadata/Items/DivinationCards/DivinationCardDoedresMadness",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            function (tpl_args, frame)
                return string.find(tpl_args.name, i18n.shavronne, 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, i18n.shavronne)))
        end,
        groups = {
            {
                -- The Aesthete
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheAesthete",
                amount = 8,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            function (tpl_args, frame)
                return string.find(tpl_args.name, i18n.rigwald, 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, i18n.rigwald)))
        end,
        groups = {
            {
                -- The Wolf
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheWolf",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            function (tpl_args, frame)
                return string.find(tpl_args.name, i18n.lioneye, 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, i18n.lioneye)))
        end,
        groups = {
            {
                -- The Lion
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheLion",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_drop_restricted = false,
        },
        condition = {
            h.conditions.unique,
            function (tpl_args, frame)
                return string.find(tpl_args.name, i18n.farrul, 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, i18n.farrul)))
        end,
        groups = {
            {
                -- Council of Cats (Кошачий совет)
                item_id = "Metadata/Items/DivinationCards/DivinationCardCouncilOfCats",
                amount = 4,
            },
        },
    },
    -- 
    -- League-specific items
    --
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.league{league = m_game.constants.leagues['Nemesis'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Nemesis'].name)))
        end,
        groups = {
            {
                -- The Valkyrie
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheValkyrie",
                amount = 8,
               
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.league{league = m_game.constants.leagues['Nemesis'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Nemesis'].name)))
        end,
        groups = {
            {
                -- The Undaunted
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheUndaunted",
                amount = 5,
               
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.league{league = m_game.constants.leagues['Beyond'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Beyond'].name)))
        end,
        groups = {
            {
                -- The Calling
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheCalling",
                amount = 6,
               
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.league{league = m_game.constants.leagues['Breach'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Breach'].name)))
        end,
        groups = {
            {
                -- The Breach
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheBreach",
                amount = 4,
               
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.league{league = m_game.constants.leagues['Delve'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Delve'].name)))
        end,
        groups = {
            {
                -- Alone in the Darkness
                item_id = "Metadata/Items/DivinationCards/DivinationCardAloneInTheDarkness",
                amount = 5,
               
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.league{league = m_game.constants.leagues['Bestiary'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Bestiary'].name)))
        end,
        groups = {
            {
                -- Boon of the First Ones
                item_id = "Metadata/Items/DivinationCards/DivinationCardBoonOfTheFirstOnes",
                amount = 6,
            },
        },
    },
    {
        defaults = {
            is_drop_restricted = false,
        },
        condition = {
            h.conditions.unique, 
            h.conditions.factory.league{league = m_game.constants.leagues['Metamorph'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', string.format(i18n.fmt.x_item, m_game.constants.leagues['Metamorph'].name)))
        end,
        groups = {
            {
                -- Haunting Shadows (Преследующие тени)
                item_id = "Metadata/Items/DivinationCards/DivinationCardHauntingShadows",
                amount = 4,
            },
        },
    },
    {
        defaults = {
            is_drop_restricted = false,
        },
        condition = {
            h.conditions.normal, 
            h.conditions.factory.league{league = m_game.constants.leagues['Abyss'].name},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('rare', string.format(i18n.fmt.x_item, m_game.constants.leagues['Abyss'].name)))
        end,
        groups = {
            {
                -- Abyssal Incubator (Инкубатор Бездны)
                item_id = "Metadata/Items/Currency/CurrencyIncubationAbyss",
                amount = 1,
            },
        },
    },
    --
    -- Subset of item class
    --
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Jewel'},
            function(tpl_args, frame)
                -- Get Primordial modifier from stats
                if tpl_args._stats.primordial_jewel_count then
                    return true
                end
                return false
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, string.format('%s %s', m_util.html.poe_color('mod', i18n.primordial), m_util.html.poe_color('unique', i18n.jewel)))
        end,
        groups = {
            {
                -- The Primordial (Первородный)
                item_id = "Metadata/Items/DivinationCards/DivinationCardThePrimordial",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_drop_restricted = false,
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='is_talisman', value=true},
            h.conditions.factory.arg{arg='talisman_tier', value=1},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('rare', i18n.tier_1_talisman))
        end,
        groups = {
            {
                -- Call to the First Ones
                item_id = "Metadata/Items/DivinationCards/DivinationCardCallToTheFirstOnes",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_drop_restricted = false,
            is_corrupted = false,
        },
        condition = {
            h.conditions.factory.arg{arg='is_talisman', value=true},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, i18n.talisman)
        end,
        groups = {
            {
                -- Primal Incubator
                item_id = "Metadata/Items/Currency/CurrencyIncubationTalismans",
                amount = 1,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='MapFragment'},
            h.conditions.factory.arg{arg='tags', value='breachstone'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', i18n.breachstone))
        end,
        groups = {
            {
                -- The Obscured
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheObscured",
                amount = 7,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='is_essence', value=true},
            h.conditions.factory.arg{arg='essence_level', value=7},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_amount, m_util.html.poe_color('currency', i18n.deafening_essence), 3)
        end,
        groups = {
            {
                -- The Cacophony
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheCacophony",
                amount = 8,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='is_essence', value=true},
            h.conditions.factory.arg{arg='essence_level', value=6},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_amount, m_util.html.poe_color('currency', i18n.shrieking_essence), 9)
        end,
        groups = {
            {
                -- Harmony of Souls
                item_id = "Metadata/Items/DivinationCards/DivinationCardHarmonyOfSouls",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='is_essence', value=true},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_amount, m_util.html.poe_color('currency', i18n.essence), 3)
        end,
        groups = {
            {
                -- Three Voices
                item_id = "Metadata/Items/DivinationCards/DivinationCardThreeVoices",
                amount = 3,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
            h.conditions.factory.arg{arg='map_tier', value=5},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', string.format(i18n.fmt.tier_x_map, 5)))
        end,
        groups = {
            {
                -- Cartographer's Delight
                item_id = "Metadata/Items/DivinationCards/DivinationCardCartographersDelight",
                amount = 3,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
            h.conditions.factory.arg{arg='map_tier', value=14},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', string.format(i18n.fmt.tier_x_map, 14)))
        end,
        groups = {
            {
                -- The Surveyor
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheSurveyor",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
            h.conditions.factory.arg{arg='map_tier', value=15},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', string.format(i18n.fmt.tier_x_map, 15)))
        end,
        groups = {
            {
                -- Lost Worlds
                item_id = "Metadata/Items/DivinationCards/DivinationCardLostWorlds",
                amount = 8,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
            h.conditions.factory.arg{arg='map_tier', value=15},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('rare', string.format(i18n.fmt.tier_x_map, 15)))
        end,
        groups = {
            {
                -- The Trial
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheTrial",
                amount = 7,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
            h.conditions.factory.arg{arg='map_tier', value=16},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_unidentified_corrupted, m_util.html.poe_color('rare', string.format(i18n.fmt.tier_x_map, 16)))
        end,
        groups = {
            {
                -- Left to Fate
                item_id = "Metadata/Items/DivinationCards/DivinationCardLeftToFate",
                amount = 4,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Active Skill Gem'},
            h.conditions.factory.arg{arg='gem_tags', value=m_game.constants.item.gem_tags.golem.tag},
            h.conditions.factory.arg{arg='max_level', value=20},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('gem', string.format(i18n.fmt.level_x_y_gem, 21, m_game.constants.item.gem_tags.golem.tag)))
        end,
        groups = {
            {
                -- The Rite of Elements
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheRiteOfElements",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Active Skill Gem'},
            h.conditions.factory.arg{arg='gem_tags', value=m_game.constants.item.gem_tags.vaal.tag},
        },
        text = function(tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('gem', string.format(i18n.fmt.superior_x_gem_q_y, m_game.constants.item.gem_tags.vaal.tag, 20)))
        end,
        groups = {
            {
                -- Volatile Power
                item_id = "Metadata/Items/DivinationCards/DivinationCardVolatilePower",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='MapFragment'},
            function (tpl_args, frame)
                return string.find(tpl_args.metadata_id, 'Scarab', 1, true)
            end,
            h.conditions.factory.arg{arg='tags', value='gilded_scarab'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', i18n.gilded_scarab))
        end,
        groups = {
            {
                -- More is Never Enough
                item_id = "Metadata/Items/DivinationCards/DivinationCardMoreIsNeverEnough",
                amount = 7,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='MapFragment'},
            function (tpl_args, frame)
                return string.find(tpl_args.metadata_id, 'Scarab', 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', i18n.scarab))
        end,
        groups = {
            {
                -- Cameria's Cut
                item_id = "Metadata/Items/DivinationCards/DivinationCardCameriasCut",
                amount = 2,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='MapFragment'},
            function (tpl_args, frame)
                return string.find(tpl_args.metadata_id, 'Scarab', 1, true)
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', i18n.scarab))
        end,
        groups = {
            {
                -- Skittering Incubator
                item_id = "Metadata/Items/Currency/CurrencyIncubationScarabs",
                amount = 1,
            },
        },
    },
    --
    -- Single item class
    --
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='UniqueFragment'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.harbinger_fragment))
        end,
        groups = {
            {
                -- The Messenger
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheMessenger",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Body Armour'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.body_armour))
        end,
        groups = {
            {
                -- The Body
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheBody",
                amount = 4,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Shield'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.shield))
        end,
        groups = {
            {
                -- The Mercenary
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheMercenary",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Claw'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.claw))
        end,
        groups = {
            {
                -- The Wolverine
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheWolverine",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', values={'Dagger', 'Rune Dagger'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.dagger))
        end,
        groups = {
            {
                -- Assassin's Favour
                item_id = "Metadata/Items/DivinationCards/DivinationCardAssassinsFavour",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Sceptre'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_shaper, m_util.html.poe_color('magic', string.format('%s %s', i18n.sceptre, h.modifier_link{id='LocalIncreasedAttackSpeed8'})))
        end,
        groups = {
            {
                -- The Lord of Celebration
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheLordOfCelebration",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Bow'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.bow))
        end,
        groups = {
            {
                -- Hunter's Resolve
                item_id = "Metadata/Items/DivinationCards/DivinationCardHuntersResolve",
                amount = 8,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Jewel'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.jewel))
        end,
        groups = {
            {
                -- The Garish Power
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheGarishPower",
                amount = 4,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Jewel'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.jewel))
        end,
        groups = {
            {
                -- The Eye of the Dragon
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheEyeOfTheDragon",
                amount = 10,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Amulet'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='IncreasedEnergyShieldPercent7'}, i18n.amulet)))
        end,
        groups = {
            {
                -- The Sigil
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheSigil",
                amount = 3,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Amulet'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('rare', i18n.amulet))
        end,
        groups = {
            {
                -- The Warden
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheWarden",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Ring'},
            function (tpl_args, frame)
                return tpl_args.drop_level <= 83
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 83, m_util.html.poe_color('magic', string.format('%s %s', i18n.ring, h.modifier_link{id='ChaosResist6'})))
        end,
        groups = {
            {
                -- The Lord in Black
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheLordInBlack",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Ring'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 100, m_util.html.poe_color('rare', i18n.ring))
        end,
        groups = {
            {
                -- The Opulent
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheOpulecent",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Ring'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.ring))
        end,
        groups = {
            {
                -- Hubris
                item_id = "Metadata/Items/DivinationCards/DivinationCardHubris",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Ring'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.ring))
        end,
        groups = {
            {
                -- Blind Venture
                item_id = "Metadata/Items/DivinationCards/DivinationCardBlindVenture",
                amount = 7,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Belt'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.belt))
        end,
        groups = {
            {
                -- The Wretched
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheWretched",
                amount = 6,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Map'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.map))
        end,
        groups = {
            {
                -- The Encroaching Darkness
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheEncroachingDarkness",
                amount = 8,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Map'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.map))
        end,
        groups = {
            {
                -- Otherworldly Incubator
                item_id = "Metadata/Items/Currency/CurrencyIncubationUniqueMaps",
                amount = 8,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Wand'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.wand))
        end,
        groups = {
            {
                -- The Traitor
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheTraitor",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', values={'Staff', 'Warstaff'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.staff))
        end,
        groups = {
            {
                -- The Tower
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheTower",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Staff', 'Warstaff'}},
            function (tpl_args, frame)
                return tpl_args.drop_level <= 66
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_link_item_level_y, 5, 66, m_util.html.poe_color('normal', i18n.staff))
        end,
        groups = {
            {
                -- The Flora's Gift
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheFlorasGift",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Staff', 'Warstaff'}},
            function (tpl_args, frame)
                return tpl_args.drop_level <= 55
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_link_item_level_y, 6, 55, m_util.html.poe_color('normal', i18n.staff))
        end,
        groups = {
            {
                -- The Dark Mage
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheDarkMage",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Body Armour'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_link_item_level_y, 6, 100, m_util.html.poe_color('normal', i18n.body_armour))
        end,
        groups = {
            {
                -- The Dapper Prodigy
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheDapperProdigy",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Body Armour'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_x_link, 6, m_util.html.poe_color('normal', i18n.body_armour))
        end,
        groups = {
            {
                -- The Chains that Bind
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheChainsThatBind",
                amount = 11,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Support Skill Gem'},
            h.conditions.factory.arg{arg='max_level', value=20},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('gem', string.format(i18n.fmt.level_x_y_gem, 20, m_game.constants.item.gem_tags.support.tag)))
        end,
        groups = {
            {
                -- Gift of the Gemling Queen
                item_id = "Metadata/Items/DivinationCards/DivinationCardGiftOfTheGemlingQueen",
                amount = 9,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Support Skill Gem'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('gem', string.format(i18n.fmt.superior_x_gem_q_y, m_game.constants.item.gem_tags.support.tag, 23)))
        end,
        groups = {
            {
                -- Dialla's Subjugation
                item_id = "Metadata/Items/DivinationCards/DivinationCardDiallasSubjugation",
                amount = 7,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Body Armour'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 100, m_util.html.poe_color('rare', i18n.body_armour))
        end,
        groups = {
            {
                -- Destined to Crumble
                item_id = "Metadata/Items/DivinationCards/DivinationCardDestinedToCrumble",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('normal', i18n.map))
        end,
        groups = {
            {
                -- Boundless Realms
                item_id = "Metadata/Items/DivinationCards/DivinationCardBoundlessRealms",
                amount = 4,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Map'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('rare', i18n.map))
        end,
        groups = {
            {
                -- The Explorer
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheExplorer",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', value='Gloves'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.gloves))
        end,
        groups = {
            {
                -- Mitts
                item_id = "Metadata/Items/DivinationCards/DivinationCardMitts",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='DivinationCard'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('divination', i18n.divination_card))
        end,
        groups = {
            {
                -- The Gambler
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheGambler",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='DivinationCard'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('divination', i18n.divination_card))
        end,
        groups = {
            {
                -- Stacked Deck
                item_id = "Metadata/Items/DivinationCards/DivinationCardDeck",
                amount = 1,
            },
        },
    },
    --
    -- Multiple item classes
    --
    {
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', values={'One Hand Axe', 'Two Hand Axe'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.axe))
        end,
        groups = {
            {
                -- The Battle Born
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheBattleBorn",
                amount = 5,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', value='Active Skill Gem'},
            h.conditions.factory.arg{arg='gem_tags', value=m_game.constants.item.gem_tags.aura.tag},
            h.conditions.factory.arg{arg='max_level', value=20},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('gem', string.format(i18n.fmt.level_x_y_gem, 21, m_game.constants.item.gem_tags.aura.tag)))
        end,
        groups = {
            {
                -- The Wilted Rose
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheWiltedRose",
                amount = 7,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.gems.keys},
            h.conditions.factory.arg{arg='max_level', value=20},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('gem', string.format(i18n.fmt.level_x_gem, 20)))
        end,
        groups = {
            {
                -- The Fox
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheFox",
                amount = 6,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.gems.keys},
            h.conditions.factory.arg{arg='gem_tags', value=m_game.constants.item.gem_tags.minion.tag},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('gem', string.format(i18n.fmt.superior_x_gem_q_y, m_game.constants.item.gem_tags.minion.tag, 20)))
        end,
        groups = {
            {
                -- The Summoner
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheSummoner",
                amount = 6,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.gems.keys},
            -- since this is checking against an array, it will check if it contains the "Spell" value
            h.conditions.factory.arg{arg='gem_tags', value=m_game.constants.item.gem_tags.spell.tag},
            h.conditions.factory.arg{arg='max_level', value=20},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('gem', string.format(i18n.fmt.level_x_y_gem, 21, m_game.constants.item.gem_tags.spell.tag)))
        end,
        groups = {
            {
                --The Cataclysm
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheCataclysm",
                amount = 13,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.gems.keys},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('gem', string.format(i18n.fmt.superior_gem_q_x, 20)))
        end,
        groups = {
            {
                -- Gemcutter's Promise
                item_id = "Metadata/Items/DivinationCards/DivinationCardGemcuttersPromise",
                amount = 3,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.armor.keys},
            h.conditions.factory.arg{arg='tags', value='int_armour'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='LocalIncreasedEnergyShieldPercentAndStunRecovery6'}, i18n.armour)))
        end,
        groups = {
            {
                -- The Inoculated
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheInoculated",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.armor.keys},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('magic', string.format('%s %s', i18n.life, i18n.armour)))
        end,
        groups = {
            {
                -- The Carrion Crow
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheCarrionCrow",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Ring', 'Amulet', 'Belt'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_shaper_item_level_x, 100, m_util.html.poe_color('rare', i18n.jewellery))
        end,
        groups = {
            {
                -- Perfection
                item_id = "Metadata/Items/DivinationCards/DivinationCardPerfection",
                amount = 5,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Ring', 'Amulet', 'Belt'}},
            function (tpl_args, frame)
                return tpl_args.drop_level <= 79
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 79, m_util.html.poe_color('rare', i18n.jewellery))
        end,
        groups = {
            {
                -- The Lover
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheLover",
                amount = 2,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Ring', 'Amulet'}},
            function (tpl_args, frame)
                return tpl_args.drop_level <= 85
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 85, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='ReduceGlobalFlatManaCostStrIntMasterVendor'}, i18n.jewellery)))
        end,
        groups = {
            {
                -- Blessing of God
                item_id = "Metadata/Items/DivinationCards/DivinationCardBlessingOfGod",
                amount = 3,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Ring', 'Amulet'}},
            function (tpl_args, frame)
                return tpl_args.drop_level <= 76
            end,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 76, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='AddedLightningDamage9'}, i18n.jewellery)))
        end,
        groups = {
            {
                --Struck by Lightning
                item_id = "Metadata/Items/DivinationCards/DivinationCardStruckByLightning",
                amount = 3,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Two Hand Sword', 'Two Hand Axe', 'Two Hand Mace', 'Staff', 'Bow', 'Warstaff'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 100, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='LocalIncreasedPhysicalDamagePercent8'}, i18n.two_hand_weapon)))
        end,
        groups = {
            {
                -- Merciless Armament
                item_id = "Metadata/Items/DivinationCards/DivinationCardMercilessArmament",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Thrusting One Hand Sword', 'One Hand Sword', 'One Hand Axe', 'One Hand Mace', 'Sceptre', 'Dagger', 'Claw', 'Wand', 'Rune Dagger'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 100, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='LocalIncreasedPhysicalDamagePercent8'}, i18n.one_hand_weapon)))
        end,
        groups = {
            {
                -- The Jester
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheJester",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values={'Sceptre', 'Wand', 'Rune Dagger'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 100, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='SpellDamageOnWeapon8_'}, i18n.one_hand_weapon)))
        end,
        groups = {
            {
                -- The Road to Power
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheRoadToPower",
                amount = 7,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', values={'Thrusting One Hand Sword', 'One Hand Sword', 'Two Hand Sword'}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.sword))
        end,
        groups = {
            {
                -- The Gentleman
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheGentleman",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.weapons.keys},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_item_level_x, 100, m_util.html.poe_color('magic',string.format('%s %s', h.modifier_link{id='LocalIncreasedPhysicalDamagePercent8'}, i18n.weapon)))
        end,
        groups = {
            {
                -- The Tyrant
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheTyrant",
                amount = 9,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.weapons.keys},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('magic', string.format('%s %s', i18n.weapon, h.modifier_link{id='StrIntMasterItemGenerationCanHaveMultipleCraftedMods'})))
        end,
        groups = {
            {
                -- The Web
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheWeb",
                amount = 8,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', values_assoc=cfg.class_groups.weapons.keys},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_corrupted, m_util.html.poe_color('unique', i18n.weapon))
        end,
        groups = {
            {
                -- Atziri's Arsenal
                item_id = "Metadata/Items/DivinationCards/DivinationCardAtzirisArsenal",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.normal,
            -- specifically exclude crit flasks
            h.conditions.factory.arg{arg='class_id', values_assoc={['LifeFlask'] = true, ['ManaFlask'] = true, ['HybridFlask'] = true, ['UtilityFlask'] = true}},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('magic', string.format('%s %s', h.modifier_link{id='FlaskChanceRechargeOnCrit1'}, i18n.flask)))
        end,
        groups = {
            {
                -- The Surgeon
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheSurgeon",
                amount = 4,
            },
        },
    },
    {
        condition = {
            h.conditions.unique, 
            function (tpl_args, frame)
                return tpl_args.drop_leagues ~= nil and #tpl_args.drop_leagues ~= 0
            end
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.league_specific_item))
        end,
        groups = {
            {
                -- Time-Lost Relic
                item_id = "Metadata/Items/DivinationCards/DivinationCardTimeLostRelic",
                amount = 10,
            },
        },
    },
    --
    -- Generic items
    --
    {
        condition = {
            h.conditions.normal,
            -- should exclude all items that can't be rare
            h.conditions.factory.arg{arg='drop_rarities_ids', value='rare'},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_elder_item_level_x, 100, m_util.html.poe_color('rare', i18n.item))
        end,
        groups = {
            {
                -- The Hale Heart
                item_id = "Metadata/Items/DivinationCards/DivinationCardTheHaleHeart",
                amount = 4,
            },
        },
    },
    {
        defaults = {
            is_corrupted = false,
        },
        condition = {
            h.conditions.unique,
            h.conditions.factory.arg{arg='class_id', values_assoc={['LifeFlask'] = true, ['ManaFlask'] = true, ['HybridFlask'] = true, ['UtilityFlask'] = true, ['UtilityFlaskCritical'] = true, ['Map'] = true}, negate=true},
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random_two_implicit_corrupted, m_util.html.poe_color('unique', i18n.item))
        end,
        groups = {
            {
                -- Arrogance of the Vaal
                item_id = "Metadata/Items/DivinationCards/DivinationCardArroganceOfTheVaal",
                amount = 8,
            },
        },
    },
    {
        condition = {
            h.conditions.unique,
        },
        text = function (tpl_args, frame)
            return string.format(i18n.fmt.random, m_util.html.poe_color('unique', i18n.item))
        end,
        groups = {
            {
                -- Jack in the Box
                item_id = "Metadata/Items/DivinationCards/DivinationCardJackInTheBox",
                amount = 4,
            },
        },
    },
    -- TODO: The void?
}

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

function p.process_upgraded_from(tpl_args, frame)
    local query_data = {
        id = {},
        name = {},
        page = {},
    }
    local sets = {}
    
    -- ------------------------------------------------------------------------
    -- Manual data
    -- ------------------------------------------------------------------------
    local setid = #sets + 1
    local set
    repeat
        local prefix = string.format('upgraded_from_set%s_', setid)
        local groupid = 1
        local group
        set = {
            groups = {},
            text = m_util.cast.text(tpl_args[prefix .. 'text']),
            automatic = false,
        }
        repeat 
            local group_prefix = string.format('%sgroup%s_', prefix, groupid)
            group = {
                item_name = tpl_args[group_prefix .. 'item_name'],
                item_id = tpl_args[group_prefix .. 'item_id'], 
                item_page = tpl_args[group_prefix .. 'item_page'], 
                amount = tonumber(tpl_args[group_prefix .. 'amount']),
                notes = m_util.cast.text(tpl_args[group_prefix .. 'notes']),
            }
            
            if group.item_name ~= nil or group.item_id ~= nil or group.item_page ~= nil then
                if group.amount == nil then
                    error(string.format(i18n.errors.missing_amount, group_prefix .. 'amount'))
                else
                    for key, array in pairs(query_data) do
                        local value = group['item_' .. key]
                        if value then
                            if array[value] then
                                table.insert(array[value], {setid, groupid})
                            else
                                array[value] = {{setid, groupid}, }
                            end
                        end
                    end
                    set.groups[#set.groups+1] = group
                end
            end
            
            groupid = groupid + 1
        until group.item_name == nil and group.item_id == nil and group.item_page == nil
        
        -- set was empty, can terminate safely
        if #set.groups == 0 then
            set = nil
        else
            setid = setid + 1
            sets[#sets+1] = set
        end
    until set == nil
    -- ------------------------------------------------------------------------
    -- Automatic
    -- ------------------------------------------------------------------------
    
    --
    --  maps
    --
    local automatic_index = #sets + 1
    -- TODO: 3.9.0 Unsure how this works yet, so disabled for now
    --[[if tpl_args.atlas_connections and tpl_args.rarity_id == "normal" then
        local results = m_cargo.query(
            {'items', 'maps'},
            {'items._pageName',  'items.name'},
            {
                join='items._pageID=maps._pageID',
                where=string.format('items.class_id = "Map" AND items.rarity_id = "normal" AND maps.tier < %s AND items._pageName IN ("%s")', tpl_args.map_tier, table.concat(tpl_args.atlas_connections, '", "')),
            }
        )
        for _, row in ipairs(results) do
            sets[#sets+1] = {
                text = i18n.misc.upgraded_from_map,
                groups = {
                    {
                        item_name = row['items.name'],
                        item_page = row['items._pageName'],
                        amount = 3,
                        notes = nil,
                    },
                },
                automatic = true,
            }
        end
    end]]
    
    --
    -- oils
    --
    if tpl_args._flags.is_blight_item and tpl_args.blight_item_tier > 1 then
        local results = m_cargo.query(
            {'items', 'blight_items'},
            {'items._pageName',  'items.name'},
            {
                join='items._pageID=blight_items._pageID',
                where=string.format('blight_items.tier = %s', tpl_args.blight_item_tier - 1),
            }
        )
        for _, row in ipairs(results) do
            sets[#sets+1] = {
                text = nil,
                groups = {
                    {
                        item_name = row['items.name'],
                        item_page = row['items._pageName'],
                        amount = 3,
                        notes = nil,
                    },
                },
                automatic = true,
            }
        end
    end
    
    
    --
    -- essences
    --
    
    -- exclude remnant of corruption via type
    if tpl_args.is_essence and tpl_args.essence_type > 0 then 
        local results = m_cargo.query(
            {'items', 'essences'},
            {
                'items._pageName',  
                'items.name', 
                'items.metadata_id',
                'essences.category',
                'essences.type',
            },
            {
                join='items._pageID=essences._pageID',
                where=string.format([[
                        (essences.category="%s" AND essences.level = %s)
                        OR (essences.type = %s AND essences.level = %s)
                        OR items.metadata_id = 'Metadata/Items/Currency/CurrencyCorruptMonolith'
                        OR (%s = 6 AND essences.type = 5 AND essences.level >= 5) 
                    ]], 
                    tpl_args.essence_category, tpl_args.essence_level - 1, 
                    tpl_args.essence_type - 1, tpl_args.essence_level,
                    -- special case for corruption only essences
                    tpl_args.essence_type
                ),
                orderBy='essences.level ASC, essences.type ASC',
            }
        )
        
        local remnant = results[1]
        if remnant['items.metadata_id'] ~= 'Metadata/Items/Currency/CurrencyCorruptMonolith' then
            error(string.format('Something went seriously wrong here. Got results: %s', mw.dumpObject(results)))
        end
        for i=2, #results do
            local row = results[i]
            if row['essences.category'] == tpl_args.essence_category then
                -- 3 to 1 recipe
                sets[#sets+1] = {
                    automatic = true,
                    text = nil,
                    groups = {
                        {
                            item_id = row['items.metadata_id'],
                            item_page = row['items._pageName'],
                            item_name = row['items.name'],
                            amount = 3,
                        },
                    },
                }
                -- corruption +1
                sets[#sets+1] = {
                    automatic = true,
                    text = i18n.essence_plus_one_level,
                    groups = {
                        {
                            item_id = row['items.metadata_id'],
                            item_page = row['items._pageName'],
                            item_name = row['items.name'],
                            amount = 1,
                        },
                        {
                            item_id = remnant['items.metadata_id'],
                            item_page = remnant['items._pageName'],
                            item_name = remnant['items.name'],
                            amount = 1,
                        },
                    },
                }
            elseif tonumber(row['essences.type']) == tpl_args.essence_type - 1 then
                -- corruption type change
                sets[#sets+1] = {
                    automatic = true,
                    text = i18n.essence_type_change,
                    groups = {
                        {
                            item_id = row['items.metadata_id'],
                            item_page = row['items._pageName'],
                            item_name = row['items.name'],
                            amount = 1,
                        },
                        {
                            item_id = remnant['items.metadata_id'],
                            item_page = remnant['items._pageName'],
                            item_name = remnant['items.name'],
                            amount = 1,
                        },
                    },
                }
            end
        end
    end
    
    -- data based on mapping
    if tpl_args.drop_enabled and not tpl_args.upgraded_from_disabled then
        for _, data in ipairs(c.automatic_upgraded_from) do
            data.defaults = data.defaults or {}
            local continue = true
            for key, value in pairs(c.automatic_upgraded_from_defaults) do
                local func
                local v = data.defaults[key]
                if v == false then
                    -- check is disabled specifically, continue
                elseif v == nil then
                    func = value
                elseif type(v) == 'function' then
                    func = v
                else 
                    error(string.format('Invalid value for defaults at data %s', mw.dumpObject(data)))
                end
                if func then
                    continue = func(tpl_args, frame) and continue
                    if not continue then
                        break
                    end 
                end
            end
            for _, condition in ipairs(data.condition) do
                continue = condition(tpl_args, frame) and continue
                if not continue then
                    break
                end
            end
            
            if continue then
                sets[#sets+1] = {
                    automatic = true,
                    text = data.text(tpl_args, frame),
                    groups = data.groups,
                }
                for groupid, row in ipairs(data.groups) do
                    if query_data['id'][row.item_id] then
                        table.insert(query_data['id'][row.item_id], {#sets, groupid})
                    else
                        query_data['id'][row.item_id] = {{#sets, groupid}, }
                    end
                end
            end
        end
    end
    
    if #sets == 0 then
        return
    end
    --
    -- Fetch item data in a single query to sacrifice database load with a lot of upgraded_from references
    --
    local query_data_array = {
        id = {},
        name = {},
        page = {},
    }
    local query_fields = {
        id = 'items.metadata_id',
        page = 'items._pageName',
        name = 'items.name',
    }
    local where = {}
    local expected_count = 0
    for key, thing_array in pairs(query_data) do
        for thing, _ in pairs(thing_array) do
            table.insert(query_data_array[key], thing)
        end
        if #query_data_array[key] > 0 then
            expected_count = expected_count + #query_data_array[key]
            local q_data = table.concat(query_data_array[key], '", "')
            table.insert(where, string.format('%s IN ("%s")', query_fields[key], q_data))
        end
    end
    local results = m_cargo.query(
        {'items'},
        {'items._pageName',  'items.name', 'items.metadata_id'},
        {
            where=table.concat(where, ' OR '),
        }
    )
    
    for _, row in ipairs(results) do
        for key, thing_array in pairs(query_data) do
            local set_groups = thing_array[row[query_fields[key]]]
            if set_groups then
                for _, set_group in ipairs(set_groups) do
                    local entry = sets[set_group[1]].groups[set_group[2]]
                    for entry_key, data_key in pairs(query_fields) do
                        -- metadata_id may be nil, since we don't know them for unique items
                        if row[data_key] then
                            entry['item_' .. entry_key] = row[data_key]
                        end
                    end
                end
                -- set this to nil for error checking in later step
                thing_array[row[query_fields[key]]] = nil
            end
        end
    end
    
    -- sbow the broken references if needed
    if #results ~= expected_count then
        -- query data was pruned of existing keys earlier, so only broken keys remain
        for key, array in pairs(query_data) do
            for thing, set_groups in pairs(array) do
                for _, set_group in ipairs(set_groups) do
                    tpl_args._flags.broken_upgraded_from_reference = true
                    tpl_args._errors[#tpl_args._errors+1] = string.format(i18n.errors.upgraded_from_broken_reference, string.format('upgraded_from_set%s_group%s_item_%s', set_group[1], set_group[2], key), thing)
                end
            end
        end
    end
    
    --
    -- Check for duplicates
    --
    local delete_sets = {}
    for i=automatic_index, #sets do
        for j=1, automatic_index-1 do
            if #sets[i].groups == #sets[j].groups then
                local match = true
                for row_id, row in ipairs(sets[i].groups) do
                    -- Only the fields from the database query are matched since we can be sure they're correct. Other fields may be subject to user error.
                    for _, key in ipairs({'item_id', 'item_name', 'item_page'})  do
                        match = match and (row[key] == sets[j].groups[row_id][key])
                    end
                end
                if match then
                    tpl_args._flags.duplicate_upgraded_from_reference = true
                    tpl_args._errors[#tpl_args._errors+1] = string.format(i18n.errors.upgraded_from_duplicate, j)
                    delete_sets[#delete_sets+1] = j 
                end
            end
        end
    end
    
    for offset, index in ipairs(delete_sets) do
        table.remove(sets, index-(offset-1))
    end
    --
    -- Set data
    -- 
    tpl_args.upgrade_from_sets = sets
    
    -- set upgraded_from data
    for i, set in ipairs(sets) do
        tpl_args._subobjects[#tpl_args._subobjects+1] = {
            _table = 'upgraded_from_sets',
            set_id = i,
            text = set.text,
            automatic = set.automatic,
        }
        
        for j, group in ipairs(set.groups) do
            tpl_args._subobjects[#tpl_args._subobjects+1] = {
                _table = 'upgraded_from_groups',
                group_id = j,
                set_id = i,
                item_name = group.item_name,
                item_id = group.item_id,
                item_page = group.item_page,
                amount = group.amount,
                notes = group.notes,
            }
        end
    end
end

--
-- Debugging
--

function p.debug_validate_auto_upgraded_from(frame)
    frame = m_util.misc.get_frame(frame)
    
    local q = {}
    local chk = {}
    for _, data in ipairs(c.automatic_upgraded_from) do
        for _, group in ipairs(data.groups) do
            q[#q+1] = group.item_id
            chk[group.item_id] = {
                amount=group.amount,
                text=data.text({}, frame),
            }
        end
    end
    
    local results = m_cargo.array_query{
        tables={'items', 'stackables'},
        fields={'items.name', 'items.class_id', 'items.description', 'stackables.stack_size'},
        id_field='items.metadata_id',
        id_array=q,
        query={
            join='items._pageName=stackables._pageName',
        },
    }
    
    for _, row in ipairs(results) do
        if row['items.class_id'] == 'DivinationCard' and chk[row['items.metadata_id']].amount ~= tonumber(row['stackables.stack_size']) then
            mw.logObject(string.format('Amount mismatch %s, expected %s', row['items.metadata_id'], row['stackables.stack_size']))
        end
    end
    
    tbl = mw.html.create('table')
    tbl:attr('class', 'wikitable sortable')
    for _, row in ipairs(results) do
        tbl
            :tag('tr')
                :tag('td')
                    :wikitext(row['items.name'])
                    :done()
                :tag('td')
                    :wikitext(chk[row['items.metadata_id']].text)
                    :done()
                :tag('td')
                    :wikitext(row['items.description'])
                    :done()
                :done()
    end
    
    return tostring(tbl)
end

return p