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

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

提供:Apex Data
ナビゲーションに移動 検索に移動
(ガンシールド貫通状態の変更に対応)
(2次的なダメージ使用時にスカルピアサーライフリングを含めた表が生成できない不具合の修正)
 
(同じ利用者による、間の44版が非表示)
3行目: 3行目:
local p = {}
local p = {}


local aw = require('Module:Utility/Library')
local table = require('Module:TableExtensions')
local apex = require('Module:Utility/ApexLibrary')
local aw   = require('Module:Utility/Library')
local iu = require('Module:Utility/Image')
local apex = require('Module:Utility/ApexLibrary')
local Color = require('Module:Color')
local STKCalculator = require('Module:Apex/STKCalculator')
local STKCalculator = require('Module:Apex/STKCalculator')
local formatter -- lazily initialized
local getArgs -- lazily initialized
local getArgs -- lazily initialized
local function convertHslToRgb(hue, saturation, lightness)
hue = math.max(0, math.min(1, hue))
saturation = math.max(0, math.min(1, saturation))
lightness = math.max(0, math.min(1, lightness))
if saturation == 0 then
return lightness, lightness, lightness
else
local function to(p, q, t)
if t < 0 then t = t + 1 end
if t > 1 then t = t - 1 end
if t < 1/6 then
return p + (q - p) * 6 * t
elseif t < 3/6 then
return q
elseif t < 4/6 then
return p + (q - p) * (2/3 - t) * 6
else
return p
end
end
local q
if lightness < 0.5 then
q = lightness * (1 + saturation)
else
q = lightness + saturation - lightness * saturation
end
local p = 2 * lightness - q
return to(p, q, hue + 1/3), to(p, q, hue), to(p, q, hue - 1/3)
end
end
local function getColorAsString(hue)
local r, g, b = convertHslToRgb(hue, 0.91, 0.89)
return '#'
.. string.format('%02X', 255 * r)
.. string.format('%02X', 255 * g)
.. string.format('%02X', 255 * b)
end


local function getShotCount(part, health, shield, info, gunshield)
local function getShotCount(part, health, shield, info, gunshield)
local damages = apex.calcShotCount(info.damage, part, info.mul, health, shield, gunshield or 0, {
local damages = apex.calcShotCount(info.damage, part, info.mul, health, shield, gunshield or 0, info.opts)
ampedCover = info.amped,
beamdamage = info.beamdamage,
beamticks  = info.ticks,
hammerpointRounds = info.hammerpoint,
gunshieldBleedthrough = info.bleedthrough,
pellets = info.pellet,
round = info.useRound,
})
return #damages, damages
return #damages, damages
end
end


