%%% -------------------------------------------------------
%%% @author huangyongxing@yeah.net
%%% @doc
%%% 怪物逻辑模块
%%%
%%% 本模块主要为“怪物逻辑管理进程”的状态函数的内部接口
%%%
%%% 是私有模块，
%%% 通常仅由怪物逻辑管理进程内部调用，其他地方勿用
%%% -------------------------------------------------------
%%% 其他说明：
%%% 这里的接口主要由原来 mod_mon_active 模块中移植并调整。
%%% mon_mgr模块为每个场景分线启动一个实例，用于管理一批怪物，
%%% mon_mgr模块模拟了类似原来gen_fsm的状态事件及外部事件处理机制，
%%% 以使本模块可以方便地移植和套用原来开发的整套AI逻辑。
%%% 原来mod_mon_active中使用定时器发送消息来实现的逻辑，
%%% 虽然仍可以使用消息的方式来实现，但是除非有特别原因，
%%% 否则通常都需要改为使用state_driven和general_driven来实现，
%%% 以大幅削减消息发送数量，提升性能。
%%% @end
%%% -------------------------------------------------------
-module(mon_state_logic).
%% mon_mgr回调接口
-export([
        init/1
        ,handle_info/3
        ,terminate/3
    ]).
%% 怪物状态函数
-export([
        back/2
        ,sleep/2
        ,patrol/2
        ,revive/2
        ,trace/2
        ,dead/2
        ,walk/2
        ,walk_auto/2
        ,accumulate_power/2
    ]).
%% 其他接口
-export([
        base_to_cache/2
        ,calc_leave_distance/5
        ,calc_hurt_support_role_leave/3
        ,calc_deserted_support_role_leave/3
        ,recover_hp_no_att/3
        ,update_belong_who/4
        ,find_target_cast_mon_first/2
        ,scene_update_bl_whos/3
        ,statistics_hurt_by_team/1
        ,update_hurt_list/2
    ]).

-include("common.hrl").
-include("scene.hrl").
-include("mon.hrl").
-include("battle.hrl").
-include("dungeon.hrl").
-include("skill.hrl").
-include("attr.hrl").
-include("figure.hrl").
-include("team.hrl").
-include("def_fun.hrl").
-include("drop.hrl").
-include("collect.hrl").
-include("errcode.hrl").

-define(STOP_TIME, 250).

%% 循环检测周期(s)
-define(CYCLE_CHECK_TIME, 3).
%% 是否循环周期检测怪类型
-define(IS_CYCLE_CHECK_MON(Kind), lists:member(Kind, [9])).

%% 怪物属性和初始状态初始化
init([_Id, MonId, Scene, ScenePoolId, Xpx, Ypx, BornAngle, CopyId, BroadCast, Arg])->
    case data_mon:get(MonId) of
        #mon{} = Mon ->
            init2([_Id, Scene, ScenePoolId, Xpx, Ypx, BornAngle, CopyId, BroadCast, Arg], Mon);
        _ ->
            {badcfg, MonId}
    end.

init2([_Id, Scene, ScenePoolId, Xpx, Ypx, BornAngle, CopyId, BroadCast, Arg], M) ->
    %% 配置转换为内存数据
    Mon = base_to_cache([_Id, Scene, ScenePoolId, Xpx, Ypx, BornAngle, CopyId, self()], M),
    %% 自定义创建参数初始化
    {Mon1, InitObAct, BaseStateName, _} = set_mon(
        Arg, Mon#scene_object{aid = self()}, #ob_act{}, null, ?BROADCAST_0, []
    ),
    %% 设置动态属性
    MonAfDynamic = case Mon1#scene_object.mon#scene_mon.auto of %% 0读取默认属性，1动态根据公式生成属性 3功能自定义
        1 -> set_dynamic_mon(Mon1, max(1, util:get_world_lv()), Mon1#scene_object.mon#scene_mon.dun_r);
        2 -> set_dynamic_mon(Mon1, Mon1#scene_object.mon#scene_mon.auto_lv, Mon1#scene_object.mon#scene_mon.dun_r);
        3 -> set_custom_made_mon(Mon1);
        _ -> Mon1
    end,
    DelayGeneralDriven1 =
    case Mon1#scene_object.type == 1 of
        true when Mon1#scene_object.mon#scene_mon.kind =/= ?MON_KIND_ATT_AUTO ->
            %% 宠物副本进击怪出生时先不找目标
            case lib_dungeon_pet:is_dun_pet_scene(Scene) of
                true -> [];
                _ ->
                    lib_scene_object:put_ai(
                        MonAfDynamic#scene_object.id, Scene, ScenePoolId, CopyId, Xpx, Ypx, Mon1#scene_object.warning_range
                    ),
                    [{500, 'find_target'}]
            end;
        _ ->
            []
    end,
    MonAddAttr = case lists:keyfind(add_attr, 1, Arg) of
        {add_attr, AttrObj} -> add_mon_attr(MonAfDynamic, AttrObj);
        _ -> MonAfDynamic
    end,
    BAAfAttr = MonAddAttr#scene_object.battle_attr,
    CombatPower = lib_player_attr:calc_attr_power(BAAfAttr#battle_attr.attr),
    MonAfPower = MonAddAttr#scene_object{combat_power = CombatPower, battle_attr = BAAfAttr#battle_attr{combat_power = CombatPower}},

    DelayGeneralDriven2 =
    case InitObAct#ob_act.cycle_check_handler of
        {_, _, [Time|_]}  -> [{Time * 1000, 'cycle_check'} | DelayGeneralDriven1];
        {_, _, _}  -> [{0, 'cycle_check'} | DelayGeneralDriven1];
        _ -> DelayGeneralDriven1
    end,

    NowTimeMS = utime:unixtime(ms),
    State0 = InitObAct#ob_act{object = MonAfPower, w_point = {Mon#scene_object.x, Mon#scene_object.y}},
    State1 = mon_state_util:update_general_driven(State0, DelayGeneralDriven2, NowTimeMS),
    State  = mon_state_util:update_state_driven(State1, {1000, repeat}, NowTimeMS),

    %% 在场景进程中保存怪物信息
    lib_scene_object:insert(MonAfPower),
    %% 广播怪物
    case BroadCast  of
        ?BROADCAST_0 -> skip;
        _ ->
            lib_scene_object:broadcast_object(MonAfPower)
    end,
    %% 怪物出生ai
    {#ob_act{object = Minfo2} = NewState, StateName} = lib_mon_ai:born(State, BaseStateName),
    case State#ob_act.born_handler of
        {BornM, BornF, BornA} ->
            BornM:BornF(init, Minfo2, BornA);
        _ -> ok
    end,
    if
        Mon1#scene_object.mon#scene_mon.kind == ?MON_KIND_ATT_AUTO ->
            {ok, walk_auto, NewState};
        StateName /= null ->
            {ok, StateName, NewState};
        true->
            {ok, sleep, NewState}
    end.

%% 配置转化成内存记录
base_to_cache([Id, Scene, ScenePoolId, Xpx, Ypx, BornAngle, CopyId, Pid], M) ->
    LongNowTime = utime:longunixtime(),
    #mon{
        id=Mid, boss=Boss, kind=Kind, name=Name, career=_Career, color=Color, type=Type,
        icon=Icon, icon_effect=IconEffect, icon_texture=IconTextrue, weapon_id=WeaponId, lv=Lv,
        att=Att, hit=Hit, dodge=Dodge, crit=Crit,  hp_lim=Hp, def = Def, ten = Ten, special_attr = SpecailAttr,
        striking_distance=StrikingDistance, tracing_distance=TracingDistance,  warning_range = WarningRange,
        att_time=AttTime, att_type=AttType, retime=ReTime, is_fight_back=IsFightBack, is_be_atted=IsBeAtt,
        is_be_clicked = IsBeClicked, skill=SkillList, speed=Speed, exp=Exp, collect_time = CollectTime,
        collect_count=CollectCount, resum = Resume, auto = Auto, is_armor=IsArmor,
        is_hit_ac = IsHitAc, del_hp_each_time = DelHpEachTime, is_patrol = CanPatrol
    } = M,
    [_ResumeHp, NoBattleHpEx, ReGapTime] =
    case Resume of
        [_, _, _] = E -> E;
        _ -> [0, 0, 0]
    end,
    %% 初始化怪物技能cd
    F = fun({SkillId, SkillLv}, CdList) ->
            case data_skill:get(SkillId, SkillLv) of
                #skill{lv_data=#skill_lv{cd = Cd}} -> [{SkillId, LongNowTime + Cd} | CdList];
                _ -> CdList
            end
    end,
    SkillCdList = lists:foldl(F, [], SkillList),
    %% 初始化内存数据
    Attr = #attr{att=Att, hp = Hp, hit=Hit, dodge=Dodge,  crit=Crit, def = Def, ten = Ten, speed = Speed},
    if
        SpecailAttr == [] -> NewAttr = Attr;
        true ->
            AttrList = attr_conv:to_attr_tuple_list(Attr),
            NewAttrList = ulists:kv_list_plus_extra([AttrList, SpecailAttr]),
            NewAttr = attr_conv:to_attr_record(NewAttrList)
    end,
    Figure = #figure{name=Name, body=Icon, lv=Lv},
    BattleAttr = #battle_attr{
        hp=Hp, hp_lim=Hp, hp_lim_origin = Hp, attr=NewAttr, att_area=StrikingDistance
        ,speed=Speed, pk = #pk{}, del_hp_each_time = DelHpEachTime
    },
    SceneMon = #scene_mon{
        d_x=Xpx, d_y=Ypx, mid=Mid, boss=Boss, kind=Kind, collect_time = CollectTime
        ,collect_count=CollectCount, auto=Auto, exp=Exp
        ,retime=ReTime
    },
    #scene_object{
        sign=?BATTLE_SIGN_MON, id=Id, config_id=Mid, scene=Scene, scene_pool_id=ScenePoolId,
        copy_id=CopyId, x=Xpx, y=Ypx, type=Type, battle_attr=BattleAttr, figure=Figure, color=Color,
        icon_effect=IconEffect, icon_texture=IconTextrue, weapon_id=WeaponId, striking_distance=StrikingDistance,
        tracing_distance=TracingDistance, warning_range = WarningRange, att_time=AttTime, d_x=Xpx,
        d_y=Ypx, no_battle_hp_ex=NoBattleHpEx, hp_time=ReGapTime, is_fight_back=IsFightBack, is_be_atted=IsBeAtt,
        is_be_clicked = IsBeClicked, skill=SkillList, skill_cd = SkillCdList, att_type=AttType, aid=Pid,
        is_armor=IsArmor, mon=SceneMon, is_hit_ac=IsHitAc, create_time = utime:unixtime(), is_patrol = CanPatrol,
        angle = BornAngle
    }.

