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

モジュール:WeaponInfobox/DPS

提供:Apex Data
< モジュール:WeaponInfobox
2021年8月16日 (月) 01:53時点におけるMntone (トーク | 投稿記録)による版 (rechamber有効時の射撃レートの計算式を変更)
ナビゲーションに移動 検索に移動

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

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

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

local aw = require('Module:Utility/Library')
local apex = require('Module:Utility/ApexLibrary')
local iu = require('Module:Utility/Image')
local proto = require('Module:Utility/Prototypes')

-- Constants
local leftBorder = {
	['border-left-width'] = '1px',
	['border-left-style'] = 'solid',
}
local leftBorderWithoutTopRightBorder = {
	['border-top'] = '0 none transparent',
	['border-right'] = '0 none transparent',
	['border-left-width'] = '1px',
	['border-left-style'] = 'solid',
}

-- Protos
local MinMaxProto = {
	proto.NumberRange(0),
	proto.NumberRange(0),
}
local MultipleFirerateProto = {
	proto.NumberRange(0),
	proto.NumberRange(0),
	proto.NumberRange(0),
	proto.NumberRange(0),
}
local AnvilReceiverProto = {
	damage = {
		base     = proto.NumberRange(1),
		headshot = proto.NumberRange(1, 10),
		legshot  = proto.NumberRange(0, 1),
	},
	firerate = {
		single = proto.NumberRange(0),
	},
}
local ShatterCapsProto = {
	damage = {
		base     = proto.NumberRange(1),
		headshot = proto.NumberRange(1, 10),
		legshot  = proto.NumberRange(0, 1),
	},
	pellet = proto.NumberRange(1),
}
local DeadeyesTempoWithRechamberProto = {
	firerate = {
		single_rechamber = proto.NumberRange(0),
	},
}

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

local function calcRechamberRPS(firerate, rechamber)
	rechamber = rechamber or firerate.single_rechamber
	return math.min(firerate.single, 1 / rechamber)
end

local function getStats(stat1, stat2)
	local ret
	if stat2 then
		ret = {
			base = stat2.damage.base     or stat1.damage.base,
			head = stat2.damage.headshot or stat1.damage.headshot or 1.75,
			legs = stat2.damage.legshot  or stat1.damage.legshot  or 0.75,
		}
	else
		ret = {
			base = stat1.damage.base,
			head = stat1.damage.headshot or 1.75,
			legs = stat1.damage.legshot  or 0.75,
		}
	end
	return ret
end

local function getDamages(stat, helmets)
	local helmet = helmets.level0.disabled and helmets.level1 or helmets.level0
	
	local ret
	if stat.legs > 0 and stat.legs < 1 then
		ret = {
			aw.round(stat.legs * stat.base),
			stat.base,
			{
				aw.round(helmets.level3.func(stat.head) * stat.base),
				aw.round(helmet.func(stat.head) * stat.base),
			},
		}
	else
		ret = {
			stat.base,
			{
				aw.round(helmets.level3.func(stat.head) * stat.base),
				aw.round(helmet.func(stat.head) * stat.base),
			},
		}
	end
	return ret
end

local function getDPSs(damages, perk, pellets, firerate, useRound)
	local ret = {}
	for _, damage in ipairs(damages) do
		if proto.validateTypes(damage, MinMaxProto) then
			local minPerked = apex.calcPassiveDamage(damage[1], perk, { round = useRound })
			local maxPerked = apex.calcPassiveDamage(damage[2], perk, { round = useRound })
			table.insert(ret, { minPerked * firerate, pellets * maxPerked * firerate })
		elseif pellets > 1 then
			local perked = apex.calcPassiveDamage(damage, perk, { round = useRound })
			table.insert(ret, { perked * firerate, pellets * perked * firerate })
		else
			local perked = apex.calcPassiveDamage(damage, perk, { round = useRound })
			table.insert(ret, pellets * perked * firerate)
		end
	end
	return ret
end

local function toDPSText(value, format)
	if proto.validateTypes(value, MinMaxProto) then
		return string.format(format, string.format("%.1f", value[1]), string.format("%.1f", value[2]))
	else
		return string.format("%.1f", value)
	end
