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

モジュール:StatTable/ShotTime

提供:Apex Data
2021年8月26日 (木) 16:18時点におけるMntone (トーク | 投稿記録)による版 (改造ローダーの計算を外部ライブラリーの関数に変更)
ナビゲーションに移動 検索に移動

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

local aw            = require('Module:Utility/Library')
local apex          = require('Module:Utility/ApexLibrary')
local iu            = require('Module:Utility/Image')
local tableutil     = require('Module:Utility/TableUtil/Apex')
local TTKCalculator = require('Module:Apex/TTKCalculator')
local Hopup         = mw.loadData('Module:Stat/Hopup')
local getArgs  -- lazily initialized

local configuration = {
	en = {
		auto         = 'Auto',
		burst        = 'Burst',
		duration     = 'Duration [sec]',
		graph        = 'Graph',
		hopup        = 'Hop-Up',
		maxcharge    = 'Max charge',
		mincharge    = 'No charge',
		single       = 'Single',
		weaponname   = 'Weapon name',
	},
	ja = {
		auto         = 'オート',
		burst        = 'バースト',
		duration     = '射撃継続時間 [秒]',
		graph        = 'グラフ',
		hopup        = 'ホップアップ',
		maxcharge    = '最大溜め',
		mincharge    = '溜めなし',
		single       = '単発',
		weaponname   = '武器名',
	},
}

local function calcBurstDuration(data, firerate, magazine, delay)
	local groupCount = math.floor(math.floor(magazine / data.ammopershot) / data.burstcount)
	local duration   = groupCount * ((data.burstcount - 1) / firerate + delay) - delay
	return duration
end

local function calcDurationBase(data, firerate, shotCount, rechamber)
	local duration = shotCount / firerate
	if data.raise > 0 then
		if data.semiauto then
			duration = duration + shotCount * data.raise
		else
			duration = duration + data.raise
		end
	end
	if data.charge > 0 then
		duration = duration + shotCount * data.charge
	end
	if rechamber > 0 then
		duration = duration + shotCount * rechamber
	end
	return duration
end

local function calcDuration(data, firerate, magazine, rechamber)
	local shotCount = math.floor(magazine / data.ammopershot) - 1
	if aw.isNumberAndGreaterThanOrEqualToX(data.tempomaxcount, 1) and shotCount > data.tempomaxcount and aw.isNumberAndGreaterThanZero(data.temporechamber) then
		local duration = calcDurationBase(data, firerate, data.tempomaxcount, rechamber)
		duration = duration + calcDurationBase(data, firerate, shotCount - data.tempomaxcount, data.temporechamber)
		return duration
	else
		return calcDurationBase(data, firerate, shotCount, rechamber)
	end
end

