🌟現在、鉄壁 鉄壁ヘッドショットには対応済みです。
鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。

「モジュール:DamageTable」の版間の差分

提供:Apex Data
ナビゲーションに移動 検索に移動
(レッグショットのデフォルト値の変更)
(2次的なダメージ使用時にスカルピアサーライフリングを含めた表が生成できない不具合の修正)
 
(同じ利用者による、間の148版が非表示)
1行目: 1行目:
require('Module:Utility/mw.html Extensions')
local p = {}
local p = {}


local table = require('Module:TableExtensions')
local aw    = require('Module:Utility/Library')
local apex  = require('Module:Utility/ApexLibrary')
local Color = require('Module:Color')
local STKCalculator = require('Module:Apex/STKCalculator')
local getArgs -- lazily initialized
local getArgs -- lazily initialized


local function round(num)
local function getShotCount(part, health, shield, info, gunshield)
return math.floor(num + 0.5)
local damages = apex.calcShotCount(info.damage, part, info.mul, health, shield, gunshield or 0, info.opts)
return #damages, damages
end
 
local function renderHeader(tbl, mul, colspan, shields, res)
tbl:tag('tr')
:tag('th')
:attr('colspan', colspan)
:attr('rowspan', 2)
:wikitextIf(
mul == 1,
res.targets.caption,
function()
return string.format(res.targets.caption_format, mul)
end)
:done()
:tag('th')
:attr('rowspan', 2)
:wikitext(res.damages.caption)
:done()
:tag('th')
:attr('colspan', #shields)
:wikitext(res.shotstokill)
local row = tbl:tag('tr')
for _, shield in ipairs(shields) do
row:tag('th')
:attrIf(
shield.tooltip ~= nil,
{ ['data-tooltip'] = shield.tooltip })
:wikitext(shield.label)
end
end
 
local function renderCell(row, part, health, shield, info, gunshield, rowspan)
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
local ratio = (shotCount - info.minCount) / info.diffCount
local tooltip = { aw.stringifyRepeatingArray(damages, '→') }
if info.ttkCalculator then
local template = '<tr><th>%s&nbsp;</th><td class="cell-type-number" style="font-weight:bold">%d</td><td>&thinsp;ミリ秒</td></tr>'
local ttkc = info.ttkCalculator
local ttk0 = aw.round(1000 * ttkc:getAsLevel(shotCount, 0))
local ttk1 = aw.round(1000 * ttkc:getAsLevel(shotCount, 1))
local ttk2 = aw.round(1000 * ttkc:getAsLevel(shotCount, 2))
local ttk3 = aw.round(1000 * ttkc:getAsLevel(shotCount, 3))
if ttk2 ~= ttk3 then
table.insert(tooltip, '<br><br>キルタイム:<table class="condensedtable listtable">')
if ttk0 == ttk1 then
table.insert(tooltip, string.format(template, 'なし・Lv.1', ttk0))
table.insert(tooltip, string.format(template, 'Lv.2', ttk2))
else
table.insert(tooltip, string.format(template, 'なし', ttk0))
table.insert(tooltip, string.format(template, 'Lv.1', ttk1))
table.insert(tooltip, string.format(template, 'Lv.2', ttk2))
end
table.insert(tooltip, string.format(template, 'Lv.3', ttk3))
table.insert(tooltip, '</table>')
elseif ttk1 ~= ttk2 then
table.insert(tooltip, '<br><br>キルタイム:<table class="condensedtable listtable">')
if ttk0 == ttk1 then
table.insert(tooltip, string.format(template, 'なし・Lv.1', ttk0))
else
table.insert(tooltip, string.format(template, 'なし', ttk0))
table.insert(tooltip, string.format(template, 'Lv.1', ttk1))
end
table.insert(tooltip, string.format(template, 'Lv.2, 3', ttk2))
table.insert(tooltip, '</table>')
elseif ttk0 ~= ttk1 then
table.insert(tooltip, '<br><br>キルタイム:<table class="condensedtable listtable">')
table.insert(tooltip, string.format(template, 'なし', ttk0))
table.insert(tooltip, string.format(template, 'Lv.1, 2, 3', ttk1))
table.insert(tooltip, '</table>')
else
table.insert(tooltip, string.format('<br>キルタイム: <span class="text-style-number" style="font-weight:bold">%d</span>&thinsp;ミリ秒', ttk0))
end
end
local hue  = (info.diffCount < 5 and 0.65 or 0.85) * ratio
local color = Color.HSL(hue, 0.91, 0.89):toRGB()
row:tag('td')
:attrIf(rowspan > 1, { rowspan = rowspan })
:attr('data-tooltip', table.concat(tooltip))
:css('background-color', tostring(color))
:wikitext(shotCount)
end
 
local function renderHeaderCell(row, name, tooltip, rowinfo)
for i = 1, #rowinfo do
local cellinfo = rowinfo[i]
local colspan = cellinfo.colspan or 1
local rowspan = cellinfo.rowspan or 1
local cell = row:tag('th')
if colspan > 1 then
cell:attr('colspan', colspan)
end
if rowspan > 1 then
cell:attr('rowspan', rowspan)
end
if cellinfo.breaktop then
cell:css('border-top', '0 none transparent')
end
if cellinfo.breakbottom then
cell:css('border-bottom', '0 none transparent')
end
--if cellinfo.showbottom then
-- cell:css({
-- ['border-bottom-style'] = 'solid',
-- ['border-bottom-width'] = '1px',
-- })
--end
if cellinfo.verticalAlign then
cell:css('vertical-align', cellinfo.verticalAlign)
end
if i == #rowinfo then
if tooltip ~= nil then
cell:attr('data-tooltip', tooltip)
end
cell:wikitext(name)
elseif cellinfo.content then
if cellinfo.vertical then
cell:addClass('vertical-cell')
:tag('span')
:wikitext(cellinfo.content)
else
cell:wikitext(cellinfo.content)
end
else
cell:wikitext('&nbsp;&nbsp;')
end
end
end
 
local function renderRow(tbl, name, tooltip, part, weaponinfo, rowinfo, res, gunshield)
local row = tbl:tag('tr')
renderHeaderCell(row, name, tooltip, rowinfo)
local damage
local opts2 = aw.shallowCopy(weaponinfo.opts)
table.removeKey(opts2, 'beamdamage')
table.removeKey(opts2, 'beamticks')
if gunshield then
table.removeKey(opts2, 'disruptorRounds')
table.removeKey(opts2, 'hammerpointRounds')
damage = apex.calcDamage(weaponinfo.damage, 1, 1, opts2)
else
damage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, opts2)
end
local text
if not gunshield and weaponinfo.opts.disruptorRounds > 1 then
local optsD = aw.shallowCopy(weaponinfo.opts)
table.removeKey(optsD, 'disruptorRounds')
local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, optsD)
if weaponinfo.opts.pellets > 1 then
text = string.format(
res.damages.pelletsWithDisruptor,
damage * weaponinfo.opts.pellets,
damage,
weaponinfo.opts.pellets,
defaultDamage * weaponinfo.opts.pellets)
else
text = string.format(res.damages.disruptor, damage, defaultDamage)
end
elseif not gunshield and weaponinfo.opts.hammerpointRounds > 1 then
local optsH = aw.shallowCopy(weaponinfo.opts)
table.removeKey(optsH, 'hammerpointRounds')
local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, optsH)
if weaponinfo.opts.pellets > 1 then
text = string.format(
res.damages.pelletsWithHammerpoint,
defaultDamage * weaponinfo.opts.pellets,
damage * weaponinfo.opts.pellets,
damage,
weaponinfo.opts.pellets)
else
text = string.format(res.damages.hammerpoint, defaultDamage, damage)
end
elseif weaponinfo.opts.pellets > 1 then
text = string.format(
res.damages.pellets,
damage * weaponinfo.opts.pellets,
damage,
weaponinfo.opts.pellets)
elseif weaponinfo.opts.beamticks > 1 then
local beamdamage = apex.calcDamageFromPartAndPassive(weaponinfo.opts.beamdamage, part, weaponinfo.mul, opts2)
local alldamage = beamdamage * weaponinfo.opts.beamticks + damage
text = string.format(
res.damages.ticks,
alldamage,
beamdamage,
weaponinfo.opts.beamticks,
damage)
else
text = tostring(damage)
end
row:tag('td'):wikitext(text)
for _, shield in ipairs(weaponinfo.shields) do
renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield, 1)
end
end
 
local function getTTKCalculator(args)
if args.rps <= 0 then
return nil
end
-- 射撃レート
local firerate
if args.rpsratio1 > 1 then
if args.rpsratio2 > args.rpsratio1 then
if args.rpsratio3 > args.rpsratio2 then
firerate = {
args.rps,
args.rpsratio1 * args.rps,
args.rpsratio2 * args.rps,
args.rpsratio3 * args.rps,
}
else
local rps2 = args.rpsratio2 * args.rps
firerate = {
args.rps,
args.rpsratio1 * args.rps,
rps2,
rps2,
}
end
else
local rps1 = args.rpsratio1 * args.rps
firerate = {
args.rps,
rps1,
rps1,
rps1,
}
end
else
firerate = args.rps
end
-- 装填数
local magazine
if args.extmag0 > 0 then
if args.extmag1 > args.extmag0 then
if args.extmag2 > args.extmag1 then
if args.extmag3 > args.extmag2 then
magazine = { args.extmag0, args.extmag1, args.extmag2, args.extmag3 }
else
magazine = { args.extmag0, args.extmag1, args.extmag2, args.extmag2 }
end
else
magazine = { args.extmag0, args.extmag1, args.extmag1, args.extmag1 }
end
else
magazine = args.extmag0
end
else
magazine = math.huge
end
-- リロード時間
local reload
if args.reloadratio2 > 0 and args.reloadratio2 < 1 then
if args.reloadratio3 > 0 and args.reloadratio3 < args.reloadratio2 then
if args.reloadratio1 > 0 and args.reloadratio1 < 1 and args.reloadratio1 > args.reloadratio2 then
reload = {
args.reload,
args.reloadratio1 * args.reload,
args.reloadratio2 * args.reload,
args.reloadratio3 * args.reload
}
else
reload = {
args.reload,
args.reload,
args.reloadratio2 * args.reload,
args.reloadratio3 * args.reload
}
end
else
local reload2 = args.reloadratio2 * args.reload
reload = {
args.reload,
args.reload,
reload2,
reload2
}
end
else
reload = args.reload
end
local ttkc = require('Module:Apex/TTKCalculator').new(firerate, magazine, reload, {
raiseTime = args.raise,
})
return ttkc
end
end


