| 🌟 | 現在、  鉄壁ヘッドショットには対応済みです。 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 | 
「モジュール:MultipleStatTable」の版間の差分
		
		
		
		
		
		ナビゲーションに移動
		検索に移動
		
				
		
		
	
|  (30-30リピーターの最大チャージダメージに正しく対応できていなかった問題の修正) |  (STKとTTKの計算を分離,ガンシールドのTTK計算に対応) | ||
| 235行目: | 235行目: | ||
| -- [[ キルタイム ]] | -- [[ キルタイム ]] | ||
| local function  | local function getSTKs(stat, opts, health, shield, STKCalculator) | ||
| 	local stkc = STKCalculator.newFromStat(stat, opts) | 	local stkc = STKCalculator.newFromStat(stat, opts) | ||
| 	stkc:calcShotToKill(health, shield, 0, 1) | 	stkc:calcShotToKill(health, shield, 0, 1) | ||
| 245行目: | 245行目: | ||
| 	stkc:calcShotToKill(health, shield, 0, 0.85) | 	stkc:calcShotToKill(health, shield, 0, 0.85) | ||
| 	local stkFortified = stkc:get() | 	local stkFortified = stkc:get() | ||
| 	stkc:reset() | |||
| 	stkc:calcShotToKill(health, shield, 50, 0.85) | |||
| 	local stkFortifiedWithGunShield = stkc:get() | |||
| 	return { | 	return { | ||
| 		stkNormal, | |||
| 		stkLowProfile, | |||
| 		stkFortified, | |||
| 		stkFortifiedWithGunShield, | |||
| 	} | 	} | ||
| end | |||
| local function getTTKs(stat, stks, mode, opts, TTKCalculator) | |||
| 	local ttkc = TTKCalculator.newFromStat(stat, mode, opts) | |||
| 	local ret = {} | |||
| 	for _, stk in ipairs(stks) do | |||
| 		local obj = { | |||
| 			ttkc:getAsLevel(stk, 0), | |||
| 			ttkc:getAsLevel(stk, 1), | |||
| 			ttkc:getAsLevel(stk, 2), | |||
| 			ttkc:getAsLevel(stk, 3), | |||
| 		} | |||
| 		table.insert(ret, obj) | |||
| 	end | |||
| 	return ret | |||
| end | end | ||
| 273行目: | 276行目: | ||
| 		or 0.0001 * aw.round(10000 * ttks[2][a]) ~= 0.0001 * aw.round(10000 * ttks[2][b]) | 		or 0.0001 * aw.round(10000 * ttks[2][a]) ~= 0.0001 * aw.round(10000 * ttks[2][b]) | ||
| 		or 0.0001 * aw.round(10000 * ttks[3][a]) ~= 0.0001 * aw.round(10000 * ttks[3][b]) | 		or 0.0001 * aw.round(10000 * ttks[3][a]) ~= 0.0001 * aw.round(10000 * ttks[3][b]) | ||
| 		or 0.0001 * aw.round(10000 * ttks[4][a]) ~= 0.0001 * aw.round(10000 * ttks[4][b]) | |||
| end | end | ||
| local function compareTTKs(ttks1, ttks2) | local function compareTTKs(ttks1, ttks2) | ||
| 	for passive = 1,  | 	for passive = 1, 4 do | ||
| 		for level = 1, 4 do | 		for level = 1, 4 do | ||
| 			local ttk1 = 0.0001 * aw.round(10000 * ttks1[passive][level]) | 			local ttk1 = 0.0001 * aw.round(10000 * ttks1[passive][level]) | ||
| 289行目: | 293行目: | ||
| local function repackTTKs(ttks, level) | local function repackTTKs(ttks, level) | ||
| 	local ret = {} | |||
| 	for _, ttk in ipairs(ttks) do | |||
| 		table.insert(ret, ttk[level]) | |||
| 	end | |||
| 	return ret | |||
| end | end | ||
| 395行目: | 399行目: | ||
| 	local TTKCalculator = require('Module:Apex/TTKCalculator') | 	local TTKCalculator = require('Module:Apex/TTKCalculator') | ||
| 	local tbl = createStatTable('キルタイム [s]', 4, { '通常', '小柄', '鉄壁' }, function(data, name, stat) | 	local tbl = createStatTable('キルタイム [s]', 4, { '通常', '小柄', '鉄壁', '+ガンシールド' }, function(data, name, stat) | ||
| 		if stat.time == nil or stat.time.reload == nil then | 		if stat.time == nil or stat.time.reload == nil then | ||
| 			return | 			return | ||
| 403行目: | 407行目: | ||
| 		local ammoname = iu.ammo(ammo, { size = 20 }) | 		local ammoname = iu.ammo(ammo, { size = 20 }) | ||
| 		local basename = getWeaponLink(name, ammo, stat.localization['Japanese_Short']) | 		local basename = getWeaponLink(name, ammo, stat.localization['Japanese_Short']) | ||
| 		local stks = getSTKs(stat, opts, args.health, args.shield, STKCalculator) | |||
| 		if stat.mode.burst > 1 then | 		if stat.mode.burst > 1 then | ||
| 			if stat.mode.auto then | 			if stat.mode.auto then | ||
| 				local ttks = getTTKs(stat, TTKCalculator.AUTO, opts | 				local ttks = getTTKs(stat, stks, TTKCalculator.AUTO, opts, TTKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename .. ' (オート)', '', ttks) | 				addFromTTKs(ammo, data, ammoname, basename .. ' (オート)', '', ttks) | ||
| 			else | 			else | ||
| 				local ttks = getTTKs(stat, TTKCalculator.SINGLE, opts | 				local ttks = getTTKs(stat, stks, TTKCalculator.SINGLE, opts, TTKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename .. ' (単発)', '', ttks) | 				addFromTTKs(ammo, data, ammoname, basename .. ' (単発)', '', ttks) | ||
| 			end | 			end | ||
| 			local  | 			local burstttks = getTTKs(stat, stks, TTKCalculator.BURST, opts, TTKCalculator) | ||
| 			addFromTTKs(ammo, data, ammoname, basename .. ' (バースト)', '',  | 			addFromTTKs(ammo, data, ammoname, basename .. ' (バースト)', '', burstttks) | ||
| 		else | 		else | ||
| 			local mode = stat.mode.auto and TTKCalculator.AUTO or TTKCalculator.SINGLE | 			local mode = stat.mode.auto and TTKCalculator.AUTO or TTKCalculator.SINGLE | ||
| 			local ttks = getTTKs(stat, mode, opts | 			local ttks = getTTKs(stat, stks, mode, opts, TTKCalculator) | ||
| 			addFromTTKs(ammo, data, ammoname, basename, '', ttks) | 			addFromTTKs(ammo, data, ammoname, basename, '', ttks) | ||
| 423行目: | 428行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.useModdedLoader = true | 				opts2.useModdedLoader = true | ||
| 				local ttks2 = getTTKs(stat, TTKCalculator.AUTO, opts2 | 				local ttks2 = getTTKs(stat, stks, TTKCalculator.AUTO, opts2, TTKCalculator) | ||
| 				if not compareTTKs(ttks, ttks2) then | 				if not compareTTKs(ttks, ttks2) then | ||
| 					addFromTTKs(ammo, data, ammoname, basename, getModdedLoader(), ttks2) | 					addFromTTKs(ammo, data, ammoname, basename, getModdedLoader(), ttks2) | ||
| 435行目: | 440行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.startMaximum = true | 				opts2.startMaximum = true | ||
| 				ttksmax = getTTKs(stat, TTKCalculator.AUTO, opts2 | 				ttksmax = getTTKs(stat, stks, TTKCalculator.AUTO, opts2, TTKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, maxname, '', ttksmax) | 				addFromTTKs(ammo, data, ammoname, maxname, '', ttksmax) | ||
| 441行目: | 446行目: | ||
| 					local opts3 = aw.shallowCopy(opts2) | 					local opts3 = aw.shallowCopy(opts2) | ||
| 					opts3.useModdedLoader = true | 					opts3.useModdedLoader = true | ||
| 					local ttks3 = getTTKs(stat, TTKCalculator.AUTO, opts3 | 					local ttks3 = getTTKs(stat, stks, TTKCalculator.AUTO, opts3, TTKCalculator) | ||
| 					if not compareTTKs(ttksmax, ttks3) then | 					if not compareTTKs(ttksmax, ttks3) then | ||
| 						addFromTTKs(ammo, data, ammoname, maxname, getModdedLoader(), ttks3) | 						addFromTTKs(ammo, data, ammoname, maxname, getModdedLoader(), ttks3) | ||
| 452行目: | 457行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.useAmpedMode = true | 				opts2.useAmpedMode = true | ||
| 				local  | 				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator) | ||
| 				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE, opts2, TTKCalculator) | |||
| 				if not compareTTKs(ttks, ttks2) then | 				if not compareTTKs(ttks, ttks2) then | ||
| 					addFromTTKs(ammo, data, ammoname, basename, getAmpedMode(), ttks2) | 					addFromTTKs(ammo, data, ammoname, basename, getAmpedMode(), ttks2) | ||
| 462行目: | 468行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.useCharged = true | 				opts2.useCharged = true | ||
| 				local  | 				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator) | ||
| 				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE, opts2, TTKCalculator) | |||
| 				addFromTTKs(ammo, data, ammoname, basename, getCharged(), ttks2) | 				addFromTTKs(ammo, data, ammoname, basename, getCharged(), ttks2) | ||
| 			end | 			end | ||
| 470行目: | 477行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.useHammerpointRounds = true | 				opts2.useHammerpointRounds = true | ||
| 				local  | 				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename, getHammerpointRounds(),  | 				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE, opts2, TTKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename, getHammerpointRounds(), ttks2) | |||
| 			end | 			end | ||
| 478行目: | 486行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.useAnvilReceiver = true | 				opts2.useAnvilReceiver = true | ||
| 				local  | 				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename, getAnvilReceiver(),  | 				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE_ANVIL_RECEIVER, opts2, TTKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename, getAnvilReceiver(), ttks2) | |||
| 			end | 			end | ||
| 487行目: | 496行目: | ||
| 				local opts2 = aw.shallowCopy(opts) | 				local opts2 = aw.shallowCopy(opts) | ||
| 				opts2.useTurbocharger = true | 				opts2.useTurbocharger = true | ||
| 				local ttks2 = getTTKs(stat, TTKCalculator.AUTO, opts2 | 				local ttks2 = getTTKs(stat, stks, TTKCalculator.AUTO, opts2, TTKCalculator) | ||
| 				addFromTTKs(ammo, data, ammoname, basename, getTurbocharger(), ttks2) | 				addFromTTKs(ammo, data, ammoname, basename, getTurbocharger(), ttks2) | ||
| 495行目: | 504行目: | ||
| 					local opts3 = aw.shallowCopy(opts2) | 					local opts3 = aw.shallowCopy(opts2) | ||
| 					opts3.startMaximum = true | 					opts3.startMaximum = true | ||
| 					local ttks3 = getTTKs(stat, TTKCalculator.AUTO, opts3 | 					local ttks3 = getTTKs(stat, stks, TTKCalculator.AUTO, opts3, TTKCalculator) | ||
| 					if ttksmax ~= nil and not compareTTKs(ttksmax, ttks3) then | 					if ttksmax ~= nil and not compareTTKs(ttksmax, ttks3) then | ||
| 						addFromTTKs(ammo, data, ammoname, maxname, getTurbocharger(), ttks3) | 						addFromTTKs(ammo, data, ammoname, maxname, getTurbocharger(), ttks3) | ||
| 503行目: | 512行目: | ||
| 						local opts4 = aw.shallowCopy(opts3) | 						local opts4 = aw.shallowCopy(opts3) | ||
| 						opts4.useModdedLoader = true | 						opts4.useModdedLoader = true | ||
| 						local ttks4 = getTTKs(stat, TTKCalculator.AUTO, opts4 | 						local ttks4 = getTTKs(stat, stks, TTKCalculator.AUTO, opts4, TTKCalculator) | ||
| 						if not compareTTKs(ttks3, ttks4) then | 						if not compareTTKs(ttks3, ttks4) then | ||
| 							addFromTTKs(ammo, data, ammoname, maxname, getTurbocharger() .. '<br>' .. getModdedLoader(), ttks4) | 							addFromTTKs(ammo, data, ammoname, maxname, getTurbocharger() .. '<br>' .. getModdedLoader(), ttks4) | ||
| 514行目: | 523行目: | ||
| 					local opts3 = aw.shallowCopy(opts2) | 					local opts3 = aw.shallowCopy(opts2) | ||
| 					opts3.useModdedLoader = true | 					opts3.useModdedLoader = true | ||
| 					local ttks3 = getTTKs(stat, TTKCalculator.AUTO, opts3 | 					local ttks3 = getTTKs(stat, stks, TTKCalculator.AUTO, opts3, TTKCalculator) | ||
| 					if not compareTTKs(ttks2, ttks3) then | 					if not compareTTKs(ttks2, ttks3) then | ||
| 						addFromTTKs(ammo, data, ammoname, basename, getTurbocharger() .. '<br>' .. getModdedLoader(), ttks3) | 						addFromTTKs(ammo, data, ammoname, basename, getTurbocharger() .. '<br>' .. getModdedLoader(), ttks3) | ||
2021年3月2日 (火) 10:45時点における版
このモジュールについての説明文ページを モジュール:MultipleStatTable/doc に作成できます
require('Module:Utility/mw.html Extensions')
local p = {}
local aw = require('Module:Utility/Library')
local apex = require('Module:Utility/ApexLibrary')
local iu = require('Module:Utility/Image')
local nu = require('Module:Utility/Name')
local formatter -- lazily initialized
local getArgs -- lazily initialized
-- [[ リソース キャッシュ ]]
local cache = {}
local function getModdedLoader()
	if cache.moddedLoader then
		return cache.moddedLoader
	end
	
	cache.moddedLoader = formatter:hopup('改造ローダー') .. ' <span class="text-desktoponly">改造ローダー</span>'
	return cache.moddedLoader
end
local function getAmpedMode()
	if cache.ampedMode then
		return cache.ampedMode
	end
	
	cache.ampedMode = iu.item('シールドセル') .. ' <span class="text-desktoponly">増幅モード</span>'
	return cache.ampedMode
end
local function getHammerpointRounds()
	if cache.hammerpointRounds then
		return cache.hammerpointRounds
	end
	
	cache.hammerpointRounds = iu.hopup('ハンマーポイント弾') .. ' [[ホップアップによる武器一覧#ハンマーポイント弾|' .. formatter:epic('ハンマーポイント弾', 'text-desktoponly') .. ']]'
	return cache.hammerpointRounds
end
local function getAnvilReceiver()
	if cache.anvilReceiver then
		return cache.anvilReceiver
	end
	
	cache.anvilReceiver = iu.hopup('アンビルレシーバー') .. ' [[ホップアップによる武器一覧#アンビルレシーバー|' .. formatter:legendary('アンビルレシーバー', 'text-desktoponly') .. ']]'
	return cache.anvilReceiver
end
local function getTurbocharger()
	if cache.turbocharger then
		return cache.turbocharger
	end
	
	cache.turbocharger = iu.hopup('ターボチャージャー') .. ' [[ホップアップによる武器一覧#ターボチャージャー|' .. formatter:legendary('ターボチャージャー', 'text-desktoponly') .. ']]'
	return cache.turbocharger
end
local function getCharged()
	if cache.charged then
		return cache.charged
	end
	
	cache.charged = iu.scope('スロット') .. ' <span class="text-desktoponly">フルチャージ</span>'
	return cache.charged
end
local function getMagazineLabel(ammo, level, force)
	if not force and level == 0 then
		return ''
	end
	
	if not cache[ammo] then
		cache[ammo] = {}
	end
	if cache[ammo] and cache[ammo][1 + level] then
		return cache[ammo][1 + level]
	end
	
	local footer
	if level == 0 then
		footer = 'なし'
	else
		footer = ' - レベル' .. level
	end
	
	if ammo == 'shotgun' then
		cache[ammo][1 + level] = iu.attachment(nu.bolt('ja'), level) .. ' ' .. formatter:level(level, nu.bolt() .. footer, 'text-desktoponly')
	else
		cache[ammo][1 + level] = iu.attachment(nu.extmag(ammo, 'ja'), level) .. ' ' .. formatter:level(level, nu.extmag(ammo) .. footer, 'text-desktoponly')
	end
	return cache[ammo][1 + level]
end
local function getWeaponLink(name, ammo, shortname)
	local classSuffix = aw.stringstarts(ammo, 'special_') and 'special' or ammo
	local suitableName = string.gsub(
		name,
		'(.*)' .. string.gsub(shortname, '-', '%%-')  .. '(.*)',
		function(prefix, suffix)
			if prefix ~= '' then
				if suffix ~= '' then
					return string.format('<span class="text-desktoponly">%s</span>%s<span class="text-desktoponly">%s</span>', prefix, shortname, suffix)
				else
					return string.format('<span class="text-desktoponly">%s</span>%s', prefix, shortname)
				end
			else
				if suffix ~= '' then
					return string.format('%s<span class="text-desktoponly">%s</span>', shortname, suffix)
				else
					return shortname
				end
			end
		end)
	return string.format('[[%s|<span class="text-ammo-%s">%s</span>]]', name, classSuffix, suitableName)
end
-- [[ テーブル生成 ]]
local function passthrough(num)
	return num
end
local function createStatTable(itemname, slots, headers, fn, numformat, isdesc, zero)
	numformat = numformat or passthrough
	if isdesc == nil then
		isdesc = true
	end
	zero = zero or 0
	
	local stat = mw.loadData('Module:Stat/Weapon')
	
	local data = {}
	for key, value in pairs(stat) do
		fn(data, key, value)
	end
	if isdesc then
		table.sort(data, function(a, b)
			return a.values[1] > b.values[1]
		end)
	else
		table.sort(data, function(a, b)
			return a.values[1] < b.values[1]
		end)
	end
	
	local tbl = mw.html.create('table')
			:addClass('wikitable')
			:addClass('graphtable')
			:addClass('numbertable')
			:addClass('stattable')
			:addClass('sortable')
	local header = tbl:tag('tr')
		:addClass('row-sticky')
		:tag('th')
			:addClass('stattable-cell-header')
			:addClass('stattable-cell-header-ammo')
			:done()
		:tag('th')
			:addClass('stattable-cell-header')
			:addClass('stattable-cell-header-name')
			:attrIf(slots > 2, { colspan = slots - 1 })
			:wikitext('武器名')
			:done()
	for _, item in ipairs(headers) do
		header:tag('th')
			:addClass('graphtable-cell-header')
			:addClass('graphtable-cell-header-both')
			:addClass('stattable-cell-header')
			:addClass('stattable-cell-header-value')
			:wikitext(item)
	end
	
	local rank = 1
	local max = 0
	for i = 1, #data do
		local value = math.max(unpack(data[i].values))
		if value ~= math.huge then
			max = math.max(max, value)
		end
	end
	
	local current = max
	for i, obj in ipairs(data) do
		local row = tbl:tag('tr')
		if slots > 1 then
			for i = 1, slots do
				row:tag('td')
					:addClass('stattable-cell')
					:addClassIf(i == 1, 'stattable-cell-name')
					:addClassIf(i > 1, 'stattable-cell-attachment')
					:addClassIf(i == slots, 'stattable-cell-attachment-last')
					:wikitext(obj[i])
					:attr('align', 'left')
			end
		else
			row:tag('td')
				:addClass('stattable-cell')
				:addClass('stattable-cell-name')
				:wikitext(obj.name)
				:attr('align', 'left')
		end
		
		for _, value in ipairs(obj.values) do
			local percentage = 0.001 * aw.round(100000 * (value - zero) / (1.05 * max - zero))
			local graph = string.format('linear-gradient(to right, #A7D7F9 %s%%, transparent %s%%)', percentage, percentage)
			row:tag('td')
				:addClass('graphtable-cell')
				:addClass('graphtable-cell-both')
				:addClass('stattable-cell')
				:addClass('stattable-cell-value')
				:attr('align', 'right')
				:css('background-image', graph .. ' !important')
				:wikitext(numformat(value))
		end
	end
	return tbl
end
local function add(data, name, values)
	if values == nil or type(values) == 'number' then
		error(string.format('The "%s" value is nil or number', name))
	end
	
	if type(name) == 'table' then
		name.values = values
		table.insert(data, name)
	else
		local obj = {
			name = name,
			values = values,
		}
		table.insert(data, obj)
	end
end
-- [[ キルタイム ]]
local function getSTKs(stat, opts, health, shield, STKCalculator)
	local stkc = STKCalculator.newFromStat(stat, opts)
	stkc:calcShotToKill(health, shield, 0, 1)
	local stkNormal = stkc:get()
	stkc:reset()
	stkc:calcShotToKill(health, shield, 0, 1.05)
	local stkLowProfile = stkc:get()
	stkc:reset()
	stkc:calcShotToKill(health, shield, 0, 0.85)
	local stkFortified = stkc:get()
	stkc:reset()
	stkc:calcShotToKill(health, shield, 50, 0.85)
	local stkFortifiedWithGunShield = stkc:get()
	
	return {
		stkNormal,
		stkLowProfile,
		stkFortified,
		stkFortifiedWithGunShield,
	}
end
local function getTTKs(stat, stks, mode, opts, TTKCalculator)
	local ttkc = TTKCalculator.newFromStat(stat, mode, opts)
	local ret = {}
	for _, stk in ipairs(stks) do
		local obj = {
			ttkc:getAsLevel(stk, 0),
			ttkc:getAsLevel(stk, 1),
			ttkc:getAsLevel(stk, 2),
			ttkc:getAsLevel(stk, 3),
		}
		table.insert(ret, obj)
	end
	return ret
end
local function isDiffTTKs(ttks, a, b)
	return 0.0001 * aw.round(10000 * ttks[1][a]) ~= 0.0001 * aw.round(10000 * ttks[1][b])
		or 0.0001 * aw.round(10000 * ttks[2][a]) ~= 0.0001 * aw.round(10000 * ttks[2][b])
		or 0.0001 * aw.round(10000 * ttks[3][a]) ~= 0.0001 * aw.round(10000 * ttks[3][b])
		or 0.0001 * aw.round(10000 * ttks[4][a]) ~= 0.0001 * aw.round(10000 * ttks[4][b])
end
local function compareTTKs(ttks1, ttks2)
	for passive = 1, 4 do
		for level = 1, 4 do
			local ttk1 = 0.0001 * aw.round(10000 * ttks1[passive][level])
			local ttk2 = 0.0001 * aw.round(10000 * ttks2[passive][level])
			if ttk1 ~= ttk2 then
				return false
			end
		end
	end
	return true
end
local function repackTTKs(ttks, level)
	local ret = {}
	for _, ttk in ipairs(ttks) do
		table.insert(ret, ttk[level])
	end
	return ret
end
local function addFromTTKs(ammo, data, ammoname, basename, hopup, ttks)
	if isDiffTTKs(ttks, 3, 4) then
		add(data, { ammoname, basename, getMagazineLabel(ammo, 3), hopup }, repackTTKs(ttks, 4))
		add(data, { ammoname, basename, getMagazineLabel(ammo, 2), hopup }, repackTTKs(ttks, 3))
		if isDiffTTKs(ttks, 1, 2) then
			add(data, { ammoname, basename, getMagazineLabel(ammo, 1), hopup }, repackTTKs(ttks, 2))
			add(data, { ammoname, basename, getMagazineLabel(ammo, 0, true), hopup }, repackTTKs(ttks, 1))
		else
			add(data, { ammoname, basename, getMagazineLabel(ammo, 0, true) .. '<br>' .. getMagazineLabel(ammo, 1), hopup }, repackTTKs(ttks, 1))
		end
	elseif isDiffTTKs(ttks, 2, 3) then
		add(data, { ammoname, basename, getMagazineLabel(ammo, 2) .. '<br>' .. getMagazineLabel(ammo, 3), hopup }, repackTTKs(ttks, 3))
		if isDiffTTKs(ttks, 1, 2) then
			add(data, { ammoname, basename, getMagazineLabel(ammo, 1), hopup }, repackTTKs(ttks, 2))
			add(data, { ammoname, basename, getMagazineLabel(ammo, 0, true), hopup }, repackTTKs(ttks, 1))
		else
			add(data, { ammoname, basename, getMagazineLabel(ammo, 0, true) .. '<br>' .. getMagazineLabel(ammo, 1), hopup }, repackTTKs(ttks, 1))
		end
	elseif isDiffTTKs(ttks, 1, 2) then
		add(data, { ammoname, basename, getMagazineLabel(ammo, 1) .. '<br>' .. getMagazineLabel(ammo, 2) .. '<br>' .. getMagazineLabel(ammo, 3), hopup }, repackTTKs(ttks, 2))
		add(data, { ammoname, basename, getMagazineLabel(ammo, 0, true), hopup }, repackTTKs(ttks, 1))
	else
		add(data, { ammoname, basename, '', hopup }, repackTTKs(ttks, 1))
	end
end
local function parsePattern(str)
	local type = type(str)
	if type == 'table' then
		return str
	elseif str == nil or type ~= 'string' then
		return nil
	end
	
	local pattern = {}
	local head = false
	for i = 1, #str do
		local c = string.sub(str, i, i)
		local cont = false
		if head then
			head = false
			if c == '1' then
				table.insert(pattern, apex.HEAD_HLMLV1)
				cont = true
			elseif c == '2' then
				table.insert(pattern, apex.HEAD_HLMLV2)
				cont = true
			elseif c == '3' then
				table.insert(pattern, apex.HEAD_HLMLV3)
				cont = true
			elseif c == '0' then
				table.insert(pattern, apex.HEAD_NOHLM)
				cont = true
			else
				table.insert(pattern, apex.HEAD_NOHLM)
			end
		end
		
		if not cont then
			if c == 'H' then
				head = true
			elseif c == 'L' then
				table.insert(pattern, apex.LEGS)
			elseif c == 'S' then
				table.insert(pattern, apex.SKY)
			elseif c == ',' or c == ' ' then
				if head then
					head = false
					table.insert(pattern, apex.HEAD_NOHLM)
				end
			else
				table.insert(pattern, apex.BODY)
			end
		end
	end
	if head then
		table.insert(pattern, apex.HEAD_NOHLM)
	end
	return pattern
end
function p.bytimetokill(frame, args)
	formatter = require('Module:Utility/Formatter').new(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	args = args or getArgs(frame)
	args.health = aw.getAsNumber(args.health, 100)
	args.shield = aw.getAsNumber(args.shield, 125)
	
	local opts = {
		pattern = {
			first = parsePattern(args.firstPattern),
			loop  = parsePattern(args.loopPattern),
		},
	}
	local STKCalculator = require('Module:Apex/STKCalculator')
	local TTKCalculator = require('Module:Apex/TTKCalculator')
	
	local tbl = createStatTable('キルタイム [s]', 4, { '通常', '小柄', '鉄壁', '+ガンシールド' }, function(data, name, stat)
		if stat.time == nil or stat.time.reload == nil then
			return
		end
		
		local ammo = stat.ammo
		local ammoname = iu.ammo(ammo, { size = 20 })
		local basename = getWeaponLink(name, ammo, stat.localization['Japanese_Short'])
		local stks = getSTKs(stat, opts, args.health, args.shield, STKCalculator)
		if stat.mode.burst > 1 then
			if stat.mode.auto then
				local ttks = getTTKs(stat, stks, TTKCalculator.AUTO, opts, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, basename .. ' (オート)', '', ttks)
			else
				local ttks = getTTKs(stat, stks, TTKCalculator.SINGLE, opts, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, basename .. ' (単発)', '', ttks)
			end
			
			local burstttks = getTTKs(stat, stks, TTKCalculator.BURST, opts, TTKCalculator)
			addFromTTKs(ammo, data, ammoname, basename .. ' (バースト)', '', burstttks)
		else
			local mode = stat.mode.auto and TTKCalculator.AUTO or TTKCalculator.SINGLE
			local ttks = getTTKs(stat, stks, mode, opts, TTKCalculator)
			addFromTTKs(ammo, data, ammoname, basename, '', ttks)
			
			-- ライトマシンガン (ディヴォーションLMG・M600スピットファイア・L-スターEMG)
			if stat.category == 'light_machine_gun' then
				local opts2 = aw.shallowCopy(opts)
				opts2.useModdedLoader = true
				local ttks2 = getTTKs(stat, stks, TTKCalculator.AUTO, opts2, TTKCalculator)
				if not compareTTKs(ttks, ttks2) then
					addFromTTKs(ammo, data, ammoname, basename, getModdedLoader(), ttks2)
				end
			end
			
			-- 最高速から
			local ttksmax = nil
			if aw.isNumberAndGreaterThanOrEqualToZero(stat.firerate.auto_start) and aw.isNumber(stat.firerate.auto_maximum) and stat.firerate.auto_maximum > 1 then
				local maxname = basename .. ' (最高速)'
				local opts2 = aw.shallowCopy(opts)
				opts2.startMaximum = true
				ttksmax = getTTKs(stat, stks, TTKCalculator.AUTO, opts2, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, maxname, '', ttksmax)
				
				if stat.category == 'light_machine_gun' then
					local opts3 = aw.shallowCopy(opts2)
					opts3.useModdedLoader = true
					local ttks3 = getTTKs(stat, stks, TTKCalculator.AUTO, opts3, TTKCalculator)
					if not compareTTKs(ttksmax, ttks3) then
						addFromTTKs(ammo, data, ammoname, maxname, getModdedLoader(), ttks3)
					end
				end
			end
			
			-- センチネルの増幅モード
			if aw.isNumberAndGreaterThanZero(stat.damage.amped) then
				local opts2 = aw.shallowCopy(opts)
				opts2.useAmpedMode = true
				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator)
				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE, opts2, TTKCalculator)
				if not compareTTKs(ttks, ttks2) then
					addFromTTKs(ammo, data, ammoname, basename, getAmpedMode(), ttks2)
				end
			end
			
			-- 30-30リピーターのフルチャージ
			if aw.isNumberAndGreaterThanZero(stat.damage.charged) then
				local opts2 = aw.shallowCopy(opts)
				opts2.useCharged = true
				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator)
				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE, opts2, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, basename, getCharged(), ttks2)
			end
			
			-- ハンマーポイント弾 (モザンビーク・P2020)
			if aw.isNumber(stat.damage.hammerpoint_rounds) and stat.damage.hammerpoint_rounds > 1 then
				local opts2 = aw.shallowCopy(opts)
				opts2.useHammerpointRounds = true
				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator)
				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE, opts2, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, basename, getHammerpointRounds(), ttks2)
			end
			
			-- アンビルレシーバー (VK-47フラットライン・R-301カービン)
			if stat.damage.anvil_receiver then
				local opts2 = aw.shallowCopy(opts)
				opts2.useAnvilReceiver = true
				local stks2 = getSTKs(stat, opts2, args.health, args.shield, STKCalculator)
				local ttks2 = getTTKs(stat, stks2, TTKCalculator.SINGLE_ANVIL_RECEIVER, opts2, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, basename, getAnvilReceiver(), ttks2)
			end
			
			-- ターボチャージャー (ハボックライフル・ディヴォーションLMG)
			if aw.isNumberAndGreaterThanOrEqualToZero(stat.firerate.auto_raise_turbocharger)
			or aw.isNumberAndGreaterThanOrEqualToZero(stat.firerate.auto_start_turbocharger) then
				local opts2 = aw.shallowCopy(opts)
				opts2.useTurbocharger = true
				local ttks2 = getTTKs(stat, stks, TTKCalculator.AUTO, opts2, TTKCalculator)
				addFromTTKs(ammo, data, ammoname, basename, getTurbocharger(), ttks2)
				
				-- 最高速から
				if aw.isNumberAndGreaterThanOrEqualToZero(stat.firerate.auto_start_turbocharger) and aw.isNumber(stat.firerate.auto_maximum_turbocharger) and stat.firerate.auto_maximum_turbocharger > 1 then
					local maxname = basename .. ' (最高速)'
					local opts3 = aw.shallowCopy(opts2)
					opts3.startMaximum = true
					local ttks3 = getTTKs(stat, stks, TTKCalculator.AUTO, opts3, TTKCalculator)
					if ttksmax ~= nil and not compareTTKs(ttksmax, ttks3) then
						addFromTTKs(ammo, data, ammoname, maxname, getTurbocharger(), ttks3)
					end
					
					if stat.category == 'light_machine_gun' then
						local opts4 = aw.shallowCopy(opts3)
						opts4.useModdedLoader = true
						local ttks4 = getTTKs(stat, stks, TTKCalculator.AUTO, opts4, TTKCalculator)
						if not compareTTKs(ttks3, ttks4) then
							addFromTTKs(ammo, data, ammoname, maxname, getTurbocharger() .. '<br>' .. getModdedLoader(), ttks4)
						end
					end
				end
				
				-- ライトマシンガン (ディヴォーションLMG)
				if stat.category == 'light_machine_gun' then
					local opts3 = aw.shallowCopy(opts2)
					opts3.useModdedLoader = true
					local ttks3 = getTTKs(stat, stks, TTKCalculator.AUTO, opts3, TTKCalculator)
					if not compareTTKs(ttks2, ttks3) then
						addFromTTKs(ammo, data, ammoname, basename, getTurbocharger() .. '<br>' .. getModdedLoader(), ttks3)
					end
				end
			end
		end
	end, function(num)
		return 0.001 * aw.round(1000 * num)
	end, false, -0.1)
	return tostring(tbl)
end
return p