local function addFlatData(dataset, data, special)
	local fireratesType  = type(data.firerates)
	local magazinesType  = type(data.magazines)
	local rechambersType = type(data.rechambers)
	
	-- バーストの場合
	if aw.isNumberAndGreaterThanOrEqualToX(data.burstcount, 2) and fireratesType == 'number' then
		local delaysType = type(data.burstdelay)
		
		-- ショットガンボルトがつく場合(バースト遅延が可変)
		if delaysType == 'table' then
			if special then
				data.duration = calcBurstDuration(data, data.firerates, data.magazines, data.burstdelay[4])
				table.insert(dataset, data)
			else
				for level, delay in ipairs(data.burstdelay) do
					local data2    = aw.shallowCopy(data)
					data2.duration = calcBurstDuration(data, data.firerates, data.magazines, delay)
					data2.level    = level - 1
					table.insert(dataset, data2)
				end
			end
		
		elseif delaysType == 'number' and data.burstdelay > 0 then
			-- 拡張マガジンがつく場合
			if magazinesType == 'table' then
				if special then
					data.duration = calcBurstDuration(data, data.firerates, data.magazines[4], data.burstdelay)
					table.insert(dataset, data)
				else
					for level, magazine in ipairs(data.magazines) do
						local data2    = aw.shallowCopy(data)
						data2.duration = calcBurstDuration(data, data.firerates, magazine, data.burstdelay)
						data2.level    = level - 1
						table.insert(dataset, data2)
					end
				end
			else
				data.duration = calcBurstDuration(data, data.firerates, data.magazines, data.burstdelay)
				table.insert(dataset, data)
			end
		end
	
	-- ショットガンボルトがつく場合 (ファイアレートが可変)
	elseif fireratesType == 'table' and magazinesType == 'number' then
		if special then
			data.duration = calcDuration(data, data.firerates[4], data.magazines, data.rechambers)
			table.insert(dataset, data)
		else
			for level, firerate in ipairs(data.firerates) do
				local data2    = aw.shallowCopy(data)
				data2.duration = calcDuration(data, firerate, data.magazines, data.rechambers)
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
		end
	
	-- ショットガンボルトがつく場合 (リチャンバーが可変)
	elseif fireratesType == 'number' and magazinesType == 'number' and rechambersType == 'table' then
		if special then
			data.duration = calcDuration(data, data.firerates, data.magazines, data.rechambers[4])
			table.insert(dataset, data)
		else
			for level, rechamber in ipairs(data.rechambers) do
				local data2    = aw.shallowCopy(data)
				data2.duration = calcDuration(data, data.firerates, data.magazines, rechamber)
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
		end
	
	-- 拡張マガジンがつく場合
	elseif fireratesType == 'number' and magazinesType == 'table' then
		if special then
			data.duration = calcDuration(data, data.firerates, data.magazines[4], data.rechambers)
			table.insert(dataset, data)
		else
			for level, magazine in ipairs(data.magazines) do
				local data2    = aw.shallowCopy(data)
				data2.duration = calcDuration(data, data.firerates, magazine, data.rechambers)
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
		end
	
	-- 拡張マガジンとショットガンボルトの両方ともがつかない場合
	elseif fireratesType == 'number' and magazinesType == 'number' then
		data.duration = calcDuration(data, data.firerates, data.magazines, data.rechambers)
		table.insert(dataset, data)
	end
end

