如果你看到本段文字,说明该页面未正常加载全局JS,部分功能无法使用,请点击 刷新 重新加载页面。
如果打开页面显示缩略图创建出错,请点击刷新或页面右上WIKI功能中的刷新按钮清除页面缓存并刷新,如果还有问题,请多尝试几次。
全站通知:

一键生成带图像的开火时刻表

阅读

    

2026-01-22更新

    

最新编辑:westseo

阅读:

  

更新日期:2026-01-22

  

最新编辑:westseo

来自碧蓝航线WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索
页面贡献者 :
westseo
指挥官大人~欢迎来到碧蓝航线WIKI,您可以通过百度搜索“碧蓝航线WIKI 碧蓝海事局”直达本WIKI!如果是第一次来访的话,按“Ctrl+D”可以收藏WIKI随时查看更新哦~如果觉得WIKI好玩的话,记得安利给更多人哟ヾ(o◕∀◕)ノ。

萨拉托加头像.jpg

本文由Westseo原创。未经许可,请勿擅自修改,转载请注明出处并附带链接。

[ 把BUFF触发改成了时间和轮次 ]把两个文件(命名相同,如AL_Heatmap_App_Kai_I.m)置于同一根目录下运行(特点是有一个X轴δR,Y轴R的热图,鼠标放上去可以简明地看到“某装填值下需要多少额外/更少的δR能够恰好射出某轮攻击”。同时支持时间轴上任意调整BUFF数量/数值和开始/结束时间。考虑到某些舰船前后摇可能比较特别,所以该项也是可调的。)

AL Reload Heatmap Config I.png


AL Reload Heatmap Config II.png


AL Reload Heatmap Config III.png


AL Reload Heatmap Config IV.png


AL Reload Heatmap Config VIII.png


AL Reload Heatmap Config VI.png


(文件1,UI界面)

