| 🌟 | 現在、 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:WeaponInfobox/DPS」の版間の差分
ナビゲーションに移動
検索に移動
(30-30リピーターのDPS計算の不具合を修正) |
(マスティフショットガンのDPS表がおかしなことになっている問題に対処) |
||
| (同じ利用者による、間の13版が非表示) | |||
| 3行目: | 3行目: | ||
local p = {} | local p = {} | ||
local cfg = mw.loadData('Module:WeaponInfobox/configuration') | 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 apex = require('Module:Utility/ApexLibrary') | local apex = require('Module:Utility/ApexLibrary') | ||
local iu = require('Module:Utility/Image') | local iu = require('Module:Utility/Image') | ||
local proto = require('Module:Utility/Prototypes') | local proto = require('Module:Utility/Prototypes') | ||
local table = require('Module:TableExtensions') | |||
-- Constants | -- Constants | ||
| 19行目: | 21行目: | ||
['border-left-width'] = '1px', | ['border-left-width'] = '1px', | ||
['border-left-style'] = 'solid', | ['border-left-style'] = 'solid', | ||
} | |||
local empty = {} | |||
local steplessChargeRates = { 0.5, 1 } | |||
local structures = { | |||
{ | |||
levels = 1, | |||
root = empty, | |||
}, | |||
{ | |||
levels = 2, | |||
root = { colspan = 2 }, | |||
level2 = { offset = 1 }, | |||
extra = { offset = 1 }, | |||
}, | |||
{ | |||
levels = 3, | |||
root = { colspan = 3 }, | |||
level2 = { colspan = 2, offset = 1 }, | |||
level3 = { offset = 2, styles = { nil, { leftBorder = true } } }, | |||
extra = { offset = 2 }, | |||
}, | |||
} | } | ||
-- Protos | -- Protos | ||
local | local MultipleProto = { | ||
proto.NumberRange(0), | proto.NumberRange(0), | ||
proto.NumberRange(0), | proto.NumberRange(0), | ||
| 32行目: | 52行目: | ||
proto.NumberRange(0), | proto.NumberRange(0), | ||
} | } | ||
local | local DamageProto = { | ||
damage_near_value = proto.NumberRange(1), | |||
damage_very_far_value = proto.NumberRange(1), | |||
} | |||
local AltfireProto = { | |||
altfire = { | |||
firerate = proto.NumberRange(0), | firerate = proto.NumberRange(0), | ||
} | |||
} | |||
local RevvedUpProto = { | |||
firerate_revvedup = proto.NumberRange(1), | |||
} | |||
local BurstProto = { | |||
burst_count = proto.NumberRange(2), | |||
burst_delay = proto.NumberRange(0), | |||
firerate = proto.NumberRange(0), | |||
} | |||
local MultipleBurstProto = { | |||
burst_count = proto.NumberRange(2), | |||
burst_delay = MultipleProto, | |||
firerate = proto.NumberRange(0), | |||
} | |||
local HammerpointRoundsProto = { | |||
damage_unshielded_scale = proto.NumberRange(1), | |||
} | } | ||
local | local DisruptorRoundsProto = { | ||
damage_shield_scale = proto.NumberRange(1), | |||
} | } | ||
local ShatterCapsProto = { | local ShatterCapsProto = { | ||
damage_near_value = proto.NumberRange(1), | |||
damage_very_far_value = proto.NumberRange(1), | |||
damage_head_scale = proto.NumberRange(1), | |||
damage_legs_scale = proto.NumberRange(0, 1), | |||
pellet = proto.NumberRange(2), | |||
} | } | ||
local function | -- Get property | ||
local function getProperty(stat, stat2, key, defaultValue) | |||
return stat. | if aw.isNumber(stat[key]) then | ||
return stat[key] | |||
elseif aw.isNumber(stat2[key]) then | |||
return stat2[key] | |||
else | |||
return defaultValue | |||
end | |||
end | |||
-- Get multiple number property | |||
local function getMultipleNumberProperty(stat, stat2, key, defaultValue) | |||
if proto.validateTypes(stat[key], MultipleProto) then | |||
return stat[key] | |||
elseif proto.validateTypes(stat2[key], MultipleProto) then | |||
return stat2[key] | |||
else | |||
return defaultValue | |||
end | |||
end | end | ||
local function | -- Get property | ||
local function getBooleanProperty(stat, stat2, key, defaultValue) | |||
if | if type(stat[key]) == 'boolean' then | ||
return stat[key] | |||
elseif type(stat2[key]) == 'boolean' then | |||
return stat2[key] | |||
else | else | ||
return defaultValue | |||
end | end | ||
end | end | ||
local function | -- Calc base damages | ||
local function calcBaseDamages(stat, stat2, chargeRate) | |||
chargeRate = chargeRate or 0 | |||
local | local chargeAddScale = getProperty(stat, stat2, 'charge_additional_scale', 0) | ||
if | local nearDamage = getProperty(stat, stat2, 'damage_near_value', 0) | ||
local verfarDamage = getProperty(stat, stat2, 'damage_very_far_value', nil) or getProperty(stat, stat2, 'damage_far_value', 0) | |||
local verfarDistance = getProperty(stat, stat2, 'damage_very_far_distance', nil) or getProperty(stat, stat2, 'damage_far_distance', 15748) | |||
if chargeRate > 0 then | |||
local mul = 1 + chargeRate * chargeAddScale | |||
nearDamage = aw.round(nearDamage * mul) | |||
verfarDamage = aw.round(verfarDamage * mul) | |||
end | |||
local headDistance = getProperty(stat, stat2, 'damage_head_distance', math.huge) | |||
local headScale = getProperty(stat, stat2, 'damage_head_scale', 1) | |||
local headLv1Scale = apex.calcHeadshotMultiplier(headScale, apex.HEAD_HLMLV1) | |||
local headLv2Scale = apex.calcHeadshotMultiplier(headScale, apex.HEAD_HLMLV2) | |||
local headLv3Scale = apex.calcHeadshotMultiplier(headScale, apex.HEAD_HLMLV3) | |||
local legsScale = getProperty(stat, stat2, 'damage_legs_scale', 1) | |||
local ret = { | |||
hed1max = apex.calcPartDamage(nearDamage, headLv1Scale), | |||
hed2max = apex.calcPartDamage(nearDamage, headLv2Scale), | |||
hed3max = apex.calcPartDamage(nearDamage, headLv3Scale), | |||
bodymax = nearDamage, | |||
legsmax = apex.calcPartDamage(nearDamage, legsScale), | |||
} | |||
if nearDamage == verfarDamage then | |||
--[[if verfarDistance > headDistance then | |||
ret.hed1min = ret.bodymax | |||
ret.hed2min = ret.bodymax | |||
ret.hed3min = ret.bodymax | |||
else]] | |||
ret.hed1min = ret.hed1max | |||
ret.hed2min = ret.hed2max | |||
ret.hed3min = ret.hed3max | |||
--end | |||
ret.bodymin = ret.bodymax | |||
ret.legsmin = ret.legsmax | |||
--[[elseif verfarDistance > headDistance then | |||
ret.hed1min = verfarDamage | |||
ret.hed2min = verfarDamage | |||
ret.hed3min = verfarDamage | |||
ret.bodymin = verfarDamage | |||
ret.legsmin = apex.calcPartDamage(verfarDamage, legsScale)]] | |||
else | else | ||
ret = | ret.hed1min = apex.calcPartDamage(verfarDamage, headLv1Scale) | ||
ret.hed2min = apex.calcPartDamage(verfarDamage, headLv2Scale) | |||
ret.hed3min = apex.calcPartDamage(verfarDamage, headLv3Scale) | |||
ret.bodymin = verfarDamage | |||
ret.legsmin = apex.calcPartDamage(verfarDamage, legsScale) | |||
end | end | ||
return ret | return ret | ||
end | end | ||
local function | -- Apply perk scale (Low Profile / Fortified) to damages | ||
local function applyPerkToDamages(damages, perk, useRound) | |||
if perk ~= 1 then | |||
return table.map(damages, function(key, damage) | |||
if aw.stringstarts(key, 'hed') then | |||
local | return damage | ||
else | |||
return apex.calcPassiveDamage(damage, perk, { round = useRound }) | |||
end | |||
end) | |||
else | |||
return damages | |||
end | |||
end | |||
-- Apply fresh scale to damages (BEFORE PERK SCALE) | |||
local function applyUnshieldScaleToDamages(stat, stat2, damages) | |||
local unshieldedScale = getProperty(stat, stat2, 'damage_unshielded_scale', 0) | |||
if unshieldedScale > 1 then | |||
return table.mapValues(damages, function(damage) | |||
return apex.calcHammerpointDamage(damage, unshieldedScale) | |||
end) | |||
else | |||
return damages | |||
end | |||
end | |||
-- Apply shield scale to damages (AFTER PERK SCALE) | |||
local function applyShieldScaleToDamages(stat, stat2, damages) | |||
local shieldScale = getProperty(stat, stat2, 'damage_shield_scale', 0) | |||
if shieldScale > 1 then | |||
return table.mapValues(damages, function(damage) | |||
return apex.calcDisruptorDamage(damage, shieldScale) | |||
end) | |||
else | |||
return damages | |||
end | |||
end | |||
-- Calc DPSs | |||
local function calcDPSs(stat, stat2, damages, firerate) | |||
local pellets = getProperty(stat, stat2, 'pellet', 1) | |||
--[[if pellets >= 2 then | |||
return table.map(damages, function(key, damage) | |||
if aw.stringends(key, 'max') then | |||
return damage * firerate * pellets | |||
else | |||
return damage * firerate | |||
end | |||
end) | |||
else]] | |||
return table.mapValues(damages, function(damage) | |||
return damage * firerate * pellets | |||
end) | |||
--end | |||
end | |||
-- Calc firerates | |||
local function calcFirerates(stat, stat2, chargeRate) | |||
chargeRate = chargeRate or 0 | |||
local charge = getProperty(stat, stat2, 'charge', 0) | |||
local firerate | |||
local rechamber = getMultipleNumberProperty(stat, stat2, 'rechamber', nil) | |||
if rechamber then | |||
firerate = getProperty(stat, stat2, 'firerate', 1) | |||
else | |||
firerate = getMultipleNumberProperty(stat, stat2, 'firerate', { 1, 1.15, 1.25, 1.35 }) | |||
end | |||
if proto.validateTypes(stat, MultipleBurstProto) then | |||
firerate = { | |||
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[1], stat.firerate), | |||
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[2], stat.firerate), | |||
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[3], stat.firerate), | |||
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[4], stat.firerate), | |||
} | |||
elseif rechamber then | |||
if charge > 0 then | |||
firerate = { | |||
apex.calcRechamberFirerate(firerate, rechamber[1] + chargeRate * charge), | |||
apex.calcRechamberFirerate(firerate, rechamber[2] + chargeRate * charge), | |||
apex.calcRechamberFirerate(firerate, rechamber[3] + chargeRate * charge), | |||
apex.calcRechamberFirerate(firerate, rechamber[4] + chargeRate * charge), | |||
} | |||
else | else | ||
firerate = { | |||
apex.calcRechamberFirerate(firerate, rechamber[1]), | |||
apex.calcRechamberFirerate(firerate, rechamber[2]), | |||
apex.calcRechamberFirerate(firerate, rechamber[3]), | |||
apex.calcRechamberFirerate(firerate, rechamber[4]), | |||
} | |||
end | end | ||
end | end | ||
return | return firerate | ||
end | end | ||
local function | -- Calc firerate | ||
if proto.validateTypes( | local function calcFirerate(stat, stat2, chargeRate) | ||
chargeRate = chargeRate or math.max(0, getProperty(stat, stat2, 'charge_minimum', 0)) | |||
local charge = getProperty(stat, stat2, 'charge', 0) | |||
local firerate = getProperty(stat, stat2, 'firerate', 1) | |||
local raise = getProperty(stat, stat2, 'raise', 0) | |||
local rechamber = getProperty(stat, stat2, 'rechamber', 0) | |||
local semiauto = getBooleanProperty(stat, stat2, 'is_semi_auto', false) | |||
if proto.validateTypes(stat, BurstProto) then | |||
firerate = apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay, stat.firerate) | |||
elseif rechamber > 0 then | |||
if charge > 0 then | |||
firerate = apex.calcRechamberFirerate(firerate, rechamber + chargeRate * charge) | |||
else | |||
firerate = apex.calcRechamberFirerate(firerate, rechamber) | |||
end | |||
elseif charge > 0 then | |||
firerate = apex.calcRechamberFirerate(firerate, chargeRate * charge) | |||
elseif semiauto and raise > 0 then | |||
firerate = apex.calcRechamberFirerate(firerate, raise) | |||
end | end | ||
return firerate | |||
end | end | ||
local function renderDPSRow(tbl, name, | local function renderDPSRow(tbl, name, dps, structure, opts) | ||
local row = tbl:tag('tr') | local row = tbl:tag('tr') | ||
if aw. | if aw.isNumberAndGreaterThanOrEqualToX(structure.offset, 1) then | ||
for i = 1, | for i = 1, structure.offset do | ||
local hasLeftBorder = | local hasLeftBorder = structure.styles and structure.styles[i] and structure.styles[i].leftBorder | ||
if hasLeftBorder then | if hasLeftBorder then | ||
row:tag('th') | row:tag('th') | ||
| 161行目: | 316行目: | ||
end | end | ||
row:tag('th') | row:tag('th') | ||
:attrIf(aw.isNumber( | :attrIf(aw.isNumber(structure.colspan), { colspan = structure.colspan }) | ||
:cssIf(aw.isNumberAndGreaterThanZero( | :cssIf(aw.isNumberAndGreaterThanZero(structure.offset), leftBorder) | ||
:wikitext(name) | :wikitext(name) | ||
if | |||
if opts.legsoff then | |||
row:tag('td') | |||
:attr('align', 'right') | |||
:wikitext(opts.format(dps.bodymax, dps.bodymin)) | |||
elseif dps.legsmax == dps.bodymax and dps.legsmin == dps.bodymin then | |||
row:tag('td') | |||
:attr({ align = 'right', colspan = 2 }) | |||
:wikitext(opts.format(dps.bodymax, dps.bodymin)) | |||
:done() | |||
else | |||
row | row | ||
:tag('td') | :tag('td') | ||
:attr('align', 'right') | :attr('align', 'right') | ||
:wikitext( | :wikitext(opts.format(dps.legsmax, dps.legsmin)) | ||
:done() | :done() | ||
:tag('td') | :tag('td') | ||
:attr('align', 'right') | :attr('align', 'right') | ||
:wikitext( | :wikitext(opts.format(dps.bodymax, dps.bodymin)) | ||
end | |||
row | |||
:tag('td') | |||
:attr('align', 'right') | |||
:wikitext(opts.format(dps.hed3max, dps.hed3min)) | |||
:done() | |||
:tag('td') | |||
:attr('align', 'right') | |||
:wikitext(opts.format(dps.hed2max, dps.hed2min)) | |||
:done() | |||
:tag('td') | |||
:attr('align', 'right') | |||
:wikitext(opts.format(dps.hed1max, dps.hed1min)) | |||
end | |||
-- Render the header node | |||
local function renderHeader(tbl, opts) | |||
local header = tbl:tag('tr'):tag('th') | |||
:attrIf(opts.structure.levels > 1, { colspan = opts.structure.levels }) | |||
:wikitext('') | |||
:done() | |||
if opts.legsoff then | |||
header:tag('th'):wikitext(opts.cfg.part.bodylegs) | |||
else | else | ||
header | |||
:tag(' | :tag('th'):wikitext(opts.cfg.part.legs):done() | ||
:tag('th'):wikitext(opts.cfg.part.body) | |||
end | |||
header | |||
:tag('th'):wikitext(iu.gear('helmet', 3)) | |||
:tag('th'):wikitext(iu.gear('helmet', 2)) | |||
:tag('th'):wikitext(iu.gear('helmet', 1)) | |||
end | |||
-- Render the DPS section for Shotguns | |||
local function renderShotgunDPSSection(tbl, name, stat, baseDamages, chargeLevels, firerates, perk, opts) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
-- Normal | |||
local dps = calcDPSs(stat, empty, perkDamages, firerates[1]) | |||
renderDPSRow(tbl, name, dps, opts.structure.root, opts) | |||
-- w/bolt lvl1 | |||
local label = iu.attachment('ショットガンボルト', 1) | |||
dps = calcDPSs(stat, empty, perkDamages, firerates[2]) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
-- w/bolt lvl2 | |||
label = iu.attachment('ショットガンボルト', 2) | |||
dps = calcDPSs(stat, empty, perkDamages, firerates[3]) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
-- w/bolt lvl3 | |||
label = iu.attachment('ショットガンボルト', 3) | |||
dps = calcDPSs(stat, empty, perkDamages, firerates[4]) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
-- Hop-ups | |||
for _, metadata in ipairs(opts.hopups) do | |||
local label = iu.hopup(metadata.key) | |||
local hopupStat = stat[metadata.key] | |||
local baseDamages = calcBaseDamages(hopupStat, stat) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
local chargeLevels = getProperty(hopupStat, stat, 'charge_levels', 1) | |||
local firerates = calcFirerates(hopupStat, stat) | |||
-- Normal | |||
local dps = calcDPSs(hopupStat, stat, perkDamages, firerates[1]) | |||
renderDPSRow(tbl, label, dps, opts.structure.level2, opts) | |||
-- w/bolt lvl1 | |||
label = iu.attachment('ショットガンボルト', 1) | |||
dps = calcDPSs(hopupStat, stat, perkDamages, firerates[2]) | |||
renderDPSRow(tbl, label, dps, opts.structure.level3, opts) | |||
-- w/bolt lvl2 | |||
label = iu.attachment('ショットガンボルト', 2) | |||
dps = calcDPSs(hopupStat, stat, perkDamages, firerates[3]) | |||
renderDPSRow(tbl, label, dps, opts.structure.level3, opts) | |||
-- w/bolt lvl3 | |||
label = iu.attachment('ショットガンボルト', 3) | |||
dps = calcDPSs(hopupStat, stat, perkDamages, firerates[4]) | |||
renderDPSRow(tbl, label, dps, opts.structure.level3, opts) | |||
end | end | ||
end | end | ||
function | -- Render the DPS section with shield/unshield scale | ||
local | local function renderShieldDPSSection(tbl, name, stat, stat2, baseDamages, firerate, perk, firstStructure, structure, opts) | ||
local | local unshldDPS = calcDPSs(stat, stat2, applyPerkToDamages(applyUnshieldScaleToDamages(stat, stat2, baseDamages), perk, opts.round), firerate) | ||
local shieldDPS = calcDPSs(stat, stat2, applyPerkToDamages(applyShieldScaleToDamages(stat, stat2, baseDamages), perk, opts.round), firerate) | |||
local lvl1DPS = {} | |||
local | local lvl2DPS = {} | ||
local lvl3DPS = {} | |||
local lvl5DPS = {} | |||
for key in pairs(unshldDPS) do | |||
lvl1DPS[key] = (2 * unshldDPS[key] + shieldDPS[key]) / 3 | |||
lvl2DPS[key] = (4 * unshldDPS[key] + 3 * shieldDPS[key]) / 7 | |||
lvl3DPS[key] = ( unshldDPS[key] + shieldDPS[key]) / 2 | |||
lvl5DPS[key] = (4 * unshldDPS[key] + 5 * shieldDPS[key]) / 9 | |||
end | |||
-- w/o shield | |||
renderDPSRow(tbl, name, unshldDPS, firstStructure, opts) | |||
-- w/shield lvl1 | |||
local label = iu.gear('evo_shield', 1) | |||
renderDPSRow(tbl, label, lvl1DPS, structure, opts) | |||
-- w/shield lvl2 | |||
label = iu.gear('evo_shield', 2) | |||
renderDPSRow(tbl, label, lvl2DPS, structure, opts) | |||
-- w/shield lvl3 | |||
label = iu.gear('evo_shield', 3) | |||
renderDPSRow(tbl, label, lvl3DPS, structure, opts) | |||
-- w/shield lvl5 | |||
label = iu.gear('evo_shield', 5) | |||
renderDPSRow(tbl, label, lvl5DPS, structure, opts) | |||
end | |||
-- Render the DPS section (Normal weapons) | |||
local function renderNormalDPSSection(tbl, name, stat, baseDamages, chargeLevels, firerate, perk, opts) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
-- Normal | |||
if not (opts.special and #opts.hopups > 0 and opts.hopups[1].key == 'disruptor_rounds') then | |||
local dps = calcDPSs(stat, empty, perkDamages, firerate) | |||
renderDPSRow(tbl, name, dps, opts.structure.root, opts) | |||
end | |||
-- Altfire | |||
if opts.flags.mode_altfire then | |||
local label = opts.cfg.altfire | |||
local firerate = stat.altfire.firerate | |||
local dps = calcDPSs(stat.altfire, stat, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
end | |||
local | -- Amped mode | ||
if opts.flags.mode_amped then | |||
local label = iu.item('シールドセル') | |||
local baseDamages = calcBaseDamages(stat.energized, stat, chargeRate) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
local dps = calcDPSs(stat.energized, stat, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
end | |||
-- | -- Revved Up mode | ||
if opts.flags.mode_revvedup then | |||
if | local label = iu.grenade('テルミットグレネード') | ||
local firerate = stat.firerate_revvedup | |||
local dps = calcDPSs(stat, empty, perkDamages, firerate) | |||
firerate = | renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | ||
end | end | ||
-- Chargefire | |||
if chargeLevels == 2 then | |||
if | for _, chargeRate in ipairs(steplessChargeRates) do | ||
local label | |||
if chargeRate >= 1 then | |||
label = opts.cfg.charge_stepless_max | |||
elseif chargeRate == 0.5 then | |||
label = opts.cfg.charge_stepless_half | |||
else | |||
label = string.format(opts.cfg.charge_stepless_format, 100 * chargeRate) | |||
end | |||
local baseDamages = calcBaseDamages(stat, empty, chargeRate) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
local firerate = calcFirerate(stat, empty, chargeRate) | |||
local dps = calcDPSs(stat, empty, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
end | end | ||
elseif chargeLevels > 2 then | |||
elseif | for chargeLevel = 1, chargeLevels - 1 do | ||
local chargeRate = chargeLevel / (chargeLevels - 1) | |||
local | local label = aw.getQuantityString(opts.cfg.charge_step_quantity, chargeLevel) | ||
local firerate = calcFirerate(stat, empty, chargeRate) | |||
local dps = calcDPSs(stat, empty, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.extra, opts) | |||
end | end | ||
end | end | ||
-- Hop-ups | -- Hop-ups | ||
for _, metadata in ipairs(opts.hopups) do | |||
local label = iu.hopup(metadata.key) | |||
local hopupStat = stat[metadata.key] | |||
local baseDamages = calcBaseDamages(hopupStat, stat) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
local chargeLevels = getProperty(hopupStat, stat, 'charge_levels', 1) | |||
local firerate = calcFirerate(hopupStat, stat) | |||
-- Hammerpoint Rounds / Disruptor Rounds | |||
if metadata.key == 'hammerpoint_rounds' or metadata.key == 'disruptor_rounds' then | |||
local | local item, root, next | ||
if opts.special then | |||
item = name | |||
root = opts.structure.root | |||
next = opts.structure.level2 | |||
else | |||
local | item = label | ||
root = opts.structure.level2 | |||
next = opts.structure.level3 | |||
end | |||
renderShieldDPSSection(tbl, item, hopupStat, stat, baseDamages, firerate, perk, root, next, opts) | |||
-- Other | |||
else | else | ||
local dps = calcDPSs(hopupStat, stat, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.level2, opts) | |||
-- Amped mode | |||
if opts.flags.mode_amped then | |||
local label = iu.item('シールドセル') | |||
local mergedStat = aw.mergeTable(hopupStat, stat.energized) | |||
local baseDamages = calcBaseDamages(mergedStat, stat, chargeRate) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
local dps = calcDPSs(mergedStat, stat, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.level3, opts) | |||
end | end | ||
if | |||
-- Chargefire | |||
if chargeLevels == 2 then | |||
for _, chargeRate in ipairs(steplessChargeRates) do | |||
local label | |||
if chargeRate >= 1 then | |||
label = opts.cfg.charge_stepless_max | |||
elseif chargeRate == 0.5 then | |||
label = opts.cfg.charge_stepless_half | |||
else | |||
label = string.format(opts.cfg.charge_stepless_format, 100 * chargeRate) | |||
end | |||
local baseDamages = calcBaseDamages(hopupStat, stat, chargeRate) | |||
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round) | |||
local firerate = calcFirerate(hopupStat, stat, chargeRate) | |||
local dps = calcDPSs(hopupStat, stat, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.level3, opts) | |||
end | |||
elseif chargeLevels > 2 then | |||
for chargeLevel = 1, chargeLevels - 1 do | |||
local chargeRate = chargeLevel / (chargeLevels - 1) | |||
local label = aw.getQuantityString(opts.cfg.charge_step_quantity, chargeLevel) | |||
local firerate = calcFirerate(hopupStat, stat, chargeRate) | |||
local dps = calcDPSs(hopupStat, stat, perkDamages, firerate) | |||
renderDPSRow(tbl, label, dps, opts.structure.level3, opts) | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
end | |||
local function numformat(num) | |||
if num >= 100 then | |||
num = aw.roundx(num, 1) | |||
local int = math.floor(num) | |||
return string.format('%d<span class="text-smaller text-secondary">.%01.0f</span>', int, 10 * (num - int)) | |||
elseif num >= 10 then | |||
num = aw.roundx(num, 2) | |||
local int = math.floor(num) | |||
return string.format('%d<span class="text-smaller text-secondary">.%02.0f</span>', int, 100 * (num - int)) | |||
else | else | ||
num = aw.roundx(num, 3) | |||
local int = math.floor(num) | |||
return string.format('%d<span class="text-smaller text-secondary">.%03.0f</span>', int, 1000 * (num - int)) | |||
end | end | ||
end | |||
local function numformatmin(num) | |||
if num >= 100 then | |||
if | num = aw.round(num) | ||
local int = math.floor(num) | |||
local | return string.format('%d', int) | ||
elseif num >= 10 then | |||
num = aw.roundx(num, 1) | |||
local int = math.floor(num) | |||
return string.format('%d<span class="text-smaller text-secondary">.%01.0f</span>', int, 10 * (num - int)) | |||
else | else | ||
num = aw.roundx(num, 2) | |||
local int = math.floor(num) | |||
return string.format('%d<span class="text-smaller text-secondary">.%02.0f</span>', int, 100 * (num - int)) | |||
end | end | ||
end | |||
function p.renderDPS(stat, shieldinfo, lang) | |||
local cfg = cfg[lang].dps | |||
local opts = { | |||
cfg = cfg, | |||
flags = {}, | |||
format = function(max, min) | |||
if max ~= min then | |||
return string.format(cfg.complex, numformatmin(min), numformat(max)) | |||
else | |||
return string.format(cfg.simple, numformat(max)) | |||
end | |||
end, | |||
hopups = {}, | |||
legsoff = (stat.damage_legs_scale or 1) == 1, | |||
round = aw.getAsBoolean(stat.damage.round, false), | |||
special = aw.stringstarts(stat.ammo, 'special_'), | |||
} | |||
local baseDamages = calcBaseDamages(stat, empty) | |||
local chargeLevels = getProperty(stat, empty, 'charge_levels', 1) | |||
local tbl = mw.html.create('table'):addClass('intable') | |||
-- | -- Shotgun | ||
if | if stat.ammo == 'shotgun' then | ||
local | local firerates = calcFirerates(stat, empty) | ||
local level = 2 | |||
if (Hopup.double_tap_trigger.enabled or opts.special) and proto.validateTypes(stat.double_tap_trigger, MultipleBurstProto) then | |||
level = math.max(level, 3) | |||
table.insert(opts.hopups, { | |||
key = 'double_tap_trigger', | |||
}) | |||
end | |||
opts.structure = structures[level] | |||
renderHeader(tbl, opts) | |||
renderShotgunDPSSection(tbl, opts.cfg.perk.normal, stat, baseDamages, chargeLevels, firerates, 1, opts) | |||
-- Low Profile | |||
if aw.isNumber(shieldinfo.lowprofile) and shieldinfo.lowprofile > 1 then | |||
renderShotgunDPSSection(tbl, opts.cfg.perk.lowprofile, stat, baseDamages, chargeLevels, firerates, shieldinfo.lowprofile, opts) | |||
end | |||
-- | -- Fortified | ||
if | if aw.isNumber(shieldinfo.fortified) and shieldinfo.fortified < 1 then | ||
renderShotgunDPSSection(tbl, opts.cfg.perk.fortified, stat, baseDamages, chargeLevels, firerates, shieldinfo.fortified, opts) | |||
end | end | ||
-- | -- Other | ||
else | |||
local firerate = calcFirerate(stat, empty) | |||
local level = 1 | |||
if proto.validateTypes(stat, AltfireProto) then | |||
level = math.max(level, 2) | |||
opts.flags.mode_altfire = true | |||
end | |||
if proto.validateTypes(stat, RevvedUpProto) then | |||
level = math.max(level, 2) | |||
opts.flags.mode_revvedup = true | |||
end | |||
if (Hopup.selectfire_receiver.enabled or opts.special) and type(stat.selectfire_receiver) == 'table' then | |||
level = math.max(level, 2) | |||
table.insert(opts.hopups, { | |||
key = 'selectfire_receiver', | |||
}) | |||
end | end | ||
if Hopup.hammerpoint_rounds.enabled and proto.validateTypes(stat.hammerpoint_rounds, HammerpointRoundsProto) then | |||
level = math.max(level, 3) | |||
if | table.insert(opts.hopups, { | ||
key = 'hammerpoint_rounds', | |||
}) | |||
end | end | ||
if (Hopup.disruptor_rounds.enabled or opts.special) and proto.validateTypes(stat.disruptor_rounds, DisruptorRoundsProto) then | |||
if opts.special then | |||
if | level = math.max(level, 2) | ||
else | |||
level = math.max(level, 3) | |||
if | |||
end | end | ||
table.insert(opts.hopups, { | |||
key = 'disruptor_rounds', | |||
}) | |||
end | |||
if Hopup.anvil_receiver.enabled and type(stat.anvil_receiver) == 'table' then | |||
level = math.max(level, 2) | |||
table.insert(opts.hopups, { | |||
key = 'anvil_receiver', | |||
}) | |||
end | |||
if (Hopup.double_tap_trigger.enabled or opts.special) and proto.validateTypes(stat.double_tap_trigger, BurstProto) then | |||
level = math.max(level, 2) | |||
table.insert(opts.hopups, { | |||
key = 'double_tap_trigger', | |||
}) | |||
end | |||
if Hopup.shatter_caps.enabled and proto.validateTypes(stat.shatter_caps, ShatterCapsProto) then | |||
level = math.max(level, 2) | |||
table.insert(opts.hopups, { | |||
key = 'shatter_caps', | |||
}) | |||
end | |||
if Hopup.deadeyes_tempo.enabled and type(stat.deadeyes_tempo) == 'table' then | |||
level = math.max(level, 2) | |||
table.insert(opts.hopups, { | |||
key = 'deadeyes_tempo', | |||
}) | |||
end | |||
if proto.validateTypes(stat.energized, DamageProto) then | |||
level = level + 1 | |||
opts.flags.mode_amped = true | |||
elseif chargeLevels >= 2 then | |||
level = level + 1 | |||
end | end | ||
opts.structure = structures[level] | |||
renderHeader(tbl, opts) | |||
renderNormalDPSSection(tbl, opts.cfg.perk.normal, stat, baseDamages, chargeLevels, firerate, 1, opts) | |||
-- Low Profile | |||
if aw.isNumber(shieldinfo.lowprofile) and shieldinfo.lowprofile > 1 then | |||
renderNormalDPSSection(tbl, opts.cfg.perk.lowprofile, stat, baseDamages, chargeLevels, firerate, shieldinfo.lowprofile, opts) | |||
end | end | ||
-- | -- Fortified | ||
if | if aw.isNumber(shieldinfo.fortified) and shieldinfo.fortified < 1 then | ||
renderNormalDPSSection(tbl, opts.cfg.perk.fortified, stat, baseDamages, chargeLevels, firerate, shieldinfo.fortified, opts) | |||
end | end | ||
end | end | ||
| 500行目: | 759行目: | ||
local stat = mw.loadData('Module:Stat/Weapon')[name] | local stat = mw.loadData('Module:Stat/Weapon')[name] | ||
local sld = require('Module:Stat/Shield')[' | local sld = require('Module:Stat/Shield')['reinforcehelmets'] | ||
local node = p.renderDPS(stat, sld, lang) | local node = p.renderDPS(stat, sld, lang) | ||
2022年5月23日 (月) 13:14時点における最新版
このモジュールについての説明文ページを モジュール:WeaponInfobox/DPS/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 proto = require('Module:Utility/Prototypes')
local table = require('Module:TableExtensions')
-- Constants
local leftBorder = {
['border-left-width'] = '1px',
['border-left-style'] = 'solid',
}
local leftBorderWithoutTopRightBorder = {
['border-top'] = '0 none transparent',
['border-right'] = '0 none transparent',
['border-left-width'] = '1px',
['border-left-style'] = 'solid',
}
local empty = {}
local steplessChargeRates = { 0.5, 1 }
local structures = {
{
levels = 1,
root = empty,
},
{
levels = 2,
root = { colspan = 2 },
level2 = { offset = 1 },
extra = { offset = 1 },
},
{
levels = 3,
root = { colspan = 3 },
level2 = { colspan = 2, offset = 1 },
level3 = { offset = 2, styles = { nil, { leftBorder = true } } },
extra = { offset = 2 },
},
}
-- Protos
local MultipleProto = {
proto.NumberRange(0),
proto.NumberRange(0),
proto.NumberRange(0),
proto.NumberRange(0),
}
local DamageProto = {
damage_near_value = proto.NumberRange(1),
damage_very_far_value = proto.NumberRange(1),
}
local AltfireProto = {
altfire = {
firerate = proto.NumberRange(0),
}
}
local RevvedUpProto = {
firerate_revvedup = proto.NumberRange(1),
}
local BurstProto = {
burst_count = proto.NumberRange(2),
burst_delay = proto.NumberRange(0),
firerate = proto.NumberRange(0),
}
local MultipleBurstProto = {
burst_count = proto.NumberRange(2),
burst_delay = MultipleProto,
firerate = proto.NumberRange(0),
}
local HammerpointRoundsProto = {
damage_unshielded_scale = proto.NumberRange(1),
}
local DisruptorRoundsProto = {
damage_shield_scale = proto.NumberRange(1),
}
local ShatterCapsProto = {
damage_near_value = proto.NumberRange(1),
damage_very_far_value = proto.NumberRange(1),
damage_head_scale = proto.NumberRange(1),
damage_legs_scale = proto.NumberRange(0, 1),
pellet = proto.NumberRange(2),
}
-- Get property
local function getProperty(stat, stat2, key, defaultValue)
if aw.isNumber(stat[key]) then
return stat[key]
elseif aw.isNumber(stat2[key]) then
return stat2[key]
else
return defaultValue
end
end
-- Get multiple number property
local function getMultipleNumberProperty(stat, stat2, key, defaultValue)
if proto.validateTypes(stat[key], MultipleProto) then
return stat[key]
elseif proto.validateTypes(stat2[key], MultipleProto) then
return stat2[key]
else
return defaultValue
end
end
-- Get property
local function getBooleanProperty(stat, stat2, key, defaultValue)
if type(stat[key]) == 'boolean' then
return stat[key]
elseif type(stat2[key]) == 'boolean' then
return stat2[key]
else
return defaultValue
end
end
-- Calc base damages
local function calcBaseDamages(stat, stat2, chargeRate)
chargeRate = chargeRate or 0
local chargeAddScale = getProperty(stat, stat2, 'charge_additional_scale', 0)
local nearDamage = getProperty(stat, stat2, 'damage_near_value', 0)
local verfarDamage = getProperty(stat, stat2, 'damage_very_far_value', nil) or getProperty(stat, stat2, 'damage_far_value', 0)
local verfarDistance = getProperty(stat, stat2, 'damage_very_far_distance', nil) or getProperty(stat, stat2, 'damage_far_distance', 15748)
if chargeRate > 0 then
local mul = 1 + chargeRate * chargeAddScale
nearDamage = aw.round(nearDamage * mul)
verfarDamage = aw.round(verfarDamage * mul)
end
local headDistance = getProperty(stat, stat2, 'damage_head_distance', math.huge)
local headScale = getProperty(stat, stat2, 'damage_head_scale', 1)
local headLv1Scale = apex.calcHeadshotMultiplier(headScale, apex.HEAD_HLMLV1)
local headLv2Scale = apex.calcHeadshotMultiplier(headScale, apex.HEAD_HLMLV2)
local headLv3Scale = apex.calcHeadshotMultiplier(headScale, apex.HEAD_HLMLV3)
local legsScale = getProperty(stat, stat2, 'damage_legs_scale', 1)
local ret = {
hed1max = apex.calcPartDamage(nearDamage, headLv1Scale),
hed2max = apex.calcPartDamage(nearDamage, headLv2Scale),
hed3max = apex.calcPartDamage(nearDamage, headLv3Scale),
bodymax = nearDamage,
legsmax = apex.calcPartDamage(nearDamage, legsScale),
}
if nearDamage == verfarDamage then
--[[if verfarDistance > headDistance then
ret.hed1min = ret.bodymax
ret.hed2min = ret.bodymax
ret.hed3min = ret.bodymax
else]]
ret.hed1min = ret.hed1max
ret.hed2min = ret.hed2max
ret.hed3min = ret.hed3max
--end
ret.bodymin = ret.bodymax
ret.legsmin = ret.legsmax
--[[elseif verfarDistance > headDistance then
ret.hed1min = verfarDamage
ret.hed2min = verfarDamage
ret.hed3min = verfarDamage
ret.bodymin = verfarDamage
ret.legsmin = apex.calcPartDamage(verfarDamage, legsScale)]]
else
ret.hed1min = apex.calcPartDamage(verfarDamage, headLv1Scale)
ret.hed2min = apex.calcPartDamage(verfarDamage, headLv2Scale)
ret.hed3min = apex.calcPartDamage(verfarDamage, headLv3Scale)
ret.bodymin = verfarDamage
ret.legsmin = apex.calcPartDamage(verfarDamage, legsScale)
end
return ret
end
-- Apply perk scale (Low Profile / Fortified) to damages
local function applyPerkToDamages(damages, perk, useRound)
if perk ~= 1 then
return table.map(damages, function(key, damage)
if aw.stringstarts(key, 'hed') then
return damage
else
return apex.calcPassiveDamage(damage, perk, { round = useRound })
end
end)
else
return damages
end
end
-- Apply fresh scale to damages (BEFORE PERK SCALE)
local function applyUnshieldScaleToDamages(stat, stat2, damages)
local unshieldedScale = getProperty(stat, stat2, 'damage_unshielded_scale', 0)
if unshieldedScale > 1 then
return table.mapValues(damages, function(damage)
return apex.calcHammerpointDamage(damage, unshieldedScale)
end)
else
return damages
end
end
-- Apply shield scale to damages (AFTER PERK SCALE)
local function applyShieldScaleToDamages(stat, stat2, damages)
local shieldScale = getProperty(stat, stat2, 'damage_shield_scale', 0)
if shieldScale > 1 then
return table.mapValues(damages, function(damage)
return apex.calcDisruptorDamage(damage, shieldScale)
end)
else
return damages
end
end
-- Calc DPSs
local function calcDPSs(stat, stat2, damages, firerate)
local pellets = getProperty(stat, stat2, 'pellet', 1)
--[[if pellets >= 2 then
return table.map(damages, function(key, damage)
if aw.stringends(key, 'max') then
return damage * firerate * pellets
else
return damage * firerate
end
end)
else]]
return table.mapValues(damages, function(damage)
return damage * firerate * pellets
end)
--end
end
-- Calc firerates
local function calcFirerates(stat, stat2, chargeRate)
chargeRate = chargeRate or 0
local charge = getProperty(stat, stat2, 'charge', 0)
local firerate
local rechamber = getMultipleNumberProperty(stat, stat2, 'rechamber', nil)
if rechamber then
firerate = getProperty(stat, stat2, 'firerate', 1)
else
firerate = getMultipleNumberProperty(stat, stat2, 'firerate', { 1, 1.15, 1.25, 1.35 })
end
if proto.validateTypes(stat, MultipleBurstProto) then
firerate = {
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[1], stat.firerate),
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[2], stat.firerate),
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[3], stat.firerate),
apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay[4], stat.firerate),
}
elseif rechamber then
if charge > 0 then
firerate = {
apex.calcRechamberFirerate(firerate, rechamber[1] + chargeRate * charge),
apex.calcRechamberFirerate(firerate, rechamber[2] + chargeRate * charge),
apex.calcRechamberFirerate(firerate, rechamber[3] + chargeRate * charge),
apex.calcRechamberFirerate(firerate, rechamber[4] + chargeRate * charge),
}
else
firerate = {
apex.calcRechamberFirerate(firerate, rechamber[1]),
apex.calcRechamberFirerate(firerate, rechamber[2]),
apex.calcRechamberFirerate(firerate, rechamber[3]),
apex.calcRechamberFirerate(firerate, rechamber[4]),
}
end
end
return firerate
end
-- Calc firerate
local function calcFirerate(stat, stat2, chargeRate)
chargeRate = chargeRate or math.max(0, getProperty(stat, stat2, 'charge_minimum', 0))
local charge = getProperty(stat, stat2, 'charge', 0)
local firerate = getProperty(stat, stat2, 'firerate', 1)
local raise = getProperty(stat, stat2, 'raise', 0)
local rechamber = getProperty(stat, stat2, 'rechamber', 0)
local semiauto = getBooleanProperty(stat, stat2, 'is_semi_auto', false)
if proto.validateTypes(stat, BurstProto) then
firerate = apex.calcBurstAverageFirerate(stat.burst_count, stat.burst_delay, stat.firerate)
elseif rechamber > 0 then
if charge > 0 then
firerate = apex.calcRechamberFirerate(firerate, rechamber + chargeRate * charge)
else
firerate = apex.calcRechamberFirerate(firerate, rechamber)
end
elseif charge > 0 then
firerate = apex.calcRechamberFirerate(firerate, chargeRate * charge)
elseif semiauto and raise > 0 then
firerate = apex.calcRechamberFirerate(firerate, raise)
end
return firerate
end
local function renderDPSRow(tbl, name, dps, structure, opts)
local row = tbl:tag('tr')
if aw.isNumberAndGreaterThanOrEqualToX(structure.offset, 1) then
for i = 1, structure.offset do
local hasLeftBorder = structure.styles and structure.styles[i] and structure.styles[i].leftBorder
if hasLeftBorder then
row:tag('th')
:css(leftBorderWithoutTopRightBorder)
:wikitext(' ')
else
row:tag('th')
:css('border', '0 none transparent')
:wikitext(' ')
end
end
end
row:tag('th')
:attrIf(aw.isNumber(structure.colspan), { colspan = structure.colspan })
:cssIf(aw.isNumberAndGreaterThanZero(structure.offset), leftBorder)
:wikitext(name)
if opts.legsoff then
row:tag('td')
:attr('align', 'right')
:wikitext(opts.format(dps.bodymax, dps.bodymin))
elseif dps.legsmax == dps.bodymax and dps.legsmin == dps.bodymin then
row:tag('td')
:attr({ align = 'right', colspan = 2 })
:wikitext(opts.format(dps.bodymax, dps.bodymin))
:done()
else
row
:tag('td')
:attr('align', 'right')
:wikitext(opts.format(dps.legsmax, dps.legsmin))
:done()
:tag('td')
:attr('align', 'right')
:wikitext(opts.format(dps.bodymax, dps.bodymin))
end
row
:tag('td')
:attr('align', 'right')
:wikitext(opts.format(dps.hed3max, dps.hed3min))
:done()
:tag('td')
:attr('align', 'right')
:wikitext(opts.format(dps.hed2max, dps.hed2min))
:done()
:tag('td')
:attr('align', 'right')
:wikitext(opts.format(dps.hed1max, dps.hed1min))
end
-- Render the header node
local function renderHeader(tbl, opts)
local header = tbl:tag('tr'):tag('th')
:attrIf(opts.structure.levels > 1, { colspan = opts.structure.levels })
:wikitext('')
:done()
if opts.legsoff then
header:tag('th'):wikitext(opts.cfg.part.bodylegs)
else
header
:tag('th'):wikitext(opts.cfg.part.legs):done()
:tag('th'):wikitext(opts.cfg.part.body)
end
header
:tag('th'):wikitext(iu.gear('helmet', 3))
:tag('th'):wikitext(iu.gear('helmet', 2))
:tag('th'):wikitext(iu.gear('helmet', 1))
end
-- Render the DPS section for Shotguns
local function renderShotgunDPSSection(tbl, name, stat, baseDamages, chargeLevels, firerates, perk, opts)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
-- Normal
local dps = calcDPSs(stat, empty, perkDamages, firerates[1])
renderDPSRow(tbl, name, dps, opts.structure.root, opts)
-- w/bolt lvl1
local label = iu.attachment('ショットガンボルト', 1)
dps = calcDPSs(stat, empty, perkDamages, firerates[2])
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
-- w/bolt lvl2
label = iu.attachment('ショットガンボルト', 2)
dps = calcDPSs(stat, empty, perkDamages, firerates[3])
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
-- w/bolt lvl3
label = iu.attachment('ショットガンボルト', 3)
dps = calcDPSs(stat, empty, perkDamages, firerates[4])
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
-- Hop-ups
for _, metadata in ipairs(opts.hopups) do
local label = iu.hopup(metadata.key)
local hopupStat = stat[metadata.key]
local baseDamages = calcBaseDamages(hopupStat, stat)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
local chargeLevels = getProperty(hopupStat, stat, 'charge_levels', 1)
local firerates = calcFirerates(hopupStat, stat)
-- Normal
local dps = calcDPSs(hopupStat, stat, perkDamages, firerates[1])
renderDPSRow(tbl, label, dps, opts.structure.level2, opts)
-- w/bolt lvl1
label = iu.attachment('ショットガンボルト', 1)
dps = calcDPSs(hopupStat, stat, perkDamages, firerates[2])
renderDPSRow(tbl, label, dps, opts.structure.level3, opts)
-- w/bolt lvl2
label = iu.attachment('ショットガンボルト', 2)
dps = calcDPSs(hopupStat, stat, perkDamages, firerates[3])
renderDPSRow(tbl, label, dps, opts.structure.level3, opts)
-- w/bolt lvl3
label = iu.attachment('ショットガンボルト', 3)
dps = calcDPSs(hopupStat, stat, perkDamages, firerates[4])
renderDPSRow(tbl, label, dps, opts.structure.level3, opts)
end
end
-- Render the DPS section with shield/unshield scale
local function renderShieldDPSSection(tbl, name, stat, stat2, baseDamages, firerate, perk, firstStructure, structure, opts)
local unshldDPS = calcDPSs(stat, stat2, applyPerkToDamages(applyUnshieldScaleToDamages(stat, stat2, baseDamages), perk, opts.round), firerate)
local shieldDPS = calcDPSs(stat, stat2, applyPerkToDamages(applyShieldScaleToDamages(stat, stat2, baseDamages), perk, opts.round), firerate)
local lvl1DPS = {}
local lvl2DPS = {}
local lvl3DPS = {}
local lvl5DPS = {}
for key in pairs(unshldDPS) do
lvl1DPS[key] = (2 * unshldDPS[key] + shieldDPS[key]) / 3
lvl2DPS[key] = (4 * unshldDPS[key] + 3 * shieldDPS[key]) / 7
lvl3DPS[key] = ( unshldDPS[key] + shieldDPS[key]) / 2
lvl5DPS[key] = (4 * unshldDPS[key] + 5 * shieldDPS[key]) / 9
end
-- w/o shield
renderDPSRow(tbl, name, unshldDPS, firstStructure, opts)
-- w/shield lvl1
local label = iu.gear('evo_shield', 1)
renderDPSRow(tbl, label, lvl1DPS, structure, opts)
-- w/shield lvl2
label = iu.gear('evo_shield', 2)
renderDPSRow(tbl, label, lvl2DPS, structure, opts)
-- w/shield lvl3
label = iu.gear('evo_shield', 3)
renderDPSRow(tbl, label, lvl3DPS, structure, opts)
-- w/shield lvl5
label = iu.gear('evo_shield', 5)
renderDPSRow(tbl, label, lvl5DPS, structure, opts)
end
-- Render the DPS section (Normal weapons)
local function renderNormalDPSSection(tbl, name, stat, baseDamages, chargeLevels, firerate, perk, opts)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
-- Normal
if not (opts.special and #opts.hopups > 0 and opts.hopups[1].key == 'disruptor_rounds') then
local dps = calcDPSs(stat, empty, perkDamages, firerate)
renderDPSRow(tbl, name, dps, opts.structure.root, opts)
end
-- Altfire
if opts.flags.mode_altfire then
local label = opts.cfg.altfire
local firerate = stat.altfire.firerate
local dps = calcDPSs(stat.altfire, stat, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
end
-- Amped mode
if opts.flags.mode_amped then
local label = iu.item('シールドセル')
local baseDamages = calcBaseDamages(stat.energized, stat, chargeRate)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
local dps = calcDPSs(stat.energized, stat, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
end
-- Revved Up mode
if opts.flags.mode_revvedup then
local label = iu.grenade('テルミットグレネード')
local firerate = stat.firerate_revvedup
local dps = calcDPSs(stat, empty, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
end
-- Chargefire
if chargeLevels == 2 then
for _, chargeRate in ipairs(steplessChargeRates) do
local label
if chargeRate >= 1 then
label = opts.cfg.charge_stepless_max
elseif chargeRate == 0.5 then
label = opts.cfg.charge_stepless_half
else
label = string.format(opts.cfg.charge_stepless_format, 100 * chargeRate)
end
local baseDamages = calcBaseDamages(stat, empty, chargeRate)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
local firerate = calcFirerate(stat, empty, chargeRate)
local dps = calcDPSs(stat, empty, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
end
elseif chargeLevels > 2 then
for chargeLevel = 1, chargeLevels - 1 do
local chargeRate = chargeLevel / (chargeLevels - 1)
local label = aw.getQuantityString(opts.cfg.charge_step_quantity, chargeLevel)
local firerate = calcFirerate(stat, empty, chargeRate)
local dps = calcDPSs(stat, empty, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.extra, opts)
end
end
-- Hop-ups
for _, metadata in ipairs(opts.hopups) do
local label = iu.hopup(metadata.key)
local hopupStat = stat[metadata.key]
local baseDamages = calcBaseDamages(hopupStat, stat)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
local chargeLevels = getProperty(hopupStat, stat, 'charge_levels', 1)
local firerate = calcFirerate(hopupStat, stat)
-- Hammerpoint Rounds / Disruptor Rounds
if metadata.key == 'hammerpoint_rounds' or metadata.key == 'disruptor_rounds' then
local item, root, next
if opts.special then
item = name
root = opts.structure.root
next = opts.structure.level2
else
item = label
root = opts.structure.level2
next = opts.structure.level3
end
renderShieldDPSSection(tbl, item, hopupStat, stat, baseDamages, firerate, perk, root, next, opts)
-- Other
else
local dps = calcDPSs(hopupStat, stat, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.level2, opts)
-- Amped mode
if opts.flags.mode_amped then
local label = iu.item('シールドセル')
local mergedStat = aw.mergeTable(hopupStat, stat.energized)
local baseDamages = calcBaseDamages(mergedStat, stat, chargeRate)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
local dps = calcDPSs(mergedStat, stat, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.level3, opts)
end
-- Chargefire
if chargeLevels == 2 then
for _, chargeRate in ipairs(steplessChargeRates) do
local label
if chargeRate >= 1 then
label = opts.cfg.charge_stepless_max
elseif chargeRate == 0.5 then
label = opts.cfg.charge_stepless_half
else
label = string.format(opts.cfg.charge_stepless_format, 100 * chargeRate)
end
local baseDamages = calcBaseDamages(hopupStat, stat, chargeRate)
local perkDamages = applyPerkToDamages(baseDamages, perk, opts.round)
local firerate = calcFirerate(hopupStat, stat, chargeRate)
local dps = calcDPSs(hopupStat, stat, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.level3, opts)
end
elseif chargeLevels > 2 then
for chargeLevel = 1, chargeLevels - 1 do
local chargeRate = chargeLevel / (chargeLevels - 1)
local label = aw.getQuantityString(opts.cfg.charge_step_quantity, chargeLevel)
local firerate = calcFirerate(hopupStat, stat, chargeRate)
local dps = calcDPSs(hopupStat, stat, perkDamages, firerate)
renderDPSRow(tbl, label, dps, opts.structure.level3, opts)
end
end
end
end
end
local function numformat(num)
if num >= 100 then
num = aw.roundx(num, 1)
local int = math.floor(num)
return string.format('%d<span class="text-smaller text-secondary">.%01.0f</span>', int, 10 * (num - int))
elseif num >= 10 then
num = aw.roundx(num, 2)
local int = math.floor(num)
return string.format('%d<span class="text-smaller text-secondary">.%02.0f</span>', int, 100 * (num - int))
else
num = aw.roundx(num, 3)
local int = math.floor(num)
return string.format('%d<span class="text-smaller text-secondary">.%03.0f</span>', int, 1000 * (num - int))
end
end
local function numformatmin(num)
if num >= 100 then
num = aw.round(num)
local int = math.floor(num)
return string.format('%d', int)
elseif num >= 10 then
num = aw.roundx(num, 1)
local int = math.floor(num)
return string.format('%d<span class="text-smaller text-secondary">.%01.0f</span>', int, 10 * (num - int))
else
num = aw.roundx(num, 2)
local int = math.floor(num)
return string.format('%d<span class="text-smaller text-secondary">.%02.0f</span>', int, 100 * (num - int))
end
end
function p.renderDPS(stat, shieldinfo, lang)
local cfg = cfg[lang].dps
local opts = {
cfg = cfg,
flags = {},
format = function(max, min)
if max ~= min then
return string.format(cfg.complex, numformatmin(min), numformat(max))
else
return string.format(cfg.simple, numformat(max))
end
end,
hopups = {},
legsoff = (stat.damage_legs_scale or 1) == 1,
round = aw.getAsBoolean(stat.damage.round, false),
special = aw.stringstarts(stat.ammo, 'special_'),
}
local baseDamages = calcBaseDamages(stat, empty)
local chargeLevels = getProperty(stat, empty, 'charge_levels', 1)
local tbl = mw.html.create('table'):addClass('intable')
-- Shotgun
if stat.ammo == 'shotgun' then
local firerates = calcFirerates(stat, empty)
local level = 2
if (Hopup.double_tap_trigger.enabled or opts.special) and proto.validateTypes(stat.double_tap_trigger, MultipleBurstProto) then
level = math.max(level, 3)
table.insert(opts.hopups, {
key = 'double_tap_trigger',
})
end
opts.structure = structures[level]
renderHeader(tbl, opts)
renderShotgunDPSSection(tbl, opts.cfg.perk.normal, stat, baseDamages, chargeLevels, firerates, 1, opts)
-- Low Profile
if aw.isNumber(shieldinfo.lowprofile) and shieldinfo.lowprofile > 1 then
renderShotgunDPSSection(tbl, opts.cfg.perk.lowprofile, stat, baseDamages, chargeLevels, firerates, shieldinfo.lowprofile, opts)
end
-- Fortified
if aw.isNumber(shieldinfo.fortified) and shieldinfo.fortified < 1 then
renderShotgunDPSSection(tbl, opts.cfg.perk.fortified, stat, baseDamages, chargeLevels, firerates, shieldinfo.fortified, opts)
end
-- Other
else
local firerate = calcFirerate(stat, empty)
local level = 1
if proto.validateTypes(stat, AltfireProto) then
level = math.max(level, 2)
opts.flags.mode_altfire = true
end
if proto.validateTypes(stat, RevvedUpProto) then
level = math.max(level, 2)
opts.flags.mode_revvedup = true
end
if (Hopup.selectfire_receiver.enabled or opts.special) and type(stat.selectfire_receiver) == 'table' then
level = math.max(level, 2)
table.insert(opts.hopups, {
key = 'selectfire_receiver',
})
end
if Hopup.hammerpoint_rounds.enabled and proto.validateTypes(stat.hammerpoint_rounds, HammerpointRoundsProto) then
level = math.max(level, 3)
table.insert(opts.hopups, {
key = 'hammerpoint_rounds',
})
end
if (Hopup.disruptor_rounds.enabled or opts.special) and proto.validateTypes(stat.disruptor_rounds, DisruptorRoundsProto) then
if opts.special then
level = math.max(level, 2)
else
level = math.max(level, 3)
end
table.insert(opts.hopups, {
key = 'disruptor_rounds',
})
end
if Hopup.anvil_receiver.enabled and type(stat.anvil_receiver) == 'table' then
level = math.max(level, 2)
table.insert(opts.hopups, {
key = 'anvil_receiver',
})
end
if (Hopup.double_tap_trigger.enabled or opts.special) and proto.validateTypes(stat.double_tap_trigger, BurstProto) then
level = math.max(level, 2)
table.insert(opts.hopups, {
key = 'double_tap_trigger',
})
end
if Hopup.shatter_caps.enabled and proto.validateTypes(stat.shatter_caps, ShatterCapsProto) then
level = math.max(level, 2)
table.insert(opts.hopups, {
key = 'shatter_caps',
})
end
if Hopup.deadeyes_tempo.enabled and type(stat.deadeyes_tempo) == 'table' then
level = math.max(level, 2)
table.insert(opts.hopups, {
key = 'deadeyes_tempo',
})
end
if proto.validateTypes(stat.energized, DamageProto) then
level = level + 1
opts.flags.mode_amped = true
elseif chargeLevels >= 2 then
level = level + 1
end
opts.structure = structures[level]
renderHeader(tbl, opts)
renderNormalDPSSection(tbl, opts.cfg.perk.normal, stat, baseDamages, chargeLevels, firerate, 1, opts)
-- Low Profile
if aw.isNumber(shieldinfo.lowprofile) and shieldinfo.lowprofile > 1 then
renderNormalDPSSection(tbl, opts.cfg.perk.lowprofile, stat, baseDamages, chargeLevels, firerate, shieldinfo.lowprofile, opts)
end
-- Fortified
if aw.isNumber(shieldinfo.fortified) and shieldinfo.fortified < 1 then
renderNormalDPSSection(tbl, opts.cfg.perk.fortified, stat, baseDamages, chargeLevels, firerate, shieldinfo.fortified, opts)
end
end
return tbl
end
function p._main(name, lang)
lang = lang or 'ja'
local stat = mw.loadData('Module:Stat/Weapon')[name]
local sld = require('Module:Stat/Shield')['reinforcehelmets']
local node = p.renderDPS(stat, sld, lang)
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(name .. iu.ammo(stat.ammo, { size = 40 }))
:done()
:tag('table')
:tag('tr')
:tag('th'):wikitext('DPS'):done()
:tag('td'):done()
:tag('tr')
:tag('td'):attr('colspan', 2):node(node)
return tostring(div)
end
return p