FANDOM


--[[Category:Flower Knight description modules]]
-- Displays abilities in a consistent manner.
--<nowiki> Suppresses categories, missing files, etc.
 
--[[
To understand this module, you need to understand how FKG stores abilities.
1. A particular evolution tier for a character has 2 ability IDs.
You can think of it as the top and bottom ability descriptions.
I call these ability IDs "bundled abilities" because they store 2 abilities and a description.
2. Bundled abilities store 2 abilities of their own.
This is how they can put 2 abilities in the top/bottom ability descriptions.
This is also why flower knights can have up to 4 abilities.
I call these 2 abilities "individual abilities" since they are concrete, singular abilities.
3. Individual abilities store three values determining the properties of the ability.
They also store an ID to the exact type of ability it is.
I call these types of abilities "base abilities" because "individual abilities are individualized base abilities.
 
So it's like a character's data stores this.
bundled_ability1 = (individual_ability1(base_ability_id, val1, val2, val3),
    individual_ability2(base_ability_id, val1, val2, val3))
bundled_ability2 = (individual_ability1(base_ability_id, val1, val2, val3),
    individual_ability2(base_ability_id, val1, val2, val3))
--]]
local ability = {}
 
local getArgs = require('Module:Arguments').getArgs
local Categories = require('Module:Categories')
local utils = require('Module:Utils')
local abilityData = require('Module:Ability/Data')
local baseAbilities = abilityData.baseAbilities
local bundledAbilities = require('Module:BundledAbilityList')
 
-- The template used by DisplayAbilitiesForCharacterStat() for Template:CharacterStat.
local charStatAbilityTemplateString = [=[{| class="wikitable" align="center" style="width:100%;text-align:center;margin-top:-1px;"
|-
! scope="col" style="width:90px;max-width:90px;"|Stage
! scope="col"|Abilities
|-
|Pre-Evolved
|${tier1Abilities}
|-
|[[Evolution|Evolved]]
|${tier2Abilities}
|- ${tier3RowStyle}
|${tier3Label}
|${tier3Abilities}
|- ${tier4RowStyle}
|${tier4Label}
|${tier4Abilities}
|}
]=]
 
------------------------------
-- Functions accessed from templates.
------------------------------
 
-- Displays an ability.
function ability.translate(frame)
    local args = getArgs(frame:getParent())
    return ability._translate(args)
end
 
-- Displays abilities for a flower knight in a format for CharacterStat.
-- @args[1]: String. The character's Japanese name.
-- @returns An HTML table of the character's abilities.
function ability.DisplayAbilitiesForCharacterStat(frame)
    local args = getArgs(frame:getParent())
    if not args[1] then
        local cat = mw.loadData('Module:Categories').general.badArg
        return "Error: First arg needs to be the character's Japanese name." ..
            cat
    end
    return ability._DisplayAbilitiesForCharacterStat(args[1])
end
 
------------------------------
-- Misc functions and functions for supporting the template-called functions.
------------------------------
 
--[[
Gets the ability icon filename for some base ability.
 
Returns an empty string if there is no icon.
 
@param baseID: Integer. The base ability ID.
@param extraParam: Integer, string, or nil. Extra info for complicated abilities like Add Weakness.
@returns String. The filename for the ability. Will be an empty string if invalid.
--]]
function _getIconFilename(baseID, extraParam)
    local base = abilityData.baseAbilities[baseID]
    if not base then
        return ''
    elseif not base.iconID then
        return 'ability_icon36.png'
    elseif base.iconID == 12 then
        -- This ability is "Additional Weakness".
        -- The extra parameter is the typing added to the ability.
        return string.format('ability_icon%02d_%s.png', base.iconID, tostring(extraParam) or 'X')
    else
        return string.format('ability_icon%02d.png', base.iconID)
    end
end
 
--[[
Transforms a value for an ability based on the method.
 
Some abilities have descriptions that alter the stored the values 
for an ability. For example, a value of 150 may be displayed as 1.5 .
This function transforms the value to match the description string.
 
@param value: Integer. A value.
@param method: String or nil. The way to transform the value.
    When nil, no transformation is done.
@param short: Boolean. If false, transforms all values.
    If true, only certain types of values get changed.
@returns A float, integer, or string based on the transformation method.
--]]
function _transformValue(value, method, short)
    if short and abilityData.doNotTransformForShortDesc[method] then
        --Do not transform this value.
        return value
    end
 
    local methodFunc = abilityData.valTrans[method or 'other'] or abilityData.valTrans['other']
    return methodFunc(value) or value
