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

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

提供:Apex Data
ナビゲーションに移動 検索に移動
(増幅バリケードを経由する場合の丸め演算が間違っていた問題の修正)
(ハンマーポイント弾の正式な計算を実装)
1行目: 1行目:
local p = {}
local p = {}


local aw = require('Module:Utility/Library')
local getArgs -- lazily initialized
local getArgs -- lazily initialized


local function round(num)
local function round(num)
return math.floor(tonumber(string.format("%.3f", num)) + 0.5)
return math.floor(tonumber(string.format("%.4f", num)) + 0.5)
end
end


local function roundover(num)
local function roundover(num)
return math.ceil(tonumber(string.format("%.3f", num)) - 0.5)
return math.ceil(tonumber(string.format("%.4f", num)) - 0.5)
end
end


63行目: 64行目:
end
end


local function getShotCount(damage, health)
local function getDamage(part, info)
return math.ceil(health / damage)
local damage = info.damage
-- 増幅バリケード
if info.amped then
damage = round(1.2 * damage)
end
-- ハンマーポイント弾
if info.hammerpoint > 1 then
damage = round(info.hammerpoint * damage)
end
-- 部位倍率
if part ~= 1 then
damage = round(part * damage)
end
-- 小柄・鉄壁
if info.mul == 1.05 and not info.amped then
damage = selectiveRound(info.mul * damage, info.useRound)
elseif info.mul ~= 1 then
damage = round(info.mul * damage)
end
return damage
end
 
local function getDamageForPartAndPassive(damage, part, info)
-- 部位倍率
if part ~= 1 then
damage = round(part * damage)
end
-- 小柄・鉄壁
if info.mul == 1.05 and not info.amped then
damage = selectiveRound(info.mul * damage, info.useRound)
elseif info.mul ~= 1 then
damage = round(info.mul * damage)
end
return damage
end
 
local function _getShotCount(damage, health, info)
local count = 0
local damages = {}
local damageStack = 0
local pelletCount = info.pellet
while health > 0 do
pelletCount = pelletCount - 1
health = health - damage
damageStack = damageStack + damage
if pelletCount == 0 then
count = count + 1
pelletCount = info.pellet
table.insert(damages, damageStack)
damageStack = 0
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)
return _getShotCount(damage, 50, gunshieldinfo)
end
 
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
 
local function _getShotCountForHammerpointRounds(ampedDamage, part, health, shield, info, gunshield)
local count = 0
local damages = {}
local damageStack = 0
local pelletCount = info.pellet
if gunshield then
count, damages = _getShotCountForGunShield(info)
end
local shieldDamage = getDamageForPartAndPassive(ampedDamage, part, info)
while shield >= shieldDamage do
pelletCount = pelletCount - 1
shield = shield - shieldDamage
damageStack = damageStack + shieldDamage
if pelletCount == 0 then
count = count + 1
pelletCount = info.pellet
table.insert(damages, damageStack)
damageStack = 0
end
end
if shield > 0 then
local mergedDamage
if shield < ampedDamage then
mergedDamage = getDamageForPartAndPassive(shield + math.floor(info.hammerpoint * (ampedDamage - shield)), part, info)
else
mergedDamage = shieldDamage
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(round(info.hammerpoint * ampedDamage), part, info)
while health > 0 do
pelletCount = pelletCount - 1
health = health - healthDamage
damageStack = damageStack + healthDamage
if pelletCount == 0 then
count = count + 1
pelletCount = info.pellet
table.insert(damages, damageStack)
damageStack = 0
end
end
if pelletCount > 0 and pelletCount < info.pellet then
count = count + 1
pelletCount = info.pellet
table.insert(damages, damageStack)
end
return count, damages
end
 
