Editing Module:Abilities/cardGive feedback
Jump to navigation
Jump to search
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 2: | Line 2: | ||
local commonutils = require "Module:Utilities" | local commonutils = require "Module:Utilities" | ||
local utils = require "Module:Abilities/utils" | local utils = require "Module:Abilities/utils" | ||
local p = {} | local p = {} | ||
local data = mw.loadJsonData("Data:AbilityCards.json") | 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='', color = 'Purple'}, | |||
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', color = 'Purple'}, | |||
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', color = 'Purple'}, | |||
duration = {img='AttributeIconTechDuration.png', link='Ability Duration', color = 'Purple'}, | |||
slow = {img='MoveSlow.png', link='', color = 'Purple'}, | |||
melee_damage = {img='Melee damage.png', link='Melee Damage'}, | |||
cooldown = {img='Cooldown Icon.png', link='Ability Cooldown', color = 'Purple'}, | |||
combat_barrier = {img='Barrier.png', link='Barrier', color = 'Green'}, | |||
time = {img='AttributeIconTechDuration.png', link='Ability Duration', color = 'Purple'}, | |||
} | |||
function get_hero_key(hero_name) | function get_hero_key(hero_name) | ||
| Line 15: | Line 37: | ||
if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end | if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end | ||
for i, hero in pairs(data) do | |||
return | if hero["Name"] == hero_name then | ||
return i | |||
end | |||
end | |||
return nil | |||
end | end | ||
| Line 63: | Line 89: | ||
local info1_desc = get_info_desc(hero_key, ability_num, 1) | local info1_desc = get_info_desc(hero_key, ability_num, 1) | ||
local info1_main_boxes = get_main_boxes(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) | local info1_alt_boxes = get_alt_boxes(hero_key, ability_num, 1) | ||
if #info1_alt_boxes > | if #info1_alt_boxes > 6 then | ||
error(#info1_alt_boxes .. ' alt boxes found, but only | error(#info1_alt_boxes .. ' alt boxes found, but only 6 are supported. Please update Module:Abilities/card and Template:Ability_card_v2') | ||
end | end | ||
| Line 74: | Line 119: | ||
local info2_alt_boxes = get_alt_boxes(hero_key, ability_num, 2) | local info2_alt_boxes = get_alt_boxes(hero_key, ability_num, 2) | ||
if #info2_alt_boxes > | if #info2_alt_boxes > 6 then | ||
error(#info2_alt_boxes .. ' alt boxes found, but only | error(#info2_alt_boxes .. ' alt boxes found, but only 6 are supported. Please update Module:Abilities/card and Template:Ability_card_v2') | ||
end | end | ||
| Line 82: | Line 127: | ||
local info3_alt_boxes = get_alt_boxes(hero_key, ability_num, 3) | local info3_alt_boxes = get_alt_boxes(hero_key, ability_num, 3) | ||
if #info3_alt_boxes > | if #info3_alt_boxes > 6 then | ||
error(#info3_alt_boxes .. ' alt boxes found, but only | error(#info3_alt_boxes .. ' alt boxes found, but only 6 are supported. Please update Module:Abilities/card and Template:Ability_card_v2') | ||
end | end | ||
| Line 91: | Line 136: | ||
title = "Ability_card_v2/Card", | title = "Ability_card_v2/Card", | ||
args = { | args = { | ||
hero_key = hero_key, | hero_key = hero_key, | ||
ability_num = ability_num, | ability_num = ability_num, | ||
| Line 100: | Line 144: | ||
icon = lang.get_string(ability.Key, 'en') .. '.png', | icon = lang.get_string(ability.Key, 'en') .. '.png', | ||
channel_time = ability.AbilityChannelTime and ability.AbilityChannelTime.Value ~= 9999 and ability.AbilityChannelTime.Value, | channel_time = ability.Cast and ability.Cast.AbilityChannelTime and ability.Cast.AbilityChannelTime.Value ~= 9999 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 = ability.Radius and ability.Radius.Value, | ||
radius_ss = get_attr_ss(ability.Radius), | |||
range = ability.AbilityCastRange and ability.AbilityCastRange.Value, | range = ability.AbilityCastRange and ability.AbilityCastRange.Value, | ||
range_ss = get_attr_ss(ability.AbilityCastRange), | |||
duration = ability.AbilityDuration and ability.AbilityDuration.Value, | 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]), | -- ability_width = format_value_with_prepost(width_key, ability[width_key]), | ||
cooldown =ability.AbilityCooldown and ability.AbilityCooldown.Value, | 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), | |||
charge_cooldown = ability.AbilityCooldownBetweenCharge and ability.AbilityCooldownBetweenCharge.Value | num_of_charges = ability.AbilityCharges and ability.AbilityCharges.Value, | ||
num_of_charges = ability.AbilityCharges and ability.AbilityCharges.Value | |||
-- Info section #1 defined in "Info1" attribute | -- Info section #1 defined in "Info1" attribute | ||
info1_desc = info1_desc, | info1_desc = info1_desc, | ||
info1_mainbox1 = info1_main_boxes[1], | info1_mainbox1 = info1_main_boxes[1], | ||
info1_mainbox2 = info1_main_boxes[2], | info1_mainbox2 = info1_main_boxes[2], | ||
info1_mainbox3 = info1_main_boxes[3], | info1_mainbox3 = info1_main_boxes[3], | ||
info1_altbox1 = info1_alt_boxes[1], | info1_altbox1 = info1_alt_boxes[1], | ||
info1_altbox2 = info1_alt_boxes[2], | info1_altbox2 = info1_alt_boxes[2], | ||
| Line 141: | Line 170: | ||
info1_altbox5 = info1_alt_boxes[5], | info1_altbox5 = info1_alt_boxes[5], | ||
info1_altbox6 = info1_alt_boxes[6], | info1_altbox6 = info1_alt_boxes[6], | ||
-- Info section #2 defined in "Info2" attribute | -- Info section #2 defined in "Info2" attribute | ||
info2_desc = info2_desc, | info2_desc = info2_desc, | ||
info2_mainbox1 = info2_main_boxes[1], | info2_mainbox1 = info2_main_boxes[1], | ||
info2_mainbox2 = info2_main_boxes[2], | info2_mainbox2 = info2_main_boxes[2], | ||
info2_mainbox3 = info2_main_boxes[3], | info2_mainbox3 = info2_main_boxes[3], | ||
info2_altbox1 = info2_alt_boxes[1], | info2_altbox1 = info2_alt_boxes[1], | ||
info2_altbox2 = info2_alt_boxes[2], | info2_altbox2 = info2_alt_boxes[2], | ||
| Line 157: | Line 182: | ||
info2_altbox5 = info2_alt_boxes[5], | info2_altbox5 = info2_alt_boxes[5], | ||
info2_altbox6 = info2_alt_boxes[6], | info2_altbox6 = info2_alt_boxes[6], | ||
-- Info section #3 defined in "Info3" attribute | -- Info section #3 defined in "Info3" attribute | ||
info3_desc = info3_desc, | info3_desc = info3_desc, | ||
info3_mainbox1 = info3_main_boxes[1], | info3_mainbox1 = info3_main_boxes[1], | ||
info3_mainbox2 = info3_main_boxes[2], | info3_mainbox2 = info3_main_boxes[2], | ||
info3_mainbox3 = info3_main_boxes[3], | info3_mainbox3 = info3_main_boxes[3], | ||
info3_altbox1 = info3_alt_boxes[1], | info3_altbox1 = info3_alt_boxes[1], | ||
info3_altbox2 = info3_alt_boxes[2], | info3_altbox2 = info3_alt_boxes[2], | ||
| Line 173: | Line 194: | ||
info3_altbox5 = info3_alt_boxes[5], | info3_altbox5 = info3_alt_boxes[5], | ||
info3_altbox6 = info3_alt_boxes[6], | info3_altbox6 = info3_alt_boxes[6], | ||
-- Ability upgrades defined in "Upgrades" attribute | -- Ability upgrades defined in "Upgrades" attribute | ||
upgrade1 = upgrades[1], | upgrade1 = upgrades[1], | ||
| Line 187: | Line 207: | ||
end | end | ||
-- Get spirit scaling of attribute, return nil if none is found | |||
function get_attr_ss(attr) | |||
if not attr then | |||
return nil | |||
-- | |||
if not attr then | |||
return nil | |||
end | end | ||
local scale = attr.Scale | local scale = attr.Scale | ||
if not scale then | if not scale then | ||
return nil | return nil | ||
end | end | ||
if scale. | if scale.Type ~= 'spirit' then | ||
return nil | return nil | ||
end | end | ||
| Line 255: | Line 226: | ||
end | end | ||
return | return commonutils.round_to_sig_fig(scale.Value, 3) | ||
end | end | ||
| Line 276: | Line 237: | ||
local ability = utils.get_ability_card_data(hero_key, ability_num) | local ability = utils.get_ability_card_data(hero_key, ability_num) | ||
if(ability == nil) then return | if(ability == nil) then return "Ability Not Found" end | ||
local info_section = ability['Info'..info_section_num] | local info_section = ability['Info'..info_section_num] | ||
| Line 307: | Line 268: | ||
function get_main_boxes(hero_key, ability_num, info_section_num) | function get_main_boxes(hero_key, ability_num, info_section_num) | ||
local ability = utils.get_ability_card_data(hero_key, ability_num) | local ability = utils.get_ability_card_data(hero_key, ability_num) | ||
if(ability == nil) then return | if(ability == nil) then return {} end -- return empty table, not string | ||
local info_section = ability['Info'..info_section_num] | local info_section = ability['Info'..info_section_num] | ||
-- some abilities have no info sections | -- some abilities have no info sections | ||
if info_section == nil then return | if info_section == nil then return {} end | ||
local main = info_section.Main | local main = info_section.Main | ||
| Line 329: | Line 290: | ||
end | end | ||
end | end | ||
local main_boxes = {} | local main_boxes = {} | ||
for k, prop in ipairs(props) do -- use ipairs for ordered iteration | for k, prop in ipairs(props) do -- use ipairs for ordered iteration | ||
local value = prop.Value | 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 it scales with melee damage, set the value using the hero's base melee to match in game visuals | ||
if | if prop.Scale and prop.Scale.Type == 'melee' then | ||
local hero = get_hero_data(hero_key) | local hero = get_hero_data(hero_key) | ||
value = prop.Value + hero.LightMeleeDamage * | value = prop.Value + hero.LightMeleeDamage * prop.Scale.Value | ||
end | end | ||
-- Exclude 0 values | |||
if value and value ~= 0 then | |||
local icon = get_icon(prop.Type) | |||
if | local main_box = frame:expandTemplate{ | ||
main_box = frame:expandTemplate{ | |||
title = "Ability_card_v2/Card/MainBox", | title = "Ability_card_v2/Card/MainBox", | ||
args = { | args = { | ||
title = prop.Title, | title = prop.Title, | ||
key = prop.Key, | key = prop.Key, | ||
value = value, | value = utils.format_value_with_prepost(prop.Key, value, frame), | ||
icon = | icon = icon.img, | ||
icon_link = | icon_link = icon.link, | ||
icon_color = | icon_color = icon.color, | ||
icon_size = | icon_size = icon.size, | ||
scale_value = | scale_value = prop.Scale and commonutils.round_to_sig_fig(prop.Scale.Value, 3), | ||
scale_type = | scale_type = prop.Scale and prop.Scale.Type | ||
} | } | ||
} | } | ||
table.insert(main_boxes, main_box) | |||
end | end | ||
end | end | ||
| Line 416: | Line 349: | ||
local alt_boxes = {} | 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 = utils.format_value_with_prepost(prop.Key, prop.Value, frame), | |||
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 | return alt_boxes | ||
| Line 452: | Line 375: | ||
local UPGRADE_COST_MAP = {1, 2, 5} | local UPGRADE_COST_MAP = {1, 2, 5} | ||
function get_upgrade_boxes(hero_key, ability_num) | function get_upgrade_boxes(hero_key, ability_num) | ||
local ability = utils.get_ability_card_data(hero_key, ability_num) | local ability = utils.get_ability_card_data(hero_key, ability_num) | ||
| Line 471: | Line 384: | ||
local upgrade_boxes = {} | local upgrade_boxes = {} | ||
for k, upgrade in | for k, upgrade in pairs(upgrades) do | ||
local description = lang.get_string(upgrade.DescKey) | local description = lang.get_string(upgrade.DescKey) | ||
-- bypass some keys if they are mistakenly left in game files | -- bypass some keys if they are mistakenly left in game files | ||
local IGNORE_DESC_KEYS = {'citadel_ability_hornet_snipe_t3_desc | local IGNORE_DESC_KEYS = {'citadel_ability_hornet_snipe_t3_desc'} | ||
-- Generate a description if there is no localized string | -- Generate a description if there is no localized string | ||
if (description == nil or description == '' or commonutils.contains(IGNORE_DESC_KEYS, upgrade.DescKey)) then | if (description == nil or description == '' or commonutils.contains(IGNORE_DESC_KEYS, upgrade.DescKey)) then | ||
description = create_description(upgrade, frame | description = create_description(upgrade, frame) | ||
end | end | ||
-- | -- Vary the font size based on the number of characters to prevent overflow | ||
local fontsize = '1rem' | |||
local | 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{ | local upgrade_box = frame:expandTemplate{ | ||
title = "Ability_card_v2/Card/UpgradeBox", | title = "Ability_card_v2/Card/UpgradeBox", | ||
args = { | args = { | ||
cost = UPGRADE_COST_MAP[k], | |||
description = frame:preprocess(description), | |||
scale_value = upgrade.Scale and commonutils.round_to_sig_fig(upgrade.Scale.Value, 3), | |||
scale_type = upgrade.Scale and upgrade.Scale.Type, | |||
fontsize = fontsize | |||
} | } | ||
} | } | ||
| Line 522: | Line 420: | ||
return upgrade_boxes | return upgrade_boxes | ||
end | end | ||
function create_description(prop, frame) | function create_description(prop, frame) | ||
local | local description = '' | ||
local | local first = true | ||
local seen = {} | local seen = {} -- Set to track added combinations | ||
for k, v in pairs(prop) do | for k, v in pairs(prop) do | ||
if | local value | ||
if type(v) == 'table' then | |||
value = v.Value | |||
else | |||
value = v | |||
end | |||
local formatted_value = utils.format_value_with_prepost(k, value, frame) | |||
local attr_name = lang.get_string(k..'_label') | |||
local key = formatted_value .. '|' .. attr_name -- Unique identifier | |||
-- skip DescKey for the edge case where we are generating description instead of using localization files | |||
if k ~= 'DescKey' then | |||
-- 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 .. '<br>' | |||
end | |||
end | |||
description = description .. string.format('%s %s', formatted_value, attr_name) | |||
first = false | |||
seen[key] = true -- Mark this combination as added | |||
end | 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('[[File:%s|%s|18px|link=%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 | end | ||
| Line 660: | Line 554: | ||
return mw.getCurrentFrame():expandTemplate{ | return mw.getCurrentFrame():expandTemplate{ | ||
title = "Ability card v2/Card", | title = "Template:Ability card v2/Card", | ||
args = { | args = { | ||
hero_key = hero_key, | hero_key = hero_key, | ||
| Line 668: | Line 562: | ||
icon = lang.get_string(ability.Key, 'en') .. '.png', | icon = lang.get_string(ability.Key, 'en') .. '.png', | ||
description = mw.getCurrentFrame():preprocess(lang.get_string(ability.DescKey)), | description = mw.getCurrentFrame():preprocess(lang.get_string(ability.DescKey)), | ||
radius = ability.Radius and ability.Radius.Value, | radius = ability.Radius and ability.Radius.Value, | ||
radius_ss = get_attr_ss(ability.Radius), | |||
range = ability.AbilityCastRange and ability.AbilityCastRange.Value, | range = ability.AbilityCastRange and ability.AbilityCastRange.Value, | ||
range_ss = get_attr_ss(ability.AbilityCastRange), | |||
duration = ability.AbilityDuration and ability.AbilityDuration.Value, | 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]), | -- ability_width = format_value_with_prepost(width_key, ability[width_key]), | ||
cooldown =ability.AbilityCooldown and ability.AbilityCooldown.Value, | 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), | |||
charge_cooldown = ability.AbilityCooldownBetweenCharge | num_of_charges = ability.AbilityCharges and ability.AbilityCharges.Value, | ||
notes = notes, | |||
notes_source_page = notes_source_page | |||
num_of_charges = ability.AbilityCharges and ability.AbilityCharges.Value | |||
} | } | ||
} | } | ||