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

モジュール:DamageTable

提供:Apex Data
2021年2月11日 (木) 11:48時点におけるMntone (トーク | 投稿記録)による版 (一部の項目でヘルメットのプリセットを採用していなかった問題の修正)
ナビゲーションに移動 検索に移動

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

local p = {}

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

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

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

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

local function getDamageForPartAndPassive(damage, part, info)
	-- 部位倍率
	if part ~= 1 then
		damage = aw.round(part * damage)
	end
	
	-- 小柄・鉄壁
	if info.mul == 1.05 then
		if info.amped then
			damage = aw.roundover(info.mul * damage)
		else
			damage = aw.selectiveRound(info.mul * damage, info.useRound)
		end
	elseif info.mul ~= 1 then
		damage = aw.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 _getShotCountForGunShieldOnly(info, gunshield)
	local gunshieldinfo = aw.shallowCopy(info)
	gunshieldinfo.mul = 1
	gunshieldinfo.hammerpoint = 1
	
	local gunshielddamage = getDamage(1, gunshieldinfo)
		+ info.ticks * getDamageForPartAndPassive(info.beamdamage, 1, gunshieldinfo)
	return _getShotCount(gunshielddamage, gunshield, gunshieldinfo)
end

local function _getShotCountForGunShield(damage, health, info, gunshield)
	local gunshieldcount, damages = _getShotCountForGunShieldOnly(info, gunshield)
	local count, damages2 = _getShotCount(damage, health, info)
	for _, value in ipairs(damages2) do
		table.insert(damages, value)
	end
	return gunshieldcount + count, damages
end