local function getShotCount(part, health, shield, info, gunshield)
gunshield = gunshield or false
if info.hammerpoint > 1 then
local damage
if info.amped then
damage = round(1.2 * info.damage)
else
damage = info.damage
end
return _getShotCountForHammerpointRounds(damage, part, health, shield, info, gunshield)
else
return _getShotCountForDefault(part, health + shield, info, gunshield)
end
end
 
local function _arrangeArray(cache, count)
if count > 1 then
return cache .. '×' .. count
else
return cache
end
end
 
local function arrangeArray(array, separator)
local cache = array[1]
local count = 0
local output = nil
for _, damage in ipairs(array) do
if cache == damage then
count = count + 1
else
local text = _arrangeArray(cache, count)
if output ~= nil then
output = output .. separator .. text
else
output = text
end
cache = damage
count = 1
end
end
local text = _arrangeArray(cache, count)
if output ~= nil then
output = output .. separator .. text
else
output = text
end
return output
end
end


93行目: 299行目:
end
end


local function renderCell(row, allDamage, health, minCount, maxCount, offset)
local function renderCell(row, part, health, shield, info, gunshield)
local shotCount = getShotCount(allDamage, health)
local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
local ratio = (shotCount + offset - minCount) / (maxCount - minCount)
local ratio = (shotCount - info.minCount) / (info.maxCount - info.minCount)
row:tag('td')
row:tag('td')
:attr('align', 'right')
:attr('align', 'right')
:attr('title', arrangeArray(damages, '→'))
:css('background-color', getColorAsString(0.85 * ratio))
:css('background-color', getColorAsString(0.85 * ratio))
:wikitext(offset + shotCount)
:wikitext(shotCount)
end
 
local function renderRowForSTK(row, damage, pellet, minCount, maxCount, offset)
offset = offset or 0
local allDamage = pellet * damage
renderCell(row, allDamage, 100, minCount, maxCount, offset)
renderCell(row, allDamage, 120, minCount, maxCount, offset)
renderCell(row, allDamage, 150, minCount, maxCount, offset)
renderCell(row, allDamage, 175, minCount, maxCount, offset)
renderCell(row, allDamage, 200, minCount, maxCount, offset)
renderCell(row, allDamage, 225, minCount, maxCount, offset)
end
end


137行目: 332行目:
end
end


local function renderRow(table, name, damage, pellet, minCount, maxCount, rowinfo)
local function renderRow(table, name, part, weaponinfo, rowinfo, gunshield)
local row = table:tag('tr')
local row = table:tag('tr')
renderHeaderCell(row, name, rowinfo)
renderHeaderCell(row, name, rowinfo)
if pellet > 1 then
local damage
row:tag('td'):attr('align', 'right'):wikitext((damage * pellet) .. ' <span style="white-space:nowrap"><small>(' .. damage .. ' × ' .. pellet .. ')</small></span>')   
if gunshield then
if weaponinfo.amped then
damage = round(1.2 * weaponinfo.damage)
else
damage = weaponinfo.damage
end
else
damage = getDamage(part, weaponinfo)
end
if weaponinfo.pellet > 1 then
row:tag('td'):attr('align', 'right'):wikitext((damage * weaponinfo.pellet) .. ' <span style="white-space:nowrap"><small>(' .. damage .. ' × ' .. weaponinfo.pellet .. ')</small></span>')   
else
else
row:tag('td'):attr('align', 'right'):wikitext(damage)
row:tag('td'):attr('align', 'right'):wikitext(damage)
end
end
renderRowForSTK(row, damage, pellet, minCount, maxCount)
end
local function renderRowForGunShiled(table, damage, baseDamage, pellet, minCount, maxCount, rowinfo)
local row = table:tag('tr')
renderHeaderCell(row, '+ガンシールド', rowinfo)
local countForGunShiled = getShotCount(pellet * baseDamage, 50)
renderCell(row, part, 100,  0, weaponinfo, gunshield)
if pellet > 1 then
renderCell(row, part,  70, 50, weaponinfo, gunshield)
row:tag('td'):attr('align', 'right'):wikitext((baseDamage * pellet) .. ' <span style="white-space:nowrap"><small>(' .. baseDamage .. ' × ' .. pellet .. ')</small></span>')  
renderCell(row, part, 100, 50, weaponinfo, gunshield)
else
renderCell(row, part, 100, 75, weaponinfo, gunshield)
row:tag('td'):attr('align', 'right'):wikitext(baseDamage)
renderCell(row, part, 100, 100, weaponinfo, gunshield)
end
renderCell(row, part, 100, 125, weaponinfo, gunshield)
renderRowForSTK(row, damage, pellet, minCount, maxCount, countForGunShiled)
end
 
