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

モジュール:DamageTable

提供:Apex Data
2021年2月7日 (日) 01:25時点におけるMntone (トーク | 投稿記録)による版 (ガンシールドを含めた回数に対するチャージライフルのビーム演算の不具合を修正)
ナビゲーションに移動 検索に移動

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

local p = {}

local aw = require('Module:Utility/Library')
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 _getShotCountForGunShield(damage, health, 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)
	local gunshieldcount, damages = _getShotCount(gunshielddamage, gunshield, gunshieldinfo)
	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 = _getShotCountForGunShield(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 _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, 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', arrangeArray(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)
	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 = formatter:hopup('スカルピアサーライフリング', { rariry = args.skullpiercerrariry })
	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 = 0.2 + 0.8 * skullpiercer
		renderRow(
			table,
			skullpiercerPrefix .. formatter:common('(x' .. skullpiercerLv1Mul .. ')'),
			'0.2 + 0.8 × ' .. skullpiercer,
			skullpiercerLv1Mul, weaponinfo, info.level3)
	end
	
	local hlmLv1Mul = 0.2 + 0.8 * headMul
	renderRow(
		table,
		formatter:common('Lv.1 (x' .. hlmLv1Mul .. ')'),
		'0.2 + 0.8 × ' .. headMul,
		hlmLv1Mul, weaponinfo, info.level2)
	
	if skullpiercer > 1 then
		local skullpiercerLv2Mul = 0.4 + 0.6 * skullpiercer
		renderRow(
			table,
			skullpiercerPrefix .. formatter:rare('(x' .. skullpiercerLv2Mul .. ')'),
			'0.4 + 0.6 × ' .. skullpiercer,
			skullpiercerLv2Mul, weaponinfo, info.level3)
	end
	
	local hlmLv2Mul = 0.4 + 0.6 * headMul
	renderRow(
		table,
		formatter:rare('Lv.2 (x' .. hlmLv2Mul .. ')'),
		'0.4 + 0.6 × ' .. headMul,
		hlmLv2Mul, weaponinfo, info.level2)
	
	if skullpiercer > 1 then
		local skullpiercerLv3Mul = 0.5 + 0.5 * skullpiercer
		renderRow(
			table,
			skullpiercerPrefix .. formatter:epic('(x' .. skullpiercerLv3Mul .. ')'),
			'0.5 + 0.5 × ' .. skullpiercer,
			skullpiercerLv3Mul, weaponinfo, info.level3)
	end
	
	local hlmLv3Mul = 0.5 + 0.5 * headMul
	renderRow(
		table,
		formatter:epic('Lv.3') .. '/' .. formatter:legendary('4') .. ' ' .. formatter:epic('(x' .. hlmLv3Mul .. ')'),
		'0.5 + 0.5 × ' .. 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 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.skullpiercerrariry = args.skullpiercerrariry 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