111 bugfix250107.1

点击登录看百科更方便

全站通知:

模块:DPS

来自War RobotsWIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

此模块的文档可以在模块:DPS/doc创建

local p = {}
--------------------------------------------------------
--  DPS算法模块 作者 啃泥丝,请勿搬运或公开
--------------------------------------------------------
--  工具函数:判断是否有值
p["是否有值"] = function(frame)
    local value = frame.args[1] or ""
    return value ~= nil and mw.text.trim(value) ~= ""
end
--  工具函数:将传入的值 约束为纯数字
local function toNum(val)
    return tonumber(val) or 0
end
--  工具函数:四舍五入 取为整数
local function round(x, digits)
    digits = digits or 0
    local scale = 10 ^ digits
    return math.floor(x * scale + 0.5) / scale
end
--  工具函数:给数字添加千分位的逗号
local function numComma(n)
    local num = tonumber(n)
    if not num then return "0" end
    local str = tostring(math.floor(num)) -- 去掉小数部分 
    return str:reverse():gsub("(%d%d%d)", "%1,"):reverse():gsub("^,", "")
end
--  工具函数:构造 context
local function buildContext(args)
    local level = toNum(args["级"])
    local levelKey = "面板伤害" .. tostring(level)
    local oneShotDamage = toNum(args[levelKey])

    local context = {
        level = level,
        oneShotDamage = oneShotDamage,
        name = args["英文"],
        reloadWhileshot = args["边射边装"], -- 非数值字段保留原样
        
        varDamageMod = toNum(args["伤害修正"]),
        ammoEachShot = toNum(args["每发消耗子弹"]),
        timeEachReload = toNum(args["每次装弹时间"]),
        ammoEachReload = toNum(args["每次装弹数量"]),
        timeShotInterval = toNum(args["射击间隔"]),
        ammoAllPack = toNum(args["弹匣总数"]),
        ammoEachBurst = toNum(args["连发子弹量"]),
        timeShotIntervalBurst = toNum(args["连发间隔"]),
        timeOverheatPause = toNum(args["过热停火时间"]),
        timeOverheatCool = toNum(args["过热冷却时间"]),
        timeOverheatShotInterval = toNum(args["过热射速"]),
        timeOverheatTrigger = toNum(args["过热触发时间"]),
        stacksCharge = toNum(args["蓄能层数"]),
        timeAccShotInterval = toNum(args["加速射击间隔"]),
        timeAccTrigger = toNum(args["加速激活时间"]),

        rawArgs = args
    }
    
    -- 通用预计算公式(所有子函数都能用)
    -- 弹匣伤害 = 一共打了几发 * 每发伤害
    context.damageAllPack = ( context.ammoAllPack / context.ammoEachShot ) * ( context.oneShotDamage / context.varDamageMod )  
    
    return context
    
end

--------------------------------------------------------
--------------------------------------------------------
--  主入口
p["DPS算法模块"] = function(frame)
    local context = buildContext(frame.args)
    return p["主判断机制"](context)
end

--------------------------------------------------------
--  对于特殊类别武器的判断
--------------------------------------------------------
local weaponCategoryMap = {}
-- 工具函数:批量设置映射
local function addCategory(names, category)
    for _, name in ipairs(names) do
        weaponCategoryMap[name] = category
    end
end

--  添加例外情况
addCategory({ "Howler", "Growler", "Screamer" , "Sport_Howler" , "Sport_Screamer" }, "音爆")