local function calcDamage(base, mul, part, amped, useRound)
if mul == 1 then
return round(part * base)
elseif mul == 1.05 and not amped then
return selectiveRound(mul * round(part * base), useRound)
else
return round(mul * round(part * base))
end
end
end


local function renderTable(args, frame)
local function renderTable(args, frame)
local hopup = args.hopup or 1
local hopupsup
if hopup == 1 then
hopupsup = args.hopupsup or 1
else
hopupsup = hopup
end
local damage = args.damage or 20
local damagemin = math.min(damage, args.damagemin)
local rawDamage, baseDamage
if args.amped then
rawDamage = round(1.2 * damage)
else
rawDamage = damage
end
baseDamage = round(hopup * rawDamage)
local pellet = args.pellet or 1
local mul = args.mul or 1
local skullpiercer = args.skullpiercer or 1
local skullpiercer = args.skullpiercer or 1
local headMul = args.head or 2
local headMul = args.head or 2
local legMul = args.leg or 0.8
local minCount = getShotCount(pellet * calcDamage(round(hopupsup * round(1.2 * math.max(damage, args.damagemax))), 1.05, math.max(headMul, skullpiercer), true, args.round), 100)
local minCount, _ = getShotCount(
local maxCount = math.max(
math.max(headMul, skullpiercer),
getShotCount(pellet * calcDamage(damagemin, 0.85, legMul, false, args.round), 225),
100,
getShotCount(pellet * damagemin, 50) + getShotCount(pellet * calcDamage(damagemin, 0.85, 1, false, args.round), 225))
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 = {
damage = args.damage,
mul = args.mul,
pellet = args.pellet,
amped = args.amped,
hammerpoint = args.hammerpoint,
minCount = minCount,
maxCount = maxCount,
}
local colspan, info
local colspan, info
if skullpiercer > 1 then
if skullpiercer > 1 then
268行目: 463行目:
end
end
renderHeader(table, mul, colspan)
renderHeader(table, args.mul, colspan)
if skullpiercer > 1 then
if skullpiercer > 1 then
274行目: 469行目:
table,
table,
skullpiercerHtml .. '(x' .. skullpiercer .. ')',
skullpiercerHtml .. '(x' .. skullpiercer .. ')',
calcDamage(baseDamage, mul, skullpiercer, args.amped, args.round),
skullpiercer, weaponinfo, info.level3top)
pellet, minCount, maxCount, info.level3top)
end
end
281行目: 475行目:
table,
table,
"頭 (x" .. headMul .. ")",
"頭 (x" .. headMul .. ")",
calcDamage(baseDamage, mul, headMul, args.amped, args.round),
headMul, weaponinfo, info.level1top)
pellet, minCount, maxCount, info.level1top)
if skullpiercer > 1 then
if skullpiercer > 1 then
289行目: 482行目:
table,
table,
skullpiercerHtml .. '<span class="text-rarity text-rarity-common">(x' .. skullpiercerLv1Mul .. ')</span>',
skullpiercerHtml .. '<span class="text-rarity text-rarity-common">(x' .. skullpiercerLv1Mul .. ')</span>',
calcDamage(baseDamage, mul, skullpiercerLv1Mul, args.amped, args.round),
skullpiercerLv1Mul, weaponinfo, info.level3)
pellet, minCount, maxCount, info.level3)
end
end
297行目: 489行目:
table,
table,
'<span class="text-rarity text-rarity-common">Lv.1 (x' .. hlmLv1Mul .. ')</span>',
'<span class="text-rarity text-rarity-common">Lv.1 (x' .. hlmLv1Mul .. ')</span>',
calcDamage(baseDamage, mul, hlmLv1Mul, args.amped, args.round),
hlmLv1Mul, weaponinfo, info.level2)
pellet, minCount, maxCount, info.level2)
if skullpiercer > 1 then
if skullpiercer > 1 then
305行目: 496行目:
table,
table,
skullpiercerHtml .. '<span class="text-rarity text-rarity-rare">(x' .. skullpiercerLv2Mul .. ')</span>',
skullpiercerHtml .. '<span class="text-rarity text-rarity-rare">(x' .. skullpiercerLv2Mul .. ')</span>',
calcDamage(baseDamage, mul, skullpiercerLv2Mul, args.amped, args.round),
skullpiercerLv2Mul, weaponinfo, info.level3)
pellet, minCount, maxCount, info.level3)
end
end
313行目: 503行目:
table,
table,
'<span class="text-rarity text-rarity-rare">Lv.2 (x' .. hlmLv2Mul .. ')</span>',
'<span class="text-rarity text-rarity-rare">Lv.2 (x' .. hlmLv2Mul .. ')</span>',
calcDamage(baseDamage, mul, hlmLv2Mul, args.amped, args.round),
hlmLv2Mul, weaponinfo, info.level2)
pellet, minCount, maxCount, info.level2)
if skullpiercer > 1 then
if skullpiercer > 1 then
321行目: 510行目:
table,
table,
skullpiercerHtml .. '<span class="text-rarity text-rarity-epic">(x' .. skullpiercerLv3Mul .. ')</span>',
skullpiercerHtml .. '<span class="text-rarity text-rarity-epic">(x' .. skullpiercerLv3Mul .. ')</span>',
calcDamage(baseDamage, mul, skullpiercerLv3Mul, args.amped, args.round),
skullpiercerLv3Mul, weaponinfo, info.level3)
pellet, minCount, maxCount, info.level3)
end
end
329行目: 517行目:
table,
table,
'<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>',
'<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>',
calcDamage(baseDamage, mul, hlmLv3Mul, args.amped, args.round),
hlmLv3Mul, weaponinfo, info.level2)
pellet, minCount, maxCount, info.level2)
local bodyDamage = calcDamage(baseDamage, mul, 1, args.amped, args.round)
if legMul == 1 then
if legMul == 1 then
if mul == 0.85 then
if args.mul == 0.85 then
renderRow(table, "胴・脚", bodyDamage, pellet, minCount, maxCount, info.level1gunshiled)
renderRow(table, "胴・脚",       1,     weaponinfo, info.level1gunshiled)
renderRowForGunShiled(table, bodyDamage, baseDamage, pellet, minCount, maxCount, info.level2gunshiled)
renderRow(table, '+ガンシールド', 1,     weaponinfo, info.level2gunshiled, true)
else
else
renderRow(table, "胴・脚", bodyDamage, pellet, minCount, maxCount, info.level1)
renderRow(table, "胴・脚", 1, weaponinfo, info.level1)
end
end
else
else
if mul == 1.05 then
if args.mul == 1.05 then
renderRow(table, "胴・脚", bodyDamage, pellet, minCount, maxCount, info.level1)
renderRow(table, "胴・脚", 1, weaponinfo, info.level1)
else
else
if mul == 0.85 then
if args.mul == 0.85 then
renderRow(table, "胴", bodyDamage, pellet, minCount, maxCount, info.level1gunshiled)
renderRow(table, "胴",           1, weaponinfo, info.level1gunshiled)
renderRowForGunShiled(table, bodyDamage, rawDamage, pellet, minCount, maxCount, info.level2gunshiled)
renderRow(table, '+ガンシールド', 1, weaponinfo, info.level2gunshiled, true)
else
else
renderRow(table, "胴", bodyDamage, pellet, minCount, maxCount, info.level1)
renderRow(table, "胴", 1, weaponinfo, info.level1)
end
end
renderRow(
renderRow(table, "脚 (x" .. args.leg .. ")", args.leg, weaponinfo, info.level1)
table, "脚 (x" .. legMul .. ")",
calcDamage(baseDamage, mul, legMul, args.amped, args.round),
pellet, minCount, maxCount, info.level1)
end
end
end
end
361行目: 544行目:


