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

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

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


local aw = require('Module:Utility/Library')
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 convertHslToRgb(hue, saturation, lightness)
local function getShotCount(part, health, shield, info, gunshield)
hue = math.max(0, math.min(1, hue))
local damages = apex.calcShotCount(info.damage, part, info.mul, health, shield, gunshield or 0, info.opts)
saturation = math.max(0, math.min(1, saturation))
return #damages, damages
lightness = math.max(0, math.min(1, lightness))
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)
if saturation == 0 then
local row = tbl:tag('tr')
return lightness, lightness, lightness
for _, shield in ipairs(shields) do
else
row:tag('th')
local function to(p, q, t)
:attrIf(
if t < 0 then t = t + 1 end
shield.tooltip ~= nil,
if t > 1 then t = t - 1 end
{ ['data-tooltip'] = shield.tooltip })
:wikitext(shield.label)
if t < 1/6 then
end
return p + (q - p) * 6 * t
end
elseif t < 3/6 then
 
return q
local function renderCell(row, part, health, shield, info, gunshield, rowspan)
elseif t < 4/6 then
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
return p + (q - p) * (2/3 - t) * 6
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
else
return p
table.insert(tooltip, string.format(template, 'なし', ttk0))
table.insert(tooltip, string.format(template, 'Lv.1', ttk1))
end
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
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 q
local cell = row:tag('th')
if lightness < 0.5 then
if colspan > 1 then
q = lightness * (1 + saturation)
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
else
q = lightness + saturation - lightness * saturation
cell:wikitext('&nbsp;&nbsp;')
end
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
end
end


local function getColorAsString(hue)
local function renderRow(tbl, name, tooltip, part, weaponinfo, rowinfo, res, gunshield)
local r, g, b = convertHslToRgb(hue, 0.91, 0.89)
local row = tbl:tag('tr')
return '#'
renderHeaderCell(row, name, tooltip, rowinfo)
.. string.format('%02X', 255 * r)
.. string.format('%02X', 255 * g)
.. string.format('%02X', 255 * b)
end
 
local function getDamage(part, info)
local damage = info.damage
-- 増幅バリケード
local damage
if info.amped then
local opts2 = aw.shallowCopy(weaponinfo.opts)
damage = aw.round(1.2 * damage)
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
end
-- ハンマーポイント弾
local text
if info.hammerpoint > 1 then
if not gunshield and weaponinfo.opts.disruptorRounds > 1 then
damage = math.floor(info.hammerpoint * damage)
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
end
row:tag('td'):wikitext(text)
-- 部位倍率
for _, shield in ipairs(weaponinfo.shields) do
if part ~= 1 then
renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield, 1)
damage = aw.round(part * damage)
end
end
 
local function getTTKCalculator(args)
if args.rps <= 0 then
return nil
end
end
-- 小柄・鉄壁
-- 射撃レート
if info.mul == 1.05 then
local firerate
if info.amped then
if args.rpsratio1 > 1 then
damage = aw.roundover(info.mul * damage)
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
else
damage = aw.selectiveRound(info.mul * damage, info.useRound)
local rps1 = args.rpsratio1 * args.rps
firerate = {
args.rps,
rps1,
rps1,
rps1,
}
end
end
elseif info.mul ~= 1 then
else
damage = aw.round(info.mul * damage)
firerate = args.rps
end
end
return damage
-- 装填数
end
local magazine
 
if args.extmag0 > 0 then
local function getDamageForPartAndPassive(damage, part, info)
if args.extmag1 > args.extmag0 then
-- 部位倍率
if args.extmag2 > args.extmag1 then
if part ~= 1 then
if args.extmag3 > args.extmag2 then
damage = aw.round(part * damage)
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
end
-- 小柄・鉄壁
-- リロード時間
if info.mul == 1.05 then
local reload
if info.amped then
if args.reloadratio2 > 0 and args.reloadratio2 < 1 then
damage = aw.roundover(info.mul * damage)
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
else
damage = aw.selectiveRound(info.mul * damage, info.useRound)
local reload2 = args.reloadratio2 * args.reload
reload = {
args.reload,
args.reload,
reload2,
reload2
}
end
end
elseif info.mul ~= 1 then
else
damage = aw.round(info.mul * damage)
reload = args.reload
end
end
return damage
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
end


