FANDOM


-- <pre> -- Module - HoverTooltip
--[==============================================================================[  
This module contains helper functions for formatting content of this
wiki's infobox templates.
 
This includes:
    - Parsing the infobox's parameters to return relevant page categories.
    - Parsing the 'set' parameter for alternative inputs.
]==============================================================================]
 
-- load Dev wiki utility modules
local getArgs = require('Dev:Arguments').getArgs
 
--=======================================
--[==[Data Tables]==]
-- These tables will need to be regulary updated as new sets are released
 
-- Input alternatives for the 'set' parameter
local sets = {
  -- Expansions
    ['0'] = 'The Empty Throne',
    ['set0'] = 'The Empty Throne',
    ['1'] = 'The Empty Throne',
    ['set1'] = 'The Empty Throne',
    ['base'] = 'The Empty Throne',
    ['base set'] = 'The Empty Throne',
    ['emptythrone'] = 'The Empty Throne',
    ['theemptythrone'] = 'The Empty Throne',
    ['2'] = 'Omens of the Past',
    ['set2'] = 'Omens of the Past',
    ['omensofthepast'] = 'Omens of the Past',
    ['3'] = 'The Dusk Road',
    ['set3'] = 'The Dusk Road',
    ['duskroad'] = 'The Dusk Road',
    ['theduskroad'] = 'The Dusk Road',
    ['4'] = 'The Fall of Argenport',
    ['set4'] = 'The Fall of Argenport',
    ['thefallofargenport'] = 'The Fall of Argenport',
    ['fallofargenport'] = 'The Fall of Argenport',
    ['5'] = 'Defiance',
    ['set5'] = 'Defiance',
    ['defiance'] = 'Defiance',
    ['6'] = 'Dark Frontier',
    ['set6'] = 'Dark Frontier',
    ['darkfrontier'] = 'Dark Frontier',
    ['set7'] = 'The Flame of Xulta',
    ['theflameofxulta'] = 'The Flame of Xulta',
    ['set8'] = 'Echoes of Eternity',
    ['echoesofeternity'] = 'Echoes of Eternity',
  -- Campaigns
    ['1001'] = "Jekk's Bounty",
    ['set1001'] = "Jekk's Bounty",
    ['jekksbounty'] = "Jekk's Bounty",
    ['1002'] = 'The Tale of Horus Traver',
    ['set1002'] = 'The Tale of Horus Traver',
    ['horustraver'] = 'The Tale of Horus Traver',
    ['taleofhorustraver'] = 'The Tale of Horus Traver',
    ['thetaleofhorustraver'] = 'The Tale of Horus Traver',
    ['1003'] = 'Dead Reckoning',
    ['set1003'] = 'Dead Reckoning',
    ['deadreckoning'] = 'Dead Reckoning',
    ['1004'] = 'Into Shadow',
    ['set1004'] = 'Into Shadow',
    ['intoshadow'] = 'Into Shadow',
    ['1005'] = 'Homecoming',
    ['set1005'] = 'Homecoming',
    ['homecoming'] = 'Homecoming',
    ['1006'] = 'The Trials of Grodov',
    ['set1006'] = 'The Trials of Grodov',
    ['thetrialsofgrodov'] = 'The Trials of Grodov',
    ['1007'] = 'Promises by Firelight',
    ['set1007'] = 'Promises by Firelight',
    ['promisesbyfirelight'] = 'Promises by Firelight',
  -- Other
    ['alpha'] = 'Alpha',
    ['beta'] = 'Closed Beta',
    ['ab'] = 'Alpha-Beta',
    ['alphabeta'] = 'Alpha-Beta',
    ['ai'] = 'AI/Campaign',
    ['aionly'] = 'AI/Campaign',
    ['ai/campaign'] = 'AI/Campaign',
    ['campaign'] = 'AI/Campaign',
    ['computer'] = 'AI/Campaign',
}
 