function p._main(args, frame)
function p._main(args, frame)
-- init value
local initValues = {
damage = 20,
damagemin = 1000,
damagemax = 0,
pellet = 1,
head = 2,
leg = 0.8,
mul = 1,
skullpiercer = 1,
hammerpoint = 1,
hammerpointsup = 1,
}
-- fix arguments
for key, value in pairs(initValues) do
args[key] = aw.getAsNumber(args[key], value)
end
args.round = aw.getAsBoolean(args.round, false)
args.amped = aw.getAsBoolean(args.amped, false)
return tostring(renderTable(args, frame))
return tostring(renderTable(args, frame))
end
end
369行目: 573行目:
end
end
args = getArgs(frame)
args = getArgs(frame)
-- fix arguments
args.damage = tonumber(args.damage)
args.damagemin = tonumber(args.damagemin)
args.damagemax = tonumber(args.damagemax)
args.pellet = tonumber(args.pellet)
args.head = tonumber(args.head)
args.leg = tonumber(args.leg)
args.mul = tonumber(args.mul)
args.round = args.round == 'true'
args.amped = args.amped == 'true'
args.skullpiercer = tonumber(args.skullpiercer)
args.hopup = tonumber(args.hopup)
args.hopupsup = tonumber(args.hopupsup)
return p._main(args, frame)
return p._main(args, frame)

