Jump to content

Module:Spell: Difference between revisions

From Apogea Wiki
Jayarrowz (talk | contribs)
No edit summary
Jayarrowz (talk | contribs)
No edit summary
 
(One intermediate revision by the same user not shown)
Line 106: Line 106:
-- base_damage = "20 Base Damage" (gold)
-- base_damage = "20 Base Damage" (gold)
-- magic_scaling = "75% Magic" (purple/orchid)
-- magic_scaling = "75% Magic" (purple/orchid)
-- damage_scaling = "65% Damage" (purple/orchid)
-- damage_scaling = "65% Damage" (color-peachy-salmon)
-- attack_speed = "+10 Attack Speed" (gold)
-- attack_speed = "+10 Attack Speed" (gold)
local function formatFormula(baseDamage, magicScaling, damageScaling, attackSpeed)
local function formatFormula(baseDamage, magicScaling, damageScaling, attackSpeed)
Line 132: Line 132:
     end
     end
      
      
     -- Damage scaling in purple/orchid
     -- Damage scaling in color-peachy-salmon
     if damageScaling and damageScaling ~= "" then
     if damageScaling and damageScaling ~= "" then
         if #parts > 0 then
         if #parts > 0 then
             table.insert(parts, string.format(' + <span class="color-orchid">%s</span>', damageScaling))
             table.insert(parts, string.format(' + <span class="color-peachy-salmon">%s</span>', damageScaling))
         else
         else
             table.insert(parts, string.format('<span class="color-orchid">%s</span>', damageScaling))
             table.insert(parts, string.format('<span class="color-peachy-salmon">%s</span>', damageScaling))
         end
         end
     end
     end
Line 176: Line 176:
local function getSpellData(name)
local function getSpellData(name)
     local tables = 'Spells'
     local tables = 'Spells'
     local fields = 'name,sprite,type,magic,ability,cost,hp_cast,cooldown,description,base_damage,magic_scaling,damage_scaling,attack_speed'
     local fields = 'name,sprite,type,magic,ability,cost,hp_cast,cooldown,description,base_damage,magic_scaling,damage_scaling,attack_speed,effect'
     local args = {
     local args = {
         where = 'name="' .. name .. '"',
         where = 'name="' .. name .. '"',
Line 188: Line 188:
     end
     end
     return nil
     return nil
end
local function formatEffect(effect)
    if not effect or effect == "" then
        return nil
    end
    local lines = {}
    for line in effect:gmatch("[^\n]+") do
        local trimmed = line:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            table.insert(
                lines,
                string.format('<p class="color-columbia">%s</p>', trimmed)
            )
        end
    end
    return lines
end
end


Line 215: Line 234:
             magic_scaling = args.magic_scaling or args.magicScaling,
             magic_scaling = args.magic_scaling or args.magicScaling,
             damage_scaling = args.damage_scaling or args.damageScaling,
             damage_scaling = args.damage_scaling or args.damageScaling,
             attack_speed = args.attack_speed or args.attackSpeed
             attack_speed = args.attack_speed or args.attackSpeed,
            effect = args.effect
         }
         }
     else
     else
Line 282: Line 302:
         table.insert(html, formulaDisplay)
         table.insert(html, formulaDisplay)
     end
     end
   
    -- Spell effect (white text, shown below formula)
local effectLines = formatEffect(data.effect)
if effectLines then
    for _, line in ipairs(effectLines) do
        table.insert(html, line)
    end
end
      
      
     -- Magic requirement (white)
     -- Magic requirement (white)

Latest revision as of 23:31, 30 January 2026

Documentation for this module may be created at Module:Spell/doc

local p = {}

--[[
    Spell Module for Apogea Wiki
    
    Displays spell tooltips matching in-game styling:
    - Orange spell name (font-bitcell)
    - White cost/cooldown in parentheses
    - White description
    - Orange/coral spell type (e.g., "Fire Spell")
    - Green formula values
    - White requirement text
    
    Uses existing color classes from Tokens.css
]]

-- Spell type colors (matching in-game - uses existing color classes)
-- Fire Spell appears orange/coral in-game
local typeColors = {
    light = "columbia",      -- light blue
    blade = "coral",         -- red/orange
    physical = "coral",      -- red/orange
    conjure = "conifer",     -- green
    death = "orchid",        -- purple
    fire = "coral",          -- red/orange (Fire Spell is orange in screenshot)
    arrow = "coral",         -- red/orange
    time = "gold",           -- yellow/gold
    energy = "coral",        -- red/orange
    heal = "columbia",       -- light blue
    holy = "gold",           -- yellow/gold
    earth = "gold",          -- yellow/gold
    mystic = "columbia",     -- light blue
    defense = "gold",        -- yellow/gold
    water = "columbia"       -- light blue
}

