%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 战斗BUFF管理
%%% @end
%%% -------------------------------------------------------
-module(lib_skill_buff).
-export([
        pack_buff/3
        ,set_combo_buff/4
        ,del_combo_buff/2
        ,add_buff/3
        ,add_buff/4
        ,clean_buff/2
        ,clean_buff/3
        ,clean_mon_buff/3
        ,is_can_move/3
        ,is_can_attack/4
        ,is_prohibed_jump/1
        ,is_silence/2
        ,is_unresume/2
        ,is_buff_att/2
        ,own_buff/3
        ,get_minus_cd/2
        ,assist_clean_timeout_buff/3
        ,assist_add_attr_buff/7
        ,assist_add_effect_buff/6
        ,calc_attr_last_effect/7
        ,calc_other_last_effect/10
        ,calc_active_attr_effect/12
        ,calc_active_other_effect/14
        ,handle_steath_state/9
        % ,get_foot_point/3
        ,calc_speed/6
        ,calc_speed_helper/3
        ,calc_hp_resume/3
        ,calc_hp_be_resume/3
        ,remove_type_buff/3
        ,calc_aer_passive_skill/8
        ,calc_der_passive_skill/8
        ,check_have_effect_list/2
        ,check_passive_skill_cd/3
        ,skill_buff_to_goods_buff/2
        ,calc_sprint_pos/5
        ,merge_skill_eff/1
        ,check_revive_trigger/2
    ]).
%% Buff列表基本操作接口
-export([
        to_buff/9
        ,buff_find/3
        ,buff_find_id/2
        ,buff_store/4
        ,buff_delete/3
        ,buff_delete_id/2
        ,buff_sum_params/2
        ,buff_list_merge/2
    ]).
-compile({inline, [to_buff/9]}).

-include("common.hrl").
-include("attr.hrl").
-include("skill.hrl").
-include("battle_obj_select.hrl").
-include("battle.hrl").
-include("scene.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("def_fun.hrl").

-define(DELAY_DEL_BUFF_TIME, 0).

%% 打包buff发送给客户端显示
pack_buff([ #battle_buff{
            key = {K, SkillId},
            skill_lv = SkillLv,
            stack = Stack,
            param_int = Int, param_float = Float,
            time_end = TimeEnd,
            effect_id = EffectId
        } | TailBuffs ], NowTime, BuffBinList) ->
    LeftTime =  TimeEnd - NowTime,
    case LeftTime > 0 of
        false -> pack_buff(TailBuffs, NowTime, BuffBinList);
        true  ->
            Float1 = round(Float * 1000),
            BuffBin = <<K:16, EffectId:16, SkillId:32, SkillLv:8, Stack:8, Int:32/signed, Float1:32/signed, TimeEnd:64>>,
            NewBuffBinList = [BuffBin | BuffBinList],
            pack_buff(TailBuffs, NowTime, NewBuffBinList)
    end;
pack_buff([], _NowTime, BuffBinList) ->
    L = length(BuffBinList),
    Data = list_to_binary(BuffBinList),
    <<L:16, Data/binary>>;
pack_buff([_ | TailBuffs], NowTime, BuffBinList) ->
    pack_buff(TailBuffs, NowTime, BuffBinList).

%% 设置连击buff
set_combo_buff(OtherBuffList, MainSkillId, MainSkillLv, TimeEnd) ->
    Buff = to_buff(?SPBUFF_COMBO, MainSkillId, MainSkillLv, 0, 0, 0, TimeEnd, 0, 0),
    [Buff | buff_delete(?SPBUFF_COMBO, MainSkillId, OtherBuffList)].

del_combo_buff(OtherBuffList, MainSkillId) ->
    buff_delete(?SPBUFF_COMBO, MainSkillId, OtherBuffList).

del_all_combo_buff(BuffList, _AccList) ->
    buff_delete_id(?SPBUFF_COMBO, BuffList).

%% 添加buff
add_buff(#player_status{id=Id, scene=SceneId, scene_pool_id=ScenePoolId} = Player, SkillId, SkillLv) ->
    mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_skill_buff, add_buff, [Id, SkillId, SkillLv]),
    {ok, Player}.

add_buff(EtsScene, Id, SkillId, SkillLv) when is_integer(Id) ->
    case lib_scene_agent:get_user(Id) of
        #ets_scene_user{} = User ->
            add_buff(EtsScene, User, SkillId, SkillLv);
        _ -> ignore
    end;

add_buff(EtsScene, User, SkillId, SkillLv) ->
    NewUser =
    case mod_battle:assist(User, User, server, SkillId, SkillLv, EtsScene) of
        {false, _ErrCode, _Aer} -> _Aer;
        {true, Aer, _MainSkillId} -> Aer
    end,
    case is_record(NewUser, ets_scene_user) of
        true  ->
            lib_scene_agent:put_user(NewUser);
        false ->
            ignore
    end.

%% add_buff(BuffList, K, BuffType, SkillId, SkillLv, Stack, Int, Float, LastTime, EffectId, AttrId, NowTime) ->
%%     {_, BuffList1} = calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, AttrId, SkillId, SkillLv, NowTime, BuffList),
%%     BuffList1.