local function roundover(num)
local function getContentLanguage(lang)
return math.ceil(num - 0.5)
local language = lang or mw.language.getContentLanguage().code
return language == 'en' and 'en' or 'ja'
end
end


local function renderHeader(table, mul)
-- Calc all base damages
local row = table:tag('tr')
local function calcBaseDamages(damage, headScale, legsScale, helmets, opts)
local cell = row:tag('th')
local headLv1Scale = helmets.level1.func(headScale)
:attr('colspan', 2)
local headLv2Scale = helmets.level2.func(headScale)
:attr('rowspan', 2)
local headLv3Scale = helmets.level3.func(headScale)
if mul == 1 then
local ret = {
cell:wikitext('部位')
scale_hed0 = headScale,
elseif mul == 1.05 then
scale_hed1 = headLv1Scale,
cell:wikitext('部位 (小柄)')
scale_hed2 = headLv2Scale,
elseif mul == 0.85 then
scale_hed3 = headLv3Scale,
cell:wikitext('部位 (鉄壁)')
scale_body = 1,
scale_legs = legsScale,
}
if opts.beamdamage > 0 then
ret.dschrg_hed0 = aw.round(headScale    * opts.beamdamage)
ret.dschrg_hed1 = aw.round(headLv1Scale * opts.beamdamage)
ret.dschrg_hed2 = aw.round(headLv2Scale * opts.beamdamage)
ret.dschrg_hed3 = aw.round(headLv3Scale * opts.beamdamage)
ret.dschrg_body = opts.beamdamage
if legsScale < 1 then
ret.dschrg_legs = aw.round(legsScale * opts.beamdamage)
else
ret.dschrg_legs = opts.beamdamage
end
end
-- Amped Cover
if opts.ampedCover then
damage = aw.round(1.2 * damage)
end
-- Additional Scale
if opts.charged > 1 then
damage = aw.round(opts.charged * damage)
end
-- Unshield Scale
if opts.hammerpointRounds > 1 then
local shieldDamage = damage
local unshldDamage = apex.calcHammerpointDamage(damage, opts.hammerpointRounds)
-- Passthrough
if opts.passthrough < 1 then
shieldDamage = apex.calcPassthroughDamage(shieldDamage, opts.passthrough)
unshldDamage = apex.calcPassthroughDamage(unshldDamage, opts.passthrough)
end
local shieldLegs, unshldLegs
if legsScale < 1 then
shieldLegs = aw.round(legsScale * shieldDamage)
unshldLegs = aw.round(legsScale * unshldDamage)
else
shieldLegs = shieldDamage
unshldLegs = unshldDamage
end
ret.shield_hed0 = aw.round(headScale    * shieldDamage)
ret.shield_hed1 = aw.round(headLv1Scale * shieldDamage)
ret.shield_hed2 = aw.round(headLv2Scale * shieldDamage)
ret.shield_hed3 = aw.round(headLv3Scale * shieldDamage)
ret.shield_body = shieldDamage
ret.shield_legs = shieldLegs
ret.unshld_hed0 = aw.round(headScale    * unshldDamage)
ret.unshld_hed1 = aw.round(headLv1Scale * unshldDamage)
ret.unshld_hed2 = aw.round(headLv2Scale * unshldDamage)
ret.unshld_hed3 = aw.round(headLv3Scale * unshldDamage)
ret.unshld_body = unshldDamage
ret.unshld_legs = unshldLegs
-- Other
else
else
cell:wikitext('部位 (x' .. mul .. ')')
-- Passthrough
if opts.passthrough < 1 then
damage = apex.calcPassthroughDamage(damage, opts.passthrough)
end
local legs
if legsScale < 1 then
legs = aw.round(legsScale * damage)
else
legs = damage
end
ret.shield_hed0 = aw.round(headScale    * damage)
ret.shield_hed1 = aw.round(headLv1Scale * damage)
ret.shield_hed2 = aw.round(headLv2Scale * damage)
ret.shield_hed3 = aw.round(headLv3Scale * damage)
ret.shield_body = damage
ret.shield_legs = legs
ret.unshld_hed0 = ret.shield_hed0
ret.unshld_hed1 = ret.shield_hed1
ret.unshld_hed2 = ret.shield_hed2
ret.unshld_hed3 = ret.shield_hed3
ret.unshld_body = ret.shield_body
ret.unshld_legs = ret.shield_legs
end
end
row:tag('th')
:attr('rowspan', 2)
:wikitext('ダメージ')
row:tag('th')
:attr('colspan', 5)
:wikitext('確殺数')
row = table:tag('tr')
return ret
row:tag('th'):wikitext('100<small> HP</small>')
row:tag('th'):wikitext('<span class="text-rarity text-rarity-common">150<small> HP</small></span>')
row:tag('th'):wikitext('<span class="text-rarity text-rarity-rare">175<small> HP</small></span>')
row:tag('th'):wikitext('<span class="text-rarity text-rarity-epic">200<small> HP</small></span>')
row:tag('th'):wikitext('<span class="text-rarity text-rarity-heirloom">225<small> HP</small></span>')
end
end


local function renderRow(table, name, damage, rowspan)
-- Apply perk/shield scale to damages
rowspan = rowspan or 0
local function applyPerkAndShieldScaleToDamages(damages, perkScale, ignorePerkScaleForHS, opts)
if perkScale ~= 1 then
if opts.disruptorRounds > 1 then
return table.map(damages, function(key, val)
if ignorePerkScaleForHS and (aw.stringstarts(key, 'shield_hed') or aw.stringstarts(key, 'unshld_hed') or aw.stringstarts(key, 'dschrg_hed')) then
if aw.stringstarts(key, 'shield_') then
return apex.calcDisruptorDamage(val, opts.disruptorRounds)
else
return val
end
elseif aw.stringstarts(key, 'shield_') or aw.stringstarts(key, 'unshld_') or aw.stringstarts(key, 'dschrg_') then
local perkedDamage = apex.calcPassiveDamage(val, perkScale, { round = opts.round })
if aw.stringstarts(key, 'shield_') then
return apex.calcDisruptorDamage(perkedDamage, opts.disruptorRounds)
else
return perkedDamage
end
else
return val
end
end)
else
return table.map(damages, function(key, val)
if ignorePerkScaleForHS and (aw.stringstarts(key, 'shield_hed') or aw.stringstarts(key, 'unshld_hed') or aw.stringstarts(key, 'dschrg_hed')) then
return val
elseif aw.stringstarts(key, 'shield_') or aw.stringstarts(key, 'unshld_') or aw.stringstarts(key, 'dschrg_') then
return apex.calcPassiveDamage(val, perkScale, { round = opts.round })
else
return val
end
end)
end
elseif opts.disruptorRounds > 1 then
return table.map(damages, function(key, val)
if aw.stringstarts(key, 'shield_') then
return apex.calcDisruptorDamage(val, opts.disruptorRounds)
else
return val
end
end)
else
return damages
end
end
 
-- Compare damage
local function equalPartDamage(damages, part0, part1)
if damages['dschrg_' .. part0] and damages['dschrg_' .. part1] then
return damages['shield_' .. part0] == damages['shield_' .. part1] and damages['unshld_' .. part0] == damages['unshld_' .. part1] and damages['dschrg_' .. part0] == damages['dschrg_' .. part1]
else
return damages['shield_' .. part0] == damages['shield_' .. part1] and damages['unshld_' .. part0] == damages['unshld_' .. part1]
end
end
 
-- Render the damage row
local function renderRow2(tbl, name, tooltip, damages, partname, weaponinfo, rowinfo, res, rowspan, gunshield)
rowspan = rowspan or 1
local scale = damages['scale_' .. partname]
local shieldDamage = damages['shield_' .. partname]
local unshldDamage = damages['unshld_' .. partname]
local dschrgDamage = damages['dschrg_' .. partname]
local row = tbl:tag('tr')
renderHeaderCell(row, name, tooltip, rowinfo)
local row = table:tag('tr')
local text
if rowspan > 1 then
if shieldDamage > unshldDamage then
row:tag('th')
if weaponinfo.opts.pellets > 1 then
:attr('colspan', 2)
text = string.format(
:css('border-bottom', "0 none transparent")
res.damages.pelletsWithDisruptor,
:wikitext(name)
shieldDamage * weaponinfo.opts.pellets,
elseif rowspan == 1 then
shieldDamage,
row:tag('th')
weaponinfo.opts.pellets,
:attr('colspan', 2)
unshldDamage * weaponinfo.opts.pellets)
:wikitext(name)
else
text = string.format(res.damages.disruptor, shieldDamage, unshldDamage)
end
elseif shieldDamage < unshldDamage then
if weaponinfo.opts.pellets > 1 then
text = string.format(
res.damages.pelletsWithHammerpoint,
shieldDamage * weaponinfo.opts.pellets,
unshldDamage * weaponinfo.opts.pellets,
unshldDamage,
weaponinfo.opts.pellets)
else
text = string.format(res.damages.hammerpoint, shieldDamage, unshldDamage)
end
elseif dschrgDamage and weaponinfo.opts.beamticks >= 1 then
text = string.format(
res.damages.ticks,
dschrgDamage * weaponinfo.opts.beamticks + shieldDamage,
dschrgDamage,
weaponinfo.opts.beamticks,
shieldDamage)
else
else
row:tag('th')
if weaponinfo.opts.pellets > 1 then
:css('border-top', "0 none transparent")
text = string.format(
:css('border-bottom', "0 none transparent")
res.damages.pellets,
:wikitext('&nbsp;&nbsp;&nbsp;')
shieldDamage * weaponinfo.opts.pellets,
row:tag('th')
shieldDamage,
:wikitext(name)
weaponinfo.opts.pellets)
else
text = tostring(shieldDamage)
end
end
row:tag('td')
:attr('rowspan', rowspan)
:wikitext(text)
for _, shield in ipairs(weaponinfo.shields) do
renderCell(row, scale, shield.health, shield.shield, weaponinfo, gunshield, rowspan)
end
end
 
