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

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

提供:Apex Data
ナビゲーションに移動 検索に移動
(丸め関数を「Module:Utility/Library」に移動した際,接頭辞を忘れているものがあった不具合を修正)
(2次的なダメージ使用時にスカルピアサーライフリングを含めた表が生成できない不具合の修正)
 
(同じ利用者による、間の99版が非表示)
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 = aw.round(info.hammerpoint * damage)
local optsD = aw.shallowCopy(weaponinfo.opts)
end
table.removeKey(optsD, 'disruptorRounds')
local defaultDamage = apex.calcDamage(weaponinfo.damage, part, weaponinfo.mul, optsD)
-- 部位倍率
if weaponinfo.opts.pellets > 1 then
if part ~= 1 then
text = string.format(
damage = aw.round(part * damage)
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 info.mul == 1.05 and not info.amped then
renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield, 1)
damage = aw.selectiveRound(info.mul * damage, info.useRound)
elseif info.mul ~= 1 then
damage = aw.round(info.mul * damage)
end
end
return damage
end
end


local function getDamageForPartAndPassive(damage, part, info)
local function getTTKCalculator(args)
-- 部位倍率
if args.rps <= 0 then
if part ~= 1 then
return nil
damage = aw.round(part * damage)
end
end
-- 小柄・鉄壁
-- 射撃レート
if info.mul == 1.05 and not info.amped then
local firerate
damage = aw.selectiveRound(info.mul * damage, info.useRound)
if args.rpsratio1 > 1 then
elseif info.mul ~= 1 then
if args.rpsratio2 > args.rpsratio1 then
damage = aw.round(info.mul * damage)
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
end
return damage
-- 装填数
end
local magazine
 