local function createDataset(dataset, stats, res, addData, __debug)
	for name, stat in pairs(stats) do
		local baseData = {
			ammo        = stat.ammo,
			ammoicon    = stat.ammo,
			ammopershot = stat.ammo_per_shot or 1,
			charge      = -1,
			duration    = -1,
			firerates   = stat.firerate,
			level       = -1,
			magazines   = stat.magazine,
			name        = name,
			semiauto    = stat.is_semi_auto,
			raise       = stat.raise or 0,
			rechambers  = stat.rechamber or 0,
			shortname   = stat.localization['Japanese_Short'],
		}
		local special  = aw.stringstarts(stat.ammo, 'special_')
		
		-- Use TTKCalculator
		if name == 'ディヴォーションLMG' then
			local ttkc = TTKCalculator.newFromStat(stat)
			for level, magazine in ipairs(stat.magazine) do
				local data2    = aw.shallowCopy(baseData)
				data2.duration = ttkc:getAsLevel(magazine, level - 1)
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
			
			ttkc = TTKCalculator.newFromStat(stat, { useTurbocharger = true })
			for level, magazine in ipairs(stat.magazine) do
				local data2    = aw.shallowCopy(baseData)
				data2.duration = ttkc:getAsLevel(magazine, level - 1)
				data2.hopup    = { 'turbocharger' }
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
			
			local moddedLoaderMagazines = apex.getMagazineWithModdedLoader(stat.magazine)
			ttkc = TTKCalculator.newFromStat(stat, { useModdedLoader = true })
			for level, magazine in ipairs(moddedLoaderMagazines) do
				local data2    = aw.shallowCopy(baseData)
				data2.duration = ttkc:getAsLevel(magazine, level - 1)
				data2.hopup    = { 'modded_loader' }
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
			
			ttkc = TTKCalculator.newFromStat(stat, { useTurbocharger = true, useModdedLoader = true })
			for level, magazine in ipairs(moddedLoaderMagazines) do
				local data2    = aw.shallowCopy(baseData)
				data2.duration = ttkc:getAsLevel(magazine, level - 1)
				data2.hopup    = { 'turbocharger', 'modded_loader' }
				data2.level    = level - 1
				table.insert(dataset, data2)
			end
		
		-- Mag is inf
		elseif stat.magazine == math.huge then
			local overheatsType = type(stat.overheat)
			if overheatsType == 'table' then
				local virtualMagazines = {
					math.ceil(stat.overheat[1] * stat.firerate),
					math.ceil(stat.overheat[2] * stat.firerate),
					math.ceil(stat.overheat[3] * stat.firerate),
					math.ceil(stat.overheat[4] * stat.firerate),
				}
				baseData.magazines = virtualMagazines
				addData(dataset, baseData, special)
				
				-- Modded Loader
				if stat.category == 'light_machine_gun' then
					local moddedLoaderVirtualMagazines  = {
						math.ceil(1.15 * stat.overheat[1] * stat.firerate),
						math.ceil(1.15 * stat.overheat[2] * stat.firerate),
						math.ceil(1.15 * stat.overheat[3] * stat.firerate),
						math.ceil(1.15 * stat.overheat[4] * stat.firerate),
					}
					local moddedLoaderData = aw.shallowCopy(baseData)
					moddedLoaderData.hopup = { 'modded_loader' }
					moddedLoaderData.magazines = moddedLoaderVirtualMagazines
					addData(dataset, moddedLoaderData, special)
				end
				
			elseif overheatsType == 'number' then
				baseData.magazines = math.ceil(stat.overheat * stat.firerate)
				addData(dataset, baseData, special)
				
				-- Modded Loader
				if stat.category == 'light_machine_gun' then
					local moddedLoaderData = aw.shallowCopy(baseData)
					moddedLoaderData.hopup = { 'modded_loader' }
					moddedLoaderData.magazines = math.ceil(1.15 * stat.overheat * stat.firerate)
					addData(dataset, moddedLoaderData, special)
				end
			end
		
		-- Other
		else
			-- Altfire mode
			if type(stat.altfire) == 'table' and aw.isNumberAndGreaterThanZero(stat.altfire.firerate) then
				local burstfireData      = aw.shallowCopy(baseData)
				burstfireData.burstcount = stat.burst_count
				burstfireData.burstdelay = stat.burst_delay
				burstfireData.option     = res.burst
				addData(dataset, burstfireData, special)
				
				local altfireData     = aw.shallowCopy(baseData)
				altfireData.firerates = stat.altfire.firerate
				altfireData.semiauto  = stat.altfire.is_semi_auto == nil and stat.is_semi_auto or stat.altfire.is_semi_auto
				altfireData.option    = altfireData.semiauto and res.single or res.auto
				addData(dataset, altfireData, special)
			
			-- Burst mode
			elseif aw.isNumberAndGreaterThanOrEqualToX(stat.burst_count, 2) then
				local burstfireData      = aw.shallowCopy(baseData)
				burstfireData.burstcount = stat.burst_count
				burstfireData.burstdelay = stat.burst_delay
				addData(dataset, burstfireData, special)
			
			-- Chargefire
			elseif aw.isNumberAndGreaterThanZero(stat.charge) then
				baseData.option = res.mincharge
				addData(dataset, baseData, special)
				
				local maxData  = aw.shallowCopy(baseData)
				maxData.charge = stat.charge
				maxData.option = res.maxcharge
				addData(dataset, maxData, special)
			
			-- Charge Rifle
			elseif aw.isNumberAndGreaterThanZero(stat.sustained_discharge_duration) then
				baseData.raise = stat.sustained_discharge_duration
				addData(dataset, baseData, special)
			
			-- Other
			else
				addData(dataset, baseData, special)
			end
			
			-- Modded Loader
			if stat.category == 'light_machine_gun' or stat.ammo == 'minigun' then
				local moddedLoaderData     = aw.shallowCopy(baseData)
				moddedLoaderData.hopup     = { 'modded_loader' }
				moddedLoaderData.magazines = apex.getMagazineWithModdedLoader(stat.magazine)
				addData(dataset, moddedLoaderData, special)
			
				-- Revved Up mode
				if aw.isNumberAndGreaterThanOrEqualToX(stat.firerate_revvedup, stat.firerate) then
					local label = iu.grenade('テルミットグレネード') .. ' <span class="text-desktoponly">連射速度上昇モード</span>'
					
					local revvedUpData     = aw.shallowCopy(baseData)
					revvedUpData.ammoicon  = stat.ammo .. '_revved_up'
					revvedUpData.firerates = stat.firerate_revvedup
					revvedUpData.option    = label
					addData(dataset, revvedUpData, special)
					
					local moddedLoaderRevvedUpData     = aw.shallowCopy(moddedLoaderData)
					moddedLoaderRevvedUpData.ammoicon  = stat.ammo .. '_revved_up'
					moddedLoaderRevvedUpData.firerates = stat.firerate_revvedup
					moddedLoaderRevvedUpData.option    = label
					addData(dataset, moddedLoaderRevvedUpData, special)
				end
			end
			
			-- Turbocharger
			if (__debug or Hopup.turbocharger.enabled) and type(stat.turbocharger) == 'table' and aw.isNumberAndGreaterThanZero(stat.turbocharger.raise) then
				local turbochargerData = aw.shallowCopy(baseData)
				turbochargerData.hopup = { 'turbocharger' }
				turbochargerData.raise = stat.turbocharger.raise or stat.raise or 0
				addData(dataset, turbochargerData, special)
			end
			
			-- Selectfire Receiver
			if (__debug or Hopup.selectfire_receiver.enabled) and type(stat.selectfire_receiver) == 'table' and aw.isNumberAndGreaterThanZero(stat.selectfire_receiver.firerate) then
				local semiauto
				if stat.selectfire_receiver.semiauto then
					semiauto = stat.selectfire_receiver.semiauto
				else
					semiauto = stat.semiauto
				end
				
				local selectfireReceiverData       = aw.shallowCopy(baseData)
				selectfireReceiverData.ammopershot = stat.selectfire_receiver.ammo_per_shot or stat.ammo_per_shot or 1
				selectfireReceiverData.firerates   = stat.selectfire_receiver.firerate
				selectfireReceiverData.hopup       = { 'selectfire_receiver' }
				selectfireReceiverData.raise       = stat.selectfire_receiver.raise or stat.raise or 0
				selectfireReceiverData.semiauto    = semiauto
				addData(dataset, selectfireReceiverData, special)
			end
				
			-- Anvil Receiver
			if (__debug or Hopup.anvil_receiver.enabled) and type(stat.anvil_receiver) == 'table' and aw.isNumberAndGreaterThanZero(stat.anvil_receiver.firerate) then
				local anvilReceiverData       = aw.shallowCopy(baseData)
				anvilReceiverData.ammopershot = stat.anvil_receiver.ammo_per_shot or 2
				anvilReceiverData.firerates   = stat.anvil_receiver.firerate
				anvilReceiverData.hopup       = { 'anvil_receiver' }
				addData(dataset, anvilReceiverData, special)
			end
				
			-- Double Tap Trigger
			if (__debug or Hopup.double_tap_trigger.enabled) and type(stat.double_tap_trigger) == 'table' and aw.isNumberAndGreaterThanOrEqualToX(stat.double_tap_trigger.burst_count, 2) then
				local doubleTapTriggerData = aw.shallowCopy(baseData)
				doubleTapTriggerData.burstcount = stat.double_tap_trigger.burst_count
				doubleTapTriggerData.burstdelay = stat.double_tap_trigger.burst_delay
				doubleTapTriggerData.firerates  = stat.double_tap_trigger.firerate
				doubleTapTriggerData.hopup      = { 'double_tap_trigger' }
				addData(dataset, doubleTapTriggerData, special)
			end
				
			-- Deadeye's Tempo
			if (__debug or Hopup.deadeyes_tempo.enabled) and type(stat.deadeyes_tempo) == 'table' and aw.isNumberAndGreaterThanZero(stat.deadeyes_tempo.rechamber) then
				local deadeyesTempoData          = aw.shallowCopy(baseData)
				deadeyesTempoData.hopup          = { 'deadeyes_tempo' }
				deadeyesTempoData.tempomaxcount  = stat.deadeyes_tempo.firerate_maximum_count
				deadeyesTempoData.temporechamber = stat.deadeyes_tempo.rechamber
				addData(dataset, deadeyesTempoData, special)
			end
				
			-- Boosted Loader
			if (__debug or Hopup.boosted_loader.enabled) and type(stat.boosted_loader) == 'table' then
				if type(stat.altfire) == 'table' and aw.isNumberAndGreaterThanZero(stat.altfire.firerate) then
					local boostedLoaderData      = aw.shallowCopy(baseData)
					boostedLoaderData.burstcount = stat.burst_count
					boostedLoaderData.burstdelay = stat.burst_delay
					boostedLoaderData.hopup      = { 'boosted_loader' }
					boostedLoaderData.magazines  = stat.boosted_loader.magazine
					boostedLoaderData.option     = res.burst
					addData(dataset, boostedLoaderData, special)
					
					local altfireBoostedLoaderData     = aw.shallowCopy(baseData)
					altfireBoostedLoaderData.firerates = stat.altfire.firerate
					altfireBoostedLoaderData.hopup     = { 'boosted_loader' }
					altfireBoostedLoaderData.magazines = stat.boosted_loader.magazine
					altfireBoostedLoaderData.semiauto  = stat.altfire.is_semi_auto == nil and stat.is_semi_auto or stat.altfire.is_semi_auto
					altfireBoostedLoaderData.option    = altfireBoostedLoaderData.semiauto and res.single or res.auto
					addData(dataset, altfireBoostedLoaderData, special)
				else
					local boostedLoaderData     = aw.shallowCopy(baseData)
					boostedLoaderData.hopup     = { 'boosted_loader' }
					boostedLoaderData.magazines = stat.boosted_loader.magazine
					addData(dataset, boostedLoaderData, special)
				end
			end
		end
	end
