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

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

提供:Apex Data
ナビゲーションに移動 検索に移動
(ボセックコンパウンドボウの最大溜めの弾速表示に対応)
(ヘッド・レッグショット倍率をダメージ表記と同じセルに入れるように変更)
173行目: 173行目:
stat.charged)
stat.charged)
else
else
damageText = tostring(base)
damageText = string.format('<span class="text-type-number">%s</span>', base)
end
end
else
else
return
return
end
end
renderRow(tbl, cfg.name, damageText)
local cell = createCellInRow(tbl, cfg.name)
cell:wikitext(damageText)
local hlm1 = 0.2 + 0.8 * stat.headshot
local hlm1 = 0.2 + 0.8 * stat.headshot
187行目: 188行目:
local cattext = string.format(cfg.head_category, stat.headshot)
local cattext = string.format(cfg.head_category, stat.headshot)
local intable = tbl:tag('tr')
local intable =
:tag('td')
--tbl:tag('tr'):tag('td'):attr('colspan', 2)
:attr('colspan', 2)
cell
:tag('table')
:tag('table')
:addClass('condensedtable')
:addClass('condensedtable')
:addClass('listtable')
:addClass('listtable')
if sprm > 1 then
if sprm > 1 then
local spr1 = 0.2 + 0.8 * sprm
local spr1 = 0.2 + 0.8 * sprm

2021年5月8日 (土) 21:48時点における版

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

require('Module:Utility/mw.html Extensions')

local p = {}
local cfg = mw.loadData('Module:WeaponInfobox/configuration')

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

local function calcBurstAverageRPS(stat, delay)
	delay = delay or stat.firerate.burst_delay
	return stat.mode.burst / ((stat.mode.burst - 1) / stat.firerate.burst + delay)
end

local function createCellInRow(tbl, name)
	return tbl:tag('tr')
		:tag('th')
			:wikitext(name)
			:done()
		:tag('td')
end

local function renderRow(tbl, name, item)
	createCellInRow(tbl, name):wikitext(item)
end

local function renderFormatRow(tbl, name, default, common, rare, epic, opts)
	opts = opts or {}
	opts.separator = opts.separator or '&nbsp;-&nbsp;'
	local row = tbl:tag('tr')
		:addClassIf(opts.class and type(opts.class) == 'string', opts.class)
	
	if name ~= nil then
		row:tag('th')
			:attrIf(opts.headerAlign and type(opts.headerAlign) == 'string', { align = opts.headerAlign })
			:wikitext(name)
	end

	local first = true
	if default ~= nil then
		first = false
		row:tag('td')
			:addClass('cell-type-number')
			:attrIf(opts.align and type(opts.align) == 'string', { align = opts.align })
			:wikitext(default)
	end
	
	if common ~= nil then
		if not first then
			row:tag('td'):wikitext(opts.separator)
		end
		first = false
		row:tag('td')
			:addClass('cell-type-number')
			:attrIf(opts.align and type(opts.align) == 'string', { align = opts.align })
			:wikitext(formatter:common(common))
	end
	
	if rare ~= nil then
		if not first then
			row:tag('td'):wikitext(opts.separator)
		end
		first = false
		row:tag('td')
			:addClass('cell-type-number')
			:attrIf(opts.align and type(opts.align) == 'string', { align = opts.align })
			:wikitext(formatter:rare(rare))
	end
	
	if epic ~= nil then
		if not first then
			row:tag('td'):wikitext(opts.separator)
		end
		first = false
		row:tag('td')
			:addClass('cell-type-number')
			:attrIf(opts.align and type(opts.align) == 'string', { align = opts.align })
			:wikitext(formatter:epic(epic))
	end
	
	if opts.footer ~= nil then
		row:tag('td')
			:attrIf(opts.footerAlign   and type(opts.footerAlign) == 'string',   { align   = opts.footerAlign }, { align = 'left' })
			:attrIf(opts.footerColspan and type(opts.footerColspan) == 'number', { colspan = opts.footerColspan })
			:wikitext(opts.footer)
	end
	return row
end

local function renderReleaseDate(tbl, cfg, release)
	local r = os.date("*t", release)
	local releaseText = string.format(
		'<time datetime="%04d-%02d-%02dT%02d:00:00.000+0900">%d年%d月%d日 %d時</time>~',
		r.year, r.month, r.day, r.hour, r.year, r.month, r.day, r.hour)
	renderRow(tbl, cfg.name, releaseText)
end