local function renderHeader(tbl, mul, colspan, shields)
local function renderHeader(tbl, mul, colspan, shields, res)
tbl:tag('tr')
tbl:tag('tr')
:tag('th')
:tag('th')
73行目: 22行目:
:wikitextIf(
:wikitextIf(
mul == 1,
mul == 1,
'部位',
res.targets.caption,
function()
function()
return '部位 (x' .. mul .. ')'
return string.format(res.targets.caption_format, mul)
end)
end)
:done()
:done()
:tag('th')
:tag('th')
:attr('rowspan', 2)
:attr('rowspan', 2)
:wikitext('ダメージ')
:wikitext(res.damages.caption)
:done()
:done()
:tag('th')
:tag('th')
:attr('colspan', #shields)
:attr('colspan', #shields)
:wikitext('確殺数')
:wikitext(res.shotstokill)
local row = tbl:tag('tr')
local row = tbl:tag('tr')
96行目: 45行目:
end
end


local function renderCell(row, part, health, shield, info, gunshield)
local function renderCell(row, part, health, shield, info, gunshield, rowspan)
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
local ratio = (shotCount - info.minCount) / (info.maxCount - info.minCount)
local ratio = (shotCount - info.minCount) / info.diffCount
local tooltip = { aw.stringifyRepeatingArray(damages, '→') }
local tooltip = { aw.stringifyRepeatingArray(damages, '→') }
if info.ttkCalculator then
if info.ttkCalculator then
140行目: 89行目:
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')
row:tag('td')
:attrIf(rowspan > 1, { rowspan = rowspan })
:attr('data-tooltip', table.concat(tooltip))
:attr('data-tooltip', table.concat(tooltip))
:css('background-color', getColorAsString(0.85 * ratio))
:css('background-color', tostring(color))
:wikitext(shotCount)
:wikitext(shotCount)
end
end
150行目: 102行目:
local cellinfo = rowinfo[i]
local cellinfo = rowinfo[i]
local colspan = cellinfo.colspan or 1
local colspan = cellinfo.colspan or 1
local rowspan = cellinfo.rowspan or 1
local cell = row:tag('th')
local cell = row:tag('th')
if colspan > 1 then
if colspan > 1 then
cell:attr('colspan', colspan)
cell:attr('colspan', colspan)
end
if rowspan > 1 then
cell:attr('rowspan', rowspan)
end
end
if cellinfo.breaktop then
if cellinfo.breaktop then
cell:css('border-top', '0 none')
cell:css('border-top', '0 none transparent')
end
end
if cellinfo.breakbottom then
if cellinfo.breakbottom then
cell:css('border-bottom', '0 none')
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
end
if i == #rowinfo then
if i == #rowinfo then
166行目: 131行目:
end
end
cell:wikitext(name)
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
else
cell:wikitext('&nbsp;&nbsp;')
cell:wikitext('&nbsp;&nbsp;')
172行目: 145行目:
end
end


local function renderRow(tbl, name, tooltip, part, weaponinfo, rowinfo, gunshield)
local function renderRow(tbl, name, tooltip, part, weaponinfo, rowinfo, res, gunshield)
local row = tbl:tag('tr')
local row = tbl:tag('tr')
renderHeaderCell(row, name, tooltip, rowinfo)
renderHeaderCell(row, name, tooltip, rowinfo)
local opts = {
ampedCover            = weaponinfo.amped,
hammerpointRounds    = weaponinfo.hammerpoint,
gunshieldBleedthrough = weaponinfo.bleedthrough,
round                = weaponinfo.useRound,
}
local damage
local damage
local opts2 = aw.shallowCopy(weaponinfo.opts)
table.removeKey(opts2, 'beamdamage')
table.removeKey(opts2, 'beamticks')
if gunshield then
if gunshield then
damage = apex.calcDamage(weaponinfo.damage, 1, 1, {
table.removeKey(opts2, 'disruptorRounds')
ampedCover = weaponinfo.amped,
table.removeKey(opts2, 'hammerpointRounds')
round      = weaponinfo.useRound,
damage = apex.calcDamage(weaponinfo.damage, 1, 1, opts2)
})
else
else
damage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, opts)
damage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, opts2)
end
end
local text
local text
if weaponinfo.hammerpoint > 1 then
if not gunshield and weaponinfo.opts.disruptorRounds > 1 then
local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, {
local optsD = aw.shallowCopy(weaponinfo.opts)
ampedCover = weaponinfo.amped,
table.removeKey(optsD, 'disruptorRounds')
round      = weaponinfo.useRound,
local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, optsD)
})
if weaponinfo.opts.pellets > 1 then
if weaponinfo.pellet > 1 then
text = string.format(
text = string.format(
'<small>%s → </small>%s <small><span style="white-space:nowrap">(%s&thinsp;×&thinsp;%s)</span></small>',
res.damages.pelletsWithDisruptor,
defaultDamage * weaponinfo.pellet,
damage * weaponinfo.opts.pellets,
damage * weaponinfo.pellet,
damage,
damage,
weaponinfo.pellet)
weaponinfo.opts.pellets,
defaultDamage * weaponinfo.opts.pellets)
else
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(
text = string.format(
'<small>%s → </small>%s',
res.damages.pelletsWithHammerpoint,
defaultDamage,
defaultDamage * weaponinfo.opts.pellets,
damage)
damage * weaponinfo.opts.pellets,
damage,
weaponinfo.opts.pellets)
else
text = string.format(res.damages.hammerpoint, defaultDamage, damage)
end
end
elseif weaponinfo.pellet > 1 then
elseif weaponinfo.opts.pellets > 1 then
text = string.format(
text = string.format(
'%s <small><span style="white-space:nowrap">(%s&thinsp;×&thinsp;%s)</span></small>',
res.damages.pellets,
damage * weaponinfo.pellet,
damage * weaponinfo.opts.pellets,
damage,
damage,
weaponinfo.pellet)
weaponinfo.opts.pellets)
elseif weaponinfo.ticks > 1 then
elseif weaponinfo.opts.beamticks > 1 then
local beamdamage = apex.calcDamageFromPartAndPassive(weaponinfo.beamdamage, part, weaponinfo.mul, opts)
local beamdamage = apex.calcDamageFromPartAndPassive(weaponinfo.opts.beamdamage, part, weaponinfo.mul, opts2)
local alldamage = beamdamage * weaponinfo.ticks + damage
local alldamage = beamdamage * weaponinfo.opts.beamticks + damage
text = string.format(
text = string.format(
'%s<small> <span style="white-space:nowrap">(%s&thinsp;×&thinsp;%s&thinsp;+&thinsp;%s)</span></small>',
res.damages.ticks,
alldamage,
alldamage,
beamdamage,
beamdamage,
weaponinfo.ticks,
weaponinfo.opts.beamticks,
damage)
damage)
else
else
233行目: 211行目:
for _, shield in ipairs(weaponinfo.shields) do
for _, shield in ipairs(weaponinfo.shields) do
renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield)
renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield, 1)
end
end
end
end
299行目: 277行目:
if args.reloadratio2 > 0 and args.reloadratio2 < 1 then
if args.reloadratio2 > 0 and args.reloadratio2 < 1 then
if args.reloadratio3 > 0 and args.reloadratio3 < args.reloadratio2 then
if args.reloadratio3 > 0 and args.reloadratio3 < args.reloadratio2 then
reload = {
if args.reloadratio1 > 0 and args.reloadratio1 < 1 and args.reloadratio1 > args.reloadratio2 then
args.reload,
reload = {
args.reload,
args.reload,
args.reloadratio2 * args.reload,
args.reloadratio1 * args.reload,
args.reloadratio3 * 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
else
local reload2 = args.reloadratio2 * args.reload
local reload2 = args.reloadratio2 * args.reload
319行目: 306行目:
local ttkc = require('Module:Apex/TTKCalculator').new(firerate, magazine, reload, {
local ttkc = require('Module:Apex/TTKCalculator').new(firerate, magazine, reload, {
raise = args.raise,
raiseTime = args.raise,
})
})
return ttkc
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
end


local function renderTable(args)
local function renderTable(args)
local shieldinfo = require('Module:Stat/Shield')[args.shieldpreset]
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 skullpiercer = args.skullpiercer or 1
local headMul = args.head or 2
local headMul = args.head or 2
local minCount, _ = getShotCount(
local hideNoHelmets = shieldinfo.helmets.level0.disabled
math.max(headMul, skullpiercer),
local minHead
100,
if hideNoHelmets then
0,
minHead = shieldinfo.helmets.level1.func(args.headmax)
{
else
damage = math.max(args.damage, args.damagemax),
minHead = args.headmax
beamdamage = math.max(args.beamdamage, args.beamdamagemax),
end
ticks = args.ticks,
if shieldinfo.sameHeadshotDamageToNormalOnFortified then
mul = math.max(args.mul, args.mulmax),
args.mul2 = shieldinfo.fortified
pellet = math.max(args.pellet, args.pelletmax),
end
amped = true,
local hasSecond = args.mul2 > 0 and args.mul2 ~= 1
hammerpoint = math.max(args.hammerpoint, args.hammerpointsup),
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)
local damagemin = math.min(args.damage, args.damagemin)
local mininfo = {
-- Get/calc min count
damage = damagemin,
local minCount
beamdamage = math.min(args.beamdamage, args.beamdamagemin),
if args.mincount > 0 then
ticks = args.ticks,
minCount = args.mincount
mul = math.min(args.mul, args.mulmin),
else
pellet = math.min(args.pellet, args.pelletmin),
minCount, _ = getShotCount(
amped = false,
minHead,
hammerpoint = 1,
100,
bleedthrough = shieldinfo.gunshieldBleedthrough,
0,
}
{
local maxshield = shieldinfo.shields[#shieldinfo.shields]
damage = math.max(args.damage, args.damagemax),
local legCount, _ = getShotCount(math.min(args.leg, args.legmin), maxshield.health, maxshield.shield, mininfo)
mul    = math.max(args.mul, args.mulmax),
local bodyWithGunSheild, _ = getShotCount(1, maxshield.health, maxshield.shield, mininfo, shieldinfo.gunshield)
local maxCount = math.max(legCount, bodyWithGunSheild)
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)
local ttkCalculator = getTTKCalculator(args)
-- Define weapon info
local weaponinfo = {
local weaponinfo = {
damage = args.damage,
damage     = args.damage,
beamdamage = args.beamdamage,
mul       = args.mul,
ticks = args.ticks,
mul = args.mul,
minCount  = minCount,
pellet = args.pellet,
diffCount  = maxCount - minCount,
amped = args.amped,
shields    = shieldinfo.shields,
hammerpoint = args.hammerpoint,
helmets    = shieldinfo.helmets,
bleedthrough = shieldinfo.gunshieldBleedthrough,
useRound = args.round,
minCount = minCount,
maxCount = maxCount,
shields = shieldinfo.shields,
ttkCalculator = ttkCalculator,
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,
},
}
}
388行目: 747行目:
},
},
level2 = {
level2 = {
{ breaktop = true, breakbottom = true },
{ breaktop = true, breakbottom = false, colspan = 2 },
{ breaktop = true, breakbottom = false, colspan = 2 },
},
},
396行目: 754行目:
},
},
level3 = {
level3 = {
{ breaktop = true, breakbottom = true },
{ breaktop = false, breakbottom = true },
{ 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 },
{ 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 },
},
},
level3top = {
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 },
{ breaktop = false, breakbottom = true, colspan = 2 },
},
level3 = {
{ breaktop = false, breakbottom = false },
},
level3gunshiled = {
{ breaktop = true, breakbottom = false },
{ breaktop = false, breakbottom = false },
{ breaktop = false, breakbottom = false },
},
},
409行目: 812行目:
info = {
info = {
level1 = {
level1 = {
{ breaktop = false, breakbottom = true, colspan = 2 },
{ breaktop = false, breakbottom = false, colspan = 2 },
},
},
level1gunshiled = {
level1gunshiled = {
415行目: 818行目:
},
},
level2 = {
level2 = {
{ breaktop = true, breakbottom = true },
{ 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 },
{ breaktop = false, breakbottom = false },
},
},
423行目: 829行目:
},
},
}
}
info.level1top = info.level1
info.level1top = info.level1gunshiled
end
end
local skullpiercerPrefix = iu.hopup('スカルピアサーライフリング', { rarity = args.skullpiercerrarity })
local table = mw.html.create('table')
local table = mw.html.create('table')
:addClass('wikitable')
:addClass('wikitable')
436行目: 841行目:
end
end
renderHeader(table, args.mul, colspan, shieldinfo.shields)
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)
if skullpiercer > 1 then
-- Headshot damage
renderRow(
if headMul > 1 then
table,
-- w/Skullpiercer Rifling
skullpiercerPrefix .. '(x' .. skullpiercer .. ')',
if skullpiercer > 1 then
nil,
if not hideNoHelmets then
skullpiercer, weaponinfo, info.level3top)
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
end
renderRow(
-- Body shot damage
table,
local label
"頭 (x" .. headMul .. ")",
if headMul <= 1 then
nil,
if noLegsShot then
headMul, weaponinfo, info.level1top)
label = res.targets.all
else
if skullpiercer > 1 then
label = res.targets.headbody
local skullpiercerLv1Mul = shieldinfo.helmets.level1.func(skullpiercer)
end
renderRow(
elseif noLegsShot then
table,
label = res.targets.bodylegs
skullpiercerPrefix .. formatter:common('(x' .. skullpiercerLv1Mul .. ')'),
else
string.format(shieldinfo.helmets.level1.text, skullpiercer),
label = res.targets.body
skullpiercerLv1Mul, weaponinfo, info.level3)
end
end
local hlmLv1Mul = shieldinfo.helmets.level1.func(headMul)
if args.mul < 1 or args.forcegunshield then
renderRow(
renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1gunshiled, res)
table,
formatter:common('Lv.1 (x' .. hlmLv1Mul .. ')'),
string.format(shieldinfo.helmets.level1.text, headMul),
hlmLv1Mul, weaponinfo, info.level2)
if skullpiercer > 1 then
local skullpiercerLv2Mul = shieldinfo.helmets.level2.func(skullpiercer)
renderRow(
renderRow(
table,
table,
skullpiercerPrefix .. formatter:rare('(x' .. skullpiercerLv2Mul .. ')'),
res.targets.gunshield,
string.format(shieldinfo.helmets.level2.text, skullpiercer),
shieldinfo.gunshieldLabel,
skullpiercerLv2Mul, weaponinfo, info.level3)
1, weaponinfo, info.level2gunshiled, res, shieldinfo.gunshield)
else
renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1, res)
end
end
local hlmLv2Mul = shieldinfo.helmets.level2.func(headMul)
-- Legs shot damage
renderRow(
if not noLegsShot then
table,
local legsLabel = string.format(res.targets.legs, args.leg)
formatter:rare('Lv.2 (x' .. hlmLv2Mul .. ')'),
renderRow2(table, legsLabel, nil, perkedDamages, 'legs', weaponinfo, info.level1, res)
string.format(shieldinfo.helmets.level2.text, headMul),
hlmLv2Mul, weaponinfo, info.level2)
if skullpiercer > 1 then
local skullpiercerLv3Mul = shieldinfo.helmets.level3.func(skullpiercer)
renderRow(
table,
skullpiercerPrefix .. formatter:epic('(x' .. skullpiercerLv3Mul .. ')'),
string.format(shieldinfo.helmets.level3.text, skullpiercer),
skullpiercerLv3Mul, weaponinfo, info.level3)
end
end
local hlmLv3Mul = shieldinfo.helmets.level3.func(headMul)
-- the 2nd place of damages
renderRow(
if hasSecond then
table,
local rowinfoSecondTop, rowinfoSecondGunShield, label2
formatter:epic('Lv.3') .. '/' .. formatter:legendary('4') .. ' ' .. formatter:epic('(x' .. hlmLv3Mul .. ')'),
if not noLegsShot2 then
string.format(shieldinfo.helmets.level3.text, headMul),
rowinfoSecondTop      = info.level2gunshiled
hlmLv3Mul, weaponinfo, info.level2)
rowinfoSecondGunShield = info.level3gunshiled
label2                = label
if args.leg == 1 then
elseif args.mul2 < 1 then
if args.mul < 1 or args.forcegunshield then
rowinfoSecondTop      = info.level1gunshiled
renderRow(table, "胴・脚", nil, 1, weaponinfo, 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(
renderRow(
table,
table,
'+ガンシールド',
res.targets.gunshield,
'耐久値 ' .. shieldinfo.gunshield,
shieldinfo.gunshieldLabel,
1, weaponinfo, info.level2gunshiled, shieldinfo.gunshield)
1, weaponinfo2, rowinfoSecondGunShield, res, shieldinfo.gunshield)
else
else
renderRow(table, "胴・脚", nil, 1, weaponinfo, info.level1)
renderRow2(table, label2, nil, perkedDamages2, 'body', weaponinfo2, info.level2, res)
end
end
else
if shieldinfo.legAsBodyOnLowProfile and args.mul == 1.05 then
-- Legs shot damage
renderRow(table, "胴・脚", nil, 1, weaponinfo, info.level1)
if not noLegsShot2 then
else
local legsLabel = string.format(res.targets.legs, args.leg)
if args.mul < 1 or args.forcegunshield then
renderRow2(table, legsLabel, nil, perkedDamages2, 'legs', weaponinfo2, info.level2, res)
renderRow(table, "胴", nil, 1, weaponinfo, info.level1gunshiled)
renderRow(
table,
'+ガンシールド',
'耐久値 ' .. shieldinfo.gunshield,
1, weaponinfo, info.level2gunshiled, shieldinfo.gunshield)
else
renderRow(table, "胴", nil, 1, weaponinfo, info.level1)
end
renderRow(table, "脚 (x" .. args.leg .. ")", nil, args.leg, weaponinfo, info.level1)
end
end
end
end
533行目: 986行目:


function p._main(args, frame)
function p._main(args, frame)
formatter = require('Module:Utility/Formatter').new(frame)
-- init value
-- init value
local initValues = {
local initValues = {
mincount = 0,
maxcount = 250,
damage = 20,
damage = 20,
damagemin = 1000,
damagemin = 1000,
544行目: 997行目:
beamdamagemax = 0,
beamdamagemax = 0,
ticks = 0,
ticks = 0,
ticksmin = math.huge,
ticksmax = 0,
pellet = 1,
pellet = 1,
pelletmin = 0,
pelletmin = 0,
pelletmax = 0,
pelletmax = 0,
head = 2,
head = 2,
headmax = -1,
leg = 0.8,
leg = 0.8,
legmin = 0,
legmin = 0,
mul = 1,
mul = 1,
mul2 = 0,
mulmin = 0.85,
mulmin = 0.85,
mulmax = 1.05,
mulmax = 1,
charged = 1,
chargedmax = 1,
skullpiercer = 1,
skullpiercer = 1,
disruptor = 1,
disruptorsup = 1,
hammerpoint = 1,
hammerpoint = 1,
hammerpointsup = 1,
hammerpointsup = 1,
passthrough = 1,
passthroughsup = 1,
rps = 0,
rps = 0,
rpsratio1 = 0,
rpsratio1 = 0,
562行目: 1,025行目:
raise = 0,
raise = 0,
reload = 0,
reload = 0,
reloadratio2 = 0.95,
reloadratio1 = 0.963,
reloadratio2 = 0.933,
reloadratio3 = 0.9,
reloadratio3 = 0.9,
extmag0 = 0,
extmag0 = 0,
579行目: 1,043行目:
if args.pelletmax == 0 then
if args.pelletmax == 0 then
args.pelletmax = args.pellet
args.pelletmax = args.pellet
end
if args.headmax == -1 then
args.headmax = math.max(args.head, args.skullpiercer)
end
end
if args.legmin == 0 then
if args.legmin == 0 then
592行目: 1,059行目:
args.amped = aw.getAsBoolean(args.amped, false)
args.amped = aw.getAsBoolean(args.amped, false)
args.forcegunshield = aw.getAsBoolean(args.forcegunshield, false)
args.forcegunshield = aw.getAsBoolean(args.forcegunshield, false)
args.rarity = args.rarity or 'epic'
args.skullpiercerrarity = args.skullpiercerrarity or 'legendary'
args.skullpiercerrarity = args.skullpiercerrarity or 'legendary'
args.shieldpreset = args.shieldpreset or 'evoshieldonly'
args.shieldpreset = args.shieldpreset or 'reinforcehelmets'
return tostring(renderTable(args))
return tostring(renderTable(args))

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