ElEditors, Interface administrators, Administrators
70,866
edits
No edit summary |
No edit summary |
||
(45 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
-- pystart | -- pystart | ||
require('Module:CommonFunctions') | require('Module:CommonFunctions') | ||
local i18n = require('Module:I18n') | |||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
local inspect = require('Module:Inspect').inspect | local inspect = require('Module:Inspect').inspect | ||
local getTranslations = i18n.getTranslations | |||
local p = {} | local p = {} | ||
Line 8: | Line 10: | ||
function p.main(frame) | function p.main(frame) | ||
local args = getArgs(frame) | local args = getArgs(frame) | ||
local tr = getTranslations(frame, 'Template:Damage', args.lang, true) | |||
local out | local out | ||
function translate(key) | |||
return i18n.translate(tr, key) | |||
end | |||
function inArgs(key) | function inArgs(key) | ||
Line 27: | Line 34: | ||
for _, mode in ipairs(modes) do | for _, mode in ipairs(modes) do | ||
func(mode) | func(mode) | ||
end | |||
end | |||
function forEachDamageType(func) | |||
for _, damage_type in ipairs({ 'min', 'max' }) do | |||
func(damage_type) | |||
end | end | ||
end | end | ||
Line 45: | Line 58: | ||
do_table = args[1] == 'true', | do_table = args[1] == 'true', | ||
character = args[2] or args.char or 'Elsword', | character = args[2] or args.char or 'Elsword', | ||
lang_suffix = args.lang and ('/' .. args.lang) or '', | |||
lang_append = args.lang ~= nil and args.lang ~= '', | |||
format = args.format ~= 'false', | format = args.format ~= 'false', | ||
no_max = args.no_max == 'true', | no_max = args.no_max == 'true', | ||
Line 85: | Line 100: | ||
{ | { | ||
key = '', | key = '', | ||
name = 'Normal', | name = translate('Normal'), | ||
value = 1 | value = 1 | ||
}, | }, | ||
{ | { | ||
key = 'enhanced', | key = 'enhanced', | ||
name = 'Enhanced (Trait)', | name = translate('Enhanced (Trait)'), | ||
value = args.enhanced ~= nil and 0.8 | value = args.enhanced ~= nil and 0.8 | ||
}, | }, | ||
{ | { | ||
key = 'empowered', | key = 'empowered', | ||
name = 'Empowered', | name = translate('Empowered'), | ||
value = args.empowered == 'true' and 1.2 or tonumber(args.empowered) or false | value = args.empowered == 'true' and 1.2 or tonumber(args.empowered) or false | ||
}, | }, | ||
{ | { | ||
key = 'useful', | key = 'useful', | ||
name = 'Useful', | name = translate('Useful'), | ||
value = (args.hits_useful or args.avg_hits_useful) and (args.useful_penalty or args.useful or 0.8) or false | value = (args.hits_useful or args.avg_hits_useful) and (args.useful_penalty or args.useful or 0.8) or false | ||
}, | }, | ||
{ | { | ||
key = 'heavy', | key = 'heavy', | ||
name = 'Heavy', | name = translate('Heavy'), | ||
value = args.heavy ~= nil and 1.44 | value = args.heavy ~= nil and 1.44 | ||
} | } | ||
Line 126: | Line 141: | ||
--]] | --]] | ||
local passive_name = v | local passive_name = v | ||
local passive_title = v .. OPTIONS.lang_suffix | |||
local is_custom = string.find(k, '_define') ~= nil | local is_custom = string.find(k, '_define') ~= nil | ||
local passive_index = string.match(k, "%d") | local passive_index = string.match(k, "%d") | ||
local passive_values = split(is_custom and v or | local passive_values = split(is_custom and v or | ||
frame:preprocess('{{:' .. passive_name .. '}}{{#arrayprint:' .. passive_name .. '}}')); | frame:preprocess('{{:' .. passive_name .. '}}{{#arrayprint:' .. passive_name .. '}}')); | ||
local display_title | |||
if is_custom then | if is_custom then | ||
passive_name = passive_values[#passive_values] | passive_name = passive_values[#passive_values] | ||
passive_values[#passive_values] = nil | passive_values[#passive_values] = nil | ||
elseif OPTIONS.lang_append then | |||
--[[ | |||
Translate page's display title to passive name. | |||
Customized will override this name, thus no need to perform the translation | |||
--]] | |||
display_title = i18n.getTranslatedTitle(passive_title) | |||
end | end | ||
Line 140: | Line 163: | ||
value = passive_values[1], | value = passive_values[1], | ||
value_pvp = passive_values[2], | value_pvp = passive_values[2], | ||
alias = args['alias' .. passive_index] or (passive_index == OPTIONS.append_index and OPTIONS.append_name), | alias = args['alias' .. passive_index] or (passive_index == OPTIONS.append_index and OPTIONS.append_name) or display_title, | ||
suffix = args['suffix' .. passive_index] and (' ' .. args['suffix' .. passive_index]) or '', | suffix = args['suffix' .. passive_index] and (' ' .. args['suffix' .. passive_index]) or '', | ||
prefix = args['prefix' .. passive_index] and ( | prefix = args['prefix' .. passive_index] and (args['prefix' .. passive_index] .. ' ') or '', | ||
exist = frame:preprocess('{{#ifexist:' .. passive_name .. '|true|false}}') == 'true' | exist = frame:preprocess('{{#ifexist:' .. passive_name .. '|true|false}}') == 'true' | ||
} | } | ||
elseif | elseif string.match(v, '^[()+%-*/%d%s,.i]+$') then | ||
--[[ | --[[ | ||
Change how args are received. | Change how args are received. | ||
Line 257: | Line 280: | ||
else | else | ||
DAMAGE_CONFIG = BASE_DAMAGE_CONFIG | DAMAGE_CONFIG = BASE_DAMAGE_CONFIG | ||
end | |||
-- Helper function to check if a table is not empty | |||
local function isTableNotEmpty(tbl) | |||
return next(tbl) ~= nil | |||
end | |||
-- Function to apply inheritance for a specific damage type and argument | |||
local function applyInheritance(mainArgValues, inheritArg, mainArgValue, inheritValue) | |||
if mainArgValue == '' then | |||
return inheritValue | |||
elseif mainArgValue and string.find(mainArgValue, 'i') and inheritValue then | |||
return eval(mainArgValue:gsub('i', inheritValue)) | |||
end | |||
return mainArgValue | |||
end | |||
-- Function to apply inheritance for a specific argument key | |||
local function applyInheritanceForKey(args, prefix, argTable, damageTypeIndex, damageType) | |||
local mainKey = argTable[1] .. damageType | |||
local mainKeyPrefixed = prefix .. mainKey | |||
local mainArgValues = args[mainKeyPrefixed] | |||
if mainArgValues then | |||
local i = 1 | |||
local cancelDmgLen = args.cancel_dmg and #args.cancel_dmg or 0 | |||
while i <= (#args.dmg + cancelDmgLen) do | |||
local mainArgValue = mainArgValues[i] | |||
for ix, inheritKey in ipairs(argTable) do | |||
local inheritArg = args[prefix .. inheritKey .. damageType] or args[inheritKey .. damageType] | |||
-- Basic damage/hits inheritance request detected. Ignore min/max. | |||
if damageType and mainKey:gsub(damageType, "") == argTable[#argTable] then | |||
inheritArg = args[prefix .. inheritKey] or args[inheritKey] | |||
end | |||
if inheritArg and inheritArg[i] and | |||
(damageTypeIndex == 1 and ix ~= 1 or damageTypeIndex ~= 1) and tonumber(inheritArg[i]) | |||
then | |||
mainArgValues[i] = applyInheritance(mainArgValues, inheritArg, mainArgValue, inheritArg[i]) | |||
break | |||
end | |||
end | |||
i = i + 1 | |||
end | |||
end | |||
end | end | ||
Line 262: | Line 334: | ||
function inherit(mode) | function inherit(mode) | ||
local prefix = mode == 'PvE' and '' or string.lower(mode .. '_') | local prefix = mode == 'PvE' and '' or string.lower(mode .. '_') | ||
for configKey, configValue in pairs(DAMAGE_CONFIG) do | |||
for argTableKey, argTable in pairs(configValue) do | |||
if argTableKey ~= 'provided' and isTableNotEmpty(argTable) then | |||
for damageTypeIndex, damageType in ipairs({ '', '_min', '_max' }) do | |||
applyInheritanceForKey(args, prefix, argTable, damageTypeIndex, damageType) | |||
end | end | ||
end | end | ||
Line 312: | Line 353: | ||
for config_key, config_value in pairs(DAMAGE_CONFIG) do | for config_key, config_value in pairs(DAMAGE_CONFIG) do | ||
for k, v in pairs(config_value) do | for k, v in pairs(config_value) do | ||
local output_value = | local output_value = {} | ||
for _, v2 in ipairs(v) do | |||
local arg_from_template = args[prefix .. v2] or args[v2] | -- When both min and max are found, we need to break from the loop. | ||
local isValueFound = { min = false, max = false } | |||
for _, v2 in ipairs(v) do -- This array holds the argument names with fallbacks | |||
forEachDamageType(function(damage_type) | |||
-- If there already is a value for this damage type (min or max), do not continue. | |||
if isValueFound[damage_type] == true then | |||
return | |||
end | |||
local arg_from_template = | |||
args[prefix .. v2 .. '_' .. damage_type] | |||
or args[v2 .. '_' .. damage_type] | |||
or args[prefix .. v2] | |||
or args[v2]; | |||
if arg_from_template ~= nil then | |||
if k == 'provided' then | |||
output_value = true | |||
-- Do not generate total_damage values at all if the skill can't reach them. | |||
if string.find(config_key, 'total_') and OPTIONS.no_max then | |||
output_value = false | |||
end | |||
else | |||
if type(output_value) ~= "table" then | |||
output_value = {} | |||
end | |||
output_value[damage_type] = arg_from_template | |||
end | |||
-- Mark the value as found. | |||
isValueFound[damage_type] = true | |||
else | |||
if k == 'provided' then | |||
output_value = false | output_value = false | ||
else | |||
output_value[damage_type] = {} | |||
end | end | ||
end | end | ||
end) | |||
-- Both values found, we can now break the loop. | |||
if isValueFound.min and isValueFound.max then | |||
break | break | ||
end | end | ||
end | end | ||
Line 351: | Line 418: | ||
local new_value = table.deep_copy(damage_value) | local new_value = table.deep_copy(damage_value) | ||
for k, hit_count in ipairs(new_value.hit_counts) do | forEachDamageType(function(damage_type) | ||
for k, hit_count in ipairs(new_value.hit_counts[damage_type]) do | |||
hit_count = hit_count == '' and 1 or hit_count | |||
new_value.hit_counts[damage_type][k] = hit_count * | |||
end | ((string.find(damage_key, 'awk') and args.awk_count) and args.awk_count[1] or args.count[1]) | ||
end | |||
end) | |||
WITH_EACH[mode][damage_key:gsub("total_", "each_")] = damage_value | WITH_EACH[mode][damage_key:gsub("total_", "each_")] = damage_value | ||
Line 372: | Line 441: | ||
for mode, mode_content in pairs(DAMAGE_PARSED) do | for mode, mode_content in pairs(DAMAGE_PARSED) do | ||
for damage_key, damage_value in pairs(mode_content) do | for damage_key, damage_value in pairs(mode_content) do | ||
local i = 1 | forEachDamageType(function(damage_type) | ||
local i = 1 | |||
local output = 0 | |||
-- Check if to even generate the damage. | |||
if damage_value.provided then | |||
-- Loop through damage numbers and multiply them with hits. | |||
for k, damage_number in ipairs(damage_value.damage_numbers[damage_type]) do | |||
local hit_count = damage_value.hit_counts[damage_type][i] | |||
hit_count = hit_count == '' and 1 or hit_count | |||
output = output + (damage_number * hit_count) | |||
i = i + 1 | |||
end | |||
-- Write the result to a separate object. | |||
if not BASIC_DAMAGE[mode][damage_key] then | |||
BASIC_DAMAGE[mode][damage_key] = {} | |||
end | |||
BASIC_DAMAGE[mode][damage_key][damage_type] = output | |||
end | end | ||
end) | |||
end | |||
end | end | ||
end | end | ||
Line 397: | Line 471: | ||
for damage_key, damage_value in pairs(mode_content) do | for damage_key, damage_value in pairs(mode_content) do | ||
local cancel_candidate = BASIC_DAMAGE[mode]['cancel_' .. damage_key] | local cancel_candidate = BASIC_DAMAGE[mode]['cancel_' .. damage_key] | ||
if not string.find(damage_key, 'cancel_') and cancel_candidate then | forEachDamageType(function(damage_type) | ||
if not string.find(damage_key, 'cancel_') and cancel_candidate then | |||
end | BASIC_DAMAGE[mode][damage_key][damage_type] = damage_value[damage_type] + | ||
cancel_candidate[damage_type] | |||
end | |||
end) | |||
end | end | ||
end | end | ||
Line 421: | Line 498: | ||
--]] | --]] | ||
if (trait.value and trait.key ~= 'useful') or (string.find(damage_key, 'useful') and trait.key == 'useful') then | if (trait.value and trait.key ~= 'useful') or (string.find(damage_key, 'useful') and trait.key == 'useful') then | ||
forEachDamageType(function(damage_type) | |||
damage_value * trait.value | local new_key = damage_key .. | ||
((trait.key == 'useful' or trait.key == '') and "" or ('_' .. trait.key)); | |||
if not WITH_TRAITS[mode][new_key] then | |||
WITH_TRAITS[mode][new_key] = {} | |||
end | |||
WITH_TRAITS[mode][new_key][damage_type] = damage_value[damage_type] * trait.value | |||
end) | |||
end | end | ||
end | end | ||
Line 441: | Line 524: | ||
for mode, mode_content in pairs(WITH_TRAITS) do | for mode, mode_content in pairs(WITH_TRAITS) do | ||
for damage_key, damage_value in pairs(mode_content) do | for damage_key, damage_value in pairs(mode_content) do | ||
local combinations = { {} } | forEachDamageType(function(damage_type) | ||
local combinations = { {} } | |||
for passive_key, passive in pairs(PASSIVES) do | |||
local count = #combinations | |||
for i = 1, count do | |||
local new_combination = { unpack(combinations[i]) } | |||
table.insert(new_combination, passive_key) | |||
table.insert(combinations, new_combination) | |||
end | |||
end | end | ||
for _, combination in pairs(combinations) do | |||
local passive_multiplier = 1 | |||
local name_suffix = '' | |||
if #combination > 0 then | |||
table.sort(combination) | |||
for _, passive_key in pairs(combination) do | |||
passive_multiplier = passive_multiplier * | |||
tonumber(PASSIVES[passive_key][mode == 'PvE' and 'value' or 'value_pvp']) | |||
name_suffix = name_suffix .. '_passive' .. passive_key | |||
end | |||
end | |||
local new_damage_key = damage_key .. name_suffix; | |||
if not WITH_PASSIVES[mode][new_damage_key] then | |||
WITH_PASSIVES[mode][new_damage_key] = {} | |||
end | end | ||
WITH_PASSIVES[mode][new_damage_key][damage_type] = damage_value[damage_type] * passive_multiplier | |||
end | end | ||
end) | |||
end | |||
end | end | ||
end | end | ||
Line 470: | Line 559: | ||
local RANGE = { | local RANGE = { | ||
min_count = args.range_min_count and args.range_min_count[1] | min_count = args.range_min_count and args.range_min_count[1], | ||
max_count = args.range_max_count and args.range_max_count[1] | max_count = args.range_max_count and args.range_max_count[1], | ||
PvE = { | PvE = { | ||
min = args.range_min and args.range_min[1] | min = args.range_min and args.range_min[1], | ||
max = args.range_max and args.range_max[1] | max = args.range_max and args.range_max[1] | ||
}, | }, | ||
PvP = { | PvP = { | ||
min = args.range_min and (args.range_min[2] or args.range_min[1]) | min = args.range_min and (args.range_min[2] or args.range_min[1]), | ||
max = args.range_max and (args.range_max[2] or args.range_max[1]) | max = args.range_max and (args.range_max[2] or args.range_max[1]) | ||
} | } | ||
} | } | ||
Line 488: | Line 577: | ||
for damage_key, damage_value in pairs(mode_content) do | for damage_key, damage_value in pairs(mode_content) do | ||
WITH_RANGE[mode][damage_key] = { min = 0, max = 0 } | WITH_RANGE[mode][damage_key] = { min = 0, max = 0 } | ||
for | forEachDamageType(function(damage_type) | ||
local | local range_count = RANGE[damage_type .. '_count'] or 1; | ||
-- If min count preset, use range_max for the multiplier. | |||
WITH_RANGE[mode][damage_key][ | local range_multiplier = RANGE[mode][damage_type] or (damage_type == 'min' and RANGE.min_count and RANGE[mode].max) or 1; | ||
local final_range_multiplier = (1 + ((range_multiplier - 1) * range_count)); | |||
local perm_buff = OPTIONS.perm_buff[mode]; | |||
local final_damage_value = damage_value[damage_type] * final_range_multiplier * perm_buff; | |||
WITH_RANGE[mode][damage_key][damage_type] = not OPTIONS.format and final_damage_value or | |||
formatDamage(final_damage_value) | formatDamage(final_damage_value) | ||
end | end) | ||
end | end | ||
end | end | ||
Line 559: | Line 653: | ||
{ | { | ||
type = 'extra', | type = 'extra', | ||
text = { 'Average' }, | text = { translate('Average') }, | ||
is_visible = OPTIONS.no_max, | is_visible = OPTIONS.no_max, | ||
no_damage = true | no_damage = true | ||
Line 566: | Line 660: | ||
type = 'passives', | type = 'passives', | ||
text = checkPassives({ | text = checkPassives({ | ||
output = { 'Base' }, | output = { translate('Base') }, | ||
action = function(passive, output) | action = function(passive, output) | ||
if passive.is_combined then | if passive.is_combined then | ||
Line 598: | Line 692: | ||
type = 'passive_appended', | type = 'passive_appended', | ||
text = { | text = { | ||
'Normal', | translate('Normal'), | ||
OPTIONS.is_append and | OPTIONS.is_append and | ||
link(PASSIVES[OPTIONS.append_index].name, | link(PASSIVES[OPTIONS.append_index].name, | ||
Line 612: | Line 706: | ||
{ | { | ||
type = 'awakening', | type = 'awakening', | ||
text = { 'Regular', (function() | text = { translate('Regular'), (function() | ||
if OPTIONS.dmp then | if OPTIONS.dmp then | ||
return link('Dynamo Point System', 'Dynamo Configuration', | return link('Dynamo Point System' .. OPTIONS.lang_suffix, 'Dynamo Configuration', args.awk_prefix, | ||
OPTIONS.dmp ~= 'false' and (fillTemplate('({1} DMP)', { OPTIONS.dmp })) .. (args.awk_suffix and (' ' .. args.awk_suffix) or '')) | |||
elseif args.awk_alias then | elseif args.awk_alias then | ||
return link | return link(args.awk_alias[1], args.awk_alias[2], args.awk_prefix, args.awk_suffix) | ||
end | end | ||
return link('Awakening Mode') | return link('Awakening Mode' .. OPTIONS.lang_suffix, translate('Awakening Mode'), args.awk_prefix, args.awk_suffix) | ||
end)() | end)() | ||
}, | }, | ||
Line 629: | Line 723: | ||
type = 'traits', | type = 'traits', | ||
text = checkTraits({ | text = checkTraits({ | ||
output = { 'Normal' }, | output = { translate('Normal') }, | ||
action = function(trait, output) | action = function(trait, output) | ||
table.insert(output, trait.name) | table.insert(output, trait.name) | ||
Line 644: | Line 738: | ||
type = 'cancel', | type = 'cancel', | ||
text = { | text = { | ||
'Cancel', 'Full' | translate('Cancel'), | ||
translate('Full'), | |||
}, | }, | ||
keywords = { 'cancel' }, | keywords = { 'cancel' }, | ||
Line 654: | Line 749: | ||
text = { | text = { | ||
(inArgs('count') and not OPTIONS.use_avg) and | (inArgs('count') and not OPTIONS.use_avg) and | ||
( | (fillTemplate(translate('Per {1}'), { args.count_name or translate('Group') })) or | ||
'Max' | translate('Average'), | ||
translate('Max') | |||
}, | }, | ||
keywords = (function() | keywords = (function() | ||
Line 737: | Line 833: | ||
function doInitialCell(new_row) | function doInitialCell(new_row) | ||
return new_row:tag('th'):wikitext('Mode') | return new_row:tag('th'):wikitext(translate('Mode')) | ||
end | end | ||
Line 796: | Line 892: | ||
function doContentByMode(mode) | function doContentByMode(mode) | ||
local mode_row = TABLE:new() | local mode_row = TABLE:new() | ||
mode_row:tag('td'):wikitext(frame:expandTemplate { title = mode }) | mode_row:tag('td'):wikitext(frame:expandTemplate { title = translate(mode) }) | ||
local damage_entries = returnDamageInOrder() | local damage_entries = returnDamageInOrder() | ||
local last_number | local last_number | ||
Line 830: | Line 926: | ||
end | end | ||
doTable() | if OPTIONS.do_table then | ||
doTable() | |||
end | |||
-- Dump all values if wanted. | -- Dump all values if wanted. | ||
Line 844: | Line 942: | ||
if OPTIONS.bug then | if OPTIONS.bug then | ||
bug = frame:expandTemplate { | bug = frame:expandTemplate { | ||
title = 'SkillText', | title = translate('SkillText'), | ||
args = { 'FreeTraining' } | args = { 'FreeTraining' } | ||
} | } |