-- Type to plural category mapping
local typePlurals = {
    ["Light"] = "Light Spells",
    ["Blade"] = "Blade Spells",
    ["Physical"] = "Physical Spells",
    ["Conjure"] = "Conjure Spells",
    ["Death"] = "Death Spells",
    ["Fire"] = "Fire Spells",
    ["Arrow"] = "Arrow Spells",
    ["Time"] = "Time Spells",
    ["Energy"] = "Energy Spells",
    ["Heal"] = "Heal Spells",
    ["Holy"] = "Holy Spells",
    ["Earth"] = "Earth Spells",
    ["Mystic"] = "Mystic Spells",
    ["Defense"] = "Defense Spells",
    ["Water"] = "Water Spells"
}

-- Pluralize a type name for category
local function pluralize(singular)
    return typePlurals[singular] or (singular .. " Spells")
end

-- Format description - handles line breaks (white text)
local function formatDescription(desc)
    if not desc or desc == "" then
        return {}
    end
    
    local lines = {}
    -- Split by newlines and create paragraph for each
    for line in desc:gmatch("[^\n]+") do
        local trimmed = line:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            -- White text for description
            table.insert(lines, string.format('<p>%s</p>', trimmed))
        end
    end
    
    return lines
end

-- Format the cost display (mana = white, health = orange)
local function formatCost(cost, hpCast)
    if not cost or cost == "" then
        return nil
    end
    
    local costStr = tostring(cost)
    
    if hpCast and (hpCast == "yes" or hpCast == "true" or hpCast == "1") then
        -- Health cost - orange (same color as spell name)
        return string.format('<span class="color-pumpkin">%s health</span>', costStr)
    else
        -- Mana cost - white (no special color class needed)
        return string.format('%s mana', costStr)
    end
end

-- Format the cooldown display (white)
local function formatCooldown(cd)
    if not cd or cd == "" then
        return nil
    end
    return string.format('%ss cooldown', cd)
end

-- Format the spell formula with correct colors using separate fields
-- base_damage = "20 Base Damage" (gold)
-- magic_scaling = "75% Magic" (purple/orchid)
-- damage_scaling = "65% Damage" (color-peachy-salmon)
-- attack_speed = "+10 Attack Speed" (gold)
local function formatFormula(baseDamage, magicScaling, damageScaling, attackSpeed)
    if (not baseDamage or baseDamage == "") and 
       (not magicScaling or magicScaling == "") and 
       (not damageScaling or damageScaling == "") and
       (not attackSpeed or attackSpeed == "") then
        return nil
    end
    
    local parts = {}
    
    -- Base damage in gold
    if baseDamage and baseDamage ~= "" then
        table.insert(parts, string.format('<span class="color-gold">%s</span>', baseDamage))
    end
    
    -- Magic scaling in purple/orchid
    if magicScaling and magicScaling ~= "" then
        if #parts > 0 then
            table.insert(parts, string.format(' + <span class="color-orchid">%s</span>', magicScaling))
        else
            table.insert(parts, string.format('<span class="color-orchid">%s</span>', magicScaling))
        end
    end
    
    -- Damage scaling in color-peachy-salmon
    if damageScaling and damageScaling ~= "" then
        if #parts > 0 then
            table.insert(parts, string.format(' + <span class="color-peachy-salmon">%s</span>', damageScaling))
        else
            table.insert(parts, string.format('<span class="color-peachy-salmon">%s</span>', damageScaling))
        end
    end
    
    -- Attack speed in gold
    if attackSpeed and attackSpeed ~= "" then
        if #parts > 0 then
            table.insert(parts, string.format(' + <span class="color-gold">%s</span>', attackSpeed))
        else
            table.insert(parts, string.format('<span class="color-gold">%s</span>', attackSpeed))
        end
    end
    
    if #parts == 0 then
        return nil
    end
    
    return string.format('<p>Spell Formula: %s</p>', table.concat(parts, ""))
end

-- Format magic requirement text
local function formatMagicRequirement(magic)
    if not magic or magic == "" or magic == "0" or tonumber(magic) == 0 then
        return nil
    end
    return string.format('<p>You need %s Magic to use this spell.</p>', magic)
end

-- Format ability requirement text
local function formatAbilityRequirement(ability)
    if not ability or ability == "" or ability == "0" or tonumber(ability) == 0 then
        return nil
    end
    return string.format('<p>You need %s Ability to use this spell.</p>', ability)
end

-- Query spell data from Cargo
local function getSpellData(name)
    local tables = 'Spells'
    local fields = 'name,sprite,type,magic,ability,cost,hp_cast,cooldown,description,base_damage,magic_scaling,damage_scaling,attack_speed,effect'
    local args = {
        where = 'name="' .. name .. '"',
        limit = 1
    }
    
    local result = mw.ext.cargo.query(tables, fields, args)
    
    if result and result[1] then
        return result[1]
    end
    return nil
end

local function formatEffect(effect)
    if not effect or effect == "" then
        return nil
    end

    local lines = {}
    for line in effect:gmatch("[^\n]+") do
        local trimmed = line:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            table.insert(
                lines,
                string.format('<p class="color-columbia">%s</p>', trimmed)
            )
        end
    end

    return lines
end

