Module:Sandbox/Aurelain/ArtifactsOverview

From Heroes of Might and Magic: Olden Era Official Wiki

This module renders a giant table with all artifacts. Scroll artifacts are consolidated into 3 generic scroll artifacts (normal, enchanted and mythic). Campaign artifacts are excluded.

A hyper parameter is supported. For more information, see Module:UtilHyper/doc.

Usage

Input: {{#invoke:Sandbox/Aurelain/ArtifactsOverview|display | lang=en | hyper=Attack=Hero_Basics#Attributes}}

Output:

Artifact🗯️Descriptionupgradeslotrarityartifact_set_idgoods_valuemax_levelcost_basecost_per_level
[[|]]+10% to Persuasion Power in Diplomacy.Upgrade: +5% more Persuasion Power in Diplomacy.backlegendaryambassadors_word_item_set2400026040
[[|]]+10% to Persuasion Power in Diplomacy.Upgrade: +5% more Persuasion Power in Diplomacy.item_slotlegendaryambassadors_word_item_set2400026040
[[|]]The hero ignores the restriction on using the same Magic School twice.Cannot be upgraded.item_slotrare600013020
[[|]]+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.armorlegendaryangelic_alliance_item_set2400026040
[[|]]+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.beltlegendaryangelic_alliance_item_set2400026040
[[|]]+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.headlegendaryangelic_alliance_item_set2400026040
[[|]]+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.right_handlegendaryangelic_alliance_item_set2400026040
[[|]]+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.bootslegendaryangelic_alliance_item_set2400026040
[[|]]+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.left_handlegendaryangelic_alliance_item_set2400026040
[[|]]+1 Speed to friendly creatures.Cannot be upgraded.backepic1200014530
[[|]]+12 Defense, but –4 Attack.Upgrade: +4 more Defense.right_handlegendarybeelzebubs_blessing_item_set2400026040
[[|]]+12 Attack, but –4 Defense.Upgrade: +4 more Attack.left_handlegendarybeelzebubs_blessing_item_set2400026040
[[|]]+12 Knowledge, but –4 Spell Power.Upgrade: +4 more Knowledge.headlegendarybeelzebubs_blessing_item_set2400026040
[[|]]+12 Spell Power, but –4 Knowledge.Upgrade: +4 more Spell Power.armorlegendarybeelzebubs_blessing_item_set2400026040
[[|]]+1 Defense.Upgrade: +1 more Defense. Can be upgraded an unlimited number of times.item_slotcommon30009991515
[[|]]+1 Attack.Upgrade: +1 more Attack. Can be upgraded an unlimited number of times.item_slotcommon30009991515
[[|]]+1 Knowledge.Upgrade: +1 more Knowledge. Can be upgraded an unlimited number of times.item_slotcommon30009991515
[[|]]+1 Spell Power.Upgrade: +1 more Spell Power. Can be upgraded an unlimited number of times.item_slotcommon30009991515
[[|]]Heroic Strike restores 4 mana.Upgrade: +4 more mana.right_handrarelegendary_hero_item_set600023020
[[|]]Makes all tier-1 spells available to the hero.Cannot be upgraded.item_slotrare600013020
[[|]]Allows the hero to cast all Daylight spells.Cannot be upgraded.item_slotlegendary2400016040
[[|]]Allows the hero to cast all Nightshade spells.Cannot be upgraded.item_slotlegendary2400016040
[[|]]Allows the hero to cast all Primal spells.Cannot be upgraded.item_slotlegendary2400016040
[[|]]Allows the hero to cast all Arcane spells.Cannot be upgraded.item_slotlegendary2400016040
[[|]]Forbids the wearer and the enemy hero to cast tier-1 and 2 spells in combat.Cannot be upgraded.ringrare600013020
[[|]]+3 Defense.Cannot be upgraded.armorcommon300011510
[[|]]Makes all tier-3 spells available to the hero.Cannot be upgraded.armorepic1200014530
[[|]]+4 Knowledge.Upgrade: +2 more Knowledge. Can be upgraded an unlimited number of times.headrare60009996060
[[|]]Replaces all the hero’s spells with their Masterful versions (if they exist).Cannot be upgraded.item_slotlegendary2400016040
[[|]]Friendly creatures take –30% counterattack Damage.Upgrade: –15% less counterattack Damage.ringepicduelists_pride_item_set1200024530
[[|]]Friendly creatures can counterattack even if the attacking enemy has “Swift Strike.”Upgrade: Friendly creatures deal +5% Melee Damage.right_handcommonduelists_pride_item_set300021510
[[|]]Friendly creatures deal +10% Melee DamageUpgrade: +5% more Melee Damage.left_handrareduelists_pride_item_set600023020
[[|]]+2 Morale and Luck.Cannot be upgraded.armorrare600013020
[[|]]All negative effects on friendly creatures last for –1 round(s).Upgrade: –1 fewer round(s).beltrareelixir_of_life_item_set600023020
[[|]]Friendly creatures have +2 HP.Upgrade: +1 more HP.item_slotlegendaryelixir_of_life_item_set1200024530
[[|]]All positive effects on friendly creatures last for +1 round(s).Upgrade: +1 more round(s).ringrareelixir_of_life_item_set600023020
[[|]]item_slotrare5500100
[[|]]Produces +1 Gem, Crystal, or Mercury daily. (The resource depends on the bearer’s faction.)Cannot be upgraded.beltrare600013020
[[|]]Spells and creature abilities ignore 40% of enemy magic resistance.Upgrade: Ignore +20% more magic resistance.right_handrareethereal_knowledge_item_set600023020
[[|]]Spells cost 2 less mana.Upgrade: 1 less mana.bootscommonethereal_knowledge_item_set300021510
[[|]]+1 level(s) to all spells while worn.Upgrade: +1 more spell level(s).headlegendaryethereal_knowledge_item_set2400026040
[[|]]Whenever the opponent casts a spell, restores 40% of its cost as mana to the wearer.Upgrade: Restores 60% of the spell’s cost.armorrareethereal_knowledge_item_set600023020
[[|]]Heroic Strike kills +1 unit(s). Works on creatures up to tier 7.Upgrade: +1 more unit(s).left_handlegendarylegendary_hero_item_set2400026040
[[|]]Grants constant flight.Cannot be upgraded.backlegendary2400016040
[[|]]+1 level to all spells.Cannot be upgraded.right_handrare600013020
[[|]]The wearer keeps their army whenever they flee from battle.Cannot be upgraded.item_slotepic1200014530
[[|]]–1 Speed to all enemies.Cannot be upgraded.left_handepic1200014530
[[|]]–10% Magic Damage taken. Friendly creatures are immune to the effects of “Berserk.”Upgrade: –5% less Magic Damage taken.backraregifts_of_dwarven_lords_item_set600023020
[[|]]–20% to enemy hero Spell Power. Friendly creatures are immune to effects that prevent them from attacking.Upgrade: –10% more to enemy Spell Power.ringepicgifts_of_dwarven_lords_item_set1200024530
[[|]]–10% to enemy hero Spell Power. Friendly creatures are immune to the effects that kill additional units.Upgrade: –5% more.ringraregifts_of_dwarven_lords_item_set600023020
[[|]]–10% Magic Damage taken. Friendly creatures are immune to the effects of “Sleep.”Upgrade: –5% less Magic Damage taken.beltepicgifts_of_dwarven_lords_item_set1200024530
[[|]]Grants a random wondrous reward after a random number of upgrades.Can have unlimited upgrades.item_slotepic120009999600
[[|]]+1 sight radius.Cannot be upgraded.headcommon300011510
[[|]]While worn, grants all unlearned Subskills of the following Skills: Logistics, Battle Magic, Wisdom.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Leadership, Insight, Diplomacy, Tactics.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Luck, Economy, Daylight Magic.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Arcane Magic, Scouting, Battlecraft, Thaumaturgy.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Righteousness, Necromancy, Murmuring, Triumvirate’s Strength, Abyssal Communion, Summon Swarm.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Offense, Summon Avatar, Siegecraft, Combat.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Sorcery, Nightshade Magic, Resistance.Cannot be upgraded.unic_slotepic2100014530
[[|]]While worn, grants all unlearned Subskills of the following Skills: Defense, Primal Magic, Recruitment.Cannot be upgraded.unic_slotepic2100014530
[[|]]The cooldowns of all spells are reduced by 1 round(s).Cannot be upgraded.right_handrare600013020
[[|]]Generates 2 Focus Point(s) at the start of the battle.Upgrade: +2 more Focus Point(s).headcommoninner_song_item_set300021510
[[|]]Friendly creatures’ abilities deal +40% Damage.Upgrade: +20% more Damage.item_slotcommoninner_song_item_set300021510
[[|]]All effects applied by friendly creatures last +1 round(s).Upgrade: +1 more round(s).right_handcommoninner_song_item_set300021510
[[|]]+2 Defense.Upgrade: +1 more Defense.item_slotrarekeepers_fortitude_item_set600023020
[[|]]+2 Defense.Upgrade: +1 more Defense.ringrarekeepers_fortitude_item_set600023020
[[|]]+2 Defense.Upgrade: +1 more Defense.headcommonknights_honor_item_set300021510
[[|]]Friendly creatures deal +1 Damage.Upgrade: +1 more Damage.item_slotepicknights_honor_item_set1200023020
[[|]]+3 Attack.Upgrade: +2 more Attack.left_handrareknights_honor_item_set600023020
[[|]]+2 Attack.Upgrade: +1 more Attack.right_handcommonknights_honor_item_set300021510
[[|]]+3 Defense.Upgrade: +2 more Defense.armorrareknights_honor_item_set600023020
[[|]]+50% to all creature growth in all your cities.Cannot be upgraded.bootsepic1200014530
[[|]]Friendly creatures can still use Ranged attacks while adjacent to enemy creatures.Upgrade: Friendly creatures deal +5% Ranged Damage.backepicliving_arrows_item_set1200024530
[[|]]Friendly creatures deal +10% Ranged Damage.Upgrade: +5% more Ranged Damage.item_slotrareliving_arrows_item_set600023020
[[|]]Eliminates Ranged Damage penalty for friendly creatures.Upgrade: Friendly creatures deal +5% Ranged Damage.left_handepicliving_arrows_item_set1200024530
[[|]]Friendly creatures have +1 Initiative.Cannot be upgraded.ringepic1200014530
[[|]]Negates all effects that restrict creature rearrangement, surrendering, fleeing, and using the Spellbook and hero abilities in battle.Cannot be upgraded.beltlegendary2400016040
Magic Scrollitem_slotcommon2750100

-- Usage: {{#invoke:ArtifactsOverview|display | lang=en}}
local p = {}
local f = mw.ustring.format

-- Note: The right side is just a fallback, it will seldom be used.
local TRANSLATION_IDS = {
    wiki_none = 'None',
    wiki_artifact = 'Artifact',
    unit_window_narrative = 'Description'
}

-- Displays a variable
------------------------------------------------------------------------------------------------------------------------
local function dump(target)
    return '<pre>' .. mw.dumpObject(target) .. '</pre>'
end

-- Spawns a safe robotic url from an English name, fit for adding it before the visible name
------------------------------------------------------------------------------------------------------------------------
local function getUrl(nameEn, lang)
    if not nameEn then
        return ''
    end
    local url = nameEn
    if lang ~= 'en' then
        url = url .. '/' .. lang
    end
    return url
end

-- Retrieves the text for some specific ids from Cargo Translations.
------------------------------------------------------------------------------------------------------------------------
local function translateIds(ids, lang, extra)
    -- Key list
    local list = {}
    for key, _ in pairs(ids) do
        list[#list + 1] = key
    end
    local idListString = '"' .. table.concat(list, '", "') .. '"'

    -- Cargo
    local where = {}
    table.insert(where, 'target_id IN (' .. idListString .. ')')
    table.insert(where, 'language = "' .. lang .. '"')
    if extra then
        table.insert(where, extra)
    end
    local results = mw.ext.cargo.query('Translation', 'target_id, name', {
        where = table.concat(where, ' AND '),
        limit = 100
    })

    -- Dictionary
    local dictionary = mw.clone(ids)
    for key, value in pairs(dictionary) do
        dictionary[key] = value .. '🗯️'
    end
    for _, row in ipairs(results) do
        local name = row['name']
        if name and name ~= '' then
            dictionary[row['target_id']] = name
        end
    end
    return dictionary
end

-- Converts a dictionary into a linear array
------------------------------------------------------------------------------------------------------------------------
local function flatten(hub)
    local list = {}
    for _, entry in pairs(hub) do
        list[#list + 1] = entry
    end
    return list
end

-- Interrogates the main Cargo table
------------------------------------------------------------------------------------------------------------------------
local function queryMain(lang)
    -- fields:
    local fields = {}
    table.insert(fields, 'A.id = id');
    table.insert(fields, 'A.slot = slot');
    table.insert(fields, 'A.rarity = rarity');
    table.insert(fields, 'A.artifact_set_id = artifact_set_id');
    table.insert(fields, 'A.goods_value = goods_value');
    table.insert(fields, 'A.max_level = max_level');
    table.insert(fields, 'A.cost_base = cost_base');
    table.insert(fields, 'A.cost_per_level = cost_per_level');
    table.insert(fields, 'T.language = language');
    table.insert(fields, 'T.name = name');
    table.insert(fields, 'T.description = description');
    table.insert(fields, 'T.type = type');

    -- where:
    local where = {}
    table.insert(where, 'A.id NOT LIKE "campaign_%"')
    table.insert(where, '(T.language = "en" OR T.language = "' .. lang .. '")')

    -- query:
    return mw.ext.cargo.query('Artifact=A, Translation=T', table.concat(fields, ','), {
        join = 'A.id = T.target_id',
        where = table.concat(where, ' AND '),
        limit = 500
    })
end

-- Folds magic scrolls into 3 artifacts (normal, enchanted and mythic):
------------------------------------------------------------------------------------------------------------------------
local function consolidateScrolls(lang, results)
    local hub = {}
    for _, row in ipairs(results) do
        local id = row.id
        if id:find('magic_scroll') then
            if id:find('mythic') then
                id = 'mythic_scroll_box_artifact'
            else
                if id:find('enchanted') then
                    id = 'enchanted_magic_scroll_artifact'
                else
                    id = 'magic_scroll_artifact'
                end
            end
            local entry = hub[id]
            if not entry then
                entry = {}
                entry.id = id or ''
                entry.spells = {}
                entry.slot = row.slot or ''
                entry.rarity = row.rarity or ''
                entry.artifact_set_id = row.artifact_set_id or ''
                entry.goods_value = row.goods_value or 0
                entry.max_level = row.max_level or 0
                entry.cost_base = row.cost_base or 0
                entry.cost_per_level = row.cost_per_level or 0
                hub[id] = entry
            end
            if row.language == lang then
                entry.name = row.name or ''
            end
            if row.language == 'en' then
                entry.nameEn = row.name or ''
                local spellId = row.id:gsub('.*artifact_', '')
                table.insert(entry.spells, spellId)
            end
        end
    end
    return hub
end

-- Folds all Cargo results into a single dictionary:
------------------------------------------------------------------------------------------------------------------------
local function consolidate(lang, results)
    local hub = consolidateScrolls(lang, results)
    for _, row in ipairs(results) do
        local id = row.id
        if not id:find('magic_scroll') then
            local entry = hub[id]
            if not entry then
                entry = {}
                entry.id = id or ''
                entry.slot = row.slot or ''
                entry.rarity = row.rarity or ''
                entry.artifact_set_id = row.artifact_set_id or ''
                entry.goods_value = row.goods_value or 0
                entry.max_level = row.max_level or 0
                entry.cost_base = row.cost_base or 0
                entry.cost_per_level = row.cost_per_level or 0
                hub[id] = entry
            end
            if row.language == lang then
                entry.name = row.name or ''
                if row.type == 'artifact' then
                    entry.description = row.description or ''
                end
                if row.type == 'artifact_upgrade' then
                    entry.upgrade = row.description or ''
                end
                if row.type == 'artifact_narrative' then
                    entry.narrative = row.description or ''
                end
            end
            if row.language == 'en' then
                entry.nameEn = row.name or ''
            end
        end
    end
    return hub
end

------------------------------------------------------------------------------------------------------------------------
local function sortList(a, b)
    return a.id < b.id
end

-- Boilerplate for the header cells
------------------------------------------------------------------------------------------------------------------------
local function addTh(tr, text)
    return tr:tag('th'):wikitext(text)
end

-- Boilerplate for the body cells
------------------------------------------------------------------------------------------------------------------------
local function addTd(tr, content)
    if type(content) == "string" then
        return tr:tag('td'):wikitext(content)
    else
        return tr:tag('td'):node(content)
    end
end

-- Boilerplate for the separator rows
------------------------------------------------------------------------------------------------------------------------
local function addSeparator(htmlTable, className, content)
    htmlTable:tag('tr')
        :addClass('separator')
        :addClass(className)
        :tag('td'):attr('data-sort-value', ''):attr('colspan', 100):wikitext(content):done()
end

-- TABLE HEADER
------------------------------------------------------------------------------------------------------------------------
local function createHeader(htmlTable, words)
    local tr = htmlTable:tag('tr')
    --addTh(tr, 'id')
    addTh(tr, words.wiki_artifact):attr('colspan', 2)
    addTh(tr, words.unit_window_narrative)
    addTh(tr, 'upgrade')
    --addTh(tr, 'narrative')
    addTh(tr, 'slot')
    addTh(tr, 'rarity')
    addTh(tr, 'artifact_set_id')
    addTh(tr, 'goods_value')
    addTh(tr, 'max_level')
    addTh(tr, 'cost_base')
    addTh(tr, 'cost_per_level')
end

------------------------------------------------------------------------------------------------------------------------
local function renderIcon(id, nameEn, lang)
    local url = getUrl(nameEn, lang)
    return f('[[File:%s.png|64px|link=%s]]', id, url)
end

------------------------------------------------------------------------------------------------------------------------
local function renderName(name, nameEn, lang)
    local url = getUrl(nameEn, lang)
    return f('[[%s|%s]]', url, name)
end

-- TABLE BODY
------------------------------------------------------------------------------------------------------------------------
local function createBody(htmlTable, list, lang)
    local currentType = list[1].param1
    local currentFaction = list[1].faction
    for _, item in ipairs(list) do
        -- separators
        if item.faction ~= currentFaction then
            currentFaction = item.faction
            currentType = item.class_type
            addSeparator(htmlTable, 'separator-large', '')
        elseif item.class_type ~= currentType then
            currentType = item.class_type
            addSeparator(htmlTable, 'separator-tiny', '')
        end

        local tr = htmlTable:tag('tr'):addClass('artifact')
        --addTd(tr, item.id)
        addTd(tr, renderIcon(item.id, item.nameEn, lang))
        addTd(tr, renderName(item.name, item.nameEn, lang))
        addTd(tr, item.description)
        addTd(tr, item.upgrade)
        --addTd(tr, item.narrative)
        addTd(tr, item.slot)
        addTd(tr, item.rarity)
        addTd(tr, item.artifact_set_id)
        addTd(tr, item.goods_value)
        addTd(tr, item.max_level)
        addTd(tr, item.cost_base)
        addTd(tr, item.cost_per_level)
    end
end

-- M A I N
------------------------------------------------------------------------------------------------------------------------
function p.display(frame)
    --do return dump(frame) end

    -- Args
    local args = frame.args
    local lang = args.lang ~= '' and args.lang or 'en'
    --lang = 'ja'

    -- Language
    local words = translateIds(TRANSLATION_IDS, lang)

    -- Cargo
    local results = queryMain(lang)
    local hub = consolidate(lang, results)
    local list = flatten(hub);
    if #list == 0 then return words.wiki_none end

    -- Various manipulations
    table.sort(list, sortList)

    -- Table
    local htmlTable = mw.html.create('table')
    htmlTable:addClass('wikitable sortable')
    createHeader(htmlTable, words)
    createBody(htmlTable, list, lang)

    -- Output
    local styleTag = frame:extensionTag('templatestyles', '', { src = frame:getTitle() .. '/styles.css' })
    return '<div class="artifacts-overview">' .. styleTag .. tostring(htmlTable) .. '</div>'
end

return p