🌟 | 現在、 鉄壁ヘッドショットには対応済みです。 鉄壁HSは通常HSと同じダメージになります。LMG及びDMR、チャージライフル、ハンマーポイント弾を除き、すべてのダメージ値が一致していることを確認しています。 |
「モジュール:Apex/TTKCalculator」の版間の差分
ナビゲーションに移動
検索に移動
(セグメントリロードのストックによる短縮効果の実装) |
(セグメントリロードのストックによる短縮効果の不具合を修正) |
||
60行目: | 60行目: | ||
self.reload = tonumber(reloads) | self.reload = tonumber(reloads) | ||
elseif self.isSegmented then | elseif self.isSegmented then | ||
self.reload = ((self.magazine - 1) * self.reloads + self.reloadEnd) * attachment.sniper_stock.reload_segment[1 + self. | if self.level > 0 then | ||
self.reload = ((self.magazine - 1) * self.reloads + self.reloadEnd) * attachment.sniper_stock.reload_segment[self.level] | |||
else | |||
self.reload = (self.magazine - 1) * self.reloads + self.reloadEnd | |||
end | |||
else | else | ||
self.reload = reloads | self.reload = reloads |
2021年8月16日 (月) 19:18時点における版
このモジュールについての説明文ページを モジュール:Apex/TTKCalculator/doc に作成できます
local TTKCalculator = {} local attachment = mw.loadData('Module:Stat/Attachment') local aw = require('Module:Utility/Library') -- モード TTKCalculator.BURST = 0 TTKCalculator.AUTO = 100 TTKCalculator.SINGLE = 200 TTKCalculator.SINGLE_ANVIL_RECEIVER = 201 -- [prop] 発射レート function TTKCalculator:_loadFirerates() local firerates = self.firerates local type = type(firerates) if type == 'table' then self.firerate = firerates[1 + self.level] elseif type == 'string' then self.firerate = tonumber(firerates) else self.firerate = firerates end end function TTKCalculator:setFirerates(firerates) self.firerates = firerates or 1 self:_loadFirerates() end -- [prop] 装填数 function TTKCalculator:_loadMagazines() local magazines = self.magazines local type = type(magazines) if type == 'table' then self.magazine = magazines[1 + self.level] elseif type == 'string' then self.magazine = tonumber(magazines) else self.magazine = magazines end end function TTKCalculator:setMagazines(magazines) self.magazines = magazines or 20 self:_loadMagazines() if self.isSegmented then self:_loadReloads() end end -- [prop] リロード時間 function TTKCalculator:_loadReloads() local reloads = self.reloads local type = type(reloads) if type == 'table' then self.reload = reloads[1 + self.level] elseif type == 'string' then self.reload = tonumber(reloads) elseif self.isSegmented then if self.level > 0 then self.reload = ((self.magazine - 1) * self.reloads + self.reloadEnd) * attachment.sniper_stock.reload_segment[self.level] else self.reload = (self.magazine - 1) * self.reloads + self.reloadEnd end else self.reload = reloads end end function TTKCalculator:setReloads(reloads) self.reloads = reloads or 0 self:_loadReloads() end -- [prop] リチャンバー時間 function TTKCalculator:_loadRechambers() local rechambers = self.rechambers local type = type(rechambers) if type == 'table' then self.rechamber = rechambers[1 + self.level] elseif type == 'string' then self.rechamber = tonumber(rechambers) else self.rechamber = rechambers end end function TTKCalculator:setRechambers(rechambers) self.rechambers = rechambers or 0 self:_loadRechambers() end -- [prop] レベル function TTKCalculator:_load() self:_loadFirerates() self:_loadMagazines() self:_loadReloads() self:_loadRechambers() end function TTKCalculator:setLevel(level) self.level = level self:_load() end -- [func] TTK算出関数 function TTKCalculator:_getTtk(left) return (left - 1) / self.firerate + (left - 1) * (self.rechamber + self.charged) end function TTKCalculator:_getDeadeyesTempoTtkCore(left) return (left - 1) / self.firerate + (left - 1) * (self.tempoRechamber + self.tempoCharged) end function TTKCalculator:_getDeadeyesTempoTtk(left) local targetCount = 1 + self.maximum if left <= targetCount then return self:_getTtk(left) else local normalTTK = self:_getTtk(targetCount) local tempoTTK = self:_getDeadeyesTempoTtkCore(left - self.maximum) return normalTTK + tempoTTK end end function TTKCalculator:_getBurstTtkInOneMagazine(left, skipLastDelay) local ttk = 0 while left > 0 do local current = math.min(self.burst, left) left = left - current if current > 1 then ttk = ttk + (current - 1) / self.firerate end if left == 0 and skipLastDelay then break end if current == self.burst then ttk = ttk + self.burstDelay end end return ttk end function TTKCalculator:_getVariableFirerateTtkInOneMagazine(left) local ttk = 0 -- The first left = left - 1 -- To the last while left > 0 do left = left - 1 local current = self.start + (self.firerate - self.start) * math.min(1, ttk / self.duration) local time = 1 / current ttk = ttk + time + self.rechamber + self.charged end return ttk end function TTKCalculator:get(shot) local ttk = 0 local magazine = math.floor(self.magazine / self.roundsPerShot) -- バースト if self.burst > 1 and self.burst <= magazine then local function getTtkInOneMag(left, burst, burstDelay, firerate) end while shot > magazine do local shotTTK = self:_getBurstTtkInOneMagazine(magazine) ttk = ttk + self.raise + shotTTK + self.reload shot = shot - magazine end local shotTTK = self:_getBurstTtkInOneMagazine(shot, true) ttk = ttk + self.raise + shotTTK -- 可変オート elseif self.start > 0 and self.duration > 0 then local first = true local virtualMagazine = magazine - self.maximum if magazine > 0 then if self.startMaximum and shot > virtualMagazine then local shotTTK = self:_getTtk(virtualMagazine) ttk = ttk + self.raise + shotTTK + self.reload shot = shot - virtualMagazine first = false end while shot > magazine do local shotTTK = self:_getVariableFirerateTtkInOneMagazine(magazine) ttk = ttk + self.raise + shotTTK + self.reload shot = shot - magazine end end local shotTTK if self.startMaximum and first then shotTTK = self:_getTtk(shot) else shotTTK = self:_getVariableFirerateTtkInOneMagazine(shot) end ttk = ttk + self.raise + shotTTK -- 最高速スタート w/デッドアイズテンポ elseif self.startMaximum and self.tempoCharged > 0 or self.tempoRechamber > 0 then local first = true local virtualMagazine = magazine - self.maximum if magazine > 0 then if self.startMaximum and shot > virtualMagazine then local shotTTK = self:_getDeadeyesTempoTtkCore(virtualMagazine) ttk = ttk + self.raise + shotTTK + self.reload shot = shot - virtualMagazine first = false end while shot > magazine do local shotTTK = self:_getDeadeyesTempoTtk(magazine) ttk = ttk + self.raise + shotTTK + self.reload shot = shot - magazine end end local shotTTK if self.startMaximum and first then shotTTK = self:_getDeadeyesTempoTtkCore(shot) else shotTTK = self:_getDeadeyesTempoTtk(shot) end ttk = ttk + self.raise + shotTTK -- 非バースト (オート・単発) else local getTtk -- デッドアイズテンポ if self.tempoCharged > 0 or self.tempoRechamber > 0 then getTtk = TTKCalculator._getDeadeyesTempoTtk -- 通常 else getTtk = TTKCalculator._getTtk end if magazine > 0 then while shot > magazine do local shotTTK = getTtk(self, magazine) ttk = ttk + self.raise + shotTTK + self.reload shot = shot - magazine end end local shotTTK = getTtk(self, shot) ttk = ttk + self.raise + shotTTK end return ttk end function TTKCalculator:getAsLevel(shot, level) self:setLevel(level) return self:get(shot) end -- コンストラクター function TTKCalculator.new(firerates, magazines, reloads, opts) local obj = setmetatable({ firerates = firerates or 1, magazines = magazines or math.huge, reloads = reloads or 0, raise = opts.raise or 0, start = opts.start or 0, duration = opts.duration or 0, maximum = opts.maximum or 0, startMaximum = opts.startMaximum or false, burst = opts.burst or 1, burstDelay = opts.burstDelay or 0, charged = opts.charged or 0, tempoCharged = opts.tempoCharged or 0, rechambers = opts.rechambers or 0, tempoRechamber = opts.tempoRechamber or 0, reloadEnd = opts.reloadEnd or 0, roundsPerShot = opts.roundsPerShot or 1, isSegmented = opts.isSegmented, level = opts.level or 0, }, { __index = TTKCalculator }) obj:_load() return obj end function TTKCalculator.newFromStat(stat, mode, opts2) mode = mode or TTKCalculator.AUTO opts2 = opts2 or {} local firerate, magazine, reload local opts = { startMaximum = opts2.startMaximum or false } local isModdedLoader = (stat.category == 'light_machine_gun' or stat.ammo == 'minigun') and opts2.useModdedLoader -- バースト if stat.mode.burst > 1 and mode == TTKCalculator.BURST then firerate = stat.firerate.burst opts.burst = stat.mode.burst opts.burstDelay = stat.firerate.burst_delay opts.rechambers = 0 opts.roundsPerShot = stat.ammo_per_shot or 1 -- オート elseif stat.mode.auto and (mode == TTKCalculator.AUTO or not stat.mode.single) then if opts2.useRevvedUpMode then firerate = stat.firerate.auto_amped or stat.firerate.auto else firerate = stat.firerate.auto end if opts2.useTurbocharger and stat.turbocharger then opts.raise = stat.turbocharger.firerate.auto_raise or stat.firerate.auto_raise or 0 opts.start = stat.turbocharger.firerate.auto_start or stat.firerate.auto_start or 0 opts.duration = stat.turbocharger.firerate.auto_duration or stat.firerate.auto_duration or 0 opts.maximum = stat.turbocharger.firerate.auto_maximum or stat.firerate.auto_maximum or 0 else opts.raise = stat.firerate.auto_raise or 0 opts.start = stat.firerate.auto_start or 0 opts.duration = stat.firerate.auto_duration or 0 opts.maximum = stat.firerate.auto_maximum or 0 end opts.rechambers = 0 opts.roundsPerShot = stat.ammo_per_shot or 1 -- 単発 elseif stat.mode.single then -- アンビルレシーバー if stat.anvil_receiver and mode == TTKCalculator.SINGLE_ANVIL_RECEIVER then firerate = stat.anvil_receiver.firerate.single or stat.firerate.single opts.charged = 0 opts.rechambers = stat.anvil_receiver.firerate.single_rechamber or stat.firerate.single_rechamber or 0 opts.roundsPerShot = stat.anvil_receiver.ammo_per_shot or 2 -- 通常の単発 else firerate = stat.firerate.single if opts2.useCharged then opts.charged = stat.firerate.single_charged or 0 else opts.charged = stat.firerate.single_charged_minimum or 0 end opts.rechambers = stat.firerate.single_rechamber or 0 opts.roundsPerShot = stat.ammo_per_shot or 1 -- デッドアイズテンポ if opts2.useDeadeyesTempo then if opts2.useCharged and aw.isNumberAndGreaterThanZero(stat.deadeyes_tempo.firerate.single_charged) then opts.tempoCharged = stat.deadeyes_tempo.firerate.single_charged end if aw.isNumberAndGreaterThanZero(stat.deadeyes_tempo.firerate.single_rechamber) then opts.tempoRechamber = stat.deadeyes_tempo.firerate.single_rechamber end opts.maximum = stat.deadeyes_tempo.firerate.single_maximum end end -- プレースホルダー else firerate = 1 opts.charged = 0 opts.rechambers = 0 opts.roundsPerShot = 1 end -- 装填数 if isModdedLoader then if type(stat.magazine) == 'table' then if aw.stringstarts(stat.ammo, 'special_') then magazine = aw.round(1.15 * stat.magazine[4]) else magazine = { aw.round(1.15 * stat.magazine[1]), aw.round(1.15 * stat.magazine[2]), aw.round(1.15 * stat.magazine[3]), aw.round(1.15 * stat.magazine[4]), } end elseif stat.magazine == math.huge then if type(stat.overheat) == 'table' then magazine = { math.ceil(1.15 * stat.overheat[1] * firerate), math.ceil(1.15 * stat.overheat[2] * firerate), math.ceil(1.15 * stat.overheat[3] * firerate), math.ceil(1.15 * stat.overheat[4] * firerate), } elseif aw.isNumberAndGreaterThanZero(stat.overheat) then magazine = math.ceil(1.15 * stat.overheat * firerate) else magazine = math.huge -- dummy end else magazine = aw.round(1.15 * stat.magazine) end -- L-スターEMG elseif stat.magazine == math.huge then if type(stat.overheat) == 'table' then magazine = { math.ceil(stat.overheat[1] * firerate), math.ceil(stat.overheat[2] * firerate), math.ceil(stat.overheat[3] * firerate), math.ceil(stat.overheat[4] * firerate), } elseif aw.isNumberAndGreaterThanZero(stat.overheat) then magazine = math.ceil(stat.overheat * firerate) else magazine = math.huge -- dummy end -- ブーステッドローダー elseif opts2.useBoostedLoader then magazine = stat.boosted_loader and stat.boosted_loader.magazine or stat.magazine -- 拡張マガジンがつく場合 elseif type(stat.magazine) == 'table' then -- 物資投下武器 if aw.stringstarts(stat.ammo, 'special_') then magazine = stat.magazine[4] else magazine = stat.magazine end -- 拡張マガジンがつかない場合 else magazine = stat.magazine end -- セグメントリロード if aw.isNumberAndGreaterThanZero(stat.time.reload_segment_loop) and aw.isNumberAndGreaterThanZero(stat.time.reload_segment_end) then reload = stat.time.reload_segment_loop or 0 opts.isSegmented = true opts.reloadEnd = stat.time.reload_segment_end or 0 -- 拡張マガジンがつかない elseif not stat.attachments.extended_mag_or_shotgun_bolt then reload = stat.time.reloadempty or stat.time.reload -- 物資投下武器 elseif aw.stringstarts(stat.ammo, 'special_') then -- ストックがつく if stat.attachments.stock then -- スナイパーストック if stat.category == 'sniper' or stat.category == 'marksman_weapon' then reload = (stat.time.reloadempty or stat.time.reload) * attachment.sniper_stock.reload[3] -- 標準ストック else reload = (stat.time.reloadempty or stat.time.reload) * attachment.standard_stock.reload[3] end -- ストックがつかない else reload = stat.time.reloadempty or stat.time.reload end -- ストックがつかない elseif not stat.attachments.stock then reload = stat.time.reloadempty or stat.time.reload -- その他 else local muls if stat.category == 'sniper' or stat.category == 'marksman_weapon' then muls = attachment.sniper_stock.reload else muls = attachment.standard_stock.reload end local base = stat.time.reloadempty or stat.time.reload if isModdedLoader then base = 0.75 * base end reload = { base, muls[1] * base, muls[2] * base, muls[3] * base, } end return TTKCalculator.new(firerate, magazine, reload, opts) end function TTKCalculator._test(name, count, opts) local stat = mw.loadData('Module:Stat/Weapon')[name] local mode = stat.mode.auto and TTKCalculator.AUTO or TTKCalculator.SINGLE local ttkc = TTKCalculator.newFromStat(stat, mode, opts) return ttkc:get(count) end return TTKCalculator