if args.extmag0 > 0 then
local function _getShotCount(damage, health, info)
if args.extmag1 > args.extmag0 then
local count = 0
if args.extmag2 > args.extmag1 then
local damages = {}
if args.extmag3 > args.extmag2 then
local damageStack = 0
magazine = { args.extmag0, args.extmag1, args.extmag2, args.extmag3 }
local pelletCount = info.pellet
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
while health > 0 do
-- リロード時間
pelletCount = pelletCount - 1
local reload
health = health - damage
if args.reloadratio2 > 0 and args.reloadratio2 < 1 then
damageStack = damageStack + 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
if pelletCount == 0 then
reload = {
count = count + 1
args.reload,
pelletCount = info.pellet
args.reloadratio1 * args.reload,
table.insert(damages, damageStack)
args.reloadratio2 * args.reload,
damageStack = 0
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
end
else
reload = args.reload
end
end
if damageStack > 0 then
count = count + 1
local ttkc = require('Module:Apex/TTKCalculator').new(firerate, magazine, reload, {
table.insert(damages, damageStack)
raiseTime = args.raise,
end
})
return count, damages
return ttkc
end
end


local function _getShotCountForGunShield(info)
local function getContentLanguage(lang)
local gunshieldinfo = aw.shallowCopy(info)
local language = lang or mw.language.getContentLanguage().code
gunshieldinfo.mul = 1
return language == 'en' and 'en' or 'ja'
gunshieldinfo.hammerpoint = 1
local damage = getDamage(1, gunshieldinfo)
return _getShotCount(damage, 50, gunshieldinfo)
end
end


local function _getShotCountForDefault(part, health, info, gunshield)
-- Calc all base damages
local damage = getDamage(part, info)
local function calcBaseDamages(damage, headScale, legsScale, helmets, opts)
if gunshield then
local headLv1Scale = helmets.level1.func(headScale)
local gunshieldcount, damages = _getShotCountForGunShield(info)
local headLv2Scale = helmets.level2.func(headScale)
local count, damages2 = _getShotCount(damage, health, info)
local headLv3Scale = helmets.level3.func(headScale)
for _, value in ipairs(damages2) do
local ret = {
table.insert(damages, value)
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
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
-- Amped Cover
count, damages = _getShotCountForGunShield(info)
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
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(aw.round(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
else
local text = _arrangeArray(cache, count)
text = string.format(res.damages.disruptor, shieldDamage, unshldDamage)
if output ~= nil then
end
output = output .. separator .. text
elseif shieldDamage < unshldDamage then
else
if weaponinfo.opts.pellets > 1 then
output = text
text = string.format(
end
res.damages.pelletsWithHammerpoint,
cache = damage
shieldDamage * weaponinfo.opts.pellets,
count = 1
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
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)
-- 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
row:tag('th')
if equalPartDamage(damages, 'hed0', 'hed1') then
:attr('data-tooltip', '金バックパック蘇生')
if equalPartDamage(damages, 'hed0', 'hed2') then
:wikitext('<span class="text-rarity text-rarity-legendary">120<small> HP</small></span>')
if equalPartDamage(damages, 'hed0', 'hed3') then
row:tag('th'):wikitext('<span class="text-rarity text-rarity-common">150<small> HP</small></span>')
hed0Rowspan = 4
row:tag('th'):wikitext('<span class="text-rarity text-rarity-rare">175<small> HP</small></span>')
else
row:tag('th'):wikitext('<span class="text-rarity text-rarity-epic">200<small> HP</small></span>')
hed0Rowspan = 3
row:tag('th'):wikitext('<span class="text-rarity text-rarity-heirloom">225<small> HP</small></span>')
end
end
else
 
hed0Rowspan = 2
local function renderCell(row, part, health, shield, info, gunshield)
end
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
else
local ratio = (shotCount - info.minCount) / (info.maxCount - info.minCount)
hed0Rowspan = 1
row:tag('td')
end
: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
else
damage = weaponinfo.damage
minHead = args.headmax
end
end
if shieldinfo.sameHeadshotDamageToNormalOnFortified then
args.mul2 = shieldinfo.fortified
end
local hasSecond = args.mul2 > 0 and args.mul2 ~= 1
local noLegsShot = args.leg < 0 or args.leg >= 1 or (shieldinfo.legAsBodyOnLowProfile and args.mul > 1)
local noLegsShot2 = args.leg < 0 or args.leg >= 1 or (shieldinfo.legAsBodyOnLowProfile and args.mul2 > 1)
-- Get/calc min count
local minCount
if args.mincount > 0 then
minCount = args.mincount
else
else
damage = getDamage(part, weaponinfo)
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
if weaponinfo.pellet > 1 then
row:tag('td'):wikitext((damage * weaponinfo.pellet) .. ' <span style="white-space:nowrap"><small>(' .. damage .. ' × ' .. weaponinfo.pellet .. ')</small></span>') 
-- Get/calc max count
local maxCount
if args.maxcount < 250 then
maxCount = args.maxcount
else
else
row:tag('td'):wikitext(damage)
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,  0, weaponinfo, gunshield)
local ttkCalculator = getTTKCalculator(args)
renderCell(row, part,  70,  50, weaponinfo, gunshield)
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(
-- Define weapon info
math.max(headMul, skullpiercer),
100,
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(args.leg, 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,
minCount = minCount,
shields    = shieldinfo.shields,
maxCount = maxCount,
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,
},
}
}
394行目: 747行目:
},
},
level2 = {
level2 = {
{ breaktop = true, breakbottom = true },
{ breaktop = true, breakbottom = false, colspan = 2 },
{ breaktop = true, breakbottom = false, colspan = 2 },
},
},
402行目: 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 },
},
},
415行目: 812行目:
info = {
info = {
level1 = {
level1 = {
{ breaktop = false, breakbottom = true, colspan = 2 },
{ breaktop = false, breakbottom = false, colspan = 2 },
},
},
level1gunshiled = {
level1gunshiled = {
421行目: 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 },
},
},
429行目: 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
442行目: 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')
447行目: 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 legMul == 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
530行目: 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,
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,
}
}
545行目: 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
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
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.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