-- Render the helmet damage
local function renderHelmetSection(tbl, damages, res, weaponinfo, helmets, info)
local skipLvl1    = false
local skipLvl2    = false
local hed1Label  = string.format(res.targets.helmet1, damages.scale_hed1)
local hed2Label  = string.format(res.targets.helmet2, damages.scale_hed2)
local hed3Label  = string.format(res.targets.helmet3, damages.scale_hed3)
local hed1Tooltip = string.format(helmets.level1.text, damages.scale_hed0)
local hed2Tooltip = string.format(helmets.level2.text, damages.scale_hed0)
local hed3Tooltip = string.format(helmets.level3.text, damages.scale_hed0)
if not helmets.level0.disabled then
local hed0Rowspan
if equalPartDamage(damages, 'hed0', 'hed1') then
if equalPartDamage(damages, 'hed0', 'hed2') then
if equalPartDamage(damages, 'hed0', 'hed3') then
hed0Rowspan = 4
else
hed0Rowspan = 3
end
else
hed0Rowspan = 2
end
else
hed0Rowspan = 1
end
local hed0Label = string.format(res.targets.head_format, damages.scale_hed0)
renderRow2(tbl, hed0Label, nil, damages, 'hed0', weaponinfo, info.level1top, res, hed0Rowspan)
if hed0Rowspan >= 4 then
renderHeaderCell(tbl:tag('tr'), hed1Label, hed1Tooltip, info.level2first)
renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
renderHeaderCell(tbl:tag('tr'), hed3Label, hed3Tooltip, info.level2)
return
elseif hed0Rowspan == 3 then
renderHeaderCell(tbl:tag('tr'), hed1Label, hed1Tooltip, info.level2first)
renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
skipLvl1 = true
skipLvl2 = true
elseif hed0Rowspan == 2 then
renderHeaderCell(tbl:tag('tr'), hed1Label, hed1Tooltip, info.level2first)
skipLvl1 = true
end
end
-- Lvl 1
if not skipLvl1 then
local hed1Rowspan
if equalPartDamage(damages, 'hed1', 'hed2') then
if equalPartDamage(damages, 'hed1', 'hed3') then
hed1Rowspan = 3
else
hed1Rowspan = 2
end
else
hed1Rowspan = 1
end
renderRow2(tbl, hed1Label, hed1Tooltip, damages, 'hed1', weaponinfo, info.level2first, res, hed1Rowspan)
if hed1Rowspan >= 3 then
renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
renderHeaderCell(tbl:tag('tr'), hed3Label, hed3Tooltip, info.level2)
return
elseif hed1Rowspan == 2 then
renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
skipLvl2 = true
end
end
-- Lvl 2
if not skipLvl2 then
local hed2Rowspan
if equalPartDamage(damages, 'hed2', 'hed3') then
hed2Rowspan = 2
else
hed2Rowspan = 1
end
renderRow2(tbl, hed2Label, hed2Tooltip, damages, 'hed2', weaponinfo, info.level2, res, hed2Rowspan)
if hed2Rowspan >= 2 then
renderHeaderCell(tbl:tag('tr'), hed3Label, hed3Tooltip, info.level2)
return
end
end
end
row:tag('td'):attr('align', 'right'):wikitext(damage)
row:tag('td'):attr('align', 'right'):wikitext(math.ceil(100 / damage))
-- Lvl 3
row:tag('td'):attr('align', 'right'):wikitext(math.ceil(150 / damage))
renderRow2(tbl, hed3Label, hed3Tooltip, damages, 'hed3', weaponinfo, info.level2, res, 1)
row:tag('td'):attr('align', 'right'):wikitext(math.ceil(175 / damage))
row:tag('td'):attr('align', 'right'):wikitext(math.ceil(200 / damage))
row:tag('td'):attr('align', 'right'):wikitext(math.ceil(225 / damage))
end
end