local function renderCategory(tbl, cfg, stat)
	local category = nu.type(stat)
	local item = string.format('[[武器#%s|%s]][[Category:%s]]', category, category, category)
	renderRow(tbl, cfg.name, item)
end

local function renderAmmo(tbl, cfg, stat)
	local ammo = nu.ammo(stat)
	local classSuffix
	if aw.stringstarts(stat, 'special_') then
		classSuffix = 'special'
	else
		classSuffix = stat
	end
	local item = string.format(
		'%s [[弾薬#%s|<span class="text-ammo text-ammo-%s">%s</span>]][[Category:%s]]',
		iu.ammo(stat, { size = 24 }),
		ammo,
		classSuffix,
		ammo,
		ammo)
	renderRow(tbl, cfg.name, item)
end

local function renderMode(tbl, cfg, stat)
	local builder = require('Module:Utility/StringBuilder').new()
	if stat.burst > 1 then
		builder:appendFormat(cfg.burst, stat.burst)
		builder:append(cfg.burst_category)
	end
	
	if stat.auto then
		if not builder:isEmpty() then
			builder:append(cfg.separator)
		end
		builder:append(cfg.auto, cfg.auto_category)
	end
	
	if stat.single then
		if not builder:isEmpty() then
			builder:append(cfg.separator)
		end
		builder:append(cfg.single, cfg.single_category)
	end
	
	renderRow(tbl, cfg.name, tostring(builder))
end

local function renderDamage(tbl, cfg, stat, ammo, pellet)
	local base = stat.base
	local typename = type(base)
	local damageText
	if pellet ~= nil then
		damageText = string.format(cfg.shotgun, base, pellet)
	elseif typename == 'table' then
		damageText = table.concat(base, ' - ')
	elseif typename == 'number' then
		if stat.anvil_receiver ~= nil then
			damageText = string.format(
				'<span class="text-type-number">%s</span> <span class="text-separator">/</span> %s <span class="text-type-number">%s</span>',
				base,
				iu.hopup('anvil_receiver'),
				formatter:legendary(stat.anvil_receiver.base))
		elseif aw.isNumberAndGreaterThanZero(stat.amped) then
			damageText = string.format(
				'<span class="text-type-number">%s</span> <span class="text-separator">/</span> %s <span class="text-type-number">%s</span>',
				base,
				iu.item('シールドセル'),
				stat.amped)
		elseif aw.isNumberAndGreaterThanZero(stat.charged) then
			damageText = string.format(
				'<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>',
				base,
				stat.charged)
		else
			damageText = string.format('<span class="text-type-number">%s</span>', base)
		end
	else
		return
	end
	local cell = createCellInRow(tbl, cfg.name)
	cell:wikitext(damageText)
	
	local hlm1 = 0.2 + 0.8 * stat.headshot
	local hlm2 = 0.4 + 0.6 * stat.headshot
	local hlm3 = 0.5 + 0.5 * stat.headshot
	local leg = stat.legshot
	local sprm = stat.skullpiercer_rifling or 1
	
	local cattext = string.format(cfg.head_category, stat.headshot)
	local intable =
		--tbl:tag('tr'):tag('td'):attr('colspan', 2)
		cell
			:tag('table')
				:addClass('condensedtable')
				:addClass('listtable')
	if sprm > 1 then
		local spr1 = 0.2 + 0.8 * sprm
		local spr2 = 0.4 + 0.6 * sprm
		local spr3 = 0.5 + 0.5 * sprm
		
		renderFormatRow(
			intable,
			cfg.head,
			nil, hlm1, hlm2, hlm3,
			{ align = 'left', footer = cfg.unit .. cattext })
		renderFormatRow(
			intable,
			iu.hopup('skullpiercer_rifling') .. '&nbsp;',
			nil, spr1, spr2, spr3,
			{ class = 'no-list-style', align = 'left', footer = cfg.unit })
	else
		renderFormatRow(
			intable,
			cfg.head,
			nil, hlm1, hlm2, hlm3,
			{ align = 'left', footer = cfg.unit .. cattext })
	end
	
	local legtext
	if stat.anvil_receiver ~= nil then
		legtext = string.format(
			'<span class="text-type-number">%s</span>%s <span class="text-separator">/</span> %s %s',
			leg,
			cfg.unit,
			iu.hopup('anvil_receiver'),
			formatter:legendary('<span class="text-type-number">' .. stat.anvil_receiver.legshot .. '</span>' .. cfg.unit))
	elseif aw.isNumberAndGreaterThanZero(stat.legshot_charged) then
		legtext = string.format(
			'<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>%s',
			leg,
			stat.legshot_charged,
			cfg.unit)
	else
		legtext = string.format(
			'<span class="text-type-number">%s</span>%s',
			leg,
			cfg.unit)
	end
	
	intable:tag('tr')
		:tag('th')
			:wikitext(cfg.legs)
			:done()
		:tag('td')
			:attr('align', 'left')
			:attr('colspan', 8)
			:wikitext(legtext)