--  主判断机制
p["主判断机制"] = function(context)
    local args = context.rawArgs
    local name = context.rawArgs["英文"] or ""
    local category = weaponCategoryMap[name]

    if p["是否有值"]{args = {args["边射边装"]}} then
    	
    	if p["是否有值"]{args = {args["连发子弹量"]}} then
    		if category == "音爆" then
                return "音爆的例外情况"
    		else
                return p["计算_边射边装_连发子弹量"](context)
            end
    	end
        if p["是否有值"]{args = {args["过热触发时间"]}} then
            return p["计算_边射边装_过热触发时间"](context)
        end
        if p["是否有值"]{args = {args["加速射击间隔"]}} then
            return p["计算_边射边装_加速射击间隔"](context)
        end
        if not p["是否有值"]{args = {args["连发子弹量"]}} and
           not p["是否有值"]{args = {args["过热触发时间"]}} and
           not p["是否有值"]{args = {args["加速射击间隔"]}} then
            return p["计算_边射边装_无额外机制"](context)
        end
        return "边射边装的例外情况"

    else
        if p["是否有值"]{args = {args["连发子弹量"]}} then
            return p["计算_弹匣装弹_连发子弹量"](context)
        end
        if p["是否有值"]{args = {args["蓄能层数"]}} then
            return p["计算_弹匣装弹_蓄能层数"](context)
        end
        if p["是否有值"]{args = {args["加速射击间隔"]}} then
            return p["计算_弹匣装弹_加速射击间隔"](context)
        end
        if not p["是否有值"]{args = {args["连发子弹量"]}} and
           not p["是否有值"]{args = {args["过热触发时间"]}} and
           not p["是否有值"]{args = {args["加速射击间隔"]}} then
            return p["计算_弹匣装弹_无额外机制"](context)
        end
        return "弹匣装弹的例外情况"
    end
end



-- 添加各类
addCategory({"Biomechanoid_Nucleon", "Futuristic_Nucleon", "Nucleon","Techno_Atomizer", "Ivory_Atomizer", "Atomizer","Futuristic_Quarker", "Quarker" }, "雾枪")
addCategory({ "Inferno", "Pyro", "武器测试页面" }, "泰坦火")
addCategory({ "Arbiter", "Equalizer" }, "泰坦转轮炸")
addCategory({"Ardent_Hwangje", "Hwangje","Ardent_Yeoje", "Yeoje","Ardent_Taeja", "Taeja"}, "雾光")

p["计算_边射边装_过热触发时间"] = function(context)
    local name = context.rawArgs["英文"] or ""
    local category = weaponCategoryMap[name]

    if category == "雾枪" then
        return p["计算_边射边装_过热触发时间_雾枪"](context)
    elseif category == "泰坦火" then
        return p["计算_边射边装_过热触发时间_泰坦火"](context)
    elseif category == "泰坦转轮炸" then
        return p["计算_边射边装_过热触发时间_泰坦转轮炸"](context)
    elseif category == "雾光" then
        return p["计算_边射边装_过热触发时间_雾光"](context)
    else
        return "边射边装 过热机制 的例外情况"
    end
end


--------------------------------------------------------
--  计算公式
--------------------------------------------------------

p["计算_弹匣装弹_无额外机制"] = function(context)
	-- 清空弹匣时间 = 一共几个间隔 * 每个间隔的时间
	context.timeShotAllPack = ( context.ammoAllPack / context.ammoEachShot -1 ) * context.timeShotInterval
    return
    	"武器 " .. context.name .. " 所属机制【弹匣装弹 无额外机制】 \n\n" ..
    	"按住开火," .. round( context.timeShotAllPack ,2) .. " 秒内射击 "  .. round( context.ammoAllPack / context.ammoEachShot ,0) .. " 次,直至射空弹匣 \n\n" ..
        "弹匣伤害 = " .. numComma(round( context.damageAllPack ,0)) .. " (指把整个弹匣射空造成的总伤害) \n\n" ..
        "爆发DPS = " .. numComma(round( context.damageAllPack / context.timeShotAllPack ,0)) .. " (纯输出时,造成的DPS) \n\n" ..
        "持续DPS = " .. numComma(round( context.damageAllPack / (context.timeShotAllPack + context.timeEachReload) ,0)) .. " (算上装弹所需时间,造成的DPS) \n\n" 
end