local function renderTable(args)
local function renderTable(args)
local mul = args.mul or 1
local lang = getContentLanguage(args.lang)
local roundFunc
local res = mw.loadData('Module:DamageTable/configuration')[lang]
if mul == 0.85 then
local shieldinfo = args.shieldinfo or require('Module:Stat/Shield')[args.shieldpreset]
roundFunc = round
local skullpiercer = args.skullpiercer or 1
local headMul = args.head or 2
local hideNoHelmets = shieldinfo.helmets.level0.disabled
local minHead
if hideNoHelmets then
minHead = shieldinfo.helmets.level1.func(args.headmax)
else
else
roundFunc = roundover
minHead = args.headmax
end
end
if shieldinfo.sameHeadshotDamageToNormalOnFortified then
args.mul2 = shieldinfo.fortified
end
local hasSecond = args.mul2 > 0 and args.mul2 ~= 1
local noLegsShot = args.leg < 0 or args.leg >= 1 or (shieldinfo.legAsBodyOnLowProfile and args.mul > 1)
local noLegsShot2 = args.leg < 0 or args.leg >= 1 or (shieldinfo.legAsBodyOnLowProfile and args.mul2 > 1)
-- Get/calc min count
local minCount
if args.mincount > 0 then
minCount = args.mincount
else
minCount, _ = getShotCount(
minHead,
100,
0,
{
damage = math.max(args.damage, args.damagemax),
mul    = math.max(args.mul, args.mulmax),
opts = {
ampedCover              = true,
beamdamage              = math.max(args.beamdamage,  args.beamdamagemax),
beamticks                = math.max(args.ticks,      args.ticksmax),
charged                  = math.max(args.charged,    args.chargedmax),
disruptorRounds          = math.max(args.disruptor,  args.disruptorsup),
gunshieldBleedthrough    = shieldinfo.gunshieldBleedthrough,
hammerpointRounds        = math.max(args.hammerpoint, args.hammerpointsup),
ignorePassiveForHeadshot = shieldinfo.sameHeadshotDamageToNormalOnFortified,
passthrough              = 1,
pellets                  = math.max(args.pellet,      args.pelletmax),
round                    = args.round,
}
})
end
-- Get/calc max count
local maxCount
if args.maxcount < 250 then
maxCount = args.maxcount
else
local damagemin = math.min(args.damage, args.damagemin)
local mininfo = {
damage = damagemin,
mul    = math.min(args.mul, args.mulmin),
opts = {
ampedCover              = false,
beamdamage              = math.min(args.beamdamage,  args.beamdamagemin),
beamticks                = math.min(args.ticks,      args.ticksmin),
charged                  = 1,
disruptorRounds          = 1,
gunshieldBleedthrough    = shieldinfo.gunshieldBleedthrough,
hammerpointRounds        = 1,
ignorePassiveForHeadshot = shieldinfo.sameHeadshotDamageToNormalOnFortified,
passthrough              = math.min(args.passthrough, args.passthroughsup),
pellets                  = math.min(args.pellet, args.pelletmin),
round                    = args.round,
}
}
local maxshield = shieldinfo.shields[#shieldinfo.shields]
local legCount, _ = getShotCount(math.min(args.leg, args.legmin), maxshield.health, maxshield.shield, mininfo)
local bodyWithGunSheild, _ = getShotCount(1, maxshield.health, maxshield.shield, mininfo, shieldinfo.gunshield)
maxCount = math.max(legCount, bodyWithGunSheild)
end
local ttkCalculator = getTTKCalculator(args)
-- Define weapon info
local weaponinfo = {
damage    = args.damage,
mul        = args.mul,
minCount  = minCount,
diffCount  = maxCount - minCount,
shields    = shieldinfo.shields,
helmets    = shieldinfo.helmets,
ttkCalculator = ttkCalculator,
opts = {
ampedCover              = args.amped,
beamdamage              = args.beamdamage,
beamticks                = args.ticks,
charged                  = args.charged,
disruptorRounds          = args.disruptor,
gunshieldBleedthrough    = shieldinfo.gunshieldBleedthrough,
hammerpointRounds        = args.hammerpoint,
ignorePassiveForHeadshot = shieldinfo.sameHeadshotDamageToNormalOnFortified,
passthrough              = args.passthrough,
pellets                  = args.pellet,
round                    = args.round,
},
}
local baseDamage = args.damage or 20
local colspan, info
local headMul = args.head or 2
if skullpiercer > 1 then
local headDamage = roundFunc(headMul * baseDamage)
colspan = 3
local legMul = args.leg or 0.8
info = {
level1 = {
{ breaktop = false, breakbottom = false, colspan = 3 },
},
level1top = {
{ breaktop = true, breakbottom = true, colspan = 3 },
},
level1gunshiled = {
{ breaktop = false, breakbottom = true, colspan = 3 },
},
level2 = {
{ breaktop = true, breakbottom = false, colspan = 2 },
},
level2gunshiled = {
{ breaktop = true, breakbottom = false },
{ breaktop = false, breakbottom = false, colspan = 2 },
},
level3 = {
{ breaktop = false, breakbottom = true },
{ breaktop = false, breakbottom = false, showbottom = true },
},
level3first = {
hideNoHelmets and { breaktop = false, breakbottom = false, rowspan = 6, content = res.targets.head, vertical = res.targets.head_vertical, verticalAlign = res.targets.head_verticalalign } or { breaktop = true,  breakbottom = false, rowspan = 6 },
{ breaktop = false, breakbottom = true },
{ breaktop = false, breakbottom = false, showbottom = true },
},
level3top = {
{ breaktop = false, breakbottom = true, colspan = 2 },
{ breaktop = false, breakbottom = false, showbottom = true },
},
}
info.level2first = info.level2
if hasSecond and not noLegsShot2 then
info.level1gunshiled = nil
info.level2gunshiled = {
{ breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.fortified, vertical = res.targets.fortified_vertical, verticalAlign = res.targets.fortified_verticalalign },
{ breaktop = false, breakbottom = true, colspan = 2 },
}
info.level3gunshiled = {
{ breaktop = true, breakbottom = false },
{ breaktop = false, breakbottom = false },
}
end
elseif hasSecond and not noLegsShot2 then
colspan = 3
info = {
level1 = {
{ breaktop = false, breakbottom = false, colspan = 3 },
},
level1top = {
{ breaktop = false, breakbottom = true, colspan = 3 },
},
level2 = {
{ breaktop = false, breakbottom = false, colspan = 2 },
},
level2first = {
hideNoHelmets and { breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.head, vertical = res.targets.head_vertical, verticalAlign = res.targets.head_verticalalign } or { breaktop = true,  breakbottom = false, rowspan = 3 },
{ breaktop = false, breakbottom = false, colspan = 2 },
},
level2gunshiled = {
{ breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.fortified, vertical = res.targets.fortified_vertical, verticalAlign = res.targets.fortified_verticalalign },
{ breaktop = false, breakbottom = true, colspan = 2 },
},
level3 = {
{ breaktop = false, breakbottom = false },
},
level3gunshiled = {
{ breaktop = true, breakbottom = false },
{ breaktop = false, breakbottom = false },
},
}
else
colspan = 2
info = {
level1 = {
{ breaktop = false, breakbottom = false, colspan = 2 },
},
level1gunshiled = {
{ breaktop = false, breakbottom = true, colspan = 2 },
},
level2 = {
{ breaktop = false, breakbottom = false },
},
level2first = {
hideNoHelmets and { breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.head, vertical = res.targets.head_vertical, verticalAlign = res.targets.head_verticalalign } or { breaktop = true,  breakbottom = false, rowspan = 3 },
{ breaktop = false, breakbottom = false },
},
level2gunshiled = {
{ breaktop = true, breakbottom = false },
{ breaktop = false, breakbottom = false },
},
}
info.level1top = info.level1gunshiled
end
local table = mw.html.create('table')
local table = mw.html.create('table')
:addClass('wikitable')
:addClass('wikitable')
renderHeader(table, mul)
:addClass('numbertable')
renderRow(
:addClass('damagetable')
table,
if args.caption ~= nil then
"頭 (x" .. headMul .. ")",
table:tag('caption')
roundFunc(mul * headDamage),
:wikitext(args.caption)
4)
end
local hlmLv1Mul = 0.2 + 0.8 * headMul
local baseDamages = calcBaseDamages(weaponinfo.damage, headMul, args.leg, weaponinfo.helmets, weaponinfo.opts)
renderRow(
local perkedDamages = applyPerkAndShieldScaleToDamages(baseDamages, weaponinfo.mul, shieldinfo.sameHeadshotDamageToNormalOnFortified, weaponinfo.opts)
table,
renderHeader(table, args.mul, colspan, shieldinfo.shields, res)
'<span class="text-rarity text-rarity-common">Lv.1 (x' .. hlmLv1Mul .. ')</span>',
roundFunc(mul * round(hlmLv1Mul * baseDamage)))
local hlmLv2Mul = 0.4 + 0.6 * headMul
-- Headshot damage
renderRow(
if headMul > 1 then
table,
-- w/Skullpiercer Rifling
'<span class="text-rarity text-rarity-rare">Lv.2 (x' .. hlmLv2Mul .. ')</span>',
if skullpiercer > 1 then
roundFunc(mul * round(hlmLv2Mul * baseDamage)))
if not hideNoHelmets then
renderRow(
table,
string.format(res.targets.skullpiercer0, args.skullpiercerrarity, skullpiercer),
nil,
skullpiercer, weaponinfo, info.level3top, res)
renderRow2(
table,
string.format(res.targets.head_format, perkedDamages.scale_hed0),
nil,
perkedDamages,
'hed0',
weaponinfo, info.level1top, res)
end
local skullpiercerLv1Mul = shieldinfo.helmets.level1.func(skullpiercer)
renderRow(
table,
string.format(res.targets.skullpiercer1, args.skullpiercerrarity, skullpiercerLv1Mul),
string.format(shieldinfo.helmets.level1.text, skullpiercer),
skullpiercerLv1Mul, weaponinfo, info.level3first, res)
renderRow2(
table,
string.format(res.targets.helmet1, perkedDamages.scale_hed1),
string.format(shieldinfo.helmets.level1.text, headMul),
perkedDamages,
'hed1',
weaponinfo, info.level2first, res)
local skullpiercerLv2Mul = shieldinfo.helmets.level2.func(skullpiercer)
renderRow(
table,
string.format(res.targets.skullpiercer2, args.skullpiercerrarity, skullpiercerLv2Mul),
string.format(shieldinfo.helmets.level2.text, skullpiercer),
skullpiercerLv2Mul, weaponinfo, info.level3, res)
renderRow2(
table,
string.format(res.targets.helmet2, perkedDamages.scale_hed2),
string.format(shieldinfo.helmets.level2.text, headMul),
perkedDamages,
'hed2',
weaponinfo, info.level2, res)
local skullpiercerLv3Mul = shieldinfo.helmets.level3.func(skullpiercer)
renderRow(
table,
string.format(res.targets.skullpiercer3, args.skullpiercerrarity, skullpiercerLv3Mul),
string.format(shieldinfo.helmets.level3.text, skullpiercer),
skullpiercerLv3Mul, weaponinfo, info.level3, res)
renderRow2(
table,
string.format(res.targets.helmet3, perkedDamages.scale_hed3),
string.format(shieldinfo.helmets.level3.text, headMul),
perkedDamages,
'hed3',
weaponinfo, info.level2, res)
-- w/o Skullpiercer Rifling
else
renderHelmetSection(table, perkedDamages, res, weaponinfo, shieldinfo.helmets, info)
end
end
local hlmLv3Mul = 0.5 + 0.5 * headMul
-- Body shot damage
renderRow(
local label
table,
if headMul <= 1 then
'<span class="text-rarity text-rarity-epic">Lv.3</span>/<span class="text-rarity text-rarity-legendary">4 (x' .. hlmLv3Mul .. ')</span>',
if noLegsShot then
roundFunc(mul * round(hlmLv3Mul * baseDamage)))
label = res.targets.all
if mul > 1 then
else
renderRow(table, "胴・脚", roundFunc(mul * baseDamage), 1)
label = res.targets.headbody
end
elseif noLegsShot then
label = res.targets.bodylegs
else
else
renderRow(table, "胴", roundFunc(mul * baseDamage), 1)
label = res.targets.body
renderRow(table, "脚 (x" .. legMul .. ")", roundFunc(mul * round(legMul * baseDamage)), 1)
end
if args.mul < 1 or args.forcegunshield then
renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1gunshiled, res)
renderRow(
table,
res.targets.gunshield,
shieldinfo.gunshieldLabel,
1, weaponinfo, info.level2gunshiled, res, shieldinfo.gunshield)
else
renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1, res)
end
-- Legs shot damage
if not noLegsShot then
local legsLabel = string.format(res.targets.legs, args.leg)
renderRow2(table, legsLabel, nil, perkedDamages, 'legs', weaponinfo, info.level1, res)
end
-- the 2nd place of damages
if hasSecond then
local rowinfoSecondTop, rowinfoSecondGunShield, label2
if not noLegsShot2 then
rowinfoSecondTop      = info.level2gunshiled
rowinfoSecondGunShield = info.level3gunshiled
label2                = label
elseif args.mul2 < 1 then
rowinfoSecondTop      = info.level1gunshiled
rowinfoSecondGunShield = info.level2gunshiled
label2                = string.format(res.targets.fortified_prefix, args.mul2, label)
else
rowinfoSecondTop      = info.level1
rowinfoSecondGunShield = nil
label2                = string.format(res.targets.fortified_prefix, args.mul2, label)
end
local weaponinfo2    = aw.shallowCopy(weaponinfo)
weaponinfo2.mul     = args.mul2
local perkedDamages2 = applyPerkAndShieldScaleToDamages(baseDamages, weaponinfo2.mul, shieldinfo.sameHeadshotDamageToNormalOnFortified, weaponinfo2.opts)
if args.mul2 < 1 then
renderRow2(table, label2, nil, perkedDamages2, 'body', weaponinfo2, rowinfoSecondTop, res)
renderRow(
table,
res.targets.gunshield,
shieldinfo.gunshieldLabel,
1, weaponinfo2, rowinfoSecondGunShield, res, shieldinfo.gunshield)
else
renderRow2(table, label2, nil, perkedDamages2, 'body', weaponinfo2, info.level2, res)
end
-- Legs shot damage
if not noLegsShot2 then
local legsLabel = string.format(res.targets.legs, args.leg)
renderRow2(table, legsLabel, nil, perkedDamages2, 'legs', weaponinfo2, info.level2, res)
end
end
end
return table
return table
end
end


function p._main(args)
function p._main(args, frame)
-- init value
local initValues = {
mincount = 0,
maxcount = 250,
damage = 20,
damagemin = 1000,
damagemax = 0,
beamdamage = 0,
beamdamagemin = 0,
beamdamagemax = 0,
ticks = 0,
ticksmin = math.huge,
ticksmax = 0,
pellet = 1,
pelletmin = 0,
pelletmax = 0,
head = 2,
headmax = -1,
leg = 0.8,
legmin = 0,
mul = 1,
mul2 = 0,
mulmin = 0.85,
mulmax = 1,
charged = 1,
chargedmax = 1,
skullpiercer = 1,
disruptor = 1,
disruptorsup = 1,
hammerpoint = 1,
hammerpointsup = 1,
passthrough = 1,
passthroughsup = 1,
rps = 0,
rpsratio1 = 0,
rpsratio2 = 0,
rpsratio3 = 0,
raise = 0,
reload = 0,
reloadratio1 = 0.963,
reloadratio2 = 0.933,
reloadratio3 = 0.9,
extmag0 = 0,
extmag1 = 0,
extmag2 = 0,
extmag3 = 0,
}
-- fix arguments
for key, value in pairs(initValues) do
args[key] = aw.getAsNumber(args[key], value)
end
if args.pelletmin == 0 then
args.pelletmin = args.pellet
end
if args.pelletmax == 0 then
args.pelletmax = args.pellet
end
if args.headmax == -1 then
args.headmax = math.max(args.head, args.skullpiercer)
end
if args.legmin == 0 then
args.legmin = args.leg
end
if args.beamdamagemin == 0 then
args.beamdamagemin = args.beamdamage
end
if args.beamdamagemax == 0 then
args.beamdamagemax = args.beamdamage
end
args.round = aw.getAsBoolean(args.round, false)
args.amped = aw.getAsBoolean(args.amped, false)
args.forcegunshield = aw.getAsBoolean(args.forcegunshield, false)
args.rarity = args.rarity or 'epic'
args.skullpiercerrarity = args.skullpiercerrarity or 'legendary'
args.shieldpreset = args.shieldpreset or 'reinforcehelmets'
return tostring(renderTable(args))
return tostring(renderTable(args))
end
end
128行目: 1,072行目:
args = getArgs(frame)
args = getArgs(frame)
-- fix arguments
return p._main(args, frame)
args.damage = tonumber(args.damage)
args.head = tonumber(args.head)
args.leg = tonumber(args.leg)
args.mul = tonumber(args.mul)
return p._main(args)
end
end