end

local function renderMultipleFirerateRow(intbl, name, cfg, rps, opts)
	opts = opts or {}
	renderFormatRow(
		intbl,
		name .. cfg.rps.name,
		string.format(cfg.rps.format, 0.01 * aw.round(100 * rps[1])),
		string.format(cfg.rps.format, 0.01 * aw.round(100 * rps[2])),
		string.format(cfg.rps.format, 0.01 * aw.round(100 * rps[3])),
		string.format(cfg.rps.format, 0.01 * aw.round(100 * rps[4])),
		{ class = opts.rpsClass or '', separator = cfg.rps.separator, footer = cfg.rps.unit })
	renderFormatRow(
		intbl,
		cfg.rpm.name,
		string.format(cfg.rpm.format, aw.comma(0.1 * aw.round(600 * rps[1]))),
		string.format(cfg.rpm.format, aw.comma(0.1 * aw.round(600 * rps[2]))),
		string.format(cfg.rpm.format, aw.comma(0.1 * aw.round(600 * rps[3]))),
		string.format(cfg.rpm.format, aw.comma(0.1 * aw.round(600 * rps[4]))),
		{ class = opts.rpmClass and 'all-secondary ' .. opts.rpmClass or 'all-secondary', headerAlign = 'right', separator = cfg.rpm.separator, footer = cfg.rpm.unit })
end

local function renderFirerateRow(intbl, name, cfg, rps, opts)
	opts = opts or {}
	intbl:tag('tr')
		:addClassIf(opts.class and type(opts.class) == 'string', opts.class)
		:tag('th')
			:wikitext(name)
			:done()
		:tag('td')
			:addClass('cell-type-number')
			:attr('align', 'right')
			:wikitext(string.format(cfg.rps.format, rps))
			:done()
		:tag('td')
			:wikitext(cfg.rps.unit)
			:done()
		:tag('td')
			:tag('small')
				:addClass('text-secondary')
				:wikitext(cfg.rpm.name)
				:done()
			:done()
		:tag('td')
			:addClass('cell-type-number')
			:attr('align', 'right')
			:tag('small')
				:addClass('text-secondary')
				:wikitext(aw.comma(0.1 * aw.round(600 * rps)))
				:done()
			:done()
		:tag('td')
			:tag('small')
				:addClass('text-secondary')
				:wikitext(cfg.rpm.unit)
end

local function renderFirerateCell(cell, cfg, rps, opts)
	opts = opts or {}
	opts.header = opts.header or ''
	cell:wikitext(string.format(
		opts.header .. '<span class="text-type-number">' .. cfg.rps.format .. '</span>' .. cfg.rps.unit .. '<span class="text-secondary"><small>(<span class="text-type-number">%s</span>' .. cfg.rpm.unit .. '</small></span>',
		rps,
		aw.comma(0.1 * aw.round(600 * rps))))
end