p["计算_弹匣装弹_连发子弹量"] = function(context)
	-- 清空弹匣时间 = 一共几个间隔 * 每个间隔的时间
	context.timeShotAllPack = ( context.ammoAllPack / context.ammoEachShot -1 ) * context.timeShotInterval
	-- 连发轮数 = 弹匣总数 / 每轮射几发
	context.numBurstGroup = context.ammoAllPack / context.ammoEachBurst 
	-- 总轮间时间=(连发轮数-1)*连发间隔时间
	context.timeAllBurstGap = ( context.numBurstGroup - 1 ) * context.timeShotIntervalBurst 
	-- 总射击间隔时间=(连发弹药量-1)*连发轮数*射击间隔时间
	context.timeAllOnlyShotInterval = ( context.ammoEachBurst -1 ) * context.numBurstGroup * context.timeShotInterval 
	-- 清空弹匣时间 = 总轮间时间+总射击间隔时间
	context.timeShotAllPack = context.timeAllBurstGap + context.timeAllOnlyShotInterval
	-- 爆发dps=弹匣伤害/射空弹匣时间
	context.bDPS = context.damageAllPack / context.timeShotAllPack
	context.cDPS = context.damageAllPack / ( context.timeShotAllPack + context.timeEachReload )
    return
    	"武器 " .. context.name .. " 所属机制【弹匣装弹 多连发武器】 \n\n" ..
    	"按住开火," .. round( context.timeShotAllPack ,2) .. " 秒内 " .. context.numBurstGroup .. " 轮,共射击 "  .. round( context.ammoAllPack / context.ammoEachShot ,0) .. " 次,直至射空弹匣 \n\n" ..
        "弹匣伤害 = " .. numComma(round( context.damageAllPack ,0)) .. " (指把整个弹匣射空造成的总伤害) \n\n" ..
        "爆发DPS = " .. numComma(round( context.bDPS ,0)) .. " (纯输出时,造成的DPS) \n\n" ..
        "持续DPS = " .. numComma(round( context.cDPS ,0)) .. " (算上装弹所需时间,造成的DPS) \n\n" 
end

p["计算_弹匣装弹_蓄能层数"] = function(context)
	-- 无蓄能伤害伤害 = 蓄满能伤害 / 蓄能次数
	context.unChargeDamage = context.oneShotDamage / context.ammoAllPack
	-- 小 dps = 无蓄能伤害伤害 / 射击间隔
	context.cDPS0 = context.unChargeDamage / context.timeEachReload 
	-- 大 dps = 蓄满能伤害 / 每次装弹时间(=初次装弹时间+一共续几次能乘一秒)
	context.cDPS1 = context.oneShotDamage / ( context.timeEachReload + context.ammoAllPack )
    return
    	"武器 " .. context.name .. " 所属机制【弹匣装弹 蓄能武器】 \n\n" ..
    	"按住开火每 " .. round( context.timeEachReload ,2) .. " 秒,即可射击一次。或等待 "  ..  round( ( context.timeEachReload + context.ammoAllPack ) ,2)  .. " 秒,蓄满能 \n\n" ..
        "小持续DPS = " .. numComma(round( context.cDPS0 ,0)) .. " (不蓄能,有一发射一发) \n\n" ..
        "大持续DPS = " .. numComma(round( context.cDPS1 ,0)) .. " (蓄满能射一发,周而复始) \n\n"  
end

p["计算_弹匣装弹_加速射击间隔"] = function(context)
	-- 共射几次 = 总弹匣 / 每次射几个
	context.numShotAllPack =  context.ammoAllPack / context.ammoEachShot 
	-- 预热前那几秒能射几次 = 加速激活时间 / 射击间隔
	context.numShotBeforeAcc = context.timeAccTrigger / context.timeShotInterval 
	-- 预热之后加速能射几次 = 共射几次 - 预热前那几秒能射几次
	context.numShotAfterAcc = context.numShotAllPack - context.numShotBeforeAcc
	-- 遇热之后射击的总时间 = ( 预热之后加速能射几次 -1 ) * 加速射击间隔
	context.timeShotAfterAcc = ( context.numShotAfterAcc - 1 ) * context.timeAccShotInterval 
	-- 清空弹匣时间 = 预热的时间 + 遇热之后射击的总时间
	context.timeShotAllPack = context.timeAccTrigger + context.timeShotAfterAcc
    return
    	"武器 " .. context.name .. " 所属机制【弹匣装弹 射击后加速】 \n\n" ..
    	"按住开火," .. round( context.timeAccTrigger ,2) .. " 秒后开始加速射击 \n\n" ..
        "弹匣伤害 = " .. numComma(round( context.damageAllPack ,0)) .. " (指把整个弹匣射空造成的总伤害) \n\n" ..
        "爆发DPS = " .. numComma(round( context.damageAllPack / context.timeShotAllPack ,0)) .. " (按住开火,依照机制完整射一轮,该期间内的DPS) \n\n" ..
        "持续DPS = " .. numComma(round( context.damageAllPack / (context.timeShotAllPack + context.timeEachReload) ,0)) .. " (算上装弹所需时间,造成的DPS) \n\n" 