-- Main spell infobox function
function p.spell(frame)
    local args = frame:getParent().args
    local spellName = args.name or args[1] or mw.title.getCurrentTitle().text
    local float = args.float or "right"
    local spriteSize = args.spriteSize or "64"
    local previewMode = args.preview == "true" or args.preview == "1"
    
    local data
    
    -- If preview mode or direct data provided, use args instead of Cargo
    if previewMode or args.type or args.cost then
        data = {
            name = spellName,
            sprite = args.sprite,
            type = args.type,
            magic = args.magic,
            ability = args.ability,
            cost = args.cost,
            hp_cast = args.hp_cast or args.hpCast,
            cooldown = args.cooldown or args.cd,
            description = args.description or args.desc,
            base_damage = args.base_damage or args.baseDamage,
            magic_scaling = args.magic_scaling or args.magicScaling,
            damage_scaling = args.damage_scaling or args.damageScaling,
            attack_speed = args.attack_speed or args.attackSpeed,
            effect = args.effect
        }
    else
        -- Query Cargo for spell data
        data = getSpellData(spellName)
        
        if not data then
            return '<span class="error">Spell not found: ' .. (spellName or 'nil') .. '</span>'
        end
    end
    
    local spellType = string.lower(data.type or "physical")
    local typeColor = typeColors[spellType] or "coral"
    -- Sprite must be explicitly provided (like ItemEntry does with sprite=Red Book)
    local spellbookSprite = data.sprite or data.name
    
    local html = {}
    
    -- Container with tooltip-panel styling (uses existing class from Common.css)
    -- Add text-align:left to override the centered default
    if float == "none" then
        table.insert(html, '<div class="tooltip-panel font-apogea-long infobox" style="text-align:left;">')
    else
        table.insert(html, string.format('<div class="tooltip-panel font-apogea-long infobox" style="float:%s; margin-%s:15px; margin-bottom:10px; text-align:left;">', 
            float, 
            float == "right" and "left" or "right"))
    end
    
    -- Sprite
    table.insert(html, string.format('{{Sprite|%s|%s|class=pageimage}}', spellbookSprite, spriteSize))
    
    -- Spell name (orange, bitcell font) with cost and cooldown in parentheses (white)
    local costDisplay = formatCost(data.cost, data.hp_cast)
    local cooldownDisplay = formatCooldown(data.cooldown)
    
    local titleParts = {}
    if costDisplay then
        table.insert(titleParts, costDisplay)
    end
    if cooldownDisplay then
        table.insert(titleParts, cooldownDisplay)
    end
    
    if #titleParts > 0 then
        table.insert(html, string.format('<p class="font-bitcell"><span class="color-pumpkin">%s</span> (%s):</p>', 
            data.name, 
            table.concat(titleParts, ", ")))
    else
        table.insert(html, string.format('<p class="font-bitcell color-pumpkin">%s</p>', data.name))
    end
    
    -- Description (white)
    local descLines = formatDescription(data.description)
    for _, line in ipairs(descLines) do
        table.insert(html, line)
    end
    
    -- Spell type (colored based on type - e.g., "Fire Spell" in orange/coral)
    local typeDisplay = data.type or "Physical"
    typeDisplay = typeDisplay:sub(1,1):upper() .. typeDisplay:sub(2):lower()
    table.insert(html, string.format('<p class="color-%s">%s Spell</p>', typeColor, typeDisplay))
    
    -- Spell formula (base damage & attack speed in gold, magic/damage scaling in purple)
    local formulaDisplay = formatFormula(data.base_damage, data.magic_scaling, data.damage_scaling, data.attack_speed)
    if formulaDisplay then
        table.insert(html, formulaDisplay)
    end
    
    -- Spell effect (white text, shown below formula)
	local effectLines = formatEffect(data.effect)
	if effectLines then
	    for _, line in ipairs(effectLines) do
	        table.insert(html, line)
	    end
	end
    
    -- Magic requirement (white)
    local magicReq = formatMagicRequirement(data.magic)
    if magicReq then
        table.insert(html, magicReq)
    end
    
    -- Ability requirement (white)
    local abilityReq = formatAbilityRequirement(data.ability)
    if abilityReq then
        table.insert(html, abilityReq)
    end
    
    table.insert(html, '</div>')
    
    -- Categories (skip in preview mode)
    local categories = ''
    if not previewMode then
        categories = '[[Category:Spells]]'
        if data.type and data.type ~= "" then
            local typeCapitalized = data.type:sub(1,1):upper() .. data.type:sub(2):lower()
            categories = categories .. '[[Category:' .. pluralize(typeCapitalized) .. ']]'
        end
    end
    
    return frame:preprocess(table.concat(html, '\n')) .. categories
end

-- Compact spell display for lists/tables
function p.spellLink(frame)
    local args = frame:getParent().args
    local spellName = args.name or args[1]
    
    if not spellName then
        return '<span class="error">No spell name provided</span>'
    end
    
    local data = getSpellData(spellName)
    
    if not data then
        return '[[' .. spellName .. ']]'
    end
    
    -- Use sprite from Cargo data
    local sprite = data.sprite or spellName
    
    local html = string.format('{{Sprite|%s|16}} [[%s]]', sprite, spellName)
    
    return frame:preprocess(html)
end

return p