classdef AL_Heatmap_App_Kai_I < matlab.apps.AppBase

    properties (Access = private)
        PresetData 
        Range_R = 50:1:400 
        Range_dR = -40:1:80 
        IsLoading = true 
    end
    
    properties (Access = public)
        UIFigure
        GridLayout
        LeftPanel
        RightPanel
        TabGroup
        TabSettings
        TabBuffs
        TabAdvanced
        TabHelp
        TabLog
        
        % Settings Tab
        TypeDropDown
        CurrentTimeEdit
        ShipBaseReloadEdit
        CatReloadEdit 
        PreCastEdit
        PostCastEdit
        FireDurEdit
        SimDurationEdit
        HeatmapSwitch
        
        % Buff Tab
        BuffTable
        AddRowBtn
        DelRowBtn
        BuffPreviewAx
        SkillTotalEdit % Moved permanent here
        FinalCDREdit   % Moved permanent here
        
        % Advanced Tab
        SplitPartsEdit
        SplitIntervalEdit
        HitDelayEdit
        CalcModeSwitch
        
        % Log Tab
        LogTextArea
        
        % Main Plot
        Ax
    end
    
    methods (Access = public)
        function app = AL_Heatmap_App_Kai_I
            % Initialize Data - Step by Step
            keys = {
                '战列 (BB) - [Pre 0.17s / Post 0.00s]', ...
                '驱逐 (DD) - [Pre 0.16s / Post 0.10s]', ...
                '轻巡 (CL) - [Pre 0.18s / Post 0.10s]', ...
                '重巡 (CA) - [Pre 0.20s / Post 0.10s]', ...
                '超巡 (CB) - [Pre 0.20s / Post 0.10s]', ...
                '航母 (CV) - [Pre 0.00s / Post 0.00s]', ...
                '自定义'
            };
            
            vals = {
                [0.17, 0.00], ... % BB
                [0.16, 0.10], ... % DD
                [0.18, 0.10], ... % CL
                [0.20, 0.10], ... % CA
                [0.20, 0.10], ... % CB
                [0.00, 0.00], ... % CV
                [0.00, 0.00]      % Custom
            };
            
            app.PresetData = containers.Map(keys, vals);
            
            createUI(app);
            app.IsLoading = false;
            onPresetChanged(app); 
        end
    end
    
    methods (Access = private)
        
        function createUI(app)
            try
                % Main Window
                app.UIFigure = uifigure('Name', 'AL Reload Heatmap Kai I', ...
                    'Position', [100, 100, 1150, 750], 'Color', 'w'); 
                
                app.GridLayout = uigridlayout(app.UIFigure, [1, 2]);
                app.GridLayout.ColumnWidth = {480, '1x'}; 
                
                % Left Panel
                app.LeftPanel = uipanel(app.GridLayout, 'Title', '配置与说明', ...
                    'FontSize', 14, 'FontWeight', 'bold', 'BackgroundColor', 'w');
                app.LeftPanel.Layout.Column = 1;
                
                GridMain = uigridlayout(app.LeftPanel, [1, 1]);
                GridMain.Padding = [5 5 5 5];
                app.TabGroup = uitabgroup(GridMain);
                
                % ============================================================ 
                % Tab 1: Settings
                % ============================================================ 
                app.TabSettings = uitab(app.TabGroup, 'Title', '参数设定', 'Scrollable', 'on');
                GridSettings = uigridlayout(app.TabSettings, [12, 1]); 
                GridSettings.Padding = [10 10 10 10];
                GridSettings.RowSpacing = 8;
                GridSettings.RowHeight = {'fit','fit','fit','fit','fit','fit','fit','fit','fit','fit','fit','1x'};
                
                % 1. Preset
                GridPreset = uigridlayout(GridSettings, [1, 2]); GridPreset.Padding=[0 0 0 0]; GridPreset.ColumnWidth={100,'1x'};
                uilabel(GridPreset, 'Text', '1. 舰种预设:', 'FontWeight','bold'); 
                app.TypeDropDown = uidropdown(GridPreset, 'Items', keys(app.PresetData), 'Value', '航母 (CV) - [Pre 0.00s / Post 0.00s]', 'ValueChangedFcn', @(s,e) onPresetChanged(app));
                
                % 2. Panel Data
                uilabel(GridSettings, 'Text', '2. 面板数据 (基准值)', 'FontWeight','bold', 'FontColor', [0.8 0 0]);
                GridPanelData = uigridlayout(GridSettings, [3, 2]); GridPanelData.Padding=[10 0 0 0]; GridPanelData.RowHeight={'fit','fit','fit'}; GridPanelData.ColumnWidth={'1x', 100};
                uilabel(GridPanelData, 'Text', '面板显示射速 (s):'); app.CurrentTimeEdit = uieditfield(GridPanelData, 'numeric', 'Value', 22.50, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                uilabel(GridPanelData, 'Text', '船坞装填 (白值+科技+装备):'); app.ShipBaseReloadEdit = uieditfield(GridPanelData, 'numeric', 'Value', 180, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                uilabel(GridPanelData, 'Text', '指挥猫装填:'); app.CatReloadEdit = uieditfield(GridPanelData, 'numeric', 'Value', 0, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                % 3. Action Params
                uilabel(GridSettings, 'Text', '3. 动作参数 (调轴)', 'FontWeight','bold', 'FontColor', [0 0.4 0.8]);
                GridAction = uigridlayout(GridSettings, [2, 4]); GridAction.Padding=[10 0 0 0]; GridAction.RowHeight={'fit','fit'}; GridAction.ColumnWidth={50,'1x',50,'1x'};
                uilabel(GridAction, 'Text', '前摇:'); app.PreCastEdit = uieditfield(GridAction, 'numeric', 'ValueChangedFcn', @(s,e) refreshPlots(app));
                uilabel(GridAction, 'Text', '后摇:'); app.PostCastEdit = uieditfield(GridAction, 'numeric', 'ValueChangedFcn', @(s,e) refreshPlots(app));
                uilabel(GridAction, 'Text', '开火:', 'FontColor','red'); app.FireDurEdit = uieditfield(GridAction, 'numeric', 'Value', 0.00, 'FontColor','red', 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                % 4. Simulation Control
                uilabel(GridSettings, 'Text', '4. 模拟控制', 'FontWeight', 'bold');
                GridSim = uigridlayout(GridSettings, [3, 2]); GridSim.Padding=[10 0 0 0]; GridSim.ColumnWidth={100, '1x'};
                
                uilabel(GridSim, 'Text', '模拟时长(s):'); app.SimDurationEdit = uieditfield(GridSim, 'numeric', 'Value', 90, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                GridBtns = uigridlayout(GridSim, [1, 3]); GridBtns.Padding=[0 0 0 0]; GridBtns.ColumnSpacing=5; 
                GridBtns.Layout.Column = [1 2];
                uibutton(GridBtns, 'Text', '10s', 'ButtonPushedFcn', @(s,e) setDuration(app, 10));
                uibutton(GridBtns, 'Text', '20s', 'ButtonPushedFcn', @(s,e) setDuration(app, 20));
                uibutton(GridBtns, 'Text', '90s', 'ButtonPushedFcn', @(s,e) setDuration(app, 90));
                
                uilabel(GridSim, 'Text', '装填机制:');
                app.HeatmapSwitch = uiswitch(GridSim, 'slider', 'Items', {'串行', '并行'}, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                % ============================================================ 
                % Tab 2: Buff Configuration
                % ============================================================ 
                app.TabBuffs = uitab(app.TabGroup, 'Title', '战斗环境配置');
                GridBuffs = uigridlayout(app.TabBuffs, [3, 1]);
                GridBuffs.RowHeight = {'fit', '1x', 150}; 
                GridBuffs.Padding = [10 10 10 10];
                
                % Permanent Stats
                GridPerm = uigridlayout(GridBuffs, [1, 4]); GridPerm.Padding=[0 0 0 0];
                uilabel(GridPerm, 'Text', '常驻装填加成(%):'); app.SkillTotalEdit = uieditfield(GridPerm, 'numeric', 'Value', 0, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                uilabel(GridPerm, 'Text', '常驻CDR(%):', 'FontColor', '#7E2F8E'); app.FinalCDREdit = uieditfield(GridPerm, 'numeric', 'Value', 0, 'FontColor', '#7E2F8E', 'FontWeight', 'bold', 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                % Table Controls
                GridTbl = uigridlayout(GridBuffs, [2, 1]); 
                GridTbl.RowHeight = {'1x', 40}; 
                GridTbl.Padding = [0 0 0 0];
                
                % Create BuffTable inside GridTbl
                app.BuffTable = uitable(GridTbl);
                app.BuffTable.Layout.Row = 1;
                app.BuffTable.Layout.Column = 1;
                app.BuffTable.ColumnName = {'触发模式', '开始', '结束', 'CDR%', '装填%'};
                app.BuffTable.ColumnFormat = {{'时间(s)', '轮次(R)'}, 'numeric', 'numeric', 'numeric', 'numeric'};
                app.BuffTable.ColumnEditable = true;
                app.BuffTable.ColumnWidth = {80, 50, 50, 50, 50};
                
                % Default: Time mode
                app.BuffTable.Data = {'时间(s)', 0, 20, 0, 0; '时间(s)', 20, 100, 0, 0};
                app.BuffTable.CellEditCallback = @(s,e) refreshPlots(app);
                
                GridTblBtns = uigridlayout(GridTbl, [1, 2]);
                GridTblBtns.ColumnWidth = {'1x', '1x'}; 
                GridTblBtns.Padding = [2 2 2 2]; % Reduced padding
                
                app.AddRowBtn = uibutton(GridTblBtns, 'Text', '+ 添加修正行', ...
                    'FontSize', 12, 'FontWeight', 'bold', ...
                    'ButtonPushedFcn', @(s,e) addBuffRow(app));
                
                app.DelRowBtn = uibutton(GridTblBtns, 'Text', '- 删除选中行', ...
                    'FontSize', 12, 'FontWeight', 'bold', ...
                    'ButtonPushedFcn', @(s,e) delBuffRow(app));
                
                % Preview Plot
                app.BuffPreviewAx = uiaxes(GridBuffs);
                app.BuffPreviewAx.Title.String = '装填速率与开火时间预测';
                app.BuffPreviewAx.XLabel.String = 'Time (s)';
                app.BuffPreviewAx.YLabel.String = '相对速率 (x1 = Base)';
                app.BuffPreviewAx.YGrid = 'on';
                
                % ============================================================ 
                % Tab 3: Advanced
                % ============================================================ 
                app.TabAdvanced = uitab(app.TabGroup, 'Title', '高级设置', 'Scrollable', 'on');
                GridAdv = uigridlayout(app.TabAdvanced, [6, 2]);
                GridAdv.Padding = [20 20 20 20];
                GridAdv.RowHeight = {'fit', 'fit', 'fit', 'fit', 'fit', '1x'}; 
                GridAdv.ColumnWidth = {'1x', 120};
                
                uilabel(GridAdv, 'Text', '伤害结算模式:', 'FontWeight', 'bold', 'FontSize', 14);
                app.CalcModeSwitch = uiswitch(GridAdv, 'slider', 'Items', {'平滑模型', '阶梯模型'});
                app.CalcModeSwitch.ValueChangedFcn = @(s,e) refreshPlots(app);
                
                uilabel(GridAdv, 'Text', '注意: 阶梯模型下, 只有完整的一波攻击才会计入收益。', 'FontColor', '#808080', 'FontSize', 10);
                uilabel(GridAdv, 'Text', '');
                
                uilabel(GridAdv, 'Text', '攻击波次 / 底座数 (Split Parts):', 'FontSize', 12);
                app.SplitPartsEdit = uieditfield(GridAdv, 'numeric', 'Value', 1, 'Limits', [1 10], 'RoundFractionalValues', 'on', 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                uilabel(GridAdv, 'Text', '波次间隔 (Interval, s):', 'FontSize', 12);
                app.SplitIntervalEdit = uieditfield(GridAdv, 'numeric', 'Value', 0, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                uilabel(GridAdv, 'Text', '命中延迟 (Hit Delay, s):', 'FontSize', 12);
                app.HitDelayEdit = uieditfield(GridAdv, 'numeric', 'Value', 0.5, 'ValueChangedFcn', @(s,e) refreshPlots(app));
                
                % ============================================================ 
                % Tab 4: Help
                % ============================================================ 
                app.TabHelp = uitab(app.TabGroup, 'Title', '说明文本', 'Scrollable', 'on');
                GridHelp = uigridlayout(app.TabHelp, [1, 1]);
                GridHelp.Padding = [10 10 10 10];
                
                HelpLines = {
                    '【多重 Buff 配置】';
                    '在“战斗环境配置”页签中,你可以自由定义复杂的时间轴 Buff。';
                    '表格每一行代表一个生效的时间段。';
                    '系统会自动计算重叠时间段内的 Buff 叠加效果。';
                    '';
                    '【参数说明】';
                    'CDR修正(%): 减少技能冷却时间,例如 -15 代表减少 15% CD。';
                    '装填值修正(%): 增加面板装填属性,例如 50 代表装填值 +50%。';
                    '';
                    '关于前后摇:https://wiki.biligame.com/blhx/5B5的其他研究合集#舰船的可手动开火武器(含后排卡炮研究)';
                    '关于武器开火时间:https://wiki.biligame.com/blhx/全武器对护甲补正一览';
                };
                
                uitextarea(GridHelp, 'Value', strjoin(HelpLines, '\n'), 'Editable', 'off', ...
                    'FontName', 'Microsoft YaHei', 'FontSize', 12);
                
                % ============================================================ 
                % Tab 5: Log
                % ============================================================ 
                app.TabLog = uitab(app.TabGroup, 'Title', '模拟日志', 'Scrollable', 'on');
                GridLog = uigridlayout(app.TabLog, [2, 1]);
                GridLog.RowHeight = {40, '1x'};
                GridLog.Padding = [10 10 10 10];
                
                uibutton(GridLog, 'Text', '生成当前配置的时间轴报告', ...
                    'BackgroundColor', [0.9 0.95 1], ...
                    'FontWeight', 'bold', ...
                    'ButtonPushedFcn', @(s,e) showTimelineReport(app));
                
                app.LogTextArea = uitextarea(GridLog, 'Value', '...', 'Editable', 'off', 'FontName', 'Consolas', 'FontSize', 11);
                
                % --- Right Panel ---
                app.RightPanel = uipanel(app.GridLayout, 'BorderType', 'none'); 
                app.RightPanel.Layout.Column = 2;
                GridRight = uigridlayout(app.RightPanel, [1, 1]);
                GridRight.Padding = [10 10 10 10];
                
                app.Ax = uiaxes(GridRight);
                app.Ax.Title.String = '收益热图 (有效轮次)';
                app.Ax.XLabel.String = '新增白值 (\Delta R)';
                app.Ax.YLabel.String = '船坞装填 (Base)';
                colormap(app.Ax, 'parula'); 
                
            catch ME
                uialert(app.UIFigure, ME.message, 'Init Error');
            end
        end
        
        function setDuration(app, val)
            if app.IsLoading, return; end
            app.SimDurationEdit.Value = val;
            refreshPlots(app);
        end
        
        function addBuffRow(app)
            d = app.BuffTable.Data;
            % Create a 5-column row matching the new format:
            % {Mode, Start, End, CDR, Rld}
            newRow = {'时间(s)', 0, app.SimDurationEdit.Value, 0, 0};
            
            if isempty(d)
                app.BuffTable.Data = newRow;
            elseif iscell(d)
                app.BuffTable.Data = [d; newRow];
            else
                app.BuffTable.Data = [num2cell(d); newRow]; 
            end
            refreshPlots(app);
        end

        function delBuffRow(app)
            d = app.BuffTable.Data;
            if isempty(d), return; end
            
            % Get selection
            sel = app.BuffTable.Selection; 
            % sel is [row, col] or multiple indices.
            if isempty(sel), return; end
            
            rowsToDelete = unique(sel(:,1));
            d(rowsToDelete, :) = [];
            
            app.BuffTable.Data = d;
            refreshPlots(app);
        end

        function onPresetChanged(app)
            key = app.TypeDropDown.Value;
            if isKey(app.PresetData, key)
                vals = app.PresetData(key);
                app.PreCastEdit.Value  = vals(1);
                app.PostCastEdit.Value = vals(2);
                refreshPlots(app);
            end
        end
        
        function refreshPlots(app)
            if app.IsLoading, return; end
            
            % Gather Inputs
            v_CurTime = app.CurrentTimeEdit.Value;
            v_ShipBase = app.ShipBaseReloadEdit.Value;
            v_CatReload = app.CatReloadEdit.Value;
            v_Pre = app.PreCastEdit.Value;
            v_Post = app.PostCastEdit.Value;
            v_Fire = app.FireDurEdit.Value;
            v_Skill = app.SkillTotalEdit.Value;
            v_Final = app.FinalCDREdit.Value;
            v_Table = app.BuffTable.Data;
            v_Dur = app.SimDurationEdit.Value;
            v_CalcMode = app.CalcModeSwitch.Value;
            v_Split = app.SplitPartsEdit.Value;
            v_Interval = app.SplitIntervalEdit.Value;
            v_HitDelay = app.HitDelayEdit.Value;
            
            v_RangeR = app.Range_R;
            v_RangeDR = app.Range_dR;
            
            % SINGLE LINE CALL - NO CONTINUATION DOTS
            [Map_Rounds, Map_Time, Time_S, Time_P, CurveData] = ReloadModel_Heatmap_Kai_I.calculateScaling(v_RangeR, v_RangeDR, v_CurTime, v_ShipBase, v_CatReload, v_Pre, v_Post, v_Fire, v_Skill, v_Final, v_Table, v_Dur, v_CalcMode, v_Split, v_Interval, v_HitDelay); 
            
            if strcmp(app.HeatmapSwitch.Value, '串行')
                ZData = Map_Rounds.S; TData = Map_Time.S;
            else
                ZData = Map_Rounds.P; TData = Map_Time.P;
            end
            
            [X, Y] = meshgrid(app.Range_dR, app.Range_R);
            cla(app.Ax); 
            
            % 1. Draw Surface
            s = surf(app.Ax, X, Y, ZData);
            s.UserData = TData; % Store the timeline data in the object
            
            set(s, 'PickableParts', 'visible', 'HitTest', 'on');
            
            view(app.Ax, 2); 
            shading(app.Ax, 'flat'); 
            axis(app.Ax, 'tight');
            hold(app.Ax, 'on');
            
            % 2. Configure Tooltip (The Safe Way)
            % Rename existing rows and apply Bold format (\\bf)
            s.DataTipTemplate.DataTipRows(1).Label = '新增装填';
            s.DataTipTemplate.DataTipRows(1).Format = '\\bf%g';
            
            s.DataTipTemplate.DataTipRows(2).Label = '船坞装填';
            s.DataTipTemplate.DataTipRows(2).Format = '\\bf%g';
            
            s.DataTipTemplate.DataTipRows(3).Label = '有效轮次';
            s.DataTipTemplate.DataTipRows(3).Format = '\\bf%.2f';
            
            % Append custom row referencing UserData
            % MATLAB automatically maps (i,j) of UserData to the surface point
            row4 = dataTipTextRow('理论射速(s)', 'UserData', '\\bf%.3f');
            s.DataTipTemplate.DataTipRows(end+1) = row4;
            
            s.DataTipTemplate.Interpreter = 'tex';
            
            % 3. Add Overlays (Ghost Mode)
            
            % Red Star
            plot3(app.Ax, 0, app.ShipBaseReloadEdit.Value, max(ZData(:))+10, ...
                'p', 'MarkerSize', 14, 'MarkerEdgeColor', 'r', ...
                'MarkerFaceColor', 'r', 'LineWidth', 1.5, ...
                'PickableParts', 'none', 'HitTest', 'off'); 

            % Contours
            [C, h_cont] = contour3(app.Ax, X, Y, ZData + 0.2, ...
                'LineColor', 'k', 'ShowText', 'off', ...
                'PickableParts', 'none', 'HitTest', 'off');
            clabel(C, h_cont, 'Color', 'w', 'FontSize', 9, ...
                'FontWeight', 'bold', 'LabelSpacing', 300);

            hold(app.Ax, 'off'); 
            
            % Restore Colorbar
            cb = colorbar(app.Ax); 
            cb.Label.String = '有效总轮次 (Total Rounds)';
            
            % 4. Update Time Domain Plot (BuffPreviewAx)
            % Calculate specific fire timings AND visual equivalent curve
            v_HitDelay = app.HitDelayEdit.Value; 
            [FireTimes, VisualCurve] = ReloadModel_Heatmap_Kai_I.runSingleTrace(v_CurTime, v_ShipBase, v_CatReload, v_Pre, v_Post, v_Fire, v_Skill, v_Final, v_Table, v_Dur, app.HeatmapSwitch.Value, v_HitDelay);
            
            cla(app.BuffPreviewAx);
            hold(app.BuffPreviewAx, 'on');
            
            % Draw Area (Integration Metaphor: Area = Progress)
            % Use VisualCurve (includes Round Buff equivalent speed)
            ar = area(app.BuffPreviewAx, VisualCurve.T, VisualCurve.Spd);
            ar.FaceColor = [0.0 0.45 0.74]; % MATLAB Blue
            ar.FaceAlpha = 0.15;
            ar.EdgeColor = [0.0 0.45 0.74];
            ar.LineWidth = 1.5;
            
            % Configure Tooltip for the Area Plot
            ar.DataTipTemplate.DataTipRows(1).Label = '当前时间';
            ar.DataTipTemplate.DataTipRows(1).Format = '%.2fs';
            ar.DataTipTemplate.DataTipRows(2).Label = '实时速率';
            ar.DataTipTemplate.DataTipRows(2).Format = '%.2f';
            
            % Draw Vertical Stems for Fire Events
            for k = 1:length(FireTimes)
                ft = FireTimes(k);
                if ft <= v_Dur
                    % Interpolate speed at exact fire time
                    spd_at_fire = interp1(VisualCurve.T, VisualCurve.Spd, ft, 'linear', 'extrap');
                    
                    % Draw vertical dashed line up to the curve only
                    plot(app.BuffPreviewAx, [ft ft], [0 spd_at_fire], 'r--', 'LineWidth', 1, 'PickableParts', 'none', 'HitTest', 'off');
                    
                    % Draw Marker at intersection
                    p = plot(app.BuffPreviewAx, ft, spd_at_fire, 'ro', 'MarkerFaceColor', 'r', 'MarkerSize', 5);
                    
                    % Custom Tooltip for the marker
                    p.DataTipTemplate.DataTipRows(1).Label = '开火时间';
                    p.DataTipTemplate.DataTipRows(2).Label = '等效速率';
                end
            end
            
            grid(app.BuffPreviewAx, 'on');
            title(app.BuffPreviewAx, '等效装填速率系数 与 开火时点');
            xlabel(app.BuffPreviewAx, '时间 (s)');
            ylabel(app.BuffPreviewAx, '等效装填速率');
            xlim(app.BuffPreviewAx, [0, v_Dur]);
            
            % Manually set limits (Robust against auto-scaling glitches)
            max_spd = max(VisualCurve.Spd);
            upper_lim = max(max_spd * 1.15, 1.2);
            ylim(app.BuffPreviewAx, [0, upper_lim]);
            
            hold(app.BuffPreviewAx, 'off');
        end
        
        function showTimelineReport(app)
            % Extract Local Variables
            v_CurTime = app.CurrentTimeEdit.Value;
            v_ShipBase = app.ShipBaseReloadEdit.Value;
            v_CatReload = app.CatReloadEdit.Value;
            v_Pre = app.PreCastEdit.Value;
            v_Post = app.PostCastEdit.Value;
            v_Fire = app.FireDurEdit.Value;
            v_Skill = app.SkillTotalEdit.Value;
            v_Final = app.FinalCDREdit.Value;
            v_Table = app.BuffTable.Data;
            v_Dur = app.SimDurationEdit.Value;
            v_MapSwitch = app.HeatmapSwitch.Value;
            v_CalcMode = app.CalcModeSwitch.Value;
            v_Split = app.SplitPartsEdit.Value;
            v_Interval = app.SplitIntervalEdit.Value;
            v_HitDelay = app.HitDelayEdit.Value;
            
            % SINGLE LINE CALL - NO CONTINUATION DOTS
            Report = ReloadModel_Heatmap_Kai_I.generateTimelineReport(v_CurTime, v_ShipBase, v_CatReload, v_Pre, v_Post, v_Fire, v_Skill, v_Final, v_Table, v_Dur, v_MapSwitch, v_CalcMode, v_Split, v_Interval, v_HitDelay);
                
            app.LogTextArea.Value = Report;
        end
    end
end
                                                                                                           (文件2,计算模块)
classdef ReloadModel_Heatmap_Kai_I
    
    methods (Static)
        function vec = parseInput(textVal)
            try
                textVal = strtrim(textVal);
                if startsWith(textVal, 'loop:', 'IgnoreCase', true)
                    patternStr = extractAfter(textVal, 'loop:');
                    patternVec = str2num(patternStr); %#ok<ST2NM>
                    if isempty(patternVec), vec = 0; return; end
                    vec = repmat(patternVec, 1, 500); 
                else
                    vec = str2num(textVal); %#ok<ST2NM>
                    if isempty(vec), vec = 0; end
                end
            catch
                vec = 0;
            end
        end

        % ============================================================ 
        % Main Calculation Entry Point
        % ============================================================ 
        function [Map_Rounds, Map_Time, Time_S, Time_P, CurveData] = calculateScaling(Range_BaseR, Range_AddR, CurrentPanelTime, ...
                ShipBaseReload, CatReload, PreCast, PostCast, FireDuration, TotalSkillPct, FinalCDR_Pct, ...
                BuffTableData, SimDuration, CalcMode, SplitParts, SplitInterval, HitDelay)
            
            % BuffTableData: 
            % Legacy: Numeric [Start, End, CDR, Rld]
            % Modern: Cell {Mode, Start, End, CDR, Rld}
            
            Buffs_Time = [];
            Buffs_Round = [];
            
            % 1. Parse Buff Table (Split into Time/Round)
            if ~isempty(BuffTableData)
                if iscell(BuffTableData)
                    % New 5-Column Format
                    Modes = BuffTableData(:,1);
                    Vals  = cell2mat(BuffTableData(:, 2:5)); % [Start, End, CDR, Rld]
                    
                    % Filter Time Mode
                    IsTime = contains(Modes, '时间');
                    if any(IsTime)
                        Buffs_Time = Vals(IsTime, :);
                    end
                    
                    % Filter Round Mode
                    IsRound = contains(Modes, '轮次');
                    if any(IsRound)
                        Buffs_Round = Vals(IsRound, :);
                    end
                else
                    % Legacy Numeric Format (Assume Time)
                    Buffs_Time = BuffTableData;
                end
            end
            
            % 2. Data Cleaning
            Range_BaseR = reshape(Range_BaseR, 1, []); 
            Range_AddR  = reshape(Range_AddR, 1, []);
            
            % 3. Reference Data (Dock)
            Ref_T_Dock = CurrentPanelTime;
            Ref_R_Dock = ShipBaseReload; 
            Valid_Ref_R_Dock = max(Ref_R_Dock + 100, 0.0001);
            
            % 4. Grid Generation
            [X_Add, Y_Base] = meshgrid(Range_AddR, Range_BaseR);
            
            % 5. Calculate R_Target Matrix (Base + Cat + Add)
            R_Target_Base = (Y_Base + CatReload + X_Add); 
            Valid_R_Target_Base = max(R_Target_Base + 100, 0.0001);
            
            % 6. Calculate Raw Paper Time Matrix (No Skills)
            T_Paper_Trgt_Raw = Ref_T_Dock .* sqrt(Valid_Ref_R_Dock ./ Valid_R_Target_Base);
            
            % 7. Simulation (Pass separated buffs)
            [Rounds_S, AvgTime_S, CurveData] = ReloadModel_Heatmap_Kai_I.simulateTimeline(...
                T_Paper_Trgt_Raw, PreCast, PostCast, FireDuration, ...
                TotalSkillPct, FinalCDR_Pct, Buffs_Time, Buffs_Round, ...
                SimDuration, 'S', CalcMode, SplitParts, SplitInterval, HitDelay);
            
            [Rounds_P, AvgTime_P, ~] = ReloadModel_Heatmap_Kai_I.simulateTimeline(...
                T_Paper_Trgt_Raw, PreCast, PostCast, FireDuration, ...
                TotalSkillPct, FinalCDR_Pct, Buffs_Time, Buffs_Round, ...
                SimDuration, 'P', CalcMode, SplitParts, SplitInterval, HitDelay);
            
            Map_Rounds = struct('S', Rounds_S, 'P', Rounds_P);
            Map_Time = struct('S', AvgTime_S, 'P', AvgTime_P);
            Time_S = AvgTime_S;
            Time_P = AvgTime_P;
        end
        
        % ============================================================ 
        % Timeline Simulator (Matrix Interpolation + Dynamic Buffs)
        % ============================================================ 
        function [Total_Rounds, Avg_Rate, CurveData] = simulateTimeline(T_Paper_Raw, Pre, Post, Fire, Perm_Rld_Pct, Perm_CDR_Pct, Buffs_Time, Buffs_Round, Duration, Mode, CalcMode, SplitParts, SplitInterval, HitDelay)
            
            [rows, cols] = size(T_Paper_Raw); 
            Num_Pixels = rows * cols;
            T_Raw_Flat = T_Paper_Raw(:); 
            Max_Shots = 500; 
            
            % -- 1. Build Global Speed Function v(t) (Time-based Buffs ONLY) --
            T_Axis = 0 : 0.05 : (Duration * 2); 
            if T_Axis(end) < Duration * 2, T_Axis(end+1) = Duration*2; end
            
            % Base Curves (Permanent Stats)
            % CDR Logic: Additive (Negative Input = Reduction)
            % e.g. -15% CDR -> Factor 0.85
            Rld_Curve = ones(size(T_Axis)) * (1 + Perm_Rld_Pct/100);
            CDR_Curve = ones(size(T_Axis)) * (1 + Perm_CDR_Pct/100);
            
            % Apply Time-based Buffs [Start, End, CDR, Rld]
            if ~isempty(Buffs_Time)
                for i = 1:size(Buffs_Time, 1)
                    tStart = Buffs_Time(i, 1);
                    tEnd   = Buffs_Time(i, 2);
                    valCDR = Buffs_Time(i, 3);
                    valRld = Buffs_Time(i, 4);
                    
                    mask = (T_Axis >= tStart) & (T_Axis < tEnd);
                    if valRld ~= 0, Rld_Curve(mask) = Rld_Curve(mask) + (valRld/100); end
                    % CDR: Add value
                    if valCDR ~= 0, CDR_Curve(mask) = CDR_Curve(mask) + (valCDR/100); end
                end
            end
            
            CDR_Curve(CDR_Curve < 0.1) = 0.1; 
            Rld_Curve(Rld_Curve < 0.1) = 0.1;
            Speed_Curve = sqrt(Rld_Curve) ./ CDR_Curve;
            
            % Integral of Speed -> Effective Progress (Global Time Flow)
            Progress_Curve = cumtrapz(T_Axis, Speed_Curve);
            
            % Pack Curve Data for Visualization
            CurveData = struct('T', T_Axis, 'Spd', Speed_Curve, 'Prg', Progress_Curve);
            
            % -- 2. Simulation Loop (Round-based Buffs applied here) --
            Cum_Times = zeros(Num_Pixels, Max_Shots);
            Current_Time_Flat = zeros(Num_Pixels, 1);
            
            if strcmp(Mode, 'P') || strcmp(Mode, '并行')
                ActionTime = Post + Fire; 
            else
                ActionTime = 0; 
            end
            
            for k = 1:Max_Shots
                % A. Determine Round-based Modifiers for this Shot (k)
                Rld_Mod_Round = 0;
                CDR_Mod_Round = 0;
                
                if ~isempty(Buffs_Round)
                    % Filter buffs active for round k
                    % Col 1: StartR, Col 2: EndR, Col 3: CDR, Col 4: Rld
                    RoundMask = (Buffs_Round(:,1) <= k) & (Buffs_Round(:,2) >= k);
                    if any(RoundMask)
                        Rld_Mod_Round = sum(Buffs_Round(RoundMask, 4));
                        CDR_Mod_Round = sum(Buffs_Round(RoundMask, 3));
                    end
                end
                
                % B. Calculate Effective Required Time for this specific shot
                
                Base_Rld_Factor = sqrt(1 + Perm_Rld_Pct/100);
                Total_Rld_Factor = sqrt(1 + (Perm_Rld_Pct + Rld_Mod_Round)/100);
                
                % Multiplier to shrink T_Raw based on added Rld stats
                Rld_Multiplier = Total_Rld_Factor / Base_Rld_Factor; 
                
                % Multiplier for CDR
                % Additive: 1 + (-15/100) = 0.85
                CDR_Multiplier = (1 + CDR_Mod_Round/100);
                
                % Adjusted Requirement for this round
                T_Step_Target = T_Raw_Flat ./ Rld_Multiplier .* CDR_Multiplier;
                
                % C. Solve Integral (Standard Logic)
                T_start = Current_Time_Flat;
                P_start = interp1(T_Axis, Progress_Curve, T_start, 'linear', 'extrap');
                P_target = P_start + T_Step_Target;
                T_end_load = interp1(Progress_Curve, T_Axis, P_target, 'linear', 'extrap');
                
                Actual_Load_Time = max(T_end_load - T_start, 0);
                
                % D. Cycle
                if strcmp(Mode, 'P') || strcmp(Mode, '并行')
                    Cycle_Time = Pre + max(Actual_Load_Time, ActionTime);
                else
                    Cycle_Time = Pre + Actual_Load_Time + Post + Fire;
                end
                
                Current_Time_Flat = Current_Time_Flat + Cycle_Time;
                Cum_Times(:, k) = Current_Time_Flat;
            end
            
            % -- 3. Results --
            Full_Rounds = sum(Cum_Times <= Duration, 2);
            
            Avg_Cycle_Time = zeros(Num_Pixels, 1);
            Has_Completed_Shots = Full_Rounds > 0;
            if any(Has_Completed_Shots)
                idx_last = sub2ind(size(Cum_Times), find(Has_Completed_Shots), Full_Rounds(Has_Completed_Shots));
                Time_Used_Last = Cum_Times(idx_last);
                Avg_Cycle_Time(Has_Completed_Shots) = Time_Used_Last ./ Full_Rounds(Has_Completed_Shots);
            end
            if any(~Has_Completed_Shots)
                Avg_Cycle_Time(~Has_Completed_Shots) = Cum_Times(~Has_Completed_Shots, 1);
            end
            
            if strcmp(CalcMode, '平滑模型')
                Has_Shots = Full_Rounds > 0;
                Fractional = zeros(Num_Pixels, 1);
                
                if any(Has_Shots)
                    % Calculate fractional progress for the INCOMPLETE round
                    % Need to apply NEXT round's buffs to predict T_Cycle_Next
                    
                    % Vectorized "Next Round" Logic
                    Row_I = find(Has_Shots);
                    Col_I = Full_Rounds(Row_I) + 1; % The next round index (k+1)
                    
                    % Current End Time
                    idx_curr = sub2ind(size(Cum_Times), Row_I, Col_I-1);
                    T_End_Curr = Cum_Times(idx_curr);
                    Rem_Time = Duration - T_End_Curr;
                    
                    % We need T_Cycle_Next for each pixel. 
                    % Since T_Cycle_Next depends on Round Buffs for (k+1), we can't just take Cum_Times(:, k+1) - Cum_Times(:, k)
                    % because Cum_Times is calculated up to Max_Shots, which usually covers it.
                    % Checking if Max_Shots covers k+1
                    Valid_Next = Col_I <= Max_Shots;
                    
                    % Filter valid ones
                    Row_I = Row_I(Valid_Next);
                    Col_I = Col_I(Valid_Next);
                    Rem_Time = Rem_Time(Valid_Next);
                    
                    if ~isempty(Row_I)
                        idx_next = sub2ind(size(Cum_Times), Row_I, Col_I);
                        idx_curr_valid = sub2ind(size(Cum_Times), Row_I, Col_I-1);
                        T_Cycle_Next = Cum_Times(idx_next) - Cum_Times(idx_curr_valid);
                        T_Cycle_Next(T_Cycle_Next < 0.001) = 0.001;
                        Fractional(Row_I) = Rem_Time ./ T_Cycle_Next;
                    end
                end
                Total_Rounds = Full_Rounds + Fractional;
            else
                % Staircase
                Score_Base = Full_Rounds;
                Partial_Score = zeros(Num_Pixels, 1);
                Check_Mask = Full_Rounds < Max_Shots;
                if any(Check_Mask)
                    Check_Idx = Full_Rounds(Check_Mask) + 1;
                    idx_fire = sub2ind(size(Cum_Times), find(Check_Mask), Check_Idx);
                    T_Fire_End = Cum_Times(idx_fire);
                    for j = 1:SplitParts
                        Part_Delay = HitDelay + (j-1) * SplitInterval;
                        Part_Hit_Time = T_Fire_End + Part_Delay;
                        Hit_Mask = Part_Hit_Time <= Duration;
                        Partial_Score(Check_Mask) = Partial_Score(Check_Mask) + Hit_Mask .* (1/SplitParts);
                    end
                end
                Total_Rounds = Score_Base + Partial_Score;
            end
            
            Total_Rounds = reshape(Total_Rounds, rows, cols);
            Avg_Rate = reshape(Avg_Cycle_Time, rows, cols);
        end
        
        % ============================================================ 
        % Single Trace Calculator (For Visualization)
        % ============================================================ 
        function [FireTimes, VisualCurve] = runSingleTrace(CurrentPanelTime, ShipBaseReload, CatReload, PreCast, PostCast, FireDuration, TotalSkillPct, FinalCDR_Pct, ...
                BuffTableData, SimDuration, Mode, HitDelay)
            
            % 1. Parse Buffs
            Buffs_Time = []; Buffs_Round = [];
            if ~isempty(BuffTableData)
                if iscell(BuffTableData)
                    Modes = BuffTableData(:,1);
                    Vals  = cell2mat(BuffTableData(:, 2:5));
                    IsTime = contains(Modes, '时间');
                    IsRound = contains(Modes, '轮次');
                    if any(IsTime), Buffs_Time = Vals(IsTime, :); end
                    if any(IsRound), Buffs_Round = Vals(IsRound, :); end
                else
                    Buffs_Time = BuffTableData;
                end
            end
            
            % 2. Base Calculation
            Ref_T_Dock = CurrentPanelTime;
            Ref_R_Dock = ShipBaseReload; 
            R_Current_Base = (ShipBaseReload + CatReload);
            Valid_Ref_R = max(Ref_R_Dock + 100, 0.0001);
            Valid_R_Cur = max(R_Current_Base  + 100, 0.0001);
            T_Paper_Raw = Ref_T_Dock .* sqrt(Valid_Ref_R ./ Valid_R_Cur);
            
            % 3. Build Speed Curve (For Integral)
            T_Axis = 0 : 0.05 : (SimDuration * 2); 
            if T_Axis(end) < SimDuration * 2, T_Axis(end+1) = SimDuration*2; end
            
            Rld_Curve = ones(size(T_Axis)) * (1 + TotalSkillPct/100);
            CDR_Curve = ones(size(T_Axis)) * (1 + FinalCDR_Pct/100);
            
            if ~isempty(Buffs_Time)
                for i = 1:size(Buffs_Time, 1)
                    tStart = Buffs_Time(i, 1);
                    tEnd   = Buffs_Time(i, 2);
                    valCDR = Buffs_Time(i, 3);
                    valRld = Buffs_Time(i, 4);
                    mask = (T_Axis >= tStart) & (T_Axis < tEnd);
                    if valRld ~= 0, Rld_Curve(mask) = Rld_Curve(mask) + (valRld/100); end
                    if valCDR ~= 0, CDR_Curve(mask) = CDR_Curve(mask) + (valCDR/100); end
                end
            end
            CDR_Curve(CDR_Curve < 0.1) = 0.1; 
            Rld_Curve(Rld_Curve < 0.1) = 0.1;
            Speed_Curve = sqrt(Rld_Curve) ./ CDR_Curve;
            Progress_Curve = cumtrapz(T_Axis, Speed_Curve);
            
            % Initialize Visual Curve (Copy of Base Speed)
            Visual_Speed = Speed_Curve;
            
            % 4. Simulation Loop
            if strcmp(Mode, '并行') || strcmp(Mode, 'P'), ActionTime = PostCast+FireDuration;
            else, ActionTime = 0; end
            
            Current_Time = 0;
            FireTimes = [];
            Max_Shots = 200;
            
            for k = 1:Max_Shots
                % Round Buffs
                Rld_Mod_Round = 0;
                CDR_Mod_Round = 0;
                if ~isempty(Buffs_Round)
                    RoundMask = (Buffs_Round(:,1) <= k) & (Buffs_Round(:,2) >= k);
                    if any(RoundMask)
                        Rld_Mod_Round = sum(Buffs_Round(RoundMask, 4));
                        CDR_Mod_Round = sum(Buffs_Round(RoundMask, 3));
                    end
                end
                
                Base_Rld_Factor = sqrt(1 + TotalSkillPct/100);
                Total_Rld_Factor = sqrt(1 + (TotalSkillPct + Rld_Mod_Round)/100);
                Rld_Multiplier = Total_Rld_Factor / Base_Rld_Factor; 
                CDR_Multiplier = (1 + CDR_Mod_Round/100);
                
                Req_Time = T_Paper_Raw ./ Rld_Multiplier .* CDR_Multiplier;
                
                % Solve
                P_start = interp1(T_Axis, Progress_Curve, Current_Time, 'linear', 'extrap');
                P_target = P_start + Req_Time;
                T_end_load = interp1(Progress_Curve, T_Axis, P_target, 'linear', 'extrap');
                Actual_Load = T_end_load - Current_Time;
                
                % VISUALIZATION: Apply Round Buff Equivalent Speed
                % If CDR_Multiplier is 0.5 (fast), Effective Speed is x2.
                % Apply ONLY during loading phase [Current_Time, Current_Time + Actual_Load]
                if CDR_Multiplier ~= 1
                    Visual_Factor = 1 / max(CDR_Multiplier, 0.01); 
                    VisMask = (T_Axis >= Current_Time) & (T_Axis <= (Current_Time + Actual_Load));
                    Visual_Speed(VisMask) = Visual_Speed(VisMask) * Visual_Factor;
                end
                
                if strcmp(Mode, '并行') || strcmp(Mode, 'P')
                    T_Cycle = PreCast + max(Actual_Load, ActionTime);
                else
                    T_Cycle = PreCast + Actual_Load + PostCast + FireDuration;
                end
                
                T_Finish = Current_Time + T_Cycle;
                
                if T_Finish > SimDuration + 5, break; end
                
                FireTimes(end+1) = T_Finish;
                Current_Time = T_Finish;
            end
            
            VisualCurve = struct('T', T_Axis, 'Spd', Visual_Speed);
        end

        % ============================================================ 
        % Timeline Report Generator
        % ============================================================ 
        function ReportStr = generateTimelineReport(CurrentPanelTime, ShipBaseReload, CatReload, PreCast, PostCast, FireDuration, TotalSkillPct, FinalCDR_Pct, ...
                BuffTableData, SimDuration, Mode, CalcMode, SplitParts, SplitInterval, HitDelay)
            
            % Split Buffs
            Buffs_Time = []; Buffs_Round = [];
            if ~isempty(BuffTableData)
                if iscell(BuffTableData)
                    Modes = BuffTableData(:,1);
                    Vals  = cell2mat(BuffTableData(:, 2:5));
                    IsTime = strcmp(Modes, '时间(s)');
                    IsRound = strcmp(Modes, '轮次(R)');
                    if any(IsTime), Buffs_Time = Vals(IsTime, :); end
                    if any(IsRound), Buffs_Round = Vals(IsRound, :); end
                else
                    Buffs_Time = BuffTableData;
                end
            end

            Ref_T_Dock = CurrentPanelTime;
            Ref_R_Dock = ShipBaseReload; 
            R_Current_Base = (ShipBaseReload + CatReload);
            Valid_Ref_R = max(Ref_R_Dock + 100, 0.0001);
            Valid_R_Cur = max(R_Current_Base  + 100, 0.0001);
            T_Paper_Raw = Ref_T_Dock .* sqrt(Valid_Ref_R ./ Valid_R_Cur);
            
            % Build Time Speed Curve
            T_Axis = 0 : 0.05 : (SimDuration * 2); 
            if T_Axis(end) < SimDuration * 2, T_Axis(end+1) = SimDuration*2; end
            
            % CDR Logic: Additive (Negative Input = Reduction)
            Rld_Curve = ones(size(T_Axis)) * (1 + TotalSkillPct/100);
            CDR_Curve = ones(size(T_Axis)) * (1 + FinalCDR_Pct/100);
            
            if ~isempty(Buffs_Time)
                for i = 1:size(Buffs_Time, 1)
                    tStart = Buffs_Time(i, 1);
                    tEnd   = Buffs_Time(i, 2);
                    valCDR = Buffs_Time(i, 3);
                    valRld = Buffs_Time(i, 4);
                    mask = (T_Axis >= tStart) & (T_Axis < tEnd);
                    if valRld ~= 0, Rld_Curve(mask) = Rld_Curve(mask) + (valRld/100); end
                    % CDR: Add value
                    if valCDR ~= 0, CDR_Curve(mask) = CDR_Curve(mask) + (valCDR/100); end
                end
            end
            CDR_Curve(CDR_Curve < 0.1) = 0.1; 
            Rld_Curve(Rld_Curve < 0.1) = 0.1;
            Speed_Curve = sqrt(Rld_Curve) ./ CDR_Curve;
            Progress_Curve = cumtrapz(T_Axis, Speed_Curve);
            
            if strcmp(Mode, '并行') || strcmp(Mode, 'P'), ModeStr = '并行 (P)'; ActionTime = PostCast+FireDuration;
            else, ModeStr = '串行 (S)'; ActionTime = 0; end
            
            ReportStr = sprintf('=== 射击时间轴报告 (Hybrid Mode) ===\n');
            ReportStr = [ReportStr sprintf('Mode: %s | Calc: %s\n', ModeStr, CalcMode)];
            ReportStr = [ReportStr sprintf('RawLoad: %.3fs | SimDur: %.1fs\n', T_Paper_Raw, SimDuration)];
            ReportStr = [ReportStr sprintf('PermRld: %+g%% | PermCDR: %+g%%\n', TotalSkillPct, FinalCDR_Pct)];
            ReportStr = [ReportStr sprintf('--------------------------------------------------\n')];
            ReportStr = [ReportStr sprintf('| R# | Load(s) | Cycle(s) | Finish | Mods | HitTimes\n')];
            
            Current_Time = 0;
            Total_Effective = 0;
            Max_Shots = 200;
            
            for k = 1:Max_Shots
                % Apply Round Buffs
                Rld_Mod_Round = 0;
                CDR_Mod_Round = 0;
                Mods_Str = '';
                
                if ~isempty(Buffs_Round)
                    RoundMask = (Buffs_Round(:,1) <= k) & (Buffs_Round(:,2) >= k);
                    if any(RoundMask)
                        Rld_Mod_Round = sum(Buffs_Round(RoundMask, 4));
                        CDR_Mod_Round = sum(Buffs_Round(RoundMask, 3));
                        if Rld_Mod_Round~=0, Mods_Str=[Mods_Str sprintf('Rld%+d%%(R) ', Rld_Mod_Round)]; end
                        if CDR_Mod_Round~=0, Mods_Str=[Mods_Str sprintf('CDR%+d%%(R) ', CDR_Mod_Round)]; end
                    end
                end
                
                % Apply Time Buffs (Visual Reporting Only)
                % Check if Current_Time falls within any Time Buff interval
                if ~isempty(Buffs_Time)
                    for i = 1:size(Buffs_Time, 1)
                        tStart = Buffs_Time(i, 1);
                        tEnd   = Buffs_Time(i, 2);
                        if (Current_Time >= tStart) && (Current_Time < tEnd)
                            valCDR = Buffs_Time(i, 3);
                            valRld = Buffs_Time(i, 4);
                            if valRld~=0, Mods_Str=[Mods_Str sprintf('Rld%+d%%(T) ', valRld)]; end
                            if valCDR~=0, Mods_Str=[Mods_Str sprintf('CDR%+d%%(T) ', valCDR)]; end
                        end
                    end
                end
                
                % Adjust Req Time
                Base_Rld_Factor = sqrt(1 + TotalSkillPct/100);
                Total_Rld_Factor = sqrt(1 + (TotalSkillPct + Rld_Mod_Round)/100);
                Rld_Multiplier = Total_Rld_Factor / Base_Rld_Factor; 
                
                % Multiplier for CDR
                % Additive: 1 + (-15/100) = 0.85
                CDR_Multiplier = (1 + CDR_Mod_Round/100);
                
                Req_Time = T_Paper_Raw ./ Rld_Multiplier .* CDR_Multiplier;
                
                % Solve Integral
                P_start = interp1(T_Axis, Progress_Curve, Current_Time, 'linear', 'extrap');
                P_target = P_start + Req_Time;
                T_end_load = interp1(Progress_Curve, T_Axis, P_target, 'linear', 'extrap');
                Actual_Load = T_end_load - Current_Time;
                
                if strcmp(Mode, '并行') || strcmp(Mode, 'P')
                    T_Cycle = PreCast + max(Actual_Load, ActionTime);
                else
                    T_Cycle = PreCast + Actual_Load + PostCast + FireDuration;
                end
                
                T_Finish = Current_Time + T_Cycle;
                
                Hit_Str = '';
                Parts_Hit = 0;
                for j = 1:SplitParts
                    H_Time = T_Finish + HitDelay + (j-1)*SplitInterval;
                    if H_Time <= SimDuration
                        Hit_Str = [Hit_Str sprintf('%.1f ', H_Time)];
                        Parts_Hit = Parts_Hit + 1;
                    else
                        Hit_Str = [Hit_Str sprintf('(%.1f) ', H_Time)];
                    end
                end
                
                Current_Time = T_Finish;
                
                if Parts_Hit == 0 && strcmp(CalcMode, '阶梯模型')
                    ReportStr = [ReportStr sprintf('|%02d | %.3f | %.3f | %.2f | %s | Timeout\n', k, Actual_Load, T_Cycle, T_Finish, Mods_Str)];
                    break;
                elseif T_Finish > SimDuration && strcmp(CalcMode, '平滑模型')
                     Time_Used = Current_Time - T_Cycle;
                     Frac = (SimDuration - Time_Used) / T_Cycle;
                     Total_Effective = (k-1) + Frac;
                     ReportStr = [ReportStr sprintf('|%02d | %.3f | %.3f | >%.2f | %s | +%.2f (Smooth)\n', k, Actual_Load, T_Cycle, SimDuration, Mods_Str, Frac)];
                     break;
                else
                     ReportStr = [ReportStr sprintf('|%02d | %.3f | %.3f | %.2f | %s | %s\n', k, Actual_Load, T_Cycle, T_Finish, Mods_Str, Hit_Str)];
                     Total_Effective = Total_Effective + Parts_Hit/SplitParts;
                end
            end
            
            ReportStr = [ReportStr sprintf('--------------------------------------------------\n')];
            ReportStr = [ReportStr sprintf('Total Effective Rounds: %.2f\n', Total_Effective)];
        end
    end
end