end
 
--[[
Gives a message for an ability that can't be translated.
 
@param baseAbilityID: String/Int. The base ability ID.
@param val0: String/Int. The first value. Usually the effectiveness.
@param val1: String/Int. The second value. Usually the number of targets.
@param val2: String/Int. The third value. Usually a limit or a rate.
@param val3: String/Int. The fourth value. Used for Super Counter's multiplier.
@returns A string that is a placeholder for the ability description.
--]]
function _UnknownAbilityMessage(baseAbilityID, val0, val1, val2, val3)
    local cat = mw.loadData('Module:Categories').ability.err.bad
    return utils.emphasize(
        utils.format('[[Module:Ability/Data|Error: Unknown base ability: ${id}. Values: (${val0}, ${val1}, ${val2})]]',
            {id=baseAbilityID, val0=val0, val1=val1, val2=val2})) ..
        cat
end
 
--[[
Translates a single, concrete ability into plain text.
 
No additional formatting or images are added.
 
@param baseAbilityID: String/Int. The base ability ID.
@param val0: String/Int. The first value. Usually the effectiveness.
@param val1: String/Int. The second value. Usually the number of targets.
@param val2: String/Int. The third value. Usually a limit or a rate.
@param val3: String/Int. The fourth value. Used for Super Counter's multiplier.
@param short: Boolean. If non-zero, uses the short description for abilities.
@returns A string that is the translated ability description.
    If baseID is 0 or invalid, an empty string is returned.
--]]
function ability._getIndividualDesc(baseAbilityID, val0, val1, val2, val3, short)
    local baseAbility = baseAbilities[tonumber(baseAbilityID)]
    if not baseAbility then
        return _UnknownAbilityMessage(baseAbilityID, val0, val1, val2, val3)
    end
 
    -- Add a very specific exception for individual ability 1931.
    -- This ability for Dipladenia does nothing at all.
    if tonumber(baseAbilityID) == 1 and tonumber(val0) == 0 then
        return ''
    end
 
    -- Add a specific excection for base ability 1501.
    -- If the number of targeted allies (val 1) is 0, then change it to 1.
    -- It used to be an ignored value, but Dogwood's bloom made it affect 5.
    if tonumber(baseAbilityID) == 1501 and tonumber(val1) == 0 then
        val1 = 1
    end
 
    -- Exception for individual ability 8136.
    -- val1 is set to 30 (crit damage), but it's supposed to be 5 (people).
    if tonumber(baseAbilityID) == 1701 and tonumber(val1) > 5 then
        val1 = 5
    end
 
    local abilityVals = {
        val0 = _transformValue(tonumber(val0), baseAbility.toVal0, short),
        val1 = _transformValue(tonumber(val1), baseAbility.toVal1, short),
        val2 = _transformValue(tonumber(val2), baseAbility.toVal2, short),
        val3 = _transformValue(tonumber(val3), baseAbility.toVal3, short),}
    return utils.format(short and baseAbility.short or baseAbility[1],
        abilityVals)
end
 
--[[
Writes one ability section for _getIndividualizedDescriptions.
 
Please don't use this function anywhere else. Its use is highly specialized.
 
@param abilityDesc: String. The full ability description.
@param baseID: Integer. The base ability ID.
@param extraParam: Integer, string, or nil. Extra info for complicated abilities like Add Weakness.
@param short: Boolean. If true, shrinks the icon to fit with short descriptions.
@param showImg: Boolean. If true, shows the image.
@param showHelp: Boolean. If true, shows a link to the Ability Particulars page,
    and also writes extra info about the ability.
@returns String. The full ability description with formatting included.
--]]
function __getOneIndAbilityDesc(abilityDesc, baseID, extraParam, short,
        showImg, showHelp)
 
    local img = ''
    if showImg then 
        img = _getIconFilename(baseID, extraParam)
        if img ~= '' then
            img = '<span style="float:left">[[File:' .. img ..
            (short and '|16px' or '') ..
            ']]</span>'
        end
    end
 
    local help = ''
    if showHelp and baseAbilities[baseID] and baseAbilities[baseID].see then
        help = string.format(
            '<span class="ttip" title="Particulars">[[Ability_Particulars#%s|( ? )]]</span>',
            baseAbilities[baseID].see)
    end
 
    return string.format('<div style="clear:both;">%s%s.%s</div>', img,
        string.gsub(abilityDesc, "XX", utils.emphasize("XX")), help)