end

p["计算_边射边装_无额外机制"] = function(context)

    -- 【面板算法】:忽略装弹,计算射空弹匣所需时间
    context.timeShotAllPack = (context.ammoAllPack / context.ammoEachShot - 1) * context.timeShotInterval

    -- 仿真初始化:设置初始状态和事件时间
    local simTime = 0                                   -- 当前仿真时钟(秒)
    local currentAmmo = context.ammoAllPack             -- 当前剩余子弹数
    local nextShotTime = context.timeShotInterval       -- 下一次射击事件触发时间
    local nextReloadTime = context.timeEachReload       -- 下一次装填完成时间
    local damageAccrued = 0                             -- 累计造成的总伤害

    -- 事件驱动仿真循环:模拟直到无法再次射击
    while true do
        if currentAmmo >= context.ammoEachShot and nextShotTime <= nextReloadTime then
            -- 射击事件:消耗子弹并记录伤害
            simTime = nextShotTime
            currentAmmo = currentAmmo - context.ammoEachShot
            damageAccrued = damageAccrued + ( context.oneShotDamage  / context.varDamageMod ) * context.ammoEachShot
            nextShotTime = nextShotTime + context.timeShotInterval
        else
            -- 装填事件:补充子弹并更新装填完成时间
            simTime = nextReloadTime
            currentAmmo = math.min(currentAmmo + context.ammoEachReload, context.ammoAllPack)
            nextReloadTime = nextReloadTime + context.timeEachReload
            -- 保证装填后下一次射击时间不早于当前时间 + 射击间隔
            if nextShotTime < simTime + 1e-9 then
                nextShotTime = simTime + context.timeShotInterval
            end
        end
        -- 如果子弹不足以进行下一次射击,则退出仿真
        if currentAmmo < context.ammoEachShot then
            break
        end
    end

    context.timeToEmpty = simTime  - context.timeShotInterval    -- 动态射空所需时间
    context.damageTimeToEmpty = damageAccrued       -- 动态射空造成的总伤害   

    -- 计算哑火后持续DPS:每发循环时间取装弹或射击周期的最大值
    context.timePerLoopAfterEmpty = math.max(
        context.timeEachReload / context.ammoEachReload,
        context.timeShotInterval / context.ammoEachShot
    )
    context.cDPS = ( context.oneShotDamage  / context.varDamageMod ) / context.timePerLoopAfterEmpty

    -- 返回格式化结果字符串
    return
        "武器 " .. context.name .. " 所属机制【边射边装 无额外机制】\n\n\n" ..
        "【面板算法】,按照该武器实际的弹匣容积,进行计算:\n\n" ..
        "按住开火," .. round(context.timeShotAllPack,2) .. " 秒内射空弹匣,但由于边射边装,此时弹匣仍有剩余\n\n" ..
        "弹匣伤害 = " .. numComma(round(context.damageAllPack,0)) .. " (指把整个弹匣射空,造成的总伤害)\n\n" ..
        "爆发DPS = " .. numComma(round(context.damageAllPack / context.timeShotAllPack,0)) .. " (上述区间内,持续射击的DPS)\n\n\n" ..
        "【动态算法】,考虑到边打边装,以射到哑火为截止,进行计算:\n\n" ..
        "按住开火," .. round( context.timeToEmpty ,2) .. " 秒内射空弹匣容量,此时首轮爆发结束,进入慢速边射边装阶段\n\n" ..
        "弹匣伤害 = " .. numComma(round(context.damageTimeToEmpty,0)) .. " (指把整个动态弹匣射到哑火,造成的总伤害)\n\n" ..
        "爆发DPS = " .. numComma(round(context.damageTimeToEmpty / context.timeToEmpty,0)) .. " (上述区间内,持续射击的DPS)\n\n\n" ..
        "持续DPS = " .. numComma(round(context.cDPS,0)) .. " (当射到哑火后,打一点装一点,缓慢射击时的DPS)\n\n"
end


