| 🌟 | 現在、 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
モジュール:MultipleStatTable
ナビゲーションに移動
検索に移動
このモジュールについての説明文ページを モジュール: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,
'(.*)' .. shortname .. '(.*)',
'<span class="text-desktoponly">%1</span>' .. shortname .. '<span class="text-desktoponly">%2</span>')
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 getTTKs(stat, mode, opts, health, shield, STKCalculator, TTKCalculator)
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()
local ttkc = TTKCalculator.newFromStat(stat, mode, opts)
return {
{
ttkc:getAsLevel(stkNormal, 0),
ttkc:getAsLevel(stkNormal, 1),
ttkc:getAsLevel(stkNormal, 2),
ttkc:getAsLevel(stkNormal, 3),
},
{
ttkc:getAsLevel(stkLowProfile, 0),
ttkc:getAsLevel(stkLowProfile, 1),
ttkc:getAsLevel(stkLowProfile, 2),
ttkc:getAsLevel(stkLowProfile, 3),
},
{
ttkc:getAsLevel(stkFortified, 0),
ttkc:getAsLevel(stkFortified, 1),
ttkc:getAsLevel(stkFortified, 2),
ttkc:getAsLevel(stkFortified, 3),
},
}
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])
end
local function compareTTKs(ttks1, ttks2)
for passive = 1, 3 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)
return {
ttks[1][level],
ttks[2][level],
ttks[3][level],
}
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'])
if stat.mode.burst > 1 then
if stat.mode.auto then
local ttks = getTTKs(stat, TTKCalculator.AUTO, opts, args.health, args.shield, STKCalculator, TTKCalculator)
addFromTTKs(ammo, data, ammoname, basename .. ' (オート)', '', ttks)
else
local ttks = getTTKs(stat, TTKCalculator.SINGLE, opts, args.health, args.shield, STKCalculator, TTKCalculator)
addFromTTKs(ammo, data, ammoname, basename .. ' (単発)', '', ttks)
end
local ttks = getTTKs(stat, TTKCalculator.BURST, opts, args.health, args.shield, STKCalculator, TTKCalculator)
addFromTTKs(ammo, data, ammoname, basename .. ' (バースト)', '', ttks)
else
local mode = stat.mode.auto and TTKCalculator.AUTO or TTKCalculator.SINGLE
local ttks = getTTKs(stat, mode, opts, args.health, args.shield, STKCalculator, 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, TTKCalculator.AUTO, opts2, args.health, args.shield, STKCalculator, TTKCalculator)
if not compareTTKs(ttks, ttks2) then
addFromTTKs(ammo, data, ammoname, basename, getModdedLoader(), ttks2)
end
end
-- センチネルの増幅モード
if aw.isNumberAndGreaterThanZero(stat.damage.amped) then
local opts2 = aw.shallowCopy(opts)
opts2.useAmpedMode = true
local ttks2 = getTTKs(stat, TTKCalculator.SINGLE, opts2, args.health, args.shield, STKCalculator, 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.useAmpedMode = true
local ttks2 = getTTKs(stat, TTKCalculator.SINGLE, opts2, args.health, args.shield, STKCalculator, 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 ttks = getTTKs(stat, TTKCalculator.SINGLE, opts2, args.health, args.shield, STKCalculator, TTKCalculator)
addFromTTKs(ammo, data, ammoname, basename, getHammerpointRounds(), ttks)
end
-- アンビルレシーバー (VK-47フラットライン・R-301カービン)
if stat.damage.anvil_receiver then
local opts2 = aw.shallowCopy(opts)
opts2.useAnvilReceiver = true
local ttks = getTTKs(stat, TTKCalculator.SINGLE_ANVIL_RECEIVER, opts2, args.health, args.shield, STKCalculator, TTKCalculator)
addFromTTKs(ammo, data, ammoname, basename, getAnvilReceiver(), ttks)
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, TTKCalculator.AUTO, opts2, args.health, args.shield, STKCalculator, TTKCalculator)
addFromTTKs(ammo, data, ammoname, basename, getTurbocharger(), ttks)
-- ライトマシンガン (ディヴォーションLMG)
if stat.category == 'light_machine_gun' then
local opts3 = aw.shallowCopy(opts2)
opts3.useModdedLoader = true
local ttks3 = getTTKs(stat, TTKCalculator.AUTO, opts3, args.health, args.shield, STKCalculator, 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