end

local function renderDPSRow(tbl, name, format, data, opts)
	local row = tbl:tag('tr')
	if aw.isNumberAndGreaterThanZero(opts.offset) then
		for i = 1, opts.offset do
			local hasLeftBorder = opts.offsetStyles and opts.offsetStyles[i] and opts.offsetStyles[i].leftBorder
			if hasLeftBorder then
				row:tag('th')
					:css(leftBorderWithoutTopRightBorder)
					:wikitext('&nbsp;')
			else
				row:tag('th')
					:css('border', '0 none transparent')
					:wikitext('&nbsp;')
			end
		end
	end
	row:tag('th')
		:attrIf(aw.isNumber(opts.colspan), { colspan = opts.colspan })
		:cssIf(aw.isNumberAndGreaterThanZero(opts.offset), leftBorder)
		:wikitext(name)
	if #data >= 3 then
		if data[1] == nil then
			row:tag('td')
				:attr('align', 'center')
				:addClass('disabled')
				:wikitext('–')
		else
			row:tag('td')
				:addClass('cell-type-number')
				:attr('align', 'right')
				:wikitext(toDPSText(data[1], format))
		end
		row
			:tag('td')
				:addClass('cell-type-number')
				:attr('align', 'right')
				:wikitext(toDPSText(data[2], format))
				:done()
			:tag('td')
				:attr('align', 'right')
				:wikitext(toDPSText(data[3], format))
	else
		row
			:tag('td')
				:addClass('cell-type-number')
				:attr('align', 'right')
				:attrIf(opts.hideLegs, { colspan = 2 })
				:wikitext(toDPSText(data[1], format))
				:done()
			:tag('td')
				:attr('align', 'right')
				:wikitext(toDPSText(data[2], format))
	end
end