return p
return p

2022年8月10日 (水) 12:59時点における最新版

このモジュールについての説明文ページを モジュール:DamageTable/doc に作成できます

require('Module:Utility/mw.html Extensions')

local p = {}

local table = require('Module:TableExtensions')
local aw    = require('Module:Utility/Library')
local apex  = require('Module:Utility/ApexLibrary')
local Color = require('Module:Color')
local STKCalculator = require('Module:Apex/STKCalculator')
local getArgs -- lazily initialized

local function getShotCount(part, health, shield, info, gunshield)
	local damages = apex.calcShotCount(info.damage, part, info.mul, health, shield, gunshield or 0, info.opts)
	return #damages, damages
end

local function renderHeader(tbl, mul, colspan, shields, res)
	tbl:tag('tr')
		:tag('th')
			:attr('colspan', colspan)
			:attr('rowspan', 2)
			:wikitextIf(
				mul == 1,
				res.targets.caption,
				function()
					return string.format(res.targets.caption_format, mul)
				end)
			:done()
		:tag('th')
			:attr('rowspan', 2)
			:wikitext(res.damages.caption)
			:done()
		:tag('th')
			:attr('colspan', #shields)
			:wikitext(res.shotstokill)
	
	local row = tbl:tag('tr')
	for _, shield in ipairs(shields) do
		row:tag('th')
			:attrIf(
				shield.tooltip ~= nil,
				{ ['data-tooltip'] = shield.tooltip })
			:wikitext(shield.label)
	end
end

local function renderCell(row, part, health, shield, info, gunshield, rowspan)
	local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
	local ratio = (shotCount - info.minCount) / info.diffCount
	local tooltip = { aw.stringifyRepeatingArray(damages, '→') }
	if info.ttkCalculator then
		local template = '<tr><th>%s&nbsp;</th><td class="cell-type-number" style="font-weight:bold">%d</td><td>&thinsp;ミリ秒</td></tr>'
		local ttkc = info.ttkCalculator
		local ttk0 = aw.round(1000 * ttkc:getAsLevel(shotCount, 0))
		local ttk1 = aw.round(1000 * ttkc:getAsLevel(shotCount, 1))
		local ttk2 = aw.round(1000 * ttkc:getAsLevel(shotCount, 2))
		local ttk3 = aw.round(1000 * ttkc:getAsLevel(shotCount, 3))
		
		if ttk2 ~= ttk3 then
			table.insert(tooltip, '<br><br>キルタイム:<table class="condensedtable listtable">')
			if ttk0 == ttk1 then
				table.insert(tooltip, string.format(template, 'なし・Lv.1', ttk0))
				table.insert(tooltip, string.format(template, 'Lv.2', ttk2))
			else
				table.insert(tooltip, string.format(template, 'なし', ttk0))
				table.insert(tooltip, string.format(template, 'Lv.1', ttk1))
				table.insert(tooltip, string.format(template, 'Lv.2', ttk2))
			end
			table.insert(tooltip, string.format(template, 'Lv.3', ttk3))
			table.insert(tooltip, '</table>')
		elseif ttk1 ~= ttk2 then
			table.insert(tooltip, '<br><br>キルタイム:<table class="condensedtable listtable">')
			if ttk0 == ttk1 then
				table.insert(tooltip, string.format(template, 'なし・Lv.1', ttk0))
			else
				table.insert(tooltip, string.format(template, 'なし', ttk0))
				table.insert(tooltip, string.format(template, 'Lv.1', ttk1))
			end
			table.insert(tooltip, string.format(template, 'Lv.2, 3', ttk2))
			table.insert(tooltip, '</table>')
		elseif ttk0 ~= ttk1 then
			table.insert(tooltip, '<br><br>キルタイム:<table class="condensedtable listtable">')
			table.insert(tooltip, string.format(template, 'なし', ttk0))
			table.insert(tooltip, string.format(template, 'Lv.1, 2, 3', ttk1))
			table.insert(tooltip, '</table>')
		else
			table.insert(tooltip, string.format('<br>キルタイム: <span class="text-style-number" style="font-weight:bold">%d</span>&thinsp;ミリ秒', ttk0))
		end
	end
	
	local hue   = (info.diffCount < 5 and 0.65 or 0.85) * ratio
	local color = Color.HSL(hue, 0.91, 0.89):toRGB()
	row:tag('td')
		:attrIf(rowspan > 1, { rowspan = rowspan })
		:attr('data-tooltip', table.concat(tooltip))
		:css('background-color', tostring(color))
		:wikitext(shotCount)
end

local function renderHeaderCell(row, name, tooltip, rowinfo)
	for i = 1, #rowinfo do
		local cellinfo = rowinfo[i]
		local colspan = cellinfo.colspan or 1
		local rowspan = cellinfo.rowspan or 1
		
		local cell = row:tag('th')
		if colspan > 1 then
			cell:attr('colspan', colspan)
		end
		if rowspan > 1 then
			cell:attr('rowspan', rowspan)
		end
		if cellinfo.breaktop then
			cell:css('border-top', '0 none transparent')
		end
		if cellinfo.breakbottom then
			cell:css('border-bottom', '0 none transparent')
		end
		--if cellinfo.showbottom then
		--	cell:css({
		--		['border-bottom-style'] = 'solid',
		--		['border-bottom-width'] = '1px',
		--	})
		--end
		if cellinfo.verticalAlign then
			cell:css('vertical-align', cellinfo.verticalAlign)
		end
		if i == #rowinfo then
			if tooltip ~= nil then
				cell:attr('data-tooltip', tooltip)
			end
			cell:wikitext(name)
		elseif cellinfo.content then
			if cellinfo.vertical then
				cell:addClass('vertical-cell')
					:tag('span')
						:wikitext(cellinfo.content)
			else
				cell:wikitext(cellinfo.content)
			end
		else
			cell:wikitext('&nbsp;&nbsp;')
		end
	end
end

local function renderRow(tbl, name, tooltip, part, weaponinfo, rowinfo, res, gunshield)
	local row = tbl:tag('tr')
	renderHeaderCell(row, name, tooltip, rowinfo)
	
	local damage
	local opts2 = aw.shallowCopy(weaponinfo.opts)
	table.removeKey(opts2, 'beamdamage')
	table.removeKey(opts2, 'beamticks')
	if gunshield then
		table.removeKey(opts2, 'disruptorRounds')
		table.removeKey(opts2, 'hammerpointRounds')
		damage = apex.calcDamage(weaponinfo.damage, 1, 1, opts2)
	else
		damage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, opts2)
	end
	
	local text
	if not gunshield and weaponinfo.opts.disruptorRounds > 1 then
		local optsD = aw.shallowCopy(weaponinfo.opts)
		table.removeKey(optsD, 'disruptorRounds')
		local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, optsD)
		if weaponinfo.opts.pellets > 1 then
			text = string.format(
				res.damages.pelletsWithDisruptor,
				damage * weaponinfo.opts.pellets,
				damage,
				weaponinfo.opts.pellets,
				defaultDamage * weaponinfo.opts.pellets)
		else
			text = string.format(res.damages.disruptor, damage, defaultDamage)
		end
	elseif not gunshield and weaponinfo.opts.hammerpointRounds > 1 then
		local optsH = aw.shallowCopy(weaponinfo.opts)
		table.removeKey(optsH, 'hammerpointRounds')
		local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, optsH)
		if weaponinfo.opts.pellets > 1 then
			text = string.format(
				res.damages.pelletsWithHammerpoint,
				defaultDamage * weaponinfo.opts.pellets,
				damage * weaponinfo.opts.pellets,
				damage,
				weaponinfo.opts.pellets)
		else
			text = string.format(res.damages.hammerpoint, defaultDamage, damage)
		end
	elseif weaponinfo.opts.pellets > 1 then
		text = string.format(
			res.damages.pellets,
			damage * weaponinfo.opts.pellets,
			damage,
			weaponinfo.opts.pellets)
	elseif weaponinfo.opts.beamticks > 1 then
		local beamdamage = apex.calcDamageFromPartAndPassive(weaponinfo.opts.beamdamage, part, weaponinfo.mul, opts2)
		local alldamage = beamdamage * weaponinfo.opts.beamticks + damage
		text = string.format(
			res.damages.ticks,
			alldamage,
			beamdamage,
			weaponinfo.opts.beamticks,
			damage)
	else
		text = tostring(damage)
	end
	row:tag('td'):wikitext(text)
	
	for _, shield in ipairs(weaponinfo.shields) do
		renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield, 1)
	end
end