local function _getShotCountForDefault(part, health, info, gunshield)
	local damage = getDamage(part, info)
	if info.ticks > 0 then
		local alldamage = info.ticks * getDamageForPartAndPassive(info.beamdamage, part, info) + damage
		if gunshield > 0 then
			return _getShotCountForGunShield(alldamage, health, info, gunshield)
		else
			return _getShotCount(alldamage, health, info)
		end
	elseif gunshield > 0 then
		return _getShotCountForGunShield(damage, health, info, gunshield)
	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 > 0 then
		count, damages = _getShotCountForGunShieldOnly(info, gunshield)
	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(math.floor(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 0
	
	if info.hammerpoint > 1 then
		local damage
		if info.amped then
			damage = aw.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 renderHeader(table, mul, colspan, shields)
	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', #shields)
		:wikitext('確殺数')
	
	row = table:tag('tr')
	for _, shield in ipairs(shields) do
		local cell = row:tag('th')
		if shield.tooltip ~= nil then
			cell:attr('data-tooltip', shield.tooltip)
		end
		cell:wikitext(shield.label)
	end
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('data-tooltip', aw.stringifyRepeatingArray(damages, '→'))
		:css('background-color', getColorAsString(0.85 * ratio))
		: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 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
			if tooltip ~= nil then
				cell:attr('data-tooltip', tooltip)
			end
			cell:wikitext(name)
		else
			cell:wikitext('&nbsp;&nbsp;')
		end
	end
end

local function renderRow(table, name, tooltip, part, weaponinfo, rowinfo, gunshield)
	local row = table:tag('tr')
	renderHeaderCell(row, name, tooltip, rowinfo)
	
	local damage
	if gunshield then
		if weaponinfo.amped then
			damage = aw.round(1.2 * weaponinfo.damage)
		else
			damage = weaponinfo.damage
		end
	else
		damage = getDamage(part, weaponinfo)
	end
	if weaponinfo.pellet > 1 then
		row:tag('td'):wikitext((damage * weaponinfo.pellet) .. ' <span style="white-space:nowrap"><small>(' .. damage .. ' × ' .. weaponinfo.pellet .. ')</small></span>') 
	elseif weaponinfo.ticks > 1 then
		local beamdamage = getDamageForPartAndPassive(weaponinfo.beamdamage, part, weaponinfo)
		local alldamage = beamdamage * weaponinfo.ticks + damage
		row:tag('td'):wikitext(alldamage .. ' <span style="white-space:nowrap"><small>(' .. beamdamage .. '×' .. weaponinfo.ticks .. ' + ' .. damage .. ')</small></span>') 
	else
		row:tag('td'):wikitext(damage)
	end
	
	for _, shield in ipairs(weaponinfo.shields) do
		renderCell(row, part, shield.health, shield.shield, weaponinfo, gunshield)
	end
end

local function renderTable(args)
	local shieldinfo = require('Module:Stat/Shield')[args.shieldpreset]
	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),
			beamdamage = math.max(args.beamdamage, args.beamdamagemax),
			ticks = args.ticks,
			mul = math.max(args.mul, args.mulmax),
			pellet = args.pellet,
			amped = true,
			hammerpoint = math.max(args.hammerpoint, args.hammerpointsup),
		})
	local damagemin = math.min(args.damage, args.damagemin)
	local mininfo = {
		damage = damagemin,
		beamdamage = math.min(args.beamdamage, args.beamdamagemin),
		ticks = args.ticks,
		mul = math.min(args.mul, args.mulmin),
		pellet = args.pellet,
		amped = false,
		hammerpoint = 1,
	}
	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)
	local maxCount = math.max(legCount, bodyWithGunSheild)
	local weaponinfo = {
		damage = args.damage,
		beamdamage = args.beamdamage,
		ticks = args.ticks,
		mul = args.mul,
		pellet = args.pellet,
		amped = args.amped,
		hammerpoint = args.hammerpoint,
		useRound = args.round,
		minCount = minCount,
		maxCount = maxCount,
		shields = shieldinfo.shields,
	}
	
	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 skullpiercerPrefix = iu.hopup('スカルピアサーライフリング', { rarity = args.skullpiercerrarity })
	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, shieldinfo.shields)
	
	if skullpiercer > 1 then
		renderRow(
			table,
			skullpiercerPrefix .. '(x' .. skullpiercer .. ')',
			nil,
			skullpiercer, weaponinfo, info.level3top)
	end
	
	renderRow(
		table,
		"頭 (x" .. headMul .. ")",
		nil,
		headMul, weaponinfo, info.level1top)
	
	if skullpiercer > 1 then
		local skullpiercerLv1Mul = shieldinfo.helmets.level1.func(skullpiercer)
		renderRow(
			table,
			skullpiercerPrefix .. formatter:common('(x' .. skullpiercerLv1Mul .. ')'),
			string.format(shieldinfo.helmets.level1.text, skullpiercer),
			skullpiercerLv1Mul, weaponinfo, info.level3)
	end
	
	local hlmLv1Mul = shieldinfo.helmets.level1.func(headMul)
	renderRow(
		table,
		formatter:common('Lv.1 (x' .. hlmLv1Mul .. ')'),
		string.format(shieldinfo.helmets.level1.text, headMul),
		hlmLv1Mul, weaponinfo, info.level2)
	
	if skullpiercer > 1 then
		local skullpiercerLv2Mul = shieldinfo.helmets.level2.func(skullpiercer)
		renderRow(
			table,
			skullpiercerPrefix .. formatter:rare('(x' .. skullpiercerLv2Mul .. ')'),
			string.format(shieldinfo.helmets.level2.text, skullpiercer),
			skullpiercerLv2Mul, weaponinfo, info.level3)
	end
	
	local hlmLv2Mul = shieldinfo.helmets.level2.func(headMul)
	renderRow(
		table,
		formatter:rare('Lv.2 (x' .. hlmLv2Mul .. ')'),
		string.format(shieldinfo.helmets.level2.text, headMul),
		hlmLv2Mul, weaponinfo, info.level2)
	
	if skullpiercer > 1 then
		local skullpiercerLv3Mul = shieldinfo.helmets.level3.func(skullpiercer)
		renderRow(
			table,
			skullpiercerPrefix .. formatter:epic('(x' .. skullpiercerLv3Mul .. ')'),
			string.format(shieldinfo.helmets.level3.text, skullpiercer),
			skullpiercerLv3Mul, weaponinfo, info.level3)
	end
	
	local hlmLv3Mul = shieldinfo.helmets.level3.func(headMul)
	renderRow(
		table,
		formatter:epic('Lv.3') .. '/' .. formatter:legendary('4') .. ' ' .. formatter:epic('(x' .. hlmLv3Mul .. ')'),
		string.format(shieldinfo.helmets.level3.text, headMul),
		hlmLv3Mul, weaponinfo, info.level2)
	
	if args.leg == 1 then
		if args.mul < 1 or args.forcegunshield then
			renderRow(table, "胴・脚", nil, 1, weaponinfo, info.level1gunshiled)
			renderRow(
				table,
				'+ガンシールド',
				'耐久値 ' .. shieldinfo.gunshield,
				1, weaponinfo, info.level2gunshiled, shieldinfo.gunshield)
		else
			renderRow(table, "胴・脚", nil, 1, weaponinfo, info.level1)
		end
	else
		if shieldinfo.legAsBodyOnLowProfile and args.mul == 1.05 then
			renderRow(table, "胴・脚", nil, 1, weaponinfo, info.level1)
		else
			if args.mul < 1 or args.forcegunshield then
				renderRow(table, "胴", nil, 1, weaponinfo, info.level1gunshiled)
				renderRow(
					table,
					'+ガンシールド',
					'耐久値 ' .. shieldinfo.gunshield,
					1, weaponinfo, info.level2gunshiled, shieldinfo.gunshield)
			else
				renderRow(table, "胴", nil, 1, weaponinfo, info.level1)
			end
			renderRow(table, "脚 (x" .. args.leg .. ")", nil, args.leg, weaponinfo, info.level1)
		end
	end
	
	return table
end

function p._main(args, frame)
	formatter = require('Module:Utility/Formatter').new(frame)
	
	-- init value
	local initValues = {
		damage = 20,
		damagemin = 1000,
		damagemax = 0,
		beamdamage = 0,
		beamdamagemin = 0,
		beamdamagemax = 0,
		ticks = 0,
		pellet = 1,
		head = 2,
		leg = 0.8,
		legmin = 0,
		mul = 1,
		mulmin = 0.85,
		mulmax = 1.05,
		skullpiercer = 1,
		hammerpoint = 1,
		hammerpointsup = 1,
	}
	
	-- fix arguments
	for key, value in pairs(initValues) do
		args[key] = aw.getAsNumber(args[key], value)
	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.skullpiercerrarity = args.skullpiercerrarity or 'legendary'
	args.shieldpreset = args.shieldpreset or 'evoshieldonly'
	
	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