Module:Test

From Elwiki

More

  • More

Documentation for this module may be created atModule:Test/doc

-- pystartrequire('Module:CommonFunctions')locali18n=require('Module:I18n')localgetArgs=require('Module:Arguments').getArgslocalinspect=require('Module:Inspect').inspectlocalgetTranslations=i18n.getTranslationslocalp={}-- ========================================-- CONSTANTS-- ========================================localCONSTANTS={MODES={'PvE','PvP'},DAMAGE_TYPES={'min','max'},TO_SPLIT={'append','awk_alias'},DEFAULT_TRAITS={-- An empty trait so we keep the original values there.{key='',name='Normal',-- Will be translated latervalue=1},{key='enhanced',name='Enhanced (Trait)',-- Will be translated latervalue_func=function(args)returnargs.enhanced~=niland0.8end},{key='empowered',name='Empowered',-- Will be translated latervalue_func=function(args)returnargs.empowered=='true'and1.2ortonumber(args.empowered)orfalseend},{key='useful',name='Useful',-- Will be translated latervalue_func=function(args)return(args.hits_usefulorargs.avg_hits_useful)and(args.useful_penaltyorargs.usefulor0.8)orfalseend},{key='heavy',name='Heavy',-- Will be translated latervalue_func=function(args)returnargs.heavy~=niland1.44end}},BASE_DAMAGE_CONFIG={total_damage={damage_numbers={'dmg'},hit_counts={'hits'},provided={'dmg'}},total_damage_awk={damage_numbers={'awk_dmg','dmg'},hit_counts={'awk_hits','hits'},provided={'awk_dmg','awk_hits'}},avg_damage={damage_numbers={'dmg'},hit_counts={'avg_hits','hits'},provided={'avg_hits'}},avg_damage_awk={damage_numbers={'awk_dmg','dmg'},hit_counts={'avg_awk_hits','awk_hits','avg_hits','hits'},provided={'avg_awk_hits','awk_dmg_and_avg_hits'}-- Special marker},-- Store the logic for Useful traitstotal_damage_useful={damage_numbers={'dmg'},hit_counts={'hits_useful','hits'},provided={'hits_useful'}},total_damage_awk_useful={damage_numbers={'awk_dmg','dmg'},hit_counts={'awk_hits_useful','awk_hits','hits_useful','hits'},provided={'awk_hits_useful'}},avg_damage_useful={damage_numbers={'dmg'},hit_counts={'avg_hits_useful','hits_useful','avg_hits','hits'},provided={'avg_hits_useful'}},avg_damage_awk_useful={damage_numbers={'awk_dmg','dmg'},hit_counts={'avg_awk_hits_useful','avg_awk_hits','hits_useful','hits'},provided={'avg_awk_hits_useful'}},}}-- ========================================-- UTILITY FUNCTIONS-- ========================================localUtils={}functionUtils.forEach(func)for_,modeinipairs(CONSTANTS.MODES)dofunc(mode)endendfunctionUtils.forEachDamageType(func)for_,damage_typeinipairs(CONSTANTS.DAMAGE_TYPES)dofunc(damage_type)endendfunctionUtils.createDamageDataTable()localnewTable={}for_,modeinipairs(CONSTANTS.MODES)donewTable[mode]={}endreturnnewTableendfunctionUtils.isTableNotEmpty(tbl)returnnext(tbl)~=nilend-- ========================================-- INPUT PROCESSING-- ========================================localInputProcessor={}functionInputProcessor.parseOptions(args)return{do_table=args[1]=='true',character=args[2]orargs.charor'Elsword',lang_suffix=args.langand('/'..args.lang)or'',lang_append=args.lang~=nilandargs.lang~='',format=args.format~='false',no_max=args.no_max=='true',is_append=args.append~=nil,append_index=args.appendandtonumber(split(args.append)[1]),append_name=args.appendandsplit(args.append)[2],combine_suffix=args.combine_suffixand(' '..args.combine_suffix)or'',combine=(function()localoutput={}ifnotargs.combinethenreturnnilendfor_,passive_keyinipairs(split(args.combine))dotable.insert(output,tonumber(passive_key))endif#output==0thenreturnnilendreturnoutputend)(),perm_buff={PvE=args.perm_buffor1,PvP=args.pvp_perm_bufforargs.perm_buffor1},bug=args.bug=='true',dump=args.dump=='true',dump_table_data=args.dump_table_data=='true',dump_parsed=args.dump_parsed=='true',prefix=args.prefix,use_avg=args.use_avg=='true',dmp=args.dmp=='true'and3orargs.dmp}endfunctionInputProcessor.parsePassives(args,frame,options)localpassives={}fork,vinpairs(args)doifstring.find(k,'passive')thenlocalpassive_name=vlocalpassive_title=v..options.lang_suffixlocalis_custom=string.find(k,'_define')~=nillocalpassive_index=string.match(k,"%d")localpassive_values=split(is_customandvorframe:preprocess('{{:'..passive_name..'}}{{#arrayprint:'..passive_name..'}}'))localdisplay_titleifis_customthenpassive_name=passive_values[#passive_values]passive_values[#passive_values]=nilelseifoptions.lang_appendthendisplay_title=i18n.getTranslatedTitle(passive_title)endpassives[tonumber(passive_index)]={name=passive_name,value=passive_values[1],value_pvp=passive_values[2],alias=args['alias'..passive_index]or(passive_index==options.append_indexandoptions.append_name)ordisplay_title,suffix=args['suffix'..passive_index]and(' '..args['suffix'..passive_index])or'',prefix=args['prefix'..passive_index]and(args['prefix'..passive_index]..' ')or'',exist=frame:preprocess('{{#ifexist:'..passive_name..'|true|false}}')=='true'}endendreturnpassivesendfunctionInputProcessor.processNumericArgs(args,frame)localprocessed_args=table.deep_copy(args)fork,vinpairs(args)doifstring.match(v,'^[()+%-*/%d%s,.i]+$')thenlocalsplit_values=split(v)fork2,v2inpairs(split_values)doifnotstring.find(v,'[a-zA-Z]+')thensplit_values[k2]=frame:preprocess('{{#expr:'..v2..'}}')endendprocessed_args[k]=split_valueselseifinArrayHasValue(k,CONSTANTS.TO_SPLIT)thenprocessed_args[k]=split(v)endendreturnprocessed_argsendfunctionInputProcessor.setDefaultHitCounts(args)-- Set basic hit count to 1 for all damage.fork,vinipairs(args.dmg)doifnotargs.hitsthenargs.hits={}endifnotargs.hits[k]thenargs.hits[k]=1endend-- Set basic hit count to 1 for all cancel damage.ifargs.cancel_dmgthenfork,vinipairs(args.cancel_dmg)doifnotargs.cancel_hitsthenargs.cancel_hits={}endifnotargs.cancel_hits[k]thenargs.cancel_hits[k]=1endendendend-- ========================================-- CONFIGURATION BUILDING-- ========================================localConfigBuilder={}functionConfigBuilder.buildDamageConfig(args)localfunctionhandleCancel()localprocessed_keys={}forconfig_key,config_valueinpairs(CONSTANTS.BASE_DAMAGE_CONFIG)doifnotconfig_key:match('cancel_')thenlocalnew_config_value={}forarg_table_key,arg_tableinpairs(config_value)dolocalnew_arg_table={}for_,arginipairs(arg_table)dotable.insert(new_arg_table,'cancel_'..arg)endnew_config_value[arg_table_key]=new_arg_tableendlocalnew_key='cancel_'..config_keyprocessed_keys[new_key]=new_config_valueendendreturnprocessed_keysendifargs.cancel_dmgthenlocalcancel_configs=handleCancel()returntable.fuse(CONSTANTS.BASE_DAMAGE_CONFIG,cancel_configs)elsereturnCONSTANTS.BASE_DAMAGE_CONFIGendendfunctionConfigBuilder.buildTraits(args,translate)localtraits={}for_,trait_templateinipairs(CONSTANTS.DEFAULT_TRAITS)dolocaltrait={key=trait_template.key,name=translate(trait_template.name),value=trait_template.valueor(trait_template.value_funcandtrait_template.value_func(args))}table.insert(traits,trait)endreturntraitsend-- ========================================-- INHERITANCE SYSTEM-- ========================================localInheritanceProcessor={}functionInheritanceProcessor.applyInheritance(mainArgValues,inheritArg,mainArgValue,inheritValue)ifmainArgValue==''thenreturninheritValueelseifmainArgValueandstring.find(mainArgValue,'i')andinheritValuethenreturnmainArgValue:gsub('i',inheritValue)endreturnmainArgValueendfunctionInheritanceProcessor.applyInheritanceForKey(args,prefix,argTable,damageTypeIndex,damageType,eval)localmainKey=argTable[1]..damageTypelocalmainKeyPrefixed=prefix..mainKeylocalmainArgValues=args[mainKeyPrefixed]ifmainArgValuesthenlocali=1localcancelDmgLen=args.cancel_dmgand#args.cancel_dmgor0whilei<=(#args.dmg+cancelDmgLen)dolocalmainArgValue=mainArgValues[i]forix,inheritKeyinipairs(argTable)dolocalinheritArg=args[prefix..inheritKey..damageType]orargs[inheritKey..damageType]-- Basic damage/hits inheritance request detected. Ignore min/max.ifdamageTypeandmainKey:gsub(damageType,"")==argTable[#argTable]theninheritArg=args[prefix..inheritKey]orargs[inheritKey]endifinheritArgandinheritArg[i]and(damageTypeIndex==1andix~=1ordamageTypeIndex~=1)andtonumber(inheritArg[i])thenmainArgValues[i]=InheritanceProcessor.applyInheritance(mainArgValues,inheritArg,mainArgValue,inheritArg[i])breakendendi=i+1endendendfunctionInheritanceProcessor.inherit(args,damageConfig,eval)localfunctioninheritForMode(mode)localprefix=mode=='PvE'and''orstring.lower(mode..'_')forconfigKey,configValueinpairs(damageConfig)doforargTableKey,argTableinpairs(configValue)doifargTableKey~='provided'andUtils.isTableNotEmpty(argTable)thenfordamageTypeIndex,damageTypeinipairs({'','_min','_max'})doInheritanceProcessor.applyInheritanceForKey(args,prefix,argTable,damageTypeIndex,damageType,eval)endendendendendUtils.forEach(inheritForMode)end-- ========================================-- DAMAGE CALCULATION ENGINE-- ========================================localDamageEngine={}functionDamageEngine.parseConfig(args,damageConfig,options)localdamageParsed=Utils.createDamageDataTable()localfunctionparseConfigForMode(mode)localprefix=mode=='PvE'and''orstring.lower(mode..'_')forconfig_key,config_valueinpairs(damageConfig)dofork,vinpairs(config_value)dolocaloutput_value={}-- When both min and max are found, we need to break from the loop.localisValueFound={min=false,max=false}for_,v2inipairs(v)do-- This array holds the argument names with fallbacksUtils.forEachDamageType(function(damage_type)-- If there already is a value for this damage type (min or max), do not continue.ifisValueFound[damage_type]==truethenreturnendlocalarg_from_template=args[prefix..v2..'_'..damage_type]orargs[v2..'_'..damage_type]orargs[prefix..v2]orargs[v2]ifarg_from_template~=nilthenifk=='provided'thenoutput_value=true-- Special handling for awk_dmg_and_avg_hits markerifv2=='awk_dmg_and_avg_hits'thenoutput_value=args.awk_dmgandargs.avg_hitsend-- Do not generate total_damage values at all if the skill can't reach them.ifstring.find(config_key,'total_')andoptions.no_maxthenoutput_value=falseendelseiftype(output_value)~="table"thenoutput_value={}endoutput_value[damage_type]=arg_from_templateend-- Mark the value as found.isValueFound[damage_type]=trueelseifk=='provided'thenoutput_value=falseelseoutput_value[damage_type]={}endendend)-- Both values found, we can now break the loop.ifisValueFound.minandisValueFound.maxthenbreakendendifdamageParsed[mode][config_key]==nilthendamageParsed[mode][config_key]={}enddamageParsed[mode][config_key][k]=output_valueendendendUtils.forEach(parseConfigForMode)returndamageParsedendfunctionDamageEngine.handleEachDamage(damageParsed,args)localwithEach=table.deep_copy(damageParsed)formode,mode_contentinpairs(damageParsed)dofordamage_key,damage_valueinpairs(mode_content)doifstring.find(damage_key,'total_')thenlocalnew_value=table.deep_copy(damage_value)Utils.forEachDamageType(function(damage_type)fork,hit_countinipairs(new_value.hit_counts[damage_type])dohit_count=hit_count==''and1orhit_countnew_value.hit_counts[damage_type][k]=hit_count*((string.find(damage_key,'awk')andargs.awk_count)andargs.awk_count[1]orargs.count[1])endend)withEach[mode][damage_key:gsub("total_","each_")]=damage_valuewithEach[mode][damage_key]=new_valueendendendreturnwithEachendfunctionDamageEngine.calculateBasicDamage(damageParsed)localbasicDamage=Utils.createDamageDataTable()formode,mode_contentinpairs(damageParsed)dofordamage_key,damage_valueinpairs(mode_content)doUtils.forEachDamageType(function(damage_type)locali=1localoutput=0-- Check if to even generate the damage.ifdamage_value.providedthen-- Loop through damage numbers and multiply them with hits.fork,damage_numberinipairs(damage_value.damage_numbers[damage_type])dolocalhit_count=damage_value.hit_counts[damage_type][i]hit_count=hit_count==''and1orhit_countoutput=output+(damage_number*hit_count)i=i+1end-- Write the result to a separate object.ifnotbasicDamage[mode][damage_key]thenbasicDamage[mode][damage_key]={}endbasicDamage[mode][damage_key][damage_type]=outputendend)endendreturnbasicDamageendfunctionDamageEngine.addCancelDamage(basicDamage,args)ifnotargs.cancel_dmgthenreturnbasicDamageendformode,mode_contentinpairs(basicDamage)dofordamage_key,damage_valueinpairs(mode_content)dolocalcancel_candidate=basicDamage[mode]['cancel_'..damage_key]Utils.forEachDamageType(function(damage_type)ifnotstring.find(damage_key,'cancel_')andcancel_candidatethenbasicDamage[mode][damage_key][damage_type]=damage_value[damage_type]+cancel_candidate[damage_type]endend)endendreturnbasicDamageend-- ========================================-- TRAIT PROCESSING-- ========================================localTraitProcessor={}functionTraitProcessor.applyTraits(basicDamage,traits)localwithTraits=Utils.createDamageDataTable()formode,mode_contentinpairs(basicDamage)dofordamage_key,damage_valueinpairs(mode_content)dofor_,traitinpairs(traits)doif(trait.valueandtrait.key~='useful')or(string.find(damage_key,'useful')andtrait.key=='useful')thenUtils.forEachDamageType(function(damage_type)localnew_key=damage_key..((trait.key=='useful'ortrait.key=='')and""or('_'..trait.key))ifnotwithTraits[mode][new_key]thenwithTraits[mode][new_key]={}endwithTraits[mode][new_key][damage_type]=damage_value[damage_type]*trait.valueend)endendendendreturnwithTraitsend-- ========================================-- PASSIVE PROCESSING-- ========================================localPassiveProcessor={}functionPassiveProcessor.generatePassiveCombinations(passives)localcombinations={{}}forpassive_key,passiveinpairs(passives)dolocalcount=#combinationsfori=1,countdolocalnew_combination={unpack(combinations[i])}table.insert(new_combination,passive_key)table.insert(combinations,new_combination)endendreturncombinationsendfunctionPassiveProcessor.applyPassives(withTraits,passives)localwithPassives=Utils.createDamageDataTable()formode,mode_contentinpairs(withTraits)dofordamage_key,damage_valueinpairs(mode_content)doUtils.forEachDamageType(function(damage_type)localcombinations=PassiveProcessor.generatePassiveCombinations(passives)for_,combinationinpairs(combinations)dolocalpassive_multiplier=1localname_suffix=''if#combination>0thentable.sort(combination)for_,passive_keyinpairs(combination)dopassive_multiplier=passive_multiplier*tonumber(passives[passive_key][mode=='PvE'and'value'or'value_pvp'])name_suffix=name_suffix..'_passive'..passive_keyendendlocalnew_damage_key=damage_key..name_suffixifnotwithPassives[mode][new_damage_key]thenwithPassives[mode][new_damage_key]={}endwithPassives[mode][new_damage_key][damage_type]=damage_value[damage_type]*passive_multiplierendend)endendreturnwithPassivesend-- ========================================-- RANGE PROCESSING-- ========================================localRangeProcessor={}functionRangeProcessor.buildRangeConfig(args)return{min_count=args.range_min_countandargs.range_min_count[1],max_count=args.range_max_countandargs.range_max_count[1],PvE={min=args.range_minandargs.range_min[1],max=args.range_maxandargs.range_max[1]},PvP={min=args.range_minand(args.range_min[2]orargs.range_min[1]),max=args.range_maxand(args.range_max[2]orargs.range_max[1])}}endfunctionRangeProcessor.applyRanges(withPassives,range,options,formatDamage)localwithRange=Utils.createDamageDataTable()formode,mode_contentinpairs(withPassives)dofordamage_key,damage_valueinpairs(mode_content)dowithRange[mode][damage_key]={min=0,max=0}Utils.forEachDamageType(function(damage_type)localrange_count=range[damage_type..'_count']or1-- If min count preset, use range_max for the multiplier.localrange_multiplier=range[mode][damage_type]or(damage_type=='min'andrange.min_countandrange[mode].max)or1localfinal_range_multiplier=(1+((range_multiplier-1)*range_count))localperm_buff=options.perm_buff[mode]localfinal_damage_value=damage_value[damage_type]*final_range_multiplier*perm_buffwithRange[mode][damage_key][damage_type]=notoptions.formatandfinal_damage_valueorformatDamage(final_damage_value)end)endendreturnwithRangeend-- ========================================-- TABLE GENERATION-- ========================================localTableGenerator={}functionTableGenerator.checkTraits(traits,settings)localoutputifnotsettingsthenoutput=falseelseoutput=settings.outputor{}endfortrait_index,traitinipairs(traits)doiftrait.value~=falseandtrait_index~=1thenifsettingsandtype(settings.action)=='function'thensettings.action(trait,output,settings)elsereturntrueendendendreturnoutputendfunctionTableGenerator.checkPassives(passives,options,args,link,sortPassives,settings)localoutput=settings.outputor{}localPASSIVES_WITH_COMBINED=table.deep_copy(passives)-- Handle combined passives properly.ifoptions.combinethentable.insert(PASSIVES_WITH_COMBINED,{is_combined=true})endforpassive_index,passiveinipairs(PASSIVES_WITH_COMBINED)do-- Determine if passive should be skippedlocalskip_passive=falseifpassive.is_combinedthen-- Skip combined passive if any of its constituent passives are being appendedifoptions.is_appendandoptions.combineandinArrayHasValue(options.append_index,options.combine)thenskip_passive=trueelseskip_passive=falseendelse-- Regular passive logic: skip if it's appended or part of combine (unless display_separated)skip_passive=(options.is_appendandoptions.append_index==passive_index)or(inArrayHasValue(passive_index,options.combineor{})andnotinArrayHasValue(passive_index,args.display_separatedor{}))endifnotskip_passivetheniftype(settings.action)=='function'thensettings.action(passive,output,passive_index)elsereturntrueendendendreturnoutputendfunctionTableGenerator.buildTableContent(options,passives,traits,args,translate,inArgs,link,sortPassives,fillTemplate)return{{type='extra',text={translate('Average')},is_visible=options.no_max,no_damage=true},{type='passives',text=TableGenerator.checkPassives(passives,options,args,link,sortPassives,{output={translate('Base')},action=function(passive,output)ifpassive.is_combinedthen-- Handling combined passive header name.localcombo={}for_,passive_keyinipairs(options.combine)dopassive=passives[passive_key]table.insert(combo,link(passive.name,passive.alias,passive.prefix,passive.suffix,passive.exist))endtable.insert(output,table.concat(combo,'/')..options.combine_suffix)elsetable.insert(output,link(passive.name,passive.alias,passive.prefix,passive.suffix,passive.exist))endend}),keywords=TableGenerator.checkPassives(passives,options,args,link,sortPassives,{action=function(passive,output,passive_index)ifpassive.is_combinedthen-- Handling combined passive damage cells.table.insert(output,sortPassives('passive'..table.concat(options.combine,'_passive')))elsetable.insert(output,'passive'..passive_index)endend}),is_visible=notoptions.no_maxor#passives>0},{type='passive_appended',text={translate('Normal'),options.is_appendand(function()-- Check if the appended passive is part of a combined passiveifoptions.combineandinArrayHasValue(options.append_index,options.combine)then-- Handle combined passive in appendlocalcombo={}for_,passive_keyinipairs(options.combine)dolocalpassive=passives[passive_key]table.insert(combo,link(passive.name,passive.alias,passive.prefix,passive.suffix,passive.exist))endreturntable.concat(combo,'/')..options.combine_suffixelse-- Handle single passive in appendreturnlink(passives[options.append_index].name,passives[options.append_index].aliasoroptions.append_nameornil,passives[options.append_index].prefix,passives[options.append_index].suffix,passives[options.append_index].exist)endend)()},keywords={options.is_appendand(function()-- Generate appropriate keyword for appended passiveifoptions.combineandinArrayHasValue(options.append_index,options.combine)thenreturnsortPassives('passive'..table.concat(options.combine,'_passive'))elsereturn'passive'..options.append_indexendend)()ornil},is_visible=options.is_appendorfalse},{type='awakening',text={translate('Regular'),(function()ifoptions.dmpthenreturnlink('Dynamo Point System'..options.lang_suffix,'Dynamo Configuration',args.awk_prefix,options.dmp~='false'and(fillTemplate('({1} DMP)',{options.dmp}))..(args.awk_suffixand(' '..args.awk_suffix)or''))elseifargs.awk_aliasthenreturnlink(args.awk_alias[1],args.awk_alias[2],args.awk_prefix,args.awk_suffix)endreturnlink('Awakening Mode'..options.lang_suffix,translate('Awakening Mode'),args.awk_prefix,args.awk_suffix)end)()},keywords={'awk'},keyword_next_to_main_key=true,is_visible=inArgs('awk_dmg')orinArgs('awk_hits')orinArgs('avg_awk_hits')orfalse},{type='traits',text=TableGenerator.checkTraits(traits,{output={translate('Normal')},action=function(trait,output)table.insert(output,trait.name)end}),keywords=TableGenerator.checkTraits(traits,{action=function(trait,output)table.insert(output,trait.key)end}),is_visible=TableGenerator.checkTraits(traits)},{type='cancel',text={translate('Cancel'),translate('Full'),},keywords={'cancel'},keyword_first=true,is_visible=inArgs('cancel_dmg')},{type='hit_count',text={(inArgs('count')andnotoptions.use_avg)and(fillTemplate(translate('Per {1}'),{args.count_nameortranslate('Group')}))ortranslate('Average'),translate('Max')},keywords=(function()ifinArgs('avg_hits')orinArgs('count')thenreturn{(inArgs('count')andnotoptions.use_avg)and'each'or'avg','total'}endreturn{'total'}end)(),is_visible=((inArgs('avg_hits')orinArgs('count'))andnotoptions.no_max)orfalse}}endfunctionTableGenerator.returnDamageInOrder(tableContent,options,inArgs,sortPassives)localmain_key='damage'localall_list={}fori=#tableContent,1,-1dolocalcurrent_row=tableContent[i]localnew_list={}ifnotcurrent_row.no_damagethenifi==#tableContentthenfor_,keywordinipairs(current_row.keywords)doifnotoptions.no_maxor(options.no_maxandkeyword~='total')thenlocalnew_key=keyword..'_'..main_keytable.insert(new_list,new_key)endendelseifcurrent_row.is_visiblethenfor_,keywordinipairs(current_row.keywords)dofor_,prev_keyinipairs(all_list)dolocalnew_key=prev_key..'_'..keywordifcurrent_row.keyword_next_to_main_keythennew_key=prev_key:gsub(main_key,main_key..'_'..keyword)elseifcurrent_row.keyword_firstthennew_key=keyword..'_'..prev_keyendtable.insert(new_list,new_key)endendendfor_,new_keyinipairs(new_list)dotable.insert(all_list,sortPassives(new_key))endendendifinArgs('cancel_dmg')thenlocalnew_list={}fori,damage_keyinipairs(all_list)dolocalregex="^(%w+_)"localprefix='cancel_'localmatch=string.match(damage_key,regex)if(match==prefix)thennew_list[i]=damage_key:gsub(prefix,"")elsenew_list[i]=prefix..damage_keyendendall_list=new_listendreturnall_listendfunctionTableGenerator.generateTable(finalDamage,tableContent,options,frame,translate,args,inArgs,sortPassives)localTABLE=mw.html.create('table'):attr({cellpadding=5,border=1,style='border-collapse: collapse; text-align: center',class='colortable-'..options.character})functionTABLE:new()returnself:tag('tr')endlocalfunctiondoInitialCell(new_row)returnnew_row:tag('th'):wikitext(translate('Mode'))endlocalfunctiondoHeaders()localcurrent_multiplier=0localinitial_header_celllocaliterations=0forrow_index,rowinipairs(tableContent)doifrow.is_visiblethenlocalnew_row=TABLE:new()localnext_multiplier=0ifiterations==0andnotinitial_header_celltheninitial_header_cell=doInitialCell(new_row)endlocalcolspan_value=1fork,vinipairs(tableContent)doifk>row_indexandv.is_visiblethencolspan_value=colspan_value*#v.textendendfori=1,(current_multiplier==0and1orcurrent_multiplier),1dofor_,textinipairs(row.text)dolocalnew_cell=new_row:tag('th')new_cell:attr('colspan',colspan_value):wikitext(text)next_multiplier=next_multiplier+1endendcurrent_multiplier=next_multiplieriterations=iterations+1endendinitial_header_cell:attr('rowspan',iterations)endlocalfunctiondoRangeText(damage_number)ifdamage_numberanddamage_number.min==damage_number.maxthendamage_number=damage_number.minelseifdamage_numberthendamage_number=damage_number.min..'<span style="white-space: nowrap;"> ~</span> '..damage_number.maxendreturndamage_numberendlocalfunctiondoContentByMode(mode)localmode_row=TABLE:new()mode_row:tag('td'):wikitext(frame:expandTemplate{title=translate(mode)})localdamage_entries=TableGenerator.returnDamageInOrder(tableContent,options,inArgs,sortPassives)locallast_numberlocallast_unique_cellfor_,damage_keyinipairs(damage_entries)doifargs.dump_names~='true'thenlocaldamage_number=finalDamage[mode][damage_key]damage_number=doRangeText(damage_number)iflast_number~=damage_numberthenlocalnew_cell=mode_row:tag('td'):wikitext(damage_numberorframe:expandTemplate{title='color',args={'red','&#35;ERROR'}})last_unique_cell=new_cellelselast_unique_cell:attr('colspan',(last_unique_cell:getAttr('colspan')or1)+1)endlast_number=damage_numberelsemode_row:tag('td'):wikitext(damage_key)endendenddoHeaders()Utils.forEach(doContentByMode)returnTABLEend-- ========================================-- OUTPUT PROCESSING-- ========================================localOutputProcessor={}functionOutputProcessor.generateOutput(frame,args,finalDamage,damageParsed,tableContent,options,translate,doVariables,inspect_dump)localout=nil-- Dump all values if wanted.ifoptions.dump_table_datathenreturninspect_dump(frame,tableContent)elseifoptions.dumpthenreturninspect_dump(frame,finalDamage)elseifoptions.dump_parsedthenreturninspect_dump(frame,damageParsed)endlocalbug=''ifoptions.bugthenbug=frame:expandTemplate{title=translate('SkillText'),args={'FreeTraining'}}end-- Transform into variableslocalvariables=doVariables(frame,finalDamage,options.prefix)localtable_output=''ifoptions.do_tablethenlocalTABLE=TableGenerator.generateTable(finalDamage,tableContent,options,frame,translate,args,function(key)returnargs[key]~=nilend,sortPassives)table_output=tostring(TABLE)endifout~=nilthenreturninspect_dump(frame,out)endreturnvariables..bug..table_outputend-- ========================================-- MAIN PROCESS-- ========================================functionp.main(frame)localargs=getArgs(frame)localtr=getTranslations(frame,'Template:Damage',args.lang,true)localoutfunctiontranslate(key)returni18n.translate(tr,key)endfunctioninArgs(key)ifargs[key]~=nilthenreturntrueendend-- User requested optionslocalOPTIONS=InputProcessor.parseOptions(args)-- Define a table with parsed damage information of all kind.localBASIC_DAMAGE=Utils.createDamageDataTable()-- Define a table with trait names and their values to apply.localTRAITS=ConfigBuilder.buildTraits(args,translate)functioneval(s)returnframe:preprocess('{{#expr:'..s..'}}')end-- A table with user-requested passive skills (empty by default).localPASSIVES=InputProcessor.parsePassives(args,frame,OPTIONS)InputProcessor.setDefaultHitCounts(args)-- Store a configuration that will tell the main function how to behave given different inputs.-- It will always take the first value if available. If not, fall back to the other (recursively).localDAMAGE_CONFIG=ConfigBuilder.buildDamageConfig(args)InheritanceProcessor.inherit(args,DAMAGE_CONFIG,eval)localDAMAGE_PARSED=DamageEngine.parseConfig(args,DAMAGE_CONFIG,OPTIONS)ifargs.countthenDAMAGE_PARSED=DamageEngine.handleEachDamage(DAMAGE_PARSED,args)endlocalbasicDamage=DamageEngine.calculateBasicDamage(DAMAGE_PARSED)-- Adding missing cancel part damage to full, so that repetition wouldn't be a problem.basicDamage=DamageEngine.addCancelDamage(basicDamage,args)localWITH_TRAITS=TraitProcessor.applyTraits(basicDamage,TRAITS)localWITH_PASSIVES=PassiveProcessor.applyPassives(WITH_TRAITS,PASSIVES)localRANGE=RangeProcessor.buildRangeConfig(args)localWITH_RANGE=RangeProcessor.applyRanges(WITH_PASSIVES,RANGE,OPTIONS,formatDamage)localFINAL_DAMAGE=WITH_RANGE-- Build table structurelocalTABLE_CONTENT=TableGenerator.buildTableContent(OPTIONS,PASSIVES,TRAITS,args,translate,inArgs,link,sortPassives,fillTemplate)-- Generate and return outputreturnOutputProcessor.generateOutput(frame,args,FINAL_DAMAGE,DAMAGE_PARSED,TABLE_CONTENT,OPTIONS,translate,doVariables,inspect_dump)endreturnp-- pyend