2021年2月1日 (月) 18:05時点における版

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

local p = {}

local aw = require('Module:Utility/Library')
local getArgs -- lazily initialized

local function round(num)
	return math.floor(tonumber(string.format("%.4f", num)) + 0.5)
end

local function roundover(num)
	return math.ceil(tonumber(string.format("%.4f", num)) - 0.5)
end

local function selectiveRound(num, useRound)
	useRound = useRound or false
	if useRound then
		return round(num)
	else
		return roundover(num)
	end
end

local function convertHslToRgb(hue, saturation, lightness)
	hue = math.max(0, math.min(1, hue))
	saturation = math.max(0, math.min(1, saturation))
	lightness = math.max(0, math.min(1, lightness))
	
	if saturation == 0 then
		return lightness, lightness, lightness
	else
		local function to(p, q, t)
			if t < 0 then t = t + 1 end
			if t > 1 then t = t - 1 end
			
			if t < 1/6 then
				return p + (q - p) * 6 * t
			elseif t < 3/6 then
				return q
			elseif t < 4/6 then
				return p + (q - p) * (2/3 - t) * 6
			else
				return p
			end
		end
		
		local q
		if lightness < 0.5 then
			q = lightness * (1 + saturation)
		else
			q = lightness + saturation - lightness * saturation
		end
		
		local p = 2 * lightness - q
		return to(p, q, hue + 1/3), to(p, q, hue), to(p, q, hue - 1/3)
	end
end

local function getColorAsString(hue)
	local r, g, b = convertHslToRgb(hue, 0.91, 0.89)
	return '#'
		.. string.format('%02X', 255 * r)
		.. string.format('%02X', 255 * g)
		.. string.format('%02X', 255 * b)
end