end

local p = {}
function p._main(args, __debug)
	args = args or {}
	if __debug == nil then
		__debug = true
	end
	
	local lang  = args.lang or 'ja'
	local res   = configuration[lang]
	local stats = mw.loadData('Module:Stat/Weapon')
	
	local dataset = {}
	createDataset(dataset, stats, res, addFlatData, __debug)
	
	local metadata = {
		tableutil.RankCell.new('', 'duration', true),
		tableutil.AmmoCell.new('', 'ammoicon', {
			attributes  = { align = 'center' },
			cellClass   = 'st-desktop',
			footerClass = 'st-desktop',
			headerClass = 'st-desktop',
			headerStyle = { width = '36px' },
		}),
		tableutil.WeaponNameCell.new(res.weaponname, 'name', {
			ammoIndex      = 'ammo',
			attributes     = { align = 'left' },
			cellClass      = 'st-dashed-right st-mobile-none-right st-mobile-padding0-right',
			headerColspan  = 3,
			optionIndex    = 'option',
			shortnameIndex = 'shortname',
		}),
		tableutil.MagazineCell.new(res.mag, 'level', {
			ammoIndex      = 'ammo',
			attributes     = { align = 'left' },
			cellClass      = 'st-dashed-both st-mobile-none-both st-mobile-padding0-both',
			headerColspan  = 0,
		}),
		tableutil.HopupCell.new(res.hopup, 'hopup', {
			attributes     = { align = 'left' },
			cellClass      = 'st-dashed-left st-mobile-none-left st-mobile-padding0-both',
			headerColspan  = 0,
		}),
		tableutil.LineValueCell.new(res.duration, 'duration', {
			cellClass   = 'st-mobile-graph st-mobile-textshadow st-mobile-last',
			footerClass = 'st-mobile-axis st-mobile-last',
			format      = function(val)
				if val >= 10 then
					return string.format('%.2f', val)
				else
					return string.format('%.3f', val)
				end
			end,
			headerClass = 'st-mobile-last',
			headerStyle = {
				['min-width'] = '80px',
				['max-width'] = '8em',
			},
			preferredGridCount = 3,
		}),
		tableutil.LineCell.new(res.graph, 'duration', {
			cellClass   = 'st-desktop st-desktop-graph',
			footerClass = 'st-desktop',
			headerClass = 'st-desktop',
			headerStyle = {
				['min-width'] = '200px',
			},
		}),
	}
	local node = tableutil.createTable(dataset, metadata)
	return tostring(node)
	--mw.logObject(dataset)
	--return dataset
end

function p.main(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	args = getArgs(frame)
	
	return p._main(args, false)
end

return p