local function _getShotCount(damage, health, info)
-- Calc all base damages
local count = 0
local function calcBaseDamages(damage, headScale, legsScale, helmets, opts)
local damages = {}
local headLv1Scale = helmets.level1.func(headScale)
local damageStack = 0
local headLv2Scale = helmets.level2.func(headScale)
local pelletCount = info.pellet
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,
}
while health > 0 do
if opts.beamdamage > 0 then
pelletCount = pelletCount - 1
ret.dschrg_hed0 = aw.round(headScale    * opts.beamdamage)
health = health - damage
ret.dschrg_hed1 = aw.round(headLv1Scale * opts.beamdamage)
damageStack = damageStack + damage
ret.dschrg_hed2 = aw.round(headLv2Scale * opts.beamdamage)
ret.dschrg_hed3 = aw.round(headLv3Scale * opts.beamdamage)
if pelletCount == 0 then
ret.dschrg_body = opts.beamdamage
count = count + 1
if legsScale < 1 then
pelletCount = info.pellet
ret.dschrg_legs = aw.round(legsScale * opts.beamdamage)
table.insert(damages, damageStack)
else
damageStack = 0
ret.dschrg_legs = opts.beamdamage
end
end
end
end
if damageStack > 0 then
count = count + 1
table.insert(damages, damageStack)
end
return count, damages
end
local function _getShotCountForGunShield(info)
local gunshieldinfo = aw.shallowCopy(info)
gunshieldinfo.mul = 1
gunshieldinfo.hammerpoint = 1
local damage = getDamage(1, gunshieldinfo)
-- Amped Cover
return _getShotCount(damage, 50, gunshieldinfo)
if opts.ampedCover then
end
damage = aw.round(1.2 * damage)
 
local function _getShotCountForDefault(part, health, info, gunshield)
local damage = getDamage(part, info)
if gunshield then
local gunshieldcount, damages = _getShotCountForGunShield(info)
local count, damages2 = _getShotCount(damage, health, info)
for _, value in ipairs(damages2) do
table.insert(damages, value)
end
return gunshieldcount + count, damages
else
return _getShotCount(damage, health, info)
end
end
end
local function _getShotCountForHammerpointRounds(ampedDamage, part, health, shield, info, gunshield)
local count = 0
local damages = {}
local damageStack = 0
local pelletCount = info.pellet
if gunshield then
-- Additional Scale
count, damages = _getShotCountForGunShield(info)
if opts.charged > 1 then
damage = aw.round(opts.charged * damage)
end
end
local shieldDamage = getDamageForPartAndPassive(ampedDamage, part, info)
-- Unshield Scale
while shield >= shieldDamage do
if opts.hammerpointRounds > 1 then
pelletCount = pelletCount - 1
local shieldDamage = damage
shield = shield - shieldDamage
local unshldDamage = apex.calcHammerpointDamage(damage, opts.hammerpointRounds)
damageStack = damageStack + shieldDamage
if pelletCount == 0 then
-- Passthrough
count = count + 1
if opts.passthrough < 1 then
pelletCount = info.pellet
shieldDamage = apex.calcPassthroughDamage(shieldDamage, opts.passthrough)
table.insert(damages, damageStack)
unshldDamage = apex.calcPassthroughDamage(unshldDamage, opts.passthrough)
damageStack = 0
end
end
end
local shieldLegs, unshldLegs
if shield > 0 then
if legsScale < 1 then
local mergedDamage
shieldLegs = aw.round(legsScale * shieldDamage)
if shield < ampedDamage then
unshldLegs = aw.round(legsScale * unshldDamage)
mergedDamage = getDamageForPartAndPassive(shield + math.floor(info.hammerpoint * (ampedDamage - shield)), part, info)
else
else
mergedDamage = shieldDamage
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
end
pelletCount = pelletCount - 1
health = health - (mergedDamage - shield)
damageStack = damageStack + mergedDamage
end
if pelletCount == 0 then
count = count + 1
pelletCount = info.pellet
table.insert(damages, damageStack)
damageStack = 0
end
local healthDamage = getDamageForPartAndPassive(math.floor(info.hammerpoint * ampedDamage), part, info)
while health > 0 do
pelletCount = pelletCount - 1
health = health - healthDamage
damageStack = damageStack + healthDamage
if pelletCount == 0 then
local legs
count = count + 1
if legsScale < 1 then
pelletCount = info.pellet
legs = aw.round(legsScale * damage)
table.insert(damages, damageStack)
else
damageStack = 0
legs = damage
end
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
if pelletCount > 0 and pelletCount < info.pellet then
return ret
count = count + 1
pelletCount = info.pellet
table.insert(damages, damageStack)
end
return count, damages
end
end