local function renderFirerate(tbl, cfg, stat)
	local firerate = stat.firerate
	if firerate == nil then
		return
	end
	
	local mode = stat.mode
	local cell = createCellInRow(tbl, cfg.name)
	local rps
	if mode.auto then
		if mode.single then
			if firerate.anvil_receiver or firerate.auto ~= firerate.single then
				local intbl = cell:tag('table')
								:addClass('condensedtable')
								:addClass('listtable')
				if firerate.auto ~= firerate.single then
					local singleRPS
					if aw.isNumberAndGreaterThanZero(firerate.single_rechamber) then
						singleRPS = 1 / (1 / firerate.single + firerate.single_rechamber)
					else
						singleRPS = firerate.single
					end
					renderFirerateRow(intbl, cfg.auto, cfg, firerate.auto)
					renderFirerateRow(intbl, cfg.single, cfg, singleRPS)
				else
					renderFirerateRow(intbl, cfg.auto_single, cfg, firerate.auto)
				end
				
				if firerate.anvil_receiver then
					renderFirerateRow(intbl, iu.hopup('anvil_receiver'), cfg, firerate.anvil_receiver)
				end
				return
			end
		elseif mode.burst > 1 then
			if type(firerate.auto) == 'table' then
				local newcell = tbl:tag('tr')
					:tag('td')
						:attr('colspan', 2)
				renderFirerateCell(newcell:tag('ul'):tag('li'), cfg, firerate.burst, { header = cfg.burst })
				
				intbl = newcell:tag('table')
					:addClass('condensedtable')
					:addClass('listtable')
				local average = {
					calcBurstAverageRPS(stat, firerate.burst_delay[1]),
					calcBurstAverageRPS(stat, firerate.burst_delay[2]),
					calcBurstAverageRPS(stat, firerate.burst_delay[3]),
					calcBurstAverageRPS(stat, firerate.burst_delay[4]),
				}
				renderMultipleFirerateRow(intbl, cfg.burst_average, cfg, average, { rpsClass = 'no-list-style', rpmClass = 'no-list-style' })
				renderMultipleFirerateRow(intbl, cfg.auto,          cfg, firerate.auto, { rpmClass = 'no-list-style' })
			else
				local intbl = cell:tag('table')
					:addClass('condensedtable')
					:addClass('listtable')
				local average = calcBurstAverageRPS(stat)
				renderFirerateRow(intbl, cfg.burst,         cfg, firerate.burst)
				renderFirerateRow(intbl, cfg.burst_average, cfg, average, { class = 'no-list-style' })
				renderFirerateRow(intbl, cfg.auto,          cfg, firerate.auto)
			end
			return
		end
		rps = firerate.auto
	elseif mode.single then
		if mode.burst > 1 then
			local intbl = cell:tag('table')
							:addClass('condensedtable')
							:addClass('listtable')
			local average = calcBurstAverageRPS(stat)
			renderFirerateRow(intbl, cfg.burst,         cfg, firerate.burst)
			renderFirerateRow(intbl, cfg.burst_average, cfg, average, { class = 'no-list-style' })
			
			local singleRPS
			if aw.isNumberAndGreaterThanZero(firerate.single_rechamber) then
				singleRPS = 1 / (1 / firerate.single + firerate.single_rechamber)
			else
				singleRPS = firerate.single
			end
			renderFirerateRow(intbl, cfg.single, cfg, singleRPS)
			return
		end
		
		if aw.isNumberAndGreaterThanZero(firerate.single_rechamber) then
			rps = 1 / (1 / firerate.single + firerate.single_rechamber)
		else
			rps = firerate.single
		end
	else
		return
	end
	
	if type(rps) == 'table' then
		local intbl = cell:tag('table'):addClass('condensedtable')
		renderMultipleFirerateRow(intbl, '', cfg, rps)
	else
		renderFirerateCell(cell, cfg, rps)
	end
end

local function renderProjectileSpeed(tbl, cfg, projectile_speed, projectile_speed_charged)
	if projectile_speed == nil then
		return
	end
	
	local text
	if projectile_speed == math.huge then
		text = cfg.hitscan
	elseif projectile_speed_charged ~= nil then
		text = string.format(
			'<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>%s',
			string.format(cfg.format, aw.comma(aw.roundx(0.0254 * projectile_speed, 2))),
			string.format(cfg.format, aw.comma(aw.roundx(0.0254 * projectile_speed_charged, 2))),
			cfg.unit)
	else
		text = string.format(
			'<span class="text-type-number">%s</span>%s',
			string.format(cfg.format, aw.comma(aw.roundx(0.0254 * projectile_speed, 2))),
			cfg.unit)
	end
	renderRow(tbl, cfg.name, text)
end

local function renderRowDPS(tbl, name, leg, body, head, skullpiercer, tag)
	tag = tag or 'td'
	
	local row = tbl:tag('tr')
	row:tag('th'):wikitext(name)
	if leg == '' then
		row:tag(tag)
			:attr('align', 'center')
			:addClass('disabled')
			:wikitext('–')
	elseif leg ~= nil and leg ~= body then
		row:tag(tag):wikitext(leg)
	end
	row
		:tag(tag):wikitext(body):done()
		:tag(tag):wikitext(head)
	if skullpiercer ~= nil then
		row:tag(tag):wikitext(skullpiercer)
	end
end

local function toDPSText(dps)
	return string.format("%.1f", dps)
end