-- Convert the 'set' result back to its set# (for creating the external database link url)
-- Set 0 cards will need to be specifically filtered in the function
local setNumbers = {
    -- sets
    ['The Empty Throne']      = '1',
    ['Omens of the Past']     = '2',
    ['The Dusk Road']         = '3',
    ['The Fall of Argenport'] = '4',
    ['Defiance']              = '5',
    ['Dark Frontier']         = '6',
    ['The Flame of Xulta']    = '7',
    ['Echoes of Eternity']    = '8',
 
    -- campaigns/bundles
    ["Jekk's Bounty"]            = '1001',
    ['The Tale of Horus Traver'] = '1002',
    ['Dead Reckoning']           = '1003',
    ['Into Shadow']              = '1004',
    ['Homecoming']               = '1005',
    ['The Trials of Grodov']     = '1006',
    ['Promises by Firelight']    = '1007',
 
    -- these other sets will be checked against for determining
    -- external link eligibility and throne format deckbuilding legality
    ['Alpha']       = '-1',
    ['Closed Beta'] = '-1',
    ['Alpha-Beta']  = '-1',
    ['AI/Campaign'] = '-1',
}
 
-- Sigils (legal in all formats)
local sigils = {
    ['Fire Sigil'] = '',
    ['Time Sigil'] = '',
    ['Justice Sigil'] = '',
    ['Primal Sigil'] = '',
    ['Shadow Sigil'] = '',
}
 
-- Current Draft format set packs & Draft Pack contents
local dPacks = require("Module:Data/Draft Packs")
local draft = {}
draft.Sets = {-- Draft set packs
    ['Echoes of Eternity'] = '',
}
draft.Pack = dPacks.current -- current Draft Pack contents
 
-- Current Expedition format list
local expedition = {}
expedition.Sets = { -- Expedition-legal sets
    ['The Flame of Xulta'] = '',
    ['Promises by Firelight'] = '',
    ['Echoes of Eternity'] = '',
}
expedition.draftPack = draft.Pack -- expedition-used Draft Packs
-- expedition.draftPack = dPacks.expedition -- expedition-used Draft Packs
expedition.Cards = { -- other Expedition-legal cards (promos, etc)
    -- ['Albon Roa, of the Order'] = '',
    -- ['Garden of Omens'] = '',
    -- ['Jishu, the Burning Brush'] = '',
    -- ['Oizio, Adaptive Spy'] = '',
    -- ['Genetrix Irel IV'] = '',
    -- ['Lieutenant Relia'] = '',
    -- ['Dizo, Cabal Chairman'] = '',
} 
expedition.partialSets = { -- partial Expedition-legal sets/campaigns
    -- ['Dead Reckoning'] = '',
    -- ['The Trials of Grodov'] = '',
}
expedition.partialSetExceptions = { -- partial set cards excluded from Expedition
    -- ['Hailstorm'] = '',
    -- ['Equivocate'] = '',
    -- ['Saber-Tooth Prideleader'] = '',
    -- ['Gnash, Desert Prince'] = '',
}
 
-- Possible influence icon calls & converted categories
local factionParams = {
    ['f'] = 'Fire',
    ['fire'] = 'Fire',
    ['red'] = 'Fire',
    ['r'] = 'Fire',
    ['p'] = 'Primal',
    ['primal'] = 'Primal',
    ['blue'] = 'Primal',
    ['t'] = 'Time',
    ['time'] = 'Time',
    ['yellow'] = 'Time',
    ['j'] = 'Justice',
    ['justice'] = 'Justice',
    ['green'] = 'Justice',
    ['s'] = 'Shadow',
    ['shadow'] = 'Shadow',
    ['purple'] = 'Shadow',
}
 
-- Possible faction pairs
local factionPairs = {
    ['Primal;Shadow;'] = 'Feln',
    ['Primal;Time;'] = 'Elysian',
    ['Justice;Time;'] = 'Combrei',
    ['Fire;Shadow;'] = 'Stonescar',
    ['Fire;Justice;'] = 'Rakano',
    ['Fire;Time;'] = 'Praxis',
    ['Justice;Primal;'] = 'Hooru',
    ['Shadow;Time;'] = 'Xenan',
    ['Fire;Primal;'] = 'Skycrag',
    ['Justice;Shadow;'] = 'Argenport',
}
-- Possible faction trios
local factionTrios = {
    -- 'Shards'
--    ['Justice;Primal;Shadow;'] = '',
--    ['Fire;Primal;Shadow;'] = '',
--    ['Fire;Shadow;Time;'] = '',
--    ['Fire;Justice;Time;'] = '',
--    ['Justice;Primal;Time;'] = '',
    -- 'Wedges'
    ['Fire;Justice;Shadow;'] = 'Winchest',
    ['Fire;Primal;Time;'] = 'Jennev',
    ['Justice;Shadow;Time;'] = 'Kerendon',
    ['Fire;Justice;Primal;'] = 'Ixtun',
    ['Primal;Shadow;Time;'] = 'Auralian',
}
-- -----------------------------------------------------
--[======[Non-Invokable Functions ]======]
-- These functions cannot be accessed from wiki articles or templates. They are
--   used by other parts of this module.
-- See 'Invokable Functions' section below for accessable functions.
 
 
-- Wrap string with categorization wikitext 
function categorizeString(s)
    s = '[[Category:'.. s ..']]'
    return s