local function getShotCount(part, health, shield, info, gunshield)
-- Apply perk/shield scale to damages
gunshield = gunshield or false
local function applyPerkAndShieldScaleToDamages(damages, perkScale, ignorePerkScaleForHS, opts)
if perkScale ~= 1 then
if info.hammerpoint > 1 then
if opts.disruptorRounds > 1 then
local damage
return table.map(damages, function(key, val)
if info.amped then
if ignorePerkScaleForHS and (aw.stringstarts(key, 'shield_hed') or aw.stringstarts(key, 'unshld_hed') or aw.stringstarts(key, 'dschrg_hed')) then
damage = aw.round(1.2 * info.damage)
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
else
damage = info.damage
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
end
return _getShotCountForHammerpointRounds(damage, part, health, shield, info, gunshield)
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
else
return _getShotCountForDefault(part, health + shield, info, gunshield)
return damages
end
end
end
end


local function _arrangeArray(cache, count)
-- Compare damage
if count > 1 then
local function equalPartDamage(damages, part0, part1)
return cache .. '×' .. count
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
else
return cache
return damages['shield_' .. part0] == damages['shield_' .. part1] and damages['unshld_' .. part0] == damages['unshld_' .. part1]
end
end
end
end


local function arrangeArray(array, separator)
-- Render the damage row
local cache = array[1]
local function renderRow2(tbl, name, tooltip, damages, partname, weaponinfo, rowinfo, res, rowspan, gunshield)
local count = 0
rowspan = rowspan or 1
local output = nil
for _, damage in ipairs(array) do
local scale = damages['scale_' .. partname]
if cache == damage then
local shieldDamage = damages['shield_' .. partname]
count = count + 1
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
else
local text = _arrangeArray(cache, count)
text = string.format(res.damages.hammerpoint, shieldDamage, unshldDamage)
if output ~= nil then
end
output = output .. separator .. text
elseif dschrgDamage and weaponinfo.opts.beamticks >= 1 then
else
text = string.format(
output = text
res.damages.ticks,
end
dschrgDamage * weaponinfo.opts.beamticks + shieldDamage,
cache = damage
dschrgDamage,
count = 1
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
end
end
row:tag('td')
:attr('rowspan', rowspan)
:wikitext(text)
local text = _arrangeArray(cache, count)
for _, shield in ipairs(weaponinfo.shields) do
if output ~= nil then
renderCell(row, scale, shield.health, shield.shield, weaponinfo, gunshield, rowspan)
output = output .. separator .. text
else
output = text
end
end
return output
end
end


local function renderHeader(table, mul, colspan, goldbackpack)
-- Render the helmet damage
local row = table:tag('tr')
local function renderHelmetSection(tbl, damages, res, weaponinfo, helmets, info)
local cell = row:tag('th')
local skipLvl1    = false
:attr('colspan', colspan)
local skipLvl2    = false
:attr('rowspan', 2)
local hed1Label  = string.format(res.targets.helmet1, damages.scale_hed1)
if mul == 1 then
local hed2Label  = string.format(res.targets.helmet2, damages.scale_hed2)
cell:wikitext('部位')
local hed3Label  = string.format(res.targets.helmet3, damages.scale_hed3)
else
local hed1Tooltip = string.format(helmets.level1.text, damages.scale_hed0)
cell:wikitext('部位 (x' .. mul .. ')')
local hed2Tooltip = string.format(helmets.level2.text, damages.scale_hed0)
end
local hed3Tooltip = string.format(helmets.level3.text, damages.scale_hed0)
row:tag('th')
:attr('rowspan', 2)
:wikitext('ダメージ')
row:tag('th')
:attr('colspan', 6)
:wikitext('確殺数')
row = table:tag('tr')
if not helmets.level0.disabled then
row:tag('th'):wikitext('100<small> HP</small>')
local hed0Rowspan
if goldbackpack then
if equalPartDamage(damages, 'hed0', 'hed1') then
row:tag('th')
if equalPartDamage(damages, 'hed0', 'hed2') then
:attr('data-tooltip', '金バックパック蘇生')
if equalPartDamage(damages, 'hed0', 'hed3') then
:wikitext('<span class="text-rarity text-rarity-legendary">120<small> HP</small></span>')
hed0Rowspan = 4
end
else
row:tag('th'):wikitext('<span class="text-rarity text-rarity-common">150<small> HP</small></span>')
hed0Rowspan = 3
row:tag('th'):wikitext('<span class="text-rarity text-rarity-rare">175<small> HP</small></span>')
end
row:tag('th'):wikitext('<span class="text-rarity text-rarity-epic">200<small> HP</small></span>')
else
row:tag('th'):wikitext('<span class="text-rarity text-rarity-heirloom">225<small> HP</small></span>')
hed0Rowspan = 2
end
end
 
else
local function renderCell(row, part, health, shield, info, gunshield)
hed0Rowspan = 1
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
end
local ratio = (shotCount - info.minCount) / (info.maxCount - info.minCount)
row:tag('td')
:attr('data-tooltip', arrangeArray(damages, '→'))
:css('background-color', getColorAsString(0.85 * ratio))
:wikitext(shotCount)
end
 
local function renderHeaderCell(row, name, rowinfo)
for i = 1, #rowinfo do
local cellinfo = rowinfo[i]
local colspan = cellinfo.colspan or 1
local cell = row:tag('th')
local hed0Label = string.format(res.targets.head_format, damages.scale_hed0)
if colspan > 1 then
renderRow2(tbl, hed0Label, nil, damages, 'hed0', weaponinfo, info.level1top, res, hed0Rowspan)
cell:attr('colspan', colspan)
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
if cellinfo.breaktop then
end
cell:css('border-top', '0 none')
-- 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
end
if cellinfo.breakbottom then
cell:css('border-bottom', '0 none')
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
if i == #rowinfo then
end
cell:wikitext(name)
-- Lvl 2
if not skipLvl2 then
local hed2Rowspan
if equalPartDamage(damages, 'hed2', 'hed3') then
hed2Rowspan = 2
else
else
cell:wikitext('&nbsp;&nbsp;')
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
end
-- Lvl 3
renderRow2(tbl, hed3Label, hed3Tooltip, damages, 'hed3', weaponinfo, info.level2, res, 1)
end
end


local function renderRow(table, name, part, weaponinfo, rowinfo, gunshield)
local function renderTable(args)
local row = table:tag('tr')
local lang = getContentLanguage(args.lang)
renderHeaderCell(row, name, rowinfo)
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 damage
local hideNoHelmets = shieldinfo.helmets.level0.disabled
if gunshield then
local minHead
if weaponinfo.amped then
if hideNoHelmets then
damage = aw.round(1.2 * weaponinfo.damage)
minHead = shieldinfo.helmets.level1.func(args.headmax)
else
damage = weaponinfo.damage
end
else
else
damage = getDamage(part, weaponinfo)
minHead = args.headmax
end
if shieldinfo.sameHeadshotDamageToNormalOnFortified then
args.mul2 = shieldinfo.fortified
end
end
if weaponinfo.pellet > 1 then
local hasSecond = args.mul2 > 0 and args.mul2 ~= 1
row:tag('td'):wikitext((damage * weaponinfo.pellet) .. ' <span style="white-space:nowrap"><small>(' .. damage .. ' × ' .. weaponinfo.pellet .. ')</small></span>') 
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
else
row:tag('td'):wikitext(damage)
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
end
renderCell(row, part, 100,   0, weaponinfo, gunshield)
-- Get/calc max count
if weaponinfo.goldbackpack then
local maxCount
renderCell(row, part, 70, 50, weaponinfo, gunshield)
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
end
renderCell(row, part, 100,  50, weaponinfo, gunshield)
renderCell(row, part, 100,  75, weaponinfo, gunshield)
renderCell(row, part, 100, 100, weaponinfo, gunshield)
renderCell(row, part, 100, 125, weaponinfo, gunshield)
end
local function renderTable(args, frame)
local skullpiercer = args.skullpiercer or 1
local headMul = args.head or 2
local minCount, _ = getShotCount(
local ttkCalculator = getTTKCalculator(args)
math.max(headMul, skullpiercer),
100,
-- Define weapon info
0,
{
damage = math.max(args.damage, args.damagemax),
mul = 1.05,
pellet = args.pellet,
amped = true,
hammerpoint = math.max(args.hammerpoint, args.hammerpointsup),
})
local damagemin = math.min(args.damage, args.damagemin)
local mininfo = {
damage = damagemin,
mul = 0.85,
pellet = args.pellet,
amped = false,
hammerpoint = 1,
}
local legCount, _ = getShotCount(math.min(args.leg, args.legmin), 100, 125, mininfo)
local bodyWithGunSheild, _ = getShotCount(1, 100, 125, mininfo, true)
local maxCount = math.max(legCount, bodyWithGunSheild)
local weaponinfo = {
local weaponinfo = {
damage = args.damage,
damage     = args.damage,
mul = args.mul,
mul       = args.mul,
pellet = args.pellet,
amped = args.amped,
minCount  = minCount,
hammerpoint = args.hammerpoint,
diffCount  = maxCount - minCount,
useRound = args.round,
shields    = shieldinfo.shields,
minCount = minCount,
helmets    = shieldinfo.helmets,
maxCount = maxCount,
ttkCalculator = ttkCalculator,
goldbackpack = args.goldbackpack,
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,
},
}
}
408行目: 747行目:
},
},
level2 = {
level2 = {
{ breaktop = true, breakbottom = true },
{ breaktop = true, breakbottom = false, colspan = 2 },
{ breaktop = true, breakbottom = false, colspan = 2 },
},
},
416行目: 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 },
},
},
429行目: 812行目:
info = {
info = {
level1 = {
level1 = {
{ breaktop = false, breakbottom = true, colspan = 2 },
{ breaktop = false, breakbottom = false, colspan = 2 },
},
},
level1gunshiled = {
level1gunshiled = {
435行目: 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 },
},
},
443行目: 829行目:
},
},
}
}
info.level1top = info.level1
info.level1top = info.level1gunshiled
end
local skullpiercerHtml
if frame ~= nil then
skullpiercerHtml = frame:expandTemplate { title = 'Hopup', args = { "スカルピアサーライフリング" }} .. ' '
else
skullpiercerHtml = ''
end
end
456行目: 835行目:
:addClass('wikitable')
:addClass('wikitable')
:addClass('numbertable')
:addClass('numbertable')
:addClass('damagetable')
if args.caption ~= nil then
if args.caption ~= nil then
table:tag('caption')
table:tag('caption')
461行目: 841行目:
end
end
renderHeader(table, args.mul, colspan)
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
skullpiercerHtml .. '(x' .. skullpiercer .. ')',
if skullpiercer > 1 then
skullpiercer, weaponinfo, info.level3top)
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
end
renderRow(
-- Body shot damage
table,
local label
"頭 (x" .. headMul .. ")",
if headMul <= 1 then
headMul, weaponinfo, info.level1top)
if noLegsShot then
label = res.targets.all
if skullpiercer > 1 then
else
local skullpiercerLv1Mul = 0.2 + 0.8 * skullpiercer
label = res.targets.headbody
renderRow(
end
table,
elseif noLegsShot then
skullpiercerHtml .. '<span class="text-rarity text-rarity-common">(x' .. skullpiercerLv1Mul .. ')</span>',
label = res.targets.bodylegs
skullpiercerLv1Mul, weaponinfo, info.level3)
else
label = res.targets.body
end
end
local hlmLv1Mul = 0.2 + 0.8 * headMul
if args.mul < 1 or args.forcegunshield then
renderRow(
renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1gunshiled, res)
table,
'<span class="text-rarity text-rarity-common">Lv.1 (x' .. hlmLv1Mul .. ')</span>',
hlmLv1Mul, weaponinfo, info.level2)
if skullpiercer > 1 then
local skullpiercerLv2Mul = 0.4 + 0.6 * skullpiercer
renderRow(
renderRow(
table,
table,
skullpiercerHtml .. '<span class="text-rarity text-rarity-rare">(x' .. skullpiercerLv2Mul .. ')</span>',
res.targets.gunshield,
skullpiercerLv2Mul, weaponinfo, info.level3)
shieldinfo.gunshieldLabel,
1, weaponinfo, info.level2gunshiled, res, shieldinfo.gunshield)
else
renderRow2(table, label, nil, perkedDamages, 'body', weaponinfo, info.level1, res)
end
end
local hlmLv2Mul = 0.4 + 0.6 * headMul
-- Legs shot damage
renderRow(
if not noLegsShot then
table,
local legsLabel = string.format(res.targets.legs, args.leg)
'<span class="text-rarity text-rarity-rare">Lv.2 (x' .. hlmLv2Mul .. ')</span>',
renderRow2(table, legsLabel, nil, perkedDamages, 'legs', weaponinfo, info.level1, res)
hlmLv2Mul, weaponinfo, info.level2)
if skullpiercer > 1 then
local skullpiercerLv3Mul = 0.5 + 0.5 * skullpiercer
renderRow(
table,
skullpiercerHtml .. '<span class="text-rarity text-rarity-epic">(x' .. skullpiercerLv3Mul .. ')</span>',
skullpiercerLv3Mul, weaponinfo, info.level3)
end
end
local hlmLv3Mul = 0.5 + 0.5 * headMul
-- the 2nd place of damages
renderRow(
if hasSecond then
table,
local rowinfoSecondTop, rowinfoSecondGunShield, label2
'<span class="text-rarity text-rarity-epic">Lv.3</span>/<span class="text-rarity text-rarity-legendary">4</span> <span class="text-rarity text-rarity-epic">(x' .. hlmLv3Mul .. ')</span>',
if not noLegsShot2 then
hlmLv3Mul, weaponinfo, info.level2)
rowinfoSecondTop      = info.level2gunshiled
rowinfoSecondGunShield = info.level3gunshiled
if args.leg == 1 then
label2                = label
if args.mul == 0.85 then
elseif args.mul2 < 1 then
renderRow(table, "胴・脚",        1,    weaponinfo, info.level1gunshiled)
rowinfoSecondTop      = info.level1gunshiled
renderRow(table, '+ガンシールド', 1,    weaponinfo, info.level2gunshiled, true)
rowinfoSecondGunShield = info.level2gunshiled
label2                = string.format(res.targets.fortified_prefix, args.mul2, label)
else
else
renderRow(table, "胴・脚", 1, weaponinfo, info.level1)
rowinfoSecondTop      = info.level1
rowinfoSecondGunShield = nil
label2                = string.format(res.targets.fortified_prefix, args.mul2, label)
end
end
else
if args.mul == 1.05 then
local weaponinfo2    = aw.shallowCopy(weaponinfo)
renderRow(table, "胴・脚", 1, weaponinfo, info.level1)
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
else
if args.mul == 0.85 then
renderRow2(table, label2, nil, perkedDamages2, 'body', weaponinfo2, info.level2, res)
renderRow(table, "胴",           1, weaponinfo, info.level1gunshiled)
end
renderRow(table, '+ガンシールド', 1, weaponinfo, info.level2gunshiled, true)
else
-- Legs shot damage
renderRow(table, "胴", 1, weaponinfo, info.level1)
if not noLegsShot2 then
end
local legsLabel = string.format(res.targets.legs, args.leg)
renderRow(table, "脚 (x" .. args.leg .. ")", args.leg, weaponinfo, info.level1)
renderRow2(table, legsLabel, nil, perkedDamages2, 'legs', weaponinfo2, info.level2, res)
end
end
end
end
544行目: 988行目:
-- init value
-- init value
local initValues = {
local initValues = {
mincount = 0,
maxcount = 250,
damage = 20,
damage = 20,
damagemin = 1000,
damagemin = 1000,
damagemax = 0,
damagemax = 0,
beamdamage = 0,
beamdamagemin = 0,
beamdamagemax = 0,
ticks = 0,
ticksmin = math.huge,
ticksmax = 0,
pellet = 1,
pellet = 1,
pelletmin = 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,
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,
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,
}
}
560行目: 1,037行目:
for key, value in pairs(initValues) do
for key, value in pairs(initValues) do
args[key] = aw.getAsNumber(args[key], value)
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
end
if args.legmin == 0 then
if args.legmin == 0 then
args.legmin = args.leg
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
end
args.round = aw.getAsBoolean(args.round, false)
args.round = aw.getAsBoolean(args.round, false)
args.amped = aw.getAsBoolean(args.amped, false)
args.amped = aw.getAsBoolean(args.amped, false)
args.goldbackpack = aw.getAsBoolean(args.goldbackpack, true)
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, frame))
return tostring(renderTable(args))
end
end



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