end
 
--[[
Translates a bundled ability's 1~2 individual abilities.
 
@param bundle: Table. A valid entry from "bundledAbilities".
    This table should have the ID and vals for 3 individualized abilities.
@param short: Boolean. If true, uses the short description for abilities.
@param showImg: Boolean. If true, shows the image.
@param showHelp: Boolean. If true, shows a link to the Ability Particulars page,
    and also writes extra info about the ability.
@returns A table with values {prefix, ability1, ability2, ability3, suffix}.
    They are all strings.
--]]
function _getIndividualizedDescriptions(bundle, short, showImg, showHelp)
    -- Change passed args to actual bools.
    short = utils.tobool(short)
    showImg = utils.tobool(showImg)
    showHelp = utils.tobool(showHelp)
 
    local individualizedDescs = {prefix='', ability1='', ability2='', ability3='', suffix=''}
 
    -- Compose the format strings for the individual ability descriptions.
    individualizedDescs.ability1 = ability._getIndividualDesc(
        bundle['ability1ID'], bundle['ability1Val0'], bundle['ability1Val1'],
        bundle['ability1Val2'], bundle['ability1Val3'], short)
    individualizedDescs.ability2 = ability._getIndividualDesc(
        bundle['ability2ID'], bundle['ability2Val0'], bundle['ability2Val1'],
        bundle['ability2Val2'], bundle['ability2Val3'], short)
    individualizedDescs.ability3 = ability._getIndividualDesc(
        bundle['ability3ID'], bundle['ability3Val0'], bundle['ability3Val1'],
        bundle['ability3Val2'], bundle['ability3Val3'], short)
 
    -- Put divs around ability strings and add periods.
    if individualizedDescs.ability1 ~= '' then
        individualizedDescs.ability1 = __getOneIndAbilityDesc(
            individualizedDescs.ability1,
            bundle['ability1ID'], bundle['ability1Val0'],
            short, showImg, showHelp)
    end
    if individualizedDescs.ability2 ~= '' then
        individualizedDescs.ability2 = __getOneIndAbilityDesc(
            individualizedDescs.ability2,
            bundle['ability2ID'], bundle['ability2Val0'],
            short, showImg, showHelp)
    end
    if individualizedDescs.ability3 ~= '' then
        individualizedDescs.ability3 = __getOneIndAbilityDesc(
            individualizedDescs.ability3,
            bundle['ability3ID'], bundle['ability3Val0'],
            short, showImg, showHelp)
    end
 
    return individualizedDescs
end
 
------------------------------
-- Functions called through templates. These ones have the args pre-formatted.
------------------------------
 
--[[
Displays an ability. Only call through "translate".
 
See Module:MasterCharacterData for the ability IDs related to characters.
 
Example debugger usages:
=p._translate({1701})
=p._translate({1701, short=0, showImg=1, showHelp=1})
 
@param args: A table of arguments. The following arguments are allowed.
@param args[1]: String or integer. The unique ability ID for a character.
@param args.short: If set true, uses the short description for abilities.
    Defaults to false.
@param args.showImg: Boolean. If true, shows the image. Defaults to false.
@param args.showHelp: Boolean. If true, shows a link to the Ability Particulars
    page, and also writes extra info about the ability. Defaults to false.
@returns String. The fully translated ability.
--]]
function _translate(args)
    local retString = ''
    local uniqueID = tonumber(args[1])
 
    if not uniqueID then
        local cat = mw.loadData('Module:Categories').ability.err.badCall
        return 'Error: You need to pass the unique ability ID.' .. cat
    end
 
    if uniqueID == 0 then
        -- This bundled ability is empty.
        return ''
    end
 
    individualizedAbilities = bundledAbilities[uniqueID]
    if not individualizedAbilities then
        local cat = mw.loadData('Module:Categories').ability.err.badCall
        return 'Error: Unknown ability ID: ' .. uniqueID .. cat
    end
 
    descs = _getIndividualizedDescriptions(
        individualizedAbilities, args.short, args.showImg, args.showHelp)
    retString = string.format("%s%s%s%s%s", descs.prefix, descs.ability1,
        descs.ability2, descs.ability3, descs.suffix)
 
    return retString