end
 
-- Convert input string parameter into table of separate unit types
function parseUnitTypes(s)
    local uTypes = {}
    for unitType in mw.text.gsplit(s, '[^A-Za-z0-9]') do
        table.insert(uTypes, unitType)
    end
    return uTypes
end
 
-- Convert input string parameter into table of faction categories
function parseFactions(s)
    -- tables for found factions and categories
    local factions = {}
    local categories = {}
 
    -- Check if {{icon}} template has been used, if so it has already been processed
    if string.match(s, 'File:') then
        for req in string.gmatch(s, 'Icon req %a') do
            req = string.lower(req)
            req = string.sub(req, -1)
 
            req = factionParams[req] -- convert to faction string
            factions[req] = req -- combine multiples
        end
 
    -- If {{icon}} wasn't used
    else
        s = string.lower(s)
        s = string.gsub(s, ',', '') -- strip commas
 
        -- skip if anything other than base influence abbrieviations were used
        if string.match(s, '[^fptjs]') then else
            for req in string.gmatch(s, '%a') do
                req = factionParams[req] -- convert to faction string
                factions[req] = req -- combine multiples
            end
        end
    end
 
    -- add base faction categories,
    -- convert faction table to numbered keys & sort
    local count = 0
    local temp = {}
    for _,v in pairs(factions) do
        count = count + 1
        table.insert(temp, v)
    end
    factions = temp
    table.sort(factions)
 
    -- add faction types
    if count == 0 then
        table.insert(categories, categorizeString('Neutral'))
 
    -- add single faction categories
    elseif count == 1 then
        table.insert(categories, categorizeString(factions[1]))
 
    -- add 2- and 3-faction categories
    elseif count == 2 or count == 3 then
        local fPair = ''
        for i,v in ipairs(factions) do
            fPair = fPair .. v .. ';'
        end
 
        if count == 2 then
            fPair = factionPairs[fPair] or ''
        else
            fPair = factionTrios[fPair] or 'Multifaction'
        end
 
        if fPair ~= '' then
            table.insert(categories, categorizeString(fPair))
        end
    elseif count >= 4 then
        table.insert(categories, categorizeString('Multifaction'))
    end
 
    return categories
end
 
-- convert string list into a table list
--   use for named template parameters which may contain one or more values
function listToTable(string, pattern)
    local table1 = {} -- for first pass, remove text between brackets: []
    local table2 = {} -- for second pass, split text using separator
    local tableCombined
    -- local capture = '%(([^%(%)]*)%)' -- old
    local capture = '%[([^%[%]]*)%]'
    local seperator = pattern or ','
    local n -- throwaway variable for gsub returns
 
    -- capture text tagged inside brackets, place into table1
    for value in string.gmatch(string, capture) do
        table.insert(table1, value)
    end
 
    -- remove captured text from the string
    string, n = string.gsub(string, capture .. ',', '')
    string, n = string.gsub(string, capture, '')
 
    -- split text at commas, place into table2
    table2 = mw.text.split(string, seperator)
 
    -- combine tables
    tableCombined = mergeTables(table1, table2)
 
    -- clean excess whitespace
    for i,v in pairs(tableCombined) do
        tableCombined[i] = mw.text.trim(v)
        -- remove 'whitespace' values
        if tableCombined[i] == '' then table.remove(tableCombined, i) end
    end
 
    return tableCombined
end
 
-- Create a table of another table's keys
function keysToTable(t)
    local keyset={}
    local n=0
 
    for k,v in pairs(t) do
        n=n+1
        keyset[n]=k
    end
 
    return keyset
end
 