local function getTTKCalculator(args)
	if args.rps <= 0 then
		return nil
	end
	
	-- 射撃レート
	local firerate
	if args.rpsratio1 > 1 then
		if args.rpsratio2 > args.rpsratio1 then
			if args.rpsratio3 > args.rpsratio2 then
				firerate = {
					args.rps,
					args.rpsratio1 * args.rps,
					args.rpsratio2 * args.rps,
					args.rpsratio3 * args.rps,
				}
			else
				local rps2 = args.rpsratio2 * args.rps
				firerate = {
					args.rps,
					args.rpsratio1 * args.rps,
					rps2,
					rps2,
				}
			end
		else
			local rps1 = args.rpsratio1 * args.rps
			firerate = {
				args.rps,
				rps1,
				rps1,
				rps1,
			}
		end
	else
		firerate = args.rps
	end
	
	-- 装填数
	local magazine
	if args.extmag0 > 0 then
		if args.extmag1 > args.extmag0 then
			if args.extmag2 > args.extmag1 then
				if args.extmag3 > args.extmag2 then
					magazine = { args.extmag0, args.extmag1, args.extmag2, args.extmag3 }
				else
					magazine = { args.extmag0, args.extmag1, args.extmag2, args.extmag2 }
				end
			else
				magazine = { args.extmag0, args.extmag1, args.extmag1, args.extmag1 }
			end
		else
			magazine = args.extmag0
		end
	else
		magazine = math.huge
	end
	
	-- リロード時間
	local reload
	if args.reloadratio2 > 0 and args.reloadratio2 < 1 then
		if args.reloadratio3 > 0 and args.reloadratio3 < args.reloadratio2 then
			if args.reloadratio1 > 0 and args.reloadratio1 < 1 and args.reloadratio1 > args.reloadratio2 then
				reload = {
					args.reload,
					args.reloadratio1 * args.reload,
					args.reloadratio2 * args.reload,
					args.reloadratio3 * args.reload
				}
			else
				reload = {
					args.reload,
					args.reload,
					args.reloadratio2 * args.reload,
					args.reloadratio3 * args.reload
				}
			end
		else
			local reload2 = args.reloadratio2 * args.reload
			reload = {
				args.reload,
				args.reload,
				reload2,
				reload2
			}
		end
	else
		reload = args.reload
	end
	
	local ttkc = require('Module:Apex/TTKCalculator').new(firerate, magazine, reload, {
		raiseTime = args.raise,
	})
	return ttkc
end

local function getContentLanguage(lang)
	local language = lang or mw.language.getContentLanguage().code
	return language == 'en' and 'en' or 'ja'
end

-- Calc all base damages
local function calcBaseDamages(damage, headScale, legsScale, helmets, opts)
	local headLv1Scale = helmets.level1.func(headScale)
	local headLv2Scale = helmets.level2.func(headScale)
	local headLv3Scale = helmets.level3.func(headScale)
	local ret = {
		scale_hed0 = headScale,
		scale_hed1 = headLv1Scale,
		scale_hed2 = headLv2Scale,
		scale_hed3 = headLv3Scale,
		scale_body = 1,
		scale_legs = legsScale,
	}
	
	if opts.beamdamage > 0 then
		ret.dschrg_hed0 = aw.round(headScale    * opts.beamdamage)
		ret.dschrg_hed1 = aw.round(headLv1Scale * opts.beamdamage)
		ret.dschrg_hed2 = aw.round(headLv2Scale * opts.beamdamage)
		ret.dschrg_hed3 = aw.round(headLv3Scale * opts.beamdamage)
		ret.dschrg_body = opts.beamdamage
		if legsScale < 1 then
			ret.dschrg_legs = aw.round(legsScale * opts.beamdamage)
		else
			ret.dschrg_legs = opts.beamdamage
		end
	end
	
	-- Amped Cover
	if opts.ampedCover then
		damage = aw.round(1.2 * damage)
	end
	
	-- Additional Scale
	if opts.charged > 1 then
		damage = aw.round(opts.charged * damage)
	end
	
	-- Unshield Scale
	if opts.hammerpointRounds > 1 then
		local shieldDamage = damage
		local unshldDamage = apex.calcHammerpointDamage(damage, opts.hammerpointRounds)
		
		-- Passthrough
		if opts.passthrough < 1 then
			shieldDamage = apex.calcPassthroughDamage(shieldDamage, opts.passthrough)
			unshldDamage = apex.calcPassthroughDamage(unshldDamage, opts.passthrough)
		end
		
		local shieldLegs, unshldLegs
		if legsScale < 1 then
			shieldLegs = aw.round(legsScale * shieldDamage)
			unshldLegs = aw.round(legsScale * unshldDamage)
		else
			shieldLegs = shieldDamage
			unshldLegs = unshldDamage
		end
		
		ret.shield_hed0 = aw.round(headScale    * shieldDamage)
		ret.shield_hed1 = aw.round(headLv1Scale * shieldDamage)
		ret.shield_hed2 = aw.round(headLv2Scale * shieldDamage)
		ret.shield_hed3 = aw.round(headLv3Scale * shieldDamage)
		ret.shield_body = shieldDamage
		ret.shield_legs = shieldLegs
			
		ret.unshld_hed0 = aw.round(headScale    * unshldDamage)
		ret.unshld_hed1 = aw.round(headLv1Scale * unshldDamage)
		ret.unshld_hed2 = aw.round(headLv2Scale * unshldDamage)
		ret.unshld_hed3 = aw.round(headLv3Scale * unshldDamage)
		ret.unshld_body = unshldDamage
		ret.unshld_legs = unshldLegs
		
	-- Other
	else
		-- Passthrough
		if opts.passthrough < 1 then
			damage = apex.calcPassthroughDamage(damage, opts.passthrough)
		end
		
		local legs
		if legsScale < 1 then
			legs = aw.round(legsScale * damage)
		else
			legs = damage
		end
		
		ret.shield_hed0 = aw.round(headScale    * damage)
		ret.shield_hed1 = aw.round(headLv1Scale * damage)
		ret.shield_hed2 = aw.round(headLv2Scale * damage)
		ret.shield_hed3 = aw.round(headLv3Scale * damage)
		ret.shield_body = damage
		ret.shield_legs = legs
		
		ret.unshld_hed0 = ret.shield_hed0
		ret.unshld_hed1 = ret.shield_hed1
		ret.unshld_hed2 = ret.shield_hed2
		ret.unshld_hed3 = ret.shield_hed3
		ret.unshld_body = ret.shield_body
		ret.unshld_legs = ret.shield_legs
	end
	
	return ret
end

-- Apply perk/shield scale to damages
local function applyPerkAndShieldScaleToDamages(damages, perkScale, ignorePerkScaleForHS, opts)
	if perkScale ~= 1 then
		if opts.disruptorRounds > 1 then
			return table.map(damages, function(key, val)
				if ignorePerkScaleForHS and (aw.stringstarts(key, 'shield_hed') or aw.stringstarts(key, 'unshld_hed') or aw.stringstarts(key, 'dschrg_hed')) then
					if aw.stringstarts(key, 'shield_') then
						return apex.calcDisruptorDamage(val, opts.disruptorRounds)
					else
						return val
					end
				elseif aw.stringstarts(key, 'shield_') or aw.stringstarts(key, 'unshld_') or aw.stringstarts(key, 'dschrg_') then
					local perkedDamage = apex.calcPassiveDamage(val, perkScale, { round = opts.round })
					if aw.stringstarts(key, 'shield_') then
						return apex.calcDisruptorDamage(perkedDamage, opts.disruptorRounds)
					else
						return perkedDamage
					end
				else
					return val
				end
			end)
		else
			return table.map(damages, function(key, val)
				if ignorePerkScaleForHS and (aw.stringstarts(key, 'shield_hed') or aw.stringstarts(key, 'unshld_hed') or aw.stringstarts(key, 'dschrg_hed')) then
					return val
				elseif aw.stringstarts(key, 'shield_') or aw.stringstarts(key, 'unshld_') or aw.stringstarts(key, 'dschrg_') then
					return apex.calcPassiveDamage(val, perkScale, { round = opts.round })
				else
					return val
				end
			end)
		end
	elseif opts.disruptorRounds > 1 then
		return table.map(damages, function(key, val)
			if aw.stringstarts(key, 'shield_') then
				return apex.calcDisruptorDamage(val, opts.disruptorRounds)
			else
				return val
			end
		end)
	else
		return damages
	end
end

-- Compare damage
local function equalPartDamage(damages, part0, part1)
	if damages['dschrg_' .. part0] and damages['dschrg_' .. part1] then
		return damages['shield_' .. part0] == damages['shield_' .. part1] and damages['unshld_' .. part0] == damages['unshld_' .. part1] and damages['dschrg_' .. part0] == damages['dschrg_' .. part1]
	else
		return damages['shield_' .. part0] == damages['shield_' .. part1] and damages['unshld_' .. part0] == damages['unshld_' .. part1]
	end
end

-- Render the damage row
local function renderRow2(tbl, name, tooltip, damages, partname, weaponinfo, rowinfo, res, rowspan, gunshield)
	rowspan = rowspan or 1
	
	local scale = damages['scale_' .. partname]
	local shieldDamage = damages['shield_' .. partname]
	local unshldDamage = damages['unshld_' .. partname]
	local dschrgDamage = damages['dschrg_' .. partname]
	
	local row = tbl:tag('tr')
	renderHeaderCell(row, name, tooltip, rowinfo)
	
	local text
	if shieldDamage > unshldDamage then
		if weaponinfo.opts.pellets > 1 then
			text = string.format(
				res.damages.pelletsWithDisruptor,
				shieldDamage * weaponinfo.opts.pellets,
				shieldDamage,
				weaponinfo.opts.pellets,
				unshldDamage * weaponinfo.opts.pellets)
		else
			text = string.format(res.damages.disruptor, shieldDamage, unshldDamage)
		end
	elseif shieldDamage < unshldDamage then
		if weaponinfo.opts.pellets > 1 then
			text = string.format(
				res.damages.pelletsWithHammerpoint,
				shieldDamage * weaponinfo.opts.pellets,
				unshldDamage * weaponinfo.opts.pellets,
				unshldDamage,
				weaponinfo.opts.pellets)
		else
			text = string.format(res.damages.hammerpoint, shieldDamage, unshldDamage)
		end
	elseif dschrgDamage and weaponinfo.opts.beamticks >= 1 then
		text = string.format(
			res.damages.ticks,
			dschrgDamage * weaponinfo.opts.beamticks + shieldDamage,
			dschrgDamage,
			weaponinfo.opts.beamticks,
			shieldDamage)
	else
		if weaponinfo.opts.pellets > 1 then
			text = string.format(
				res.damages.pellets,
				shieldDamage * weaponinfo.opts.pellets,
				shieldDamage,
				weaponinfo.opts.pellets)
		else
			text = tostring(shieldDamage)
		end
	end
	row:tag('td')
		:attr('rowspan', rowspan)
		:wikitext(text)
	
	for _, shield in ipairs(weaponinfo.shields) do
		renderCell(row, scale, shield.health, shield.shield, weaponinfo, gunshield, rowspan)
	end
