| 🌟 | 現在、 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:WeaponInfobox」の版間の差分
ナビゲーションに移動
検索に移動
(クイックドローホルスターの据銃時間に色を付けるように改善) |
(アンビルレシーバーが出現しない場合にアンビルレシーバー用の拡散を非表示するように対処) |
||
| (同じ利用者による、間の189版が非表示) | |||
| 2行目: | 2行目: | ||
local p = {} | local p = {} | ||
local cfg = mw.loadData('Module:WeaponInfobox/configuration') | |||
local Hopup = mw.loadData('Module:Stat/Hopup') | |||
local aw = require('Module:Utility/Library') | local aw = require('Module:Utility/Library') | ||
local iu = require('Module:Utility/Image') | 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 createCellInRow(tbl, name) | local function createCellInRow(tbl, name) | ||
| 28行目: | 31行目: | ||
opts.separator = opts.separator or ' - ' | opts.separator = opts.separator or ' - ' | ||
local row = tbl:tag('tr') | local row = tbl:tag('tr') | ||
:addClassIf(opts.class | :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 | if name ~= nil then | ||
| 35行目: | 39行目: | ||
:wikitext(name) | :wikitext(name) | ||
end | end | ||
row:tag('td') | 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 common ~= nil then | ||
row:tag('td'):wikitext(opts.separator) | 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 | end | ||
if rare ~= nil then | if rare ~= nil then | ||
row:tag('td'):wikitext(opts.separator) | 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 | end | ||
if epic ~= nil then | if epic ~= nil then | ||
row:tag('td'):wikitext(opts.separator) | 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 opts.footer ~= nil then | if opts.footer ~= nil then | ||
row:tag('td'):wikitext(opts.footer) | 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 | end | ||
return row | return row | ||
end | end | ||
local | 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, | local function renderCategory(tbl, cfg, stat, lang) | ||
local category = | local category = nu.type(stat, lang, 1) | ||
local item = string.format('[[武器#%s|%s]][[Category:%s]]', category, category, category) | local item | ||
renderRow(tbl, name, 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 | end | ||
local function renderAmmo(tbl, cfg, stat, lang) | |||
createCellInRow(tbl, cfg.name) | |||
:addClass('cell-ammo') | |||
:wikitext(inu.ammo(stat, { lang = lang, size = 24 })) | |||
local function renderAmmo(tbl, | |||
. | |||
end | end | ||
local function | local function renderCost(tbl, cfg, stat, ammo) | ||
if stat == nil or aw.stringstarts(ammo, 'special_') then | |||
return | |||
end | 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 | else | ||
builder:append(cfg.auto, cfg.auto_category) | |||
end | end | ||
end | end | ||
local function renderMode(tbl, | local function renderMode(tbl, cfg, stat, isSpecial) | ||
local | local builder = StringBuilder.new() | ||
if stat. | appendMode(builder, cfg, stat) | ||
if stat.altfire then | |||
builder:append(cfg.separator) | |||
appendMode(builder, cfg, stat.altfire, stat) | |||
end | end | ||
if (Hopup.selectfire_receiver.enabled or isSpecial) and stat.selectfire_receiver then | |||
if stat. | 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 | end | ||
if (Hopup.double_tap_trigger.enabled or isSpecial) and stat.double_tap_trigger then | |||
if stat. | 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 | end | ||
renderRow(tbl, cfg.name, tostring(builder)) | |||
renderRow(tbl, name, | |||
end | end | ||
local | local HammerpointRoundsProto = { | ||
hammerpoint_rounds = { | |||
local | 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 | else | ||
damage = stat.damage | |||
pellet = stat.pellet or 1 | |||
end | 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 | 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 | end | ||
return tostring(builder) | |||
local | end | ||
if | |||
local | 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 | else | ||
text = string.format( | |||
'<span class="text-type-number">%s</span>%s', | |||
legs, | |||
cfg.unit) | |||
end | end | ||
tbl:tag('tr') | tbl:tag('tr') | ||
:tag('th') | |||
:wikitext(cfg.legs) | |||
:done() | |||
:tag('td') | :tag('td') | ||
:attr('colspan', | :attr('align', 'left') | ||
: | :attr('colspan', 6) | ||
:wikitext(text) | |||
end | end | ||
local | local DamageProto = { | ||
damage = { | |||
local function | base = proto.NumberRange(1), | ||
if | }, | ||
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 | return | ||
end | end | ||
local | 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 | end | ||
if | local sprm = stat.damage.skullpiercer_rifling or 1 | ||
local | if sprm > 1 then | ||
local nameS = iu.hopup('skullpiercer_rifling') .. ' ' | |||
renderHeadRow(intable, nameS, sprm, cfg, true) | |||
end | end | ||
renderHeadEffectiveRange(intable, cfg, stat, stat) | |||
-- Legsshot | |||
renderLegsRow(intable, cfg, stat, stat) | |||
local | -- [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 | ||
end | end | ||
local function | 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 | end | ||
local function | local function renderProjectileSpeed(tbl, cfg, projectile_speed, projectile_speed_charged) | ||
if not aw.isNumberAndGreaterThanZero(projectile_speed) then | |||
return | |||
if | |||
end | end | ||
local | 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 | 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 | end | ||
renderRow(tbl, cfg.name, text) | |||
end | end | ||
local function | local ShatterCapsMoveSpeedProto = { | ||
if stat. | 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 | return | ||
end | end | ||
local | local text | ||
if | -- [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 | else | ||
text = getMoveSpeedText(cfg, stat.move_speed) | |||
end | 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 | end | ||
local function | 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 | else | ||
return | return math.ceil(overheat * firerate) | ||
end | end | ||
end | end | ||
local | local MultipleNumberProto = { | ||
local function | 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 ammo == ' | 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 | 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 | end | ||
else | |||
local intable = createCellInRow(tbl, name) | 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') | :tag('table') | ||
:addClass('condensedtable') | :addClass('condensedtable') | ||
renderFormatRow( | renderFormatRow( | ||
intable, | intable, | ||
' | '', | ||
magazine[1], magazine[2], magazine[3], magazine[4], | |||
{ separator = ' - ', footer = cfg.unit }) | |||
local moddedLoaderMagazine = apex.calcMagazinesWithModdedLoader(magazine) | |||
renderFormatRow( | renderFormatRow( | ||
intable, | 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( | renderFormatRow( | ||
intable, | intable, | ||
'', | |||
magazine[1], magazine[2], magazine[3], magazine[4], | |||
{ separator = ' - ', footer = cfg.unit }) | |||
{ | |||
renderFormatRow( | renderFormatRow( | ||
intable, | intable, | ||
iu.hopup('boosted_loader') .. ' ', | |||
string.format( | 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 | ||
end | end | ||
renderRow(tbl, cfg.name, text) | |||
end | end | ||
local function renderDuration(tbl, cfg, stat, lang) | |||
local function | local node, isSimple = require('Module:WeaponInfobox/Duration').renderDuration(stat, lang) | ||
local cell = createCellInRow(tbl, cfg.name) | |||
if isSimple then | |||
cell:node(node) | |||
local | |||
if | |||
else | else | ||
tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) | |||
end | end | ||
end | |||
local | |||
if | local function renderSpread(tbl, cfg, stat, lang, frame) | ||
local | local Spread = require('Module:WeaponInfobox/Spread') | ||
if | 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 stat.spread_charged then | |||
local | local node2 = Spread.renderSpread(stat.spread_charged , nil, lang) | ||
if | 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 | ||
end | end | ||
local function | 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 | end | ||
local function | local function renderSpreadDecay(tbl, cfg, stat) | ||
stat | if not (aw.isNumberAndGreaterThanZero(stat.spread.decay_rate) and aw.isNumberAndGreaterThanZero(stat.spread.decay_delay)) then | ||
return | |||
end | |||
local | local cell = createCellInRow(tbl, cfg.name) | ||
local | |||
local | -- [Hop-up] Anvil Receiver | ||
renderReleaseDate( | 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 | ||
renderCategory( | local intbl = cell:tag('table') | ||
renderAmmo( | :addClass('condensedtable') | ||
:addClass('raritytable') | |||
renderDamage( | renderSpreadDecayRow( | ||
intbl, | |||
'', | |||
renderMagazine( | 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 | 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 | end | ||
return | return tbl | ||
end | end | ||
function | 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