local function getDamage(part, info)
	local damage = info.damage
	
	-- 増幅バリケード
	if info.amped then
		damage = round(1.2 * damage)
	end
	
	-- ハンマーポイント弾
	if info.hammerpoint > 1 then
		damage = round(info.hammerpoint * damage)
	end
	
	-- 部位倍率
	if part ~= 1 then
		damage = round(part * damage)
	end
	
	-- 小柄・鉄壁
	if info.mul == 1.05 and not info.amped then
		damage = selectiveRound(info.mul * damage, info.useRound)
	elseif info.mul ~= 1 then
		damage = round(info.mul * damage)
	end
	
	return damage
end

local function getDamageForPartAndPassive(damage, part, info)
	-- 部位倍率
	if part ~= 1 then
		damage = round(part * damage)
	end
	
	-- 小柄・鉄壁
	if info.mul == 1.05 and not info.amped then
		damage = selectiveRound(info.mul * damage, info.useRound)
	elseif info.mul ~= 1 then
		damage = round(info.mul * damage)
	end
	
	return damage
end

local function _getShotCount(damage, health, info)
	local count = 0
	local damages = {}
	local damageStack = 0
	local pelletCount = info.pellet
	
	while health > 0 do
		pelletCount = pelletCount - 1
		health = health - damage
		damageStack = damageStack + damage
		
		if pelletCount == 0 then
			count = count + 1
			pelletCount = info.pellet
			table.insert(damages, damageStack)
			damageStack = 0
		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)
	return _getShotCount(damage, 50, gunshieldinfo)
end

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

local function _getShotCountForHammerpointRounds(ampedDamage, part, health, shield, info, gunshield)
	local count = 0
	local damages = {}
	local damageStack = 0
	local pelletCount = info.pellet
	
	if gunshield then
		count, damages = _getShotCountForGunShield(info)
	end
	
	local shieldDamage = getDamageForPartAndPassive(ampedDamage, part, info)
	while shield >= shieldDamage do
		pelletCount = pelletCount - 1
		shield = shield - shieldDamage
		damageStack = damageStack + shieldDamage
		
		if pelletCount == 0 then
			count = count + 1
			pelletCount = info.pellet
			table.insert(damages, damageStack)
			damageStack = 0
		end
	end
	
	if shield > 0 then
		local mergedDamage
		if shield < ampedDamage then
			mergedDamage = getDamageForPartAndPassive(shield + math.floor(info.hammerpoint * (ampedDamage - shield)), part, info)
		else
			mergedDamage = shieldDamage
		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(round(info.hammerpoint * ampedDamage), part, info)
	while health > 0 do
		pelletCount = pelletCount - 1
		health = health - healthDamage
		damageStack = damageStack + healthDamage
		
		if pelletCount == 0 then
			count = count + 1
			pelletCount = info.pellet
			table.insert(damages, damageStack)
			damageStack = 0
		end
	end
	
	if pelletCount > 0 and pelletCount < info.pellet then
		count = count + 1
		pelletCount = info.pellet
		table.insert(damages, damageStack)
	end
	
	return count, damages
end

local function getShotCount(part, health, shield, info, gunshield)
	gunshield = gunshield or false
	
	if info.hammerpoint > 1 then
		local damage
		if info.amped then
			damage = round(1.2 * info.damage)
		else
			damage = info.damage
		end
		return _getShotCountForHammerpointRounds(damage, part, health, shield, info, gunshield)
	else
		return _getShotCountForDefault(part, health + shield, info, gunshield)
	end
end

local function _arrangeArray(cache, count)
	if count > 1 then
		return cache .. '×' .. count
	else
		return cache
	end
end

local function arrangeArray(array, separator)
	local cache = array[1]
	local count = 0
	local output = nil
	for _, damage in ipairs(array) do
		if cache == damage then
			count = count + 1
		else
			local text = _arrangeArray(cache, count)
			if output ~= nil then
				output = output .. separator .. text
			else
				output = text
			end
			cache = damage
			count = 1
		end
	end
	
	local text = _arrangeArray(cache, count)
	if output ~= nil then
		output = output .. separator .. text
	else
		output = text
	end
	return output