local function renderDPSTable(cell, name, pellet, base, damages, firerate, useRound)
	local headDamage = aw.round(damages.headshot * base)
	local legDamage = aw.round(damages.legshot * base)
	local skullpiercer = damages.skullpiercer_rifling
	local selectiveRound = useRound and aw.round or aw.roundover
	
	local skullpiercerHeader = nil
	local commonSkullpiercerDPS = nil
	local lowprofileSkullpiercerDPS = nil
	local fortifiedSkullpiercerDPS = nil
	if skullpiercer ~= nil then
		skullpiercerHeader = iu.hopup('skullpiercer_rifling')
		
		local skullpiercerDamage = aw.round(skullpiercer * base)
		commonSkullpiercerDPS = toDPSText(pellet * skullpiercerDamage * firerate)
		lowprofileSkullpiercerDPS = toDPSText(pellet * selectiveRound(1.05 * skullpiercerDamage) * firerate)
		fortifiedSkullpiercerDPS = toDPSText(pellet * aw.round(0.85 * skullpiercerDamage) * firerate)
	end
	
	if type(firerate) == 'table' then
		firerate = firerate[1]
	end
	
	local intable = cell:tag('table')
		:addClass('intable')
		:addClass('numbertable')
	local lowprofileLegDamagePlaceholder
	if damages.legshot == 1 then
		lowprofileLegDamagePlaceholder = nil
		renderRowDPS(intable, name, nil, '胴', '頭', skullpiercerHeader, 'th')
	else
		lowprofileLegDamagePlaceholder = ''
		renderRowDPS(intable, name, '脚', '胴', '頭', skullpiercerHeader, 'th')
	end
	renderRowDPS(
		intable,
		'通常',
		toDPSText(pellet * legDamage * firerate),
		toDPSText(pellet * base * firerate),
		toDPSText(pellet * headDamage * firerate),
		commonSkullpiercerDPS)
	renderRowDPS(
		intable,
		'小柄',
		lowprofileLegDamagePlaceholder,
		toDPSText(pellet * selectiveRound(1.05 * base, useRound) * firerate),
		toDPSText(pellet * selectiveRound(1.05 * headDamage, useRound) * firerate),
		lowprofileSkullpiercerDPS)
	renderRowDPS(
		intable,
		'鉄壁',
		toDPSText(pellet * aw.round(0.85 * legDamage) * firerate),
		toDPSText(pellet * aw.round(0.85 * base) * firerate),
		toDPSText(pellet * aw.round(0.85 * headDamage) * firerate),
		fortifiedSkullpiercerDPS)
end

local function renderDPS(tbl, name, stat)
	if stat.firerate == nil then
		return
	end
	
	local damage = stat.damage.base
	renderRow(tbl, name, '')
	
	local pellet = stat.pellet or 1
	local useRound = stat.damage.round or false
	local cell = tbl:tag('tr'):tag('td'):attr('colspan', 2)
	
	if type(damage) == 'table' then
		for _, damage in ipairs(damage) do
			if stat.mode.single then
				renderDPSTable(
					cell,
					'',
					pellet,
					damage,
					stat.damage,
					stat.firerate.single,
					useRound)
			end
		end
	else
		if stat.mode.burst and stat.mode.burst > 1 then
			local average
			if type(stat.firerate.burst_delay) == 'table' then
				average = calcBurstAverageRPS(stat, stat.firerate.burst_delay[1])
			else
				average = calcBurstAverageRPS(stat)
			end
			renderDPSTable(
				cell,
				'',
				pellet,
				stat.damage.base,
				stat.damage,
				average,
				useRound)
		end
		
		if stat.mode.auto then
			renderDPSTable(
				cell,
				'',
				pellet,
				stat.damage.base,
				stat.damage,
				stat.firerate.auto,
				useRound)
		end
		
		if stat.mode.single and stat.firerate.auto ~= stat.firerate.single then
			renderDPSTable(
				cell,
				'',
				pellet,
				stat.damage.base,
				stat.damage,
				stat.firerate.single,
				useRound)
		end
	
		if stat.damage.anvil_receiver and stat.firerate.anvil_receiver then
			renderDPSTable(
				cell,
				iu.hopup('anvil_receiver'),
				pellet,
				stat.damage.anvil_receiver.base,
				stat.damage.anvil_receiver,
				stat.firerate.anvil_receiver,
				useRound)
		end
	end
end