-- Combine two tables together. Merge any values with non-number keys, append 
--   the rest to the end of the 1st table
function mergeTables(t1, t2)
    -- error correct for nil arguments
    if t1 and not t2 then return t1 end
    if t2 and not t1 then return t2 end
 
    -- throw error if an argument isn't a table
    if type(t1) ~= 'table' or type(t2) ~= 'table' then
       error("Module:HoverTooltip - 'mergeTables' requires two tables. It was given a "
        .. type(t1) .. " and a " .. type(t2) .. ".")
    end
 
    -- error correct for empty table arguments
    local keys1 = keysToTable(t1)
    if not keys1[1] then return t2 end
 
    local keys2 = keysToTable(t2)
    if not keys2[1] then return t1 end
 
    -- find max numerical key for the first table
    local keyMax = 0
    for k,_ in ipairs(t1) do
        if type(k) == 'number' and k >= keyMax then
            keyMax = k
        end
    end
 
    -- combine tables
    for k,v in ipairs(t2) do
        if type(k) == 'number' then -- append # keyed values to end
            keyMax = keyMax + 1
            t1[keyMax] = v
        else --                        merge/add anything else
            t1[k] = v
        end
    end
 
    return t1
end
 
--=======================================
--[==[Invokable Functions]==]
-- These Frame Object functions can be called from a wiki article or template 
--   with the syntax: {{#invoke:ModuleName|functionName|arg1|arg2|...}}
local p = {}
 
-- testing function, uncomment and change as needed to find errors.
-- recommend testing in a sandboxed wiki page (user page, temlate sandbox, etc)        
--[==============================================================================[ 
function p.test1(frame)
    local args = frame.args
    local parent = frame:getParent().args
 
    local parameters = {} -- table to hold applicable template parameters
    local categories = {}
 
    parameters.factions = args['factions'] or parent['factions'] or ''
    parameters.cardtype = args['type'] or parent['type'] or ''
 
    categories = table.concat(categories, '')
    return mw.text.nowiki(categories)
 
end
]==============================================================================]
 
-- Main function - parse relevant Infobox template parameters and return as category wikitext
function p.addCategories(frame)
    local args = getArgs(frame) -- clean the arguments using Module:Arguments on the dev wiki
 
    local parameters = {} -- table to hold applicable template parameters
 
    parameters.set = args['set'] or ''
    parameters.rarity = args['rarity'] or ''
    parameters.cardtype = args['type'] or ''
    parameters.subtype = args['subtype'] or ''
    parameters.hero = args['hero'] or ''
    parameters.requirements = args['requirements'] or ''
    parameters.factions = args['factions'] or ''
    parameters.cost = args['cost'] or ''
    parameters.keywords = args['keywords'] or ''
    parameters.artist = args['artist'] or ''
    parameters.voiceactor = args['voiceactor'] or ''
    parameters.flavor = args['flavor'] or ''
    parameters.categories = args['categories'] or ''
 
    local categories = {} -- table to hold categories
 
    -- add simple categories
    if string.lower(parameters.hero) == 'true' then
        table.insert(categories, categorizeString('Hero'))
    end
 
    if parameters.artist ~= '' then
        table.insert(categories, categorizeString(parameters.artist))
    end
 
    if parameters.voiceactor ~= '' then
        table.insert(categories, categorizeString(parameters.voiceactor))
    end
 
    if parameters.flavor ~= '' then
        table.insert(categories, categorizeString('Flavor'))
    end
 
    -- parse for set name
    local set = ''
    if parameters.set ~= '' then
        set = string.lower(parameters.set) -- format for data table check
        local n -- placeholder
 
        -- strip whitespace & unwanted characters
        set, n = string.gsub( set, '[^A-Za-z0-9]', '')
 
        if sets[set] then
            if set == 'alpha' or set == 'beta' or sets[set] == 'Alpha-Beta' then
                table.insert(categories, categorizeString('Alpha-Beta'))
            elseif sets[set] == 'AI/Campaign' then
                table.insert(categories, categorizeString('AI-Campaign'))
            else
                table.insert(categories, categorizeString(sets[set]))
            end
        end
    end
 
    -- add rarity category, unless there's an exception
    if parameters.rarity ~= '' then
        if set ~= '' and sets[set] == 'AI-Only' then else
            table.insert(categories, categorizeString(parameters.rarity))
        end
    end
 
    -- parse for card type/sub-type categories
    if parameters.cardtype ~= '' then
 
            -- categorize using cardtype if no subtype
        if parameters.subtype == '' then
            table.insert(categories, categorizeString(parameters.cardtype))
 
            -- categorize using subtype unless its a unit
        else
            if string.lower(parameters.cardtype) == 'unit' then
                -- if it is a unit, categorize each unitType
                local uTypes = parseUnitTypes(parameters.subtype)
                for _, unitType in ipairs(uTypes) do
                    table.insert(categories, categorizeString(unitType))
                end
            else
                table.insert(categories, categorizeString(parameters.subtype))
            end
        end
    end
 
    -- parse for faction categories
    if parameters.factions ~= '' then
        -- table for found factions
        local factions = parseFactions(parameters.factions)
 
        for i,v in ipairs(factions) do
            table.insert(categories, v)
        end
 
    -- if 'factions' parameter isn't used (and if not a Power card),
    -- parse the influence 'requirements' parameter for factions instead 
    elseif string.lower(parameters.cardtype) ~= 'power' then
        if parameters.cost ~= '' then
            if parameters.requirements ~= '' then
                -- table for found factions
                local factions = parseFactions(parameters.requirements)
 
                for i,v in ipairs(factions) do
                    table.insert(categories, v)
                end
            else
                table.insert(categories, categorizeString('Neutral'))
            end
        end
    end
 
    -- parse for keywords categories
    if parameters.keywords ~= '' then
        -- split & find keywords
        for keyword in mw.text.gsplit(parameters.keywords, ',') do
            keyword = mw.text.trim(keyword) -- strip excess whitespace
            -- If keyword is surrounded by brackets ('[' and ']'), then skip categorization
            -- this enables hover-tooltips for the keyword for "Keyword Matters" categories
            if string.find(keyword, "[%[%]]") then
                -- do nothing
            -- Don't add an 'Agenda' category for now - would be redundant with 'Site'.
            -- If Agenda is later added to non-Site cards, remove this stiputation.
            elseif string.lower(keyword) == "agenda" then
            -- Group 'Black Market' into 'Market'
            elseif string.lower(keyword) == "black market" then
                table.insert(categories, categorizeString("Market"))
            else
                table.insert(categories, categorizeString(keyword))
            end
        end
    end
 
    -- parse for extra specified categories
    if parameters.categories ~= '' then
        -- split & find 
        for cat in mw.text.gsplit(parameters.categories, ',') do
            cat = mw.text.trim(cat) -- strip excess whitespace
            table.insert(categories, categorizeString(cat))
        end
    end
 
    -- convert category table to string
    categories = table.concat(categories, '')
 
    --local n -- placeholder
    -- remove any accidental empty categories caused by used but empty parameters
    --categories, n = string.gsub(categories, '[[Category:]]', '')
 
    return categories
--    return mw.text.nowiki(categories)
end    
 
function p.typeIcon(frame)
    local args = getArgs(frame) -- clean the arguments using Module:Arguments on the dev wiki
 
    local cardType = args['type'] or ''
    local subtype = args['subtype'] or ''
 
    if cardType == '' and subtype == '' then return '' end
 
    cardType = string.lower(cardType) -- format for data table check
    subtype = string.lower(subtype) -- format for data table check
 
    local icons = {
        ["unit"] = "[[File:Icon Unit.png|x16px|link=Units]] ",
        ["power"] = "[[File:Icon Power.png|x16px|link=Power]] ",
        ["spell"] = "[[File:Icon Spell.png|x16px|link=Spells]] ",
        ["fast spell"] = "[[File:Icon Spell.png|x16px|link=Spells]] ",
        ["attachment"] = "[[File:Icon Attachment.png|x16px|link=Attachments]] ",
        ["weapon"] = "[[File:Icon Attachment.png|x16px|link=Attachments]] ",
        ["curse"] = "[[File:Icon Attachment.png|x16px|link=Attachments]] ",
        ["relic"] = "[[File:Icon Attachment.png|x16px|link=Attachments]] ",
        ["relic weapon"] = "[[File:Icon Attachment.png|x16px|link=Attachments]] ",
        ["cursed relic"] = "[[File:Icon Attachment.png|x16px|link=Attachments]] ",
        }
 
    if icons[cardType] then
        return icons[cardType]
    elseif icons[subtype] then
        return icons[subtype]
    else
        return ''
    end
end
 
-- WIP replacement for 'p.typeIcon', to move all type/subtype parsing to Lua (currently text parsing is done in-template with wikitext parser functions)
function p.cardType(frame)
    local args = getArgs(frame) -- clean the arguments using Module:Arguments on the dev wiki
 
    local cardType = args['type'] or ''
    local subtype = args['subtype'] or ''
 
    -- return empty string if neither parameter is specified
    if (cardType .. subtype) == '' then return '' end
 
    -- lowercase type parameters
    local slCT, slST = string.lower(cardType), string.lower(subtype)
 
    -- set supertype
    local supertype = ''
        local hero = args['hero'] or nil
        if hero == 'true' then supertype = '[[Hero]] ' end
 
    -- table of type/subtype icons & links
    local typeInfo = {
        ["unit"] = { 
            ["icon"] = "[[File:Icon Unit.png|x16px|alt=Unit icon|link=Unit]] ", 
            ["link"] = "[[Unit]]" },
        ["power"] = { 
            ["icon"] = "[[File:Icon Power.png|x16px|alt=Power icon|link=Power]] ", 
            ["link"] = "[[Power]]" },
        ["spell"] = { 
            ["icon"] = "[[File:Icon Spell.png|x16px|alt=Spell icon|link=Spell]] ", 
            ["link"] = "[[Spell]]" },
        ["fast spell"] = { 
            ["icon"] = "[[File:Icon Spell.png|x16px|alt=Spell icon|link=Spell]] ", 
            ["link"] = "[[Fast Spell]]" },
        ["site"] = { 
            ["icon"] = "",--"[[File:Icon Site.png|x16px|alt=Site icon|link=Site]] ", 
            ["link"] = "[[Site]]" },
        ["attachment"] = { 
            ["icon"] = "[[File:Icon Attachment.png|x16px|alt=Attachment icon|link=Attachment]] ", 
            ["link"] = "[[Attachment]]" },
        ["weapon"] = { 
            ["icon"] = "[[File:Icon Attachment.png|x16px|alt=Attachment icon|link=Weapon]] ", 
            ["link"] = "[[Weapon]]" },
        ["curse"] = { 
            ["icon"] = "[[File:Icon Attachment.png|x16px|alt=Attachment icon|link=Curse]] ", 
            ["link"] = "[[Curse]]" },
        ["relic"] = { 
            ["icon"] = "[[File:Icon Attachment.png|x16px|alt=Attachment icon|link=Relic]] ", 
            ["link"] = "[[Relic]]" },
        ["relic weapon"] = { 
            ["icon"] = "[[File:Icon Attachment.png|x16px|alt=Attachment icon|link=Relic Weapon]] ", 
            ["link"] = "[[Relic Weapon]]" },
        ["cursed relic"] = { 
            ["icon"] = "[[File:Icon Attachment.png|x16px|alt=Attachment Icon|link=Cursed Relic]] ", 
            ["link"] = "[[Cursed Relic]]" },
        }
 
    -- parse type icon and link
    local icon, link = '', ''
 
    if typeInfo[slST] then
        icon = typeInfo[slST]["icon"]
    elseif typeInfo[slCT] then
        icon = typeInfo[slCT]["icon"]
    end
 
    if typeInfo[slST] then
        link = typeInfo[slST]["link"]
    elseif typeInfo[slCT] then
        link = typeInfo[slCT]["link"]
    else
        link = cardType
    end
 
    -- format output text
    local text = ''
 
    text = icon .. supertype .. link
 
    -- add unit subtypes
    if slCT == 'unit' and subtype ~= '' then
        local uTypes = parseUnitTypes(subtype)
        for i, unitType in ipairs(uTypes) do
            uTypes[i] = '[[:Category:' .. unitType .. '|' .. unitType .. ']]'
        end
        subtype = table.concat(uTypes, ' ')
        text = text .. ' &ndash; ' .. subtype
    end
 
    return text
end
 
-- Parse and return the set name, add set icon
function p.setName(frame)
    local args = getArgs(frame)
 
    -- return empty string if set parameter isn't specified
    local set = args['set'] or nil
    if set then else return '' end
 
    set = string.lower(set) -- format for data table check
    local n -- placeholder
 
    -- strip whitespace & unwanted characters
    set, n = string.gsub( set, '[^A-Za-z0-9]', '')
 
    if sets[set] then
        if set == '0' or set == 'set0' then
            set = '[[File:Icon The Empty Throne.png|24px|link=]] [[The Empty Throne]] (Set 0)'
 
        elseif set == 'alpha' or set == 'beta' or sets[set] == 'Alpha-Beta' then
            set = sets[set]
 
        elseif sets[set] == 'AI/Campaign'then
            set = '[[AI-Campaign Cards|AI/Campaign]]'
 
        -- temporarily skip, until set icon is made
        --or set == '6' or set == 'set6' or set == 'darkfrontier' then
        elseif set == '1007' or set == 'set1007' or set == 'promisesbyfirelight' 
         or set == '8' or set == 'set8' or set == 'echoesofeternity' then
            set = '[[' .. sets[set] .. ']]'
 
        else
            set = '[[File:Icon ' .. sets[set] .. '.png|24px|link=]] [[' .. sets[set] .. ']]'
        end
    else
        return "Oops something is wrong! Check the spelling of the infobox's 'set' parameter. Check [[Module:InfoboxTools]] for possible inputs, and update it if a new set has been released."
    end
 
    return set
end
 
-- Check if the card is legal in the current Expedition format
function p.expeditionCheck(frame)
    local args = getArgs(frame)
 
    -- return empty string if set parameter isn't specified
    local set = args['set'] or nil
    local card = args['card'] or args['title'] or mw.title.getCurrentTitle().text
    if set then else return '' end
 
    set = string.lower(set) -- format for data table check
    local n -- placeholder
 
    -- strip whitespace & unwanted characters
    set, n = string.gsub( set, '[^A-Za-z0-9]', '')
 
    -- check that the set name is legal
    if sets[set] then
        set = sets[set]
    else
        return 'false'
    end
 
    -- templates should evaluate this output with '{{#ifeq:true|...}}'
    if expedition.Sets[set] then
        return 'true'
    elseif expedition.partialSets[set] then
        if expedition.partialSetExceptions[card] then
            return 'false'
        else
            return 'true'
        end
    elseif expedition.Cards[card] or expedition.draftPack[card] or sigils[card] then
        return 'true'
    else
        return 'false'
    end
end
 
-- Check if the card is in the current Draft format
function p.draftCheck(frame)
    local args = getArgs(frame)
 
    -- return empty string if set parameter isn't specified
    local set = args['set'] or nil
    if set then else return '' end
 
    local card = args['title'] or mw.title.getCurrentTitle().text
 
    set = string.lower(set) -- format for data table check
    local n -- placeholder
 
    -- strip whitespace & unwanted characters
    set, n = string.gsub( set, '[^A-Za-z0-9]', '')
 
    if sets[set] and draft.Sets[sets[set]] then
        return 'true' -- templates should evaluate this output with '{{#ifeq:true|...}}'
    elseif draft.Pack[card] then
        return 'true'
    else
        return 'false'
    end
end
 
-- test
 
function p.draftList()
    local contents = {}
 
    for i,_ in pairs(draft.Pack) do
        table.insert(contents, i)
    end
    contents = table.concat(contents, '<br/>')
    return contents
end
 
 
function p.expeditionList()
 
    local sets = {}
    for i,_ in pairs(expedition.Sets) do
        table.insert(sets, i)
    end
    for i,_ in pairs(expedition.partialSets) do
        table.insert(sets, i)
    end
    sets = table.concat(sets, '<br/>')
 
    local expections = {}
    for i,_ in pairs(expedition.partialSetExceptions) do
        table.insert(expections, i)
    end
    expections = table.concat(expections, '<br/>')
 
    local cards = {}
    for i,_ in pairs(expedition.Cards) do
        table.insert(cards, i)
    end
    cards = table.concat(cards, '<br/>')
 
    local result = '=== Sets ===\n' .. sets
              .. '\n==== Exceptions ====\n' .. expections
              .. '\n=== Promos ===\n' .. cards
              .. '\n=== Draft Pack ===\n' .. p.draftList()
    return result
end
 
-- Check the card's legality in different formats
function p.formats(frame)
    local args = getArgs(frame)
 
    -- return empty string if set parameter isn't specified
    local set = args['set'] or nil
    if set then else return '' end
 
    local card = args['title'] or mw.title.getCurrentTitle().text
 
    set = string.lower(set) -- format for data table check
    local n -- placeholder
 
    -- strip whitespace & unwanted characters
    set, n = string.gsub( set, '[^A-Za-z0-9]', '')
 
    -- Check that 'set' is valid
    set = sets[set] or ''
    if set == '' then return '' end
    -- Filter out non-expansion/campaign pseudo-'sets' (alpha/beta cards, AI cards, etc)
    local setNum = setNumbers[set] or 'notFound'
    if setNum == '-1' or setNum == 'notFound' then return '' end
 
    -- Check formats
    local formats = {
      [1] = {"[[Throne]]", "Not Legal"},
      [2] = {"[[Expedition]]", "Not Legal"},
      [3] = {"[[Draft]]", "Not Legal"},
    }
 
    -- Ranked/Throne format. Currently, andything that passed the filters is always true.
    formats[1][2] = "Legal"
 
    -- Check the current Expedition format
    if expedition.Sets[set] then
        formats[2][2] = "Legal"
    elseif expedition.partialSets[set] then
        if expedition.partialSetExceptions[card] then
            -- nothing
        else
            formats[2][2] = "Legal"
        end
    elseif expedition.Cards[card] or expedition.draftPack[card] or sigils[card] then
        formats[2][2] = "Legal"
    end
 
    -- Check the current Draft format
    if draft.Sets[set] then
        formats[3][2] = "Legal"
    elseif draft.Pack[card] or sigils[card] then
        formats[3][2] = "Legal"
    end
 
    local result = {}
    for _, v in ipairs(formats) do
        local formatCheck = v[1] .. ' &ndash; ' .. v[2]
        table.insert(result, formatCheck)
    end
 
    result = '*' .. table.concat(result, '\n*')
    return result
end
 
-- Parse 'set' and 'cardnumber' parameters, convert to url for external database links
function p.databaseURL(frame)
    local args = getArgs(frame)
 
    local set = args['set'] or ''
    local cardnumber = args['cardnumber'] or ''
 
    -- Check that 'set' and 'cardnumber' are used
    if set == '' or cardnumber == '' then return '' end
 
    -- Check that 'cardnumber' is a number
    if tonumber(cardnumber) then else return '' end
 
    set = string.lower(set) -- format for data table check
    local n -- placeholder
 
    -- strip whitespace & unwanted characters
    set, n = string.gsub( set, '[^A-Za-z0-9]', '')
 
    -- Check that 'set' is valid
    if not sets[set] then return '' end
 
    -- Fix for Set 0
    if set == '0' or set == 'set0' then
        set = '0'
    else
        set = sets[set]
    -- Convert set name to set number
        set = setNumbers[set] or 'removeMe'
    end
 
    -- Filter out non-expansion/campaign pseudo-'sets' (alpha/beta cards, AI cards, etc)
    -- or anything not added to 'setNumbers'
    if set == '-1' or set == 'removeMe' then return '' end
 
    -- Create URLs
    local cardURL = 'https://eternalwarcry.com/cards/details/'
    local deckURL = 'https://eternalwarcry.com/decks?ic='
 
    cardURL = '[' .. cardURL .. set .. '-' .. cardnumber .. ' Card]'
    deckURL = '[' .. deckURL .. set .. '-' .. cardnumber .. ' Decks]'
 
    -- Format result
    local result = "\n*" .. cardURL .. "\n*" .. deckURL
 
    return result
end
 
 
-- Parse and return list of cards created by (or tansformed into) by the main card.
function p.tokensList(frame)
    local args = frame.args
    local parent = frame:getParent().args
 
    local tokens = args['tokens'] or parent['tokens'] or ''
 
    if tokens == '' then return '' end
 
    -- convert to table
    tokens = listToTable(tokens)
 
    -- format table into list wikitext
    for i,token in ipairs(tokens) do
        tokens[i] = "* [[" .. token .. "]]"
    end
    tokens = table.concat(tokens, "\n")
 
    return tokens
end
 
-- -----------------------------------------------------
return p
-- </pre> [[Category:Eternal Wiki Modules]]
Community content is available under CC-BY-SA unless otherwise noted.