p["计算_边射边装_加速射击间隔"] = function(context)
    -- 计算“边射边装”同时“射击后加速”机制下的DPS,采用事件驱动离散仿真

    -- 【面板算法】:忽略装弹,计算加速前后总射击时间
    -- 总射击次数
    context.numShotAllPack = context.ammoAllPack / context.ammoEachShot
    -- 预热(加速激活)阶段可射次数
    context.numShotBeforeAcc = context.timeAccTrigger / context.timeShotInterval
    -- 加速阶段可射次数
    context.numShotAfterAcc = context.numShotAllPack - context.numShotBeforeAcc
    -- 加速阶段射击总时间
    context.timeShotAfterAcc = (context.numShotAfterAcc - 1) * context.timeAccShotInterval
    -- 总射击时间 = 预热时间 + 加速阶段射击总时间
    context.timeShotAllPack = context.timeAccTrigger + context.timeShotAfterAcc

    -- 【动态算法】初始化仿真状态
    local simTime       = 0                                -- 仿真时钟(秒)
    local currentAmmo   = context.ammoAllPack              -- 当前剩余子弹数
    local nextShotTime  = context.timeShotInterval         -- 下次射击事件时间
    local nextReloadTime= context.timeEachReload           -- 下次装填完成事件时间
    local accelTime     = context.timeAccTrigger           -- 加速触发事件时间
    local damageAccrued = 0                                -- 累计造成的伤害
    local accelTriggered= false                            -- 是否进入加速阶段

    -- 事件驱动循环:直到无法再射
    while currentAmmo >= context.ammoEachShot do
        -- 找到最近的事件时间(射击/装填/加速)
        local nextEvent = math.min(
            nextShotTime,
            nextReloadTime,
            accelTriggered and math.huge or accelTime
        )
        simTime = nextEvent

        if not accelTriggered and simTime == accelTime then
            -- 加速触发事件:切换射速
            accelTriggered = true
            nextShotTime = simTime + context.timeAccShotInterval

        elseif nextEvent == nextShotTime and currentAmmo >= context.ammoEachShot then
            -- 射击事件:消耗子弹并累加伤害
            currentAmmo   = currentAmmo - context.ammoEachShot
            damageAccrued = damageAccrued + (context.oneShotDamage / context.varDamageMod) * context.ammoEachShot
            -- 安排下一次射击时间
            if accelTriggered then
                nextShotTime = simTime + context.timeAccShotInterval
            else
                nextShotTime = simTime + context.timeShotInterval
            end

        else
            -- 装填事件:恢复子弹并安排下次装填
            currentAmmo     = math.min(currentAmmo + context.ammoEachReload, context.ammoAllPack)
            nextReloadTime  = simTime + context.timeEachReload
            -- 保证装填后射击时间不早于当前仿真时刻 + 相应间隔
            if accelTriggered then
                nextShotTime = math.max(nextShotTime, simTime + context.timeAccShotInterval)
            else
                nextShotTime = math.max(nextShotTime, simTime + context.timeShotInterval)
            end
        end
    end

    -- 仿真结果赋值
    context.timeToEmpty       = simTime - context.timeShotInterval               -- 动态射空所需时间
    context.damageTimeToEmpty = damageAccrued          -- 动态射空造成的总伤害
    -- 哑火后每发循环时间,取装弹或加速射击间隔的最大值
    context.timePerLoopAfterEmpty = math.max(
        context.timeEachReload / context.ammoEachReload,
        context.timeAccShotInterval / context.ammoEachShot
    )
    context.cDPS = (context.oneShotDamage  / context.varDamageMod) / context.timePerLoopAfterEmpty

    -- 返回格式化结果
    return
        "武器 " .. context.name .. " 所属机制【边射边装 射击后加速】\n\n\n" ..
        "【面板算法】,按照该武器实际的弹匣容积,进行计算:\n\n" ..
        "按住开火," .. round(context.timeShotAllPack,2) .. " 秒内射空弹匣(忽略装弹),其中前 " .. 
        round(context.timeAccTrigger,2) .. " 秒使用原始射速,之后使用加速射速\n\n" ..
        "弹匣伤害 = " .. numComma(round(context.damageAllPack,0)) .. " (指把整个弹匣射空造成的总伤害)\n\n" ..
        "爆发DPS = " .. numComma(round(context.damageAllPack / context.timeShotAllPack,0)) .. " (上述区间内的平均DPS)\n\n\n" ..
        "【动态算法】,考虑到边射边装和加速,以射到哑火为截止,进行计算:\n\n" ..
        "按住开火," .. round(context.timeToEmpty ,2) .. " 秒内射空弹匣,此时进入哑火阶段\n\n" ..
        "弹匣伤害 = " .. numComma(round(context.damageTimeToEmpty,0)) .. " (指把整个动态弹匣射到哑火造成的总伤害)\n\n" ..
        "爆发DPS = " .. numComma(round(context.damageTimeToEmpty / context.timeToEmpty,0)) .. " (该区间平均DPS)\n\n\n" ..
        "持续DPS = " .. numComma(round(context.cDPS,0)) .. " (哑火后持续边射边装时的DPS)\n\n"