local function renderMagazineRow(intbl, name, mag, rsvmag, cfg, opts)
	opts = opts or {}
	intbl:tag('tr')
		:addClassIf(opts.class and type(opts.class) == 'string', opts.class)
		:tag('th')
			:wikitext(name)
			:done()
		:tag('td')
			:addClass('cell-type-number')
			:attr('align', 'right')
			:wikitext(mag)
			:done()
		:tag('td')
			:wikitext(cfg.unit)
			:done()
		:tag('td')
			:addClass('text-secondary')
			:wikitext('&nbsp;/&nbsp;')
			:done()
		:tag('td')
			:addClass('cell-type-number')
			:attr('align', 'right')
			:wikitext(rsvmag == math.huge and cfg.infinity or rsvmag)
			:done()
		:tag('td')
			:wikitext(cfg.unit)
end

local function renderMagazine(tbl, cfg, stat, isModdedLoaderAttachable)
	local magazine = stat.magazine
	local typename = type(magazine)
	if typename == 'table' then
		if isModdedLoaderAttachable then
			local intable = createCellInRow(tbl, cfg.name)
				:tag('table')
					:addClass('condensedtable')
			renderFormatRow(
				intable,
				'',
				magazine[1], magazine[2], magazine[3], magazine[4], { footer = cfg.units })
			renderFormatRow(
				intable,
				formatter:hopup('改造ローダー') .. '&nbsp;',
				aw.round(1.15 * magazine[1]),
				aw.round(1.15 * magazine[2]),
				aw.round(1.15 * magazine[3]),
				aw.round(1.15 * magazine[4]),
				{ footer = cfg.units })
		else
			local text = formatter:format(magazine[1], magazine[2], magazine[3], magazine[4], cfg.units, ' - ')
			renderRow(tbl, cfg.name, text)
		end
	elseif typename == 'number' then
		local text
		if magazine == math.huge then
			if aw.isNumber(stat.overheat) and stat.firerate and stat.firerate.auto then
				text = string.format(
					'%s (<span class="text-type-number">%d</span>%s <span class="text-separator">/</span> %s <span class="text-type-number">%d</span>%s)',
					cfg.infinity,
					1 + math.floor(stat.overheat * stat.firerate.auto),
					cfg.unit,
					formatter:hopup('改造ローダー'),
					1 + math.floor(1.15 * stat.overheat * stat.firerate.auto),
					cfg.unit)
			else
				text = cfg.infinity
			end
			
		elseif stat.rounds_per_shot and stat.rounds_per_shot.single and stat.rounds_per_shot.single > 1 then
			local times = magazine / stat.rounds_per_shot.single
			local timesText
			if type(cfg.times.format) == 'table' then
				if times > 2 then
					timesText = string.format(cfg.times.format[3], times)
				else
					timesText = cfg.times.format[times]
				end
			else
				timesText = string.format(cfg.times.format, times)
			end
			
			text = string.format(
				'<span class="text-type-number">%d</span>%s <span class="text-secondary"><small>(%s)</small></span>',
				magazine, cfg.unit, timesText)
			
		elseif aw.isNumber(stat.magazine_reserve) then
			if isModdedLoaderAttachable then
				local intbl = createCellInRow(tbl, cfg.name)
					:tag('table')
						:addClass('condensedtable')
				renderMagazineRow(intbl, '', magazine, stat.magazine_reserve, cfg)
				renderMagazineRow(intbl, formatter:hopup('改造ローダー') .. '&nbsp;', aw.round(1.15 * magazine), stat.magazine_reserve, cfg)
				return
			else
				text = string.format(
					'<span class="text-type-number">%d</span>%s <span class="text-separator">/</span> <span class="text-type-number">%s</span>%s',
					magazine,
					cfg.unit,
					stat.magazine_reserve == math.huge and cfg.infinity or stat.magazine_reserve,
					cfg.unit)
			end
			
		else
			text = string.format('<span class="text-type-number">%d</span>%s', magazine, cfg.unit)
		end
		renderRow(tbl, cfg.name, text)
	end
end

