%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 战斗流程主逻辑
%%% 主要为场景调用后，参战方数据初始化到技能释放流程
%%% @end
%%% -------------------------------------------------------
-module(mod_battle).
-compile(export_all).
-include("scene.hrl").
-include("server.hrl").
-include("skill.hrl").
-include("battle.hrl").
-include("common.hrl").
-include("motion.hrl").
-include("attr.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("team.hrl").
-include("def_fun.hrl").
-include("role.hrl").
-include("def_module.hrl").
-include("def_act_label.hrl").

%% 待补充完全后，去掉export_all
-export([
        assist/6, assist/7, assist/8
        ,battle/2
        ,init_data/1                                    % 通过人物/怪物数据，初始化战斗者数据
        ,send_to_node_pid/3
        ,send_to_node_mon/4
    ]).

%% @spec -> {true, #ets_scene_user | #scene_object } | {false, ErrCode, #ets_scene_user | #scene_object}
%% 玩家和怪物发起攻击
%% @end
battle(BattleExtraArgs, EtsScene) ->
    #battle_args{
        att_user = OriginalAer,         %% 场景上的玩家或者怪物数据
        der_list = DerList,
        is_combo = IsCombo,
        skill_id = SkillId,
        skill_lv = SkillLv,
        now_time = NowTime,
        att_x    = AttX,
        att_y    = AttY,
        att_angle = AttAngle,
        att_object_id = AttObjectId,
        skill_stage = SkillStage,
        sign     = Sign} = BattleExtraArgs,
    %% 将场景上的玩家或者怪物数据转成#battle_status{}必要的战斗数据
    Aer = if
        Sign =:= ?BATTLE_SIGN_PARTNER orelse Sign =:= ?BATTLE_SIGN_PET orelse
        Sign =:= ?BATTLE_SIGN_WEAPON orelse Sign =:= ?BATTLE_SIGN_CHILD ->
            init_data(OriginalAer, Sign, AttObjectId);
        true ->
            init_data(OriginalAer, NowTime)
    end,
    InitDerList = [init_data(Der, NowTime) || Der <- DerList],
    MainDerList = case InitDerList of
        [Fst|T] -> [Fst#battle_status{is_main = 1}|T];
        _ -> InitDerList
    end,
    NewBattleExtraArgs = BattleExtraArgs#battle_args{der_list = MainDerList},
    %% 判断玩家是不是处理特殊状态
    case check_use_skill(Aer, IsCombo, SkillId, SkillLv, SkillStage, NowTime) of
        {true, AfterEffAer, #skill{is_shake_pre=1}=_SkillR, _} ->
            %% AfHideAer = break_steath(Aer, AfterEffAer#battle_status{shaking_skill=SkillId}, SkillR, EtsScene),
            EtsAer = back_data(AfterEffAer, OriginalAer, AttObjectId),
            send_active_msg(AfterEffAer, EtsAer, [], SkillId, SkillStage, SkillLv, AttX, AttY, AttAngle, AttObjectId,
                EtsScene#ets_scene.broadcast, NowTime),
            {true, EtsAer, SkillId};

        {true, AfterEffAer, SkillR, MainSkillId} ->
            {AfBattleAer, DefList} = do_battle(AfterEffAer, NewBattleExtraArgs, SkillR, MainSkillId, EtsScene),
            {AfMergeCdAer, CdMinusList} = merge_skill_cd(AfBattleAer, SkillId, NowTime),
            EtsAer = back_data(AfMergeCdAer, OriginalAer, AttObjectId),
            send_active_msg(AfMergeCdAer, EtsAer, DefList, SkillId, SkillStage, SkillLv, AttX, AttY, AttAngle, AttObjectId,
                EtsScene#ets_scene.broadcast, NowTime),
            send_minus_cd(AfMergeCdAer, CdMinusList),
            send_trigger_passive(AfBattleAer),
            {true, EtsAer, MainSkillId};

        {false, ErrCode, AfterEffAer} ->
            {false, ErrCode, AfterEffAer}
    end.

%% 辅助技能释放
assist(OriginalAer, OriginalDer, IsCombo, SkillId, SkillLv, EtsScene) ->
    assist(OriginalAer, OriginalDer, IsCombo, SkillId, SkillLv, 0, [], EtsScene).

assist(OriginalAer, OriginalDer, IsCombo, SkillId, SkillLv, AttObjectId, EtsScene) ->
    assist(OriginalAer, OriginalDer, IsCombo, SkillId, SkillLv, AttObjectId, [], EtsScene).

assist(OriginalAer, OriginalDer, IsCombo, SkillId, SkillLv, AttObjectId, SkillAdjust, EtsScene) ->
    NowTime = utime:longunixtime(),
    %% 初始数据
    Aer = init_data(OriginalAer, NowTime),
    Der = init_data(OriginalDer, NowTime),
    SkillStage = 1,
    case check_use_skill(Aer, IsCombo, SkillId, SkillLv, SkillStage, NowTime) of
        {false, ErrCode, AfterEffAer} ->
            {false, ErrCode, AfterEffAer};

        {true, AfterEffAer, #skill{is_shake_pre=1}=_SkillR, MainSkillId} ->
            DBuffList = lib_skill_buff:pack_buff(
                AfterEffAer#battle_status.battle_attr#battle_attr.attr_buff_list
                ++ AfterEffAer#battle_status.battle_attr#battle_attr.other_buff_list,
                NowTime,
                []
            ),
            AssistList = [[AfterEffAer#battle_status.sign, AfterEffAer#battle_status.id, AfterEffAer#battle_status.battle_attr#battle_attr.hp, DBuffList]],
            send_assist_msg(Aer#battle_status.sign, Aer#battle_status.id, SkillId, SkillLv, AttObjectId, AssistList,
                Aer#battle_status.copy_id, Aer#battle_status.x, Aer#battle_status.y, EtsScene#ets_scene.broadcast),
            %% 打破隐身
            %% AfHideAer = break_steath(Aer, AfterEffAer, SkillR, EtsScene),
            {true, back_data(AfterEffAer#battle_status{shaking_skill=SkillId}, OriginalAer), MainSkillId};

        {true, AfterEffAer, SkillR, MainSkillId} ->
            AssistAer = assist_skill(AfterEffAer, Der, SkillR, SkillStage, SkillAdjust, AttObjectId, NowTime, EtsScene),
            %% AfHideAer = break_steath(Aer, AssistAer, SkillR, EtsScene),
            {true, back_data(AssistAer, OriginalAer), MainSkillId}
    end.

%% 检查是否可以使用此技能
%% retrun: {true, 攻击者#battle_status{}, #skill{}, MainSkillId} | {false, ErrCode, #battle_status{}}
%% 其中NextSkillId, NextSkillTime 用于场景对象使用连续技, 为0时没有连续技能

%% 服务器触发的辅助或者被动技能，不会被玩家自身的状态打断
check_use_skill(Aer, server, SkillId, SkillLv, SkillStage, NowTime) ->
    case data_skill:get(SkillId, SkillLv) of
        [] ->
            {false, ?ERR_CONF, Aer};
        #skill{is_shake_pre = IsShakePre, lv_data = #skill_lv{cd = Cd}} = SkillR ->
            #battle_status{skill_cd = SkillCd} = Aer,
            case check_cd_skill(SkillCd, SkillId, NowTime, SkillStage) of
                false ->
                    {false, ?ERR_CD, Aer};
                true ->
                    Aer1 = check_set_skill_cd(IsShakePre, Aer, SkillId, NowTime, Cd, SkillStage),
                    {true, Aer1, SkillR, 0}
            end
    end;
check_use_skill(_Aer, IsCombo, SkillId, SkillLv, SkillStage, NowTime) ->
    case check_aer_buff_negative(_Aer, SkillId, SkillLv, NowTime) of
        {false, ErrorCode, Aer} ->
            {false, ErrorCode, Aer};
        {true, SkillR, Aer} ->
            #skill{is_shake_pre = IsShakePre, type = _Type, combo = Combo, lv_data = #skill_lv{cd=Cd}} = SkillR,
            case IsCombo of
                0 ->
                    #battle_status{sign = _Sign, skill_combo = SkillCombo, skill_cd = SkillCd, battle_attr = BattleAttr} = Aer,
                    CheckCdResult = check_cd_skill(SkillCd, SkillId, NowTime, SkillStage),
                    IsEnoughAnger = lib_battle:check_skill_use_cond_anger(BattleAttr, SkillR, SkillStage),
                    if
                        CheckCdResult =:= false ->
                            {false, ?ERR_CD, Aer};
                        not IsEnoughAnger -> %% 怒气不足
                            {false, ?ERR_ANGER_NO_ENOUGH, Aer};
                        true -> %% 检查玩家身上是不是存在combo技能
                            case check_combo_skill(SkillCombo, SkillId, IsCombo, []) of
                                {_, ComboR, IsSetCd, T} ->
                                    Aer1 = set_aer_combo_buff(Aer#battle_status{skill_combo=T}, ComboR#skill_combo.next_time, NowTime,
                                        ComboR#skill_combo.main_skill_id, ComboR#skill_combo.main_skill_lv),
                                    Aer2 = check_set_skill_cd(IsSetCd, Aer1, SkillId, NowTime, Cd, SkillStage),
                                    {true, Aer2, SkillR, ComboR#skill_combo.main_skill_id};

                                false -> %% 新主动技能
                                    case Combo of %% 有combo技能
                                        [{_, LastTime, _}|T] when T /= [] ->
                                            IsSetCd = case IsShakePre of
                                                0 -> 1;
                                                _ -> 0
                                            end,
                                            ComboR = #skill_combo{main_skill_id=SkillId, main_skill_lv=SkillLv, main_skill_cd=Cd,
                                                is_set_cd=IsSetCd, next_time=LastTime, combo_list=T},
                                            Aer1 = Aer#battle_status{skill_combo=[ComboR|lists:keydelete(SkillId, #skill_combo.main_skill_id, SkillCombo)]},
                                            Aer2 = set_aer_combo_buff(Aer1, LastTime, NowTime, SkillId, SkillLv),
                                            Aer3 = check_set_skill_cd(IsShakePre, Aer2, SkillId, NowTime, Cd, SkillStage),
                                            {true, Aer3, SkillR, SkillId};
                                        _  ->
                                            Aer1 = check_set_skill_cd(IsShakePre, Aer, SkillId, NowTime, Cd, SkillStage),
                                            {true, Aer1, SkillR, 0} %% 无副技能，即没前摇
                                    end
                            end
                    end;
                _ -> %% 其他情况默认放combo
                    #battle_status{skill_combo=SkillCombo} = Aer,
                    case check_combo_skill(SkillCombo, SkillId, IsCombo, []) of
                        {1, InitComboR, IsSetCd, T} ->
                            ComboR = InitComboR#skill_combo{main_skill_lv = SkillLv},
                            #skill_combo{main_skill_id = MainSkillId, main_skill_cd = MainSkillCd} = ComboR,
                            Aer1 = del_aer_combo_buff(Aer#battle_status{skill_combo=T}, MainSkillId),
                            Aer2 = check_set_skill_cd(IsSetCd, Aer1, MainSkillId, NowTime, MainSkillCd, SkillStage),
                            {true, Aer2, SkillR, MainSkillId}; %% 无后续技能，即没前摇
                        {2, InitComboR, IsSetCd, T} ->
                            ComboR = InitComboR#skill_combo{main_skill_lv = SkillLv},
                            #skill_combo{main_skill_id = MainSkillId, main_skill_cd = MainSkillCd} = ComboR,
                            Aer1 = set_aer_combo_buff(Aer#battle_status{skill_combo=T}, ComboR#skill_combo.next_time, NowTime,
                                MainSkillId, ComboR#skill_combo.main_skill_lv),
                            Aer2 = check_set_skill_cd(IsSetCd, Aer1, MainSkillId, NowTime, MainSkillCd, SkillStage),
                            {true, Aer2, SkillR, MainSkillId};
                        false ->
                            {false, ?ERR_COMBO, Aer}
                    end
            end
    end.

%% 检查攻击者的负面状态
check_aer_buff_negative(Aer, SkillId, SkillLv, NowTime)->
    case data_skill:get(SkillId, SkillLv) of
        #skill{type = Type, lv_data = #skill_lv{effect = EffectBuff}} = SkillR when
        Type == ?SKILL_TYPE_ACTIVE orelse Type == ?SKILL_TYPE_ASSIST ->
            case lib_skill_buff:buff_find_id(?SPBUFF_CLEAN_BUFF, EffectBuff) of
                [] ->
                    IsNormal = lib_skill_api:is_normal(SkillId),
                    BA = Aer#battle_status.battle_attr,
                    case lib_skill_buff:is_can_attack(IsNormal, Aer#battle_status.sign, BA#battle_attr.other_buff_list, NowTime) of
                        {false, BuffType, _Time} ->
                            if
                                BuffType == ?SPBUFF_DIZZY -> {false, ?ERR_DIZZY, Aer};
                                BuffType == ?SPBUFF_SILENCE -> {false, ?ERR_SILENCE, Aer};
                                BuffType == ?SPBUFF_FEAR -> {false, ?ERR_FEAR, Aer};
                                true -> {false, ?ERR_OTHER_BUFF, Aer}
                            end;
                        _ ->
                            {true, SkillR, Aer}
                    end;
                _Buffs ->
                    {true, SkillR, Aer}
            end;
        _ ->
            {false, ?ERR_CONF, Aer}
    end.

%% 检查副技能释放是否合法
%% skill_combo有多组副技能，看看是否有合法的组
check_combo_skill([], _, _, _) -> false;

%% 检查技能id是不是combo链接技能
check_combo_skill([#skill_combo{is_set_cd=IsSetCd, combo_list=[{SkillId, NextTime, _}|ET]}=ComboR |T], SkillId, _, Result) ->
    case ET of
        [] ->   %% 剩余一次，移除该副技能
            %% Res=1 技能已经释放完
            {1, ComboR#skill_combo{next_time=0, is_set_cd=1}, IsSetCd, Result++T};
        _  ->
            %% Res=2 还有后续副技能
            ComboR1 = ComboR#skill_combo{next_time=NextTime, combo_list=ET, is_set_cd=1},
            {2, ComboR1, IsSetCd, [ComboR1|Result]++T}
    end;
check_combo_skill([H|T], SkillId, IsCombo, Result) ->
    check_combo_skill(T, SkillId, IsCombo, [H|Result]).

%% 检查技能cd
check_cd_skill(SkillCd, SkillId, NowTime, SkillStage) when SkillStage =< 1 ->
    case lists:keyfind(SkillId, 1, SkillCd) of
        {_, EndTime} when EndTime - 50 > NowTime ->
            %% 给予一定时间宽容（50ms）
            false;
        _ -> true
    end;
check_cd_skill(_SkillCd, _SkillId, _NowTime, _SkillStage) ->
    true.

%% 检查设置技能CD
check_set_skill_cd(IsSetCd, Aer = #battle_status{}, Combo, NowTime, MainSkillCd, SkillStage) ->
    if
        IsSetCd =:= 1 ->        % （Combo技能）已经设置过技能CD
            Aer;
        SkillStage > 1 ->       % 多段技能后续阶段不需要设置CD，按第一阶段计CD
            Aer;
        MainSkillCd =:= 0 ->    % 主技能不需要设置CD（前摇技能）
            Aer;
        true ->
            set_skill_cd(Aer, Combo, NowTime, MainSkillCd)
    end;
check_set_skill_cd(_IsSetCd, Aer, _Combo, _NowTime, _MainSkillCd, _SkillStage) ->
    Aer.

-ifdef(DEV_SERVER).
set_skill_cd(#battle_status{id = Id, skill_cd=SkillCd} = Aer, SkillId, NowTime, CD) ->
    case get({nocd, Id}) of  %% gm命令添加测试用的nocd
        undefined ->
            EndTime = NowTime + CD,
            NewSkillCd = [{SkillId, EndTime} | lists:keydelete(SkillId, 1, SkillCd)],
            Aer#battle_status{skill_cd = NewSkillCd};
        _ ->
            Aer
    end.
-else.
set_skill_cd(#battle_status{id = _Id, skill_cd=SkillCd} = Aer, SkillId, NowTime, CD) ->
    EndTime = NowTime + CD,
    NewSkillCd = [{SkillId, EndTime} | lists:keydelete(SkillId, 1, SkillCd)],
    Aer#battle_status{skill_cd = NewSkillCd}.
-endif.

%% 合并技能cd
merge_skill_cd(#battle_status{skill_cd = SkillCd, skill_minus_cd = SkillMinusCd} = BA, NowSkillId, NowTime) ->
    %% 持续性减CD BUFF
    BuffList = BA#battle_status.battle_attr#battle_attr.other_buff_list,
    MinusCdRatio = lib_skill_buff:get_minus_cd(BuffList, NowTime),
    case {MinusCdRatio > 0, lists:keyfind(NowSkillId, 1, SkillCd)} of
        {true, {NowSkillId, EndTime}} when EndTime > NowTime ->
            LeftTime = max(EndTime - NowTime, 0),
            NewLeftTime = round( max(LeftTime * (1-MinusCdRatio), 0) ),
            NewEndTime = NowTime + NewLeftTime,
            ChangeList = [{NowSkillId, NewEndTime}],
            NewCdList = lists:keystore(NowSkillId, 1, SkillCd, {NowSkillId, NewEndTime});
        _ ->
            ChangeList = [],
            NewCdList = SkillCd
    end,
    %% 一次性BUFF
    F = fun({SkillId, Time}, {AccChangeList, AccCdList}) ->
            #skill{type = SkillType} = data_skill:get(SkillId, 1),
            case {SkillType==?SKILL_TYPE_ACTIVE, lists:keyfind(SkillId, 1, AccCdList)} of
                {true, {SkillId, AccEndTime}} ->
                    NewAccEndTime = max(AccEndTime - Time, 0),
                    NewAccCdList = lists:keystore(SkillId, 1, AccCdList, {SkillId, NewAccEndTime}),
                    { [{SkillId, NewAccEndTime} |AccChangeList], NewAccCdList};
                _ -> {AccChangeList, AccCdList}
            end
    end,
    {AccChangeList, AccCdList} = lists:foldl(F, {[], NewCdList}, SkillMinusCd),
    NewBA = BA#battle_status{skill_cd = AccCdList, skill_minus_cd = []},
    LastChangeList = ChangeList ++ AccChangeList,
    {NewBA, LastChangeList}.

%% 设置攻击者的连招buff
set_aer_combo_buff(Aer, LastTime, NowTime, MainSkillId, MainSkillLv) ->
    #battle_status{battle_attr=#battle_attr{other_buff_list=OtherBuffL}=BA} = Aer,
    NewOtherBuff = lib_skill_buff:set_combo_buff(OtherBuffL, MainSkillId, MainSkillLv, NowTime+LastTime+2000),
    Aer#battle_status{battle_attr=BA#battle_attr{other_buff_list=NewOtherBuff}}.

del_aer_combo_buff(Aer, MainSkillId) ->
    #battle_status{battle_attr=#battle_attr{other_buff_list=OtherBuffL}=BA} = Aer,
    NewOtherBuff = lib_skill_buff:del_combo_buff(OtherBuffL, MainSkillId),
    Aer#battle_status{battle_attr=BA#battle_attr{other_buff_list=NewOtherBuff}}.

%%初始化战斗双方属性
init_data(Arr) ->
    init_data(Arr, utime:longunixtime()).

init_data(Arr, _NowTime) when is_record(Arr, scene_object) ->
    #scene_object{mon=Mon, dummy=Dummy} = Arr,
    D = #battle_status{
        id          = Arr#scene_object.id,
        config_id   = Arr#scene_object.config_id,
        figure      = Arr#scene_object.figure,
        scene       = Arr#scene_object.scene,
        copy_id     = Arr#scene_object.copy_id,
        battle_attr = Arr#scene_object.battle_attr#battle_attr{skill_effect=#skill_effect{}},
        x           = Arr#scene_object.x,
        y           = Arr#scene_object.y,
        sign        = Arr#scene_object.sign,
        pid         = Arr#scene_object.aid,
        att_type    = Arr#scene_object.att_type,
        online      = ?ONLINE_ON,
        skill_passive = Arr#scene_object.skill_passive,
        skill_cd    = Arr#scene_object.skill_cd,
        skill_combo = Arr#scene_object.skill_combo,
        shaking_skill = Arr#scene_object.shaking_skill,
        skill_owner = Arr#scene_object.skill_owner,
        is_be_atted = Arr#scene_object.is_be_atted,
        guild_id    = Arr#scene_object.figure#figure.guild_id,
        is_armor    = Arr#scene_object.is_armor,
        cannot_beatt_list = Arr#scene_object.cannot_beatt_list,
        personality = Arr#scene_object.personality
    },
    MonD = case is_record(Mon, scene_mon) of
        true ->
            D#battle_status{
                mid = Mon#scene_mon.mid,
                kind = Mon#scene_mon.kind,
                boss = Mon#scene_mon.boss,
                owner= Mon#scene_mon.owner,
                owner_name = Mon#scene_mon.owner_name
            };
        false -> D
    end,
    DummyD = case is_record(Dummy, scene_dummy) of
        true ->
            MonD#battle_status{
                team_id = Dummy#scene_dummy.team_id
            };
        false -> MonD
    end,
    DummyD;
init_data(Arr, NowTime) when is_record(Arr, ets_scene_user) ->
    %% 式神，需要转换为式神属性
    {BattleAttr, OriginHp} = god_api:scene_init_user(Arr),
    %% 判断
    SkillBuffer = god_api:check_sp_skill(#skill_effect{}, Arr#ets_scene_user.god_fighting, NowTime),
    #ets_scene_user{
        scene_motion = #scene_motion{no_injury_time = NoInjuryTime}
    } = Arr,
    #battle_status{
        id       = Arr#ets_scene_user.id,
        server_id= Arr#ets_scene_user.server_id,
        node     = Arr#ets_scene_user.node,
        scene    = Arr#ets_scene_user.scene,
        copy_id  = Arr#ets_scene_user.copy_id,
        figure   = Arr#ets_scene_user.figure,
        battle_attr = BattleAttr#battle_attr{skill_effect = SkillBuffer},
        skill_passive = Arr#ets_scene_user.skill_passive,
        skill_cd    = Arr#ets_scene_user.skill_cd,
        skill_combo = Arr#ets_scene_user.skill_combo,
        shaking_skill = Arr#ets_scene_user.shaking_skill,
        x = Arr#ets_scene_user.x,
        y = Arr#ets_scene_user.y,
        sign = ?BATTLE_SIGN_PLAYER,
        pid  = Arr#ets_scene_user.pid,
        sid  = Arr#ets_scene_user.sid,
        team_id = Arr#ets_scene_user.team#status_team.team_id,
        team_skill = Arr#ets_scene_user.team#status_team.team_skill,
        team_skill_num = Arr#ets_scene_user.team#status_team.skill_num,
        guild_id= Arr#ets_scene_user.figure#figure.guild_id,
        att_list = Arr#ets_scene_user.att_list,
        boss_tired = get_boss_tired(?SCENE_TYPE_REWARD_BOSS, Arr),
        deserted_boss_tired = get_boss_tired(?SCENE_TYPE_DESERTED_BOSS, Arr),
        unite_buffs = Arr#ets_scene_user.unite_buffs,
        att_tired = Arr#ets_scene_user.att_tired,
        act_label = Arr#ets_scene_user.act_label,
        boss_crush_r = Arr#ets_scene_user.boss_crush_r,
        can_att_list = Arr#ets_scene_user.can_att_list,
        cannot_beatt_list = Arr#ets_scene_user.cannot_beatt_list
        ,team_leader_name = Arr#ets_scene_user.team#status_team.leader_name
        ,support_id = Arr#ets_scene_user.support_id                    % 协助玩家id
        ,support_name = Arr#ets_scene_user.support_name                % 协助玩家名字
        ,support_team = Arr#ets_scene_user.support_team                % 协助玩家队伍id
        ,support_team_name = Arr#ets_scene_user.support_team_name      % 协助玩家队长名字
        ,support_content = Arr#ets_scene_user.support_content
        ,badge = Arr#ets_scene_user.badge                              % 社团徽章
        ,motion_no_injury = NoInjuryTime
        ,god_fighting = Arr#ets_scene_user.god_fighting % 召唤出来的式神[#god_fighting{}]
        ,origin_hp = OriginHp
        ,online = Arr#ets_scene_user.online
    };
init_data(Arr, _) ->
    ?ERR("init_data: error:~p~n", [Arr]),
    #battle_status{}.

%% 封装其他战斗类型(宠物,宝宝)的战斗体
init_data(Arr, Sign, _AttObjectId) when is_record(Arr, ets_scene_user)->
    Battler = #battle_status{
        id       = Arr#ets_scene_user.id,
        server_id= Arr#ets_scene_user.server_id,
        node     = Arr#ets_scene_user.node,
        scene    = Arr#ets_scene_user.scene,
        copy_id  = Arr#ets_scene_user.copy_id,
        figure   = Arr#ets_scene_user.figure,
        battle_attr = Arr#ets_scene_user.battle_attr,
        skill_passive = [],
        skill_cd    = Arr#ets_scene_user.skill_cd,
        skill_combo = Arr#ets_scene_user.skill_combo,
        shaking_skill = Arr#ets_scene_user.shaking_skill,
        x = Arr#ets_scene_user.x,
        y = Arr#ets_scene_user.y,
        sign = Sign,
        pid  = Arr#ets_scene_user.pid,
        sid  = Arr#ets_scene_user.sid,
        team_id = Arr#ets_scene_user.team#status_team.team_id,
        guild_id= Arr#ets_scene_user.figure#figure.guild_id,
        online = Arr#ets_scene_user.online,
        att_list = Arr#ets_scene_user.att_list,
        boss_tired = get_boss_tired(?SCENE_TYPE_REWARD_BOSS, Arr),
        deserted_boss_tired = get_boss_tired(?SCENE_TYPE_DESERTED_BOSS, Arr),
        unite_buffs = Arr#ets_scene_user.unite_buffs,
        att_tired = Arr#ets_scene_user.att_tired,
        act_label = Arr#ets_scene_user.act_label,
        boss_crush_r = Arr#ets_scene_user.boss_crush_r,
        can_att_list = Arr#ets_scene_user.can_att_list
        ,team_leader_name = Arr#ets_scene_user.team#status_team.leader_name
        ,support_id = Arr#ets_scene_user.support_id                    % 协助玩家id
        ,support_name = Arr#ets_scene_user.support_name                % 协助玩家名字
        ,support_team = Arr#ets_scene_user.support_team                % 协助玩家队伍id
        ,support_team_name = Arr#ets_scene_user.support_team_name      % 协助玩家队长名字
        ,support_content = Arr#ets_scene_user.support_content          % 协助玩家简要信息
        ,badge = Arr#ets_scene_user.badge                              % 社团徽章
    },
    if
        Sign =:= ?BATTLE_SIGN_PARTNER ->
            BA = Arr#ets_scene_user.battle_attr#battle_attr{skill_effect = #skill_effect{}},
            EntityBA = Arr#ets_scene_user.partner_battle_attr,
            Battler#battle_status{battle_attr = BA, entity_attr = EntityBA#battle_attr.attr};
        Sign =:= ?BATTLE_SIGN_PET ->
            BA = Arr#ets_scene_user.battle_attr#battle_attr{skill_effect = #skill_effect{}},
            Battler#battle_status{battle_attr = BA};
        Sign =:= ?BATTLE_SIGN_CHILD ->
            BA = Arr#ets_scene_user.battle_attr#battle_attr{skill_effect = #skill_effect{}},
            EntityBA = Arr#ets_scene_user.child_battle_attr,
            Battler#battle_status{battle_attr = BA, entity_attr = EntityBA#battle_attr.attr};
        Sign =:= ?BATTLE_SIGN_WEAPON ->
            BA = Arr#ets_scene_user.battle_attr#battle_attr{skill_effect = #skill_effect{}},
            EntityBA = Arr#ets_scene_user.baby_battle_attr,
            Battler#battle_status{battle_attr = BA, entity_attr = EntityBA#battle_attr.attr};
        true ->
            BA = #battle_attr{skill_effect=#skill_effect{}}, %% 异常情况
            Battler#battle_status{battle_attr = BA}
    end;
init_data(Arr, Sign, AttObjectId) ->
    ?ERR("init_data: error:~p~n", [Arr, Sign, AttObjectId]),
    #battle_status{}.

back_data(Aer, EtsAer) ->
    back_data(Aer, EtsAer, 0).

%% 回写
%% !!!如有修改!!!，必须修改对应的:lib_battle_api:slim_back_data()
back_data(Aer, #ets_scene_user{battle_attr = #battle_attr{attr = AttrOrigin}} = EtsAer, _AttObjectId) ->
    NewEtsAer = EtsAer#ets_scene_user{
        skill_cd    = Aer#battle_status.skill_cd,
        skill_combo = Aer#battle_status.skill_combo,
        shaking_skill = Aer#battle_status.shaking_skill,
        x = Aer#battle_status.x,
        y = Aer#battle_status.y},
    %% 如果有式神数据，需要回写为非式神数据
    {GodFighting, NewBattleAttr} = god_api:scene_after_battle_attack(EtsAer, Aer#battle_status.battle_attr),
    NewEtsAer#ets_scene_user{battle_attr = NewBattleAttr#battle_attr{attr = AttrOrigin}, god_fighting = GodFighting};
%% !!!如有修改!!!，必须修改对应的:lib_battle_api:slim_back_data()
back_data(Aer, #scene_object{}=EtsAer, _AttObjectId) ->
    EtsAer#scene_object{
        battle_attr = Aer#battle_status.battle_attr,
        skill_cd    = Aer#battle_status.skill_cd,
        skill_combo = Aer#battle_status.skill_combo,
        shaking_skill = Aer#battle_status.shaking_skill,
        x = Aer#battle_status.x,
        y = Aer#battle_status.y
    }.

%%发送消息
send_active_msg(Aer, EtsAer, DefList, SkillId, SkillStage, SkillLv, AttX, AttY, AttAngle, AttObjectId, Broadcast, NowTime) ->
    #battle_status{
        id = Id,
        x = X,
        y = Y,
        scene = _Scene,
        sign = Sign,
        battle_attr=BA
    } = Aer,
    #battle_attr{hp=Hp, attr_buff_list=AttrBuffList, other_buff_list=OtherBuffList} = BA,
    AerBuffList = lib_skill_buff:pack_buff(AttrBuffList++OtherBuffList, NowTime, []),
    RoleHp = god_api:get_fighting_role_hp(Aer, Hp),
    GodHp = god_api:get_fighting_god_hp(Aer, Hp),
    {ok, BinData}  = pt_200:write(20001, [Sign, Id, RoleHp, 0, SkillId, SkillStage, SkillLv,
            X, Y, AttX, AttY, AttAngle, AttObjectId, AerBuffList, GodHp, DefList]),
    lib_battle:send_to_scene_area(EtsAer, Broadcast, BinData),
    ok.

%% 发送cd减少消息
send_minus_cd(#battle_status{node = Node, sign = ?BATTLE_SIGN_PLAYER, sid = Sid}, CdMinusList=[_|_]) ->
    {ok, BinData} = pt_200:write(20018, [CdMinusList]),
    lib_server_send:send_to_sid(Node, Sid, BinData),
    ok;
send_minus_cd(_, _) -> ok.

send_trigger_passive(BattleAer) ->
    #battle_status{triggers_passive = Triggers, sid = Sid, node = Node} = BattleAer,
    {TriggersSelf, TriggersBroadcast} = lists:foldl(
        fun({SkillCareer, SkillId, CD}, {TempSelf, TempBroadcast}) ->
            if
                SkillCareer =:= ?SKILL_CAREER_JUMP ->
                    {[{SkillId, CD}|TempSelf], TempBroadcast};
                SkillCareer =:= ?SKILL_CAREER_WARSOUL ->
                    {TempSelf, [SkillId|TempBroadcast]};
                SkillCareer =:= ?SKILL_CAREER_PET ->
                    {TempSelf, [SkillId|TempBroadcast]};
                true ->
                    {TempSelf, TempBroadcast}
            end
        end, {[], []}, Triggers
    ),
    case TriggersSelf =/= [] of
        true  ->
            {ok, BinData} = pt_200:write(20003, [TriggersSelf]),
            lib_server_send:send_to_sid(Node, Sid, BinData);
        false ->
            ok
    end,
    case TriggersBroadcast =/= [] of
        true  ->
            {ok, BinData2} = pt_200:write(20007, [TriggersBroadcast, BattleAer#battle_status.id]),
            lib_scene_agent:send_to_local_area_scene(BattleAer#battle_status.copy_id, BattleAer#battle_status.x, BattleAer#battle_status.y, BinData2);
        false ->
            ok
    end.

%% 发送辅助技能信息
send_assist_msg(Sign, Id, SkillId, SkillLv, AttObjectId, AssistList, CopyId, X, Y, Broadcast) ->
    {ok, BinData} = pt_200:write(20006, [Sign, Id, SkillId, SkillLv, AttObjectId, AssistList]),
    lib_battle:send_to_scene(CopyId, X, Y, Broadcast, BinData).

%% 施放辅助技能
assist_skill(AfterEffAer, User, SkillR, SkillStage, _SkillAdjust, AttObjectId, NowTime, EtsScene) ->
    %% _SkillAdjust 辅助技能这里，技能调整参数暂时并未做处理，只在主动技能那边生效
    #battle_status{sign=Sign, id=Id, scene = SceneId, copy_id=CopyId, x=X, y=Y, team_id=TeamId, battle_attr=BA, skill_owner=SkillOwner} = AfterEffAer,
    #battle_attr{group=Group} = BA,
    #skill{id=SkillId, lv=SkillLv, mod=Mod, obj=Obj, career = _SkillCareer, lv_data=LvData} = SkillR,
    #skill_lv{area=Area} = LvData,
    Assisters = case Mod of
        %% 群体相同类型队友
        2 when (TeamId > 0 orelse Group > 0) andalso Obj == 8 ->
            {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),
            Args = {Group, Sign, Id, OwnerSign, OwnerId},
            EtsList = lib_scene_calc:get_scene_object_for_assist(SceneId, CopyId, X, Y, Area, TeamId, Args),
            FunFilter = fun(#scene_object{sign=ObjectSign}) when Sign>0 andalso Sign == ObjectSign -> true;
                (#ets_scene_user{}) when Sign == ?BATTLE_SIGN_PLAYER -> true;
                (_) -> false
            end,
            TmpL = [init_data(E)||E<-lists:filter(FunFilter, EtsList)],
            [AfterEffAer|TmpL];
        %% 群体，对敌方怪物释放。玩家自己和友方怪物需要排除在外
        %% 策划大佬的特殊需求，又不好用主动技能触发(前端没有技能表现)。姑且这么做吧。
        2 when (TeamId > 0 orelse Group > 0) andalso Obj == 2 ->
            {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),
            Args = {Group, Sign, Id, OwnerSign, OwnerId},
            EtsList = lib_scene_calc:get_scene_mon_for_assist(SceneId, CopyId, X, Y, Area, TeamId, Args),
            FunFilter = fun(#scene_object{sign=ObjectSign}) when ObjectSign > 0 -> true;
                (_) -> false
            end,
            TmpL = [init_data(E)||E<-lists:filter(FunFilter, EtsList)],
            TmpL;
        %% 群体
        2 when TeamId > 0 orelse Group > 0 ->
            {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),
            Args = {Group, Sign, Id, OwnerSign, OwnerId},
            EtsList = lib_scene_calc:get_scene_object_for_assist(SceneId, CopyId, X, Y, Area, TeamId, Args),
            TmpL = [init_data(E)||E<-EtsList],
            [AfterEffAer|TmpL];
        %% 群体没队伍
        2 ->
            [AfterEffAer];

        %% 单体自己，buff作用方必须是自己，则被施法者为空
        1 when Obj == 1 ->
            [AfterEffAer];
        %% 血量最少的人员
        1 when Obj == 5 ->
            {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),
            Args = {Group, Sign, Id, OwnerSign, OwnerId},
            EtsList = lib_scene_calc:get_scene_object_for_assist(SceneId, CopyId, X, Y, Area, TeamId, Args),
            Fminhp = fun(#battle_status{battle_attr=#battle_attr{hp=Hp1, hp_lim=HpLim1}}, #battle_status{battle_attr=#battle_attr{hp=Hp2, hp_lim=HpLim2}}) ->
                    Hp1/HpLim1  =< Hp2/HpLim2
            end,
            case EtsList of
                [] -> [AfterEffAer]; %% 周围没队友，给自己加
                [Elem] ->
                    [MinHpElem|_] = lists:sort(Fminhp, [AfterEffAer, init_data(Elem)]),
                    [MinHpElem];
                _ ->
                    [MinHpElem|_] = lists:sort(Fminhp, [AfterEffAer|[init_data(E)||E<-EtsList]]),
                    [MinHpElem]
            end;
        %% 单体主人
        1 when Obj == 7 andalso is_record(SkillOwner, skill_owner) ->
            {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),
            OwnerObject = case OwnerSign of
                ?BATTLE_SIGN_PLAYER -> lib_scene_agent:get_user(OwnerId);
                _ -> lib_scene_object_agent:get_object(OwnerId)
            end,
            case OwnerObject=/=[] of
                true -> [init_data(OwnerObject)];
                false -> []
            end;
        %% 客户端选择的是非自己
        1 when User#battle_status.sign /= Sign orelse User#battle_status.id /= Id ->
            [User];
        _ ->
            []
    end,
    Assisters1 = lib_skill_buff:assist_add_attr_buff(LvData#skill_lv.attr, AfterEffAer, Assisters, NowTime, SkillId, SkillLv, EtsScene#ets_scene.broadcast),
    Assisters2 = case SkillStage =< 1 of
        true  -> lib_skill_buff:assist_add_effect_buff(LvData#skill_lv.effect, AfterEffAer, Assisters1, NowTime, SkillId, SkillLv);
        false -> Assisters1
    end,

    F = fun(EUser, [TmpAer, DerList]) ->
            #battle_status{
                id= EId, node = ENode, pid = EPid, sign = ESign,
                battle_attr = #battle_attr{attr_buff_list = AttrBuffList, other_buff_list = OtherBuffList}
            } = EUser,
            Msg = {'buff', AttrBuffList, OtherBuffList},
            %% 更新战斗状态
            case ESign of
                ?BATTLE_SIGN_PLAYER ->
                    case lib_scene_agent:get_user(EId) of
                        #ets_scene_user{} = EtsUser ->
                            lib_scene_agent:put_user(back_data(EUser, EtsUser));
                        _ -> skip
                    end,
                    send_to_node_pid(ENode, EPid, Msg);
                _ ->
                    case lib_scene_object_agent:get_object(EId) of
                        #scene_object{} = Object ->
                            lib_scene_object_agent:put_object(back_data(EUser, Object));
                        _ -> skip
                    end,
                    send_to_node_mon(ENode, EPid, EId, Msg)
            end,
            %% 打包buff列表
            DBuffList = lib_skill_buff:pack_buff(EUser#battle_status.battle_attr#battle_attr.attr_buff_list
                ++EUser#battle_status.battle_attr#battle_attr.other_buff_list, NowTime, []),
            % DBuffList = lib_skill_buff:pack_buff(EUser#battle_status.battle_attr#battle_attr.other_buff_list, NowTime, []),
            TmpAer1 = case EId == Id andalso Sign == ESign of
                true  -> EUser;
                false -> TmpAer
            end,
            [TmpAer1, [[ESign, EId, EUser#battle_status.battle_attr#battle_attr.hp, DBuffList]|DerList] ]
    end,
    [AfAssistAer, BrocastAssisters] = lists:foldl(F, [AfterEffAer, []], Assisters2),

    send_assist_msg(Sign, Id, SkillId, SkillLv, AttObjectId, BrocastAssisters, CopyId, X, Y, EtsScene#ets_scene.broadcast),
    AfAssistAer.

%% 使用技能
do_battle(Aer, BattleExtraArgs, SkillR, MainSkillId, EtsScene) ->
    #battle_status{copy_id = CopyId, x = AerX, y = AerY} = Aer,
    %% 按技能调整参数修正技能效果
    #battle_args{skill_adjust = SkillAdjust} = BattleExtraArgs,
    AdjustParams = #adjust_params{copy_id = CopyId, aer_x = AerX, aer_y = AerY},
    SkillConfig = skill_adjust_api:adjust(AdjustParams, SkillR, SkillAdjust),
    {AerAfBattle, DerList} = do_active_skill(Aer, BattleExtraArgs, SkillConfig, MainSkillId, EtsScene),
    {AerAfBattle, DerList}.

%% 攻击防守方
do_active_skill(Aer, BattleExtraArgs, SkillConfig, MainSkillId, EtsScene) ->
    #battle_args{
        der_list = DerList, now_time = NowTime, att_x = AttX, att_y = AttY
    } = BattleExtraArgs,
    #battle_status{battle_attr = BAAer, att_list = AttList} = Aer,
    #battle_attr{attr_buff_list = AttrBuffListAer, attr = AttrAer } = BAAer,
    %% 正在攻击我的怪物数量
    AttMonList   = [{AttId, Time}||{AttId, Time} <- AttList, NowTime-Time < 3000],
    %% 持续效果
    AfAttrEffBA  = lib_skill_buff:calc_attr_last_effect(
        AttrBuffListAer, NowTime, AttrAer, BAAer, AttrAer, BAAer, []),
    AfOtherEffBA = lib_skill_buff:calc_other_last_effect(
        AfAttrEffBA#battle_attr.other_buff_list, NowTime, MainSkillId,
        SkillConfig#skill.id, AfAttrEffBA#battle_attr.attr,
        AfAttrEffBA, AfAttrEffBA#battle_attr.attr, BAAer, [], ?BATTLE_MOMENT_ATTACK
    ),
    AfAttrEffAer = Aer#battle_status{battle_attr = AfOtherEffBA, att_list = AttMonList},
    %% 筛选可攻击对象
    % CheckPkSafeTime = utime:unixtime(),
    FilterDerList = filter_der_list(DerList, Aer, AttX, AttY, EtsScene, SkillConfig, NowTime),
    {AerAfCalcHurt, DerListAfCalcHurt} = do_core_battle_list(FilterDerList, AfAttrEffAer, SkillConfig, BattleExtraArgs, EtsScene),
    %% 扣除怒气
    AerClearAnger = clear_anger(AerAfCalcHurt, SkillConfig),
    %% 基础属性不能变更
    LastBA = AerClearAnger#battle_status.battle_attr,
    LastAer = AerClearAnger#battle_status{battle_attr = LastBA#battle_attr{attr = BAAer#battle_attr.attr} },
    {LastAer, DerListAfCalcHurt}.

do_core_battle_list(DerList, AerInit, Skill, BattleExtraArgs, EtsScene) ->
    #battle_status{
        battle_attr = BattleAttr,
        skill_minus_cd = SkillMinusCd,
        triggers_passive = TriggersPassive,
        x = AerX, y = AerY} = AerInit,
    %% 在更前面根据客户端请求获取防守方数据列表的时候，已经将主目标排在前面，所以第一个即为主目标
    IsFirstDer = true,
    Acc = #{
        der_list_acc => []
        ,skill_cd_add_acc => []
        ,battle_attr_acc_aer => BattleAttr
        ,skill_minus_cd_acc => SkillMinusCd
        ,triggers_passive_acc => TriggersPassive
        ,aer_x_acc => AerX
        ,aer_y_acc => AerY
    },
    do_core_battle_list_2(DerList, AerInit, IsFirstDer, Skill, BattleExtraArgs, EtsScene, Acc).

do_core_battle_list_2([Der | DerList], AerInit, IsFirstDer, Skill, BattleExtraArgs, EtsScene, Acc = #{
        der_list_acc := DerListAcc
        ,skill_cd_add_acc := SkillCdAddAcc
        ,battle_attr_acc_aer := BattleAttrAcc
        ,skill_minus_cd_acc := SkillMinusCdAcc
        ,triggers_passive_acc := TriggersPassiveAcc
        ,aer_x_acc := AerX
        ,aer_y_acc := AerY
    }) ->
    #battle_status{skill_cd = InitSkillCdAer} = AerInit,
    #battle_attr{
        attr_buff_list = LastAttrBuffList,
        other_buff_list = LastOtherBuffList
    } = BattleAttrAcc,
    {TmpAer, NewDer} = do_core_battle(AerInit, Der, Skill, IsFirstDer, BattleExtraArgs, EtsScene),
    %% 只能继承部分属性变更
    %% 攻击方保持初始参与战斗时的状态与每个防守方计算战斗结果
    #battle_status{
        battle_attr = TmpBattleAttr = #battle_attr{
            attr_buff_list = AttrBuffList,
            other_buff_list = OtherBuffList
        },
        skill_minus_cd = TmpSkillMinusCd,
        triggers_passive = TmpTriggersPassive,
        skill_cd = TmpSkillCd, x = X, y = Y} = TmpAer,
    %% 技能CD更新
    NewSkillCdAddAcc =
    case TmpSkillCd =/= InitSkillCdAer of
        true  ->
            %% 计算出相对原始技能CD列表的更新列表
            SkillCdAddList = calc_skill_cd_update(InitSkillCdAer, TmpSkillCd),
            %% 排重计入累计更新列表中
            calc_skill_cd(SkillCdAddAcc, SkillCdAddList);
        false ->
            SkillCdAddAcc
    end,
    NewDerListAcc = [NewDer | DerListAcc],
    %% 继承BUFF变更
    NewAttrBuffList  = lib_skill_buff:buff_list_merge(AttrBuffList, LastAttrBuffList),
    NewOtherBuffList = lib_skill_buff:buff_list_merge(OtherBuffList, LastOtherBuffList),
    %% 继承其他战斗数据变更
    %% FIXME skill_minus_cd、triggers_passive应该要做数据合并，
    %%       暂时先只继承攻击主目标后的数据
    {NewBattleAttrAccAer, NewSkillMinusCd, NewTriggersPassive, NewAerX, NewAerY} =
    case IsFirstDer of
        true  ->    % 主目标，取完整数据
            {TmpBattleAttr, TmpSkillMinusCd, TmpTriggersPassive, X, Y};
        false ->    % 非主目标，只继承BUFF变更数据
            BattleAttrAccUpdate = BattleAttrAcc#battle_attr{
                attr_buff_list = NewAttrBuffList,
                other_buff_list = NewOtherBuffList
            },
            {BattleAttrAccUpdate, SkillMinusCdAcc, TriggersPassiveAcc, AerX, AerY}
    end,
    NewAcc = Acc#{
        der_list_acc => NewDerListAcc
        ,skill_cd_add_acc => NewSkillCdAddAcc
        ,battle_attr_acc_aer => NewBattleAttrAccAer
        ,skill_minus_cd => NewSkillMinusCd
        ,triggers_passive_acc => NewTriggersPassive
        ,aer_x_acc => NewAerX
        ,aer_y_acc => NewAerY
    },
    do_core_battle_list_2(DerList, AerInit, false, Skill, BattleExtraArgs, EtsScene, NewAcc);
do_core_battle_list_2([],
    AerInit = #battle_status{skill_cd = InitSkillCdAer},
    _IsFirstDer, _Skill, _BattleExtraArgs, _EtsScene,
    #{
        der_list_acc := NewDerListAcc
        ,skill_cd_add_acc := SkillCdAddAcc
        ,battle_attr_acc_aer := BattleAttrAcc
        ,skill_minus_cd_acc := SkillMinusCdAcc
        ,triggers_passive_acc := TriggersPassiveAcc
        ,aer_x_acc := NewAerX
        ,aer_y_acc := NewAerY
    }) ->
    NewSkillCdList = calc_skill_cd(InitSkillCdAer, SkillCdAddAcc),
    AerAcc = AerInit#battle_status{
        skill_cd = NewSkillCdList
        ,battle_attr = BattleAttrAcc
        ,skill_minus_cd = SkillMinusCdAcc
        ,triggers_passive = TriggersPassiveAcc
        ,x = NewAerX
        ,y = NewAerY
    },
    {AerAcc, NewDerListAcc}.

%% 战斗核心计算
do_core_battle(Aer, Der, SkillR, IsFirstDer, BattleExtraArgs, EtsScene) ->
    #battle_args{now_time = NowTime, guard_dun_buff = GuardDunBuff} = BattleExtraArgs,
    #skill{id = SkillId, calc = IsCalc, lv = SkillLv,
        lv_data = #skill_lv{attr = AttrList, effect = EffectList, trigger = Trigger}
    } = SkillR,
    #ets_scene{type = SceneType, broadcast = Broadcast} = EtsScene,

    %% 计算防守方持续buff
    #battle_status{battle_attr = DerBA} = Der,
    AfAtlBuffDerBA = lib_skill_buff:calc_attr_last_effect(DerBA#battle_attr.attr_buff_list, NowTime, DerBA#battle_attr.attr, DerBA, DerBA#battle_attr.attr, DerBA, []),

    AfOthBuffDerBA = lib_skill_buff:calc_other_last_effect(AfAtlBuffDerBA#battle_attr.other_buff_list, NowTime, 0, SkillId,
        AfAtlBuffDerBA#battle_attr.attr, AfAtlBuffDerBA, AfAtlBuffDerBA#battle_attr.attr, AfAtlBuffDerBA, [], ?BATTLE_MOMENT_DEFENCE),
    AfOthBuffDer = Der#battle_status{battle_attr = AfOthBuffDerBA},

    %% 计算伤害类型
    HurtType = lib_battle_hurt_calc:calc_hurt_type(Aer, AfOthBuffDer, IsCalc, NowTime),

    %% 计算攻击玩家被动技能(攻击者攻击前触发)
    AerBfBAPassiveArgs = #battle_passive_args{aer = Aer, der = AfOthBuffDer, trigger_time = ?SKILL_BUFF_TG_TIME_ATTBF,
        hp = Aer#battle_status.battle_attr#battle_attr.hp, hurt_type = HurtType},
    {AttAfPassiveAer, AttAfPassiveDer}
    = lib_skill_buff:calc_aer_passive_skill(Aer, AfOthBuffDer, Aer, Der, IsFirstDer, BattleExtraArgs, Broadcast, AerBfBAPassiveArgs),

    %% 计算防守玩家被动技能(防守者被攻击前触发)(防守者为主动触发方,所以二者位置互换)
    DerBfBAPassiveArgs = #battle_passive_args{aer = Aer, der = AttAfPassiveDer, trigger_time = ?SKILL_BUFF_TG_TIME_DEFBF,
        hp = AttAfPassiveDer#battle_status.battle_attr#battle_attr.hp, hurt_type = HurtType},
    {DefAfPassiveDer, DefAfPassiveAer, _}
    = lib_skill_buff:calc_der_passive_skill(AttAfPassiveDer, AttAfPassiveAer, Der, Aer,
        IsFirstDer, BattleExtraArgs, Broadcast, DerBfBAPassiveArgs),

    %% 主技能效果
    {AfAtAer, AfAtDer}  = lib_skill_buff:calc_active_attr_effect(AttrList, Trigger, DefAfPassiveAer, DefAfPassiveDer,
        DefAfPassiveAer#battle_status.battle_attr#battle_attr.attr_buff_list,
        DefAfPassiveDer#battle_status.battle_attr#battle_attr.attr_buff_list,
        NowTime, SkillId, SkillLv, Aer, Der, Broadcast),
    {AfBuffAer1, AfBuffDer1}  = lib_skill_buff:calc_active_other_effect(EffectList, Trigger, AfAtAer, AfAtDer,
        AfAtAer#battle_status.battle_attr#battle_attr.other_buff_list,
        AfAtDer#battle_status.battle_attr#battle_attr.other_buff_list,
        SkillId, SkillLv, Aer, Der, IsFirstDer, BattleExtraArgs, Broadcast, AerBfBAPassiveArgs),
    % ?INFO("skilleff:~w~nother_buff_list change:~w~n(old:~w => new:~w)", [
    %         AfBuffAer1#battle_status.battle_attr#battle_attr.skill_effect,
    %         AfBuffAer1#battle_status.battle_attr#battle_attr.other_buff_list --
    %         AfAtAer#battle_status.battle_attr#battle_attr.other_buff_list,
    %         AfAtAer#battle_status.battle_attr#battle_attr.other_buff_list,
    %         AfBuffAer1#battle_status.battle_attr#battle_attr.other_buff_list
    %     ]),
    %% 世界BOSS护盾判断
    world_boss_mgr:check_shield_buff(AfAtAer, AfBuffAer1, AfBuffDer1),
    %% 计算伤害
    {Hpb1, Hurt1} = lib_battle_hurt_calc:calc_hurt(AfBuffAer1, AfBuffDer1, SkillR, HurtType, SceneType, GuardDunBuff),

    %% 计算血量护盾
    {NewHpb, Hurt, NewDerBA} = calc_shield_hp(Hpb1, Hurt1, AfBuffDer1#battle_status.battle_attr),
    %% 计算攻击玩家被动技能(攻击方攻击后触发):判断造成的伤害类型
    AerAfBAPassiveArgs = #battle_passive_args{aer = Aer, der = AfOthBuffDer, trigger_time = ?SKILL_BUFF_TG_TIME_ATTAF,
        hp = AfBuffAer1#battle_status.battle_attr#battle_attr.hp, hurt_type = HurtType, hurt = Hurt},
    {AfBuffAer2, AfBuffDer2} = lib_skill_buff:calc_aer_passive_skill(AfBuffAer1, AfBuffDer1#battle_status{battle_attr = NewDerBA},
        Aer, Der, IsFirstDer, BattleExtraArgs, Broadcast, AerAfBAPassiveArgs),
    %% 计算防守玩家被动技能(防守者被攻击后触发)
    %% [Aer, AfOthBuffDer, ?SKILL_BUFF_TG_TIME_DEFAF, NewHpb, HurtType, Hurt]
    DerAfBAPassiveArgs = #battle_passive_args{aer = Aer, der = AfOthBuffDer, trigger_time = ?SKILL_BUFF_TG_TIME_DEFAF, hp = NewHpb, hurt_type = HurtType, hurt = Hurt},
    {AfBuffDer, AfBuffAer, HpLast} = lib_skill_buff:calc_der_passive_skill(AfBuffDer2, AfBuffAer2, Der, Aer, IsFirstDer,
        BattleExtraArgs, Broadcast, DerAfBAPassiveArgs),
    %% 计算反弹伤害
    {AfReBAer, AfReBAerHurt} = calc_rebound_hurt(AfBuffAer, AfBuffDer, Hurt1),
    % ?INFO("skill effect of aer:~naer buff aer2 eff: ~w~nAfBuffAer eff:~w~n, AfReBAer eff:~w", [
    %         AfBuffAer2#battle_status.battle_attr#battle_attr.skill_effect,
    %         AfBuffAer#battle_status.battle_attr#battle_attr.skill_effect,
    %         AfReBAer#battle_status.battle_attr#battle_attr.skill_effect
    %     ]),
    %% 计算吸血
    AfSuBAer = calc_suck_blood(AfReBAer, AfBuffDer, Hurt1),
    %% 处理是否破隐身
    %% AfHideAer = break_steath(Aer, AfSuBAer, SkillR, EtsScene),
    %% 最后统一赋值
    LastAer = AfSuBAer,
    {IsRepelled, RepelDer} = lib_battle_repel:calc_repel(LastAer, AfBuffDer, SkillR),

    %% 攻击方
    LastAerSign = calc_real_sign(LastAer#battle_status.sign),
    #battle_status{
        battle_attr = #battle_attr{anger = AerAnger, anger_lim = AerAngerLim,group = Group}
        ,figure = #figure{flag=Flag,guild_name=AerGName}
    } = LastAer,

    [ReturnAtter0, RetrunSign] = case LastAer#battle_status.skill_owner of
        %% 父类属性
        #skill_owner{id=OwnerId, pid=OwnerPid, node=OwnerNode, guild_id = OwnerGuildId, team_id=OwnerTeamId, sign=OwnerSign} ->
            [ #battle_return_atter{
                    sign = OwnerSign, id = OwnerId, node = OwnerNode, server_id = LastAer#battle_status.server_id, mid = LastAer#battle_status.mid,
                    kind = LastAer#battle_status.kind, pid = OwnerPid, team_id = OwnerTeamId, team_leader_name = LastAer#battle_status.team_leader_name, x = LastAer#battle_status.x,
                    y = LastAer#battle_status.y, hide = LastAer#battle_status.battle_attr#battle_attr.hide,
                    guild_id = OwnerGuildId,guild_name=AerGName,flag=Flag,real_id = LastAer#battle_status.id, real_sign = LastAer#battle_status.sign,
                    real_name = LastAer#battle_status.figure#figure.name, lv = LastAer#battle_status.figure#figure.lv,
                    career = LastAer#battle_status.figure#figure.career
                    ,badge = LastAer#battle_status.badge,group=Group
                    ,anger = AerAnger, anger_lim = AerAngerLim, attr_skill_id = SkillId
                    ,att_time = NowTime, online = LastAer#battle_status.online
                }
                ,OwnerSign
            ];
        _ ->
            [ #battle_return_atter{
                    sign = LastAer#battle_status.sign, id = LastAer#battle_status.id, node = LastAer#battle_status.node,
                    server_id = LastAer#battle_status.server_id, team_leader_name = LastAer#battle_status.team_leader_name,
                    mid = LastAer#battle_status.mid, kind = LastAer#battle_status.kind, pid = LastAer#battle_status.pid,
                    team_id = LastAer#battle_status.team_id, x = LastAer#battle_status.x, y = LastAer#battle_status.y,
                    hide = LastAer#battle_status.battle_attr#battle_attr.hide, guild_id = LastAer#battle_status.guild_id,guild_name=AerGName,flag=Flag,
                    real_id = LastAer#battle_status.id, real_sign = LastAer#battle_status.sign, real_name = LastAer#battle_status.figure#figure.name,
                    lv = LastAer#battle_status.figure#figure.lv, career = LastAer#battle_status.figure#figure.career
                    ,badge = LastAer#battle_status.badge,group=Group
                    ,anger = AerAnger, anger_lim = AerAngerLim, attr_skill_id = SkillId
                    ,att_time = NowTime, online = LastAer#battle_status.online
                }
                ,LastAerSign
            ]
    end,
    ReturnAtter = check_support_return(ReturnAtter0, RepelDer, LastAer),
    %% 有一些怪物，不会死亡，需要正常计算伤害，计算完后，血量不变
    IsMonster = Der#battle_status.sign == ?BATTLE_SIGN_MON orelse Der#battle_status.sign == ?BATTLE_SIGN_DUMMY,
    Hpb = case IsMonster of
        true  ->
            Object  = lib_scene_object_agent:get_object(Der#battle_status.id),
            lib_mon:deal_with_alive_mon_hp(Object, HpLast);
        false ->
            Object = undefined,
            HpLast
    end,
    RepelBA = RepelDer#battle_status.battle_attr,
    LastDer = RepelDer#battle_status{battle_attr=RepelBA#battle_attr{hp=Hpb}},
    
    BattleReturn = #battle_return{hp = Hpb, hurt = Hurt, sign = RetrunSign, atter = ReturnAtter,
        move_x = case LastDer#battle_status.x =/= Der#battle_status.x of
            true  -> LastDer#battle_status.x;
            false -> 0
        end,
        move_y = case LastDer#battle_status.y =/= Der#battle_status.y of
            true  -> LastDer#battle_status.y;
            false -> 0
        end,
        attr_buff_list  = LastDer#battle_status.battle_attr#battle_attr.attr_buff_list,
        other_buff_list = LastDer#battle_status.battle_attr#battle_attr.other_buff_list,
        god_id          = god_api:is_in_god_fighting(LastDer)
        },

    %% 更新防守方数据
    if
        not is_pid(Der#battle_status.pid) -> skip;
        Der#battle_status.sign == ?BATTLE_SIGN_PLAYER -> %% 防守方是玩家
            case Der#battle_status.sign == ?BATTLE_SIGN_PLAYER andalso Hpb == 0 andalso
                (EtsScene#ets_scene.type == ?SCENE_TYPE_NORMAL orelse EtsScene#ets_scene.type == ?SCENE_TYPE_OUTSIDE) of %% 野外非pk场景击杀玩家
                true  ->
                    GodId = god_api:is_in_god_fighting(Der),
                    if
                        RetrunSign == ?BATTLE_SIGN_PLAYER andalso %% 攻击方是人
                        Der#battle_status.battle_attr#battle_attr.pk#pk.pk_value == 0 andalso
                        GodId == 0 -> %% 击杀非红名且不处于唤神召唤的玩家+1
                            send_to_node_pid(
                                Aer#battle_status.node,
                                Aer#battle_status.pid,
                                {'change_pk_value', Der#battle_status.figure#figure.name, 1}
                            );
                        true ->
                            skip
                    end;
                false ->
                    skip
            end,
            BA = LastDer#battle_status.battle_attr,
            if
                Aer#battle_status.sign == ?BATTLE_SIGN_MON -> %% 记录被怪物攻击数量
                    %% 只保留最新的5个
                    NewAttList1 = [{Aer#battle_status.id, NowTime}|
                        lists:keydelete(Aer#battle_status.id, 1, LastDer#battle_status.att_list)],
                    NewAttList = lists:sublist(NewAttList1, 5);
                true ->
                    NewAttList = LastDer#battle_status.att_list
            end,
            %% 更新防守玩家信息
            der_update_scene_info(LastDer#battle_status{att_list = NewAttList, battle_attr=BA#battle_attr{attr=DerBA#battle_attr.attr}},
                BattleReturn, ReturnAtter, NowTime);
        IsMonster ->
            send_to_node_mon(
                Der#battle_status.node,
                Der#battle_status.pid,
                Der#battle_status.id,
                {'battle_info', BattleReturn}
            ),
            BA = LastDer#battle_status.battle_attr,
            NewDer = LastDer#battle_status{battle_attr=BA#battle_attr{attr=DerBA#battle_attr.attr}},
            lib_scene_object_agent:put_object(back_data(NewDer, Object));
        true ->
            skip
    end,

    %% 处理攻击方数据
    [AerRetrunAtter, AerRetrunSign] = case Der#battle_status.skill_owner of
        %% 父类属性
        #skill_owner{id=DerOwnerId, pid=DerOwnerPid, node=DerOwnerNode, guild_id = DerOwnerGuildId, team_id = DerOwnerTeamId, sign = DerOwnerSign} ->
            [ #battle_return_atter{
                    sign = DerOwnerSign, id = DerOwnerId, node = DerOwnerNode, server_id = Der#battle_status.server_id, mid = Der#battle_status.mid,
                    kind = Der#battle_status.kind, pid = DerOwnerPid, team_id = DerOwnerTeamId, x = Der#battle_status.x,
                    y = Der#battle_status.y, hide = Der#battle_status.battle_attr#battle_attr.hide, guild_id = DerOwnerGuildId,
                    real_id = Der#battle_status.id, real_sign = Der#battle_status.sign, real_name = Der#battle_status.figure#figure.name,
                    lv = Der#battle_status.figure#figure.lv, career = Der#battle_status.figure#figure.career, online = Der#battle_status.online},
                DerOwnerSign];
        _ ->
            [ #battle_return_atter{
                    sign = Der#battle_status.sign, id = Der#battle_status.id, node = Der#battle_status.node,
                    server_id = Der#battle_status.server_id,
                    mid = Der#battle_status.mid, kind = Der#battle_status.kind, pid = Der#battle_status.pid,
                    team_id = Der#battle_status.team_id, x = Der#battle_status.x, y = Der#battle_status.y,
                    hide = Der#battle_status.battle_attr#battle_attr.hide, guild_id = Der#battle_status.guild_id,
                    real_id = Der#battle_status.id, real_sign = Der#battle_status.sign, real_name = Der#battle_status.figure#figure.name,
                    lv = Der#battle_status.figure#figure.lv, career = Der#battle_status.figure#figure.career, online = Der#battle_status.online},
                Der#battle_status.sign]
    end,
    AerBattleReturn = #battle_return{ hurt = AfReBAerHurt, sign = AerRetrunSign, atter = AerRetrunAtter },
    if
        not is_pid(Aer#battle_status.pid) -> skip;
        AfReBAerHurt =< 0 -> skip;
        Aer#battle_status.sign == ?BATTLE_SIGN_MON ->
            send_to_node_mon(
                Aer#battle_status.node,
                Aer#battle_status.pid,
                Aer#battle_status.id,
                {'rebound_battle_info', AerBattleReturn}
            );
        true -> skip
    end,
    %% 只发特殊效果buff
    LastDList = case Der#battle_status.sign of
        ?BATTLE_SIGN_FAKE -> [];
        _ ->
            AfBuffBA = LastDer#battle_status.battle_attr,
            DBuffList = lib_skill_buff:pack_buff(AfBuffBA#battle_attr.attr_buff_list ++ AfBuffBA#battle_attr.other_buff_list, NowTime, []),
            GodHp = god_api:get_fighting_god_hp(LastDer, Hpb),
            %% 式神变身 防守方恢复玩家血量
            LastDefHp = god_api:get_fighting_role_hp(LastDer, Hpb),
            [Der#battle_status.sign, Der#battle_status.id, LastDefHp, 0, 0,
                Hurt, HurtType, LastDer#battle_status.x, LastDer#battle_status.y,
                IsRepelled,
                AfBuffBA#battle_attr.skill_effect#skill_effect.interrupt, DBuffList, GodHp]
    end,
    {LastAer, LastDList}.

calc_real_sign(?BATTLE_SIGN_PARTNER) -> ?BATTLE_SIGN_PLAYER;
calc_real_sign(?BATTLE_SIGN_PET)     -> ?BATTLE_SIGN_PLAYER;
calc_real_sign(?BATTLE_SIGN_WEAPON)  -> ?BATTLE_SIGN_PLAYER;
calc_real_sign(?BATTLE_SIGN_CHILD)   -> ?BATTLE_SIGN_PLAYER;
calc_real_sign(Sign) -> Sign.

%% 计算血量护盾
calc_shield_hp(Hpb, Hurt, DerBA) ->
    #battle_attr{shield_hp = ShieldHp} = DerBA,
    if
        Hurt =< 0 orelse ShieldHp =< 0 -> {Hpb, Hurt, DerBA};
        true ->
            NewHurt = max(0, Hurt-ShieldHp),
            NewShieldHp = max(0, ShieldHp-Hurt),
            NewHpb = if
                ShieldHp >= Hurt -> Hpb + Hurt;
                true -> Hpb + ShieldHp
            end,
            if
                NewShieldHp > 0 ->
                    {NewHpb, NewHurt, DerBA#battle_attr{shield_hp = NewShieldHp}};
                true ->
                    NewOtBuff = lib_skill_buff:buff_delete_id(?SPBUFF_SHIELD, DerBA#battle_attr.other_buff_list),
                    {NewHpb, NewHurt, DerBA#battle_attr{shield_hp = NewShieldHp, other_buff_list = NewOtBuff}}
            end
    end.

%% 计算反弹伤害
%% @return {Aer, ReboundHurt}
%% calc_rebound_hurt(#battle_status{kind=?MON_KIND_IMMUNE}=Aer, _, _) -> {Aer, 0};
calc_rebound_hurt(Aer, Der, Hurt) ->
    #battle_status{battle_attr=#battle_attr{hp=Hp}=BA} = Aer,
    #battle_status{battle_attr = DerBA} = Der,
    #battle_attr{skill_effect = SkillEffD} = DerBA,
    ReboundHurt = case SkillEffD#skill_effect.rebound of
        {Int, Float} -> util:ceil(Hurt * Float + Int);
        _ -> 0
    end,
    {NewHp, HurtToAer} =
    case Hp > ReboundHurt of
        true ->
            {Hp-ReboundHurt, ReboundHurt};
        false ->
            {1, Hp - 1}
    end,
    NewAer = Aer#battle_status{battle_attr=BA#battle_attr{hp=NewHp}},
    {NewAer, HurtToAer}.

%% 计算吸血伤害
calc_suck_blood(Aer, #battle_status{is_main = 1, battle_attr = BADer}, Hurt) ->
    #battle_status{battle_attr = BAAer} = Aer,
    #battle_attr{
        hp = Hp, hp_lim = HpLim,
        skill_effect = #skill_effect{suck_blood = EffSuckBlood},
        attr = #attr{
            suck_blood = SuckBloodAer,
            suck_blood_add = SuckBloodAddAer
        }
    } = BAAer,
    #battle_attr{
        attr = #attr{
            suck_blood_minus = SuckBloodMinusDer
        }
    } = BADer,
    %% 攻方技能效果部分的吸血影响因子
    {SuckBloodIntAer, SuckBloodFloatAer} =
    case EffSuckBlood of
        {_, _} -> EffSuckBlood;
        _      -> {0, 0}
    end,
    %% 基础吸血量
    SuckBloodBaseHp = Hurt * (SuckBloodAer / ?RATIO_MAX + SuckBloodFloatAer) + SuckBloodIntAer,
    %% 计算吸血加成+吸血抵抗影响
    %% （技能那边暂时无对应投放的类型，所以直接取人物属性参与运算）
    AddAndMinusFactor = max(
        1 + (SuckBloodAddAer - SuckBloodMinusDer) / ?RATIO_MAX,
        0
    ),
    SuckBloodHp = max(
        round(SuckBloodBaseHp * AddAndMinusFactor),
        0
    ),
    % ?INFO("skill effect SuckBlood:~w", [{SuckBloodIntAer, SuckBloodFloatAer}]),
    % ?INFO("~ts,伤害:~w,吸血基础:~w,吸血量:~w,吸血属性:~w,吸血加成:~w,吸血减免:~w", [
    %         lib_battle_hurt_calc:battler_base(Aer), Hurt,
    %         SuckBloodBaseHp, SuckBloodHp,
    %         SuckBloodAer, SuckBloodAddAer, SuckBloodMinusDer
    %     ]),
    case SuckBloodHp > 0 of
        true  ->
            NewHp = min(Hp + SuckBloodHp, HpLim),
            Aer#battle_status{battle_attr = BAAer#battle_attr{hp = NewHp}};
        false ->
            Aer
    end;
calc_suck_blood(Aer, _Der, _Hurt) ->
    Aer.

%% 根据出手技能类型，判断是否清除攻击方怒气值
clear_anger(Aer, #skill{type = ?SKILL_TYPE_ACTIVE, is_anger = 1}) ->
    BattleAttr = Aer#battle_status.battle_attr,
    Aer#battle_status{battle_attr = BattleAttr#battle_attr{anger = 0}};
clear_anger(Aer, _SkillConfig) ->
    Aer.

%% 防守方数据更新(玩家)
der_update_scene_info(Der, BattleReturn, _RetrunAtter, NowTime) ->
    %% 防守方信息
    #battle_status{id = DefId, node = DefNode, pid = DefPid, att_list = AttList, battle_attr = BA} = Der,

    %% 防守方需更新的信息
    #battle_return{hp = Hp, move_x = MoveX, move_y = MoveY, god_id = GodId} = BattleReturn,
    User = lib_scene_agent:get_user(DefId),
    Msg = case Hp > 0 of
        true ->
            {'battle_info', BattleReturn};
        false when GodId > 0 -> % 式神死了而已
            {'battle_info', BattleReturn};
        false ->
            {'battle_info_die', BattleReturn}
    end,

    send_to_node_pid(DefNode, DefPid, Msg),

    Ref1 = lib_battle:begin_resume_timer(User#ets_scene_user.hp_resume_ref, ?BATTLE_SIGN_PLAYER,
        DefId, BA, NowTime, BA#battle_attr.hp_resume_time),

    {NewGodFighting, GodBA} = god_api:scene_after_battle_defend(User, Der, BA#battle_attr{hp = Hp}, NowTime),

    NewUser = User#ets_scene_user{
        x = case MoveX == 0 of
            true -> User#ets_scene_user.x;
            false -> MoveX
        end,
        y = case MoveY == 0 of
            true -> User#ets_scene_user.y;
            false -> MoveY
        end,
        battle_attr = GodBA#battle_attr{attr = User#ets_scene_user.battle_attr#battle_attr.attr}, %% 基础属性不能改
        att_list = AttList,
        skill_cd = Der#battle_status.skill_cd,
        skill_combo = Der#battle_status.skill_combo,
        shaking_skill = Der#battle_status.shaking_skill,
        hp_resume_ref = Ref1,
        god_fighting = NewGodFighting},
    %% 打断采集怪物
    %% NewUser1 = lib_battle:interrupt_collect(NewUser),
    LastUser = offline_hosting_battle:check_stop_battle(Hp =:= 0 andalso GodId =:= 0, NewUser),
    lib_scene_agent:put_user(LastUser),
    ok.

%% 发送消息
send_to_node_pid(Node, Pid, Msg) ->
    if
        Node =:= none andalso Pid =:= none -> skip; %% 打空的时候,两个参数都是none
        Node =:= none -> Pid ! Msg;
        true -> rpc:cast(Node, erlang, send, [Pid, Msg])
    end.
%% 向节点怪物进程发送针对特定怪物的消息
send_to_node_mon(Node, Pid, MonId, Msg) ->
    if
        Node =:= none andalso Pid =:= none -> skip; %% 打空的时候,两个参数都是none
        Node =:= none -> mon_mgr:send(Pid, MonId, Msg);
        true -> rpc:cast(Node, mon_mgr, send, [Pid, MonId, Msg])
    end.

%% 获取boss疲劳值
get_boss_tired(SceneType, Arr)->
    case data_scene:get(Arr#ets_scene_user.scene) of
        #ets_scene{type = SceneType} ->
            if SceneType == ?SCENE_TYPE_REWARD_BOSS ->
                    Arr#ets_scene_user.boss_tired;
                SceneType == ?SCENE_TYPE_DESERTED_BOSS ->
                    Arr#ets_scene_user.deserted_boss_tired;
                true ->
                    0
            end;
        _ -> 0
    end.

%% 检查是不是都玩家
is_have_def_player([])-> false;
is_have_def_player([[Sign|_]|_Defs]) when Sign == ?BATTLE_SIGN_PLAYER -> true;
is_have_def_player([_|Defs]) -> is_have_def_player(Defs).

%% 协助信息返回判断
check_support_return(ReturnAtter, #battle_status{sign = DerSign, mid = BossId}, Aer) ->
    case Aer of
        #battle_status{support_content = {deserted_boss, {_, BossId}}} ->
            ReturnAtter#battle_return_atter{
                support_id = Aer#battle_status.support_id
                ,support_name = Aer#battle_status.support_name
                ,support_team = Aer#battle_status.support_team
                ,support_team_name = Aer#battle_status.support_team_name
            };
        #battle_status{support_content = {deserted_boss, {_, _}}} ->
            deserted_boss_mgr:supporter_attack_check(DerSign, Aer),
            ReturnAtter;
        _ ->
            ReturnAtter#battle_return_atter{
                support_id = Aer#battle_status.support_id
                ,support_name = Aer#battle_status.support_name
                ,support_team = Aer#battle_status.support_team
                ,support_team_name = Aer#battle_status.support_team_name
            }
    end.

%% 过滤防守方列表
filter_der_list(DerList, Aer, AttX, AttY, Scene, SkillConfig, NowTimeMS) ->
    SkillDistance = lib_battle:calc_skill_distance(SkillConfig),
    %% pk_protect_time目前使用的是秒级数据，精度较差
    NowTimeSec = NowTimeMS div 1000,
    FilterDerList = [
        Der ||
        Der <- DerList,
        filter_der(Der, Aer, Scene, SkillDistance, SkillConfig, NowTimeSec)
    ],
    case FilterDerList of
        [_|_] -> FilterDerList;
        _ ->
            FakeDer = #battle_status{x = AttX, y = AttY, sign = ?BATTLE_SIGN_FAKE,
                figure = #figure{},
                battle_attr = #battle_attr{
                    attr = #attr{},
                    skill_effect = #skill_effect{}
                }
            },
            [FakeDer]
    end.

filter_der(
    Der = #battle_status{id = DerId, sign = DerSign},
    Aer = #battle_status{id = AerId, sign = AerSign},
    Scene, SkillDistance, SkillConfig, NowTime) ->
    if
        {AerId, AerSign} =:= {DerId, DerSign} -> false;
        true ->
            DistanceRet = lib_battle:check_distance(SkillConfig, SkillDistance, Aer, Der),
            PkRet       = lib_battle:check_pk_and_safe(Aer, Der, Scene, NowTime),
            if DistanceRet == true andalso PkRet == true -> true;
                true -> false
            end
    end.

%% 根据技能新旧CD状态，计算需要更新的数据，用于同步到战斗者状态数据
calc_skill_cd_update(OldSkillCdList, NewSkillCdList) ->
    NewSkillCdList -- OldSkillCdList.

%% 根据SkillCdUpdate，计算最新状态的SkillCdList
calc_skill_cd(OldSkillCdList, [SkillCdInfo = {SkillId, _SkillTime} | SkillCdUpdate]) ->
    NewSkillCdList = lists:keystore(SkillId, 1, OldSkillCdList, SkillCdInfo),
    calc_skill_cd(NewSkillCdList, SkillCdUpdate);
calc_skill_cd(SkillCdList, [_ | SkillCdUpdate]) ->
    calc_skill_cd(SkillCdList, SkillCdUpdate);
calc_skill_cd(SkillCdList, []) ->
    SkillCdList.