%% 清理buff
clean_buff(#player_status{id=Id, scene=SceneId, scene_pool_id=ScenePoolId, battle_attr = BA} = Player, SkillId) ->
    %%先清PS身上battle_attr
    {_, AttrBuffList} = clean_buff_helper(BA#battle_attr.attr_buff_list, SkillId, [], []),
    {_, OtherBuffList} = clean_buff_helper(BA#battle_attr.other_buff_list, SkillId, [], []),
    NewBA = BA#battle_attr{attr_buff_list=AttrBuffList, other_buff_list=OtherBuffList},
    mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_skill_buff, clean_buff, [Id, SkillId]),
    {ok, Player#player_status{battle_attr = NewBA}}.

clean_buff(EtsScene, Id, SkillId) when is_integer(Id) ->
    case lib_scene_agent:get_user(Id) of
        #ets_scene_user{} = User ->
            User1 = clean_buff(EtsScene, User, SkillId),
            lib_scene_agent:put_user(User1),
            ok;
        _ -> skip
    end;
clean_buff(EtsScene, #ets_scene_user{battle_attr=BA}=User, SkillId) ->
    {Result1, AttrBuffList} = clean_buff_helper(BA#battle_attr.attr_buff_list, SkillId, [], []),
    {Result2, OtherBuffList} = clean_buff_helper(BA#battle_attr.other_buff_list, SkillId, [], Result1),
    calc_buff_when_clean(Result2, User#ets_scene_user{battle_attr=BA#battle_attr{attr_buff_list=AttrBuffList, other_buff_list=OtherBuffList}}, EtsScene).

%% 清理buff递归函数
clean_buff_helper([#battle_buff{key = {K, SkillId}} | T], SkillId, Buffs, BuffIds) ->
    clean_buff_helper(T, SkillId, Buffs, [K | BuffIds]);
clean_buff_helper([Buff | T], SkillId, Buffs, BuffIds) ->
    clean_buff_helper(T, SkillId, [Buff | Buffs], BuffIds);
clean_buff_helper([], _SkillId, Buffs, BuffIds) ->
    {BuffIds, Buffs}.

calc_buff_when_clean([K|T], #ets_scene_user{id=Id, copy_id=CopyId, x=X, y=Y, battle_attr=BA} = User, EtsScene) ->
    User1 = case K of
        ?SPEED ->
            NowTime = utime:longunixtime(),
            [Float, Int] = lib_skill_buff:calc_speed_helper(BA#battle_attr.attr_buff_list, NowTime, [1.0, 0]),
            Speed = round(BA#battle_attr.speed * Float+Int),
            {ok, BinData} = pt_120:write(12082, [?BATTLE_SIGN_PLAYER, Id, Speed]),
            lib_battle:send_to_scene(CopyId, X, Y, EtsScene#ets_scene.broadcast, BinData),
            User#ets_scene_user{battle_attr=BA#battle_attr{speed=Speed}};
        _ ->
            User
    end,
    calc_buff_when_clean(T, User1, EtsScene);
calc_buff_when_clean([], User, _) -> User.


%% ---------------------------------------------------------------------------
%% @doc 怪清除buff
-spec clean_mon_buff(EtsScene, ObjectId, SkillIdList) -> ok when
    ObjectId    :: integer(),
    SkillIdList :: [SkillId :: integer()],
    EtsScene    :: #ets_scene{}.
%% ---------------------------------------------------------------------------
clean_mon_buff(EtsScene, ObjectId, OldSkillIdList) ->
    case lib_scene_object_agent:get_object(ObjectId) of
        MonUser = #scene_object{battle_attr = BA, aid = MonPid} ->
            %% 对AI配置参数做检测，避免策划配置错导致崩溃报错
            SkillIdList =
            if
                is_list(OldSkillIdList) -> OldSkillIdList;
                is_integer(OldSkillIdList) -> [OldSkillIdList];
                true -> []
            end,
            #battle_attr{
                attr_buff_list = AttrBuffL, other_buff_list = OtherBuffL, hp = Hp
            } = BA,
            {Result1, AttrBuffList} = clean_buff_list_helper(AttrBuffL, SkillIdList, []),
            {_R2, OtherBuffList} = clean_buff_list_helper(OtherBuffL, SkillIdList, Result1),
            %% 消除场景中怪物buff也要同步到怪物进程身上
            NewBA = BA#battle_attr{
                attr_buff_list = AttrBuffList, other_buff_list = OtherBuffList
            },
            MonUser2 = MonUser#scene_object{battle_attr = NewBA},
            lib_scene_object_agent:put_object(MonUser2),
            #ets_scene{broadcast = Broadcast} = EtsScene,
            #scene_object{sign = Sign, copy_id = CopyId, x = X, y = Y} = MonUser,
            %% 发协议通知前端
            DBuffList = lib_skill_buff:pack_buff(OtherBuffList, utime:unixtime(), []),
            AssistList = [[Sign, ObjectId, Hp, DBuffList]],
            case SkillIdList of
                [SkillId | _] ->
                    {ok, BinData} = pt_200:write(
                        20006, [Sign, ObjectId, SkillId, 1, 0, AssistList]
                    ),
                    lib_battle:send_to_scene(CopyId, X, Y, Broadcast, BinData);
                _ ->
                    ok
            end,
            mon_mgr:send(MonPid, ObjectId, {set_mon_buff, AttrBuffList, OtherBuffList}),
            ok;
        _->
            ok
    end.

clean_buff_list_helper(BuffList, [], Result) ->
    {Result, BuffList};
clean_buff_list_helper(BuffList, [SkillId | T], Result) ->
    {Result2, BuffList2} = clean_buff_helper(BuffList, SkillId, [], Result),
    clean_buff_list_helper(BuffList2, T, Result2).


%% 判断是否有不能移动的buff
is_can_move(_, 0, _) -> false;
is_can_move([#battle_buff{key = {K, _}, time_end = T} | Tail], Speed, NowTime) when T + 100 > NowTime ->
    if
        K == ?SPBUFF_DIZZY orelse K == ?SPBUFF_IMMOBILIZE ->
            {false, T+100-NowTime};
        true -> is_can_move(Tail, Speed, NowTime)
    end;
is_can_move([_|Tail], Speed, NowTime) -> is_can_move(Tail, Speed, NowTime);
is_can_move([], _, _) -> true.

%% 判断是否有不能攻击的buff
is_can_attack(IsNormal, Sign, [#battle_buff{key = {K, _}, time_end = T} | Tail], NowTime) when T+100 > NowTime ->
    if
        K == ?SPBUFF_FEAR andalso Sign == ?BATTLE_SIGN_PLAYER  -> {false, K, T+100-NowTime};
        K == ?SPBUFF_DIZZY -> {false, K, T+100-NowTime};
        K == ?SPBUFF_SILENCE andalso IsNormal==false -> {false, K, T+100-NowTime};
        K == ?SPBUFF_NO_BATTLE -> {false, K, T+100-NowTime};
        true -> is_can_attack(IsNormal, Sign, Tail, NowTime)
    end;
is_can_attack(IsNormal, Sign, [_|Tail], NowTime) -> is_can_attack(IsNormal, Sign, Tail, NowTime);
is_can_attack(_IsNormal, _Sign, [], _) -> true.

%% 是否被禁止跳跃
is_prohibed_jump(#battle_attr{} = BattleAttr) ->
    BuffId = ?SPBUFF_PROHIBED_JUMP,
    case buff_find_id(BuffId, BattleAttr#battle_attr.other_buff_list) of
        [_BuffInfo | _] = ProhibedBuffList ->
            NowTime = utime:unixtime(ms),
            own_buff(ProhibedBuffList, BuffId, NowTime);
        _ ->
            false
    end;
is_prohibed_jump(#ets_scene_user{battle_attr = BattleAttr}) ->
    is_prohibed_jump(BattleAttr);
is_prohibed_jump(_) ->
    false.

%% 是否沉默
is_silence(BuffList, NowTime) ->
    own_buff(BuffList, ?SPBUFF_SILENCE, NowTime).

%% 回血免疫
is_unresume(BuffList, NowTime) ->
    own_buff(BuffList, ?SPBUFF_UNRESUME, NowTime).

%% 是否拥有某类BUFF
own_buff([#battle_buff{key = {BuffId, _}, time_end = TimeEnd} | _Tail], BuffId, NowTime) when TimeEnd >= NowTime ->
    true;
own_buff([_|Tail], BuffId, NowTime) ->
    own_buff(Tail, BuffId, NowTime);
own_buff([], _BuffId, _NowTime) ->
    false.

%% 减CD BUFF参数值
get_minus_cd([], _) -> 0;
get_minus_cd([#battle_buff{key = {?SPBUFF_MINUS_CD, _}, time_end = TimeEnd, param_float = Float} |_Tail], NowTime) when TimeEnd+100 > NowTime ->
    Float;
get_minus_cd([_|Tail], NowTime) -> get_minus_cd(Tail, NowTime).

%% 是否有攻击buff
%% is_buff_att([{?SPBUFF_ATT_TIMES, _, _, _, _, Int, Float, T, _, AttrId}|_], NowTime)
%%   when T >= NowTime -> {true, AttrId, trunc(Float*1000), Int};
%% is_buff_att([_|Tail], NowTime) -> is_buff_att(Tail, NowTime);
is_buff_att(_, _) -> false.

assist_clean_timeout_buff([#battle_buff{time_end = TimeEnd} | TB], NowTime, Buff) when TimeEnd >= NowTime + ?DELAY_DEL_BUFF_TIME ->
    assist_clean_timeout_buff(TB, NowTime, Buff);
assist_clean_timeout_buff([E|TB], NowTime, Buff) ->
    assist_clean_timeout_buff(TB, NowTime, [E|Buff]);
assist_clean_timeout_buff([], _NowTime, Buff) -> Buff.

%% 辅助技能buff处理
%% assist_add_attr_buff([{K, BuffType, PerMil, _AffectedParties, Int, Float, LastTime, Stack, EffectId}|T], BuffList, User, OldUser, NowTime, SkillId, SkillLv) ->
%%     BuffList1 = case (PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil) andalso LastTime>0 of
%%        true  ->
%%            {_, OB} = calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, K, SkillId, SkillLv, NowTime, BuffList),
%%            OB;
%%        false ->
%%            BuffList
%%    end,
%%    assist_add_attr_buff(T, BuffList1, User, OldUser, NowTime, SkillId, SkillLv);
%% assist_add_attr_buff([], BuffList, #battle_status{battle_attr=BA}=User, _OldUser, _NowTime, _SkillId, _SkillLv) ->
%%    User#battle_status{battle_attr=BA#battle_attr{attr_buff_list=BuffList}}.

assist_add_attr_buff([{_K, _BuffType, _PerMil, _AffectedParties, _Int, _Float, _LastTime, _Stack, _EffectId} = H|T], User, Assisters,
    NowTime, SkillId, SkillLv, Broadcast) ->
    Assisters1 = [calc_assist_attr_effect(E, H, NowTime, SkillId, SkillLv, Broadcast) || E <- Assisters],
    assist_add_attr_buff(T, User, Assisters1, NowTime, SkillId, SkillLv, Broadcast);
assist_add_attr_buff([], _User, Assisters, _, _, _, _) -> Assisters.

calc_assist_attr_effect(User, {K, BuffType, PerMil, _AffectedParties, Int, Float, LastTime, Stack, EffectId}, NowTime, SkillId, SkillLv, Broadcast) ->
    case (PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil) andalso LastTime>0 of
        true  ->
            #battle_status{battle_attr=BA=#battle_attr{attr_buff_list=AttrBuffL}} = User,
            {IsSwap, NewAttrBuffL} = calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, K, SkillId, SkillLv, NowTime, AttrBuffL),
            User1 = case K of
                ?SPEED when IsSwap == true ->
                    calc_speed(User, User, NewAttrBuffL, LastTime, NowTime, Broadcast);
                _ -> User
            end,
            case IsSwap of
                true  -> User1#battle_status{battle_attr=BA#battle_attr{attr_buff_list=NewAttrBuffL}};
                false -> User1
            end;
        false ->
            User
    end.

%% 特殊效果
assist_add_effect_buff([H|T], User, Assisters, NowTime, SkillId, SkillLv) ->
    Assisters1 = [calc_assist_other_effect(User, E, H, NowTime, SkillId, SkillLv) || E <- Assisters],
    assist_add_effect_buff(T, User, Assisters1, NowTime, SkillId, SkillLv);
assist_add_effect_buff([], _User, Assisters, _, _, _) -> Assisters.

calc_assist_other_effect(User, Assister, {K, BuffType, PerMil, _AffectedParties, Int, Float, AttrId, LastTime, _Num, EffectId}=H, NowTime, SkillId, SkillLv) ->
    case (PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil) of
        true when LastTime > 0 ->
            #battle_status{battle_attr = BA =#battle_attr{other_buff_list=OtherBuffL}} = Assister,
            {IsSwap, OtherBuffL1} = calc_buff_swap(K, BuffType, Int, Float, LastTime, 0, EffectId, AttrId, SkillId, SkillLv, NowTime, OtherBuffL),
            case IsSwap of
                true  -> assist_calc_sp_effect(H, User, Assister#battle_status{battle_attr=BA#battle_attr{other_buff_list=OtherBuffL1}}, NowTime, 0, SkillId);
                false -> Assister
            end;
        true ->
            NewAssister = case K of
                ?SPBUFF_CLEAN_BUFF ->
                    #battle_status{battle_attr = BA} = Assister,
                    #battle_attr{skill_effect = _SkillEffect, attr_buff_list = AttrBuffList,  other_buff_list = OtherBuffList} = BA,
                    NewAttrBuffList = remove_type_buff(AttrBuffList, BuffType, []),
                    NewOtherBuffList = remove_type_buff(OtherBuffList, BuffType, []),
                    NewBA = BA#battle_attr{attr_buff_list = NewAttrBuffList,  other_buff_list = NewOtherBuffList},
                    Assister#battle_status{battle_attr=NewBA};
                _ ->
                    Assister
            end,
            assist_calc_sp_effect(H, User, NewAssister, NowTime, 0, SkillId);
        false ->
            Assister
    end.

%% assist_add_effect_buff([{K, BuffType, PerMil, _AffectedParties, Int, Float, AttrId, GapTime, Num, EffectId}=H|T], BuffList, User, OldUser, NowTime, SkillId, SkillLv) ->
%%     case (PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil) andalso GapTime>0 of
%%        true  ->
%%            LastTime = GapTime*max(1,Num),
%%            case calc_buff_swap(K, BuffType, Int, Float, LastTime, 0, EffectId, AttrId, SkillId, SkillLv, NowTime, BuffList) of
%%                {true, OB} ->
%%                    User1 = assist_calc_sp_effect(H, User, OldUser, NowTime, 0),
%%                    assist_add_effect_buff(T, OB, User1, OldUser, NowTime, SkillId, SkillLv);
%%                {false, _} ->
%%                    assist_add_effect_buff(T, BuffList, User, OldUser, NowTime, SkillId, SkillLv)
%%            end;
%%        false ->
%%            assist_add_effect_buff(T, BuffList, User, OldUser, NowTime, SkillId, SkillLv)
%%    end;
%% assist_add_effect_buff([_|T], BuffList, User, OldUser, NowTime, SkillId, SkillLv)  -> assist_add_effect_buff(T, BuffList, User, OldUser, NowTime, SkillId, SkillLv);
%% assist_add_effect_buff([], BuffList, #battle_status{battle_attr=BA}=User, _OldUser, _NowTime, _SkillId, _SkillLv) ->
%%    User#battle_status{battle_attr=BA#battle_attr{other_buff_list=BuffList}}.

assist_calc_sp_effect({BuffId, _BuffType, _PerMil, _AffectedParties, Int, Float, AttrId, LastTime, Num, _EffectId}, User, Assister, NowTime, _Broadcast, _SkillId) ->
    case BuffId of
        ?SPBUFF_RESUME -> %% 辅助技能回血都是定时器回血
            Personality = User#battle_status.personality,
            #battle_status{sign = Sign, id = Id, battle_attr=#battle_attr{hp_lim = HpLim, attr=Attr}} = Assister,
            Value =  min(HpLim, value_cate(Int, Float, AttrId, 0, lib_player_attr:get_value_by_id(AttrId, Attr))),
            Value1 = min(HpLim, lib_skill_buff:calc_hp_resume(User, Value, NowTime)),
            GapTime = LastTime div max(1, Num),
            lib_scene_timer:add_timer(NowTime, 0, Sign, Id, ?SPBUFF_RESUME, {GapTime, Value1, Personality, max(1, Num)}),
            Assister;
        ?SPBUFF_SHIELD -> %% 血量护盾
            #battle_status{battle_attr = BA} = Assister,
            Value = round(Int + Float*lib_player_attr:get_value_by_id(AttrId, BA#battle_attr.attr)),
            Assister#battle_status{battle_attr = BA#battle_attr{shield_hp = Value}};
        _ ->
            Assister
    end.

%% buff叠加/替换规则
calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, AttrId, SkillId, SkillLv, NowTime, BuffList)
when BuffType == ?SKILL_NORMAL_BUFF orelse BuffType == ?SKILL_BUFF orelse BuffType == ?SKILL_DEBUFF ->
    %% 以BuffId + SkillId唯一的逻辑关系进行BUFF更替
    case buff_find(K, SkillId, BuffList) of
        #battle_buff{
            skill_lv = SkillLv0, stack = Stack0,
            param_int = Int0, param_float = Float0
        } ->    %% 普通叠加数替换
            if
                SkillLv0 < SkillLv -> %% 高等级buff，替换buff技能id
                    NewBuff = to_buff(K, SkillId, SkillLv, min(1, Stack), Int, Float, NowTime + LastTime, EffectId, AttrId),
                    NewBuffList = buff_store(K, SkillId, BuffList, NewBuff),
                    {true, NewBuffList};
                SkillLv0 > SkillLv -> %% 低等级buff，替换buff技能id(用于活动buff降级效果)
                    NewBuff = to_buff(K, SkillId, SkillLv, min(1, Stack), Int, Float, NowTime + LastTime, EffectId, AttrId),
                    NewBuffList = buff_store(K, SkillId, BuffList, NewBuff),
                    {true, NewBuffList};
                SkillLv0 == SkillLv andalso Stack > 0 andalso Stack0 < Stack  -> %% 有叠加，但是未达到最大叠加数目
                    NewInt = Int0 + Int,
                    NewFloat = trunc( (Float0 + Float) * ?RATIO_MAX ) / ?RATIO_MAX,
                    NewBuff = to_buff(K, SkillId, SkillLv0, Stack0 + 1, NewInt, NewFloat, NowTime + LastTime, EffectId, AttrId),
                    NewBuffList = buff_store(K, SkillId, BuffList, NewBuff),
                    {true, NewBuffList};
                SkillLv0 == SkillLv -> %% 等级相同，直接替换持续时间
                    NewBuff = to_buff(K, SkillId, SkillLv0, Stack0, Int0, Float0, NowTime + LastTime, EffectId, AttrId),
                    NewBuffList = buff_store(K, SkillId, BuffList, NewBuff),
                    {true, NewBuffList};
                true ->
                    {false, BuffList}
            end;
        _False ->
            NewBuff = to_buff(K, SkillId, SkillLv, min(1, Stack), Int, Float, NowTime + LastTime, EffectId, AttrId),
            {true, [NewBuff | BuffList]}
    end;
calc_buff_swap(K, BuffType, Int, Float, LastTime, _Stack, EffectId, AttrId, SkillId, SkillLv, NowTime, BuffList) ->
    case buff_find_id(K, BuffList) of
        [] ->
            NewBuff = to_buff(K, SkillId, SkillLv, 0, Int, Float, NowTime + LastTime, EffectId, AttrId),
            {true, [NewBuff | BuffList]};
        [ Buff = #battle_buff{time_end = TimeEnd0} ] when
        BuffType =:= ?SKILL_CTRL_BUFF orelse BuffType =:= ?SKILL_SP_BUFF ->
            %% 控制buff，不区分SkillId，按BuffId进行替换
            NewTimeEnd = NowTime + LastTime,
            if
                NewTimeEnd > TimeEnd0 ->
                    NewBuff = to_buff(K, SkillId, SkillLv, 0, Int, Float, NewTimeEnd, EffectId, AttrId),
                    NewBuffList = [NewBuff | lists:delete(Buff, BuffList)],
                    {true, NewBuffList};
                true ->
                    {false, BuffList}
            end;
        _Others ->
            {false, BuffList}
    end.

%% 计算属性buff
calc_attr_last_effect([ #battle_buff{key = {K, _}, param_int = Int, param_float = Float, time_end = T} = Buff | Tail],
    Time, Attr, BA, OldAttr, OldBA, BuffAccList) ->
    if
        T > Time ->
            %% 总属性=现在属性+基础属性的比列
            Value = value_cate(Int, Float, K, lib_player_attr:get_value_by_id(K, Attr), lib_player_attr:get_value_by_id(K, OldAttr)),
            Attr1 = lib_player_attr:set_value_by_id(K, Value, Attr),
            calc_attr_last_effect(Tail, Time, Attr1, BA, OldAttr, OldBA, [Buff | BuffAccList]);
        T + ?DELAY_DEL_BUFF_TIME > Time ->
            calc_attr_last_effect(Tail, Time, Attr, BA, OldAttr, OldBA, [Buff | BuffAccList]);
        true ->
            calc_attr_last_effect(Tail, Time, Attr, BA, OldAttr, OldBA, BuffAccList)
    end;
calc_attr_last_effect([], _Time, Attr, BA, _OldAttr, _OldBA, BuffAccList) ->
    BA#battle_attr{attr = Attr, attr_buff_list = BuffAccList}.

%% 计算特效buff
calc_other_last_effect([Buff | Tail], Time, MainSkillId, NowSkillId, Attr, BA, OldAttr, OldBA, BuffAccList, Moment) ->
    #battle_buff{key = {K, SkillId},
        skill_lv = SkillLv,
        stack = Stack,
        param_int = Int, param_float = Float,
        time_end = T,
        effect_id = EffectId, attr_id = AttrId
    } = Buff,
    if
        T > Time ->
            SkillEff = BA#battle_attr.skill_effect,
            SkillEff1 = case K of
                ?SPBUFF_SUPERARMOR ->
                    SkillEff#skill_effect{superarmor = 1};
                ?SPBUFF_IMMUNE ->
                    SkillEff#skill_effect{immue = 1};
                ?SPBUFF_UNSPEED ->
                    SkillEff#skill_effect{un_speed = 1};
                ?SPBUFF_REBOUND ->
                    SkillEff#skill_effect{rebound = {Int, Float}};
                ?SPBUFF_SUCK_BLOOD ->
                    SkillEff#skill_effect{suck_blood = {Int, Float}};
                ?SPBUFF_UNRESUME ->
                    SkillEff#skill_effect{unresume = 1};
                ?SPBUFF_SHIELD_COUNT ->  % 次数盾（当前剩余次数）
                    SkillEff#skill_effect{shield_count = Int};
                ?SPBUFF_NO_BATTLE ->
                    SkillEff#skill_effect{no_battle = 1};
                _ ->
                    SkillEff
            end,
            %% 如果有次数盾，需要减少一层，并检测是否需要清理，受击时生效
            NewBuffAccList = case K of
                ?SPBUFF_SHIELD_COUNT when Int =< 1 andalso Moment =:= ?BATTLE_MOMENT_DEFENCE ->
                    BuffAccList;
                ?SPBUFF_SHIELD_COUNT when Moment =:= ?BATTLE_MOMENT_DEFENCE ->
                    %% 存在次数盾，减少一层
                    NewBuff = to_buff(K, SkillId, SkillLv, Stack, Int - 1, Float, T, EffectId, AttrId),
                    [NewBuff | BuffAccList];
                _ ->
                    [Buff | BuffAccList]
            end,
            NewBA = case K of
                ?SPBUFF_BASE_ATTR_ADD_RATIO -> %% 全属性提升
                    handle_full_attr_add(BA, Float);
                _ -> BA
            end,
            calc_other_last_effect(Tail, Time, MainSkillId, NowSkillId, Attr, NewBA#battle_attr{skill_effect=SkillEff1}, OldAttr, OldBA, NewBuffAccList, Moment);
        T + ?DELAY_DEL_BUFF_TIME > Time ->
            calc_other_last_effect(Tail, Time, MainSkillId, NowSkillId, Attr, BA, OldAttr, OldBA, [Buff|BuffAccList], Moment);

        %% --------------------------------------
        %% 时间未超情况下
        K =:= ?SPBUFF_SHIELD -> %% 血量护盾时间都做替换
            calc_other_last_effect(Tail, Time, MainSkillId, NowSkillId, Attr, BA#battle_attr{shield_hp = 0}, OldAttr, OldBA, BuffAccList, Moment);
        true ->
            calc_other_last_effect(Tail, Time, MainSkillId, NowSkillId, Attr, BA, OldAttr, OldBA, BuffAccList, Moment)
        %% --------------------------------------
    end;
calc_other_last_effect([], _Time, _MainSkillId, _NowSkillId, _Attr, BA, _OldAttr, _OldBA, BuffAccList, _Moment) ->
    BA#battle_attr{other_buff_list = BuffAccList}.

%% 计算属性
%% Sum是现有属性；Base是面板属性
value_cate(Int, Float, AttrId, Sum, Base) ->
    if
        AttrId == ?HURT_ADD_RATIO orelse AttrId == ?HURT_DEL_RATIO orelse
        AttrId == ?HIT_RATIO orelse AttrId == ?DODGE_RATIO  orelse
        AttrId == ?CRIT_RATIO orelse AttrId == ?UNCRIT_RATIO ->
            FloatValue = Base * Float,
            round(Sum + FloatValue + Int);
        true ->
            FloatValue = Base * Float,
            max(1, round(Sum + FloatValue + Int))
    end.

%% 持续时间和叠加层数
calc_active_attr_effect([{K, BuffType, PerMil, AffectedParties, Int, Float, LastTime, Stack, EffectId}|T],
    Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast) ->
    case PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil of
        true ->
            #ret_get_affects{user = User, user_buff = UserBuffL, user_init = OldUser} =
            battle_obj_select:get_affected_parties(
                #args_get_affects{
                    affect = AffectedParties,
                    aer = Aer, der = Der, aer_init = OldAer, der_init = OldDer,
                    aer_buff = AerBuffList, der_buff = DerBuffList
                }
            ),
            case check_active_buff_effect_trigger(K, User, Trigger) of
                false ->
                    calc_active_attr_effect(T, Trigger, Aer, Der,  AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast);
                true ->
                    {User2, UserBuffL2} = case K of
                        ?SPEED ->
                            case calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, K, SkillId, SkillLv, NowTime, UserBuffL) of
                                {true, BuffList} ->
                                    {calc_speed(User, OldUser, OldUser#battle_status.battle_attr#battle_attr.other_buff_list++BuffList, LastTime, NowTime, Broadcast), BuffList};
                                {false, _} ->
                                    {User, UserBuffL}
                            end;
                        _ ->
                            UserBA = User#battle_status.battle_attr,
                            Value = value_cate(Int, Float, K, lib_player_attr:get_value_by_id(K, UserBA#battle_attr.attr),
                                lib_player_attr:get_value_by_id(K, OldUser#battle_status.battle_attr#battle_attr.attr)),
                            Attr1 = lib_player_attr:set_value_by_id(K, Value, UserBA#battle_attr.attr),
                            User1 = User#battle_status{battle_attr=UserBA#battle_attr{attr=Attr1}},
                            case LastTime =< 0 of
                                true  -> {User1, UserBuffL};
                                false -> {_, TmpBuffL} = calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, K, SkillId, SkillLv, NowTime, UserBuffL),
                                    {User1, TmpBuffL}
                            end
                    end,
                    ArgsSetAffects = #args_set_affects{
                        affect = AffectedParties,
                        aer = Aer, aer_buff = AerBuffList, der = Der, der_buff = DerBuffList,
                        affect_user = User2, affect_user_buff = UserBuffL2
                    },
                    #ret_set_affects{
                        aer = Aer2, aer_buff = AerBuffL2,
                        der = Der2, der_buff = DerBuffL2
                    } = battle_obj_select:set_affected_parties(ArgsSetAffects),
                    calc_active_attr_effect(T, Trigger, Aer2, Der2, AerBuffL2, DerBuffL2, NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast)
            end;
        false ->
            calc_active_attr_effect(T, Trigger, Aer, Der,  AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast)
    end;
calc_active_attr_effect([_|T], Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast) ->
    calc_active_attr_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast);
calc_active_attr_effect([], _Trigger, Aer, Der, AerBuffList, DerBuffList, _NowTime, _SkillId, _SkillLv, _OldAer, _OldDer, _) ->
    AerBA = Aer#battle_status.battle_attr, DerBA = Der#battle_status.battle_attr,
    { Aer#battle_status{battle_attr=AerBA#battle_attr{attr_buff_list=AerBuffList}}, Der#battle_status{battle_attr=DerBA#battle_attr{attr_buff_list=DerBuffList}} }.

%% 计算主动技能的特殊效果
calc_active_other_effect([{K, BuffType, PerMil, AffectedParties, Int, Float, AttrId, LastTime, _Num, EffectId}=H|T],
    Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs) ->
    #ret_get_affects{user = User, user_buff = UserBuffL, user_init = OldUser} =
    battle_obj_select:get_affected_parties(
        #args_get_affects{
            affect = AffectedParties,
            aer = Aer, der = Der, aer_init = OldAer, der_init = OldDer,
            aer_buff = AerBuffList, der_buff = DerBuffList
        }
    ),
    UserBA = User#battle_status.battle_attr,
    UserSE = UserBA#battle_attr.skill_effect,
    %% 是否免疫
    IsImmune = is_immune(User, UserSE, K, BuffType),
    case not IsImmune andalso (PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil) of
        true when LastTime > 0 ->
            {Int1, Float1}
            = if AttrId > 0 ->
                    {value_cate(Int, Float, AttrId, 0, lib_player_attr:get_value_by_id(AttrId, OldUser#battle_status.battle_attr#battle_attr.attr)), 0};
                true ->
                    {Int, Float}
            end,
            #battle_args{now_time = NowTime} = BattleExtraArgs,
            case calc_buff_swap(K, BuffType, Int1, Float1, LastTime, 0, EffectId, AttrId, SkillId, SkillLv, NowTime, UserBuffL) of
                {true, BuffList} ->
                    {CalcComboBuffUser, CalcComboBuffL} = case BuffType of
                        ?SKILL_CTRL_BUFF -> %% 控制技能
                            {User#battle_status{skill_combo=[], shaking_skill=0,
                                    battle_attr = UserBA#battle_attr{skill_effect = UserSE#skill_effect{interrupt=User#battle_status.shaking_skill}}},
                                del_all_combo_buff(BuffList, [])};
                        _ ->
                            {User, BuffList}
                    end,
                    ArgsSetAffects = #args_set_affects{
                        affect = AffectedParties,
                        aer = Aer, aer_buff = AerBuffList, der = Der, der_buff = DerBuffList,
                        affect_user = CalcComboBuffUser, affect_user_buff = CalcComboBuffL
                    },
                    #ret_set_affects{
                        aer = Aer1, aer_buff = AerBuffL1,
                        der = Der1, der_buff = DerBuffL1
                    } = battle_obj_select:set_affected_parties(ArgsSetAffects),
                    BattlePassiveArgs = BAPassiveArgs#battle_passive_args{skill_id = SkillId },
                    #ret_set_affects{
                        aer = Aer2, aer_buff = AerBuffL2,
                        der = Der2, der_buff = DerBuffL2
                    } = calc_sp_effect(H, Aer1, AerBuffL1, Der1, DerBuffL1, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BattlePassiveArgs),
                    calc_active_other_effect(T, Trigger, Aer2, Der2, AerBuffL2, DerBuffL2, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
                {false, _} ->
                    calc_active_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)
            end;
        true ->
            BattlePassiveArgs = BAPassiveArgs#battle_passive_args{skill_id = SkillId },
            #ret_set_affects{
                aer = Aer2, aer_buff = AerBuffL2,
                der = Der2, der_buff = DerBuffL2
            } = calc_sp_effect(H, Aer, AerBuffList, Der, DerBuffList, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BattlePassiveArgs),
            calc_active_other_effect(T, Trigger, Aer2, Der2, AerBuffL2, DerBuffL2, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
        false ->
            calc_active_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)
    end;
calc_active_other_effect([_|T], Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs) ->
    calc_active_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
calc_active_other_effect([], _Trigger, #battle_status{battle_attr=AerBA}=Aer, #battle_status{battle_attr=DerBA}=Der,
    AerBuffList, DerBuffList, _SkillId, _SkillLv, _OldAer, _OldDer, _IsFirstDer, _BattleExtraArgs, _Broadcast, _BAPassiveArgs) ->
    { Aer#battle_status{battle_attr = AerBA#battle_attr{other_buff_list=AerBuffList}},
        Der#battle_status{battle_attr = DerBA#battle_attr{other_buff_list=DerBuffList}} }.

%% 计算特殊效果
%% 一般不处理BUFF列表中数据的添加和替换（calc_buff_swap管理），但是可能出现删除
calc_sp_effect({BuffId, BuffType, _PerMil, AffectedParties, Int, Float, AttrId, LastTime, Num, _EffectId},
    Aer, AerBuffL, Der, DerBuffL, OldAer, OldDer,
    _IsFirstDer, #battle_args{now_time = NowTime}, Broadcast, BAPassiveArgs) ->
    #ret_get_affects{
        user = User, user_buff = UserBuffL, user_init = OldUser,
        oppo_user = _OppoUser, oppo_user_buff = _OppoUserBuffL
    } = battle_obj_select:get_affected_parties(
        #args_get_affects{
            affect = AffectedParties,
            aer = Aer, der = Der, aer_init = OldAer, der_init = OldDer,
            aer_buff = AerBuffL, der_buff = DerBuffL
        }
    ),
    case BuffId of
        ?SPBUFF_SUPERARMOR -> %% 霸体
            #battle_status{battle_attr=#battle_attr{skill_effect=SE}=UserBA} = User,
            NewUser = User#battle_status{battle_attr = UserBA#battle_attr{skill_effect = SE#skill_effect{superarmor=1}}},
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_IMMUNE -> %% 无敌
            #battle_status{battle_attr=#battle_attr{skill_effect=SE}=UserBA} = User,
            NewUser = User#battle_status{battle_attr=UserBA#battle_attr{skill_effect=SE#skill_effect{immue=1}}},
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_REBOUND -> %% 反弹
            NewUser =
            if
                BAPassiveArgs#battle_passive_args.skill_id == ?SP_SKILL_KILLING andalso User#battle_status.team_id > 0 -> %% 特殊的队伍反弹伤害
                    #battle_status{team_skill = TeamSkillId, team_skill_num = TeamSkillNum, battle_attr = UserBA} = User,
                    #battle_attr{skill_effect = SE} = UserBA,
                    if
                        TeamSkillId == 0 -> BuffId = min(TeamSkillNum+1, 3);
                        true -> BuffId = max(TeamSkillNum, 1)
                    end,
                    NewUserBA = UserBA#battle_attr{skill_effect=SE#skill_effect{rebound={Int*BuffId, Float*BuffId}}},
                    User#battle_status{battle_attr=NewUserBA};
                true -> %% 个人反弹
                    #battle_status{battle_attr=#battle_attr{skill_effect=SE}=UserBA} = User,
                    User#battle_status{battle_attr=UserBA#battle_attr{skill_effect=SE#skill_effect{rebound={Int, Float}}}}
            end,
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_SUCK_BLOOD -> %% 吸血
            #battle_status{battle_attr=#battle_attr{skill_effect=SE}=UserBA} = User,
            NewUser = User#battle_status{battle_attr=UserBA#battle_attr{skill_effect=SE#skill_effect{suck_blood={Int, Float}}}},
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_SLOW -> %% 减速
            #battle_status{battle_attr=#battle_attr{skill_effect=SE}} = User,
            NewUser =
            if
                SE#skill_effect.un_speed == 1 ->
                    User;
                true ->
                    calc_speed(User, OldUser, OldUser#battle_status.battle_attr#battle_attr.attr_buff_list ++ UserBuffL, LastTime, NowTime, Broadcast)
            end,
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_KILL -> %% 斩杀
            #battle_status{battle_attr=#battle_attr{skill_effect=SE}=UserBA} = User,
            BossMiss = is_boss_miss_debuff(User),
            NewUser =
            if
                BossMiss -> User;
                true ->
                    User#battle_status{battle_attr=UserBA#battle_attr{skill_effect=SE#skill_effect{killed=1}}}
            end,
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_RESUME when LastTime > 0 -> %% 持续回血的方式
            #battle_status{battle_attr=#battle_attr{skill_effect=SE}} = User,
            if
                SE#skill_effect.unresume == 1 -> skip;
                true ->
                    GapTime = LastTime div max(1, Num),
                    #battle_status{id = Id, sign = Sign, battle_attr=#battle_attr{ hp_lim=HpLim}} = User,
                    %% 宝宝被动技能的触发方需要修正
                    Sign1 = case Sign =:= ?BATTLE_SIGN_CHILD of
                                true -> mod_battle:calc_real_sign(Sign);
                                _ -> Sign
                            end,
                    AddHp = min(HpLim, value_cate(Int, Float, AttrId, 0, lib_player_attr:get_value_by_id(AttrId, OldUser#battle_status.battle_attr#battle_attr.attr))),
                    AddHp1 = min(HpLim, lib_skill_buff:calc_hp_resume(User, AddHp, NowTime)),
                    lib_scene_timer:add_timer(NowTime, 0, Sign1, Id, ?SPBUFF_RESUME, {GapTime, AddHp1, 0, max(1, Num)})
            end,
            %% 这里其实用户数据啥也没改，主要是为了检测是否触发定时器
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = User, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_RESUME -> %% 攻击方,防守方:瞬间回血
            #battle_status{id=Id, sign = _Sign, copy_id=CopyId, x=X, y=Y,
                battle_attr=#battle_attr{hp = Hp, hp_lim=HpLim, skill_effect=SE} = UserBA
            } = User,
            NewUser =
            if
                SE#skill_effect.unresume == 1 -> User;
                true ->
                    AddHp = min(HpLim, value_cate(Int, Float, AttrId, Hp, lib_player_attr:get_value_by_id(AttrId, OldUser#battle_status.battle_attr#battle_attr.attr))),
                    AddHp1 = min(HpLim, lib_skill_buff:calc_hp_resume(User, AddHp, NowTime)),
                    User1 = User#battle_status{battle_attr=UserBA#battle_attr{hp=AddHp1}},
                    {ok, BinData} = pt_120:write(12009, [Id, AddHp1, HpLim]),
                    lib_battle:send_to_scene(CopyId, X, Y, Broadcast, BinData),
                    %% 暂时勿删，未来策划可能重新启用此类触发条件类型
                    % %% 触发类型管理：回血触发被动技能
                    % %% 由于此处缺少Scene数据，所以需要借助定时器回调，可考虑优化为使用Scene数据，则不需要产生定时器
                    % lib_scene_timer:add_timer(NowTime, 0, Sign, Id, ?SKILL_BUFF_TRIGGER_HPRESUME, max(1, Num)),
                    User1
            end,
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_SHIELD -> %% 血量护盾
            Value = round(Int + Float*lib_player_attr:get_value_by_id(AttrId, OldUser#battle_status.battle_attr#battle_attr.attr)),
            #battle_status{battle_attr = UserBA} = User,
            NewUser = User#battle_status{battle_attr = UserBA#battle_attr{shield_hp = Value}},
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_FIRING -> %% 灼烧
            #battle_status{id=Id, sign=Sign, battle_attr = UserBA} = User,
            #battle_attr{fire_ref = FireRefs} = UserBA,
            BossMiss = is_boss_miss_debuff(User),
            NewUser =
            if
                BossMiss ->
                    User;
                length(FireRefs) >= 3 ->
                    User;
                true ->
                    #battle_passive_args{aer = Atter, hurt = Hurt} = BAPassiveArgs,
                    GapTime = LastTime div max(1, Num),
                    #battle_status{id = AtterId, sign = AttSign, figure = #figure{name = AttName}} = Atter,
                    FireHurt = round(Hurt * Float + Int),
                    FireRef = lib_scene_timer:add_timer(NowTime, 1800, Sign, Id, ?SPBUFF_FIRING, {GapTime, FireHurt,  max(1, Num), {AtterId, AttSign, AttName}}),
                    NewFireRefs = [FireRef|FireRefs],
                    User#battle_status{battle_attr=UserBA#battle_attr{fire_ref = NewFireRefs}}
            end,
            battle_obj_select:set_affected_parties(
                #args_set_affects{
                    affect = AffectedParties,
                    aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                    affect_user = NewUser, affect_user_buff = UserBuffL
                }
            );

        ?SPBUFF_BLEED -> %% 流血
            #battle_status{id = Id, sign = Sign, battle_attr = UserBA} = User,
            #battle_attr{bleed_ref = _BleedRefs} = UserBA,
            #battle_passive_args{aer = Atter} = BAPassiveArgs,
            GodUn = god_api:is_un_control(User#battle_status.god_fighting),
            %% 流血 对boss怪物无效
            BossMiss = is_boss_miss_debuff(User),
            NewUser = if
                BossMiss -> User;
                GodUn -> User;
                true ->
                    %% 流血属性修改为取防守者的属性值
                    %% 面板基础属性
                    %% 这里AttrId特殊，0标识基数取当前血量，1标识基数取血上限
                    Base = if AttrId == 0 ->
                            OldUser#battle_status.battle_attr#battle_attr.hp;
                        true ->
                            OldUser#battle_status.battle_attr#battle_attr.hp_lim
                    end,
                    Bleed = value_cate(Int, Float, 0, 0, Base),
                    GapTime = LastTime div max(1, Num),
                    #battle_status{id = AtterId, sign = AttSign, figure = #figure{name = AttName}} = Atter,
                    _BleedRef = lib_scene_timer:add_timer(NowTime, 1800, Sign, Id, ?SPBUFF_BLEED,
                        {GapTime, Bleed, max(1, Num), {AtterId, AttSign, AttName}}),
                    %% NewBleedRefs = [BleedRef|BleedRefs],
                    %% NOTE 忍者是把Ref收集起来，后面清理，需占用更多内存，但便于检查关联
                    %%   而这里不做记录，以此方式来避免泄漏
                    %%   这个关系并不是非常大，可以考虑是否移植过来
                    NewBleedRefs = [],
                    User#battle_status{battle_attr=UserBA#battle_attr{bleed_ref = NewBleedRefs}}
            end,
            ArgsSetAffects = #args_set_affects{
                affect = AffectedParties,
                aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                affect_user = NewUser, affect_user_buff = UserBuffL
            },
            battle_obj_select:set_affected_parties(ArgsSetAffects);

        ?SPBUFF_UNDIZZY ->  %% 眩晕免疫
            %% TODO 包括眩晕也需要移植过来
            %% TODO 这里逻辑移植了忍者的逻辑，但是记忆中仍要处理其他地方，后面再补齐
            #battle_status{battle_attr = UserBA} = User,
            #battle_attr{skill_effect = SkillEffect} = UserBA,
            NewSkillEffect = SkillEffect#skill_effect{current_un_dizzy = 1},
            NewUserBuffL = [Buff || Buff = #battle_buff{key = {K, _}} <- UserBuffL, K =/= ?SPBUFF_DIZZY],
            NewUser = User#battle_status{battle_attr = UserBA#battle_attr{skill_effect = NewSkillEffect}},
            ArgsSetAffects = #args_set_affects{
                affect = AffectedParties,
                aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                affect_user = NewUser, affect_user_buff = NewUserBuffL
            },
            battle_obj_select:set_affected_parties(ArgsSetAffects);

        ?SPBUFF_CLEAN_BUFF -> %% 清楚负面控制buff
            NewUserBuffL = remove_type_buff(UserBuffL, BuffType, []),
            ArgsSetAffects = #args_set_affects{
                affect = AffectedParties,
                aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                affect_user = User, affect_user_buff = NewUserBuffL
            },
            battle_obj_select:set_affected_parties(ArgsSetAffects);

        ?SPBUFF_MINUS_CD -> %% 减CD(MinusSkillId大于0减具体技能CD、等于0表示减除当前的其它技能cd)
            %% TODO 这个需要移植忍者的过来，要改的稍多，暂时不理会
            NowSkillId = BAPassiveArgs#battle_passive_args.skill_id,
            MinusSkillId = AttrId,
            MinusTime = Int,
            #battle_status{skill_minus_cd = SkillMinusCd, skill_cd = SkillCd} = User,
            if
                MinusSkillId > 0 ->
                    NewSkillMinusCd = [{Id, CD}||{Id, CD}<-SkillMinusCd, Id=/=MinusSkillId],
                    LastSkillMinusCd = [{MinusSkillId, MinusTime} | NewSkillMinusCd];
                true ->
                    LastSkillMinusCd = [{Id, MinusTime}||{Id, _CD}<-SkillCd, Id=/=NowSkillId]
            end,
            NewUser = User#battle_status{skill_minus_cd = LastSkillMinusCd},
            ArgsSetAffects = #args_set_affects{
                affect = AffectedParties,
                aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                affect_user = NewUser, affect_user_buff = UserBuffL
            },
            battle_obj_select:set_affected_parties(ArgsSetAffects);

        ?SPBUFF_BASE_ATTR_ADD_RATIO -> %% 全属性提升
            BattleAttr = User#battle_status.battle_attr,
            NewBattleAttr = handle_full_attr_add(BattleAttr, Float),
            NewUser = User#battle_status{battle_attr = NewBattleAttr},
            ArgsSetAffects = #args_set_affects{
                affect = AffectedParties,
                aer = Aer, aer_buff = AerBuffL, der = Der, der_buff = DerBuffL,
                affect_user = NewUser, affect_user_buff = UserBuffL
            },
            battle_obj_select:set_affected_parties(ArgsSetAffects);

        _ ->
            battle_obj_select:set_affected_parties_noaction(
                #args_set_affects{
                    aer = Aer, der = Der,
                    aer_buff = AerBuffL, der_buff = DerBuffL
                }
            )
    end.

%% 某些BOSS可以免疫某些持续性伤害BUFF
is_boss_miss_debuff(User) ->
    #battle_status{sign=Sign, kind = Kind, boss = Boss} = User,
    Sign =:= ?BATTLE_SIGN_MON andalso
        (Kind =:= ?MON_KIND_NORMAL orelse Kind =:= ?MON_KIND_CONVOY) andalso
        (Boss =/= ?MON_NORMAL andalso Boss =/= ?MON_NORMAL_OUSIDE andalso Boss =/= ?MON_ELITE).

%% 处理全属性提升
handle_full_attr_add(BattleAttr, Float) ->
    Attr = BattleAttr#battle_attr.attr,
    AttrList = attr_conv:to_attr_tuple_list(Attr),
    BaseAttrList = lib_player_attr:get_list_base_attr_kv(AttrList),
    NewBaseAttrList = [{AttrId, round((1+Float)*AttrValue)} ||{AttrId, AttrValue}<-BaseAttrList],
    lib_player_attr:set_battle_attr(BattleAttr, NewBaseAttrList).

%% 处理隐身状态
handle_steath_state(Sign, Id, CopyId, X, Y, Broadcast, _NowTime, _LastTime, AddorDel) ->
    {ok, BinData} = pt_120:write(12070, [Sign, Id, AddorDel]),
    lib_battle:send_to_scene(CopyId, X, Y, Broadcast, BinData),
    case AddorDel of
        %% 1 -> lib_scene_timer:add_timer(NowTime, LastTime, Sign, Id, ?SPBUFF_STEALTH, 0);
        _ -> skip
    end.

% %% 求P点在直线OT上经过T点的垂直相交的直线上的垂足
% get_foot_point({OX, OY}, {TX, TY}, {PX, PY}) ->
%     case OY == TY of
%         true  -> {TX, PY};
%         false ->
%             K  = -(TX - OX) / (TY - OY), %% 垂直方向的直线方程斜率
%             B  =  TY - K * TX,
%             K2 = math:pow(K,2),
%             {round((PX+K*PY-B*K)/(K2+1)), round((K*PX+PY*K2+B)/(K2+1))}
%     end;
% get_foot_point(AttAngle, {TX, TY}, {PX, PY}) when is_integer(AttAngle) ->
%     case AttAngle==0 orelse AttAngle==180  of
%         true  -> {TX, PY};
%         false ->
%             K = -1/math:tan(umath:degree_to_radian(AttAngle)), %% 垂直方向的直线方程斜率
%             B  =  TY - K * TX,
%             K2 = math:pow(K,2),
%             {round((PX+K*PY-B*K)/(K2+1)), round((K*PX+PY*K2+B)/(K2+1))}
%     end.

%% 计算速度
calc_speed(User, OldUser, BuffList, LastTime, NowTime, Broadcast) ->
    #battle_status{id=Id, sign=Sign, copy_id=CopyId, x=X, y=Y, battle_attr=BA} = User,
    BaseSpeed = OldUser#battle_status.battle_attr#battle_attr.attr#attr.speed,
    [Float, Int] = calc_speed_helper(BuffList, NowTime, [1.0, 0]),
    Speed = max(0, round(BaseSpeed*Float+Int)),
    lib_scene_timer:add_timer(NowTime+500, LastTime, Sign, Id, ?SPEED, []),
    case Speed /= BA#battle_attr.speed of
        true ->
            {ok, BinData} = pt_120:write(12082, [Sign, Id, Speed]),
            lib_battle:send_to_scene(CopyId, X, Y, Broadcast, BinData);
        false -> skip
    end,
    User#battle_status{battle_attr=BA#battle_attr{speed=Speed}}.

calc_speed_helper([#battle_buff{key = {?SPEED, _},
            param_int = Int, param_float = Float, time_end = TimeEnd
        } | T], NowTime, [F, I]) when TimeEnd > NowTime ->
    calc_speed_helper(T, NowTime, [F*max(0, 1+Float), I+Int]);
calc_speed_helper([#battle_buff{key = {?SPBUFF_SLOW, _},
            param_int = Int, param_float = Float, time_end = TimeEnd
        }|T], NowTime, [F, I]) when TimeEnd > NowTime ->
    calc_speed_helper(T, NowTime, [F*max(0, 1+Float), I+Int]);
calc_speed_helper([_|T], NowTime, [F, I]) ->
    calc_speed_helper(T, NowTime, [F, I]);
calc_speed_helper([], _, Result) -> Result.

%% 计算属性
%% calc_att_speed(User, OldUser, BuffList, LastTime, NowTime, Broadcast) ->
%%     #battle_status{id=Id, sign=Sign, copy_id=CopyId, x=X, y=Y, battle_attr=BA} = User,
%%     BaseAttSpeed = OldUser#battle_status.battle_attr#battle_attr.attr#attr.att_speed,
%%     [Float, Int] = calc_att_speed_helper(BuffList, NowTime, [1.0, 0]),
%%     AttSpeed = max(0, round(BaseAttSpeed*Float+Int)),
%%     lib_scene_timer:add_timer(NowTime+500, LastTime, Sign, Id, ?ATT_SPEED, []),

%%     case AttSpeed /= BA#battle_attr.att_speed of
%%         true  ->
%%             {ok, BinData} = pt_120:write(12084, [Sign, Id, AttSpeed]),
%%             lib_battle:send_to_scene(CopyId, X, Y, Broadcast, BinData);
%%         false -> skip
%%     end,
%%     User#battle_status{battle_attr=BA#battle_attr{att_speed=AttSpeed}}.

%% calc_att_speed_helper([{?ATT_SPEED, _, _, _, _, Int, Float, EndTime, _, _}|T], NowTime, [F, I]) when EndTime > NowTime ->
%%     calc_att_speed_helper(T, NowTime, [F*max(0, 1+Float), I+Int]);
%% calc_att_speed_helper([{?SPBUFF_SLOW, _, _, _, _, _, _, EndTime, _, _}|T], NowTime, [F, I]) when EndTime > NowTime -> %% 迟缓则攻速100
%%     calc_att_speed_helper(T, NowTime, [F, I-100]);
%% calc_att_speed_helper([_|T], NowTime, [F, I]) ->
%%     calc_att_speed_helper(T, NowTime, [F, I]);
%% calc_att_speed_helper([], _, Result) -> Result.

%% 计算回血效果
calc_hp_resume(User, Base, NowTime) ->
    case User of
        #ets_scene_user{battle_attr = BA} -> skip;
        #scene_object{battle_attr = BA} -> skip;
        #battle_status{battle_attr = BA} -> skip
    end,
    #battle_attr{other_buff_list = OtherL, attr_buff_list = AttrL} = BA,
    [Float, Int] = calc_hp_resume_helper(AttrL ++ OtherL, NowTime, [1.0, 0]),
    max(0, round(Base*Float+Int)).

calc_hp_resume_helper([#battle_buff{key = {?SPBUFF_RESUME_ADD_RATIO, _},
            param_int = Int, param_float = Float, time_end = TimeEnd
        } | T], NowTime, [F, I]) when TimeEnd > NowTime ->
    calc_hp_resume_helper(T, NowTime, [F*max(0, 1+Float), I+Int]);
calc_hp_resume_helper([_|T], NowTime, [F, I]) ->
    calc_hp_resume_helper(T, NowTime, [F, I]);
calc_hp_resume_helper([], _, Result) -> Result.

%% 计算被回血效果
calc_hp_be_resume(User, Base, NowTime) ->
    case User of
        #ets_scene_user{battle_attr = BA} -> skip;
        #scene_object{battle_attr = BA} -> skip;
        #battle_status{battle_attr = BA} -> skip
    end,
    #battle_attr{other_buff_list = OtherL, attr_buff_list = AttrL} = BA,
    [Float, Int] = calc_hp_be_resume_helper(AttrL ++ OtherL, NowTime, [1.0, 0]),
    max(0, round(Base * Float + Int)).

calc_hp_be_resume_helper([#battle_buff{key = {?SPBUFF_BERESUME_ADD_RATIO, _},
            param_int = Int, param_float = Float, time_end = TimeEnd
        } | T], NowTime, [F, I]) when TimeEnd > NowTime ->
    calc_hp_be_resume_helper(T, NowTime, [F*max(0, 1+Float), I+Int]);
calc_hp_be_resume_helper([_|T], NowTime, [F, I]) ->
    calc_hp_be_resume_helper(T, NowTime, [F, I]);
calc_hp_be_resume_helper([], _, Result) -> Result.

%% 删除bufftype的buff
remove_type_buff([], _BuffType, BuffList)-> BuffList;
remove_type_buff([#battle_buff{key = {BuffId, _}} = H | T], BuffType, BuffList) ->
    if
        BuffId == ?SPBUFF_DIZZY orelse BuffId == ?SPBUFF_SILENCE  orelse
        BuffId == ?SPBUFF_IMMOBILIZE orelse BuffId == ?SPBUFF_SLOW orelse
        BuffId == ?SPBUFF_BLEED ->
            remove_type_buff(T, BuffType, BuffList);
        true ->
            remove_type_buff(T, BuffType, [H|BuffList])
    end.

%% 检查是否可以触发效果
check_active_buff_effect_trigger(K, User, [{_TgType, _P, _, _PTime, _Int, _Float, SBuffId, K}])->
    %% io:format("~p ~p active trigger:~w~n", [?MODULE, ?LINE, [K]]),
    #battle_status{battle_attr = BA} = User,
    case buff_find_id(SBuffId, BA#battle_attr.attr_buff_list) of
        [] ->
            %% io:format("~p ~p BA#battle_attr.other_buff_list:~w~n", [?MODULE, ?LINE, [BA#battle_attr.other_buff_list]]),
            case buff_find_id(SBuffId, BA#battle_attr.other_buff_list) of
                [] -> false;
                _Buffs -> true
            end;
        _Buffs -> true
    end;
check_active_buff_effect_trigger(K, User, [_H|T])->
    %% io:format("~p ~p [K, _H]:~w~n", [?MODULE, ?LINE, [K, _H]]),
    check_active_buff_effect_trigger(K, User, T);
check_active_buff_effect_trigger(_K, _User, [])->
    true.

%% 攻击方被动技能触发
calc_aer_passive_skill(Aer, Der, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)->
    if
        BAPassiveArgs#battle_passive_args.hurt_type == ?HURT_TYPE_IMMUE -> {Aer, Der};
        Aer#battle_status.sign /= ?BATTLE_SIGN_PLAYER -> {Aer, Der};
        Aer#battle_status.skill_passive == [] -> {Aer, Der};
        true ->
            #battle_args{now_time = NowTime} = BattleExtraArgs,
            #battle_status{skill_passive = SkillPassive, skill_cd = SkillCd} = Aer,
            NewSkillCd = [{SkillId, Time}|| {SkillId, Time} <- SkillCd, Time > NowTime],
            calc_passive_skill(SkillPassive, Aer#battle_status{skill_cd = NewSkillCd}, Der,
                OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)
    end.

%% 受击方(Aer其实是受击方, Der其实是攻击方)
calc_der_passive_skill(Aer, Der, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs) ->
    if
        BAPassiveArgs#battle_passive_args.hurt_type == ?HURT_TYPE_IMMUE ->
            {Aer, Der, BAPassiveArgs#battle_passive_args.hp};
        Aer#battle_status.sign /= ?BATTLE_SIGN_PLAYER ->
            {Aer, Der, BAPassiveArgs#battle_passive_args.hp};
        Aer#battle_status.skill_passive == [] andalso Aer#battle_status.team_skill == 0 ->
            {Aer, Der, BAPassiveArgs#battle_passive_args.hp};
        true ->
            #battle_args{now_time = NowTime} = BattleExtraArgs,
            #battle_status{skill_passive = SkillPassive, skill_cd = SkillCd, team_skill = TeamSkill} = Aer,
            NewSkillPassive = ?IF(TeamSkill == 0, SkillPassive,  [{TeamSkill, 1}|SkillPassive]),
            NewSkillCd = [{SkillId, Time}|| {SkillId, Time} <- SkillCd, Time > NowTime],
            {NewAer, NewDer} = calc_passive_skill(NewSkillPassive, Aer#battle_status{skill_cd = NewSkillCd}, Der,
                OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs),
            #battle_status{battle_attr = NewBA} = NewAer,
            if
                NewBA#battle_attr.skill_effect#skill_effect.un_die == 0 ->
                    {NewAer, NewDer, BAPassiveArgs#battle_passive_args.hp};
                true ->
                    {NewAer, NewDer, NewAer#battle_status.battle_attr#battle_attr.hp}
            end
    end.

calc_passive_skill([], Aer, Der, _OldAer, _OldDer, _IsFirstDer, _BattleExtraArgs, _BroadCast, _BAPassiveArgs) ->
    {Aer, Der};
calc_passive_skill([{SkillId, SkillLv}|T], Aer, Der, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs) ->
    #battle_args{now_time = NowTime} = BattleExtraArgs,
    case check_passive_skill_cd(SkillId, Aer#battle_status.skill_cd, NowTime) of
        false ->
            calc_passive_skill(T, Aer, Der, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
        true ->
            case data_skill:get(SkillId, SkillLv) of
                #skill{lv_data=#skill_lv{cd = Cd, trigger = Trigger, attr = AttrList, effect = EffectList}, career = Career} ->
                    %% 属性计算
                    #battle_status{skill_cd = SkillCds, battle_attr=#battle_attr{attr_buff_list = AerBuffList}, triggers_passive = TriggerSkills} = Aer,
                    #battle_status{battle_attr=#battle_attr{attr_buff_list = DerBuffList}} = Der,
                    {NewAttrAer, NewAttrDer, AttrIsTrigger}
                    = calc_passive_attr_effect(AttrList, Trigger, Aer, Der, AerBuffList, DerBuffList,
                        NowTime, SkillId, SkillLv, OldAer, OldDer, Broadcast, BAPassiveArgs),
                    %% 特殊效果计算
                    #battle_status{battle_attr=#battle_attr{attr_buff_list = _A, other_buff_list = AerEBL}} = NewAttrAer,
                    #battle_status{battle_attr=#battle_attr{attr_buff_list = _D, other_buff_list = DerEBL}} = NewAttrDer,
                    {NewEffectAer, NewEffectDer, EffIsTrigger} =
                    calc_passive_other_effect(EffectList, Trigger, NewAttrAer, NewAttrDer, AerEBL, DerEBL,
                        SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs),
                    NewSkillCds = if
                        (AttrIsTrigger == 1 orelse EffIsTrigger == 1) andalso Cd /= 0 ->
                            [{SkillId, NowTime+Cd}| lists:keydelete(SkillId, 1, SkillCds)];
                        true -> SkillCds
                    end,
                    NewTriggerSkills =
                    case (AttrIsTrigger == 1 orelse EffIsTrigger == 1) of
                        true  -> [{Career, SkillId, NowTime+Cd}|TriggerSkills];
                        false -> TriggerSkills
                    end,
                    %io:format("~p ~p SkillCds, NewSkillCds:~w~n", [?MODULE, ?LINE, [SkillCds, NewSkillCds]]),
                    calc_passive_skill(T, NewEffectAer#battle_status{skill_cd = NewSkillCds, triggers_passive = NewTriggerSkills},
                        NewEffectDer, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
                _ ->
                    calc_passive_skill(T, Aer, Der, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)
            end
    end.

%% 计算被动属性技能效果
calc_passive_attr_effect([{K, BuffType, PerMil, AffectedParties, Int, Float, LastTime, Stack, EffectId}|T],
    Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, BroadCast, BAPassiveArgs)->
    case PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil of
        true ->
            ArgsGetAffects = #args_get_affects{
                affect = AffectedParties,
                aer = Aer, der = Der, aer_init = OldAer, der_init = OldDer,
                aer_buff = AerBuffList, der_buff = DerBuffList
            },
            #ret_get_affects{
                user = User, user_buff = UserBuffL, user_init = OldUser
            } = battle_obj_select:get_affected_parties(ArgsGetAffects),
            case check_passive_buff_effect_trigger(K, User, Trigger, BAPassiveArgs) of
                false ->
                    calc_passive_attr_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, BroadCast, BAPassiveArgs);
                {true, User10} ->
                    {User2, UserBuffL2}
                    = case K of
                        ?SPEED ->
                            case calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, K, SkillId, SkillLv, NowTime, UserBuffL) of
                                {true, BuffList} ->
                                    {calc_speed(User10, OldUser, OldUser#battle_status.battle_attr#battle_attr.attr_buff_list++BuffList,
                                            LastTime, NowTime, BroadCast), BuffList};
                                {false, _} -> {User10, UserBuffL}
                            end;
                        ?HP ->
                            %% NOTE: 这里不能设置hp值
                            %% 血量属于状态数据（会在战斗过程中发生变化），不是通常的固有属性数据，
                            %% 所以回血效果不适合用attr中的hp来实现，可使用effect中的效果来实现
                            %% 如果要做也是处理血量上限的变化，及附加一次回血（暂时非必要，不考虑）
                            {User10, UserBuffL};
                        _ ->
                            UserBA = User10#battle_status.battle_attr,
                            Value = value_cate(Int, Float, K, lib_player_attr:get_value_by_id(K, UserBA#battle_attr.attr),
                                lib_player_attr:get_value_by_id(K, OldUser#battle_status.battle_attr#battle_attr.attr)),
                            Attr1 = lib_player_attr:set_value_by_id(K, Value, UserBA#battle_attr.attr),
                            User1 = User10#battle_status{battle_attr=UserBA#battle_attr{attr=Attr1}},
                            %% 加入属性buff列表
                            case LastTime =< 0 of
                                true  -> {User1, UserBuffL};
                                false ->
                                    {_, TmpBuffL} = calc_buff_swap(K, BuffType, Int, Float, LastTime, Stack, EffectId, K, SkillId, SkillLv, NowTime, UserBuffL),
                                    {User1, TmpBuffL}
                            end
                    end,
                    ArgsSetAffects = #args_set_affects{
                        affect = AffectedParties,
                        aer = Aer, aer_buff = AerBuffList, der = Der, der_buff = DerBuffList,
                        affect_user = User2, affect_user_buff = UserBuffL2
                    },
                    #ret_set_affects{
                        aer = Aer2, aer_buff = AerBuffL2,
                        der = Der2, der_buff = DerBuffL2
                    } = battle_obj_select:set_affected_parties(ArgsSetAffects),
                    NBAPassiveArgs = BAPassiveArgs#battle_passive_args{is_trigger = 1},
                    calc_passive_attr_effect(T, Trigger, Aer2, Der2, AerBuffL2, DerBuffL2, NowTime, SkillId, SkillLv, OldAer, OldDer, BroadCast, NBAPassiveArgs)
            end;
        _ ->
            calc_passive_attr_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, BroadCast, BAPassiveArgs)
    end;
calc_passive_attr_effect([_H|T], Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, BroadCast, BAPassiveArgs)->
    calc_passive_attr_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, NowTime, SkillId, SkillLv, OldAer, OldDer, BroadCast, BAPassiveArgs);
calc_passive_attr_effect([], _Trigger, Aer, Der, AerBuffList, DerBuffList, _NowTime, _SkillId, _SkillLv, _OldAer, _OldDer, _BroadCast, BAPassiveArgs)->
    AerBA = Aer#battle_status.battle_attr, DerBA = Der#battle_status.battle_attr,
    {Aer#battle_status{battle_attr=AerBA#battle_attr{attr_buff_list=AerBuffList}},
        Der#battle_status{battle_attr=DerBA#battle_attr{attr_buff_list=DerBuffList}},
        BAPassiveArgs#battle_passive_args.is_trigger}.

%% 计算被动的特殊效果
calc_passive_other_effect([{K, BuffType, PerMil, AffectedParties, Int, Float, AttrId, LastTime, _Num, EffectId}=H|T], Trigger, Aer, Der, AerBuffList,
    DerBuffList, SkillId, SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs) ->
    ArgsGetAffects = #args_get_affects{
        affect = AffectedParties,
        aer = Aer, der = Der, aer_init = OldAer, der_init = OldDer,
        aer_buff = AerBuffList, der_buff = DerBuffList
    },
    #ret_get_affects{
        user = User, user_buff = UserBuffL, user_init = OldUser
    } = battle_obj_select:get_affected_parties(ArgsGetAffects),
    %% 检查触发条件
    case check_passive_buff_effect_trigger(K, User, Trigger, BAPassiveArgs) of
        false ->
            calc_passive_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv,
                OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
        {true, User1} ->
            UserBA = User1#battle_status.battle_attr,
            UserSE = UserBA#battle_attr.skill_effect,
            #battle_passive_args{trigger_time = _TriggerTime} = BAPassiveArgs,
            IsImmune = is_immune(User, UserSE, K, BuffType),
            case not IsImmune andalso (PerMil == 0 orelse PerMil >= 1000 orelse urand:rand(1, 1000) =< PerMil) of
                true  when LastTime > 0 ->
                    {Int1, Float1}
                    = if  AttrId > 0 ->
                            {value_cate(Int, Float, AttrId, 0, lib_player_attr:get_value_by_id(AttrId, OldUser#battle_status.battle_attr#battle_attr.attr)), 0};
                        true -> {Int, Float}
                    end,
                    #battle_args{now_time = NowTime} = BattleExtraArgs,
                    case calc_buff_swap(K, BuffType, Int1, Float1, LastTime, 0, EffectId, AttrId, SkillId, SkillLv, NowTime, UserBuffL) of
                        {true, BuffList} ->
                            {CalcComboBuffUser, CalcComboBuffL}
                            = case BuffType of
                                ?SKILL_CTRL_BUFF -> %% 控制技能
                                    {User1#battle_status{skill_combo=[], shaking_skill=0,
                                            battle_attr=UserBA#battle_attr{skill_effect=UserSE#skill_effect{interrupt=User#battle_status.shaking_skill}}},
                                        del_all_combo_buff(BuffList, [])};
                                _ -> {User1, BuffList}
                            end,
                            ArgsSetAffects = #args_set_affects{
                                affect = AffectedParties,
                                aer = Aer, aer_buff = AerBuffList, der = Der, der_buff = DerBuffList,
                                affect_user = CalcComboBuffUser, affect_user_buff = CalcComboBuffL
                            },
                            #ret_set_affects{
                                aer = Aer1, aer_buff = AerBuffL1,
                                der = Der1, der_buff = DerBuffL1
                            } = battle_obj_select:set_affected_parties(ArgsSetAffects),
                            NBAPassiveArgs = BAPassiveArgs#battle_passive_args{is_trigger = 1, skill_id = SkillId},
                            #ret_set_affects{
                                aer = Aer2, aer_buff = AerBuffL2,
                                der = Der2, der_buff = DerBuffL2
                            } = calc_sp_effect(H, Aer1, AerBuffL1, Der1, DerBuffL1, OldAer, OldDer,
                                IsFirstDer, BattleExtraArgs, Broadcast, NBAPassiveArgs
                            ),
                            calc_passive_other_effect(T, Trigger, Aer2, Der2, AerBuffL2, DerBuffL2, SkillId,
                                SkillLv, OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, NBAPassiveArgs);
                        {false, _} ->
                            calc_passive_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv,
                                OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)
                    end;
                true ->
                    %% TODO momentary_buff_list_aer 这个可能需要从忍者再移植过来
                    ArgsSetAffects = #args_set_affects{
                        affect = AffectedParties,
                        aer = Aer, aer_buff = AerBuffList, der = Der, der_buff = DerBuffList,
                        affect_user = User1, affect_user_buff = UserBuffL
                    },
                    #ret_set_affects{
                        aer = Aer1, aer_buff = AerBuffL1,
                        der = Der1, der_buff = DerBuffL1
                    } = battle_obj_select:set_affected_parties(ArgsSetAffects),
                    NBAPassiveArgs = BAPassiveArgs#battle_passive_args{is_trigger = 1, skill_id = SkillId},
                    #ret_set_affects{
                        aer = Aer2, aer_buff = AerBuffL2,
                        der = Der2, der_buff = DerBuffL2
                    } = calc_sp_effect(H, Aer1, AerBuffL1, Der1, DerBuffL1, OldAer,
                        OldDer, IsFirstDer, BattleExtraArgs, Broadcast, NBAPassiveArgs
                    ),
                    calc_passive_other_effect(T, Trigger, Aer2, Der2, AerBuffL2, DerBuffL2, SkillId, SkillLv,
                        OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, NBAPassiveArgs);
                false ->
                    calc_passive_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv,
                        OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs)
            end
    end;
calc_passive_other_effect([_|T], Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv,
    OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs) ->
    calc_passive_other_effect(T, Trigger, Aer, Der, AerBuffList, DerBuffList, SkillId, SkillLv,
        OldAer, OldDer, IsFirstDer, BattleExtraArgs, Broadcast, BAPassiveArgs);
calc_passive_other_effect([], _Trigger, #battle_status{battle_attr=AerBA}=Aer, #battle_status{battle_attr=DerBA}=Der,
    AerBuffList, DerBuffList, _SkillId, _SkillLv, _OldAer, _OldDer, _IsFirstDer, _BattleExtraArgs, _Broadcast, BAPassiveArgs) ->
    {Aer#battle_status{battle_attr = AerBA#battle_attr{other_buff_list=AerBuffList}},
        Der#battle_status{battle_attr = DerBA#battle_attr{other_buff_list=DerBuffList}},
        BAPassiveArgs#battle_passive_args.is_trigger}.

%% 被动技能触发检查
%% BfAttOrAfAtt:
%% TriggerTime:1攻击者计算伤害前触发; 2防守者计算伤害前触发; 3攻击者计算伤害后触发; 4防守者计算伤害后触发
%% K :: 属性类型ID|特殊效果ID
check_passive_buff_effect_trigger(_K, _User, [], _BAPassiveArgs)-> false;
check_passive_buff_effect_trigger(K, User, [H|T], BAPassiveArgs) ->
    %% {触发类型, 概率, 作用方, 触发时机, 整数值, 小数值, 特殊效果ID, 触发效果ID}
    case H of
        {?SKILL_BUFF_TRIGGER_HP, _PerMil, _, ?SKILL_BUFF_TG_TIME, _, Float, _AnyCustomParam, SK} when SK ==0 orelse SK == K ->
            %% 特殊血量不分攻击前后触发
            #battle_status{battle_attr = BA} = User,
            #battle_attr{hp = Hp, hp_lim = DerHpLim} = BA,
            ?IF(Hp/DerHpLim =< Float, {true, User}, false);

        {TgType, _PerMil, _, TriggerTime, _, Float, CustomParam, SK}
        when TriggerTime == BAPassiveArgs#battle_passive_args.trigger_time andalso (SK ==0 orelse SK == K) ->
            #battle_passive_args{aer = _Aer, der = Der, hurt_type = HurtType, hp = Hp, skill_id = SkillId} = BAPassiveArgs,
            #battle_status{battle_attr = BA} = User,
            %% #battle_status{sign = AttSign} = Aer,
            %% #battle_status{sign = DerSign} = Der,
            if
                %% 无条件触发（战斗中无条件触发，不判断触发时机）
                TgType == ?SKILL_BUFF_TRIGGER -> {true, User};
                %% 受到某个特殊效果触发
                TgType == ?SKILL_BUFF_TRIGGER_GET ->
                    #battle_status{battle_attr = OBA} = Der,
                    SBuffId = CustomParam,
                    BfHave = check_have_effect_list([SBuffId], OBA),
                    AfHave = check_have_effect_list([SBuffId], BA),
                    if
                        not BfHave andalso AfHave -> %% 没有,有
                            if
                                K == ?SPBUFF_UNDIZZY -> %%
                                    SkillEffect = BA#battle_attr.skill_effect,
                                    NewSkillEffect = SkillEffect#skill_effect{current_un_dizzy = 1},
                                    {true, User#battle_status{battle_attr = BA#battle_attr{skill_effect = NewSkillEffect}}};
                                true ->
                                    {true, User}
                            end;
                        true -> false
                    end;
                %% 身上有某种特殊效果触发
                TgType == ?SKILL_BUFF_TRIGGER_HAVE ->
                    SBuffId = CustomParam,
                    case check_have_effect_list([SBuffId], BA) of
                        true -> {true, User};
                        false -> false
                    end;
                %% 身上有任一特殊效果时触发
                TgType == ?SKILL_BUFF_TRIGGER_HAVE_LIST andalso is_list(CustomParam) ->
                    SBuffIdList = CustomParam,
                    case check_have_effect_list(SBuffIdList, BA) of
                        true -> {true, User};
                        false -> false
                    end;
                %% 血量触发
                TgType == ?SKILL_BUFF_TRIGGER_HP ->
                    #battle_attr{hp = Hp, hp_lim = DerHpLim} = BA,
                    ?IF(Hp/DerHpLim =< Float, {true, User}, false);
                %% 暴击时触发
                TgType == ?SKILL_BUFF_TRIGGER_CRIT andalso HurtType == ?HURT_TYPE_CRIT ->
                    {true, User};
                %% 不会在这里处理回血触发的被动技能
                TgType == ?SKILL_BUFF_TRIGGER_HPRESUME ->
                    false;
                %% 玩家死亡触发
                Hp =< 0 andalso TgType == ?SKILL_BUFF_TRIGGER_DIE ->
                    SkillEffect = BA#battle_attr.skill_effect,
                    NewSkillEffect = SkillEffect#skill_effect{un_die = 1},
                    {true, User#battle_status{battle_attr = BA#battle_attr{skill_effect = NewSkillEffect}}};
                %% 闪避时触发
                TgType == ?SKILL_BUFF_TRIGGER_MISS andalso HurtType == ?HURT_TYPE_MISS ->
                    {true, User};
                %% 被动技能某任一伤害类型时触发
                TgType == ?SKILL_BUFF_TRIGGER_HURT_TYPE_LIST andalso is_list(CustomParam) ->
                    HurtTypeList = CustomParam,
                    case lists:member(HurtType, HurtTypeList) of
                        true -> {true, User};
                        false -> false
                    end;
                %% 被动技能使用某一技能时触发
                TgType == ?SKILL_BUFF_TRIGGER_SKILL_ID andalso CustomParam == SkillId ->
                    {true, User};
                %% 在组队状态检查触发
                TgType == ?SKILL_BUFF_TRIGGER_TEAM andalso User#battle_status.team_id > 0 ->
                    {true, User};
                %% 等级高于怪物对方时触发
                TgType == ?SKILL_BUFF_TRIGGER_LV_LARGER ->
                    #battle_status{id = UserId, sign = UserSign, figure = #figure{lv = UserLevel}} = User,
                    #battle_status{id = AerId, sign = AerSign, figure = #figure{lv = AerLevel}} = Der,
                    #battle_status{id = _DerId, sign = DerSign, figure = #figure{lv = DerLevel}} = Der,
                    %% 实际上User是从技能的生效方来选择的，也不一定得是攻方或守方
                    %% 需要检查攻守身份吗？
                    IsAer = UserId =:= AerId andalso UserSign =:= AerSign,
                    if
                        IsAer andalso DerSign =/= ?BATTLE_SIGN_MON ->
                            false;
                        IsAer andalso UserLevel =< DerLevel ->
                            false;
                        AerSign =/= ?BATTLE_SIGN_MON ->
                            false;
                        UserLevel =< AerLevel ->
                            false;
                        true ->
                            {true, User}
                    end;
                %% 被动技能实际影响方的战斗身份标识限制
                TgType == ?SKILL_BUFF_TRIGGER_B_SIGN ->
                    CheckRet =
                    case CustomParam of
                        [_|_] = NeedBattleSignList ->
                            lists:member(User#battle_status.sign, NeedBattleSignList);
                        NeedBattleSign ->
                            User#battle_status.sign == NeedBattleSign
                    end,
                    case CheckRet of
                        true  -> {true, User};
                        false -> false
                    end;
                true ->
                    false
            end;
        _ ->
            check_passive_buff_effect_trigger(K, User, T, BAPassiveArgs)
    end.

%% 是否免疫被动效果
is_immune(User, UserSE, K, BuffType) ->
    Bool = UserSE#skill_effect.immue == 1 orelse
        (
            BuffType =:= ?SKILL_CTRL_BUFF andalso UserSE#skill_effect.superarmor == 1
        ) orelse
        (
            (K =:= ?SPBUFF_DIZZY
                orelse K =:= ?SPBUFF_BLEED
                orelse K =:= ?SPBUFF_KILL
                orelse K =:= ?SPBUFF_FIRING
                orelse K =:= ?SPBUFF_FEAR
            ) andalso
                (User#battle_status.is_armor == 1 orelse (User#battle_status.sign =:= ?BATTLE_SIGN_MON andalso User#battle_status.boss > ?MON_ELITE))
        ),
    Bool.


%% 检查是否有某个特殊效果
%% （同时检查 attr_buff_list 及 other_buff_list）
%% NOTE: 考察一下是否有必要检查时间（因为是战斗中触发调用，不检查其实也可以）
%%    如果要准确，应该是调用own_buff检查过期时间
check_have_effect_list([], _BA) -> false;
check_have_effect_list([SBuffId|T], BA) ->
    case check_have_attr_effect(SBuffId, BA) of
        true  -> true;
        false ->
            case check_have_other_effect(SBuffId, BA) of
                true  -> true;
                false -> check_have_effect_list(T, BA)
            end
    end.

%% 检查是否存在某BUFF数据（不检查过期时间，内部使用）
check_have_attr_effect(BuffId, BA) ->
    case buff_find_id(BuffId, BA#battle_attr.attr_buff_list) of
        [] -> false;
        _  -> true
    end.

%% 检查是否存在某BUFF数据（不检查过期时间，内部使用）
check_have_other_effect(BuffId, BA) ->
    case buff_find_id(BuffId, BA#battle_attr.other_buff_list) of
        [] -> false;
        _  -> true
    end.

%% 检查cd
check_passive_skill_cd(SkillId, SkillCds, NowTime)->
    case lists:keyfind(SkillId, 1, SkillCds) of
        {_, EndTime} when EndTime > NowTime -> false;
        _ -> true
    end.

%% 作为有图标的buff必须满足以下条件
%% 概率100%
%% 所有效果持续时间一致
skill_buff_to_goods_buff(SkillId, SkillLv) ->
    case data_skill:get_lv_data(SkillId, SkillLv) of
        % [{属性类型ID,buff类型,概率,作用方,整数值,小数值,持续时间,叠加层数,特效id},...]
        #skill_lv{attr = [{_, _, _, _, _, _, TmpTime, _, _}|_] = Attr} ->
            F = fun
                ({AttrId, _, 1000, _, Int, _, _, _, _}, Acc) ->
                    case lists:keyfind(AttrId, 1, Acc) of
                        {_, V} ->
                            lists:keystore(AttrId, 1, Acc, {AttrId, V + Int});
                        _ ->
                            [{AttrId, Int}|Acc]
                    end;
                (_, Acc) -> Acc
            end,
            EffectList = lists:foldl(F, [], Attr),
            Time = round(TmpTime div 1000),
            {SkillId, ?IF(Time > 0, Time, 0), EffectList};
        _ ->
            undefined
    end.

calc_sprint_pos(OX, OY, AttX, AttY, Offset) ->
    if
        OX == AttX -> {OX, AttY-80};
        OY == AttY -> {OX-80, AttY};
        true ->
            Distance = math:sqrt((AttX - OX)*(AttX - OX) + (AttY - OY)*(AttY - OY)),
            if
                Distance =< Offset -> {OX, OY};
                true ->
                    Unit = 1/Distance*Offset,
                    {round(AttX - (AttX - OX)*Unit), round(AttY - (AttY - OY)*Unit)}
            end
    end.

%% 合并技能效果
merge_skill_eff([Eff1, Eff2|T]) ->
    Eff = merge_skill_eff(Eff1, Eff2),
    merge_skill_eff([Eff|T]);
merge_skill_eff([Eff]) -> Eff;
merge_skill_eff([]) -> [].

merge_skill_eff([{BTypeId, BuffType, PerMil, AffectedParties, Int, Float, LastTime, Stack, EffectId}=H|T], Eff2) ->
    case lists:keyfind(BTypeId, 1, Eff2) of
        {_, _, _, _, IntO, FloatO, LastTimeO, _, _} ->
            merge_skill_eff(T, lists:keystore(BTypeId, 1, Eff2, {BTypeId, BuffType, PerMil, AffectedParties, IntO+Int, FloatO+Float, LastTimeO + LastTime, Stack, EffectId}));
        _ -> merge_skill_eff(T, [H|Eff2])
    end;
merge_skill_eff([{BTypeId, BuffType, PerMil, AffectedParties, Int, Float, AttrId, LastTime, Num, EffectId}=H|T], Eff2) ->
    case lists:keyfind(BTypeId, 1, Eff2) of
        {_, _, _, _, IntO, FloatO, _, LastTimeO, NumO, _} ->
            merge_skill_eff(T, lists:keystore(BTypeId, 1, Eff2, {BTypeId, BuffType, PerMil, AffectedParties, IntO+Int, FloatO+Float, AttrId, LastTimeO + LastTime, NumO + Num, EffectId}));
        _ -> merge_skill_eff(T, [H|Eff2])
    end;
merge_skill_eff([], Eff2) -> Eff2.

%% -------------------------------------------------------
%% Buff的转换 与 BuffList基本运算接口
%% Buff :: #battle_buff{}
%% -------------------------------------------------------

%% 转换战斗buff数据
%% BuffId即其他地方常写作K的变量 - Buff编号
to_buff(BuffId, SkillId, SkillLv, Stack, Int, Float, TimeEnd, EffId, AttrId) ->
    #battle_buff{
        key = {BuffId, SkillId}, skill_lv = SkillLv,
        stack = Stack,
        param_int = Int, param_float = Float,
        time_end = TimeEnd,
        effect_id = EffId,
        attr_id = AttrId
    }.

buff_find(BuffId, SkillId, BuffList) ->
    lists:keyfind({BuffId, SkillId}, #battle_buff.key, BuffList).

%% -> BuffListOfBuffId
buff_find_id(BuffId, BuffList) ->
    [
        Buff ||
        Buff = #battle_buff{key = {BuffIdX, _SkillId}} <- BuffList,
        BuffId =:= BuffIdX
    ].

%% Buff替换存储，如果原来不存在相当于新增
buff_store(BuffId, SkillId, BuffList, NewBuff) ->
    lists:keystore({BuffId, SkillId}, #battle_buff.key, BuffList, NewBuff).

%% 删除指定技能的特定Buff
buff_delete(BuffId, SkillId, BuffList) ->
    lists:keydelete({BuffId, SkillId}, #battle_buff.key, BuffList).

%% 删除指定的Buff
buff_delete_id(BuffId, BuffList) ->
    [
        Buff ||
        Buff = #battle_buff{
            key = {BuffIdX, _SkillId}
        } <- BuffList,
        BuffIdX =/= BuffId
    ].

%% 同一BuffId的各个Buff数据汇总Int与Float值 -> {IntSum, FloatSum}
buff_sum_params(BuffId, BuffList) ->
    BuffListOfBuffId = buff_find_id(BuffId, BuffList),
    case BuffListOfBuffId of
        [_|_] ->
            % IntFloatList = [
            %     {Int, Float} ||
            %     #battle_buff{param_int = Int, param_float = Float}
            %     <- BuffListOfBuffId
            % ],
            % {IntList, FloatList} = lists:unzip(IntFloatList),
            % {lists:sum(IntList), lists:sum(FloatList)};
            lists:foldl(fun buff_param_sum/2, {0, 0}, BuffListOfBuffId);
        _ ->
            {0, 0}
    end.

buff_param_sum(#battle_buff{param_int = Int, param_float = Float}, {IntSum, FloatSum}) ->
    {Int + IntSum, Float + FloatSum};
buff_param_sum(_, Acc) ->
    Acc.

%% buff list的合并处理
buff_list_merge([], ListB) -> ListB;
buff_list_merge(ListA, []) -> ListA;
buff_list_merge(ListA, ListB) ->
    LenA = length(ListA),
    LenB = length(ListB),
    case LenA >= LenB of
        true  -> buff_list_merge_2(ListB, ListA);
        false -> buff_list_merge_2(ListA, ListB)
    end.

%% buff list的合并
buff_list_merge_2([], Acc) -> Acc;
buff_list_merge_2([
        Buff = #battle_buff{
            key = {BuffId, SkillId},
            skill_lv = SkillLv,
            time_end = TimeEnd
        } | Others
    ], Acc) ->
    case buff_find(BuffId, SkillId, Acc) of
        Buff ->     % 相同BUFF，保留一个
            buff_list_merge_2(Others, Acc);
        #battle_buff{
            skill_lv = SkillLvAcc,
            time_end = OtherTimeEnd
        } ->        % 相同技能，相同类型的BUFF，保留级别高、剩余时间长的
            if
                SkillLvAcc >= SkillLv andalso OtherTimeEnd >= TimeEnd ->
                    buff_list_merge_2(Others, Acc);
                SkillLvAcc > SkillLv ->
                    buff_list_merge_2(Others, Acc);
                true ->
                    TmpAcc = buff_delete(BuffId, SkillId, Acc),
                    buff_list_merge_2(Others, [Buff | TmpAcc])
            end;
        _ ->    % 容错（正常数据不会执行的分支）
            TmpAcc = buff_delete(BuffId, SkillId, Acc),
            buff_list_merge_2(Others, [Buff | TmpAcc])
    end;
buff_list_merge_2([_ | Others], Acc) ->
    buff_list_merge_2(Others, Acc).

%% -------------------------------------------------------
%% 复活技能的触发管理
%% -------------------------------------------------------

%% 检查复活触发技能
check_revive_trigger(Player, _ReviveCostType = 1) ->
    #player_status{
        skill = #status_skill{
            skill_passive = SkillPassive
        }
    } = Player,
    find_revive_trigger_skill(SkillPassive);
check_revive_trigger(_Player, _) ->
    {0, 0}.

%% 查找复活触发的技能（目前只触发一个）
find_revive_trigger_skill([{SkillId, SkillLv} | Others]) ->
    case data_skill:get(SkillId, SkillLv) of
        #skill{lv_data = #skill_lv{trigger = TriggerList = [_|_]}} ->
            TriggerTimeKeyPos = 4,
            case lists:keyfind(?SKILL_BUFF_TG_TIME_GOLD_REVIVE, TriggerTimeKeyPos, TriggerList) of
                {_TgType, _PerMil, _, _TriggerTime, _Int, _Float, _AnyCustomParam, _AttrId} ->
                    %% 这里特殊，不检查AttrId与后面的attr字段中内容是否匹配
                    %% （因为调用mod_battle:assist的时候，内部也没有检查）
                    {SkillId, SkillLv};
                _ ->
                    find_revive_trigger_skill(Others)
            end;
        _ ->
            find_revive_trigger_skill(Others)
    end;
find_revive_trigger_skill([_|Others]) ->
    find_revive_trigger_skill(Others);
find_revive_trigger_skill([]) ->
    {0, 0}.