local function renderReload(tbl, cfg, reload, attachments, ammo, isModdedLoaderAttachable)
	if reload.full == nil then
		return
	end
	
	local muls
	if ammo == 'heavy' or ammo == 'special_heavy' then
		muls = { level2 = 0.92, level3 = 0.87 }
	else
		muls = { level2 = 0.95, level3 = 0.9 }
	end
	
	local incompatible = not attachments.extended_mag_or_shotgun_bolt
	if incompatible or ammo == 'shotgun' or aw.stringstarts(ammo, "special_") then
		if reload.tactical ~= nil and reload.tactical ~= reload.full then
			local tacticalText, fullText
			if incompatible or ammo == 'special_sniper' then
				tacticalText = tostring(reload.tactical)
				fullText     = tostring(reload.full)
			else
				tacticalText = tostring(muls.level3 * reload.tactical)
				fullText     = tostring(muls.level3 * reload.full)
			end
			
			local intable = createCellInRow(tbl, cfg.name)
				:tag('table')
					:addClass('condensedtable')
					:addClass('listtable')
			renderFormatRow(
				intable,
				cfg.tactical.name .. '&nbsp;',
				string.format(cfg.tactical.format, reload.tactical),
				nil, nil, nil, { footer = cfg.tactical.unit })
			if isModdedLoaderAttachable then
				renderFormatRow(
					intable,
					formatter:hopup('改造ローダー') .. '&nbsp;',
					string.format(cfg.tactical.format, 0.75 * reload.tactical),
					nil, nil, nil, { class = 'no-list-style', headerAlign = 'right', footer = cfg.tactical.unit })
			end
			
			renderFormatRow(
				intable,
				cfg.full.name .. '&nbsp;',
				string.format(cfg.full.format, reload.full),
				nil, nil, nil, { footer = cfg.full.unit })
			if isModdedLoaderAttachable then
				renderFormatRow(
					intable,
					formatter:hopup('改造ローダー') .. '&nbsp;',
					string.format(cfg.full.format, 0.75 * reload.full),
					nil, nil, nil, { class = 'no-list-style', headerAlign = 'right', footer = cfg.full.unit })
			end
		elseif isModdedLoaderAttachable then
			local time
			if incompatible then
				time = reload.full
			else
				time = muls.level3 * reload.full
			end
			local text = string.format(
				cfg.full.format .. cfg.full.unit .. ' <span class="text-separator">/</span> %s ' .. cfg.full.format .. cfg.full.unit,
				time,
				formatter:hopup('改造ローダー'),
				0.75 * time)
			renderRow(tbl, cfg.name, text)
		else
			local time
			if incompatible or ammo == 'special_sniper' then
				time = reload.full
			else
				time = muls.level3 * reload.full
			end
			renderRow(tbl, cfg.name, string.format(cfg.full.format, time) .. cfg.full.unit)
		end
	elseif reload.tactical ~= nil and reload.tactical ~= reload.full then
		renderRow(tbl, cfg.name, '')
		
		local intable = tbl:tag('tr')
			:tag('td')
				:attr('colspan', 2)
					:tag('table')
						:addClass('condensedtable')
						:addClass('listtable')
		renderFormatRow(
			intable,
			cfg.tactical.name .. '&nbsp;',
			string.format(cfg.tactical.format, reload.tactical),
			nil,
			string.format(cfg.tactical.format, muls.level2 * reload.tactical),
			string.format(cfg.tactical.format, muls.level3 * reload.tactical),
			{ footer = cfg.tactical.unit })
		if isModdedLoaderAttachable then
			renderFormatRow(
				intable,
				formatter:hopup('改造ローダー') .. '&nbsp;',
				string.format(cfg.tactical.format, 0.75 * reload.tactical),
				nil,
				string.format(cfg.tactical.format, 0.75 * muls.level2 * reload.tactical),
				string.format(cfg.tactical.format, 0.75 * muls.level3 * reload.tactical),
				{ class = 'no-list-style', headerAlign = 'right', footer = cfg.tactical.unit })
		end
		
		renderFormatRow(
			intable,
			cfg.full.name .. '&nbsp;',
			string.format(cfg.full.format, reload.full),
			nil,
			string.format(cfg.full.format, muls.level2 * reload.full),
			string.format(cfg.full.format, muls.level3 * reload.full),
			{ footer = cfg.full.unit })
		if isModdedLoaderAttachable then
			renderFormatRow(
				intable,
				formatter:hopup('改造ローダー') .. '&nbsp;',
				string.format(cfg.full.format, 0.75 * reload.full),
				nil,
				string.format(cfg.full.format, 0.75 * muls.level2 * reload.full),
				string.format(cfg.full.format, 0.75 * muls.level3 * reload.full),
				{ class = 'no-list-style', headerAlign = 'right', footer = cfg.full.unit })
		end
	else
		local text = formatter:format(
			string.format(cfg.full.format, reload.full),
			nil,
			string.format(cfg.full.format, muls.level2 * reload.full),
			string.format(cfg.full.format, muls.level3 * reload.full),
			cfg.full.unit, ' - ')
		renderRow(tbl, cfg.name, text)
	end
