Module:Abilities-test/card: Difference between revisionsGive feedback
Created page with "local lang = require "Module:Lang" local commonutils = require "Module:Utilities" local utils = require "Module:Abilities/utils" local p = {} local data = mw.loadJsonData("Data:AbilityCards.json") -- Maps attr type to an appropriate image and page link -- Optional icon size, will be defaulted on the template local ATTR_TYPE_ICON_MAP = { bullet_armor_up = {img='Bullet_Armor.png', link='Damage_Resistance', color = 'NoColor'}, bullet_armor_down = {img='Bullet_Armor.png'..." |
m Saag moved page User:Saag/Sandbox/Abilities to Module:Abilities-test/card |
||
(No difference)
| |||
Revision as of 21:45, 21 September 2025
local lang = require "Module:Lang" local commonutils = require "Module:Utilities" local utils = require "Module:Abilities/utils"
local p = {} local data = mw.loadJsonData("Data:AbilityCards.json")
-- Maps attr type to an appropriate image and page link -- Optional icon size, will be defaulted on the template local ATTR_TYPE_ICON_MAP = { bullet_armor_up = {img='Bullet_Armor.png', link='Damage_Resistance', color = 'NoColor'}, bullet_armor_down = {img='Bullet_Armor.png', link='Damage_Resistance', color = 'NoColor'}, cast = {img='AttributeIconMaxChargesIncrease.png', link=}, charges = {img='AttributeIconMaxChargesIncrease.png', link=}, damage = {img='Damage_heart.png', link=, color = 'NoColor'}, -- 'NoColor' will apply no icon color and use the original image bullet_damage = {img='Bullet_damage.png', link='Bullet_Damage', color = 'NoColor'}, fire_rate = {img='Fire Rate.png', link='Fire_Rate', color = 'Brown'}, healing = {img='Healing.png', link='Healing', color = 'Green'}, health = {img='Health.png', link='Health', color = 'Green'}, move_speed = {img='Move speed.png', link='Move Speed', color = 'Green'}, range = {img='CastRange.png', link='Ability_Range'}, tech_armor_up = {img='Spirit_Armor.png', link='Damage_Resistance', color = 'NoColor'}, tech_damage = {img='AttributeIconTechShieldHealth.png', link='Spirit_Damage', color = 'NoColor',size = '12px'}, distance = {img='AttributeIconTechRange.png', link='Ability_Range'}, duration = {img='AttributeIconTechDuration.png', link='Ability Duration'}, slow = {img='MoveSlow.png', link=}, melee_damage = {img='Melee damage.png', link='Melee Damage'}, cooldown = {img='AttributeIconTechDuration.png', link='Ability Cooldown'}, combat_barrier = {img='Barrier.png', link='Barrier', color = 'Green'}, }
function get_hero_key(hero_name) for i, hero in pairs(data) do if hero["Name"] == hero_name then return i end end return nil end
--Lua error in Module:Abilities/card at line 18: attempt to index field '?' (a nil value).-- -- Args: -- HERO_NAME (required) - Name of the hero that is found in Data:HeroData.json under "Name" -- ABILITY_NUM (required) - Selects ability at index 1 to 4 -- ADD_LINK - Add a hyperlink to the title to the associated page -- NOTES - User note data to include in the footer of the ability card p.get_ability_card = function(frame) local hero_name = frame.args[1] local ability_num = frame.args[2] local add_link = frame.args[3] local notes = frame.args[4]
local hero_key = get_hero_key(hero_name) if (hero_key == nil) then return 'Hero with name' .. hero_name .. 'not found' end
return build_ability_card(hero_key, ability_num, add_link, notes) end
-- Pulls data from Data:AbilityCards.json to populate Template:Ability card v2 function build_ability_card(hero_key, ability_num, add_link, notes, notes_source_page) local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return 'Ability data not found for hero ' ..hero_key.. ' and num ' .. ability_num end
local ability_name_localized = lang.get_string(ability.Key) local name_link = nil if add_link == 'true' then name_link = ability_name_localized end
if notes_source_page ~= nil and notes ~= "" then -- Notes comes from a /Notes page, and the notes are not blank -- Confirm the notes source page exists, otherwise, don't display any notes local title = mw.title.new(notes_source_page) if not (title and title.exists) then notes = "" end end
local frame = mw.getCurrentFrame()
local info1_desc = get_info_desc(hero_key, ability_num, 1) local info1_main_boxes = get_main_boxes(hero_key, ability_num, 1)
-- Inject Hex Duration only for Rabbit Hex if ability.Key == "ability_magician_animalhexarea" and ability.Other and ability.Other.HexDuration and ability.Other.HexDuration.Value then local icon = get_icon("duration") table.insert(info1_main_boxes, 1, frame:expandTemplate{ title = "Ability_card_v2/Card/MainBox", args = { key = "HexDuration", value = ability.Other.HexDuration.Value, icon = icon.img, icon_link = icon.link, icon_color = icon.color, } }) end
local info1_alt_boxes = get_alt_boxes(hero_key, ability_num, 1)
if #info1_alt_boxes > 6 then error(#info1_alt_boxes .. ' alt boxes found, but only 6 are supported. Please update Module:Abilities/card and Template:Ability_card_v2') end
local info2_desc = get_info_desc(hero_key, ability_num, 2) local info2_main_boxes = get_main_boxes(hero_key, ability_num, 2) local info2_alt_boxes = get_alt_boxes(hero_key, ability_num, 2)
if #info2_alt_boxes > 6 then error(#info2_alt_boxes .. ' alt boxes found, but only 6 are supported. Please update Module:Abilities/card and Template:Ability_card_v2') end
local info3_desc = get_info_desc(hero_key, ability_num, 3) local info3_main_boxes = get_main_boxes(hero_key, ability_num, 3) local info3_alt_boxes = get_alt_boxes(hero_key, ability_num, 3)
if #info3_alt_boxes > 6 then error(#info3_alt_boxes .. ' alt boxes found, but only 6 are supported. Please update Module:Abilities/card and Template:Ability_card_v2') end
local upgrades = get_upgrade_boxes(hero_key, ability_num)
return frame:expandTemplate{ title = "User:Saag/Sandbox/Ability_card_v2/Card", args = { hero_key = hero_key, ability_num = ability_num,
-- Header info defined in various attributes name = ability_name_localized, name_link = name_link, icon = lang.get_string(ability.Key, 'en') .. '.png',
channel_time = ability.Cast and ability.Cast.AbilityChannelTime and ability.Cast.AbilityChannelTime.Value, channel_time_ss = ability.Cast and get_attr_ss(ability.Cast.AbilityChannelTime), radius = ability.Radius and ability.Radius.Value, radius_ss = get_attr_ss(ability.Radius), range = ability.AbilityCastRange and ability.AbilityCastRange.Value, range_ss = get_attr_ss(ability.AbilityCastRange), duration = ability.AbilityDuration and ability.AbilityDuration.Value, duration_ss = get_attr_ss(ability.AbilityDuration), -- ability_width = format_value_with_prepost(width_key, ability[width_key]), cooldown =ability.AbilityCooldown and ability.AbilityCooldown.Value, cooldown_ss = get_attr_ss(ability.AbilityCooldown), charge_cooldown = ability.AbilityCooldownBetweenCharge and ability.AbilityCooldownBetweenCharge.Value, charge_cooldown_ss = get_attr_ss(ability.AbilityCooldownBetweenCharge), num_of_charges = ability.AbilityCharges and ability.AbilityCharges.Value,
-- Info section #1 defined in "Info1" attribute info1_desc = info1_desc, info1_mainbox1 = info1_main_boxes[1], info1_mainbox2 = info1_main_boxes[2], info1_mainbox3 = info1_main_boxes[3], info1_altbox1 = info1_alt_boxes[1], info1_altbox2 = info1_alt_boxes[2], info1_altbox3 = info1_alt_boxes[3], info1_altbox4 = info1_alt_boxes[4], info1_altbox5 = info1_alt_boxes[5], info1_altbox6 = info1_alt_boxes[6],
-- Info section #2 defined in "Info2" attribute info2_desc = info2_desc, info2_mainbox1 = info2_main_boxes[1], info2_mainbox2 = info2_main_boxes[2], info2_mainbox3 = info2_main_boxes[3], info2_altbox1 = info2_alt_boxes[1], info2_altbox2 = info2_alt_boxes[2], info2_altbox3 = info2_alt_boxes[3], info2_altbox4 = info2_alt_boxes[4], info2_altbox5 = info2_alt_boxes[5], info2_altbox6 = info2_alt_boxes[6],
-- Info section #3 defined in "Info3" attribute info3_desc = info3_desc, info3_mainbox1 = info3_main_boxes[1], info3_mainbox2 = info3_main_boxes[2], info3_mainbox3 = info3_main_boxes[3], info3_altbox1 = info3_alt_boxes[1], info3_altbox2 = info3_alt_boxes[2], info3_altbox3 = info3_alt_boxes[3], info3_altbox4 = info3_alt_boxes[4], info3_altbox5 = info3_alt_boxes[5], info3_altbox6 = info3_alt_boxes[6],
-- Ability upgrades defined in "Upgrades" attribute upgrade1 = upgrades[1], upgrade2 = upgrades[2], upgrade3 = upgrades[3],
-- User-created ability notes notes = notes, notes_source_page = notes_source_page } } end
-- Get spirit scaling of attribute, return nil if none is found function get_attr_ss(attr) if not attr then return nil end
local scale = attr.Scale if not scale then return nil end
if scale.Type ~= 'spirit' then return nil end
if scale.Value == 0 then return nil end
return commonutils.round_to_sig_fig(scale.Value, 3) end
-- Get the description for an ability's info section --Script error: The function "get_info_desc" does not exist.-- p.get_info_desc = function(frame) local hero_key = frame.args[1] local ability_num = frame.args[2] local info_section_num = frame.args[3]
local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return "Ability Not Found" end local info_section = ability['Info'..info_section_num]
-- some abilities have no info sections if info_section == nil then return end
if info_section.DescKey == nil then return end
return frame:preprocess(lang.get_string(info_section.DescKey)) end
function get_info_desc(hero_key, ability_num, info_section_num) local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return "Ability Not Found" end local info_section = ability['Info'..info_section_num]
-- some abilities have no info sections if info_section == nil then return end
if info_section.DescKey == nil then return end
local frame = mw.getCurrentFrame() return frame:preprocess(lang.get_string(info_section.DescKey)) end
function get_main_boxes(hero_key, ability_num, info_section_num) local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return "Ability Not Found" end
local info_section = ability['Info'..info_section_num]
-- some abilities have no info sections if info_section == nil then return end
local main = info_section.Main if main == nil then return end
local frame = mw.getCurrentFrame()
local props = info_section.Main.Props
local main_boxes = {} for k, prop in pairs(props) do local value = prop.Value -- If it scales with melee damage, set the value using the hero's base melee to match in game visuals if prop.Scale and prop.Scale.Type == 'melee' then local hero = get_hero_data(hero_key) value = prop.Value + hero.LightMeleeDamage * prop.Scale.Value end
-- Exclude 0 values if value and value ~= 0 then local icon = get_icon(prop.Type) local main_box = frame:expandTemplate{ title = "Ability_card_v2/Card/MainBox", args = { title = prop.Title, key = prop.Key, value = value, icon = icon.img, icon_link = icon.link, icon_color = icon.color, icon_size = icon.size, scale_value = prop.Scale and commonutils.round_to_sig_fig(prop.Scale.Value, 3), scale_type = prop.Scale and prop.Scale.Type } } table.insert(main_boxes, main_box) end end
return main_boxes end
function get_hero_data(hero_key) local hero_data = mw.loadJsonData("Data:HeroData.json") local hero = hero_data[hero_key] if (hero == nil) then return "Hero Not Found" end return hero end
function get_alt_boxes(hero_key, ability_num, info_section_num) local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return "Ability Not Found" end
local info_section = ability['Info'..info_section_num]
-- some abilities have no info sections if info_section == nil then return end
local props = info_section.Alt if props == nil then return end
local frame = mw.getCurrentFrame()
local alt_boxes = {}
for k, prop in pairs(props) do -- Some props don't have values, as those come from upgrades -- For now, we will ignore these and only show data for the base ability if prop.Value and prop.Value ~= 0 then local icon = get_icon(prop.Type) local alt_box = frame:expandTemplate{ title = "Ability_card_v2/Card/AltBox", args = { key = prop.Key, value = prop.Value, icon = icon.img, icon_link = icon.link, icon_color = icon.color,
icon_size = icon.size,
scale_value = prop.Scale and commonutils.round_to_sig_fig(prop.Scale.Value, 3), scale_type = prop.Scale and prop.Scale.Type } } table.insert(alt_boxes, alt_box) end end
return alt_boxes end
local UPGRADE_COST_MAP = {1, 2, 5} function get_upgrade_boxes(hero_key, ability_num) local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return "Ability Not Found" end
local frame = mw.getCurrentFrame()
local props = ability.Upgrades
local upgrade_boxes = {} for k, prop in pairs(props) do local description = lang.get_string(prop.DescKey)
-- Generate a description if there is no localized string if (description == nil or description == ) then description = create_description(prop, frame) end
-- Vary the font size based on the number of characters to prevent overflow local fontsize = '1rem' if #description > 60 and #description < 71 then fontsize = '0.95rem' elseif #description > 70 and #description < 91 then fontsize = '0.875rem' elseif #description > 90 then fontsize = '0.8rem' end
local upgrade_box = frame:expandTemplate{ title = "Ability_card_v2/Card/UpgradeBox", args = { cost = UPGRADE_COST_MAP[k], description = frame:preprocess(description), scale_value = prop.Scale and commonutils.round_to_sig_fig(prop.Scale.Value, 3), scale_type = prop.Scale and prop.Scale.Type, fontsize = fontsize } } table.insert(upgrade_boxes, upgrade_box) end
return upgrade_boxes end
function create_description(prop, frame)
local description =
local first = true
local seen = {} -- Set to track added combinations
for k, v in pairs(prop) do
if type(v) ~= 'table' then
local formatted_value = utils.format_value_with_prepost(k, v, frame)
local attr_name = lang.get_string(k..'_label')
local key = formatted_value .. '|' .. attr_name -- Unique identifier
-- Skip if this combination has already been added
if not seen[key] then
-- Add line break if this is not the first item
if not first then
description = description .. '
'
end
description = description .. string.format('%s %s', formatted_value, attr_name)
first = false
seen[key] = true -- Mark this combination as added
end
end
end
return description
end
p.get_attr_icon = function(frame) local attr_type = frame.args[1] local icon = get_icon(attr_type)
return string.format('%s', icon.img or , icon.size or , icon.link or ) end
function get_icon(attr_type) local mappedAttr = ATTR_TYPE_ICON_MAP[attr_type] local img = 'GenericProperty.png' local link = local size = local color = 'Grey' if mappedAttr then img = mappedAttr.img link = mappedAttr.link size = mappedAttr.size color = mappedAttr.color or color end
return {img=img, link=link, color=color, size=size} end
function find_width_key(ability) for key, value in pairs(ability) do if type(key) == "string" and key:sub(-5) == "Width" then return key end end return nil end
--for use from an ability page (ability pages are WIP), not hero pages function p.write_ability_card_from_ability_key(frame) local ability_key = frame.args[1] if ability_key == nil then return "ability_key '" .. ability_key "' not provided" end
-- Determine the hero and ability number local found_hero_key local found_ability_num for hero_key, card_data in pairs(data) do if found_hero_key == nil or found_ability_num == nil then for ability_num, ability_data in pairs(card_data) do if ability_num ~= "Name" then if ability_data['Key'] == ability_key then found_hero_key = hero_key found_ability_num = ability_num break end -- hero_key and ability_num found end end end end
if found_ability_num == nil or found_hero_key == nil then return "ability_key " .. ability_key .. " is not used by any heroes" end
-- Get notes for this ability local notes_source_page_name = utils.get_notes_source_page_name(ability_key) local notes_str = frame:preprocess("Template:"..notes source page name.."")
-- Create the ability card return p.get_ability_card_from_key(found_hero_key, found_ability_num, true, notes_str, notes_source_page_name) end
--from hero key* --notes parameter will eventually be removed, as notes_source_page would be sufficient or could even be determined from within this function function p.get_ability_card_from_key(hero_key, ability_num, add_link, notes, notes_source_page) if type(hero_key) == 'table' and hero_key.args then local frame = hero_key hero_key = frame.args[1] ability_num = frame.args[2] add_link = frame.args[3] notes = frame.args[4] notes_source_page = frame.args[5] end
local ability = utils.get_ability_card_data(hero_key, ability_num) if(ability == nil) then return 'Ability data not found for hero ' ..hero_key.. ' and num ' .. ability_num end
local ability_name_localized = lang.get_string(ability.Key) local name_link = nil if add_link == 'true' then name_link = ability_name_localized end
if notes_source_page ~= nil and notes ~= "" then --Notes comes from a /Notes page, and the notes are not blank -- Confirm the notes source page exists, otherwise, don't display any notes local title = mw.title.new(notes_source_page) if not (title and title.exists) then notes = "" end end
return mw.getCurrentFrame():expandTemplate{ title = "Template:Ability card v2/Card", args = { hero_key = hero_key, ability_num = ability_num, name = ability_name_localized, name_link = name_link, icon = lang.get_string(ability.Key, 'en') .. '.png', description = mw.getCurrentFrame():preprocess(lang.get_string(ability.DescKey)), radius = ability.Radius and ability.Radius.Value, radius_ss = get_attr_ss(ability.Radius), range = ability.AbilityCastRange and ability.AbilityCastRange.Value, range_ss = get_attr_ss(ability.AbilityCastRange), duration = ability.AbilityDuration and ability.AbilityDuration.Value, duration_ss = get_attr_ss(ability.AbilityDuration), -- ability_width = format_value_with_prepost(width_key, ability[width_key]), cooldown =ability.AbilityCooldown and ability.AbilityCooldown.Value, cooldown_ss = get_attr_ss(ability.AbilityCooldown), charge_cooldown = ability.AbilityCooldownBetweenCharge and ability.AbilityCooldownBetweenCharge.Value, charge_cooldown_ss = get_attr_ss(ability.AbilityCooldownBetweenCharge), num_of_charges = ability.AbilityCharges and ability.AbilityCharges.Value, notes = notes, notes_source_page = notes_source_page } } end
return p