Module:Sandbox/Aurelain/ArtifactsOverview: Difference between revisions

From Heroes of Might and Magic: Olden Era Official Wiki
No edit summary
No edit summary
Line 232: Line 232:
     addTh(tr, 'rarity')
     addTh(tr, 'rarity')
     addTh(tr, 'artifact_set_id')
     addTh(tr, 'artifact_set_id')
    --addTh(tr, 'goods_value')
     addTh(tr, 'max_level')
     addTh(tr, 'max_level')
     addTh(tr, 'cost_base')
     addTh(tr, 'cost_base')
Line 276: Line 275:
         addTd(tr, item.rarity)
         addTd(tr, item.rarity)
         addTd(tr, item.artifact_set_id)
         addTd(tr, item.artifact_set_id)
        addTd(tr, item.goods_value)
         addTd(tr, item.max_level)
         addTd(tr, item.max_level)
         addTd(tr, item.cost_base)
         addTd(tr, item.cost_base)

Revision as of 11:03, 29 June 2026

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_idmax_levelcost_basecost_per_level
Ambassador's Sash+10% to Persuasion Power in Diplomacy.Upgrade: +5% more Persuasion Power in Diplomacy.000
Diplomatic Gifts+10% to Persuasion Power in Diplomacy.Upgrade: +5% more Persuasion Power in Diplomacy.000
Ancient IdolThe hero ignores the restriction on using the same Magic School twice.Cannot be upgraded.000
Armor of Wonder+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.000
Celestial Sash of Bliss+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.000
Helm of Heavenly Enlightenment+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.000
Lion's Shield of Courage+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.000
Sandals of the Saint+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.000
Sword of Judgement+2 Attack, Defense, Spell Power, and Knowledge.Upgrade: +1 more to every attribute.000
Banner of Six Winds+1 Speed to friendly creatures.Cannot be upgraded.000
Chitinous Shield+12 Defense, but –4 Attack.Upgrade: +4 more Defense.000
Demon Claw+12 Attack, but –4 Defense.Upgrade: +4 more Attack.000
Beelzebub's Boon+12 Knowledge, but –4 Spell Power.Upgrade: +4 more Knowledge.000
Heartbeat+12 Spell Power, but –4 Knowledge.Upgrade: +4 more Spell Power.000
Boreoloth's Tail+1 Defense.Upgrade: +1 more Defense. Can be upgraded an unlimited number of times.000
Boreoloth's Hand+1 Attack.Upgrade: +1 more Attack. Can be upgraded an unlimited number of times.000
Boreoloth's Head+1 Knowledge.Upgrade: +1 more Knowledge. Can be upgraded an unlimited number of times.000
Boreoloth's Heart+1 Spell Power.Upgrade: +1 more Spell Power. Can be upgraded an unlimited number of times.000
CaduceusHeroic Strike restores 4 mana.Upgrade: +4 more mana.000
Deck of SpellsMakes all tier-1 spells available to the hero.Cannot be upgraded.000
Daylight TomeAllows the hero to cast all Daylight spells.Cannot be upgraded.000
Nightshade TomeAllows the hero to cast all Nightshade spells.Cannot be upgraded.000
Primal TomeAllows the hero to cast all Primal spells.Cannot be upgraded.000
Arcane TomeAllows the hero to cast all Arcane spells.Cannot be upgraded.000
Chain LinkForbids the wearer and the enemy hero to cast tier-1 and 2 spells in combat.Cannot be upgraded.000
Chain Mail+3 Defense.Cannot be upgraded.000
Robe of EnlightenmentMakes all tier-3 spells available to the hero.Cannot be upgraded.000
Crown of the Supreme Magi+4 Knowledge.Upgrade: +2 more Knowledge. Can be upgraded an unlimited number of times.000
Demonic HeartReplaces all the hero’s spells with their Masterful versions (if they exist).Cannot be upgraded.000
Brass KnucklesFriendly creatures take –30% counterattack Damage.Upgrade: –15% less counterattack Damage.000
BucklerFriendly creatures can counterattack even if the attacking enemy has “Swift Strike.”Upgrade: Friendly creatures deal +5% Melee Damage.000
RapierFriendly creatures deal +10% Melee DamageUpgrade: +5% more Melee Damage.000
Eagle Armor+2 Morale and Luck.Cannot be upgraded.000
Flask of OblivionAll negative effects on friendly creatures last for –1 round(s).Upgrade: –1 fewer round(s).000
Lifeblood FaeFriendly creatures have +2 HP.Upgrade: +1 more HP.000
Ring of LifeAll positive effects on friendly creatures last for +1 round(s).Upgrade: +1 more round(s).000
Enchanted Magic Scroll000
Endless BagProduces +1 Gem, Crystal, or Mercury daily. (The resource depends on the bearer’s faction.)Cannot be upgraded.000
Crescent DaggerSpells and creature abilities ignore 40% of enemy magic resistance.Upgrade: Ignore +20% more magic resistance.000
Magic SlippersSpells cost 2 less mana.Upgrade: 1 less mana.000
Seven Eyes+1 level(s) to all spells while worn.Upgrade: +1 more spell level(s).000
Vortex RobeWhenever the opponent casts a spell, restores 40% of its cost as mana to the wearer.Upgrade: Restores 60% of the spell’s cost.000
ExcaliburHeroic Strike kills +1 unit(s). Works on creatures up to tier 7.Upgrade: +1 more unit(s).000
Fallen Angel WingsGrants constant flight.Cannot be upgraded.000
Fine Wand+1 level to all spells.Cannot be upgraded.000
Flag of TruceThe wearer keeps their army whenever they flee from battle.Cannot be upgraded.000
Garotte–1 Speed to all enemies.Cannot be upgraded.000
Automated Antimagic Shield–10% Magic Damage taken. Friendly creatures are immune to the effects of “Berserk.”Upgrade: –5% less Magic Damage taken.000
Crimson Resonance Controller–20% to enemy hero Spell Power. Friendly creatures are immune to effects that prevent them from attacking.Upgrade: –10% more to enemy Spell Power.000
Emerald Resonance Controller–10% to enemy hero Spell Power. Friendly creatures are immune to the effects that kill additional units.Upgrade: –5% more.000
Protective Belt–10% Magic Damage taken. Friendly creatures are immune to the effects of “Sleep.”Upgrade: –5% less Magic Damage taken.000
Golden Goose EggGrants a random wondrous reward after a random number of upgrades.Can have unlimited upgrades.000
Head Torch+1 sight radius.Cannot be upgraded.000
Holy Sigil of EridoreWhile worn, grants all unlearned Subskills of the following Skills: Logistics, Battle Magic, Wisdom.Cannot be upgraded.000
Holy Sigil of InsaraWhile worn, grants all unlearned Subskills of the following Skills: Leadership, Insight, Diplomacy, Tactics.Cannot be upgraded.000
Holy Sigil of MeareaWhile worn, grants all unlearned Subskills of the following Skills: Luck, Economy, Daylight Magic.Cannot be upgraded.000
Holy Sigil of QuixWhile worn, grants all unlearned Subskills of the following Skills: Arcane Magic, Scouting, Battlecraft, Thaumaturgy.Cannot be upgraded.000
Holy Sigil of RophWhile worn, grants all unlearned Subskills of the following Skills: Righteousness, Necromancy, Murmuring, Triumvirate’s Strength, Abyssal Communion, Summon Swarm.Cannot be upgraded.000
Holy Sigil of The Second ManWhile worn, grants all unlearned Subskills of the following Skills: Offense, Summon Avatar, Siegecraft, Combat.Cannot be upgraded.000
Holy Sigil of The Seven MagiWhile worn, grants all unlearned Subskills of the following Skills: Sorcery, Nightshade Magic, Resistance.Cannot be upgraded.000
Holy Sigil of UurdtWhile worn, grants all unlearned Subskills of the following Skills: Defense, Primal Magic, Recruitment.Cannot be upgraded.000
Pelagic HourglassThe cooldowns of all spells are reduced by 1 round(s).Cannot be upgraded.000
Fancy MaskGenerates 2 Focus Point(s) at the start of the battle.Upgrade: +2 more Focus Point(s).000
Music SheetFriendly creatures’ abilities deal +40% Damage.Upgrade: +20% more Damage.000
Singing Pan-PipeAll effects applied by friendly creatures last +1 round(s).Upgrade: +1 more round(s).000
Keeper's Amulet+2 Defense.Upgrade: +1 more Defense.000
Keeper's Ring+2 Defense.Upgrade: +1 more Defense.000
Armet+2 Defense.Upgrade: +1 more Defense.000
Drum of WarFriendly creatures deal +1 Damage.Upgrade: +1 more Damage.000
Lance+3 Attack.Upgrade: +2 more Attack.000
Misericorde+2 Attack.Upgrade: +1 more Attack.000
Plate Armor+3 Defense.Upgrade: +2 more Defense.000
Legion's Leg+50% to all creature growth in all your cities.Cannot be upgraded.000
Light-and-Shade CloakFriendly creatures can still use Ranged attacks while adjacent to enemy creatures.Upgrade: Friendly creatures deal +5% Ranged Damage.000
Quivering QuiverFriendly creatures deal +10% Ranged Damage.Upgrade: +5% more Ranged Damage.000
Shroomwood BowEliminates Ranged Damage penalty for friendly creatures.Upgrade: Friendly creatures deal +5% Ranged Damage.000
Lord's RingFriendly creatures have +1 Initiative.Cannot be upgraded.000
Magic Key RingNegates all effects that restrict creature rearrangement, surrendering, fleeing, and using the Spellbook and hero abilities in battle.Cannot be upgraded.000
[[|]]000

-- 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.type = "artifact"')
    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, '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.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