end

local function renderDraw(tbl, cfg, draw, quickdraw_holster, attachments, ammo)
	if draw == nil then
		return
	end
	
	local muls
	if ammo == 'heavy' or ammo == 'special_heavy' then
		muls = { level2 = 0.92, level3 = 0.87 }
	else
		muls = { level2 = 0.95, level3 = 0.9 }
	end
	
	local incompatible = not attachments.stock
	if incompatible or aw.stringstarts(ammo, "special_") then
		local time
		if incompatible or ammo == 'special_sniper' then
			time = draw
		else
			time = 0.75 * draw
		end
		
		local text
		if quickdraw_holster > 0 then
			text = string.format(
				cfg.format .. cfg.unit .. ' <span class="text-separator">/</span> %s ' .. formatter:epic(cfg.format .. cfg.unit),
				time,
				iu.hopup('quickdraw_holster'),
				quickdraw_holster * time)
		else
			text = string.format(cfg.format, time) .. cfg.unit
		end
		renderRow(tbl, cfg.name, text)
	elseif quickdraw_holster > 0 then
		local intable = createCellInRow(tbl, cfg.name)
			:tag('table')
				:addClass('condensedtable')
		renderFormatRow(
			intable,
			'',
			string.format(cfg.format, draw),
			string.format(cfg.format, 0.85 * draw),
			string.format(cfg.format, 0.80 * draw),
			string.format(cfg.format, 0.75 * draw),
			{ footer = cfg.unit })
		renderFormatRow(
			intable,
			iu.hopup('quickdraw_holster') .. '&nbsp;',
			string.format(cfg.format, quickdraw_holster * draw),
			string.format(cfg.format, 0.85 * quickdraw_holster * draw),
			string.format(cfg.format, 0.80 * quickdraw_holster * draw),
			string.format(cfg.format, 0.75 * quickdraw_holster * draw),
			{ footer = cfg.unit })
	else
		local text = formatter:format(
			string.format(cfg.format, draw),
			string.format(cfg.format, 0.85 * draw),
			string.format(cfg.format, 0.80 * draw),
			string.format(cfg.format, 0.75 * draw),
			cfg.unit, ' - ')
		renderRow(tbl, cfg.name, text)
	end
end

local function createTable(cfg, stat)
	local isModdedLoaderAttachable = stat.category == 'light_machine_gun' or stat.ammo == 'minigun'
	local tbl = mw.html.create('table')
	renderReleaseDate(tbl, cfg.release, stat.release)
	renderCategory(tbl, cfg.category, stat.category)
	renderAmmo(tbl, cfg.ammo, stat.ammo)
	renderMode(tbl, cfg.mode, stat.mode)
	renderDamage(tbl, cfg.damage, stat.damage, stat.ammo, stat.pellet)
	renderFirerate(tbl, cfg.firerate, stat)
	renderProjectileSpeed(tbl, cfg.projectilespeed, stat.projectile_speed, stat.projectile_speed_charged)
	renderMagazine(tbl, cfg.magazine, stat, isModdedLoaderAttachable)
	if stat.time then
		if stat.time.draw then
			renderDraw(tbl, cfg.draw, stat.time.draw, stat.time.quickdraw_holster or 0, stat.attachments, stat.ammo)
		end
		
		if stat.time.reload then
			renderReload(tbl, cfg.reload, stat.time.reload, stat.attachments, stat.ammo, isModdedLoaderAttachable)
		end
	end
	renderDPS(tbl, 'DPS', stat)
	return tbl
end

local function renderInfobox(args)
	local cfglang = cfg[args and args.lang or 'ja']
	local stat = mw.loadData('Module:Stat/Weapon')[args.name]
	local div = mw.html.create('div')
		:addClass('tpl-infobox-right')
		:addClass('tpl-weapon')
	if aw.stringstarts(stat.ammo, "special_") then
		div:addClass('tpl-weapon-special')
	else
		div:addClass('tpl-weapon-' .. stat.ammo)
	end
	
	div:tag('div')
		:addClass('tpl-weapon-header')
		:wikitext(args.name .. iu.ammo(stat.ammo, { size = 40 }))
	div:node(createTable(cfglang, stat))
	return div
end

function p._main(args, frame)
	formatter = require('Module:Utility/Formatter').new(frame)
	return tostring(renderInfobox(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