end

-- Render the helmet damage
local function renderHelmetSection(tbl, damages, res, weaponinfo, helmets, info)
	local skipLvl1    = false
	local skipLvl2    = false
	local hed1Label   = string.format(res.targets.helmet1, damages.scale_hed1)
	local hed2Label   = string.format(res.targets.helmet2, damages.scale_hed2)
	local hed3Label   = string.format(res.targets.helmet3, damages.scale_hed3)
	local hed1Tooltip = string.format(helmets.level1.text, damages.scale_hed0)
	local hed2Tooltip = string.format(helmets.level2.text, damages.scale_hed0)
	local hed3Tooltip = string.format(helmets.level3.text, damages.scale_hed0)
	
	if not helmets.level0.disabled then
		local hed0Rowspan
		if equalPartDamage(damages, 'hed0', 'hed1') then
			if equalPartDamage(damages, 'hed0', 'hed2') then
				if equalPartDamage(damages, 'hed0', 'hed3') then
					hed0Rowspan = 4
				else
					hed0Rowspan = 3
				end
			else
				hed0Rowspan = 2
			end
		else
			hed0Rowspan = 1
		end
		
		local hed0Label = string.format(res.targets.head_format, damages.scale_hed0)
		renderRow2(tbl, hed0Label, nil, damages, 'hed0', weaponinfo, info.level1top, res, hed0Rowspan)
		if hed0Rowspan >= 4 then
			renderHeaderCell(tbl:tag('tr'), hed1Label, hed1Tooltip, info.level2first)
			renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
			renderHeaderCell(tbl:tag('tr'), hed3Label, hed3Tooltip, info.level2)
			return
		elseif hed0Rowspan == 3 then
			renderHeaderCell(tbl:tag('tr'), hed1Label, hed1Tooltip, info.level2first)
			renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
			skipLvl1 = true
			skipLvl2 = true
		elseif hed0Rowspan == 2 then
			renderHeaderCell(tbl:tag('tr'), hed1Label, hed1Tooltip, info.level2first)
			skipLvl1 = true
		end
	end
	
	-- Lvl 1
	if not skipLvl1 then
		local hed1Rowspan
		if equalPartDamage(damages, 'hed1', 'hed2') then
			if equalPartDamage(damages, 'hed1', 'hed3') then
				hed1Rowspan = 3
			else
				hed1Rowspan = 2
			end
		else
			hed1Rowspan = 1
		end
		
		renderRow2(tbl, hed1Label, hed1Tooltip, damages, 'hed1', weaponinfo, info.level2first, res, hed1Rowspan)
		if hed1Rowspan >= 3 then
			renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
			renderHeaderCell(tbl:tag('tr'), hed3Label, hed3Tooltip, info.level2)
			return
		elseif hed1Rowspan == 2 then
			renderHeaderCell(tbl:tag('tr'), hed2Label, hed2Tooltip, info.level2)
			skipLvl2 = true
		end
	end
	
	-- Lvl 2
	if not skipLvl2 then
		local hed2Rowspan
		if equalPartDamage(damages, 'hed2', 'hed3') then
			hed2Rowspan = 2
		else
			hed2Rowspan = 1
		end
		
		renderRow2(tbl, hed2Label, hed2Tooltip, damages, 'hed2', weaponinfo, info.level2, res, hed2Rowspan)
		if hed2Rowspan >= 2 then
			renderHeaderCell(tbl:tag('tr'), hed3Label, hed3Tooltip, info.level2)
			return
		end
	end
	
	-- Lvl 3
	renderRow2(tbl, hed3Label, hed3Tooltip, damages, 'hed3', weaponinfo, info.level2, res, 1)
end