end


p["计算_边射边装_过热触发时间_雾枪"] = function(context)
	-- 清空弹匣时间 = 一共几个间隔 * 每个间隔的时间
	context.timeShotAllPack = ( context.ammoAllPack / context.ammoEachShot -1 ) * context.timeShotInterval
    return
    	"武器 " .. context.name .. " 所属机制【边射边装 过热武器 雾枪】 \n\n" ..
    	"按住开火,射击 " .. round( context.timeOverheatTrigger ,2) .. " 秒后,射击失去精准度,弹道散布范围增大 \n\n" ..
        "弹匣伤害:无线弹药类武器,无需装弹 \n\n" ..
        "持续DPS = " .. numComma(round( context.oneShotDamage / context.timeShotInterval  ,0)) .. " (完全命中时,持续输出造成的DPS) \n\n"  
end

p["计算_边射边装_过热触发时间_泰坦火"] = function(context)
	context.damageAllPack = (10 / context.timeShotInterval) * context.oneShotDamage
	context.bDPS = (1 / context.timeShotInterval) * context.oneShotDamage
	context.cDPS0 = ((10 / context.timeShotInterval) * context.oneShotDamage) / (10 + context.timeOverheatCool)
	context.cDPS1 = ((10 / context.timeShotInterval) * context.oneShotDamage) / (10 + context.timeOverheatCool + context.timeOverheatPause )
    return
    	"武器 " .. context.name .. " 所属机制【边射边装 过热武器 泰坦火】 \n\n" ..
    	"弹匣伤害 = " .. numComma(round( context.damageAllPack ,0)) .. " (指将整个弹匣射空造成的总伤害) \n\n"  ..
    	"爆发DPS = " .. numComma(round( context.bDPS ,0)) .. " (指将整个弹匣射空造成的总伤害) \n\n"  ..
    	"聪明持续DPS = " .. numComma(round( context.cDPS0  ,0)) .. 
    	" (持续射击,直至过热的一瞬间停火,这段区间输出的DPS) \n\n"  ..
    	"愚钝持续DPS = " .. numComma(round( context.cDPS1  ,0)) .. 
    	" (持续射击,直至触发了过热,导致额外的停火时间,这段区间输出的DPS) \n\n" 
end

p["计算_边射边装_过热触发时间_雾光"] = function(context)
    return
    	"武器 " .. context.name .. " 所属机制【边射边装 过热武器 雾光】 \n\n" ..
    	"弹匣伤害:无线弹药类武器,无需装弹 \n\n" ..
    	"爆发DPS = " .. numComma(round( (1 / context.timeShotInterval) * context.oneShotDamage ,0)) .. " (满弹匣持续输出4秒,直到过热的一瞬间,平均每秒的伤害) \n\n"  ..
    	"持续DPS = " .. numComma(round( (1 / context.timeShotInterval) * context.oneShotDamage * 0.25 ,0)) .. " (过热后持续开火,平均每秒的伤害) \n\n"  
end

p["计算_边射边装_过热触发时间_泰坦转轮炸"] = function(context)
    return "泰坦转轮炸的例外情况"
end