function p.renderDPS(stat, shieldinfo, lang)
	local cfg      = cfg[lang].dps
	local basic    = getStats(stat)
	local damages  = getDamages(basic, shieldinfo.helmets)
	local pellet   = aw.getAsNumber(stat.pellet, 1)
	local useRound = aw.getAsBoolean(stat.damage.round, false)
	
	local hasAnvil = proto.validateTypes(stat.anvil_receiver, AnvilReceiverProto)
	local hasShat  = proto.validateTypes(stat.shatter_caps, ShatterCapsProto)
	local hasTempo = proto.validateTypes(stat.deadeyes_tempo, DeadeyesTempoWithRechamberProto)
	local hasTrmts = aw.isNumberAndGreaterThanZero(stat.firerate.auto_amped)
	local hasHopup = hasAnvil or hasShat or hasTempo or hasTrmts
	
	local hasAmped = aw.isNumberAndGreaterThanZero(stat.damage.amped)
	local hasChgd  = aw.isNumberAndGreaterThanZero(stat.damage.charged)
	local hasExtra = hasAmped or hasChgd
	
	-- Load firerate
	local firerate
	if aw.isNumber(stat.mode.burst) and stat.mode.burst > 1 then
		firerate = calcBurstRPS(stat)
	elseif stat.mode.auto then
		firerate = stat.firerate.auto
	elseif stat.mode.single then
		if aw.isNumberAndGreaterThanZero(stat.firerate.single_rechamber) then
			firerate = calcRechamberRPS(stat.firerate)
		elseif proto.validateTypes(stat.firerate.single_rechamber, MultipleFirerateProto) then
			firerate = {
				calcRechamberRPS(stat.firerate, stat.firerate.single_rechamber[1]),
				calcRechamberRPS(stat.firerate, stat.firerate.single_rechamber[2]),
				calcRechamberRPS(stat.firerate, stat.firerate.single_rechamber[3]),
				calcRechamberRPS(stat.firerate, stat.firerate.single_rechamber[4]),
			}
		else
			firerate = stat.firerate.single
		end
	else
		return nil
	end
	local hasMultiple = proto.validateTypes(firerate, MultipleFirerateProto)
	
	local headerColspan
	local defopts, mpopts, huopts, exopts, ex2opts
	if hasMultiple then
		if hasExtra then
			headerColspan = 3
			defopts = { colspan = 3 }
			muopts  = { offset = 1, colspan = 2 }
			exopts  = { offset = 2 }
			ex2opts = { offset = 2, offsetStyles = { nil, { leftBorder = true } } }
		else
			headerColspan = 2
			defopts = { colspan = 2 }
			muopts  = { offset = 1 }
			exopts  = {}
			ex2opts = {}
		end
		huopts      = {}
	elseif hasHopup then
		if hasExtra then
			local hideLegs = damages.legs ~= 1
			headerColspan = 3
			defopts = { colspan = 3 }
			huopts  = { offset = 1, colspan = 2, hideLegs = hideLegs }
			exopts  = { offset = 2 }
			ex2opts = { offset = 2, offsetStyles = { nil, { leftBorder = true } }, hideLegs = hideLegs }
		else
			headerColspan = 2
			defopts = { colspan = 2 }
			huopts  = { offset = 1, hideLegs = damages.legs ~= 1 }
			exopts  = {}
			ex2opts = {}
		end
		muopts      = {}
	elseif hasExtra then
		headerColspan = 1
		defopts = { colspan = 2 }
		muopts  = {}
		huopts  = {}
		exopts  = {}
		ex2opts = { offset = 1 }
	else
		headerColspan = 1
		defopts = {}
		muopts  = {}
		huopts  = {}
		exopts  = {}
		ex2opts = {}
	end
	
	-- Hop-ups
	local damages2, pellet2, firerate2, label
	if hasAnvil then
		local anvil = getStats(stat, stat.anvil_receiver)
		damages2    = getDamages(anvil, shieldinfo.helmets)
		pellet2     = pellet
		firerate2   = stat.anvil_receiver.firerate.single
		label       = iu.hopup('anvil_receiver')
	elseif hasShat then
		local shat  = getStats(stat, stat.shatter_caps)
		damages2    = getDamages(shat, shieldinfo.helmets)
		pellet2     = stat.shatter_caps.pellet
		firerate2   = firerate
		label       = iu.hopup('shatter_caps')
	elseif hasTempo then
		damages2    = damages
		pellet2     = pellet
		firerate2   = calcRechamberRPS(stat.firerate, stat.deadeyes_tempo.firerate.single_rechamber)
		label       = string.format(cfg.hopup.deadeyes_tempo, iu.hopup('deadeyes_tempo'))
	elseif hasTrmts then
		damages2    = damages
		pellet2     = pellet
		firerate2   = stat.firerate.auto_amped
		label       = iu.grenade('テルミットグレネード')
	end
	
	-- Extra damages
	local damagesE, labelE, damages2E
	if hasAmped then
		local extra = aw.shallowCopy(basic)
		extra.base  = stat.damage.amped
		damagesE    = getDamages(extra, shieldinfo.helmets)
		labelE      = iu.item('シールドセル')
	elseif hasChgd then
		local extra = aw.shallowCopy(basic)
		extra.base  = stat.damage.charged
		damagesE    = getDamages(extra, shieldinfo.helmets)
		labelE      = iu.scope('スロット')
		
		if hasShat then
			local shat2    = getStats(stat, stat.shatter_caps)
			shat2.base     = stat.shatter_caps.damage.charged
			if aw.isNumberAndGreaterThanZero(stat.shatter_caps.damage.headshot_charged) then
				shat2.head = stat.shatter_caps.damage.headshot_charged
			end
			if aw.isNumberAndGreaterThanZero(stat.shatter_caps.damage.legshot_charged) then
				local legs = stat.shatter_caps.damage.legshot_charged
				shat2.legs = legs
				
				if legs == 1 then
					ex2opts.hideLegs = true
				end
			end
			damages2E      = getDamages(shat2, shieldinfo.helmets)
		end
	end
	
	local tbl = mw.html.create('table'):addClass('intable')
	
	-- Header
	local header = tbl:tag('tr'):tag('th')
		:attrIf(headerColspan > 1, { colspan = headerColspan })
		:wikitext('')
		:done()
	if #damages >= 3 then
		header
			:tag('th')
				:wikitext(cfg.part.legs)
			:tag('th')
				:wikitext(cfg.part.body)
	else
		header:tag('th')
			:wikitext(cfg.part.bodylegs)
	end
	header:tag('th')
		:wikitext(cfg.part.head)
	
	-- Normal
	if hasMultiple then
		local normalDPS0 = getDPSs(damages, 1, pellet, firerate[1], useRound)
		local normalDPS1 = getDPSs(damages, 1, pellet, firerate[2], useRound)
		local normalDPS2 = getDPSs(damages, 1, pellet, firerate[3], useRound)
		local normalDPS3 = getDPSs(damages, 1, pellet, firerate[4], useRound)
		renderDPSRow(tbl, cfg.perk.normal, cfg.complex, normalDPS0, defopts)
		renderDPSRow(tbl, iu.attachment('ショットガンボルト', 1), cfg.complex, normalDPS1, muopts)
		renderDPSRow(tbl, iu.attachment('ショットガンボルト', 2), cfg.complex, normalDPS2, muopts)
		renderDPSRow(tbl, iu.attachment('ショットガンボルト', 3), cfg.complex, normalDPS3, muopts)
	else
		local normalDPS = getDPSs(damages, 1, pellet, firerate, useRound)
		renderDPSRow(tbl, cfg.perk.normal, cfg.complex, normalDPS, defopts)
	end
	
	--- Extra
	if hasExtra then
		local extraDPS = getDPSs(damagesE, 1, pellet, firerate, useRound)
		renderDPSRow(tbl, labelE, cfg.complex, extraDPS, exopts)
	end
		
	--- Hop-up
	if hasHopup then
		local hopupDPS = getDPSs(damages2, 1, pellet2, firerate2, useRound)
		renderDPSRow(tbl, label, cfg.complex, hopupDPS, huopts)
		
		---- Extra
		if hasTempo and hasAmped then
			local tempoExtraDPS = getDPSs(damagesE, 1, pellet2, firerate2, useRound)
			renderDPSRow(tbl, labelE, cfg.complex, tempoExtraDPS, ex2opts)
		elseif hasShat and hasChgd then
			local shatChargedDPS = getDPSs(damages2E, 1, pellet2, firerate2, useRound)
			renderDPSRow(tbl, labelE, cfg.complex, shatChargedDPS, ex2opts)
		end
	end
	
	-- Low Profile
	if aw.isNumber(shieldinfo.lowprofile) and shieldinfo.lowprofile > 1 then
		if hasMultiple then
			local normalDPS0 = getDPSs(damages, shieldinfo.lowprofile, pellet, firerate[1], useRound)
			local normalDPS1 = getDPSs(damages, shieldinfo.lowprofile, pellet, firerate[2], useRound)
			local normalDPS2 = getDPSs(damages, shieldinfo.lowprofile, pellet, firerate[3], useRound)
			local normalDPS3 = getDPSs(damages, shieldinfo.lowprofile, pellet, firerate[4], useRound)
			renderDPSRow(tbl, cfg.perk.lowprofile, cfg.complex, normalDPS0, defopts)
			renderDPSRow(tbl, iu.attachment('ショットガンボルト', 1), cfg.complex, normalDPS1, muopts)
			renderDPSRow(tbl, iu.attachment('ショットガンボルト', 2), cfg.complex, normalDPS2, muopts)
			renderDPSRow(tbl, iu.attachment('ショットガンボルト', 3), cfg.complex, normalDPS3, muopts)
		else
			local lowprofileDPS = getDPSs(damages, shieldinfo.lowprofile, pellet, firerate, useRound)
			renderDPSRow(tbl, cfg.perk.lowprofile, cfg.complex, lowprofileDPS, defopts)
		end
		
		--- Extra
		if hasExtra then
			local extraLowProfileDPS = getDPSs(damagesE, shieldinfo.lowprofile, pellet, firerate, useRound)
			renderDPSRow(tbl, labelE, cfg.complex, extraLowProfileDPS, exopts)
		end
	
		--- Hop-up
		if hasHopup then
			local hopupLowProfileDPS = getDPSs(damages2, shieldinfo.lowprofile, pellet2, firerate2, useRound)
			renderDPSRow(tbl, label, cfg.complex, hopupLowProfileDPS, huopts)
			
			---- Extra
			if hasTempo and hasAmped then
				local tempoAmpedLowProfileDPS = getDPSs(damagesE, shieldinfo.lowprofile, pellet2, firerate2, useRound)
				renderDPSRow(tbl, labelE, cfg.complex, tempoAmpedLowProfileDPS, ex2opts)
			elseif hasShat and hasChgd then
				local shatChargedLowProfileDPS = getDPSs(damages2E, shieldinfo.lowprofile, pellet2, firerate2, useRound)
				renderDPSRow(tbl, labelE, cfg.complex, shatChargedLowProfileDPS, ex2opts)
			end
		end
	end
	
	-- Fortified
	if aw.isNumber(shieldinfo.fortified) and shieldinfo.fortified < 1 then
		if hasMultiple then
			local normalDPS0 = getDPSs(damages, shieldinfo.fortified, pellet, firerate[1], useRound)
			local normalDPS1 = getDPSs(damages, shieldinfo.fortified, pellet, firerate[2], useRound)
			local normalDPS2 = getDPSs(damages, shieldinfo.fortified, pellet, firerate[3], useRound)
			local normalDPS3 = getDPSs(damages, shieldinfo.fortified, pellet, firerate[4], useRound)
			renderDPSRow(tbl, cfg.perk.fortified, cfg.complex, normalDPS0, defopts)
			renderDPSRow(tbl, iu.attachment('ショットガンボルト', 1), cfg.complex, normalDPS1, muopts)
			renderDPSRow(tbl, iu.attachment('ショットガンボルト', 2), cfg.complex, normalDPS2, muopts)
			renderDPSRow(tbl, iu.attachment('ショットガンボルト', 3), cfg.complex, normalDPS3, muopts)
		else
			local lowprofileDPS = getDPSs(damages, shieldinfo.fortified, pellet, firerate, useRound)
			renderDPSRow(tbl, cfg.perk.fortified, cfg.complex, lowprofileDPS, defopts)
		end
		
		--- Extra
		if hasExtra then
			local extraFortifiedDPS = getDPSs(damagesE, shieldinfo.fortified, pellet, firerate, useRound)
			renderDPSRow(tbl, labelE, cfg.complex, extraFortifiedDPS, exopts)
		end
	
		--- Hop-up
		if hasHopup then
			local hopupFortifiedDPS = getDPSs(damages2, shieldinfo.fortified, pellet2, firerate2, useRound)
			renderDPSRow(tbl, label, cfg.complex, hopupFortifiedDPS, huopts)
			
			---- Extra
			if hasTempo and hasAmped then
				local tempoAmpedFortifiedDPS = getDPSs(damagesE, shieldinfo.fortified, pellet2, firerate2, useRound)
				renderDPSRow(tbl, labelE, cfg.complex, tempoAmpedFortifiedDPS, ex2opts)
			elseif hasShat and hasChgd then
				local shatChargedFortifiedDPS = getDPSs(damages2E, shieldinfo.fortified, pellet2, firerate2, useRound)
				renderDPSRow(tbl, labelE, cfg.complex, shatChargedFortifiedDPS, ex2opts)
			end
		end
	end
	
	return tbl
end

function p._main(name, lang)
	lang = lang or 'ja'
	
	local stat = mw.loadData('Module:Stat/Weapon')[name]
	local sld  = require('Module:Stat/Shield')['removelowprofile']
	local node = p.renderDPS(stat, sld, lang)
	
	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(name .. iu.ammo(stat.ammo, { size = 40 }))
			:done()
		:tag('table')
			:tag('tr')
				:tag('th'):wikitext('DPS'):done()
				:tag('td'):done()
			:tag('tr')
				:tag('td'):attr('colspan', 2):node(node)
	return tostring(div)
end

return p