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🗯️nameX
Ambassador's SashAmbassador's Sash
Diplomatic GiftsDiplomatic Gifts
Ancient IdolAncient Idol
Armor of WonderArmor of Wonder
Celestial Sash of BlissCelestial Sash of Bliss
Helm of Heavenly EnlightenmentHelm of Heavenly Enlightenment
Lion's Shield of CourageLion's Shield of Courage
Sandals of the SaintSandals of the Saint
Sword of JudgementSword of Judgement
Banner of Six WindsBanner of Six Winds
Chitinous ShieldChitinous Shield
Demon ClawDemon Claw
Beelzebub's BoonBeelzebub's Boon
HeartbeatHeartbeat
Boreoloth's TailBoreoloth's Tail
Boreoloth's HandBoreoloth's Hand
Boreoloth's HeadBoreoloth's Head
Boreoloth's HeartBoreoloth's Heart
CaduceusCaduceus
Deck of SpellsDeck of Spells
Daylight TomeDaylight Tome
Nightshade TomeNightshade Tome
Primal TomePrimal Tome
Arcane TomeArcane Tome
Chain LinkChain Link
Chain MailChain Mail
Robe of EnlightenmentRobe of Enlightenment
Crown of the Supreme MagiCrown of the Supreme Magi
Demonic HeartDemonic Heart
Brass KnucklesBrass Knuckles
BucklerBuckler
RapierRapier
Eagle ArmorEagle Armor
Flask of OblivionFlask of Oblivion
Lifeblood FaeLifeblood Fae
Ring of LifeRing of Life
Enchanted Magic ScrollEnchanted Magic Scroll
Endless BagEndless Bag
Crescent DaggerCrescent Dagger
Magic SlippersMagic Slippers
Seven EyesSeven Eyes
Vortex RobeVortex Robe
ExcaliburExcalibur
Fallen Angel WingsFallen Angel Wings
Fine WandFine Wand
Flag of TruceFlag of Truce
GarotteGarotte
Automated Antimagic ShieldAutomated Antimagic Shield
Crimson Resonance ControllerCrimson Resonance Controller
Emerald Resonance ControllerEmerald Resonance Controller
Protective BeltProtective Belt
Golden Goose EggGolden Goose Egg
Head TorchHead Torch
Holy Sigil of EridoreHoly Sigil of Eridore
Holy Sigil of InsaraHoly Sigil of Insara
Holy Sigil of MeareaHoly Sigil of Mearea
Holy Sigil of QuixHoly Sigil of Quix
Holy Sigil of RophHoly Sigil of Roph
Holy Sigil of The Second ManHoly Sigil of The Second Man
Holy Sigil of The Seven MagiHoly Sigil of The Seven Magi
Holy Sigil of UurdtHoly Sigil of Uurdt
Pelagic HourglassPelagic Hourglass
Fancy MaskFancy Mask
Music SheetMusic Sheet
Singing Pan-PipeSinging Pan-Pipe
Keeper's AmuletKeeper's Amulet
Keeper's RingKeeper's Ring
ArmetArmet
Drum of WarDrum of War
LanceLance
MisericordeMisericorde
Plate ArmorPlate Armor
Legion's LegLegion's Leg
Light-and-Shade CloakLight-and-Shade Cloak
Quivering QuiverQuivering Quiver
Shroomwood BowShroomwood Bow
Lord's RingLord's Ring
Magic Key RingMagic Key Ring
Magic ScrollMagic Scroll
Golden MothGolden Moth
Golden PigGolden Pig
Skull of MilosSkull of Milos
Monster HeadMonster Head
Mythic Magic ScrollMythic Magic Scroll
Ogre's Club of HavocOgre's Club of Havoc
OmencallerOmencaller
Orb of DestructionOrb of Destruction
Orb of InhibitionOrb of Inhibition
Fish-to-the-FaceFish-to-the-Face
Last CoinLast Coin
RagsRags
Rope BeltRope Belt
Straw HatStraw Hat
Wooden RingWooden Ring
Pole StarPole Star
Crown of DragontoothCrown of Dragontooth
Dragon Scale ArmorDragon Scale Armor
Dragon Scale ShieldDragon Scale Shield
Dragon Wing TabardDragon Wing Tabard
Dragonbone GreavesDragonbone Greaves
Quiet Eye of a DragonQuiet Eye of a Dragon
Red Dragon Flame TongueRed Dragon Flame Tongue
Slithering SashSlithering Sash
Orb of DawnOrb of Dawn
Orb of EternityOrb of Eternity
Orb of TwilightOrb of Twilight
Primordial OrbPrimordial Orb
Ring of NeutralityRing of Neutrality
Liquid SilenceLiquid Silence
Amelchia's LawAmelchia's Law
TruthmakerTruthmaker
TruthseekerTruthseeker
Runestone ShardsRunestone Shards
Scholar's AmuletScholar's Amulet
Scholar's TiaraScholar's Tiara
Seal of SilenceSeal of Silence
Seven-League BootsSeven-League Boots
Shackles of WarShackles of War
Bone BootsBone Boots
Ribcage ArmorRibcage Armor
Dark HatchetDark Hatchet
Second ShadeSecond Shade
Clenching RingClenching Ring
Sirin MaskSirin Mask
Iridescent CloakIridescent Cloak
Shaman StaffShaman Staff
Shoddy ShieldShoddy Shield
Sixth FingerSixth Finger
Soulless SashSoulless Sash
Soulcaller RingSoulcaller Ring
Spellbinder's HatSpellbinder's Hat
Spells in BottlesSpells in Bottles
SpyglassSpyglass
Swamp BootsSwamp Boots
Tactical GuideTactical Guide
Shield of the Rampaging OgreShield of the Rampaging Ogre
Mirror TiaraMirror Tiara
Magic MirrorMagic Mirror
Shard of SerenityShard of Serenity
Tunic of the Cyclops KingTunic of the Cyclops King
Two-faced MaskTwo-faced Mask
Tabar Signet-RingTabar Signet-Ring
Ukh SignetUkh Signet
Voodoo DollVoodoo Doll
BackpackBackpack
Boots of TravelBoots of Travel
Warlord BootsWarlord Boots
Warrior's BeltWarrior's Belt
Warrior's AmuletWarrior's Amulet
Wizard's CloakWizard's Cloak
Wizard's AmuletWizard's Amulet

-- 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'
}

-- 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, 'T.name = name');
    table.insert(fields, 'T.language = language');

    -- 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 = {}
                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 ''
                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 ''
            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

------------------------------------------------------------------------------------------------------------------------
local function createHeader(htmlTable, words)
    local tr = htmlTable:tag('tr')
    --addTh(tr, 'id')
    addTh(tr, words.wiki_artifact):attr('colspan', 2)
    addTh(tr, 'nameX'):attr('colspan', 2)
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

------------------------------------------------------------------------------------------------------------------------
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.name)
    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