🌟 | 現在、 鉄壁ヘッドショットには対応済みです。 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
モジュール:WeaponInfobox
ナビゲーションに移動
検索に移動
このモジュールについての説明文ページを モジュール:WeaponInfobox/doc に作成できます
require('Module:Utility/mw.html Extensions') local p = {} local cfg = mw.loadData('Module:WeaponInfobox/configuration') local aw = require('Module:Utility/Library') local iu = require('Module:Utility/Image') local nu = require('Module:Utility/Name') local proto = require('Module:Utility/Prototypes') 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) local ammo = nu.ammo(stat, lang) local classSuffix if aw.stringstarts(stat, 'special_') then classSuffix = 'special' else classSuffix = stat end local item if lang == 'ja' then item = string.format( '%s [[弾薬#%s|<span class="text-ammo text-ammo-%s">%s</span>]][[Category:%s]]', iu.ammo(stat, { size = 24 }), ammo, classSuffix, ammo, ammo) else item = string.format( '%s <span class="text-ammo text-ammo-%s">%s</span>[[Category:%s]]', iu.ammo(stat, { size = 24 }), classSuffix, ammo, ammo) end createCellInRow(tbl, cfg.name) :addClass('cell-ammo') :wikitext(item) 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 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) local builder = require('Module:Utility/StringBuilder').new() appendMode(builder, cfg, stat) if stat.altfire then builder:append(cfg.separator) appendMode(builder, cfg, stat.altfire, stat) end if stat.selectfire_receiver then builder:append(cfg.separator, iu.hopup('selectfire_receiver')) appendMode(builder, cfg, stat.selectfire_receiver, stat) end if stat.double_tap_trigger then builder:append(cfg.separator, iu.hopup('double_tap_trigger')) appendMode(builder, cfg, stat.double_tap_trigger, stat) end renderRow(tbl, cfg.name, tostring(builder)) end local function getDamageText(stat) if aw.isNumber(stat.pellet) then if aw.isNumberAndGreaterThanZero(stat.damage.charged) then return string.format( '<span class="text-type-number">%s</span> × %d → <span class="text-type-number">%s</span> × %d', stat.damage.base, stat.pellet, stat.damage.charged, stat.pellet) else return string.format( '<span class="text-type-number">%s</span> × %d', stat.damage.base, stat.pellet) end else if aw.isNumberAndGreaterThanZero(stat.damage.amped) then return string.format( '<span class="text-type-number">%s</span> <span class="text-separator">/</span> %s <span class="text-type-number">%s</span>', stat.damage.base, iu.item('シールドセル'), stat.damage.amped) elseif aw.isNumberAndGreaterThanZero(stat.damage.charged) then return string.format( '<span class="text-type-number">%s</span> → <span class="text-type-number">%s</span>', stat.damage.base, stat.damage.charged) else return string.format('<span class="text-type-number">%s</span>', stat.damage.base) end end end local function renderHeadRow(tbl, name, head, cfg, nolist) local hlm1 = 0.2 + 0.8 * head local hlm2 = 0.4 + 0.6 * head local hlm3 = 0.5 + 0.5 * head local opts = { align = 'left', footer = cfg.unit } if nolist then opts.class = 'no-list-style' opts.headerAlign = 'right' end renderFormatRow( tbl, name, nil, hlm1, hlm2, hlm3, opts) end local function renderLegsRow(tbl, cfg, stat) 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', stat.legshot, stat.legshot_charged, cfg.unit) else text = string.format( '<span class="text-type-number">%s</span>%s', stat.legshot, 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), headshot = proto.NumberRange(1), legshot = proto.NumberRange(0, 1), }, } local ShatterCapsProto = { damage = { base = proto.NumberRange(1), headshot = proto.NumberRange(1), legshot = proto.NumberRange(0, 1), }, pellet = proto.NumberRange(1), } local damageHopups = { { 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) if not proto.validateTypes(stat, DamageProto) then return end local cattext = string.format(cfg.head_category, stat.damage.headshot_charged or stat.damage.headshot) local cell = createCellInRow(tbl, cfg.name) cell:wikitext(cattext) :wikitext(getDamageText(stat)) -- Headshot local sprm = stat.damage.skullpiercer_rifling or 1 local hlmc = stat.damage.headshot_charged or 1 local intable = --tbl:tag('tr'):tag('td'):attr('colspan', 2) cell :tag('table') :addClass('condensedtable') :addClass('listtable') renderHeadRow(intable, cfg.head, stat.damage.headshot, cfg) if sprm > 1 then local nameS = iu.hopup('skullpiercer_rifling') .. ' ' renderHeadRow(intable, nameS, sprm, cfg, true) elseif hlmc > 1 then local nameC = '→ ' renderHeadRow(intable, nameC, hlmc, cfg, true) end -- Legsshot renderLegsRow(intable, cfg, stat.damage) -- [Hop-Up] Anvil Receiver & Shatter Caps for _, v in ipairs(damageHopups) do if proto.validateTypes(stat[v.name], v.proto) then local intblS = cell:tag('div') :addClass('tpl-weapon-inbox') :addClass(v.dispclass) :wikitext(iu.hopup(v.name) .. ' ') :tag('span') :addClass('text-rarity') :addClass(v.textclass) :wikitext(getDamageText(stat[v.name])) :done() :tag('table') :addClass('condensedtable') :addClass('listtable') renderHeadRow(intblS, cfg.head, stat[v.name].damage.headshot, cfg) renderLegsRow(intblS, cfg, stat[v.name].damage) end end end local function renderFirerate(tbl, cfg, stat, lang) local node, newcell = require('Module:WeaponInfobox/Firerate').renderFirerate(stat, lang) if newcell then tbl:tag('tr'):tag('td'):attr('colspan', 2):node(node) else createCellInRow(tbl, cfg.name):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 function renderMoveSpeed(tbl, cfg, move_speed, move_speed_charged) if not aw.isNumberAndGreaterThanZero(move_speed) then return end local text if 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, 100 * (move_speed - 1)), string.format(cfg.format, 100 * (move_speed_charged - 1)), cfg.unit) else text = string.format( '<span class="text-type-number">%s</span>%s', string.format(cfg.format, 100 * (move_speed - 1)), cfg.unit) end renderRow(tbl, cfg.name, text) end local function renderDPS(tbl, cfg, stat, lang) local sld = require('Module:Stat/Shield')['removelowprofile'] 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 = aw.round(1.15 * 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 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 }) renderFormatRow( intable, iu.passive('modded_loader') .. ' ', aw.round(1.15 * magazine[1]), aw.round(1.15 * magazine[2]), aw.round(1.15 * magazine[3]), aw.round(1.15 * magazine[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 all-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 all-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.quickdraw_holster and stat.quickdraw_holster.spread, lang) local cell = createCellInRow(tbl, cfg.name) if node.tagName == 'span' then cell:node(node) elseif stat.anvil_receiver and stat.anvil_receiver.spread then local node2 = Spread.renderSpread(stat.anvil_receiver.spread, 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 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, 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) renderDamage(tbl, cfg.damage, stat) 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.move_speed, stat.move_speed_charged) renderMagazine(tbl, cfg.magazine, stat, isSpecial, isModdedLoaderAttachable) if stat.firerate 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, 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