🌟 | 現在、 鉄壁ヘッドショットには対応済みです。 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:WeaponInfobox」の版間の差分
ナビゲーションに移動
検索に移動
(種類へのリンクを追加) |
(アンビルレシーバーが出現しない場合にアンビルレシーバー用の拡散を非表示するように対処) |
||
(同じ利用者による、間の274版が非表示) | |||
1行目: | 1行目: | ||
require('Module:Utility/mw.html Extensions') | |||
local p = {} | local p = {} | ||
local cfg = mw.loadData('Module:WeaponInfobox/configuration') | |||
local Hopup = mw.loadData('Module:Stat/Hopup') | |||
local | local aw = require('Module:Utility/Library') | ||
local apex = require('Module:Utility/ApexLibrary') | |||
local iu = require('Module:Utility/Image') | |||
local inu = require('Module:Utility/ImageWithName') | |||
local nu = require('Module:Utility/Name') | |||
local proto = require('Module:Utility/Prototypes') | |||
local StringBuilder = require('Module:Utility/StringBuilder') | |||
local formatter -- lazily initialized | local formatter -- lazily initialized | ||
local getArgs -- lazily initialized | local getArgs -- lazily initialized | ||
local function | local function createCellInRow(tbl, name) | ||
return tbl:tag('tr') | |||
:tag('th') | |||
:wikitext(name) | |||
:done() | |||
:tag('td') | |||
end | end | ||
local function renderRow(tbl, name, item) | |||
createCellInRow(tbl, name):wikitext(item) | |||
local function | |||
end | end | ||
local | local function renderFormatRow(tbl, name, default, common, rare, epic, opts) | ||
opts = opts or {} | |||
opts.separator = opts.separator or ' - ' | |||
local row = tbl:tag('tr') | |||
:addClassIf(type(opts.class) == 'string', opts.class) | |||
:addClassIf(type(opts.rarity) == 'string', 'row-rarity-one disp-rarity-' .. (opts.rarity or 'common')) | |||
if name ~= nil then | |||
row:tag('th') | |||
:attrIf(opts.headerAlign and type(opts.headerAlign) == 'string', { align = opts.headerAlign }) | |||
:wikitext(name) | |||
if | |||
end | end | ||
local | local first = true | ||
if default ~= nil then | |||
first = false | |||
row:tag('td') | |||
if | :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 }) | |||
:tag('span') | |||
:addClass('text-rarity') | |||
:addClass('text-rarity-common') | |||
:wikitext(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 }) | |||
:tag('span') | |||
:addClass('text-rarity') | |||
:addClass('text-rarity-rare') | |||
:wikitext(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 }) | |||
:tag('span') | |||
:addClass('text-rarity') | |||
:addClass('text-rarity-epic') | |||
:wikitext(epic) | |||
end | end | ||
if | 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, lang) | |||
local category = nu.type(stat, lang, 1) | |||
local item | |||
if lang == 'ja' then | |||
item = string.format('[[武器#%s|%s]][[Category:%s]]', category, category, category) | |||
else | else | ||
item = string.format('%s[[Category:%s]]', category, category) | |||
end | end | ||
renderRow(tbl, cfg.name, item) | |||
end | end | ||
local function | local function renderAmmo(tbl, cfg, stat, lang) | ||
local | createCellInRow(tbl, cfg.name) | ||
if stat. | :addClass('cell-ammo') | ||
:wikitext(inu.ammo(stat, { lang = lang, size = 24 })) | |||
end | |||
local function renderCost(tbl, cfg, stat, ammo) | |||
if stat == nil or aw.stringstarts(ammo, 'special_') then | |||
return | |||
end | end | ||
local text = formatter:format( | |||
if | aw.comma(stat[1]), | ||
aw.comma(stat[2]), | |||
aw.comma(stat[3]), | |||
aw.comma(stat[4]), | |||
'', ' - ') | |||
renderRow(tbl, cfg.name, cfg.header .. text) | |||
end | |||
local function appendMode(builder, cfg, stat, base) | |||
if stat.is_semi_auto or (stat.is_semi_auto == nil and base.is_semi_auto) then | |||
local burstCount = stat.burst_count or (base and base.burst_count) or 1 | |||
if aw.isNumberAndGreaterThanOrEqualToX(burstCount, 2) then | |||
builder:appendFormat(cfg.burst, burstCount) | |||
builder:append(cfg.burst_category) | |||
else | else | ||
builder:append(cfg.single, cfg.single_category) | |||
end | end | ||
else | |||
builder:append(cfg.auto, cfg.auto_category) | |||
end | end | ||
end | |||
if stat. | |||
if | local function renderMode(tbl, cfg, stat, isSpecial) | ||
local builder = StringBuilder.new() | |||
appendMode(builder, cfg, stat) | |||
if stat.altfire then | |||
builder:append(cfg.separator) | |||
appendMode(builder, cfg, stat.altfire, stat) | |||
end | |||
if (Hopup.selectfire_receiver.enabled or isSpecial) and stat.selectfire_receiver then | |||
builder:append(cfg.separator, '<span class="block-rarity disp-rarity-epic">', iu.hopup('selectfire_receiver'), ' ') | |||
appendMode(builder, cfg, stat.selectfire_receiver, stat) | |||
builder:append('</span>') | |||
end | |||
if (Hopup.double_tap_trigger.enabled or isSpecial) and stat.double_tap_trigger then | |||
builder:append(cfg.separator, '<span class="block-rarity disp-rarity-epic">', iu.hopup('double_tap_trigger'), ' ') | |||
appendMode(builder, cfg, stat.double_tap_trigger, stat) | |||
builder:append('</span>') | |||
end | |||
renderRow(tbl, cfg.name, tostring(builder)) | |||
end | |||
local HammerpointRoundsProto = { | |||
hammerpoint_rounds = { | |||
damage_unshielded_scale = proto.NumberRange(1), | |||
}, | |||
} | |||
local DisruptorRoundsProto = { | |||
disruptor_rounds = { | |||
damage_shield_scale = proto.NumberRange(1), | |||
}, | |||
} | |||
local function getDamageText(stat, root, isSpecial) | |||
local builder = StringBuilder.new() | |||
local rarity = isSpecial and 'heirloom' | |||
local damage, pellet | |||
if root then | |||
damage = stat.damage or root.damage | |||
pellet = stat.pellet or root.pellet or 1 | |||
else | |||
damage = stat.damage | |||
pellet = stat.pellet or 1 | |||
end | |||
if aw.isNumberAndGreaterThanOrEqualToX(pellet, 2) then | |||
builder:appendFormat( | |||
'<span class="text-type-number">%s</span> × %d', | |||
damage.base, | |||
pellet) | |||
if aw.isNumberAndGreaterThanZero(damage.charged) then | |||
builder:appendFormat( | |||
' → <span class="text-type-number">%s</span> × %d', | |||
damage.charged, | |||
pellet) | |||
elseif proto.validateTypes(stat, HammerpointRoundsProto) then | |||
if not rarity then | |||
rarity = Hopup.hammerpoint_rounds.rarity | |||
end | |||
builder:appendFormat( | |||
' → <span class="block-rarity disp-rarity-%s text-rarity text-rarity-%s">%s \'\'\'<span class="text-type-number">%s</span> × %d\'\'\'</span>', | |||
rarity, | |||
rarity, | |||
iu.hopup('hammerpoint_rounds', { rarity = rarity }), | |||
math.floor(damage.base * stat.hammerpoint_rounds.damage_unshielded_scale), | |||
pellet) | |||
end | |||
else | |||
if aw.isNumberAndGreaterThanZero(damage.amped) then | |||
builder:appendFormat( | |||
'<span class="text-type-number">%s</span> <span class="text-separator">/</span> %s <span class="text-type-number">%s</span>', | |||
damage.base, | |||
iu.item('シールドセル'), | |||
damage.amped) | |||
elseif aw.isNumberAndGreaterThanZero(damage.charged) then | |||
builder:appendFormat( | |||
'<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>', | |||
damage.base, | |||
damage.charged) | |||
elseif proto.validateTypes(stat, HammerpointRoundsProto) then | |||
if not rarity then | |||
rarity = Hopup.hammerpoint_rounds.rarity | |||
end | |||
builder:appendFormat( | |||
'<span class="text-type-number">%s</span> → <span class="block-rarity disp-rarity-%s text-rarity text-rarity-%s">%s \'\'\'<span class="text-type-number">%s</span>\'\'\'</span>', | |||
damage.base, | |||
rarity, | |||
rarity, | |||
iu.hopup('hammerpoint_rounds', { rarity = rarity }), | |||
math.floor(damage.base * stat.hammerpoint_rounds.damage_unshielded_scale)) | |||
elseif proto.validateTypes(stat, DisruptorRoundsProto) then | |||
if not rarity then | |||
rarity = Hopup.disruptor_rounds.rarity | |||
end | |||
builder:appendFormat( | |||
'<span class="block-rarity disp-rarity-%s text-rarity text-rarity-%s">%s \'\'\'<span class="text-type-number">%s</span>\'\'\'</span> → <span class="text-type-number">%s</span>', | |||
rarity, | |||
rarity, | |||
iu.hopup('disruptor_rounds', { rarity = rarity }), | |||
math.floor(damage.base * stat.disruptor_rounds.damage_shield_scale), | |||
damage.base) | |||
else | else | ||
builder:appendFormat('<span class="text-type-number">%s</span>', damage.base) | |||
end | end | ||
end | end | ||
return tostring(builder) | |||
end | |||
local function renderHeadRow(tbl, name, head, cfg, nolist) | |||
local opts = { align = 'left', footer = cfg.unit } | |||
if nolist then | |||
opts.class = 'no-list-style' | |||
opts.headerAlign = 'right' | |||
end | |||
renderFormatRow( | |||
tbl, | |||
name, | |||
nil, | |||
apex.calcHeadshotMultiplier(head, apex.HEAD_HLMLV1), | |||
apex.calcHeadshotMultiplier(head, apex.HEAD_HLMLV2), | |||
apex.calcHeadshotMultiplier(head, apex.HEAD_HLMLV3), | |||
opts) | |||
end | |||
local function renderHeadEffectiveRange(tbl, cfg, stat, root) | |||
local headDist = stat.damage_head_distance or root.damage_head_distance | |||
if aw.isNumberAndGreaterThanZero(headDist) then | |||
local text = string.format(cfg.head_effective_range, 0.0254 * headDist) | |||
tbl:tag('tr') | |||
:addClass('no-list-style') | |||
:tag('td') | |||
:attr('align', 'left') | |||
:attr('colspan', 7) | |||
:wikitext(text) | |||
end | |||
end | end | ||
local function | local function renderLegsRow(tbl, cfg, stat, root) | ||
local | local legs = stat.damage_legs_scale or root.damage_legs_scale | ||
if | local text | ||
if aw.isNumberAndGreaterThanZero(stat.legshot_charged) then | |||
text = string.format( | |||
'<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>%s', | |||
legs, | |||
stat.legshot_charged, | |||
cfg.unit) | |||
else | else | ||
text = string.format( | |||
'<span class="text-type-number">%s</span>%s', | |||
legs, | |||
cfg.unit) | |||
end | end | ||
tbl:tag('tr') | |||
:tag('th') | |||
:wikitext(cfg.legs) | |||
:done() | |||
:tag('td') | |||
:attr('align', 'left') | |||
:attr('colspan', 6) | |||
:wikitext(text) | |||
end | |||
local DamageProto = { | |||
damage = { | |||
base = proto.NumberRange(1), | |||
}, | |||
damage_head_scale = proto.NumberRange(1), | |||
damage_legs_scale = proto.NumberRange(0, 1), | |||
} | |||
local ShatterCapsProto = { | |||
damage = { | |||
base = proto.NumberRange(1), | |||
}, | |||
damage_head_scale = proto.NumberRange(1), | |||
damage_legs_scale = proto.NumberRange(0, 1), | |||
pellet = proto.NumberRange(1), | |||
} | |||
local damageHopups = { | |||
{ | |||
name = 'selectfire_receiver', | |||
proto = DamageProto, | |||
textclass = 'text-rarity-epic', | |||
dispclass = 'disp-rarity-epic', | |||
}, | |||
{ | |||
name = 'anvil_receiver', | |||
proto = DamageProto, | |||
textclass = 'text-rarity-legendary', | |||
dispclass = 'disp-rarity-legendary', | |||
}, | |||
{ | |||
name = 'shatter_caps', | |||
proto = ShatterCapsProto, | |||
textclass = 'text-rarity-epic', | |||
dispclass = 'disp-rarity-epic', | |||
}, | |||
} | |||
local function renderDamage(tbl, cfg, stat, isSpecial) | |||
if not proto.validateTypes(stat, DamageProto) then | |||
return | |||
end | |||
local cattext = string.format(cfg.head_category, stat.damage.headshot_charged or stat.damage_head_scale or 1) | |||
local cell = createCellInRow(tbl, cfg.name) | |||
cell:wikitext(cattext .. getDamageText(stat, nil, isSpecial)) | |||
-- Headshot | |||
local head = stat.damage_head_scale or 1 | |||
local intable = cell:tag('table') | |||
:addClass('condensedtable') | |||
:addClass('listtable') | |||
renderHeadRow(intable, cfg.head, head, cfg) | |||
local | local hlmc = stat.damage.headshot_charged or 1 | ||
if hlmc > 1 then | |||
local nameC = '→ ' | |||
renderHeadRow(intable, nameC, hlmc, cfg, true) | |||
end | |||
local | local sprm = stat.damage.skullpiercer_rifling or 1 | ||
if sprm > 1 then | if sprm > 1 then | ||
local | local nameS = iu.hopup('skullpiercer_rifling') .. ' ' | ||
renderHeadRow(intable, nameS, sprm, cfg, true) | |||
end | |||
renderHeadEffectiveRange(intable, cfg, stat, stat) | |||
local | -- Legsshot | ||
renderLegsRow(intable, cfg, stat, stat) | |||
-- [Hop-Up] Selectfire Receiver, Anvil Receiver & Shatter Caps | |||
for _, v in ipairs(damageHopups) do | |||
local hopupStat = stat[v.name] | |||
if (Hopup[v.name].enabled or isSpecial) and proto.validateTypes(hopupStat, v.proto) then | |||
local intblS = cell:tag('div') | |||
:addClass('tpl-weapon-inbox') | |||
:addClass(specialOnly and 'disp-rarity-heirloom' or v.dispclass) | |||
:wikitext(iu.hopup(v.name) .. ' ') | |||
:tag('span') | |||
:addClass('text-rarity') | |||
:addClass(specialOnly and 'text-rarity-heirloom' or v.textclass) | |||
:wikitext(getDamageText(hopupStat, stat)) | |||
:done() | |||
:tag('table') | |||
:addClass('condensedtable') | |||
:addClass('listtable') | |||
local head = hopupStat.damage_head_scale or stat.damage_head_scale or 1 | |||
renderHeadRow(intblS, cfg.head, head, cfg) | |||
renderHeadEffectiveRange(intblS, cfg, hopupStat, stat) | |||
renderLegsRow (intblS, cfg, hopupStat, stat) | |||
end | |||
end | |||
end | |||
local function renderFirerate(tbl, cfg, stat, lang) | |||
local cell = createCellInRow(tbl, cfg.name) | |||
local node, newcell = require('Module:WeaponInfobox/Firerate').renderFirerate(stat, lang) | |||
if newcell then | |||
tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) | |||
else | else | ||
local | cell:node(node) | ||
end | |||
end | |||
local function renderProjectileSpeed(tbl, cfg, projectile_speed, projectile_speed_charged) | |||
if not aw.isNumberAndGreaterThanZero(projectile_speed) then | |||
return | |||
end | end | ||
local text | |||
if projectile_speed == math.huge then | |||
text = cfg.hitscan | |||
elseif aw.isNumberAndGreaterThanZero(move_speed_charged) 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 ShatterCapsMoveSpeedProto = { | |||
shatter_caps = { | |||
move_speed = proto.NumberRange(0, 1), | |||
}, | |||
} | |||
local function getMoveSpeedText(cfg, moveSpeed) | |||
return string.format( | |||
'<span class="text-type-number">%s</span>%s', | |||
string.format(cfg.format, 100 * (moveSpeed - 1)), | |||
cfg.unit) | |||
end | |||
local function renderMoveSpeed(tbl, cfg, stat) | |||
if not aw.isNumberAndGreaterThanZero(stat.move_speed) then | |||
return | |||
end | |||
local text | |||
-- [Hop-up] Shatter Caps | |||
if proto.validateTypes(stat, ShatterCapsMoveSpeedProto) and stat.move_speed ~= stat.shatter_caps.move_speed then | |||
text = string.format( | |||
'%s%s<span class="block-rarity disp-rarity-epic text-rarity text-rarity-epic">%s %s</span>', | |||
getMoveSpeedText(cfg, stat.move_speed), | |||
cfg.separator, | |||
iu.hopup('shatter_caps'), | |||
getMoveSpeedText(cfg, stat.shatter_caps.move_speed)) | |||
elseif aw.isNumberAndGreaterThanZero(stat.move_speed_charged) then | |||
text = string.format( | |||
'%s → %s', | |||
getMoveSpeedText(cfg, stat.move_speed), | |||
getMoveSpeedText(cfg, stat.move_speed_charged), | |||
cfg.unit) | |||
else | |||
text = getMoveSpeedText(cfg, stat.move_speed) | |||
end | |||
renderRow(tbl, cfg.name, text) | |||
end | |||
local function renderDPS(tbl, cfg, stat, lang) | |||
local sld = require('Module:Stat/Shield')['reinforcehelmets'] | |||
local node = require('Module:WeaponInfobox/DPS').renderDPS(stat, sld, lang) | |||
createCellInRow(tbl, cfg.name) | |||
tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) | |||
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') | :tag('td') | ||
:attr(' | :addClass('cell-type-number') | ||
: | :attr('align', 'right') | ||
:wikitext(mag) | |||
:done() | |||
:tag('td') | |||
:wikitext(cfg.unit) | |||
:done() | |||
:tag('td') | |||
:addClass('text-secondary') | |||
:wikitext(' / ') | |||
: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 | end | ||
local function renderMagazine( | local function calcVirtualMagSize(overheat, firerate, moddedLoader) | ||
if moddedLoader then | |||
if | return math.ceil(1.15 * overheat * firerate) | ||
else | |||
return math.ceil(overheat * firerate) | |||
end | |||
end | |||
local MultipleNumberProto = { | |||
proto.NumberRange(0), | |||
proto.NumberRange(0), | |||
proto.NumberRange(0), | |||
proto.NumberRange(0), | |||
} | |||
local function renderMagazine(tbl, cfg, stat, isSpecial, isModdedLoaderAttachable) | |||
local magazine = stat.magazine | |||
local typename = type(magazine) | |||
local text | |||
-- 物資投下武器 | |||
if isSpecial or stat.ammo == 'minigun' then | |||
local magazine2 | |||
if typename == 'table' then | |||
magazine2 = magazine[4] | |||
else | |||
magazine2 = magazine | |||
end | |||
if aw.isNumberAndGreaterThanZero(stat.magazine_reserve) then | |||
if isModdedLoaderAttachable then | |||
local moddedLoaderMagazine2 = apex.calcMagazineWithModdedLoader(magazine2) | |||
local moddedLoaderReserve = stat.magazine_reserve - (moddedLoaderMagazine2 - magazine2) | |||
local intbl = createCellInRow(tbl, cfg.name) | |||
:tag('table') | :tag('table') | ||
:addClass('condensedtable') | :addClass('condensedtable') | ||
renderMagazineRow(intbl, '', magazine2, stat.magazine_reserve, cfg) | |||
renderMagazineRow(intbl, iu.passive('modded_loader') .. ' ', moddedLoaderMagazine2, moddedLoaderReserve, cfg) | |||
return | |||
elseif magazine2 == math.huge then | |||
text = string.format( | |||
'%s <span class="text-separator">/</span> <span class="text-type-number">%s</span>%s', | |||
cfg.infinity, | |||
stat.magazine_reserve == math.huge and cfg.infinity or stat.magazine_reserve, | |||
cfg.unit) | |||
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', | |||
magazine2, | |||
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', magazine2, cfg.unit) | |||
end | |||
-- 拡張マガジンあり | |||
elseif 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], | |||
{ separator = ' - ', footer = cfg.unit }) | |||
local moddedLoaderMagazine = apex.calcMagazinesWithModdedLoader(magazine) | |||
renderFormatRow( | |||
intable, | |||
iu.passive('modded_loader') .. ' ', | |||
moddedLoaderMagazine[1], | |||
moddedLoaderMagazine[2], | |||
moddedLoaderMagazine[3], | |||
moddedLoaderMagazine[4], | |||
{ separator = ' - ', footer = cfg.unit }) | |||
return | |||
-- Boosted Loader | |||
elseif stat.boosted_loader and proto.validateTypes(stat.boosted_loader.magazine, MultipleNumberProto) then | |||
local intable = createCellInRow(tbl, cfg.name) | |||
:tag('table') | |||
:addClass('condensedtable') | |||
:addClass('raritytable') | |||
renderFormatRow( | |||
intable, | |||
'', | |||
magazine[1], magazine[2], magazine[3], magazine[4], | |||
{ separator = ' - ', footer = cfg.unit }) | |||
renderFormatRow( | |||
intable, | |||
iu.hopup('boosted_loader') .. ' ', | |||
stat.boosted_loader.magazine[1], | |||
stat.boosted_loader.magazine[2], | |||
stat.boosted_loader.magazine[3], | |||
stat.boosted_loader.magazine[4], | |||
{ rarity = 'legendary', separator = ' - ', footer = cfg.unit }) | |||
return | |||
else | |||
text = formatter:format(magazine[1], magazine[2], magazine[3], magazine[4], cfg.unit, ' - ') | |||
end | |||
-- 拡張マガジンなし | |||
elseif typename == 'number' then | |||
if magazine == math.huge then | |||
if aw.isNumberAndGreaterThanZero(stat.firerate) then | |||
if proto.validateTypes(stat.overheat, MultipleNumberProto) then | |||
local intable = createCellInRow(tbl, cfg.name):tag('table'):addClass('condensedtable') | |||
renderFormatRow( | |||
intable, | |||
'', | |||
calcVirtualMagSize(stat.overheat[1], stat.firerate), | |||
calcVirtualMagSize(stat.overheat[2], stat.firerate), | |||
calcVirtualMagSize(stat.overheat[3], stat.firerate), | |||
calcVirtualMagSize(stat.overheat[4], stat.firerate), | |||
{ separator = ' - ', footer = cfg.unit }) | |||
renderFormatRow( | |||
intable, | |||
cfg.seconds.name, | |||
string.format(cfg.seconds.format, stat.overheat[1]), | |||
string.format(cfg.seconds.format, stat.overheat[2]), | |||
string.format(cfg.seconds.format, stat.overheat[3]), | |||
string.format(cfg.seconds.format, stat.overheat[4]), | |||
{ class = 'no-list-style text-secondary text-smaller', headerAlign = 'right', separator = ' - ', footer = cfg.seconds.unit }) | |||
if isModdedLoaderAttachable then | |||
renderFormatRow( | |||
intable, | |||
iu.passive('modded_loader') .. ' ', | |||
calcVirtualMagSize(stat.overheat[1], stat.firerate, true), | |||
calcVirtualMagSize(stat.overheat[2], stat.firerate, true), | |||
calcVirtualMagSize(stat.overheat[3], stat.firerate, true), | |||
calcVirtualMagSize(stat.overheat[4], stat.firerate, true), | |||
{ separator = ' - ', footer = cfg.unit }) | |||
renderFormatRow( | |||
intable, | |||
cfg.seconds.name, | |||
string.format(cfg.seconds.format, 1.15 * stat.overheat[1]), | |||
string.format(cfg.seconds.format, 1.15 * stat.overheat[2]), | |||
string.format(cfg.seconds.format, 1.15 * stat.overheat[3]), | |||
string.format(cfg.seconds.format, 1.15 * stat.overheat[4]), | |||
{ class = 'no-list-style text-secondary text-smaller', headerAlign = 'right', separator = ' - ', footer = cfg.seconds.unit }) | |||
end | |||
return | |||
elseif aw.isNumberAndGreaterThanZero(stat.overheat) 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), | |||
cfg.unit, | |||
iu.passive('modded_loader'), | |||
1 + math.floor(1.15 * stat.overheat * stat.firerate), | |||
cfg.unit) | |||
else | |||
text = cfg.infinity | |||
end | |||
else | |||
text = cfg.infinity | |||
end | |||
local | elseif aw.isNumberAndGreaterThanX(stat.ammo_per_shot, 1) then | ||
local times = magazine / stat.ammo_per_shot | |||
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) | |||
else | |||
text = string.format('<span class="text-type-number">%d</span>%s', magazine, cfg.unit) | |||
end | |||
end | |||
renderRow(tbl, cfg.name, text) | |||
end | |||
local function renderDuration(tbl, cfg, stat, lang) | |||
local node, isSimple = require('Module:WeaponInfobox/Duration').renderDuration(stat, lang) | |||
local cell = createCellInRow(tbl, cfg.name) | |||
if isSimple then | |||
cell:node(node) | |||
else | |||
tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) | |||
end | |||
end | |||
local function renderSpread(tbl, cfg, stat, lang, frame) | |||
local Spread = require('Module:WeaponInfobox/Spread') | |||
local node = Spread.renderSpread(stat.spread, stat.attachments.laser_sight, stat.quickdraw_holster and stat.quickdraw_holster.spread, lang) | |||
local cell = createCellInRow(tbl, cfg.name) | |||
if node.tagName == 'span' then | |||
cell:node(node) | |||
elseif Hopup.anvil_receiver.enabled and stat.anvil_receiver and stat.anvil_receiver.spread then | |||
local node2 = Spread.renderSpread(stat.anvil_receiver.spread, stat.attachments.laser_sight, nil, lang) | |||
if frame then | |||
local tabContent = string.format( | |||
'{{#tab:%s|%s}}{{#tab:%s|%s}}', | |||
cfg.normal, tostring(node), | |||
cfg.anvil_receiver, tostring(node2)) | |||
tbl:tag('tr') | |||
:tag('td') | |||
:attr('colspan', 2) | |||
:wikitext(frame:extensionTag { name = 'tabs', content = tabContent, args = { class = 'tabs-intable' }}) | |||
else | else | ||
tbl:tag('tr') | |||
:tag('td') | |||
:attr('colspan', 2) | |||
:tag('b') | |||
:wikitext(cfg.normal) | |||
:done() | |||
:node(node) | |||
:tag('b') | |||
:wikitext(cfg.anvil_receiver) | |||
:done() | |||
:node(node2) | |||
end | end | ||
elseif | elseif stat.spread_charged then | ||
local node2 = Spread.renderSpread(stat.spread_charged , nil, lang) | |||
if frame then | |||
local tabContent = string.format( | |||
'{{#tab:%s|%s}}{{#tab:%s|%s}}', | |||
cfg.normal, tostring(node), | |||
cfg.full_charge, tostring(node2)) | |||
tbl:tag('tr') | |||
:tag('td') | |||
:attr('colspan', 2) | |||
:wikitext(frame:extensionTag { name = 'tabs', content = tabContent, args = { class = 'tabs-intable' }}) | |||
else | else | ||
tbl:tag('tr') | |||
:tag('td') | |||
:attr('colspan', 2) | |||
:tag('b') | |||
:wikitext(cfg.normal) | |||
:done() | |||
:node(node) | |||
:tag('b') | |||
:wikitext(cfg.full_charge) | |||
:done() | |||
:node(node2) | |||
end | end | ||
else | else | ||
tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) | |||
end | |||
end | |||
local function renderSpreadDecayRow(intbl, name, cfg, spread, opts) | |||
opts = opts or {} | |||
intbl:tag('tr') | |||
:addClassIf(type(opts.rarity) == 'string', 'row-rarity-one disp-rarity-' .. (opts.rarity or 'common')) | |||
:tag('th') | |||
:wikitext(name) | |||
:done() | |||
:tag('td') | |||
:addClass('cell-type-number') | |||
:attr('align', 'right') | |||
:wikitext(string.format(cfg.rate.format, spread.decay_rate)) | |||
:done() | |||
:tag('td') | |||
:wikitext(cfg.rate.unit) | |||
:done() | |||
:tag('td') | |||
:addClass('text-smaller') | |||
:wikitext(cfg.delay.name) | |||
:done() | |||
:tag('td') | |||
:addClass('cell-type-number') | |||
:addClass('text-smaller') | |||
:attr('align', 'right') | |||
:wikitext(string.format(cfg.delay.format, spread.decay_delay)) | |||
:done() | |||
:tag('td') | |||
:addClass('text-smaller') | |||
:wikitext(cfg.delay.unit) | |||
end | |||
local function renderSpreadDecay(tbl, cfg, stat) | |||
if not (aw.isNumberAndGreaterThanZero(stat.spread.decay_rate) and aw.isNumberAndGreaterThanZero(stat.spread.decay_delay)) then | |||
return | |||
end | |||
local cell = createCellInRow(tbl, cfg.name) | |||
-- [Hop-up] Anvil Receiver | |||
if stat.anvil_receiver and stat.anvil_receiver.spread and aw.isNumberAndGreaterThanZero(stat.anvil_receiver.spread.decay_rate) and aw.isNumberAndGreaterThanZero(stat.anvil_receiver.spread.decay_delay) then | |||
local intbl = cell:tag('table') | |||
:addClass('condensedtable') | |||
:addClass('raritytable') | |||
renderSpreadDecayRow( | |||
intbl, | |||
'', | |||
cfg, | |||
stat.spread) | |||
renderSpreadDecayRow( | |||
intbl, | |||
iu.hopup('anvil_receiver') .. ' ', | |||
cfg, | |||
stat.anvil_receiver.spread, | |||
{ rarity = 'legendary' }) | |||
else | |||
local text = string.format(cfg.format, stat.spread.decay_rate, stat.spread.decay_delay) | |||
cell:wikitext(text) | |||
end | end | ||
end | end | ||
local function | local function createTable(cfg, name, stat, lang, frame) | ||
local | local isSpecial = aw.stringstarts(stat.ammo, "special_") | ||
local category = | local isModdedLoaderAttachable = stat.category == 'light_machine_gun' or stat.ammo == 'minigun' | ||
local | local tbl = mw.html.create('table') | ||
renderCategory( | renderReleaseDate(tbl, cfg.release, stat.release) | ||
renderAmmo( | renderCategory(tbl, cfg.category, stat.category, lang) | ||
renderMode( | renderAmmo(tbl, cfg.ammo, stat.ammo, lang) | ||
renderDamage( | renderCost(tbl, cfg.cost, stat.cost, stat.ammo) | ||
renderMode(tbl, cfg.mode, stat, isSpecial) | |||
return | renderDamage(tbl, cfg.damage, stat, isSpecial) | ||
if stat.firerate then | |||
renderFirerate(tbl, cfg.firerate, stat, lang) | |||
end | |||
renderProjectileSpeed(tbl, cfg.projectilespeed, stat.projectile_speed, stat.projectile_speed_charged) | |||
renderMoveSpeed(tbl, cfg.movespeed, stat, stat) | |||
renderMagazine(tbl, cfg.magazine, stat, isSpecial, isModdedLoaderAttachable) | |||
if stat.firerate and name ~= 'ボセックコンパウンドボウ' then | |||
renderDPS(tbl, cfg.dps, stat, lang) | |||
end | |||
if stat.time then | |||
renderDuration(tbl, cfg.duration, stat, lang) | |||
end | |||
if stat.spread then | |||
renderSpread(tbl, cfg.spread, stat, lang, frame) | |||
renderSpreadDecay(tbl, cfg.spreaddecay, stat) | |||
end | |||
return tbl | |||
end | |||
local function renderInfobox(args, frame) | |||
local lang = args and args.lang or 'ja' | |||
local cfglang = cfg[lang] | |||
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, args.name, stat, lang, frame)) | |||
return div | |||
end | end | ||
function p._main(args, frame) | function p._main(args, frame) | ||
formatter = require('Module:Utility/Formatter').new(frame) | formatter = require('Module:Utility/Formatter').new(frame) | ||
return tostring( | return tostring(renderInfobox(args, frame)) | ||
end | end | ||
2022年8月13日 (土) 12:12時点における最新版
このモジュールについての説明文ページを モジュール:WeaponInfobox/doc に作成できます
require('Module:Utility/mw.html Extensions') local p = {} local cfg = mw.loadData('Module:WeaponInfobox/configuration') local Hopup = mw.loadData('Module:Stat/Hopup') local aw = require('Module:Utility/Library') local apex = require('Module:Utility/ApexLibrary') local iu = require('Module:Utility/Image') local inu = require('Module:Utility/ImageWithName') local nu = require('Module:Utility/Name') local proto = require('Module:Utility/Prototypes') local StringBuilder = require('Module:Utility/StringBuilder') local formatter -- lazily initialized local getArgs -- lazily initialized 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 ' - ' local row = tbl:tag('tr') :addClassIf(type(opts.class) == 'string', opts.class) :addClassIf(type(opts.rarity) == 'string', 'row-rarity-one disp-rarity-' .. (opts.rarity or 'common')) 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 }) :tag('span') :addClass('text-rarity') :addClass('text-rarity-common') :wikitext(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 }) :tag('span') :addClass('text-rarity') :addClass('text-rarity-rare') :wikitext(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 }) :tag('span') :addClass('text-rarity') :addClass('text-rarity-epic') :wikitext(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, lang) local category = nu.type(stat, lang, 1) local item if lang == 'ja' then item = string.format('[[武器#%s|%s]][[Category:%s]]', category, category, category) else item = string.format('%s[[Category:%s]]', category, category) end renderRow(tbl, cfg.name, item) end local function renderAmmo(tbl, cfg, stat, lang) createCellInRow(tbl, cfg.name) :addClass('cell-ammo') :wikitext(inu.ammo(stat, { lang = lang, size = 24 })) end local function renderCost(tbl, cfg, stat, ammo) if stat == nil or aw.stringstarts(ammo, 'special_') then return end local text = formatter:format( aw.comma(stat[1]), aw.comma(stat[2]), aw.comma(stat[3]), aw.comma(stat[4]), '', ' - ') renderRow(tbl, cfg.name, cfg.header .. text) end local function appendMode(builder, cfg, stat, base) if stat.is_semi_auto or (stat.is_semi_auto == nil and base.is_semi_auto) then local burstCount = stat.burst_count or (base and base.burst_count) or 1 if aw.isNumberAndGreaterThanOrEqualToX(burstCount, 2) then builder:appendFormat(cfg.burst, burstCount) builder:append(cfg.burst_category) else builder:append(cfg.single, cfg.single_category) end else builder:append(cfg.auto, cfg.auto_category) end end local function renderMode(tbl, cfg, stat, isSpecial) local builder = StringBuilder.new() appendMode(builder, cfg, stat) if stat.altfire then builder:append(cfg.separator) appendMode(builder, cfg, stat.altfire, stat) end if (Hopup.selectfire_receiver.enabled or isSpecial) and stat.selectfire_receiver then builder:append(cfg.separator, '<span class="block-rarity disp-rarity-epic">', iu.hopup('selectfire_receiver'), ' ') appendMode(builder, cfg, stat.selectfire_receiver, stat) builder:append('</span>') end if (Hopup.double_tap_trigger.enabled or isSpecial) and stat.double_tap_trigger then builder:append(cfg.separator, '<span class="block-rarity disp-rarity-epic">', iu.hopup('double_tap_trigger'), ' ') appendMode(builder, cfg, stat.double_tap_trigger, stat) builder:append('</span>') end renderRow(tbl, cfg.name, tostring(builder)) end local HammerpointRoundsProto = { hammerpoint_rounds = { damage_unshielded_scale = proto.NumberRange(1), }, } local DisruptorRoundsProto = { disruptor_rounds = { damage_shield_scale = proto.NumberRange(1), }, } local function getDamageText(stat, root, isSpecial) local builder = StringBuilder.new() local rarity = isSpecial and 'heirloom' local damage, pellet if root then damage = stat.damage or root.damage pellet = stat.pellet or root.pellet or 1 else damage = stat.damage pellet = stat.pellet or 1 end if aw.isNumberAndGreaterThanOrEqualToX(pellet, 2) then builder:appendFormat( '<span class="text-type-number">%s</span> × %d', damage.base, pellet) if aw.isNumberAndGreaterThanZero(damage.charged) then builder:appendFormat( ' → <span class="text-type-number">%s</span> × %d', damage.charged, pellet) elseif proto.validateTypes(stat, HammerpointRoundsProto) then if not rarity then rarity = Hopup.hammerpoint_rounds.rarity end builder:appendFormat( ' → <span class="block-rarity disp-rarity-%s text-rarity text-rarity-%s">%s \'\'\'<span class="text-type-number">%s</span> × %d\'\'\'</span>', rarity, rarity, iu.hopup('hammerpoint_rounds', { rarity = rarity }), math.floor(damage.base * stat.hammerpoint_rounds.damage_unshielded_scale), pellet) end else if aw.isNumberAndGreaterThanZero(damage.amped) then builder:appendFormat( '<span class="text-type-number">%s</span> <span class="text-separator">/</span> %s <span class="text-type-number">%s</span>', damage.base, iu.item('シールドセル'), damage.amped) elseif aw.isNumberAndGreaterThanZero(damage.charged) then builder:appendFormat( '<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>', damage.base, damage.charged) elseif proto.validateTypes(stat, HammerpointRoundsProto) then if not rarity then rarity = Hopup.hammerpoint_rounds.rarity end builder:appendFormat( '<span class="text-type-number">%s</span> → <span class="block-rarity disp-rarity-%s text-rarity text-rarity-%s">%s \'\'\'<span class="text-type-number">%s</span>\'\'\'</span>', damage.base, rarity, rarity, iu.hopup('hammerpoint_rounds', { rarity = rarity }), math.floor(damage.base * stat.hammerpoint_rounds.damage_unshielded_scale)) elseif proto.validateTypes(stat, DisruptorRoundsProto) then if not rarity then rarity = Hopup.disruptor_rounds.rarity end builder:appendFormat( '<span class="block-rarity disp-rarity-%s text-rarity text-rarity-%s">%s \'\'\'<span class="text-type-number">%s</span>\'\'\'</span> → <span class="text-type-number">%s</span>', rarity, rarity, iu.hopup('disruptor_rounds', { rarity = rarity }), math.floor(damage.base * stat.disruptor_rounds.damage_shield_scale), damage.base) else builder:appendFormat('<span class="text-type-number">%s</span>', damage.base) end end return tostring(builder) end local function renderHeadRow(tbl, name, head, cfg, nolist) local opts = { align = 'left', footer = cfg.unit } if nolist then opts.class = 'no-list-style' opts.headerAlign = 'right' end renderFormatRow( tbl, name, nil, apex.calcHeadshotMultiplier(head, apex.HEAD_HLMLV1), apex.calcHeadshotMultiplier(head, apex.HEAD_HLMLV2), apex.calcHeadshotMultiplier(head, apex.HEAD_HLMLV3), opts) end local function renderHeadEffectiveRange(tbl, cfg, stat, root) local headDist = stat.damage_head_distance or root.damage_head_distance if aw.isNumberAndGreaterThanZero(headDist) then local text = string.format(cfg.head_effective_range, 0.0254 * headDist) tbl:tag('tr') :addClass('no-list-style') :tag('td') :attr('align', 'left') :attr('colspan', 7) :wikitext(text) end end local function renderLegsRow(tbl, cfg, stat, root) local legs = stat.damage_legs_scale or root.damage_legs_scale local text if aw.isNumberAndGreaterThanZero(stat.legshot_charged) then text = string.format( '<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>%s', legs, stat.legshot_charged, cfg.unit) else text = string.format( '<span class="text-type-number">%s</span>%s', legs, cfg.unit) end tbl:tag('tr') :tag('th') :wikitext(cfg.legs) :done() :tag('td') :attr('align', 'left') :attr('colspan', 6) :wikitext(text) end local DamageProto = { damage = { base = proto.NumberRange(1), }, damage_head_scale = proto.NumberRange(1), damage_legs_scale = proto.NumberRange(0, 1), } local ShatterCapsProto = { damage = { base = proto.NumberRange(1), }, damage_head_scale = proto.NumberRange(1), damage_legs_scale = proto.NumberRange(0, 1), pellet = proto.NumberRange(1), } local damageHopups = { { name = 'selectfire_receiver', proto = DamageProto, textclass = 'text-rarity-epic', dispclass = 'disp-rarity-epic', }, { name = 'anvil_receiver', proto = DamageProto, textclass = 'text-rarity-legendary', dispclass = 'disp-rarity-legendary', }, { name = 'shatter_caps', proto = ShatterCapsProto, textclass = 'text-rarity-epic', dispclass = 'disp-rarity-epic', }, } local function renderDamage(tbl, cfg, stat, isSpecial) if not proto.validateTypes(stat, DamageProto) then return end local cattext = string.format(cfg.head_category, stat.damage.headshot_charged or stat.damage_head_scale or 1) local cell = createCellInRow(tbl, cfg.name) cell:wikitext(cattext .. getDamageText(stat, nil, isSpecial)) -- Headshot local head = stat.damage_head_scale or 1 local intable = cell:tag('table') :addClass('condensedtable') :addClass('listtable') renderHeadRow(intable, cfg.head, head, cfg) local hlmc = stat.damage.headshot_charged or 1 if hlmc > 1 then local nameC = '→ ' renderHeadRow(intable, nameC, hlmc, cfg, true) end local sprm = stat.damage.skullpiercer_rifling or 1 if sprm > 1 then local nameS = iu.hopup('skullpiercer_rifling') .. ' ' renderHeadRow(intable, nameS, sprm, cfg, true) end renderHeadEffectiveRange(intable, cfg, stat, stat) -- Legsshot renderLegsRow(intable, cfg, stat, stat) -- [Hop-Up] Selectfire Receiver, Anvil Receiver & Shatter Caps for _, v in ipairs(damageHopups) do local hopupStat = stat[v.name] if (Hopup[v.name].enabled or isSpecial) and proto.validateTypes(hopupStat, v.proto) then local intblS = cell:tag('div') :addClass('tpl-weapon-inbox') :addClass(specialOnly and 'disp-rarity-heirloom' or v.dispclass) :wikitext(iu.hopup(v.name) .. ' ') :tag('span') :addClass('text-rarity') :addClass(specialOnly and 'text-rarity-heirloom' or v.textclass) :wikitext(getDamageText(hopupStat, stat)) :done() :tag('table') :addClass('condensedtable') :addClass('listtable') local head = hopupStat.damage_head_scale or stat.damage_head_scale or 1 renderHeadRow(intblS, cfg.head, head, cfg) renderHeadEffectiveRange(intblS, cfg, hopupStat, stat) renderLegsRow (intblS, cfg, hopupStat, stat) end end end local function renderFirerate(tbl, cfg, stat, lang) local cell = createCellInRow(tbl, cfg.name) local node, newcell = require('Module:WeaponInfobox/Firerate').renderFirerate(stat, lang) if newcell then tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) else cell:node(node) end end local function renderProjectileSpeed(tbl, cfg, projectile_speed, projectile_speed_charged) if not aw.isNumberAndGreaterThanZero(projectile_speed) then return end local text if projectile_speed == math.huge then text = cfg.hitscan elseif aw.isNumberAndGreaterThanZero(move_speed_charged) 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 ShatterCapsMoveSpeedProto = { shatter_caps = { move_speed = proto.NumberRange(0, 1), }, } local function getMoveSpeedText(cfg, moveSpeed) return string.format( '<span class="text-type-number">%s</span>%s', string.format(cfg.format, 100 * (moveSpeed - 1)), cfg.unit) end local function renderMoveSpeed(tbl, cfg, stat) if not aw.isNumberAndGreaterThanZero(stat.move_speed) then return end local text -- [Hop-up] Shatter Caps if proto.validateTypes(stat, ShatterCapsMoveSpeedProto) and stat.move_speed ~= stat.shatter_caps.move_speed then text = string.format( '%s%s<span class="block-rarity disp-rarity-epic text-rarity text-rarity-epic">%s %s</span>', getMoveSpeedText(cfg, stat.move_speed), cfg.separator, iu.hopup('shatter_caps'), getMoveSpeedText(cfg, stat.shatter_caps.move_speed)) elseif aw.isNumberAndGreaterThanZero(stat.move_speed_charged) then text = string.format( '%s → %s', getMoveSpeedText(cfg, stat.move_speed), getMoveSpeedText(cfg, stat.move_speed_charged), cfg.unit) else text = getMoveSpeedText(cfg, stat.move_speed) end renderRow(tbl, cfg.name, text) end local function renderDPS(tbl, cfg, stat, lang) local sld = require('Module:Stat/Shield')['reinforcehelmets'] local node = require('Module:WeaponInfobox/DPS').renderDPS(stat, sld, lang) createCellInRow(tbl, cfg.name) tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) 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(' / ') :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 calcVirtualMagSize(overheat, firerate, moddedLoader) if moddedLoader then return math.ceil(1.15 * overheat * firerate) else return math.ceil(overheat * firerate) end end local MultipleNumberProto = { proto.NumberRange(0), proto.NumberRange(0), proto.NumberRange(0), proto.NumberRange(0), } local function renderMagazine(tbl, cfg, stat, isSpecial, isModdedLoaderAttachable) local magazine = stat.magazine local typename = type(magazine) local text -- 物資投下武器 if isSpecial or stat.ammo == 'minigun' then local magazine2 if typename == 'table' then magazine2 = magazine[4] else magazine2 = magazine end if aw.isNumberAndGreaterThanZero(stat.magazine_reserve) then if isModdedLoaderAttachable then local moddedLoaderMagazine2 = apex.calcMagazineWithModdedLoader(magazine2) local moddedLoaderReserve = stat.magazine_reserve - (moddedLoaderMagazine2 - magazine2) local intbl = createCellInRow(tbl, cfg.name) :tag('table') :addClass('condensedtable') renderMagazineRow(intbl, '', magazine2, stat.magazine_reserve, cfg) renderMagazineRow(intbl, iu.passive('modded_loader') .. ' ', moddedLoaderMagazine2, moddedLoaderReserve, cfg) return elseif magazine2 == math.huge then text = string.format( '%s <span class="text-separator">/</span> <span class="text-type-number">%s</span>%s', cfg.infinity, stat.magazine_reserve == math.huge and cfg.infinity or stat.magazine_reserve, cfg.unit) 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', magazine2, 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', magazine2, cfg.unit) end -- 拡張マガジンあり elseif 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], { separator = ' - ', footer = cfg.unit }) local moddedLoaderMagazine = apex.calcMagazinesWithModdedLoader(magazine) renderFormatRow( intable, iu.passive('modded_loader') .. ' ', moddedLoaderMagazine[1], moddedLoaderMagazine[2], moddedLoaderMagazine[3], moddedLoaderMagazine[4], { separator = ' - ', footer = cfg.unit }) return -- Boosted Loader elseif stat.boosted_loader and proto.validateTypes(stat.boosted_loader.magazine, MultipleNumberProto) then local intable = createCellInRow(tbl, cfg.name) :tag('table') :addClass('condensedtable') :addClass('raritytable') renderFormatRow( intable, '', magazine[1], magazine[2], magazine[3], magazine[4], { separator = ' - ', footer = cfg.unit }) renderFormatRow( intable, iu.hopup('boosted_loader') .. ' ', stat.boosted_loader.magazine[1], stat.boosted_loader.magazine[2], stat.boosted_loader.magazine[3], stat.boosted_loader.magazine[4], { rarity = 'legendary', separator = ' - ', footer = cfg.unit }) return else text = formatter:format(magazine[1], magazine[2], magazine[3], magazine[4], cfg.unit, ' - ') end -- 拡張マガジンなし elseif typename == 'number' then if magazine == math.huge then if aw.isNumberAndGreaterThanZero(stat.firerate) then if proto.validateTypes(stat.overheat, MultipleNumberProto) then local intable = createCellInRow(tbl, cfg.name):tag('table'):addClass('condensedtable') renderFormatRow( intable, '', calcVirtualMagSize(stat.overheat[1], stat.firerate), calcVirtualMagSize(stat.overheat[2], stat.firerate), calcVirtualMagSize(stat.overheat[3], stat.firerate), calcVirtualMagSize(stat.overheat[4], stat.firerate), { separator = ' - ', footer = cfg.unit }) renderFormatRow( intable, cfg.seconds.name, string.format(cfg.seconds.format, stat.overheat[1]), string.format(cfg.seconds.format, stat.overheat[2]), string.format(cfg.seconds.format, stat.overheat[3]), string.format(cfg.seconds.format, stat.overheat[4]), { class = 'no-list-style text-secondary text-smaller', headerAlign = 'right', separator = ' - ', footer = cfg.seconds.unit }) if isModdedLoaderAttachable then renderFormatRow( intable, iu.passive('modded_loader') .. ' ', calcVirtualMagSize(stat.overheat[1], stat.firerate, true), calcVirtualMagSize(stat.overheat[2], stat.firerate, true), calcVirtualMagSize(stat.overheat[3], stat.firerate, true), calcVirtualMagSize(stat.overheat[4], stat.firerate, true), { separator = ' - ', footer = cfg.unit }) renderFormatRow( intable, cfg.seconds.name, string.format(cfg.seconds.format, 1.15 * stat.overheat[1]), string.format(cfg.seconds.format, 1.15 * stat.overheat[2]), string.format(cfg.seconds.format, 1.15 * stat.overheat[3]), string.format(cfg.seconds.format, 1.15 * stat.overheat[4]), { class = 'no-list-style text-secondary text-smaller', headerAlign = 'right', separator = ' - ', footer = cfg.seconds.unit }) end return elseif aw.isNumberAndGreaterThanZero(stat.overheat) 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), cfg.unit, iu.passive('modded_loader'), 1 + math.floor(1.15 * stat.overheat * stat.firerate), cfg.unit) else text = cfg.infinity end else text = cfg.infinity end elseif aw.isNumberAndGreaterThanX(stat.ammo_per_shot, 1) then local times = magazine / stat.ammo_per_shot 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) else text = string.format('<span class="text-type-number">%d</span>%s', magazine, cfg.unit) end end renderRow(tbl, cfg.name, text) end local function renderDuration(tbl, cfg, stat, lang) local node, isSimple = require('Module:WeaponInfobox/Duration').renderDuration(stat, lang) local cell = createCellInRow(tbl, cfg.name) if isSimple then cell:node(node) else tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) end end local function renderSpread(tbl, cfg, stat, lang, frame) local Spread = require('Module:WeaponInfobox/Spread') local node = Spread.renderSpread(stat.spread, stat.attachments.laser_sight, stat.quickdraw_holster and stat.quickdraw_holster.spread, lang) local cell = createCellInRow(tbl, cfg.name) if node.tagName == 'span' then cell:node(node) elseif Hopup.anvil_receiver.enabled and stat.anvil_receiver and stat.anvil_receiver.spread then local node2 = Spread.renderSpread(stat.anvil_receiver.spread, stat.attachments.laser_sight, nil, lang) if frame then local tabContent = string.format( '{{#tab:%s|%s}}{{#tab:%s|%s}}', cfg.normal, tostring(node), cfg.anvil_receiver, tostring(node2)) tbl:tag('tr') :tag('td') :attr('colspan', 2) :wikitext(frame:extensionTag { name = 'tabs', content = tabContent, args = { class = 'tabs-intable' }}) else tbl:tag('tr') :tag('td') :attr('colspan', 2) :tag('b') :wikitext(cfg.normal) :done() :node(node) :tag('b') :wikitext(cfg.anvil_receiver) :done() :node(node2) end elseif stat.spread_charged then local node2 = Spread.renderSpread(stat.spread_charged , nil, lang) if frame then local tabContent = string.format( '{{#tab:%s|%s}}{{#tab:%s|%s}}', cfg.normal, tostring(node), cfg.full_charge, tostring(node2)) tbl:tag('tr') :tag('td') :attr('colspan', 2) :wikitext(frame:extensionTag { name = 'tabs', content = tabContent, args = { class = 'tabs-intable' }}) else tbl:tag('tr') :tag('td') :attr('colspan', 2) :tag('b') :wikitext(cfg.normal) :done() :node(node) :tag('b') :wikitext(cfg.full_charge) :done() :node(node2) end else tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) end end local function renderSpreadDecayRow(intbl, name, cfg, spread, opts) opts = opts or {} intbl:tag('tr') :addClassIf(type(opts.rarity) == 'string', 'row-rarity-one disp-rarity-' .. (opts.rarity or 'common')) :tag('th') :wikitext(name) :done() :tag('td') :addClass('cell-type-number') :attr('align', 'right') :wikitext(string.format(cfg.rate.format, spread.decay_rate)) :done() :tag('td') :wikitext(cfg.rate.unit) :done() :tag('td') :addClass('text-smaller') :wikitext(cfg.delay.name) :done() :tag('td') :addClass('cell-type-number') :addClass('text-smaller') :attr('align', 'right') :wikitext(string.format(cfg.delay.format, spread.decay_delay)) :done() :tag('td') :addClass('text-smaller') :wikitext(cfg.delay.unit) end local function renderSpreadDecay(tbl, cfg, stat) if not (aw.isNumberAndGreaterThanZero(stat.spread.decay_rate) and aw.isNumberAndGreaterThanZero(stat.spread.decay_delay)) then return end local cell = createCellInRow(tbl, cfg.name) -- [Hop-up] Anvil Receiver if stat.anvil_receiver and stat.anvil_receiver.spread and aw.isNumberAndGreaterThanZero(stat.anvil_receiver.spread.decay_rate) and aw.isNumberAndGreaterThanZero(stat.anvil_receiver.spread.decay_delay) then local intbl = cell:tag('table') :addClass('condensedtable') :addClass('raritytable') renderSpreadDecayRow( intbl, '', cfg, stat.spread) renderSpreadDecayRow( intbl, iu.hopup('anvil_receiver') .. ' ', cfg, stat.anvil_receiver.spread, { rarity = 'legendary' }) else local text = string.format(cfg.format, stat.spread.decay_rate, stat.spread.decay_delay) cell:wikitext(text) end end local function createTable(cfg, name, stat, lang, frame) local isSpecial = aw.stringstarts(stat.ammo, "special_") 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, lang) renderAmmo(tbl, cfg.ammo, stat.ammo, lang) renderCost(tbl, cfg.cost, stat.cost, stat.ammo) renderMode(tbl, cfg.mode, stat, isSpecial) renderDamage(tbl, cfg.damage, stat, isSpecial) if stat.firerate then renderFirerate(tbl, cfg.firerate, stat, lang) end renderProjectileSpeed(tbl, cfg.projectilespeed, stat.projectile_speed, stat.projectile_speed_charged) renderMoveSpeed(tbl, cfg.movespeed, stat, stat) renderMagazine(tbl, cfg.magazine, stat, isSpecial, isModdedLoaderAttachable) if stat.firerate and name ~= 'ボセックコンパウンドボウ' then renderDPS(tbl, cfg.dps, stat, lang) end if stat.time then renderDuration(tbl, cfg.duration, stat, lang) end if stat.spread then renderSpread(tbl, cfg.spread, stat, lang, frame) renderSpreadDecay(tbl, cfg.spreaddecay, stat) end return tbl end local function renderInfobox(args, frame) local lang = args and args.lang or 'ja' local cfglang = cfg[lang] 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, args.name, stat, lang, frame)) return div end function p._main(args, frame) formatter = require('Module:Utility/Formatter').new(frame) return tostring(renderInfobox(args, frame)) 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