local function renderTable(args)
	local lang = getContentLanguage(args.lang)
	local res = mw.loadData('Module:DamageTable/configuration')[lang]
	local shieldinfo = args.shieldinfo or require('Module:Stat/Shield')[args.shieldpreset]
	local skullpiercer = args.skullpiercer or 1
	local headMul = args.head or 2
	
	local hideNoHelmets = shieldinfo.helmets.level0.disabled
	local minHead
	if hideNoHelmets then
		minHead = shieldinfo.helmets.level1.func(args.headmax)
	else
		minHead = args.headmax
	end
	if shieldinfo.sameHeadshotDamageToNormalOnFortified then
		args.mul2 = shieldinfo.fortified
	end
	local hasSecond = args.mul2 > 0 and args.mul2 ~= 1
	local noLegsShot = args.leg < 0 or args.leg >= 1 or (shieldinfo.legAsBodyOnLowProfile and args.mul > 1)
	local noLegsShot2 = args.leg < 0 or args.leg >= 1 or (shieldinfo.legAsBodyOnLowProfile and args.mul2 > 1)
	
	-- Get/calc min count
	local minCount
	if args.mincount > 0 then
		minCount = args.mincount
	else
		minCount, _ = getShotCount(
			minHead,
			100,
			0,
			{
				damage = math.max(args.damage, args.damagemax),
				mul    = math.max(args.mul, args.mulmax),
				
				opts = {
					ampedCover               = true,
					beamdamage               = math.max(args.beamdamage,  args.beamdamagemax),
					beamticks                = math.max(args.ticks,       args.ticksmax),
					charged                  = math.max(args.charged,     args.chargedmax),
					disruptorRounds          = math.max(args.disruptor,   args.disruptorsup),
					gunshieldBleedthrough    = shieldinfo.gunshieldBleedthrough,
					hammerpointRounds        = math.max(args.hammerpoint, args.hammerpointsup),
					ignorePassiveForHeadshot = shieldinfo.sameHeadshotDamageToNormalOnFortified,
					passthrough              = 1,
					pellets                  = math.max(args.pellet,      args.pelletmax),
					round                    = args.round,
				}
			})
	end
	
	-- Get/calc max count
	local maxCount
	if args.maxcount < 250 then
		maxCount = args.maxcount
	else
		local damagemin = math.min(args.damage, args.damagemin)
		local mininfo = {
			damage = damagemin,
			mul    = math.min(args.mul, args.mulmin),
			
			opts = {
				ampedCover               = false,
				beamdamage               = math.min(args.beamdamage,  args.beamdamagemin),
				beamticks                = math.min(args.ticks,       args.ticksmin),
				charged                  = 1,
				disruptorRounds          = 1,
				gunshieldBleedthrough    = shieldinfo.gunshieldBleedthrough,
				hammerpointRounds        = 1,
				ignorePassiveForHeadshot = shieldinfo.sameHeadshotDamageToNormalOnFortified,
				passthrough              = math.min(args.passthrough, args.passthroughsup),
				pellets                  = math.min(args.pellet, args.pelletmin),
				round                    = args.round,
			}
		}
		local maxshield = shieldinfo.shields[#shieldinfo.shields]
		local legCount, _ = getShotCount(math.min(args.leg, args.legmin), maxshield.health, maxshield.shield, mininfo)
		local bodyWithGunSheild, _ = getShotCount(1, maxshield.health, maxshield.shield, mininfo, shieldinfo.gunshield)
		maxCount = math.max(legCount, bodyWithGunSheild)
	end
	
	local ttkCalculator = getTTKCalculator(args)
	
	-- Define weapon info
	local weaponinfo = {
		damage     = args.damage,
		mul        = args.mul,
		
		minCount   = minCount,
		diffCount  = maxCount - minCount,
		shields    = shieldinfo.shields,
		helmets    = shieldinfo.helmets,
		ttkCalculator = ttkCalculator,
		
		opts = {
			ampedCover               = args.amped,
			beamdamage               = args.beamdamage,
			beamticks                = args.ticks,
			charged                  = args.charged,
			disruptorRounds          = args.disruptor,
			gunshieldBleedthrough    = shieldinfo.gunshieldBleedthrough,
			hammerpointRounds        = args.hammerpoint,
			ignorePassiveForHeadshot = shieldinfo.sameHeadshotDamageToNormalOnFortified,
			passthrough              = args.passthrough,
			pellets                  = args.pellet,
			round                    = args.round,
		},
	}
	
	local colspan, info
	if skullpiercer > 1 then
		colspan = 3
		info = {
			level1 = {
				{ breaktop = false, breakbottom = false, colspan = 3 },
			},
			level1top = {
				{ breaktop = true, breakbottom = true, colspan = 3 },
			},
			level1gunshiled = {
				{ breaktop = false, breakbottom = true, colspan = 3 },
			},
			level2 = {
				{ breaktop = true, breakbottom = false, colspan = 2 },
			},
			level2gunshiled = {
				{ breaktop = true, breakbottom = false },
				{ breaktop = false, breakbottom = false, colspan = 2 },
			},
			level3 = {
				{ breaktop = false, breakbottom = true },
				{ breaktop = false, breakbottom = false, showbottom = true },
			},
			level3first = {
				hideNoHelmets and { breaktop = false, breakbottom = false, rowspan = 6, content = res.targets.head, vertical = res.targets.head_vertical, verticalAlign = res.targets.head_verticalalign } or { breaktop = true,  breakbottom = false, rowspan = 6 },
				{ breaktop = false, breakbottom = true },
				{ breaktop = false, breakbottom = false, showbottom = true },
			},
			level3top = {
				{ breaktop = false, breakbottom = true, colspan = 2 },
				{ breaktop = false, breakbottom = false, showbottom = true },
			},
		}
		info.level2first = info.level2
		
		if hasSecond and not noLegsShot2 then
			info.level1gunshiled = nil
			info.level2gunshiled = {
				{ breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.fortified, vertical = res.targets.fortified_vertical, verticalAlign = res.targets.fortified_verticalalign },
				{ breaktop = false, breakbottom = true, colspan = 2 },
			}
			info.level3gunshiled = {
				{ breaktop = true, breakbottom = false },
				{ breaktop = false, breakbottom = false },
			}
		end
	elseif hasSecond and not noLegsShot2 then
		colspan = 3
		info = {
			level1 = {
				{ breaktop = false, breakbottom = false, colspan = 3 },
			},
			level1top = {
				{ breaktop = false, breakbottom = true, colspan = 3 },
			},
			level2 = {
				{ breaktop = false, breakbottom = false, colspan = 2 },
			},
			level2first = {
				hideNoHelmets and { breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.head, vertical = res.targets.head_vertical, verticalAlign = res.targets.head_verticalalign } or { breaktop = true,  breakbottom = false, rowspan = 3 },
				{ breaktop = false, breakbottom = false, colspan = 2 },
			},
			level2gunshiled = {
				{ breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.fortified, vertical = res.targets.fortified_vertical, verticalAlign = res.targets.fortified_verticalalign },
				{ breaktop = false, breakbottom = true, colspan = 2 },
			},
			level3 = {
				{ breaktop = false, breakbottom = false },
			},
			level3gunshiled = {
				{ breaktop = true, breakbottom = false },
				{ breaktop = false, breakbottom = false },
			},
		}
	else
		colspan = 2
		info = {
			level1 = {
				{ breaktop = false, breakbottom = false, colspan = 2 },
			},
			level1gunshiled = {
				{ breaktop = false, breakbottom = true, colspan = 2 },
			},
			level2 = {
				{ breaktop = false, breakbottom = false },
			},
			level2first = {
				hideNoHelmets and { breaktop = false, breakbottom = false, rowspan = 3, content = res.targets.head, vertical = res.targets.head_vertical, verticalAlign = res.targets.head_verticalalign } or { breaktop = true,  breakbottom = false, rowspan = 3 },
				{ breaktop = false, breakbottom = false },
			},
			level2gunshiled = {
				{ breaktop = true, breakbottom = false },
				{ breaktop = false, breakbottom = false },
			},
		}
		info.level1top = info.level1gunshiled
	end
	
	local table = mw.html.create('table')
		:addClass('wikitable')
		:addClass('numbertable')
		:addClass('damagetable')
	if args.caption ~= nil then
		table:tag('caption')
			:wikitext(args.caption)
	end
	
	local baseDamages = calcBaseDamages(weaponinfo.damage, headMul, args.leg, weaponinfo.helmets, weaponinfo.opts)
	local perkedDamages = applyPerkAndShieldScaleToDamages(baseDamages, weaponinfo.mul, shieldinfo.sameHeadshotDamageToNormalOnFortified, weaponinfo.opts)
	renderHeader(table, args.mul, colspan, shieldinfo.shields, res)
	
	-- Headshot damage
	if headMul > 1 then
		-- w/Skullpiercer Rifling
		if skullpiercer > 1 then
			if not hideNoHelmets then
				renderRow(
					table,
					string.format(res.targets.skullpiercer0, args.skullpiercerrarity, skullpiercer),
					nil,
					skullpiercer, weaponinfo, info.level3top, res)
				
				renderRow2(
					table,
					string.format(res.targets.head_format, perkedDamages.scale_hed0),
					nil,
					perkedDamages,
					'hed0',
					weaponinfo, info.level1top, res)
			end
			
			local skullpiercerLv1Mul = shieldinfo.helmets.level1.func(skullpiercer)
			renderRow(
				table,
				string.format(res.targets.skullpiercer1, args.skullpiercerrarity, skullpiercerLv1Mul),
				string.format(shieldinfo.helmets.level1.text, skullpiercer),
				skullpiercerLv1Mul, weaponinfo, info.level3first, res)
			renderRow2(
				table,
				string.format(res.targets.helmet1, perkedDamages.scale_hed1),
				string.format(shieldinfo.helmets.level1.text, headMul),
				perkedDamages,
				'hed1',
				weaponinfo, info.level2first, res)
			
			local skullpiercerLv2Mul = shieldinfo.helmets.level2.func(skullpiercer)
			renderRow(
				table,
				string.format(res.targets.skullpiercer2, args.skullpiercerrarity, skullpiercerLv2Mul),
				string.format(shieldinfo.helmets.level2.text, skullpiercer),
				skullpiercerLv2Mul, weaponinfo, info.level3, res)
			renderRow2(
				table,
				string.format(res.targets.helmet2, perkedDamages.scale_hed2),
				string.format(shieldinfo.helmets.level2.text, headMul),
				perkedDamages,
				'hed2',
				weaponinfo, info.level2, res)
			
			local skullpiercerLv3Mul = shieldinfo.helmets.level3.func(skullpiercer)
			renderRow(
				table,
				string.format(res.targets.skullpiercer3, args.skullpiercerrarity, skullpiercerLv3Mul),
				string.format(shieldinfo.helmets.level3.text, skullpiercer),
				skullpiercerLv3Mul, weaponinfo, info.level3, res)
			renderRow2(
				table,
				string.format(res.targets.helmet3, perkedDamages.scale_hed3),
				string.format(shieldinfo.helmets.level3.text, headMul),
				perkedDamages,
				'hed3',
				weaponinfo, info.level2, res)
		
		-- w/o Skullpiercer Rifling
		else
			renderHelmetSection(table, perkedDamages, res, weaponinfo, shieldinfo.helmets, info)
		end
	end
	
	-- Body shot damage
	local label
	if headMul <= 1 then
		if noLegsShot then
			label = res.targets.all
		else
			label = res.targets.headbody
		end
	elseif noLegsShot then
		label = res.targets.bodylegs
	else
		label = res.targets.body
	end
		
	if args.mul < 1 or args.forcegunshield then
		renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1gunshiled, res)
		renderRow(
			table,
			res.targets.gunshield,
			shieldinfo.gunshieldLabel,
			1, weaponinfo, info.level2gunshiled, res, shieldinfo.gunshield)
	else
		renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1, res)
	end
	
	-- Legs shot damage
	if not noLegsShot then
		local legsLabel = string.format(res.targets.legs, args.leg)
		renderRow2(table, legsLabel, nil, perkedDamages, 'legs', weaponinfo, info.level1, res)
	end
	
	-- the 2nd place of damages
	if hasSecond then
		local rowinfoSecondTop, rowinfoSecondGunShield, label2
		if not noLegsShot2 then
			rowinfoSecondTop       = info.level2gunshiled
			rowinfoSecondGunShield = info.level3gunshiled
			label2                 = label
		elseif args.mul2 < 1 then
			rowinfoSecondTop       = info.level1gunshiled
			rowinfoSecondGunShield = info.level2gunshiled
			label2                 = string.format(res.targets.fortified_prefix, args.mul2, label)
		else
			rowinfoSecondTop       = info.level1
			rowinfoSecondGunShield = nil
			label2                 = string.format(res.targets.fortified_prefix, args.mul2, label)
		end
		
		local weaponinfo2    = aw.shallowCopy(weaponinfo)
		weaponinfo2.mul      = args.mul2
		local perkedDamages2 = applyPerkAndShieldScaleToDamages(baseDamages, weaponinfo2.mul, shieldinfo.sameHeadshotDamageToNormalOnFortified, weaponinfo2.opts)
		if args.mul2 < 1 then
			renderRow2(table, label2, nil, perkedDamages2, 'body', weaponinfo2, rowinfoSecondTop, res)
			renderRow(
				table,
				res.targets.gunshield,
				shieldinfo.gunshieldLabel,
				1, weaponinfo2, rowinfoSecondGunShield, res, shieldinfo.gunshield)
		else
			renderRow2(table, label2, nil, perkedDamages2, 'body', weaponinfo2, info.level2, res)
		end
	
		-- Legs shot damage
		if not noLegsShot2 then
			local legsLabel = string.format(res.targets.legs, args.leg)
			renderRow2(table, legsLabel, nil, perkedDamages2, 'legs', weaponinfo2, info.level2, res)
		end
	end
	
	return table
end

function p._main(args, frame)
	-- init value
	local initValues = {
		mincount = 0,
		maxcount = 250,
		damage = 20,
		damagemin = 1000,
		damagemax = 0,
		beamdamage = 0,
		beamdamagemin = 0,
		beamdamagemax = 0,
		ticks = 0,
		ticksmin = math.huge,
		ticksmax = 0,
		pellet = 1,
		pelletmin = 0,
		pelletmax = 0,
		head = 2,
		headmax = -1,
		leg = 0.8,
		legmin = 0,
		mul = 1,
		mul2 = 0,
		mulmin = 0.85,
		mulmax = 1,
		charged = 1,
		chargedmax = 1,
		skullpiercer = 1,
		disruptor = 1,
		disruptorsup = 1,
		hammerpoint = 1,
		hammerpointsup = 1,
		passthrough = 1,
		passthroughsup = 1,
		rps = 0,
		rpsratio1 = 0,
		rpsratio2 = 0,
		rpsratio3 = 0,
		raise = 0,
		reload = 0,
		reloadratio1 = 0.963,
		reloadratio2 = 0.933,
		reloadratio3 = 0.9,
		extmag0 = 0,
		extmag1 = 0,
		extmag2 = 0,
		extmag3 = 0,
	}
	
	-- fix arguments
	for key, value in pairs(initValues) do
		args[key] = aw.getAsNumber(args[key], value)
	end
	if args.pelletmin == 0 then
		args.pelletmin = args.pellet
	end
	if args.pelletmax == 0 then
		args.pelletmax = args.pellet
	end
	if args.headmax == -1 then
		args.headmax = math.max(args.head, args.skullpiercer)
	end
	if args.legmin == 0 then
		args.legmin = args.leg
	end
	if args.beamdamagemin == 0 then
		args.beamdamagemin = args.beamdamage
	end
	if args.beamdamagemax == 0 then
		args.beamdamagemax = args.beamdamage
	end
	args.round = aw.getAsBoolean(args.round, false)
	args.amped = aw.getAsBoolean(args.amped, false)
	args.forcegunshield = aw.getAsBoolean(args.forcegunshield, false)
	args.rarity = args.rarity or 'epic'
	args.skullpiercerrarity = args.skullpiercerrarity or 'legendary'
	args.shieldpreset = args.shieldpreset or 'reinforcehelmets'
	
	return tostring(renderTable(args))
end

function p.main(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	args = getArgs(frame)
	
	return p._main(args, frame)
end

return p