p["计算_边射边装_连发子弹量"] = function(context)
    -- 计算“边射边装”同时“射击连发”机制下的DPS,采用事件驱动离散仿真

    -- 【面板算法】:连发参数下的总射击时间(忽略装弹)
    local groups = context.ammoAllPack / context.ammoEachBurst                          -- 连发轮数
    local gapTime = (groups - 1) * context.timeShotIntervalBurst                         -- 所有轮间总时间
    local shotBurstTime = (context.ammoEachBurst - 1) * groups * context.timeShotInterval  -- 所有轮内射击总时间
    context.timeShotAllPack = gapTime + shotBurstTime

    -- 动态仿真初始化
    local simTime      = 0                        -- 当前仿真时钟(秒)
    local ammo         = context.ammoAllPack       -- 当前剩余子弹数
    local nextShot     = context.timeShotInterval  -- 下次射击事件时间
    local nextReload   = context.timeEachReload    -- 下次装填完成事件时间
    local burstCnt     = 0                        -- 当前连发轮已射击子弹数
    local dmgAccrued   = 0                        -- 累计造成的总伤害

    -- 事件驱动循环:直到子弹不足以射击
    while ammo >= context.ammoEachShot do
        -- 判断是否有装填事件
        local canReload = ammo < context.ammoAllPack
        -- 下次事件时间
        local nextEvent = math.min(
            nextShot,
            canReload and nextReload or math.huge
        )
        simTime = nextEvent

        if nextEvent == nextShot then
            -- 射击事件:扣除子弹并记录伤害
            ammo = ammo - context.ammoEachShot
            dmgAccrued = dmgAccrued + (context.oneShotDamage / context.varDamageMod) * context.ammoEachShot
            burstCnt = burstCnt + 1
            -- 安排下一次射击:轮内或轮间
            if burstCnt < context.ammoEachBurst then
                nextShot = simTime + context.timeShotInterval
            else
                burstCnt = 0
                nextShot = simTime + context.timeShotIntervalBurst
            end

        elseif canReload and nextEvent == nextReload then
            -- 装填事件:补充子弹
            ammo = math.min(ammo + context.ammoEachReload, context.ammoAllPack)
            nextReload = simTime + context.timeEachReload
            -- 装填后保证射击时间不早于对应间隔
            if burstCnt > 0 then
                nextShot = math.max(nextShot, simTime + context.timeShotInterval)
            else
                nextShot = math.max(nextShot, simTime + context.timeShotIntervalBurst)
            end
        end
    end

    -- 仿真结果赋值
    context.timeToEmpty = simTime                   -- 动态射空所需时间
    context.damageTimeToEmpty = dmgAccrued          -- 动态射空造成的总伤害
    -- 哑火后循环时间:取装弹或单发射击周期的最大值
    local loopTime = math.max(
        context.timeEachReload / context.ammoEachReload,
        context.timeShotInterval / context.ammoEachShot
    )
    context.cDPS = (context.oneShotDamage / context.varDamageMod) / loopTime

    -- 返回格式化结果字符串
    return
        "武器 " .. context.name .. " 所属机制【边射边装 多连发武器】\n\n" ..
        "【面板算法】,按照连发参数计算:按住开火," .. round(context.timeShotAllPack,2) ..
        " 秒内完成 " .. groups .. " 轮连发,每轮 " .. context.ammoEachBurst .. " 发;轮内间隔 " ..
        round(context.timeShotInterval,2) .. " 秒,轮间间隔 " .. round(context.timeShotIntervalBurst,2) .. " 秒。\n\n" ..
        "弹匣伤害 = " .. numComma(round(context.damageAllPack,0)) .. " (把整个弹匣射空造成的总伤害)\n\n" ..
        "爆发 DPS = " .. numComma(round(context.damageAllPack / context.timeShotAllPack,0)) .. " (只考虑射击时间)\n\n" ..
        "持续 DPS = " .. numComma(round(context.damageAllPack / (context.timeShotAllPack + context.timeEachReload),0)) ..
        " (包含一次装填时间)\n\n" ..
        "【动态算法】,考虑边射边装与连发,以射到哑火为截止:\n\n" ..
        "动态仿真结果:" .. round(simTime,2) .. " 秒射空弹匣,造成伤害 " .. numComma(round(dmgAccrued,0)) .. ";\n" ..
        "动态 DPS = " .. numComma(round(dmgAccrued / simTime,0)) ..
        ";哑火后 DPS = " .. numComma(round(context.cDPS,0)) .. "\n"
end


return p