end

local function renderHeader(table, mul, colspan)
	local row = table:tag('tr')
	local cell = row:tag('th')
		:attr('colspan', colspan)
		:attr('rowspan', 2)
	if mul == 1 then
		cell:wikitext('部位')
	else
		cell:wikitext('部位 (x' .. mul .. ')')
	end
	row:tag('th')
		:attr('rowspan', 2)
		:wikitext('ダメージ')
	row:tag('th')
		:attr('colspan', 6)
		:wikitext('確殺数')
	
	row = table:tag('tr')
	row:tag('th'):wikitext('100<small> HP</small>')
	row:tag('th'):wikitext('<span class="text-rarity text-rarity-legendary">120<small> HP</small></span>')
	row:tag('th'):wikitext('<span class="text-rarity text-rarity-common">150<small> HP</small></span>')
	row:tag('th'):wikitext('<span class="text-rarity text-rarity-rare">175<small> HP</small></span>')
	row:tag('th'):wikitext('<span class="text-rarity text-rarity-epic">200<small> HP</small></span>')
	row:tag('th'):wikitext('<span class="text-rarity text-rarity-heirloom">225<small> HP</small></span>')
end

local function renderCell(row, part, health, shield, info, gunshield)
	local shotCount, damages = getShotCount(part, health, shield, info, gunshield)
	local ratio = (shotCount - info.minCount) / (info.maxCount - info.minCount)
	row:tag('td')
		:attr('align', 'right')
		:attr('title', 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')
		if colspan > 1 then
			cell:attr('colspan', colspan)
		end
		if cellinfo.breaktop then
			cell:css('border-top', '0 none')
		end
		if cellinfo.breakbottom then
			cell:css('border-bottom', '0 none')
		end
		if i == #rowinfo then
			cell:wikitext(name)
		else
			cell:wikitext('&nbsp;&nbsp;')
		end
	end
end

local function renderRow(table, name, part, weaponinfo, rowinfo, gunshield)
	local row = table:tag('tr')
	renderHeaderCell(row, name, rowinfo)
	
	local damage
	if gunshield then
		if weaponinfo.amped then
			damage = round(1.2 * weaponinfo.damage)
		else
			damage = weaponinfo.damage
		end
	else
		damage = getDamage(part, weaponinfo)
	end
	if weaponinfo.pellet > 1 then
		row:tag('td'):attr('align', 'right'):wikitext((damage * weaponinfo.pellet) .. ' <span style="white-space:nowrap"><small>(' .. damage .. ' × ' .. weaponinfo.pellet .. ')</small></span>')  
	else
		row:tag('td'):attr('align', 'right'):wikitext(damage)
	end
	
	renderCell(row, part, 100,   0, weaponinfo, gunshield)
	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(
		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 = {
		damage = args.damage,
		mul = args.mul,
		pellet = args.pellet,
		amped = args.amped,
		hammerpoint = args.hammerpoint,
		minCount = minCount,
		maxCount = maxCount,
	}
	
	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 = true },
				{ breaktop = true, breakbottom = false, colspan = 2 },
			},
			level2gunshiled = {
				{ breaktop = true, breakbottom = false },
				{ breaktop = false, breakbottom = false, colspan = 2 },
			},
			level3 = {
				{ breaktop = true, breakbottom = true },
				{ breaktop = false, breakbottom = true },
				{ breaktop = false, breakbottom = false },
			},
			level3top = {
				{ breaktop = false, breakbottom = true, colspan = 2 },
				{ breaktop = false, breakbottom = false },
			},
		}
	else
		colspan = 2
		info = {
			level1 = {
				{ breaktop = false, breakbottom = true, colspan = 2 },
			},
			level1gunshiled = {
				{ breaktop = false, breakbottom = true, colspan = 2 },
			},
			level2 = {
				{ breaktop = true, breakbottom = true },
				{ breaktop = false, breakbottom = false },
			},
			level2gunshiled = {
				{ breaktop = true, breakbottom = false },
				{ breaktop = false, breakbottom = false },
			},
		}
		info.level1top = info.level1
	end
	
	local skullpiercerHtml
	if frame ~= nil then
		skullpiercerHtml = frame:expandTemplate { title = 'Hopup', args = { "スカルピアサーライフリング" }} .. ' '
	else
		skullpiercerHtml = ''
	end
	
	local table = mw.html.create('table')
		:addClass('wikitable')
		:addClass('numbertable')
	if args.caption ~= nil then
		table:tag('caption')
			:wikitext(args.caption)
	end
	
	renderHeader(table, args.mul, colspan)
	
	if skullpiercer > 1 then
		renderRow(
			table,
			skullpiercerHtml .. '(x' .. skullpiercer .. ')',
			skullpiercer, weaponinfo, info.level3top)
	end
	
	renderRow(
		table,
		"頭 (x" .. headMul .. ")",
		headMul, weaponinfo, info.level1top)
	
	if skullpiercer > 1 then
		local skullpiercerLv1Mul = 0.2 + 0.8 * skullpiercer
		renderRow(
			table,
			skullpiercerHtml .. '<span class="text-rarity text-rarity-common">(x' .. skullpiercerLv1Mul .. ')</span>',
			skullpiercerLv1Mul, weaponinfo, info.level3)
	end
	
	local hlmLv1Mul = 0.2 + 0.8 * headMul
	renderRow(
		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(
			table,
			skullpiercerHtml .. '<span class="text-rarity text-rarity-rare">(x' .. skullpiercerLv2Mul .. ')</span>',
			skullpiercerLv2Mul, weaponinfo, info.level3)
	end
	
	local hlmLv2Mul = 0.4 + 0.6 * headMul
	renderRow(
		table,
		'<span class="text-rarity text-rarity-rare">Lv.2 (x' .. hlmLv2Mul .. ')</span>',
		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
	
	local hlmLv3Mul = 0.5 + 0.5 * headMul
	renderRow(
		table,
		'<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>',
		hlmLv3Mul, weaponinfo, info.level2)
	
	if legMul == 1 then
		if args.mul == 0.85 then
			renderRow(table, "胴・脚",        1,     weaponinfo, info.level1gunshiled)
			renderRow(table, '+ガンシールド', 1,     weaponinfo, info.level2gunshiled, true)
		else
			renderRow(table, "胴・脚", 1, weaponinfo, info.level1)
		end
	else
		if args.mul == 1.05 then
			renderRow(table, "胴・脚", 1, weaponinfo, info.level1)
		else
			if args.mul == 0.85 then
				renderRow(table, "胴",            1, weaponinfo, info.level1gunshiled)
				renderRow(table, '+ガンシールド', 1, weaponinfo, info.level2gunshiled, true)
			else
				renderRow(table, "胴", 1, weaponinfo, info.level1)
			end
			renderRow(table, "脚 (x" .. args.leg .. ")", args.leg, weaponinfo, info.level1)
		end
	end
	
	return table
end

function p._main(args, frame)
	-- init value
	local initValues = {
		damage = 20,
		damagemin = 1000,
		damagemax = 0,
		pellet = 1,
		head = 2,
		leg = 0.8,
		mul = 1,
		skullpiercer = 1,
		hammerpoint = 1,
		hammerpointsup = 1,
	}
	
	-- fix arguments
	for key, value in pairs(initValues) do
		args[key] = aw.getAsNumber(args[key], value)
	end
	args.round = aw.getAsBoolean(args.round, false)
	args.amped = aw.getAsBoolean(args.amped, false)
	
	return tostring(renderTable(args, frame))
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