%% ===================================================怪物进程消息处理=============================================
%% 修改怪物属性
handle_info({'change_attr', KeyValues}, StateName, State) ->
    {Object, NewState, NewStateName, UpList} = set_mon(KeyValues, State#ob_act.object, State, StateName, 1, []),
    lib_scene_object:update(Object, UpList),
    lib_scene_object:change_attr_broadcast(UpList, Object, []),
    StateDriven = case NewStateName /= StateName of
        true  ->
            mon_state_util:conv_delay_driven({0, repeat});
        false -> NewState#ob_act.state_driven
    end,
    {next_state, NewStateName, NewState#ob_act{object = Object, state_driven = StateDriven}};

handle_info(_Info, dead, State) ->
    {next_state, dead, State};

handle_info({'ripple', _Id, _Pid, _Sign, _X, _Y, _Group}, StateName = accumulate_power, State) ->
    {next_state, StateName, State};
handle_info({'ripple', Id, Pid, Sign, X, Y, Group}, StateName, State) ->
    #ob_act{att=Att, object=SceneObj} = State,
    #scene_object{type = BattleType, tracing_distance=TraceDistance, battle_attr=#battle_attr{hp = Hp, group=MonGroup},
        mon=#scene_mon{d_x=Dx, d_y=Dy}, skill_owner=SkillOwner, can_att_list = CanAttList} = SceneObj,
    IsTrace = is_continue_trace(Dx, Dy, X, Y, TraceDistance),
    IsOwner = 
    if
        SkillOwner#skill_owner.id > 0 andalso SkillOwner#skill_owner.id == Id
        andalso SkillOwner#skill_owner.sign == Sign -> true;
        true -> false
    end,
    CanAtt = 
    if CanAttList =/= [] ->
            case lists:keyfind(Sign, 1, CanAttList) of
                {?BATTLE_SIGN_PLAYER, _AttListA} -> true;
                _ -> false
            end;
        true -> true
    end,
    {NewStateName, NewState} = 
    case Att of
        undefined when IsTrace andalso IsOwner =/=true andalso (Group == 0 orelse Group /= MonGroup)
        andalso Hp > 0 andalso CanAtt == true andalso BattleType == 1 -> %% 新目标
            NewAtt = #{id=>Id, pid=>Pid, sign=>Sign, x=>X, y=>Y},
            % old: Ref = lib_scene_object:send_event_after(State#ob_act.ref, 100, go),
            StateDriven = mon_state_util:conv_delay_driven({100, go}),
            {trace, State#ob_act{att=NewAtt, state_driven = StateDriven}};
        _ -> %% 其他目标
            {StateName, State}
    end,
    {next_state, NewStateName, NewState};

handle_info({'deserted_ripple', AiMap}, StateName, State) ->
    #ob_act{att = Att, klist = Klist, object = SceneObj} = State,
    #scene_object{tracing_distance = TraceDistance, skill_owner = SkillOwner, can_att_list = CanAttList,
        mon = #scene_mon{boss = Boss, d_x = Dx, d_y = Dy}} = SceneObj,
    #{id := RoleId, pos := {TX, TY}, deserted_boss_tired := DesertedBossTired, sign := Sign} = AiMap,
    IsTrace = is_continue_trace(Dx, Dy, TX, TY, TraceDistance),
    IsOwner = 
    if
        SkillOwner#skill_owner.id > 0 andalso SkillOwner#skill_owner.id == RoleId
        andalso SkillOwner#skill_owner.sign == Sign -> true;
        true -> false
    end,    
    CanAtt = 
    if CanAttList =/= [] ->
            case lists:keyfind(Sign, 1, CanAttList) of
                {?BATTLE_SIGN_PLAYER, _AttListA} -> true;
                _ -> false
            end;
        true -> true
    end,
    IsTired = local_boss_api:is_deserted_mon_tried(Boss, DesertedBossTired),
    if
        (not IsTrace) orelse IsOwner orelse (not CanAtt) orelse IsTired ->
            {next_state, StateName, State};
        true ->
            case lists:reverse(lists:keysort(#mon_atter.hurt, Klist)) of
                [#mon_atter{id = RoleId, hurt = Hurt}|_] when Hurt =/= 0 ->
                    case Att of
                        #{id := Id} when Id == RoleId ->
                            {next_state, StateName, State};
                        #{id := Id1} when Id1 =/= RoleId ->
                            NewAtt = Att#{id => RoleId, x => TX, y => TY, sign => Sign},
                            % old: Ref = lib_scene_object:send_event_after(OldRef, 100, go),
                            StateDriven = mon_state_util:conv_delay_driven({100, go}),
                            {next_state, trace, State#ob_act{att = NewAtt, state_driven = StateDriven}};
                        _ ->
                            NewAtt = #{id => RoleId, x => TX, y => TY, sign => Sign},
                            % old: Ref = lib_scene_object:send_event_after(OldRef, 100, go),
                            StateDriven = mon_state_util:conv_delay_driven({100, go}),
                            {next_state, trace, State#ob_act{att = NewAtt, state_driven = StateDriven}}
                    end;
                _ ->
                    {next_state, StateName, State}
            end
    end;

%% 寻找是否有攻击目标
handle_info('find_target', StateName, State) ->
    #ob_act{object=Object, att=Att} = State,
    case Att of
        undefined ->
            find_target_cast(Object),
            {next_state, StateName, State};
        #{id:=Id, x:=X, y:=Y} ->
            handle_info({'trace_info_back', [Id, X, Y, repeat]}, StateName, State);
        _ -> {next_state, StateName, State}
    end;

%% 采集怪
handle_info({'collect_mon', CollectorId, CollectorServerId, CollectorNode, CollectorPid, Type}, StateName, State) ->
    #ob_act{
        object = Minfo, clist = Clist,
        collect_del_hp_list = CLDelHpList,
        general_driven = GeneralDriven
    } = State,
    NowTimeMS = utime:unixtime(ms),
    #scene_object{
        id = ObjId, config_id = ConfigId,
        scene = SceneId, scene_pool_id = ScenePoolId,
        copy_id=CopyId, x=X, y=Y, battle_attr=BA, mon=SceneMon
    } = Minfo,
    #scene_mon{kind = Kind, collect_time = CollectTime,
        collect_times = CollectTimes, collect_count = CollectCount
    } = SceneMon,
    IsCollectMon = collect_api:is_collect_mon(Kind),
    IsIgnoreKind = collect_api:is_no_count_limit(Kind),
    RemainClTimes = CollectCount - CollectTimes,
    if
        BA#battle_attr.hp =< 0 orelse IsCollectMon == false ->
            {next_state, StateName, State};
        Type == ?COLLECT_START ->
            %% 清理过期数据
            {RealClist, _OverTimeClist} = collect_api:valid_collectors(NowTimeMS, CollectTime, lists:keydelete(CollectorId, 1, Clist)),
            ClistLen = length(RealClist),
            case IsIgnoreKind orelse ClistLen + 1 =< RemainClTimes of
                true ->
                    NewClist = [{CollectorId, CollectorPid, NowTimeMS} | RealClist],
                    case not IsIgnoreKind andalso RemainClTimes - ClistLen == 1 of  %% 只剩最后一次
                        true  -> Args = [{has_cltimes, 0}];
                        false -> Args = []
                    end,
                    NewGeneralDriven = collect_api:recalc_del_hp_driven(
                        GeneralDriven, CLDelHpList, SceneMon, NewClist, NowTimeMS
                    ),
                    lib_scene_object:update(Minfo, [{is_collecting, 1} | Args]),
                    {ok, BinData} = pt_200:write(20008, [?ERRCODE(err200_start_collect), ConfigId, ObjId]),
                    lib_battle:rpc_cast_to_node(CollectorNode, lib_server_send, send_to_uid, [CollectorId, BinData]),
                    {next_state, StateName, State#ob_act{clist = NewClist, general_driven = NewGeneralDriven}};
                false -> %% 怪物剩余采集次数不足/他人采集中
                    {ok, BinData} = pt_200:write(20008, [?ERRCODE(err200_be_collecting), ConfigId, ObjId]),
                    lib_battle:rpc_cast_to_node(CollectorNode, lib_server_send, send_to_uid, [CollectorId, BinData]),
                    {next_state, StateName, State#ob_act{clist = RealClist}}
            end;
        Type == ?COLLECT_END -> %% 结束采集
            case lists:keyfind(CollectorId, 1, Clist) of
                false ->
                    %% 并无该角色的采集信息
                    {ok, BinData} = pt_200:write(20008, [?ERRCODE(err200_collec_wrong), ConfigId, ObjId]),
                    lib_battle:rpc_cast_to_node(CollectorNode, lib_server_send, send_to_uid, [CollectorId, BinData]),
                    {next_state, StateName, State};
                {_, _, TimeMS} ->
                    %% 减少50毫秒进行判定，避免时间误差导致采集失败
                    case NowTimeMS - TimeMS >= CollectTime * 1000 - 50 of
                        true ->
                            %% 采集成功
                            lib_mon_event:be_collected(Minfo, CollectorId),
                            {ok, BinDataCLSucc} = pt_200:write(20008, [?ERRCODE(err200_finish_collect), ConfigId, ObjId]),
                            lib_battle:rpc_cast_to_node(CollectorNode, lib_server_send, send_to_uid, [CollectorId, BinDataCLSucc]),
                            case State#ob_act.collected_handler of
                                {CM, CF, CA} -> CM:CF(Minfo, CollectorServerId, CollectorNode, CollectorId, CA);
                                _ -> ignore
                            end,
                            if
                                %% 无限采集，不死亡（不死采集怪或任务采集怪）
                                % Kind == ?MON_KIND_UNDIE_COLLECT orelse
                                Kind == ?MON_KIND_TASK_COLLECT ->
                                    NewClist = lists:keydelete(CollectorId, 1, Clist),
                                    IsCollecting = ?IF(NewClist =/= [], 1, 0),
                                    lib_scene_object:update(Minfo, [{is_collecting, IsCollecting}]),
                                    {next_state, StateName, State#ob_act{clist = NewClist}};
                                CollectTimes + 1 >= CollectCount ->
                                    {ok, BinData} = pt_120:write(12081, [Minfo#scene_object.id, 0]),
                                    lib_server_send:send_to_area_scene(SceneId, ScenePoolId, CopyId, X, Y, BinData),
                                    NewMinfo = Minfo#scene_object{battle_attr = BA#battle_attr{hp=0}, mon=SceneMon#scene_mon{collect_times = 0}},
                                    lib_scene_object:update(NewMinfo, [{hp, 0}]),
                                    %% {NewState, _} = lib_mon_ai:die(State#ob_act{object = NewMinfo}, StateName),
                                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
                                    StateDriven = mon_state_util:conv_delay_driven({100, repeat}, NowTimeMS),
                                    {next_state, dead, State#ob_act{object=NewMinfo, state_driven = StateDriven}};
                                true ->
                                    NewMinfo = Minfo#scene_object{mon=SceneMon#scene_mon{collect_times = CollectTimes + 1}},
                                    NewClist = lists:keydelete(CollectorId, 1, Clist),
                                    IsCollecting = ?IF(NewClist =/= [], 1, 0),
                                    lib_scene_object:update(NewMinfo, [{is_collecting, IsCollecting}]),
                                    {next_state, StateName, State#ob_act{object = NewMinfo, clist = NewClist}}
                            end;
                        false ->
                            %% 采集时间不足
                            {ok, BinData} = pt_200:write(20008, [?ERRCODE(err200_not_enough_time), ConfigId, ObjId]),
                            lib_battle:rpc_cast_to_node(CollectorNode, lib_server_send, send_to_uid, [CollectorId, BinData]),
                            NewClist = lists:keydelete(CollectorId, 1, Clist),
                            case not IsIgnoreKind andalso RemainClTimes - length(NewClist) > 0 of
                                true -> Args = [{has_cltimes, 1}];
                                false -> Args = []
                            end,
                            ?IF(NewClist =/= [],
                                lib_scene_object:update(Minfo, Args),
                                lib_scene_object:update(Minfo, [{is_collecting, 0}|Args])
                            ),
                            {next_state, StateName, State#ob_act{clist = NewClist}}
                    end
            end;
        true ->
            handle_info({'stop_collect', CollectorId}, StateName, State)
    end;

%% 玩家停止采集怪（必须兼顾非采集怪的情况，不能影响非采集怪）
handle_info({'stop_collect', RoleId}, StateName, State) ->
    #ob_act{object = Minfo = #scene_object{mon = SceneMon}, clist = Clist} = State,
    #scene_mon{kind = Kind} = SceneMon,
    case collect_api:is_collect_mon(Kind) of
        true  ->
            case lists:keymember(RoleId, 1, Clist) of
                true  ->
                    NewClist     = lists:keydelete(RoleId, 1, Clist),
                    IsCollecting = ?IF(NewClist =/= [], 1, 0),
                    lib_scene_object:update(Minfo, [{is_collecting, IsCollecting}, {has_cltimes, 1}]),
                    {next_state, StateName, State#ob_act{clist = NewClist}};
                false ->
                    {next_state, StateName, State}
            end;
        _->
            {next_state, StateName, State}
    end;

%% 校正采集物信息  超时无人采集的采集物
handle_info('fix_collect', StateName, State) ->
    #ob_act{object = Minfo, clist = Clist} = State,
    NewClist = collect_api:clean_timeout_collectors(Minfo, Clist),
    %% ?INFO_TTY("mon:~w,MonPid:~w~nOldClist:~w~nNewClist:~w", [Minfo#scene_object.id, Minfo#scene_object.aid, Clist, NewClist]),
    NewState = State#ob_act{clist = NewClist},
    {next_state, StateName, NewState};

%% 采集扣玩家血的怪物，达到扣血时间
handle_info(?COLLECTORS_DEL_HP, StateName, State = #ob_act{
        object = Minfo, clist = Clist,
        collect_del_hp_list = CLDelHpList,
        general_driven = GeneralDriven
    }) ->
    #scene_object{mon = SceneMon} = Minfo,
    NowTime  = utime:unixtime(ms),
    {NewClist, NewCLDelHpList} = collect_api:collectors_del_hp(NowTime, CLDelHpList, Clist, Minfo),
    NewGeneralDriven = collect_api:recalc_del_hp_driven(GeneralDriven, NewCLDelHpList, SceneMon, NewClist, NowTime),
    %% ?INFO_TTY("~nClist:~w~nNewClist:~w~nCLDelHpList:~w~nNewCLDelHpList:~w~n", [Clist, NewClist, CLDelHpList, NewCLDelHpList]),
    NewState = State#ob_act{
        clist = NewClist
        ,collect_del_hp_list = NewCLDelHpList
        ,general_driven = NewGeneralDriven
    },
    {next_state, StateName, NewState};

%% 拾取
handle_info({'pick_mon', CollectorId, _CollectorNode, _CollectorPid}, _StateName, State) ->
    #ob_act{object = Minfo} = State,
    lib_mon_event:be_picked(Minfo, CollectorId),
    % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
    StateDriven = mon_state_util:conv_delay_driven({100, repeat}),
    {next_state, dead, State#ob_act{state_driven = StateDriven}};

%% 反弹伤害.
%% 注意:不要进行赋值,目前只是用于计算伤害值
%% @BattleReturn #battle_return{hurt = AfReBAerHurt, sign = AerRetrunSign, atter = AerRetrunAtter}, 只有这三个字段有效
handle_info({'rebound_battle_info', BattleReturn}, StateName, State) ->
    #battle_return{
        hurt = Hurt,
        sign = AtterSign,
        atter = Atter
    } = BattleReturn,
    #ob_act{object = Minfo} = State,
    lib_mon_event:be_rebound_hurt(Minfo, Atter, AtterSign, Hurt),
    {next_state, StateName, State};

%% 受到攻击
handle_info({'battle_info', BattleReturn}, StateName, State) ->
    #battle_return{
        hp = Hp,
        hurt = FullHurt,
        move_x = MoveX,
        move_y = MoveY,
        attr_buff_list = AttrBuffList,
        other_buff_list = OtherBuffList,
        atter = Atter,
        sign  = AtterSign  %% 1：怪物， 2：人 其他
    } = BattleReturn,
    #battle_return_atter{id = AtterId, pid = AtterPid, real_id=RealId, server_id = AtterServerId, node = AtterNode,
        guild_id = GuildId, real_name = Name, team_id = TeamId, team_leader_name = TeamName
        , x=Xatt, y=Yatt, lv = AtterLv, career = AtterCareer,support_id = SupportId, support_name = SupportName
        ,support_team = SupportTeam,support_team_name = SupportTeamName} = Atter,
    #ob_act{att = OldAtt, object = Minfo, klist = Klist, first_att = FirstAttr, bl_who = OldBLWhos,
        hp_change_handler = HpChangeHandler, hurt_list_handler = HurtListHandler, die_handler = DieHandler, be_hurt_handler = BeHurtHandler,
        move_begin_time = MoveBeginTime, each_move_time = EachMoveTime, o_point = {OX, OY}, support_klist = SupportList} = State,

    #scene_object{x = Xnow, y = Ynow, battle_attr = BA, is_hit_ac = IsHitAc, mon = Mon, scene = Scene, scene_pool_id = PoolId,
        hurt_sync_ids = HurtSyncIds, figure = #figure{lv = MonLv}} = Minfo,

    #scene_mon{boss = Boss, mid = Mid} = Mon,
    %% 最后伤害等于血量
    Hurt = min(FullHurt, Minfo#scene_object.battle_attr#battle_attr.hp),
    MonAtter = #mon_atter{
        id = AtterId, name = Name, pid = AtterPid, server_id = AtterServerId, node = AtterNode, hurt = Hurt, team_leader_name = TeamName,
        team_id = TeamId, att_sign = AtterSign, att_lv = AtterLv, att_career = AtterCareer, guild_id = GuildId,support_id = SupportId
        , support_name = SupportName,support_team = SupportTeam,support_team_name = SupportTeamName},
    %% 第一个攻击的玩家
    {IsFirstAttack, NewFirstAttr} =
    case FirstAttr == [] andalso FullHurt /= 0 of
        true  ->
            {true, MonAtter};
        false ->
            {false, FirstAttr}
    end,
    %% 如果攻击者是人，就加入伤害列表里面
    NewKlist = ?IF(AtterSign == ?BATTLE_SIGN_PLAYER andalso FullHurt =/= 0 andalso Hurt =/=0, add_hatred_list(Klist, MonAtter, Boss, Hurt), Klist),
    %% 如果攻击者是人，就加入到协助列表里
    NewSupportList = ?IF(AtterSign == ?BATTLE_SIGN_PLAYER andalso FullHurt =/= 0 andalso Hurt =/=0 andalso is_supporting_boss(Boss),
        add_support_list(SupportList, MonAtter, Hurt), SupportList),
    DropBoss = ?IS_DROP_BOSS(Boss),
    %% 定时移除离开boss范围的伤害玩家
    OldGeneralDriven = State#ob_act.general_driven,
    GeneralDriven =
    if
        not DropBoss orelse AtterSign == ?BATTLE_SIGN_MON orelse FullHurt == 0 ->
            OldGeneralDriven;
        true ->
            case mon_state_util:check_member_driven_info('hurt_remove', OldGeneralDriven) of
                true  -> OldGeneralDriven;
                false ->
                    case drop:get_boss_drop_bltype(Mid, MonLv) of
                        skip -> OldGeneralDriven;
                        _    ->
                            DelayDrivenInfo = driven_info_hurt_remove(Boss),
                            mon_state_util:update_general_driven(OldGeneralDriven, DelayDrivenInfo)
                    end
            end
    end,
    Minfo1 = Minfo#scene_object{battle_attr = BA#battle_attr{
            hp = max(0, Hp),
            attr_buff_list = AttrBuffList,
            other_buff_list = OtherBuffList}
    },
    NowTimeMS = utime:unixtime(ms),
    %% 赏金BOSS特殊处理
    Minfo2 =
    case Boss of
        ?MON_REWARD_BOSS ->
            lib_mon_event:update_boss_hit(Minfo1, NowTimeMS);
        _ ->
            Minfo1
    end,
    StateHpBuff = State#ob_act{
        object = Minfo2, general_driven = GeneralDriven,
        first_att = NewFirstAttr,
        klist = NewKlist, support_klist = NewSupportList
    },
    %% 更新怪物新归属者
    NewBLWhos = if
                    AtterSign == ?BATTLE_SIGN_MON orelse FullHurt == 0 ->
                        OldBLWhos;
                    true ->
                        update_belong_who(StateHpBuff, OldBLWhos, NewKlist, NewFirstAttr)
                end,
    State0 = StateHpBuff#ob_act{bl_who = NewBLWhos},
    %% 血量变化监听器
    {AfterHpChangeState, NewHpChangeHandler} = case HpChangeHandler of
        {Mod, Fun, Args} ->
            case Mod:Fun(State0, MonAtter, Args) of
                {ok, NewArgs} -> 
                    {State0, {Mod, Fun, NewArgs}};
                {ok, NewMod, NewFun, NewArgs} -> 
                    {State0, {NewMod, NewFun, NewArgs}};
                {ok, NewState0 = #ob_act{}, {NewMod, NewFun, NewArgs}} ->
                    {NewState0, {NewMod, NewFun, NewArgs}};
                NewState0 = #ob_act{} ->
                    {NewState0, {Mod, Fun, Args}};
                stop -> 
                    {State0, []};
                _ -> 
                    {State0, HpChangeHandler}
            end;
        _ -> 
            {State0, HpChangeHandler}
    end,
    %% 受击监听器
    AfterHurtHandler =
    case BeHurtHandler of
        {BeHurtM, BeHurtF, BeHurtA} ->
            case catch BeHurtM:BeHurtF(AfterHpChangeState, MonAtter, IsFirstAttack, BeHurtA) of
                {'EXIT', BeHurtErr} ->
                    ?ERR("apply ~p error ~p~n", [BeHurtHandler, BeHurtErr]),
                    AfterHpChangeState;
                #ob_act{} = HurtReturnState -> HurtReturnState;
                _ ->
                    AfterHpChangeState
            end;
        _ -> AfterHpChangeState
    end,
    State1 = AfterHurtHandler#ob_act{hp_change_handler = NewHpChangeHandler},
    %% 血量同步怪
    if
        HurtSyncIds =/=[] ->
            SyncAttrList = [{hurt_sync, Hurt, Hp}],
            [lib_scene_object:change_attr_by_ids(Scene, SyncPoolId, [SyncMonId], SyncAttrList)||{SyncPoolId, SyncMonId} <- HurtSyncIds];
        true -> skip
    end,
    %% 判断是否强制广播血量
    lib_scene_object:force_broad_cast_mon_hp(Minfo2),
    %% 第一次伤害，立即广播一次伤害列表
    if
        IsFirstAttack ->
            case HurtListHandler of
                {FHandlerM, FHandlerF, FHandlerA} ->
                    case catch FHandlerM:FHandlerF(Minfo2, NewKlist, FHandlerA) of
                        {'EXIT', FHurtErr} -> ?ERR("apply ~p error ~p~n", [HurtListHandler, FHurtErr]);
                        _ -> ok
                    end;
                _ -> skip
            end;
        true ->  skip
    end,
    lib_mon_event:be_hurted(Minfo2, Minfo, Atter, AtterSign, Hurt, IsFirstAttack),
    lib_mon_event:hp_change(Minfo2, Minfo, NewKlist),
    {HpAiState, HpAiStateName} = lib_mon_ai:hp_change(BA#battle_attr.hp, Hp, State1, null),
    #ob_act{object=MinfoAfHpAi} = HpAiState,
    if
        Hp =< 0 -> %% 死亡掉落，功能杀怪物处理
            lib_mon_event:be_killed(MinfoAfHpAi, NewKlist, Atter, AtterSign, NewFirstAttr, NewBLWhos),
            {AiState, _} = lib_mon_ai:die(HpAiState, Atter, StateName),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
            StateDriven = mon_state_util:conv_delay_driven({100, repeat}, NowTimeMS),
            case HurtListHandler of
                {HandlerM, HandlerF, HandlerA} ->
                    case catch HandlerM:HandlerF(Minfo2, NewKlist, HandlerA) of
                        {'EXIT', HurtErr} -> ?ERR("apply ~p error ~p~n", [HurtListHandler, HurtErr]);
                        _ -> ok
                    end;
                _ -> skip
            end,
            case DieHandler of
                {DMod, DFun, DArgs} ->
                    case catch DMod:DFun(AiState, NewKlist, Atter, AtterSign, DArgs) of
                        {'EXIT', Err} -> ?ERR("apply ~p error ~p~n", [DieHandler, Err]);
                        _ -> ok
                    end;
                _ -> ok
            end,
            {next_state, dead, AiState#ob_act{state_driven = StateDriven}};
        true ->
            if
                (HpAiStateName==null orelse HpAiStateName == trace) andalso
                MinfoAfHpAi#scene_object.is_fight_back == 1 -> %% 是否反击
                    NewAttTarget =
                    if
                        Boss == ?MON_DESERTED_BOSS orelse
                         Boss == ?MON_HOME_BOSS orelse Boss == ?MON_HOME_GBOSS -> %% 取仇恨最高玩家作为攻击目标
                            case lists:reverse(lists:keysort(#mon_atter.hurt, Klist)) of
                                [#mon_atter{id = RoleId}|_] ->
                                    case OldAtt of
                                        #{id := RoleId} -> OldAtt;
                                        _   ->
                                            #{id => RoleId, sign => ?BATTLE_SIGN_PLAYER}                                                
                                    end;
                                _ -> 
                                    #{id => RealId, x => Xatt, y => Yatt, sign => AtterSign}
                            end;
                        OldAtt == undefined -> 
                            #{id=>RealId, x=>Xatt, y=>Yatt, sign=>AtterSign};
                        true -> 
                            OldAtt
                    end,

                    {IsPlayHitAc, UpX, UpY} =
                    if
                        MoveX /= 0 orelse MoveY /= 0 ->
                            {false, MoveX, MoveY};
                        IsHitAc == 1 ->
                            % NowTime = utime:longunixtime(),
                            if
                                MoveBeginTime == 0 orelse MoveBeginTime+EachMoveTime < NowTimeMS ->
                                    {false, Xnow, Ynow};
                                true ->
                                    R = (MoveBeginTime+EachMoveTime-NowTimeMS) / EachMoveTime,
                                    { true, round((OX-Xnow)*R) + Xnow, round((OY-Ynow)*R) + Ynow }
                            end;
                        true -> {false, Xnow, Ynow}
                    end,
                    if
                        StateName == sleep orelse StateName == back orelse
                        StateName == patrol orelse StateName == walk_auto ->
                            % old:
                            % Ref1 = case IsPlayHitAc of
                            %     true  -> lib_scene_object:send_event_after(Ref, ?STOP_TIME, go);
                            %     false -> lib_scene_object:send_event_after(Ref, 0, go)
                            % end,
                            StateDriven = case IsPlayHitAc of
                                true  -> mon_state_util:conv_delay_driven({?STOP_TIME, go}, NowTimeMS);
                                false -> mon_state_util:conv_delay_driven({0, go}, NowTimeMS)
                            end,
                            NewState = HpAiState#ob_act{
                                att=NewAttTarget,
                                state_driven = StateDriven,
                                move_begin_time=0
                            },
                            {next_state, trace, NewState};
                        %% 宝宝副本怪物直接找敌人
                        StateName == walk andalso Mon#scene_mon.kind == ?MON_KIND_WALK_AUTO ->
                            mod_scene_agent:apply_cast(Scene, PoolId,
                                lib_scene_agent, get_trace_info_cast,
                                [Minfo#scene_object.aid, Minfo#scene_object.id, ?BATTLE_SIGN_PLAYER, AtterId, repeat]
                            ),
                            HpAiState1 = HpAiState#ob_act{att=NewAttTarget},
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, 5000, repeat),
                            NewState = mon_state_util:update_state_driven(HpAiState1, {5000, repeat}, NowTimeMS),
                            {next_state, walk, NewState};
                        IsPlayHitAc ->
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, ?STOP_TIME, repeat),
                            StateDriven = mon_state_util:conv_delay_driven({?STOP_TIME, repeat}, NowTimeMS),
                            MonInfoPlayerHitAc = MinfoAfHpAi#scene_object{x=UpX, y=UpY},
                            NewState = HpAiState#ob_act{
                                object = MonInfoPlayerHitAc,
                                att = NewAttTarget,
                                state_driven = StateDriven,
                                move_begin_time=0
                            },
                            {next_state, StateName, NewState};
                        true ->
                            {next_state, StateName, HpAiState#ob_act{att=NewAttTarget}}
                    end;
                HpAiStateName /= null -> %% hp_ai影响了怪物行为
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 0, repeat),
                    NewState = mon_state_util:update_state_driven(HpAiState, {0, repeat}, NowTimeMS),
                    {next_state, HpAiStateName, NewState};
                true -> %% 其余不反击的怪物，保持现状
                    {next_state, StateName, HpAiState}
            end
    end;

handle_info({'object_assist_back', Res}, StateName, State) ->
    #ob_act{selected_skill = SelectSkillMap, general_driven = OldGeneralDriven, object = SceneObject} = State,
    case Res of
        #skill_return{used_skill = SkillId} ->
            case maps:find(SkillId, SelectSkillMap) of
                {ok, SelectedSkillInfo} when is_record(SelectedSkillInfo, selected_skill) ->
                    {Next, Args} = lib_scene_object_ai:handle_assist_res(SelectedSkillInfo, SceneObject, OldGeneralDriven, Res),
                    AfBattleState = lib_scene_object_ai:set_ac_state(Args, State),
                    NextStateName0 = case Next of
                        trace -> trace;
                        _     -> back
                    end,
                    SceneMon = SceneObject#scene_object.mon,
                    NextStateName = case SceneMon#scene_mon.kind of
                                        ?MON_KIND_WALK_AUTO -> walk;
                                        _ -> NextStateName0
                                    end,
                    {next_state, NextStateName, AfBattleState};
                _ ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
                    StateDriven = mon_state_util:conv_delay_driven({1000, repeat}),
                    {next_state, StateName, State#ob_act{state_driven = StateDriven}}
            end;
        _ ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1000, repeat}),
            {next_state, StateName, State#ob_act{state_driven = StateDriven}}
    end;

handle_info({'object_battle_back', Res}, StateName, State) ->
    #ob_act{selected_skill = SelectSkillMap, general_driven = GeneralDriven, object = SceneObject} = State,
    case Res of
        #skill_return{used_skill = SkillId} ->
            case maps:find(SkillId, SelectSkillMap) of
                {ok, SelectedSkillInfo} when is_record(SelectedSkillInfo, selected_skill) ->
                    {Next, Args} = lib_scene_object_ai:handle_battle_res(SelectedSkillInfo, SceneObject, GeneralDriven, Res),
                    AfBattleState = lib_scene_object_ai:set_ac_state(Args, State),
                    NextStateName0 =
                    case Next of
                        trace -> trace;
                        _     -> back
                    end,
                    SceneMon = SceneObject#scene_object.mon, 
                    case SceneMon#scene_mon.kind of
                        ?MON_KIND_WALK_AUTO ->
                            NextStateName = walk,
                            StateDriven = mon_state_util:conv_delay_driven({3000, repeat}),
                            % AfBattleState0 = lib_dungeon_child:mon_with_new_path(AfBattleState),
                            AfBattleState1 = AfBattleState#ob_act{state_driven = StateDriven};
                        _ ->
                            NextStateName = NextStateName0,
                            AfBattleState1 = AfBattleState
                    end,
                    {next_state, NextStateName, AfBattleState1};
                _ ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
                    StateDriven = mon_state_util:conv_delay_driven({1000, repeat}),
                    {next_state, StateName, State#ob_act{state_driven = StateDriven}}
            end;
        _ ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1000, repeat}),
            {next_state, StateName, State#ob_act{state_driven = StateDriven}}
    end;


%% 设置战斗状态
handle_info({'battle_attr', BA}, StateName, State) ->
    #ob_act{object=Minfo} = State,
    {next_state, StateName, State#ob_act{object=Minfo#scene_object{battle_attr=BA}}};

%% 设置buff
handle_info({set_mon_buff, AttrBuffL, OtherBuffL}, StateName, State) ->
    #ob_act{object = Minfo = #scene_object{battle_attr = BA}} = State,
    NewBA = BA#battle_attr{attr_buff_list = AttrBuffL, other_buff_list = OtherBuffL},
    MinInfo2 = Minfo#scene_object{battle_attr = NewBA},
    {next_state, StateName, State#ob_act{object = MinInfo2}};
%% 统计自己创建的小怪死亡数量，如果数量足够了取消buff
handle_info({clean_buff_stat, MonTypeId}, StateName, State) ->
    #ob_act{ai_event = AiEvent, object = Object} = State,
    #scene_object{id = Id, scene = Scene, scene_pool_id = PoolId} = Object,
    Key = {clean_buff_stat, MonTypeId},
    State2 = case lists:keytake(Key, 1, AiEvent) of
        {value, {Key, {MonTypeId, Num, SkillIdList}}, RestList} ->
            NewNum = max(0, Num - 1),
            AiEvent2 = case NewNum =< 0 andalso Num > 0 of
                true ->
                    mod_scene_agent:apply_cast_with_scene(
                        Scene, PoolId, lib_skill_buff, clean_mon_buff, [Id, SkillIdList]
                    ),
                    RestList;
                _ ->
                    [{Key, {MonTypeId, NewNum, SkillIdList}} | RestList]
            end,
            State#ob_act{ai_event = AiEvent2};
        _ ->
            State
    end,
    {next_state, StateName, State2};
%% 追踪目标返回
handle_info({'trace_info_back', _}, StateName = accumulate_power, State) ->
    {next_state, StateName, State};

handle_info({'trace_info_back', [Id, X, Y, Msg|_]}, StateName, State) ->
    #ob_act{
        att=Att, object = Minfo, skill_link_info = SkillLinkInfo,
        next_att_time = NextAttTime, next_move_time = NextMoveTime,
        check_block = CheckBlock, w_point = {_OX, _OY}
    } = State,
    #scene_object{mon=Mon, tracing_distance=TracingDistance, x = MX, y = MY} = Minfo,
    #scene_mon{d_x=Dx, d_y=Dy, kind = Kind} = Mon,
    case Att of
        #{id := Id, sign := Sign} ->
            if
                Kind =:= ?MON_KIND_ATT orelse Kind == ?MON_KIND_ATT_AUTO
                    orelse Kind == ?MON_KIND_WALK_AUTO ->
                    Dx1 = MX,
                    Dy1 = MY;
                true ->
                    Dx1 = Dx,
                    Dy1 = Dy
            end,
            case is_continue_trace(Dx1, Dy1, X, Y, TracingDistance) of
                false when Kind == ?MON_KIND_WALK_AUTO -> %% 宝宝副本
                    StateDriven = mon_state_util:conv_delay_driven({3000, repeat}),
                    {next_state, walk, State#ob_act{att=undefined, state_driven = StateDriven}};
                false ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 500, repeat),
                    StateDriven = mon_state_util:conv_delay_driven({500, repeat}),
                    {next_state, back, State#ob_act{att=undefined, state_driven = StateDriven}};
                _R ->
                    NowTime = utime:longunixtime(),
                    case Kind == ?MON_KIND_WALK_AUTO of
                        true -> lib_dungeon_child:trace_to_att(
                                    State, X, Y,
                                    {target, Sign, Id},
                                    NextAttTime, NextMoveTime, NowTime, SkillLinkInfo, CheckBlock
                                );
                        _ ->
                            {Next, Args}  = lib_scene_object_ai:trace_to_att(
                                                        Minfo, X, Y,
                                                        {target, Sign, Id},
                                                        NextAttTime, NextMoveTime, NowTime, SkillLinkInfo, CheckBlock
                                                        ),
                            %% io:format("~p ~p {Next, Args} :~w~n", [?MODULE, ?LINE, {Next, Args}]),
                            %% 这里有可能会丢失目标！！ 既是：att = undefined
                            AfBattleState = lib_scene_object_ai:set_ac_state(Args, State),
                            NextStateName = case Next of
                                trace -> trace;
                                back  -> back;
                                _     -> back
                            end,
                            {AIState, AIStateName} = case Msg of
                                go -> lib_mon_ai:wake_up(AfBattleState, NextStateName);
                                _  -> {AfBattleState, NextStateName}
                            end,
                            {next_state, AIStateName, AIState}
                    end
            end;
        % trace(Msg, State#ob_act{att = Att#{x => X, y => Y}});
        _ ->
            {next_state, StateName, State}
    end;
%% 追踪目标返回 找不到目标
handle_info({'trace_info_back', _}, StateName, State) ->
    case StateName of
        back ->
            % old: Ref1 = lib_scene_object:send_event_after(State#ob_act.ref, 1500, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1500, repeat}),
            NewState = State#ob_act{att = undefined, state_driven = StateDriven},
            NewStateName = back,
            {next_state, NewStateName, NewState};
        _ ->
            {next_state, StateName, State}
    end;
%% 设置buff
handle_info({'buff', AttrBuffL, OtherBuffL}, StateName, State) ->
    #ob_act{object=#scene_object{battle_attr=BA}=Object} = State,
    {next_state, StateName, State#ob_act{object=Object#scene_object{battle_attr=BA#battle_attr{attr_buff_list=AttrBuffL, other_buff_list=OtherBuffL}}}};

%% 清理怪物
handle_info({'stop', Broadcast}, _StateName, State) ->
    % old: util:cancel_timer(State#ob_act.ref),
    lib_scene_object:stop(State#ob_act.object, Broadcast),
    lib_mon_event:be_stop(State#ob_act.object),
    {stop, normal, State};

%% 杀死怪物
handle_info('die', StateName, State) ->
    % util:cancel_timer(State#ob_act.ref),
    lib_scene_object:die(State#ob_act.object, 1),
    lib_mon_event:be_killed_direct(State#ob_act.object),
    lib_mon_ai:die(State, none, StateName),
    {stop, normal, State};

%% 持续施放副技能
handle_info({'combo', Args}, StateName, State) ->
    State1 = lib_scene_object_ai:combo(Args, State),
    {next_state, StateName, State1};

% %% 持续施放特殊副技能
% handle_info({'find_target_combo', Args}, StateName, State) ->
%     State1 = lib_scene_object_ai:find_target_combo(Args, State),
%     {next_state, StateName, State1};

handle_info({'combo_battle_back', Res}, StateName, State) ->
    case Res of
        #skill_return{used_skill = SkillId, aer_info = BackData} ->
            case maps:find(SkillId, State#ob_act.selected_skill) of
                {ok, SelectedSkillInfo} when is_record(SelectedSkillInfo, selected_skill) ->
                    NewState = lib_scene_object_ai:battle_combo_res(SelectedSkillInfo, State, Res),
                    {next_state, StateName, NewState};
                _ ->
                    #ob_act{object = Object} = State,
                    BackObj = lib_battle_api:update_by_slim_back_data(Object, BackData),
                    {next_state, StateName, State#ob_act{object = BackObj}}
            end;
        _ ->
            {next_state, StateName, State}
    end;

handle_info({'combo_assist_back', Res}, StateName, State) ->
    case Res of
        #skill_return{used_skill = SkillId, aer_info = BackData} ->
            case maps:find(SkillId, State#ob_act.selected_skill) of
                {ok, SelectedSkillInfo} when is_record(SelectedSkillInfo, selected_skill) ->
                    NewState = lib_scene_object_ai:assist_combo_res(SelectedSkillInfo, State, Res),
                    {next_state, StateName, NewState};
                _ ->
                    #ob_act{object = Object} = State,
                    BackObj = lib_battle_api:update_by_slim_back_data(Object, BackData),
                    {next_state, StateName, State#ob_act{object = BackObj}}
            end;
        _ ->
            {next_state, StateName, State}
    end;

%% 处理怪物的AI定时事件
handle_info({'special_event', Events}, StateName, State) ->
    NowTimeMS = utime:unixtime(ms),
    {NewState, NewStateName} = lib_mon_ai:do_ai_list(Events, State, NowTimeMS, StateName, none),
    %%lib_scene_object:insert(NewState#ob_act.object),
    % old:
    % Ref1 = case NewStateName /= StateName of
    %     true  -> lib_scene_object:send_event_after(State#ob_act.ref, 100, repeat);
    %     false -> NewState#ob_act.ref
    % end,
    StateEnd =
    case NewStateName =/= StateName of
        true  -> mon_state_util:update_state_driven(NewState, {100, repeat}, NowTimeMS);
        false -> NewState
    end,
    {next_state, NewStateName, StateEnd};

%% 处理怪物特殊AI定时事件
handle_info({ai_kill_scene_role, RoleNum}, StateName, State) ->
    lib_mon_ai:kill_scene_role(State, RoleNum),
    {next_state, StateName, State};

handle_info('hurt_remove', StateName, State) ->
    #ob_act{object=Object, klist = Klist} = State,
    #scene_object{
        aid = Pid, id = ObjectId,
        d_x = DX, d_y = DY, scene = Scene, scene_pool_id = PoolId,
        mon = #scene_mon{boss = Boss},
        copy_id = CopyId,
        tracing_distance = TracingDistance
    } = Object,
    %% 赏金BOSS范围自定义
    IsRewardBoss = local_boss_api:is_reward_boss_scene(Scene),
    if
        IsRewardBoss ->
            mod_scene_agent:apply_cast(Scene, PoolId,
                lib_scene_agent, get_scene_near_user_whole_scene,
                ['hurt_remove', Pid, ObjectId, CopyId]
            );
        true ->
            mod_scene_agent:apply_cast(Scene, PoolId,
                lib_scene_agent, get_scene_near_user,
                ['hurt_remove', Pid, ObjectId, CopyId, DX, DY, TracingDistance]
            )
    end,
    if
        StateName == sleep -> %% 休眠
            % old:
            % if
            %     Klist == [] -> 
            %         NewHurtRemoveRef = [];
            %     true -> 
            %         NewHurtRemoveRef = update_new_remove_ref(Boss, RemoveRef)
            % end,
            #ob_act{general_driven = GeneralDriven} = State,
            NewGeneralDriven =
            if
                Klist == [] ->
                    mon_state_util:remove_driven_info('hurt_remove', GeneralDriven);
                true ->
                    DelayDrivenInfo = driven_info_hurt_remove(Boss),
                    mon_state_util:update_general_driven(GeneralDriven, DelayDrivenInfo)
            end,
            {next_state, StateName, State#ob_act{general_driven = NewGeneralDriven}};
        true ->
            % old: NewHurtRemoveRef = update_new_remove_ref(Boss, RemoveRef),
            DelayDrivenInfo = driven_info_hurt_remove(Boss),
            NewState = mon_state_util:update_general_driven(State, DelayDrivenInfo),
            NewState1 =
            case Boss of
                ?MON_REWARD_BOSS ->
                    lib_mon_event:update_notice_count_interval(NewState);
                _ ->
                    NewState
            end,
            {next_state, StateName, NewState1}
    end;

%% 移除不在BOSS范围的伤害列列表：世界BOSS不清空伤害列表
%% NOTE: 此消息是自驱动的AI消息'hurt_remove'产生的外部二次消息
handle_info({'hurt_remove', UsersIds}, StateName, State) ->
    #ob_act{klist = Klist, object = Object, support_klist = SupportList} = State,
    #scene_object{mon = #scene_mon{boss = BossType}} = Object,
    case BossType of
        ?MON_WORLD_BOSS ->
            {next_state, StateName, State};
        _ ->
            LeaveIds = [MonAtter#mon_atter.id || MonAtter <- Klist, not lists:member(MonAtter#mon_atter.id, UsersIds)],
            LeaveSupport = [MonAtter#mon_atter.id || MonAtter <- SupportList, not lists:member(MonAtter#mon_atter.id, UsersIds)],
            AllLeaveIds = LeaveSupport ++ LeaveIds,
            case AllLeaveIds =:= [] of
                true  ->
                    {next_state, StateName, State};
                false ->
                    if BossType == ?MON_DESERTED_BOSS ->
                            NewState = deserted_remove_hurt_list(LeaveIds, LeaveSupport, State);
                        true ->
                            NewState = remove_hurt_list(AllLeaveIds, State)
                    end,
                    {next_state, StateName, NewState}
            end
    end;

%% 广播伤害
handle_info('broadcast_hurt_list', StateName, State) ->
    #ob_act{
        object = Minfo,
        hurt_list_broadcast = HurtListBroadcast,
        hurt_list_handler = HurtListHandler
    } = State,
    DelayTime = case HurtListBroadcast of
        undefined -> 50;
        Time      -> Time
    end,
    case HurtListHandler of
        {HandlerM, HandlerF, HandlerA} ->
            HandlerM:HandlerF(Minfo, State#ob_act.klist, HandlerA);
        _ -> skip
    end,
    lib_mon_event:broadcast_hurt_list(State#ob_act.object, State#ob_act.klist),
    % old: HurtRef = lib_mon_ai:send_after(DelayTime*1000, self(), 'broadcast_hurt_list', State#ob_act.hurt_ref),
    DelayGeneralDriven = [{DelayTime * 1000, 'broadcast_hurt_list'}],
    NewState = mon_state_util:update_general_driven(State, DelayGeneralDriven),
    {next_state, StateName, NewState};

%% 循环周期检测
handle_info('cycle_check', StateName, State) ->
    #ob_act{cycle_check_handler = CycleCheckHandler} = State,
    case CycleCheckHandler of
        {HandlerM, HandlerF, [Time|_] = HandlerA} ->
            HandlerM:HandlerF(State, StateName, HandlerA),
            % old: CycleCheckRef = lib_mon_ai:send_after(Time*1000, self(), 'cycle_check', OldRef),
            DelayGeneralDriven = [{Time * 1000, 'cycle_check'}],
            NewState = mon_state_util:update_general_driven(State, DelayGeneralDriven),
            {next_state, StateName, NewState};
        _ ->
            {next_state, StateName, State}
    end;

%% 怪物随机走路
handle_info('auto_move', sleep, State) ->
    #ob_act{object=Object} = State,
    #scene_object{mon=#scene_mon{kind=Kind}} = Object,
    case Kind of
        ?MON_KIND_NORMAL ->
            {NewObject, _Time} = lib_scene_object_ai:auto_move(Object),
            {next_state, sleep, State#ob_act{object=NewObject}};
        _ ->
            {next_state, sleep, State}
    end;

%% 睡觉
handle_info('sleep', _StateName, State) ->
    {next_state, sleep, State};

%% 找目标追踪
handle_info({'get_for_trace', Res}, StateName, State) ->
    #ob_act{att=Att, state_driven = OldStateDriven} = State,
    case Res of
        {TgSign, TgId, TgPid, TgX, TgY}->
            case Att of
                undefined ->
                    % old:
                    % if
                    %     not is_reference(Ref) -> ...;
                    %     true ->
                    %         case erlang:read_timer(Ref) of
                    %             Time when is_integer(Time) andalso Time > 100 -> ...;
                    %             _ -> ...
                    %         end
                    % end;
                    case OldStateDriven of
                        {TimeMS, _} when TimeMS > 0 ->
                            NowTimeMS = utime:unixtime(ms),
                            RemainTime = TimeMS - NowTimeMS,
                            case RemainTime > 100 of
                                true  ->
                                    % old: Ref1 = lib_scene_object:send_event_after(Ref, RemainTime, go),
                                    StateDriven = mon_state_util:conv_delay_driven({RemainTime, go}, NowTimeMS),
                                    NewState = State#ob_act{
                                        att=#{id=>TgId, sign=>TgSign, pid=>TgPid, x=>TgX, y=>TgY},
                                        state_driven = StateDriven
                                    },
                                    {next_state, trace, NewState};
                                _ ->
                                    NewState = State#ob_act{att=#{id=>TgId, sign=>TgSign, pid=>TgPid, x=>TgX, y=>TgY}},
                                    Msg = if
                                        StateName =:= trace -> repeat;
                                        true -> go
                                    end,
                                    handle_info({'trace_info_back', [TgId, TgX, TgY, Msg]}, StateName, NewState)
                            end;
                        _ ->    % {0, _} / error format
                            NewState = State#ob_act{att=#{id=>TgId, sign=>TgSign, pid=>TgPid, x=>TgX, y=>TgY}},
                            Msg = if
                                StateName =:= trace -> repeat;
                                true -> go
                            end,
                            handle_info({'trace_info_back', [TgId, TgX, TgY, Msg]}, StateName, NewState)
                            % Ref1 = lib_scene_object:send_event_after(Ref, 0, go),
                            % State#ob_act{att=#{id=>TgId, sign=>TgSign, pid=>TgPid, x=>TgX, y=>TgY}, ref = Ref1};
                    end;
                _ ->
                    {next_state, StateName, State}
            end;
        _ ->
            SleepTime = 3500,
            if
                StateName =:= sleep ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, SleepTime, repeat),
                    StateDriven = mon_state_util:conv_delay_driven({SleepTime, repeat}),
                    {next_state, sleep, State#ob_act{state_driven = StateDriven}};
                StateName =:= walk_auto ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 1500, repeat),
                    StateDriven = mon_state_util:conv_delay_driven({1500, repeat}),
                    {next_state, StateName, State#ob_act{state_driven = StateDriven}};
                true ->
                    % Ref1 = lib_scene_object:send_event_after(Ref, SleepTime, repeat),
                    {next_state, StateName, State}
            end
    end;

%% 离开或者加入队伍操作
handle_info({'team_flag', RoleId, TeamId, LeaderName}, StateName, State)->
    #ob_act{object=Object, bl_who = BLWhos, klist = KList, first_att = FirstAttr} = State,
    #scene_object{mon = #scene_mon{boss = BossType}} = Object,
    NewKList = case lists:keyfind(RoleId, #mon_atter.id, KList) of
        false -> KList;
        P ->
            NP = P#mon_atter{team_id = TeamId, team_leader_name = LeaderName},
            lists:keystore(RoleId, #mon_atter.id, KList, NP)
    end,
    NewFirstAttr = case FirstAttr of
        [] -> [];
        _ -> ?IF(FirstAttr#mon_atter.id == RoleId, FirstAttr#mon_atter{team_id = TeamId}, FirstAttr)
    end,
    DropBoss = ?IS_DROP_BOSS(BossType),
    if
        not DropBoss ->
            {next_state, StateName, State#ob_act{klist = NewKList, first_att = NewFirstAttr}};
        true ->
            %% 重新计算Boss归属
            NewBLWhos = update_belong_who(State, BLWhos, NewKList, NewFirstAttr),
            NewState = State#ob_act{bl_who = NewBLWhos, klist = NewKList, first_att = NewFirstAttr},
            if NewKList =/= KList ->
                    NewState2 = local_boss_api:restat_hurt_list(BossType, NewState);
                true ->
                    NewState2 = NewState 
            end,
            {next_state, StateName, NewState2}
    end;

%% 安全区复活
handle_info({'role_revive', RoleId}, StateName, State)->
    #ob_act{object=Object, klist = KList} = State,
    #scene_object{mon = #scene_mon{boss = Boss}} = Object,
    if
        Boss =:= ?MON_REWARD_BOSS  ->
            %% 赏金BOSS减少20%的伤害
            NewKList = case lists:keyfind(RoleId, #mon_atter.id, KList) of
                #mon_atter{hurt = Hurt} = KOne ->
                    NewKOne = KOne#mon_atter{hurt = round(0.8*Hurt)},
                    lists:keyreplace(RoleId, #mon_atter.id, KList, NewKOne);
                _ ->
                    KList
            end,
            NewState = reward_boss_mgr:re_statistics_hurt_list_by_team(Boss, State#ob_act{klist = NewKList}),
            {next_state, StateName, NewState};
        true ->
            {next_state, StateName, State}
    end;

%% 玩家第一次进入场景
handle_info({'enter', RoleId, _RolePid, _Sign}, StateName, State) ->
    #ob_act{object=Object, bl_who = BLWhos} = State,
    #scene_object{mon = Mon, scene = Scene, scene_pool_id = PoolId, copy_id = CopyId, x = Mx, y = My} = Object,
    case Object#scene_object.type == 1 of
        true ->
            DropBoss = ?IS_DROP_BOSS(Mon#scene_mon.boss),
            if
                BLWhos == [] orelse not DropBoss->
                    {next_state, StateName, State};
                true ->
                    case lists:keyfind(RoleId, #mon_atter.id, BLWhos) of
                        false -> skip;
                        MonAtter ->
                            {ok, Bin} = pt_120:write(12022, [RoleId, 1]),
                            lib_battle:rpc_cast_to_node(MonAtter#mon_atter.node, lib_server_send, send_to_uid, [RoleId, Bin]),
                            lib_server_send:send_to_area_scene(Scene, PoolId, CopyId, Mx, My, Bin),
                            mod_scene_agent:update(Scene, PoolId, RoleId, [{bl_who, 1}])
                    end,
                    {next_state, StateName, State}
            end;
        false -> {next_state, StateName, State}
    end;

%% 玩家离开场景(复活，切场景之类) 世界boss不能移除伤害列表
handle_info({'leave', RoleId, _RolePid, _Sign}, StateName, State) ->
    #ob_act{object=Object} = State,
    #scene_object{mon = Mon} = Object,
    DropBoss = ?IS_DROP_BOSS(Mon#scene_mon.boss),
    if
        not DropBoss ->
            {next_state, StateName, State};
        Mon#scene_mon.boss =:= ?MON_WORLD_BOSS ->
            {next_state, StateName, State};
        true ->
            #scene_mon{mid = Mid, boss = BossType} = Mon,
            if  BossType == ?MON_DESERTED_BOSS orelse
                BossType == ?MON_DESERTED_ELITE orelse
                BossType == ?MON_HOME_BOSS orelse
                BossType == ?MON_HOME_GBOSS ->
                    notice_leave_hate_range([RoleId], BossType, Mid);
                true ->
                    skip
            end,
            NewState1 = remove_hurt_list([RoleId], State),
            {next_state, StateName, NewState1}
    end;

%% 释放技能
handle_info({ac_skill, SkillId, SkillLv}, StateName, State) ->
    NewState = lib_scene_object_ai:object_ac_skill(State, SkillId, SkillLv),
    {next_state, StateName, NewState};

%% 移动
handle_info({move, X, Y}, StateName, State) ->
    #ob_act{object = Object} = State,
    #scene_object{battle_attr = #battle_attr{speed=Speed}} = Object,
    case lib_scene_object_ai:move(X, Y, Object, Speed, false) of
        {true, NewObject, _LastTime} ->
            {next_state, StateName, State#ob_act{object = NewObject}};
        _ ->
            {next_state, StateName, State}
    end;

handle_info({'mod', Moudle, Method, Args}, StateName, State)->
    case catch apply(Moudle, Method, [State|Args]) of
        NewState when is_record(NewState, ob_act) ->
            {next_state, StateName, NewState};
        {NextStateName, NewState} when is_record(NewState, ob_act) ->
            {next_state, NextStateName, NewState};
        {'EXIT', _Err} ->
            ?ERR("~w:mod error  Moudle = ~p, Method = ~p, Args = ~p ~n", [?MODULE, Moudle, Method, _Err]),
            {next_state, StateName, State};
        _ ->
            {next_state, StateName, State}
    end;

handle_info(_Info, StateName, State) ->
    {next_state, StateName, State}.

terminate(Reason, _StateName, _Status = #ob_act{object = Minfo}) ->
    cancle_special_event(_Status),
    #scene_object{id = ObjId,
        scene = SceneId, scene_pool_id = PoolId, copy_id = CopyId,
        config_id = ConfigId
    } = Minfo,
    terminate_gc_proc_dict(ObjId),
    case Reason of
        normal -> ignore;
        _ ->
            ?ERR("scene[~w,~w,~w] obj[~w] terminate state[~w], error:~n~p",
                [SceneId, PoolId, CopyId, ConfigId, _StateName, Reason]
            )
    end,
    ok;
terminate(_Reason, _StateName, _StateData) ->
    ?ERR("scene obj data error:~w", [_StateData]),
    ok.

%% =========处理怪物所有状态=========
%%返回默认出生点
back(_R, State) ->
    #ob_act{object = Minfo, klist=Klist, back_dest_path=BackDestPath, path = Path} = State,
    #scene_object{battle_attr=BA, x = X, y = Y, mon=#scene_mon{d_x = Dx, d_y = Dy, boss=Boss, kind = Kind}} = Minfo,
    State1 = cancle_special_event(State),
    case BA#battle_attr.hp =< 0 of
        true ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
            StateDriven = mon_state_util:conv_delay_driven({100, repeat}),
            {next_state, dead, State1#ob_act{att = undefined, state_driven = StateDriven, back_dest_path=null}};
        false ->
            ReturnPath = case BackDestPath of
                null -> lib_scene_object_ai:dest_path(X, Y, Dx, Dy, BA#battle_attr.speed);
                _ -> BackDestPath
            end,
            if
                Kind =:= ?MON_KIND_ATT ->
                    % old: util:cancel_timer(Ref),
                    if
                        Path =:= [] orelse Path =:= undefined ->
                            find_target_cast(Minfo),
                            StateDriven = mon_state_util:empty_state_driven(),
                            {next_state, sleep, State1#ob_act{state_driven = StateDriven, att = undefined}};
                        true ->
                            StateDriven = mon_state_util:empty_state_driven(),
                            walk(repeat, State1#ob_act{state_driven = StateDriven, att = undefined})
                    end;
                Kind =:= ?MON_KIND_ATT_AUTO ->
                    StateDriven = mon_state_util:empty_state_driven(),
                    walk_auto(repeat, State1#ob_act{state_driven = StateDriven, att = undefined});
                Kind =:= ?MON_KIND_WALK_AUTO ->
                    StateDriven = mon_state_util:empty_state_driven(),
                    walk(repeat, State1#ob_act{state_driven = StateDriven, att = undefined});
                true ->
                    case ReturnPath of
                        [] ->
                            Klist1 = case Boss > 0 of
                                true  -> Klist;
                                false -> []
                            end,
                            {Att1, NewStateName} = 
                            if Boss == ?MON_DESERTED_BOSS orelse Boss == ?MON_HOME_BOSS
                                orelse Boss == ?MON_HOME_GBOSS ->
                                    case Klist1 of
                                        [#mon_atter{id = MaxHurtRoleId}|_] -> 
                                            {#{id => MaxHurtRoleId, sign => ?BATTLE_SIGN_PLAYER}, trace};
                                        _ -> 
                                            {undefined, sleep}
                                    end;
                                true -> {undefined, sleep}
                            end,
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, 0, repeat),
                            StateDriven = mon_state_util:conv_delay_driven({0, repeat}),
                            NewState = State1#ob_act{
                                att = Att1,
                                back_dest_path=null,
                                klist=Klist1,
                                state_driven = StateDriven
                            },
                            {next_state, NewStateName, NewState};
                        [{NextX, NextY}|T] ->
                            {NewMinfo, Time} = lib_scene_object_ai:move_dest_path(Minfo, X, Y, NextX, NextY),
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, Time, repeat),
                            StateDriven = mon_state_util:conv_delay_driven({Time, repeat}),
                            NewState = State1#ob_act{
                                att=undefined,
                                state_driven = StateDriven,
                                object=NewMinfo,
                                back_dest_path=T
                            },
                            {next_state, back, NewState}
                    end
            end
    end.

%% 连续巡逻次数权重
patrol_weight_list() -> [{100,1}, {0,2}, {0,2}].
%% 连续巡逻间隔时间(ms)
patrol_sleep_times() -> [200, 600].

%% 是否巡逻(0否|1是)
is_patrol(MonId, _ConfigId) ->
    NowTime = utime:unixtime(),
    TimeFactor = NowTime rem 2,
    IdFactor = MonId rem 2,
    if
        TimeFactor == 0 andalso IdFactor == 0 -> 1;
        TimeFactor =/= 0 andalso IdFactor =/= 0 -> 1;
        true -> 0
    end.

%% 睡眠转巡逻时间(s)
%% sleep_2_patrol_time() -> [8*1000, 16*1000].

create_patrol_path(Angle, X, Y, DX, DY, _Range, Speed) ->
    NewRange = urand:rand(200, 300),
    if
        X =/= DX andalso Y =/= DY -> TX = DX, TY = DY;
        true ->
            TX = max(0, round(DX + NewRange * math:sin(Angle))),
            TY = max(0, round(DY + NewRange * math:cos(Angle)))
    end,
    lib_scene_object_ai:dest_path(X, Y, TX, TY, Speed).

%% 巡逻状态
patrol(_R, State) ->
    #ob_act{object = Minfo,
        patrol_path=PatrolPath,
        patrol_angle = OldPatrolAngle,
        continu_patrol_num = ContinuPatrolNum
    } = State,
    #scene_object{id = MonId, config_id = ConfigId, battle_attr=BA, x = X, y = Y,
        d_x = DX, d_y = DY, warning_range = Range} = Minfo,
    Speed = round(BA#battle_attr.speed*1),
    case {PatrolPath, ContinuPatrolNum} of
        {null, _} ->
            case is_patrol(MonId, ConfigId) of
                1 ->
                    NewPatrolNum = urand:rand_with_weight(patrol_weight_list()),
                    NewAngle = calc_patrol_angle(OldPatrolAngle),
                    NewPath = create_patrol_path(NewAngle, X, Y, DX, DY, Range, Speed),
                    NewState = State#ob_act{
                        continu_patrol_num = NewPatrolNum - 1,
                        patrol_angle = NewAngle
                    },
                    patrol_move(NewState, NewPath);
                _ ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 5000, repeat),
                    StateDriven = mon_state_util:conv_delay_driven({5000, repeat}),
                    {next_state, sleep, State#ob_act{state_driven = StateDriven}}
            end;
        {[], ContinuPatrolNum} when ContinuPatrolNum > 0 ->
            NewAngle = calc_patrol_angle(OldPatrolAngle),
            NewPath = create_patrol_path(NewAngle, X, Y, DX, DY, Range, Speed),
            NewPatrolNum = ContinuPatrolNum - 1,
            [SleepTimeMin, SleepTimeMax|_] = patrol_sleep_times(),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, urand:rand(SleepTimeMin, SleepTimeMax), repeat),
            DelayTime = urand:rand(SleepTimeMin, SleepTimeMax),
            StateDriven = mon_state_util:conv_delay_driven({DelayTime, repeat}),
            NewState = State#ob_act{
                state_driven = StateDriven,
                continu_patrol_num = NewPatrolNum,
                patrol_path = NewPath,
                patrol_angle = NewAngle
            },
            {next_state, patrol, NewState};
        _ ->
            patrol_move(State, PatrolPath)
    end.

patrol_move(State, PatrolPath) ->
    #ob_act{object = Minfo, klist=Klist} = State,
    #scene_object{config_id = _ConfigId, x = X, y = Y, mon=#scene_mon{boss=Boss, kind = _Kind}} = Minfo,
    case PatrolPath of
        [] ->
            Klist1 = case Boss > 0 of
                true  -> Klist;
                false -> []
            end,
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 5000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({5000, repeat}),
            NewState = State#ob_act{
                att = undefined,
                first_att = [],
                patrol_path = null,
                klist = Klist1,
                state_driven = StateDriven
            },
            {next_state, sleep, NewState};
        [{NextX, NextY}|T] ->
            find_target_cast(Minfo),
            {NewMinfo, Time} = lib_scene_object_ai:move_dest_path(Minfo, X, Y, NextX, NextY),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, Time, repeat),
            StateDriven = mon_state_util:conv_delay_driven({Time, repeat}),
            NewState = State#ob_act{
                att = undefined,
                state_driven = StateDriven,
                object = NewMinfo,
                patrol_path = T
            },
            {next_state, patrol, NewState}
    end.

%% 是否可以转入巡逻状态
can_trans_patrol(State) ->
    #ob_act{object=Object, is_patrol = IsPatrol} = State,
    #scene_object{
        mon= #scene_mon{kind = Kind, boss = Boss},
        can_att_list = CanAttList, cannot_beatt_list = CannotBeattList
    } = Object,
    CanPatrolMonList = [?MON_KIND_NORMAL],
    CanPatrolKind = lists:member(Kind, CanPatrolMonList),
    IsBoss = ?IS_BOSS(Boss),
    IsPatrol == 1 andalso CanPatrolKind andalso IsBoss=/=true
    andalso CanAttList==[] andalso CannotBeattList == [].

%%静止状态并回血
sleep(_R, State) ->
    % State1 = cancle_special_event(State),
    #ob_act{object=Object, bl_who = BLWho, klist = Klist} = State,
    #scene_object{id = Id, x = X, y = Y, copy_id = CopyId, scene = SceneId,
        scene_pool_id = ScenePoolId, mon = SceneMon,
        no_battle_hp_ex=NoBattleHpEx, hp_time=HpTime, battle_attr=BA
    } = Object,
    _CanPatrol = can_trans_patrol(State),
    case BA#battle_attr.hp < BA#battle_attr.hp_lim andalso
        NoBattleHpEx > 0 andalso BLWho == [] andalso Klist == [] of
        true  ->
            Hp = min(BA#battle_attr.hp + NoBattleHpEx, BA#battle_attr.hp_lim),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, HpTime, repeat),
            StateUpdateDriven = mon_state_util:update_state_driven(State, {HpTime, repeat}),
            {Object1, NewState, _, UpList} = set_mon([{hp, Hp}], Object, StateUpdateDriven, sleep, 1, []),
            {ok, BinData} = pt_120:write(12009, [Id, Hp, BA#battle_attr.hp_lim]),
            lib_server_send:send_to_area_scene(SceneId, ScenePoolId, CopyId, X, Y, BinData),
            lib_scene_object:update(Object1, UpList),
            {next_state, sleep, NewState#ob_act{first_att =[], object = Object1}};
        %% 注：封测暂不开放巡逻行为，客户端野怪区域卡
        %% false when CanPatrol ->
        %%     [TimeMin, TimeMax] = sleep_2_patrol_time(),
        %%     Time = urand:rand(TimeMin, TimeMax),
        %%     Ref1 = lib_scene_object:send_event_after(Ref, Time, repeat),
        %%     {next_state, patrol, State#ob_act{ref=Ref1}};
        false ->
            case SceneMon of
                #scene_mon{boss = BossType} when BossType == ?MON_DESERTED_BOSS
                 orelse BossType == ?MON_HOME_BOSS orelse BossType == ?MON_HOME_GBOSS ->
                    %% 即使还有仇恨列表或归属者也要定时检查
                    if BA#battle_attr.hp < BA#battle_attr.hp_lim ->
                            NewState = mon_state_util:update_state_driven(State, {HpTime, repeat}),
                            {next_state, sleep, NewState};
                        true ->
                            {next_state, sleep, State}
                    end;
                _ ->
                    {next_state, sleep, State}
            end
    end.

%% 死亡
dead(_R, State) ->
    #ob_act{object=Object, bl_who = BlWho, klist = KList, support_klist = Supportkilst} = State,
    #scene_object{x = Xnow, y = Ynow, scene = Scene, scene_pool_id = PoolId, 
        copy_id = CopyId, mon=#scene_mon{mid = MonId, boss = BossType, retime = ReTime}
    } = Object,
    if BossType == ?MON_DESERTED_BOSS orelse
        BossType == ?MON_DESERTED_ELITE orelse
        BossType == ?MON_HOME_BOSS orelse
        BossType == ?MON_HOME_GBOSS ->
            %% 脱战通知
            (BossType == ?MON_DESERTED_BOSS orelse BossType == ?MON_HOME_BOSS orelse BossType == ?MON_HOME_GBOSS) andalso
             begin 
                RoleIds = [RoleId || #mon_atter{id = RoleId} <- KList ++ Supportkilst],
                notice_leave_hate_range(RoleIds, BossType, MonId)
            end,
            update_boss_bl_whos(Scene, PoolId, CopyId, Xnow, Ynow, [], BlWho);
        true ->
            ok
    end,
    if
        ReTime =< 0 ->
            % old: util:cancel_timer(Ref),
            lib_scene_object:stop(Object, 1),
            {stop, normal, State};
        true ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, ReTime, repeat),
            StateDriven = mon_state_util:conv_delay_driven({ReTime, repeat}),
            {next_state, revive, State#ob_act{back_dest_path=null, state_driven = StateDriven}}
    end.

%% 复活
revive(_R, State) ->
    #ob_act{object = Minfo, born_handler = BornHandler} = State,
    #scene_object{mon=Mon, battle_attr=BA} = Minfo,
    #battle_attr{hp_lim=HpLim} = BA,
    NewMinfo = Minfo#scene_object{battle_attr=BA#battle_attr{hp = HpLim}, x = Mon#scene_mon.d_x, y = Mon#scene_mon.d_y},
    lib_scene_object:update(NewMinfo, [{hp, HpLim}, {xy, Mon#scene_mon.d_x, Mon#scene_mon.d_y}]),
    lib_scene_object:broadcast_object(NewMinfo),
    case BornHandler of
        {BornM, BornF, BornA} ->
            BornM:BornF(revive, NewMinfo, BornA);
        _ ->
            ok
    end,
    % old: Ref1 = lib_scene_object:send_event_after(Ref, 10, repeat),
    StateDriven = mon_state_util:conv_delay_driven({10, repeat}),
    NewState = State#ob_act{
        att = undefined,
        object = NewMinfo,
        klist = [],
        first_att = [],
        clist = [],
        state_driven = StateDriven
    },
    {next_state, sleep, NewState}.

is_continue_trace(Dx, Dy, Tx, Ty, TracingDistance) ->
    lib_scene_path:is_continue_trace(Dx, Dy, Tx, Ty, TracingDistance).

%% 追踪
trace(Msg, State) ->
    %% io:format("~p ~p Msg:~w~n", [?MODULE, ?LINE, [trace, Msg]]),
    #ob_act{att=Att, object = Minfo, next_move_time=NextMoveTime} = State,
    #scene_object{
        scene = SceneId, scene_pool_id = ScenePoolId,
        aid = Aid, id = ObjectId,
        can_att_list = CanAttList
    } = Minfo,
    case Att of
        #{id:=Id, sign:=Sign} -> skip;
        _ -> Id = 0, Sign = 0
    end,
    CanAtt = if CanAttList =/= [] ->
            case lists:keyfind(Sign, 1, CanAttList) of
                {?BATTLE_SIGN_PLAYER, _AttListA} -> true;
                {?BATTLE_SIGN_MON, _AttListA} -> true;
                _ -> false
            end;
        true -> true
    end,
    case Id=/=0 andalso CanAtt==true of %% 有目标
        true ->
            NowTimeMS = utime:unixtime(ms),
            if
                NextMoveTime > NowTimeMS ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, NextMoveTime - NowTimeMS, Msg),
                    DelayTime = NextMoveTime - NowTimeMS,
                    NewState = mon_state_util:update_state_driven(State, {DelayTime, Msg}, NowTimeMS),
                    {next_state, trace, NewState};
                true ->
                    mod_scene_agent:apply_cast(SceneId, ScenePoolId,
                        lib_scene_agent, get_trace_info_cast,
                        [Aid, ObjectId, Sign, Id, Msg]
                    ),
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 5000, repeat),
                    NewState = mon_state_util:update_state_driven(State, {5000, repeat}, NowTimeMS),
                    {next_state, back, NewState}
            end;
        _ -> %% 目标丢失，尝试等待
            % old: Ref1 = lib_scene_object:send_event_after(State#ob_act.ref, 3000, repeat),
            NewState = mon_state_util:update_state_driven(State, {3000, repeat}),
            {next_state, back, NewState}
    end.

%% 走路
%% 护送车走路，每走一步判断玩家是否在我这里
walk(_R, State) ->
    #ob_act{object=Minfo, path=MonPath, walk_end_handler = WalkEndHandler} = State,
    #scene_object{battle_attr= BA, x = X, y = Y, mon = Mon, scene = Scene} = Minfo,
    case MonPath of
        [] ->
            case Mon of
                #scene_mon{kind = ?MON_KIND_WALK_AUTO} ->
                    {NewState, NewStateName} = lib_mon_ai:walk_end(State, sleep),
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
                    NewState2 = mon_state_util:update_state_driven(NewState, {100, repeat}),
                    {next_state, NewStateName, NewState2};
                _ ->
                    StateHandler =
                    case WalkEndHandler of
                        {HandlerM, HandlerF, HandlerA} -> HandlerM:HandlerF(State, HandlerA);
                        _ -> lib_mon_event:walk_end(Minfo)
                    end,
                    State1 =
                    case is_record(StateHandler, ob_act) of
                        true  -> StateHandler;
                        false -> State#ob_act{path = undefined}
                    end,
                    {NewState, NewStateName} = lib_mon_ai:walk_end(State1, sleep),
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
                    NewState2 = mon_state_util:update_state_driven(NewState, {100, repeat}),
                    {next_state, NewStateName, NewState2}
            end;
        [{X, Y}|RemainPath] ->
            walk(_R, State#ob_act{path = RemainPath});
        [WPoint|RemainPath] ->
            case WPoint of
                {_, NextX, NextY} ->
                    ok;
                {NextX, NextY} ->
                    ok
            end,
            #battle_attr{speed=Speed} = BA,
            NowTimeMS = utime:longunixtime(),
            if
                Mon#scene_mon.kind =:= ?MON_KIND_ATT ->
                    case lib_dungeon_pet:is_dun_pet_scene(Scene) of
                        true -> ok;
                        _ -> find_target_cast(Minfo)
                    end;
                true ->
                    ok
            end,
            case lib_skill_buff:is_can_move(BA#battle_attr.other_buff_list, Speed, NowTimeMS) of
                {false, MoveWaitMs} ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, MoveWaitMs, repeat),
                    NewState = mon_state_util:update_state_driven(State, {MoveWaitMs, repeat}, NowTimeMS),
                    {next_state, walk, NewState};
                false ->
                    {next_state, sleep, State#ob_act{att=undefined}};
                true ->
                    CheckBlock =
                    case Mon of
                        #scene_mon{kind = ?MON_KIND_WALK_AUTO} -> true;
                        _ -> false
                    end,
                    case lib_scene_object_ai:move(NextX, NextY, Minfo, Speed, CheckBlock) of
                        block ->
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, 500, repeat),
                            NewState = mon_state_util:update_state_driven(State, {500, repeat}, NowTimeMS),
                            {next_state, sleep, NewState#ob_act{path=[]}};
                        {true, NewMinfo, Time} ->
                            lib_mon_event:walk(NewMinfo),
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, Time, repeat),
                            NewState = mon_state_util:update_state_driven(State, {Time, repeat}, NowTimeMS),
                            {next_state, walk, NewState#ob_act{object=NewMinfo, o_point = {X, Y}, w_point = {NextX, NextY}, path = RemainPath}}
                    end
            end;
        _ ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 500, repeat),
            NewState = mon_state_util:update_state_driven(State, {500, repeat}),
            {next_state, sleep, NewState}
    end.

%% 自动寻路
walk_auto(_R, State) ->
    #ob_act{object=Minfo, ai_option = AiOption} = State,
    case AiOption of
        {1, _} -> %% 怪物优先，直接朝目标前进
            {StateName, Time, NewState} = lib_scene_object_ai:walk_to_target(State),
            % old: NewRef = lib_scene_object:send_event_after(Ref, Time, repeat),
            NewState2 = mon_state_util:update_state_driven(NewState, {Time, repeat}),
            {next_state, StateName, NewState2};
        {Type, _AttList} -> %% 玩家优先，先打人，没人再走路
            find_target_cast_mon_first(Minfo, Type),
            {next_state, walk_auto, State};
        _ ->
            ?ERR("walk_auto ai_option = []", []),
            {next_state, walk_auto, State}
    end.

%% 蓄力状态
accumulate_power({accumulate_power_over, OldStateName}, State) ->
    % old: gen_fsm:send_event(self(), repeat),
    NewState = mon_state_util:update_state_driven(State, {0, repeat}),
    {next_state, OldStateName, NewState};
accumulate_power(_R, State) ->
    {next_state, accumulate_power, State}.
%%取消事件
cancle_special_event(State) ->
    {LastState, _} = lib_mon_ai:resume(State),
    LastState.

set_mon([], M, State, StateName, _Broadcast, UpList) ->
    {M, State, StateName, UpList};
set_mon([H|T], M, OldState, StateName, Broadcast, UpList) ->
    Res =
    case H of
        {create_key, Value}  ->
            Mon1 = M#scene_object.mon#scene_mon{create_key = Value},
            [ M#scene_object{mon = Mon1}, [{mon, Mon1}|UpList] ];
        {from_pid, Value}  ->
            Mon1 = M#scene_object.mon#scene_mon{from_pid = Value},
            [ M#scene_object{mon = Mon1}, [{mon, Mon1}|UpList] ];
        {dungeon_hp_rate, HpRateList} ->
            put({dungeon_hp_rate, M#scene_object.id}, HpRateList),
            [M, UpList];
        {collect_time, CollectTime} ->
            Mon = M#scene_object.mon,
            case Broadcast of
                1 -> lib_scene_object:broadcast_object_attr(M, [{1, CollectTime}]);
                _ -> skip
            end,
            [ M#scene_object{mon=Mon#scene_mon{collect_time=CollectTime}}, UpList];
        {skill_owner, SkillOwner} ->
            [ M#scene_object{skill_owner = SkillOwner}, [H|UpList] ];
        {special_event, Events} ->
            self() ! {'special_event', Events},
            [M, UpList];
        {group, Camp} ->
            BA = M#scene_object.battle_attr#battle_attr{group=Camp},
            [ M#scene_object{battle_attr = BA}, [{battle_attr, BA}|UpList ]];
        {mon_name, MonName} ->
            case MonName == [] of
                true -> [M, UpList];
                false ->
                    Figure1 = M#scene_object.figure#figure{name=MonName},
                    [ M#scene_object{figure=Figure1}, [{figure, Figure1},{mon_name, MonName}|UpList] ]
            end;
        {guild_id, GuildId} ->
            Figure1 = M#scene_object.figure#figure{guild_id=GuildId},
            [ M#scene_object{figure=Figure1}, [{figure, Figure1}|UpList] ];
        {hp, Hp} ->
            BattleAttr = M#scene_object.battle_attr,
            [ M#scene_object{battle_attr = BattleAttr#battle_attr{hp = Hp}}, [{hp, Hp}|UpList] ];
        {hp_ratio, Ratio} ->
            BattleAttr = M#scene_object.battle_attr,
            Hp = BattleAttr#battle_attr.hp_lim,
            [ M#scene_object{battle_attr = BattleAttr#battle_attr{hp = util:ceil(Ratio * Hp)}}, UpList ];
        {hp_lim, HpLim} ->
            BattleAttr = M#scene_object.battle_attr,
            NewBattleAttr = BattleAttr#battle_attr{hp = HpLim, hp_lim = HpLim},
            [ M#scene_object{battle_attr = NewBattleAttr}, [H | UpList] ];
        {is_be_atted, Value} ->
            [ M#scene_object{is_be_atted = Value}, [{is_be_atted, Value}|UpList] ];
        {is_be_clicked, Value} ->
            [ M#scene_object{is_be_clicked = Value}, [{is_be_clicked, Value}|UpList] ];
        {mod_args, Value} ->
            [ M#scene_object{mod_args = Value}, [{mod_args, Value}|UpList] ];
        {auto_lv, Value} ->
            Figure = M#scene_object.figure#figure{lv=Value},
            Mon1 = M#scene_object.mon#scene_mon{auto_lv=Value},
            [ M#scene_object{figure=Figure, mon=Mon1}, [{figure, Figure},{mon, Mon1}|UpList] ];
        {dun_r, Value} ->
            Mon1 = M#scene_object.mon#scene_mon{dun_r=Value},
            [ M#scene_object{mon=Mon1}, [{mon, Mon1}|UpList] ];
        {dun_crush_r, Value} ->
            %% 副本碾压系数
            Mon1 = M#scene_object.mon#scene_mon{dun_crush_r=Value},
            NewM = set_dun_crush_mon(M#scene_object{mon=Mon1}, Value),
            [ NewM, UpList ];
        {retime, Value} ->
            Mon1 = M#scene_object.mon#scene_mon{retime=Value},
            [ M#scene_object{mon=Mon1}, [{mon, Mon1}|UpList] ];
        {angle, Value} ->
            [ M#scene_object{angle=Value}, [H|UpList] ];
        {type, Value} ->
            [ M#scene_object{type=Value}, [H|UpList] ];
        {skill, Value} ->
            F = fun({SkillId, Lv}) ->
                    case data_skill:get(SkillId, Lv) of
                        #skill{type=Type, is_anger=0} when Type == ?SKILL_TYPE_ACTIVE orelse Type== ?SKILL_TYPE_ASSIST -> true;
                        _ -> false
                    end
            end,
            Skill = lists:filter(F, Value),
            [ M#scene_object{skill = Skill}, [{filter_skill, Skill}|UpList] ];
        {born_x_y, Value} ->
            {X, Y} = Value,
            Mon1 = M#scene_object.mon#scene_mon{d_x = X, d_y = Y},
            [ M#scene_object{mon=Mon1}, [{mon, Mon1}|UpList] ];
        {title, Value} ->
            Figure = M#scene_object.figure#figure{title=Value},
            [ M#scene_object{figure=Figure}, [{figure, Figure}|UpList] ];
        {lv, Value} ->
            Figure = M#scene_object.figure#figure{lv=Value},
            [ M#scene_object{figure=Figure}, [{figure, Figure},{lv, Value}|UpList] ];
        {figure, Value} ->
            [ M#scene_object{figure=Value}, [{figure, Value}|UpList] ];
        {weapon_id, Value} ->
            [ M#scene_object{weapon_id=Value}, [{weapon_id, Value}|UpList] ];
        {die_info, Value} ->
            DieInfos = M#scene_object.die_info,
            case lists:member(Value, DieInfos) of
                false ->
                    NewDieInfos = [Value|DieInfos],
                    [M#scene_object{die_info = NewDieInfos}, [{die_info, NewDieInfos}|UpList]];
                _ ->
                    [M, UpList]
            end;
        {find_target, Value} ->
            % old: erlang:send_after(Value, M#scene_object.aid, 'find_target'),
            DelayGeneralDriven = [{Value, 'find_target', add}],
            [M, UpList, mon_state_util:update_general_driven(OldState, DelayGeneralDriven)];
        {cycle_check, Value} ->
            % old: erlang:send_after(Value, M#scene_object.aid, 'cycle_check'),
            DelayGeneralDriven = [{Value, 'cycle_check'}],
            [M, UpList, mon_state_util:update_general_driven(OldState, DelayGeneralDriven)];
        {ctime, Value} ->
            Mon = M#scene_object.mon,
            [ M#scene_object{mon=Mon#scene_mon{ctime=Value}}, UpList];
        {restore_hp} ->
            BattleAttr = M#scene_object.battle_attr,
            NewHp = BattleAttr#battle_attr.hp_lim,
            [ M#scene_object{battle_attr = BattleAttr#battle_attr{hp = NewHp}}, [{hp, NewHp}|UpList] ];
        {hurt_check, Value} ->
            [ M#scene_object{hurt_check = Value}, [{hurt_check, Value}|UpList] ];
        {attr_change, Value} ->
            Fun = fun({AttType, AttVal}, TmpBA)->
                    Attr = TmpBA#battle_attr.attr,
                    case AttType of
                        att_r ->
                            NewAtt = trunc(Attr#attr.att * AttVal),
                            NewBA = TmpBA#battle_attr{attr = Attr#attr{att = NewAtt}};
                        hp_r ->
                            NewHp = trunc(TmpBA#battle_attr.hp_lim * AttVal),
                            NewBA = TmpBA#battle_attr{hp = NewHp, hp_lim = NewHp};
                        def_r ->
                            NewDef = trunc(Attr#attr.def * AttVal),
                            NewBA = TmpBA#battle_attr{attr = Attr#attr{def = NewDef}};
                        hurt_del_r ->
                            NVal = trunc(Attr#attr.hurt_del_ratio + AttVal),
                            NewBA = TmpBA#battle_attr{attr = Attr#attr{hurt_del_ratio = NVal}};
                        _ -> NewBA = TmpBA
                    end,
                    NewBA
            end,
            BattleAttr = M#scene_object.battle_attr,
            NewBattleAttr = lists:foldl(Fun, BattleAttr, Value),
            [M#scene_object{battle_attr = NewBattleAttr}, [{battle_attr, NewBattleAttr}|UpList]];
        {attr_r, Value} ->
            BattleAttr = M#scene_object.battle_attr,
            Attr = BattleAttr#battle_attr.attr,
            AttrList = attr_conv:to_attr_tuple_list(Attr),
            BaseAttrList = lib_player_attr:get_list_base_attr_kv(AttrList),
            NewBaseAttrList = lib_player_attr:set_attr_by_ratio(lists:keydelete(?HP, 1, BaseAttrList), Value),
            NewBattleAttr = lib_player_attr:set_battle_attr(BattleAttr, NewBaseAttrList),
            #battle_attr{hp = Hp, hp_lim = HpLim} = BattleAttr,
            NewHp = round(Hp * Value / ?RATIO_MAX),
            NewHpLim = round(HpLim * Value / ?RATIO_MAX),
            LastBattleAttr = NewBattleAttr#battle_attr{hp = NewHp, hp_lim = NewHpLim},
            [M#scene_object{battle_attr = LastBattleAttr}, [{battle_attr, LastBattleAttr}|UpList]];
        {icon_texture, IconTextrue} ->
            [ M#scene_object{icon_texture = IconTextrue}, UpList];
        {speed_r, Value} when Value > 0 ->
            case data_mon:get(M#scene_object.config_id) of
                #mon{speed = BaseSpeed} ->
                    #battle_attr{speed = OSpeed} = BattleAttr = M#scene_object.battle_attr,
                    case trunc(BaseSpeed * Value) of
                        OSpeed ->
                            [M, UpList];
                        Speed ->
                            NewBA = BattleAttr#battle_attr{speed = Speed},
                            [M#scene_object{battle_attr = NewBA}, [{change_speed, Speed}|UpList]]
                    end;
                _ ->
                    [M, UpList]
            end;
        {hurt_sync_ids, Value} when is_list(Value) ->
            NewValue = [{PoolId, Id}||{PoolId, Id}<-Value, Id=/=M#scene_object.id],
            [M#scene_object{hurt_sync_ids = NewValue}, UpList];
        {hurt_sync, _Value, Hp} ->
            BattleAttr = M#scene_object.battle_attr,
            OldHp = BattleAttr#battle_attr.hp,
            %% 纠正为最低血量
            TempHp = min(Hp, OldHp),
            % %% 最后伤害等于血量
            % Hurt = min(Value, TempHp),
            NewHp = max(TempHp, 0),
            NewBattleAttr = BattleAttr#battle_attr{hp = NewHp},
            % old: ?IF(NewHp =<0, erlang:send_after(1000, self(), 'die'), skip),
            TmpState =
            if
                NewHp =< 0 ->
                    DelayGeneralDriven = [{1000, 'die', add}],
                    mon_state_util:update_general_driven(OldState, DelayGeneralDriven);
                true ->
                    OldState
            end,
            [ M#scene_object{battle_attr = NewBattleAttr}, [{hp, NewHp}|UpList], TmpState];

        {attr_list, Value} ->
            BattleAttr = M#scene_object.battle_attr,
            NewBattleAttr = lib_player_attr:set_battle_attr(BattleAttr, Value),
            [ M#scene_object{battle_attr = NewBattleAttr}, [{attr_list, Value}|UpList] ];
        {pk_protect_time, Value} ->
            BattleAttr = M#scene_object.battle_attr,
            OldPk = BattleAttr#battle_attr.pk,
            NewPk = OldPk#pk{pk_protect_time = Value},
            NewBattleAttr = BattleAttr#battle_attr{pk = NewPk},
            [ M#scene_object{battle_attr = NewBattleAttr}, [{pk_protect_time, Value}|UpList] ];
        {can_att_list, Value} ->
            [ M#scene_object{can_att_list=Value}, [{can_att_list, Value}|UpList] ];
        {cannot_beatt_list, Value} ->
            [ M#scene_object{cannot_beatt_list=Value}, [{cannot_beatt_list, Value}|UpList] ];
        {act_label, Value} ->
            OldActLabel = M#scene_object.act_label,
            F = fun({K, V}, List) when V == 0-> lists:keydelete(K, 1, List);
                ({K, V}, List) -> [{K, V}|lists:keydelete(K, 1, List)]
            end,
            ActLabel = lists:foldl(F, OldActLabel, Value),
            [ M#scene_object{act_label = ActLabel}, UpList];
        {att_del_hp_each_object, Value} ->
            BattleAttr = M#scene_object.battle_attr,
            NewM = M#scene_object{battle_attr = BattleAttr#battle_attr{att_del_hp_each_object = Value}},
            [ NewM, [{att_del_hp_each_object, Value}|UpList] ];
        {att_del_hp_each_time, Value} ->
            BattleAttr = M#scene_object.battle_attr,
            NewM = M#scene_object{battle_attr = BattleAttr#battle_attr{att_del_hp_each_time = Value}},
            [ NewM, [{att_del_hp_each_time, Value}|UpList] ];
        {stop, Value} ->
            % old: erlang:send_after(Value, M#scene_object.aid, {'stop', 1}),
            DelayGeneralDriven = [{Value, {'stop',1}, add}],
            [M, UpList, mon_state_util:update_general_driven(OldState, DelayGeneralDriven)];
        {owner, Owner} ->
            Mon = M#scene_object.mon,
            [ M#scene_object{mon=Mon#scene_mon{owner = Owner}}, UpList];
        {owner_name, Name} ->
            Mon = M#scene_object.mon,
            [ M#scene_object{mon=Mon#scene_mon{owner_name = Name}}, UpList];
        {origin, Origin} ->
            [ M#scene_object{origin=Origin}, UpList];
        {pos, Value} ->
            [ M#scene_object{pos = Value}, UpList];
        _ -> [M, UpList]
    end,

    %% 从第一步计算结果中提取出怪物、更新属性列表、新的State( #ob_act{} )数据等
    case Res of
        [LastM, UpList1] ->
            State = OldState;
        [LastM, UpList1, State] ->
            ok
    end,

    NewState = case H of
        {path, Path} -> State#ob_act{path = Path};
        %% 伤害定时器引用(秒)
        {broadcast_hurt_list, DelayTime} ->
            % old: HurtRef = lib_mon_ai:send_after(DelayTime, self(), 'broadcast_hurt_list', State#ob_act.hurt_ref),
            DelayGeneralDriven2 = [{DelayTime, 'broadcast_hurt_list'}],
            NewState0 = State#ob_act{hurt_list_broadcast = DelayTime},
            mon_state_util:update_general_driven(NewState0, DelayGeneralDriven2);
        {att, Sign, Id} ->
            State#ob_act{att = #{sign=>Sign, id=>Id}};
        {hp_change_handler, ObValue} ->
            State#ob_act{hp_change_handler = ObValue};
        {born_handler, ObValue} ->
            State#ob_act{born_handler = ObValue};
        {die_handler, ObValue} ->
            State#ob_act{die_handler = ObValue};
        {collected_handler, ObValue} ->
            State#ob_act{collected_handler = ObValue};
        {walk_end_handler, ObValue} ->
            State#ob_act{walk_end_handler = ObValue};
        {cycle_check_handler, ObValue} ->
            State#ob_act{cycle_check_handler = ObValue};
        {hurt_list_handler, ObValue} ->
            State#ob_act{hurt_list_handler = ObValue};
        {be_hurt_handler, ObValue} ->
            State#ob_act{be_hurt_handler = ObValue};
        {is_patrol, ObValue} ->
            Patrol = State#ob_act.object#scene_object.is_patrol,
            case {Patrol, ObValue} of
                {1, 1} -> State;
                _ -> State#ob_act{is_patrol = ObValue}
            end;
        {born_ai, ObValue} -> %% 以属性方式传入出生ai
            NowTimeMS = utime:longunixtime(),
            case lists:keyfind(remove, 1, ObValue) of
               {_, Time, _} ->
                   mon_state_util:update_general_driven(State, [{Time, {'stop', 1}}], NowTimeMS);
               _ ->
                   State
            end;
        {walk_auto, AiValue} ->
            State#ob_act{ai_option = AiValue};
        {ai_option, AiValue} ->
            State#ob_act{ai_option = AiValue};
        {clean_hurt_list, _} ->
            State#ob_act{first_att = [], klist = []};
        _ -> State
    end,

    case H of
        {state, NewStateName} -> skip;
        _ -> NewStateName = StateName
    end,
    set_mon(T, LastM, NewState, NewStateName, Broadcast, UpList1).

set_dynamic_mon(Mon, Lv, DunRatio) ->
    case data_mon_dynamic:get(Mon#scene_object.config_id, Lv) of
        #mon_dynamic{attr_list = AttrList, exp = Exp} ->
            #scene_object{figure=Figure, mon=SceneMon, battle_attr=BA} = Mon,
            NewAttrList = [{AttrType, calc_mon_dynamic(AttrValue, Lv, DunRatio)}||{AttrType, AttrValue}<-AttrList],
            NewBA = lib_player_attr:set_battle_attr(BA, NewAttrList),
            Mon#scene_object{figure=Figure#figure{lv=Lv}, battle_attr=NewBA, mon = SceneMon#scene_mon{exp = Exp}};
        _ -> Mon
    end.

%% 功能定制
set_custom_made_mon(Mon) ->
    Mon.

calc_mon_dynamic(Base, _Lv, DunRatio) ->
    %% max(0, round(Lv*Base/100)).
    max(0, round(Base*DunRatio)).

%% 设置副本碾压怪物
set_dun_crush_mon(Mon, DunCrushR) when DunCrushR=/= 1->
    #scene_object{battle_attr=BA=#battle_attr{hp_lim = HpLim, attr=Attr, hp = Hp} } = Mon,
    #attr{att = Att} = Attr,
    NewHpLim = calc_dun_crush_mon(HpLim, DunCrushR),
    Attr1 = Attr#attr{att = calc_dun_crush_mon(Att, DunCrushR)},
    BA1 = BA#battle_attr{hp=trunc((Hp/HpLim)*NewHpLim), hp_lim=NewHpLim, attr=Attr1},
    Mon#scene_object{battle_attr=BA1};
set_dun_crush_mon(Mon, _DunCrushR) ->
    Mon.

calc_dun_crush_mon(Base, DunCrushR) ->
    %% max(0, round(Lv*Base/100)).
    max(0, round(Base*DunCrushR)).

%% 加入仇恨列表叠加伤害
add_hatred_list(Klist, MonAtter, Boss, Hurt)->
    %% 根据协助者判断伤害给谁
    #mon_atter{id = AtterId, support_id = SupportId, support_name = SupportName,
        support_team = SupportTeam, support_team_name = SupportTeamName} = MonAtter,
    case SupportId > 0 andalso is_supporting_boss(Boss) of
        true  -> %% 将伤害者信息校正为被协助者伤害信息
            FixAtter = MonAtter#mon_atter{id = SupportId, name = SupportName, team_id = SupportTeam, team_leader_name = SupportTeamName
                , support_id = 0, support_name = "", support_team = 0, support_team_name = ""},
            TrueRoleId = SupportId;
        false ->
            FixAtter = MonAtter,
            TrueRoleId = AtterId
    end,
    case lists:keyfind(TrueRoleId, #mon_atter.id, Klist) of
        false ->
            %% 第一次，则直接用新数据
            [FixAtter#mon_atter{hurt = Hurt}|Klist];
        #mon_atter{hurt = OHurt} = Old ->
            case SupportId > 0 of
                true  ->
                    %% 是协助的伤害，则用旧数据
                    [Old#mon_atter{hurt = Hurt+OHurt}|lists:keydelete(TrueRoleId, #mon_atter.id, Klist)];
                false ->
                    %% 非协助，则用新数据
                    [FixAtter#mon_atter{hurt = Hurt+OHurt}|lists:keydelete(TrueRoleId, #mon_atter.id, Klist)]
            end
    end.

%% 加入协助伤害列表
add_support_list(SupportList, MonAtter, Hurt) ->
    #mon_atter{id = AtterId, support_id = SupportId} = MonAtter,
    case SupportId > 0 of
        true  ->
            case lists:keyfind(AtterId, #mon_atter.id, SupportList) of
                false ->
                    [MonAtter#mon_atter{hurt = Hurt}|SupportList];
                #mon_atter{hurt = OHurt} ->
                    [MonAtter#mon_atter{hurt = Hurt+OHurt}|lists:keydelete(AtterId, #mon_atter.id, SupportList)]
            end;
        false ->
            SupportList
    end.

add_mon_attr(Mon, AttrObj) ->
    #scene_object{battle_attr=BA=#battle_attr{hp_lim = HpLim, attr=Attr} } = Mon,
    #attr{hp = AddedHp} = attr_conv:to_attr_record(AttrObj),
    Attr1 = lib_player_attr:add_attr(record, [AttrObj, Attr]),
    NewHpLim = HpLim + AddedHp,
    BA1 = BA#battle_attr{hp=NewHpLim, hp_lim=NewHpLim, attr=Attr1},
    Mon#scene_object{battle_attr=BA1}.

find_target_cast(Object) ->
    #scene_object{
        battle_attr=BA, aid = Aid,
        scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId,
        id=ObjectId,sign=ObjectSign, x=ObjectX, y=ObjectY,
        skill_owner=SkillOwner, warning_range=WarningRange,
        can_att_list = _CanAttList
    } = Object,
    {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),

    SelectOp = lib_scene_object_ai:get_select_option(Object),

    Args = {BA#battle_attr.group, ObjectSign, ObjectId, OwnerSign, OwnerId},
    mod_scene_agent:apply_cast(SceneId, ScenePoolId, lib_scene_calc, get_any_for_trace_cast,
        [Aid, ObjectId, CopyId, ObjectX, ObjectY, WarningRange, Args, SelectOp]).

%% 优先找怪FindType:1怪优先|2人优先|3只找怪
find_target_cast_mon_first(Object, FindType) ->
    #scene_object{
        battle_attr = BA, aid = Aid, scene = SceneId, scene_pool_id = ScenePoolId,
        copy_id = CopyId, id = ObjectId, sign = ObjectSign, x = ObjectX, y = ObjectY,
        skill_owner = SkillOwner, warning_range = WarningRange
    } = Object,
    {OwnerSign, OwnerId} = lib_scene_object_ai:get_skill_owner_args(SkillOwner),
    Args = {BA#battle_attr.group, ObjectSign, ObjectId, OwnerSign, OwnerId},
    mod_scene_agent:apply_cast(
        SceneId, ScenePoolId, lib_scene_calc, get_any_for_trace_cast_mon_first,
        [Aid, ObjectId, CopyId, ObjectX, ObjectY, WarningRange, Args, undefined, FindType]
    ).

%% 更新场景玩家的归属显示
update_boss_bl_whos(Scene, PoolId, CopyId, _X, _Y, Adds, Dels) ->
    Adds1 = [BlId1 || #mon_atter{id = BlId1} <- Adds],
    Dels1 = [BlId2 || #mon_atter{id = BlId2} <- Dels],
    mod_scene_agent:apply_cast(Scene, PoolId, ?MODULE, scene_update_bl_whos, [CopyId, Adds1, Dels1]),
    ok.

%% 场景进程执行
%% 因可能有多个归属,前端只会执行新归属穿戴操作或者新的归属移除操作,不会执行移除同范围内旧归属的操作
%% 因此还是每个归属者九宫格广播
scene_update_bl_whos(CopyId, Adds, Dels) ->
    AddSceneUsers = [ lib_scene_agent:get_user(Key) || Key <- Adds],
    DelSceneUsers = [ lib_scene_agent:get_user(Key1) || Key1 <- Dels],
    Fun = fun(SceneUser = #ets_scene_user{id = BlId, x = X, y = Y}, Flag) ->
        NewSceneUser = lib_scene_user:set_data_sub([{bl_who, Flag}], SceneUser, undefined),
        lib_scene_agent:put_user(NewSceneUser),
        {ok, Bin} = pt_120:write(12022, [BlId, Flag]),
        Areas = lib_scene_calc:get_the_area(X, Y),
        lib_scene_agent:send_to_any_area(Areas, CopyId, Bin),
        Flag;
        (_, Flag) -> Flag
    end,
    lists:foldl(Fun, 1, AddSceneUsers),
    lists:foldl(Fun, 0, DelSceneUsers).

%% 归属清空且无人击杀恢复满血
recover_hp_no_att([], [], Minfo) ->
    #scene_mon{mid = _Mid, boss = Boss} = Minfo#scene_object.mon,
    RecoverBoss = [?MON_REWARD_BOSS, ?MON_DESERTED_BOSS, ?MON_HOME_BOSS, ?MON_HOME_GBOSS], %%无人击杀恢复满血boss类型
    case lists:member(Boss, RecoverBoss) of
        true->
            update_recover_hp_no_att(Minfo);
        false-> Minfo
    end;
recover_hp_no_att(_BLWhos, _Klist, Minfo) -> Minfo.

update_recover_hp_no_att(Minfo) ->
    #scene_object{scene = Scene, scene_pool_id = PoolId, copy_id = CopyId, x = X, y = Y, battle_attr=BA} = Minfo,
    HpLim = BA#battle_attr.hp_lim,
    {ok, BinData} = pt_120:write(12081, [Minfo#scene_object.id, HpLim]),
    lib_server_send:send_to_area_scene(Scene, PoolId, CopyId, X, Y, BinData),
    Minfo1 = Minfo#scene_object{battle_attr = BA#battle_attr{hp=HpLim}},
    Minfo2 = lib_mon_event:reset_boss_hit(Minfo1),
    lib_scene_object:update(Minfo2, [{hp, HpLim}]),
    Minfo2.

%%检测离开有效区域时间
calc_leave_distance(LeaveDistance, _Object, OldKlist, NewKlist, TimeAddType) ->
    LeaveList = [ MonAttr || MonAttr<-OldKlist, lists:keymember(MonAttr#mon_atter.id, #mon_atter.id, NewKlist) == false],
    %%增加离开的有伤害玩家
    AddFun = fun(#mon_atter{id = RoleId}, TmpList)->
            lists:keystore(RoleId, 1, TmpList, {RoleId, 0})
    end,
    AddLeaveDistance = lists:foldl(AddFun, LeaveDistance, LeaveList),
    %%去掉回来的玩家
    DelFun = fun(#mon_atter{id = RoleId}, TmpList) ->
            lists:keydelete(RoleId, 1, TmpList)
    end,
    DelLeaveDistance = lists:foldl(DelFun, AddLeaveDistance, NewKlist),
    TmpleaveDistance = ?IF(TimeAddType == true, refresh_leave_distance_time(DelLeaveDistance, []), DelLeaveDistance),
    TmpleaveDistance.

refresh_leave_distance_time([], LeaveDistance) -> LeaveDistance;
refresh_leave_distance_time([{RoleId, Time}| T], LeaveDistance) ->
    %%超过1分钟自动移除
    case Time>= 60 of
        false -> refresh_leave_distance_time(T, [{ RoleId, min(60, Time + ?DROP_BLOG_CALC_TIME) } | LeaveDistance]);
        true -> refresh_leave_distance_time(T, LeaveDistance)
    end.

%% 有玩家离开 刷新伤害列表/归属。。。
remove_hurt_list(RoleIds, State) ->
    #ob_act{object=Object, bl_who = BLWhos, klist = KList, support_klist = Supportkilst, first_att = FirstAttr} = State,
    #scene_object{mon = #scene_mon{boss = Boss, mid = MonId}} = Object,
    F = fun(LeaveId, {SupportTemp, KListTemp, OldBlWhosTemp, FirstAttrTemp}) ->
            {NewSupportKilst, NewKList} = calc_hurt_support_role_leave(LeaveId, KListTemp, SupportTemp),

            NewKList1 = calc_hurt_role_leave(LeaveId, NewKList),

            OldBlWhos = lists:keydelete(LeaveId, #mon_atter.id, OldBlWhosTemp),
            NewFirstAttr = case FirstAttrTemp of
                [] -> [];
                _ -> ?IF(FirstAttrTemp#mon_atter.id == LeaveId, [], FirstAttrTemp)
            end,
            {NewSupportKilst, NewKList1, OldBlWhos, NewFirstAttr}
    end,
    {AfterSupportkilst, AfterKList, AfterBlWhos, AfterFirstAttr} =
    lists:foldl(F, {Supportkilst, KList, BLWhos, FirstAttr}, RoleIds),

    %% 重新计算Boss归属
    ThenBLWhos = case AfterBlWhos =/= BLWhos of
        true  -> update_belong_who(State, BLWhos, AfterKList, AfterFirstAttr);
        false -> AfterBlWhos
    end,
    %% 归属清空且无人击杀回复满血
    NewObject = recover_hp_no_att(ThenBLWhos, AfterKList, Object),
    % old: Ref1 = lib_scene_object:send_event_after(Ref, 100, repeat),
    notice_leave_hate_range(RoleIds, Boss, MonId),
    StateDriven = mon_state_util:conv_delay_driven({100, repeat}),
    NewState = State#ob_act{
        state_driven = StateDriven,
        bl_who = ThenBLWhos, klist = AfterKList,
        support_klist = AfterSupportkilst, first_att = AfterFirstAttr,
        object = NewObject
    },
    case AfterKList =/= KList of
        true  -> local_boss_api:restat_hurt_list(Boss, NewState);
        false -> NewState
    end.

%% 协助列表玩家离开:清除协助伤害列表，协助对象清除相应的伤害
calc_hurt_support_role_leave(LeaveId, KList, SupportKlist) ->
    case lists:keytake(LeaveId, #mon_atter.id, SupportKlist) of
        {value, #mon_atter{support_id = SupportId, hurt = Hurt}, LeftSupport} ->
            NewKList = case lists:keyfind(SupportId, #mon_atter.id, KList) of
                #mon_atter{hurt = TotalHurt} = Attacker ->
                    DelHurt = TotalHurt - Hurt,
                    case DelHurt > 0 of
                        true  ->
                            NewAttacker = Attacker#mon_atter{hurt = DelHurt},
                            lists:keyreplace(SupportId, #mon_atter.id, KList, NewAttacker);
                        false ->
                            lists:keydelete(SupportId, #mon_atter.id, KList)
                    end;
                _ ->
                    KList
            end,
            {LeftSupport, NewKList};
        _ ->
            {SupportKlist, KList}
    end.
%% 攻击列表玩家离开:清除对应伤害
calc_hurt_role_leave(LeaveId, KList) ->
    case lists:keytake(LeaveId, #mon_atter.id, KList) of
        {value, #mon_atter{}, Left} ->
            Left;
        false ->
            KList
    end.

%% 废都boss脱离范围判断
%% 被协助者脱离需触发集体协助取消
%% 协助者脱离清空仇恨伤害&保留协助状态
deserted_remove_hurt_list(LeaveIds, LeaveSupport, State) ->
    #ob_act{klist = Klist, support_klist = SupportList, bl_who = BLWho, first_att = FirstAtt, object = SceneObj} = State,
    #scene_object{config_id = MonId, mon = #scene_mon{boss = BossType}} = SceneObj,
    notice_leave_hate_range(LeaveIds ++ LeaveSupport, BossType, MonId),

    {NewKlist, NewSupportList} = calc_deserted_support_role_leave(Klist, SupportList, LeaveSupport),
    
    NewKlist1 = lists:foldl(fun(LeaveId, TempKlist) ->
            calc_hurt_role_leave(LeaveId, TempKlist)
    end, NewKlist, LeaveIds),
    
    {NewBLWho, NewFirstAtt} = lists:foldl(fun(LeaveId, {TempBLWho, TempFirstAtt}) -> 
        NewTempBLWho = lists:keydelete(LeaveId, #mon_atter.id, TempBLWho),
        NewTempFirstAtt = case TempFirstAtt of
            [] -> [];
            _  -> ?IF(TempFirstAtt#mon_atter.id == LeaveId, [], TempFirstAtt)
        end,
        {NewTempBLWho, NewTempFirstAtt}
    end, {BLWho, FirstAtt}, LeaveIds ++ LeaveSupport),
    
    %% 重新计算Boss归属
    NewBLWho1 = case NewBLWho =/= BLWho of
        true  -> update_belong_who(State, BLWho, NewKlist1, NewFirstAtt);
        false -> NewBLWho
    end,
    % Ref = lib_scene_object:send_event_after(OldRef, 100, repeat),
    %% 被协助脱离范围触发集体协助取消
    local_boss_api:leave_boss_distance_trigger(LeaveIds, BossType, MonId),
    NewState = State#ob_act{
        klist = NewKlist1, support_klist = NewSupportList,
        bl_who = NewBLWho1, first_att = NewFirstAtt
    },
    case NewKlist1 =/= Klist of
        true  -> local_boss_api:restat_hurt_list(BossType, NewState);
        false -> NewState
    end.

%% 协助者脱离范围重新计算伤害列表
calc_deserted_support_role_leave(Klist, SupportList, []) -> {Klist, SupportList};
calc_deserted_support_role_leave(Klist, SupportList, [LeaveSupport|Rest]) ->
    case lists:keytake(LeaveSupport, #mon_atter.id, SupportList) of
        {value, Supporter = #mon_atter{support_id = SupportId, hurt = Hurt}, LeftSupport} ->
            case lists:keyfind(SupportId, #mon_atter.id, Klist) of
                Attacker = #mon_atter{hurt = TotalHurt} ->
                    DelHurt = TotalHurt - Hurt,
                    NewHurt = max(0, DelHurt),
                    NewAttacker = Attacker#mon_atter{hurt = NewHurt},
                    NewKlist = lists:keyreplace(SupportId, #mon_atter.id, Klist, NewAttacker),
                    %% 保留协助者的协助状态
                    NewSupportList = [Supporter#mon_atter{hurt = 0}|LeftSupport],
                    calc_deserted_support_role_leave(NewKlist, NewSupportList, Rest);
                _ ->
                    ?ERR("can find supported id", []),
                    calc_deserted_support_role_leave(Klist, LeftSupport, Rest)
            end;
        _ ->
            calc_deserted_support_role_leave(Klist, SupportList, Rest)
    end.


%% 更新归属
update_belong_who(State, OldBLWhos, Klist, FirstAttr) ->
    #ob_act{object = Minfo} = State,
    #scene_object{x = Xnow, y = Ynow, mon = Mon, scene = Scene, battle_attr = BA,
        scene_pool_id = PoolId, copy_id = CopyId, figure = #figure{lv = MonLv}} = Minfo,
    #scene_mon{boss = Boss, mid = Mid} = Mon,
    DropBoss = ?IS_DROP_BOSS(Boss),
    if
        Boss =:= ?MON_REWARD_BOSS ->
            OldBLWhos;
        not DropBoss ->
            OldBLWhos;
        true ->
            case drop:get_boss_drop_bltype(Mid, MonLv) of
                skip ->
                    OldBLWhos;
                {DropAlloc, BlType} ->
                    {Adds, Dels, NewBLWhosTmp} = drop:calc_boss_bl_who(DropAlloc, BlType, OldBLWhos, Klist, FirstAttr, BA#battle_attr.hp),
                    update_boss_bl_whos(Scene, PoolId, CopyId, Xnow, Ynow, Adds, Dels),
                    NewBLWhosTmp
            end
    end.

get_hurt_remove_time(?MON_DESERTED_BOSS) ->
    3000;
get_hurt_remove_time(?MON_HOME_BOSS) ->
    3000;
get_hurt_remove_time(?MON_HOME_GBOSS) ->
    3000;
get_hurt_remove_time(_BossType) ->
    5000.

notice_leave_hate_range(RoleIds, BossType, MonId) ->
    if BossType == ?MON_DESERTED_BOSS ->
            {ok, BinData} = pt_461:write(46105, [MonId]),
            ClsType = config:get_cls_type(),
            if ClsType == ?NODE_CENTER ->
                    [begin 
                        Node = clusters_center:find_node(mod_player_create:get_serid_by_id(RoleId)), 
                        lib_server_send:send_to_uid(Node, RoleId, BinData)
                    end || RoleId <- RoleIds];
                ClsType == ?NODE_GAME ->
                    [lib_server_send:send_to_uid(RoleId, BinData) || RoleId <- RoleIds];
                true ->
                    skip
            end;
        BossType == ?MON_HOME_BOSS orelse BossType == ?MON_HOME_GBOSS ->
            {ok, BinData} = pt_462:write(46203, [MonId]),
            [lib_server_send:send_to_uid(RoleId, BinData) || RoleId <- RoleIds];
        true ->
            skip
    end.

%% 是否可协助的怪物
is_supporting_boss(?MON_REWARD_BOSS) -> true;
is_supporting_boss(?MON_DESERTED_BOSS) -> true;
is_supporting_boss(_BossType) -> false.

%% 生成Boss对应的hurt_remove消息驱动事件信息
driven_info_hurt_remove(Boss) ->
    RemoveTime = get_hurt_remove_time(Boss),
    [{RemoveTime, 'hurt_remove'}].


%% -> NewAngle
calc_patrol_angle(OldPatrolAngle) ->
    AngleList = [0, 90, 180, 270],
    OptAngleList =
    case OldPatrolAngle of
        undefined -> AngleList;
        _ -> lists:delete(OldPatrolAngle, AngleList)
    end,
    urand:list_rand(OptAngleList).

%%%-------------------------------------------------------------------
%% 统计队伍和人最大伤害
%% 将伤害按照队伍统计
%% return: [{teamId, roleId, hurts, roleList}|_]
statistics_hurt_by_team(Klist) ->
    List = statistics_hurt_by_team(Klist, []),
    F = fun({_, _, HurtA, _}, {_, _, HurtB, _}) -> HurtA > HurtB end,
    lists:sort(F, List).
statistics_hurt_by_team([H|T], List) ->
    #mon_atter{id = RoleId, team_id = TeamId, hurt = RoleHurt} = H,
    NewList =
        case TeamId > 0 of
            true  ->
                case lists:keyfind(TeamId, 1, List) of
                    {TeamId, 0, Hurt, RoleList} ->
                        lists:keyreplace(TeamId, 1, List, {TeamId, 0, Hurt+RoleHurt, [H|RoleList]});
                    _ ->
                        [{TeamId, 0, RoleHurt, [H]}|List]
                end;
            false ->
                [{0, RoleId, RoleHurt, [H]}|List]
        end,
    statistics_hurt_by_team(T, NewList);
statistics_hurt_by_team([], List) -> List.

%% 玩家攻击，更新伤害列表
update_hurt_list(MonAtter, HurtList) ->
    #mon_atter{hurt = Hurt, id = RoleId, team_id = TeamId} = MonAtter,
    case TeamId > 0 of
        true  ->
            case lists:keyfind(TeamId, 1, HurtList) of
                {TeamId, 0, TeamHurt, RoleList} ->
                    NewHurt = TeamHurt + Hurt,
                    NewRoleList = case lists:keyfind(RoleId, #mon_atter.id, RoleList) of
                                      #mon_atter{hurt = RoleHurt} ->
                                          TotalRoleHurt = RoleHurt + Hurt,
                                          lists:keyreplace(RoleId, #mon_atter.id, RoleList, MonAtter#mon_atter{hurt = TotalRoleHurt});
                                      _ ->
                                          TotalRoleHurt = Hurt,
                                          [MonAtter|RoleList]
                                  end,
                    SendInfo = {TeamId, 0, NewHurt, NewRoleList},
                    {SendInfo, lists:keyreplace(TeamId, 1, HurtList, SendInfo), TotalRoleHurt};
                _ ->
                    SendInfo = {TeamId, 0, Hurt, [MonAtter]},
                    {SendInfo, [SendInfo|HurtList], Hurt}
            end;
        false ->
            case lists:keyfind(RoleId, 2, HurtList) of
                {0, RoleId, RoleHurt, _} ->
                    NewHurt = RoleHurt + Hurt,
                    SendInfo = {0, RoleId, NewHurt, [MonAtter#mon_atter{hurt = NewHurt}]},
                    {SendInfo, lists:keyreplace(RoleId, 2, HurtList, SendInfo), false};
                _ ->
                    SendInfo = {0, RoleId, Hurt, [MonAtter]},
                    {SendInfo, [SendInfo|HurtList], false}
            end
    end.
%% 回收单个怪物对应的进程字典数据
%% （如果怪物有特殊的进程字典数据，一定要在这里清理，否则会有数据泄漏）
%% 注意：
%%   一般各玩法特殊的数据，尽量少放数据到单独的进程字典，除非有必要
%%   可以考虑将特殊玩法中自定义数据存储到mod_args，则无须额外清理进程字典
%%   但是怪物通用型数据，则应考虑在#ob_act{}添加字段存储数据，而不是放在mod_args中
terminate_gc_proc_dict(MonId) ->
    erase({dungeon_hp_rate, MonId}),
    ok.