end
 
--[[ Displays abilities for a flower knight in a format for CharacterStat.
@param nameJpOrAllData String or table. If it's a string, it's the
    character's Japanese name. If it's the table it's all the master data
    AND calculated data from Module:Character.
@returns An HTML table of the character's abilities.
--]]
function ability._DisplayAbilitiesForCharacterStat(nameJpOrAllData)
    local knight
 
    if type(nameJpOrAllData) == 'string' then
        -- The var was the character name
        knight = mw.loadData('Module:MasterCharacterData')[nameJpOrAllData]
        if not knight then
            return '[[Module:Ability|Error]]: Character named "' ..
                (nameJpOrAllData or '(blank)') ..
                '" has no [[Module:MasterCharacterData|master data]] entry.' ..
                Categories.general.badArg
        end
    elseif type(nameJpOrAllData) == 'table' then
        -- The var was all the character data
        knight = nameJpOrAllData
        if not knight.bundled_ability_tier1_id1 then
            return '[[Module:Ability|Error]]: Wrong character table sent as ' ..
                "arg. Did you forget to calculate ALL data?" ..
                Categories.general.bug
        end
    else
        return "[[Module:Ability|Error]]:" ..
            "1st arg must be the character's data or Japanese name." ..
            Categories.general.badArg
    end
 
    local formatArgs = {
        -- Actual ability strings.
        tier1Abilities = '',
        tier2Abilities = '',
        tier3Abilities = '',
        tier4Abilities = '',
        -- Styling/Appearance.
        tier3RowStyle = '',
        tier4RowStyle = '',
        tier3Label = (knight.rarity >= 5 and 'Bloomed') or
            '[[Rarity Growth|Rarity Grown]]<br />(Post Evolution)',
        tier4Label = (knight.rarity >= 5 and '[[Rarity Growth|Rarity Grown]]') or
            '[[Rarity Growth|Rarity Grown]]<br />(Post Bloom)',
    }
 
    -- The indices for knight.bundledAbilities are [evolution_tier][bundle_number].
    if knight.bundledAbilities[1] then
        local bundle1Str = _translate({knight.bundledAbilities[1][1],
            short=false, showImg=true, showHelp=true})
        local bundle2Str = _translate({knight.bundledAbilities[1][2],
            short=false, showImg=true, showHelp=true})
        local bundle3Str = _translate({knight.bundledAbilities[1][3] or 0,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier1Abilities = bundle1Str .. bundle2Str .. bundle3Str
        if formatArgs.tier1Abilities == '' then
            formatArgs.tier1Abilities = 'None'
        end
    end
 
    if knight.bundledAbilities[2] then
        local bundle1Str = _translate({knight.bundledAbilities[2][1],
            short=false, showImg=true, showHelp=true})
        local bundle2Str = _translate({knight.bundledAbilities[2][2],
            short=false, showImg=true, showHelp=true})
        local bundle3Str = _translate({knight.bundledAbilities[2][3] or 0,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier2Abilities = bundle1Str .. bundle2Str .. bundle3Str
        if formatArgs.tier2Abilities == '' then
            formatArgs.tier2Abilities = 'None'
        end
    end
 
    if knight.bundledAbilities[3] then
        local bundle1Str = _translate({knight.bundledAbilities[3][1],
            short=false, showImg=true, showHelp=true})
        local bundle2Str = _translate({knight.bundledAbilities[3][2],
            short=false, showImg=true, showHelp=true})
        local bundle3Str = _translate({knight.bundledAbilities[3][3] or 0,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier3Abilities = bundle1Str .. bundle2Str .. bundle3Str
        if formatArgs.tier3Abilities == '' then
            formatArgs.tier3Abilities = 'None'
        end
    else
        formatArgs.tier3RowStyle = 'style="display:none"'
    end
 
    if knight.bundledAbilities[4] then
        local bundle1Str = _translate({knight.bundledAbilities[4][1],
            short=false, showImg=true, showHelp=true})
        local bundle2Str = _translate({knight.bundledAbilities[4][2],
            short=false, showImg=true, showHelp=true})
        local bundle3Str = _translate({knight.bundledAbilities[4][3] or 0,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier4Abilities = bundle1Str .. bundle2Str .. bundle3Str
        if formatArgs.tier4Abilities == '' then
            formatArgs.tier4Abilities = 'None'
        end
    else
        formatArgs.tier4RowStyle = 'style="display:none"'
    end
 
    return utils.format(charStatAbilityTemplateString, formatArgs)
end
 
--[[ Displays abilities for a flower knight in a format for CharacterStat.
@param allData Table. All the character data from the MasterData and
    Module:Character's additional calculations.
@returns An HTML table of the character's abilities.
--]]
-- TODO: Delete this fn? CharStat is not using it anymore...
local function _DisplayAbilitiesForCharacterStatByData(allData)
    local formatArgs = {
        -- Actual ability strings.
        tier1Abilities = '',
        tier2Abilities = '',
        tier3Abilities = '',
        tier4Abilities = '',
        -- Styling/Appearance.
        tier3RowStyle = '',
        tier4RowStyle = '',
        tier3Label = (allData.rarity >= 5 and 'Bloomed') or
            '[[Rarity Growth|Rarity Grown]]<br />(Post Evolution)',
        tier4Label = (allData.rarity >= 5 and '[[Rarity Growth|Rarity Grown]]') or
            '[[Rarity Growth|Rarity Grown]]<br />(Post Bloom)',
    }
 
    -- not not logic makes the result true if it's truthy, or false if falsey.
    -- It's helpful for Scribunto where NIL table values are actually TRUTHY.
    if not allData.is_personal_skin and
            not not allData.bundled_ability_tier1_id1 then
        local tier1Bundle1 = _translate({allData.bundled_ability_tier1_id1,
            short=false, showImg=true, showHelp=true})
        local tier1Bundle2 = _translate({allData.bundled_ability_tier1_id2,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier1Abilities = tier1Bundle1 .. tier1Bundle2
        if formatArgs.tier1Abilities == '' then
            formatArgs.tier1Abilities = 'None'
        end
    end
 
    if not not allData.bundled_ability_tier2_id1 then
        local tier2Bundle1 = _translate({allData.bundled_ability_tier2_id1,
            short=false, showImg=true, showHelp=true})
        local tier2Bundle2 = _translate({allData.bundled_ability_tier2_id2,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier2Abilities = tier2Bundle1 .. tier2Bundle2
        if formatArgs.tier2Abilities == '' then
            formatArgs.tier2Abilities = 'None'
        end
    end
 
    if not not allData.bundled_ability_tier3_id1 then
        local tier3Bundle1 = _translate({allData.bundled_ability_tier3_id1,
            short=false, showImg=true, showHelp=true})
        local tier3Bundle2 = _translate({allData.bundled_ability_tier3_id2,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier3Abilities = tier3Bundle1 .. tier3Bundle2
        if formatArgs.tier3Abilities == '' then
            formatArgs.tier3Abilities = 'None'
        end
    else
        formatArgs.tier3RowStyle = 'style="display:none"'
    end
 
    if not not allData.bundled_ability_tier4_id1 then
        local tier4Bundle1 = _translate({allData.bundled_ability_tier4_id1,
            short=false, showImg=true, showHelp=true})
        local tier4Bundle2 = _translate({allData.bundled_ability_tier4_id2,
            short=false, showImg=true, showHelp=true})
        formatArgs.tier4Abilities = tier4Bundle1 .. tier4Bundle2
        if formatArgs.tier4Abilities == '' then
            formatArgs.tier4Abilities = 'None'
        end
    else
        formatArgs.tier4RowStyle = 'style="display:none"'
    end
 
    return utils.format(charStatAbilityTemplateString, formatArgs)
end
 
ability._DisplayAbilitiesForCharacterStatByData =
    _DisplayAbilitiesForCharacterStatByData
ability._getIndividualizedDescriptions = _getIndividualizedDescriptions
ability._translate = _translate
return ability
--</nowiki>
Community content is available under CC-BY-SA unless otherwise noted.