%% ---------------------------------------------------------------------------
%% @doc lib_scene_object_ai
%% @author ming_up@foxmail.com
%% @since  2017-02-11
%% @deprecated 场景对象公共ai
%% ---------------------------------------------------------------------------
-module(lib_scene_object_ai).

-export([]).

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

-export([
        % mon_to_mon_ai/7
        get_next_step/8
        ,move/5
        ,move_dest_path/5
        ,dest_path/5
        ,auto_move/1
        ,is_combo/2
        ,trace_to_att/9
        ,set_ac_state/2
        ,combo/2
        ,get_select_option/1
        ,get_skill_owner_args/1
        ,select_a_skill/6
        ,mon_speak/2
        ,mon_speak_tv_msg/2
        ,mon_center_msg/2
        ,handle_assist_res/4
        ,handle_battle_res/4
        ,assist_combo_res/3
        ,battle_combo_res/3
        ,object_ac_skill/3
        ,object_ac_skill/4
        ,object_ac_skill_donot_trace/3
        ,walk_to_target/1
        ,create_auto_patrol_path/7
    ]).

-export([print/2]).

% %% 怪物走路ai触发(必须得有分组属性)
% mon_to_mon_ai(Scene, CopyId, Xpx, Ypx, Id, Pid, Group) ->
%     case Group /= 0 of
%         true ->
%             {X, Y} = lib_scene_calc:pixel_to_logic_coordinate(Xpx, Ypx),
%             List = case lib_mon:get_ai(Scene, CopyId, X, Y) of
%                 skip -> [];
%                 Other -> Other
%             end,
%             [Aid ! {'ai', Id, Pid, 1, Group}  || Aid <- List]; %% 数字1表示是怪物走路
%         false ->
%             skip
%     end.

%% 获取下一步的坐标
get_next_step(OX, OY, StrikingDistance, SceneId, CopyId, TX, TY, IsCheckBlock) ->
    case trace_line(OX, OY, TX, TY, StrikingDistance) of
        true -> attack;
        {LineNX, LineNY} when IsCheckBlock ->
            case scene_landform:is_block(SceneId, LineNX, LineNY) of
                false -> {LineNX, LineNY};
                _     -> a_star_px(OX, OY, SceneId, CopyId, StrikingDistance, TX, TY)
            end;
        {LineNX, LineNY} -> {LineNX, LineNY}
    end.

%% 怪物移动
move(X, Y, SceneObject, Speed, IsCheckBlock) ->
    #scene_object{x=MonX, y=MonY, scene=SceneId, copy_id=_CopyId} = SceneObject,
    IsBlocked = case IsCheckBlock of
        true  -> scene_landform:is_block(SceneId, X, Y);
        false -> false
    end,
    if
        IsBlocked -> block;
        true ->
            Dis = umath:distance({X, Y}, {MonX, MonY}),
            NewSceneObject = SceneObject#scene_object{x = X, y = Y},
            % 移动消息改为由场景进程收到消息后，自行打包和广播，不在这里打包发给场景
            % lib_scene_object:send_object_move_msg(MonX, MonY, NewSceneObject),
            lib_scene_object:change_xy(NewSceneObject),
            Time = case Dis == 0 of
                true  -> 1000;
                false -> round(Dis * 1000 / max(1, Speed))
            end,
            % LastTime = max(Time - 100, 100),
            LastTime = Time,
            {true, NewSceneObject, LastTime}
    end.

%% 直线移动
move_dest_path(SceneObject, OldX, OldY, X, Y) ->
    Dis = umath:distance({OldX, OldY}, {X, Y}),
    NewSceneObject = SceneObject#scene_object{x = X, y = Y},
    % 移动消息改为由场景进程收到消息后，自行打包和广播，不在这里打包发给场景
    % lib_scene_object:send_object_move_msg(OldX, OldY, NewSceneObject),
    lib_scene_object:change_xy(NewSceneObject),
    Time = case Dis == 0 of
        true  -> 500;
        false -> round(Dis * 1000 / max(1, NewSceneObject#scene_object.battle_attr#battle_attr.speed))
    end,
    {NewSceneObject, Time}.

-define(ONE_STEP_X, 120).
-define(ONE_STEP_Y, 60).
-define(ONE_STEP,   234).

%% 随机方向移动
auto_move(SceneObject) ->
    #scene_object{x=X, y=Y, scene=SceneId, copy_id=_CopyId, d_x=Dx, d_y=Dy} = SceneObject,
    {Xnext, Ynext} = case abs(X-Dx) > ?ONE_STEP_X*2 orelse abs(Y-Dy) > ?ONE_STEP_Y*2 of
        true -> {Dx, Dy};
        _ ->
            Angle = urand:rand(-1000, 1000)*math:pi()/1000,
            TmpX = max(0, round(X+?ONE_STEP*math:sin(Angle))),
            TmpY = max(0, round(Y+?ONE_STEP*math:cos(Angle))),
            case scene_landform:is_block(SceneId, TmpX, TmpY) of
                true -> {Dx, Dy};
                _ -> {TmpX, TmpY}
            end
    end,
    move_dest_path(SceneObject, X, Y, Xnext, Ynext).

%%先进入曼哈顿距离遇到障碍物再转向A*
%%每次移动2格
%%X1,Y1 原位置
%%X2,Y2 目标位置
trace_line(OX, OY, OX, OY, _StrikingDistance) -> true; %% 已经到达目标位置
trace_line(OX, OY, TX, TY, StrikingDistance) ->
    %% ActualPx = umath:distance({OX, OY}, {TX, TY}),
    StrikingDistance1 = max(100, StrikingDistance),
    Str2 = StrikingDistance1*StrikingDistance1,
    DX = TX-OX,
    DY = TY-OY,
    %% ?PRINT("ATTACK ~p~n", [{DX, DY, DX*DX + 4*DY*DY, Str2}]),
    %% 纵方向视角问题，计2倍长度（实际上前端大佬都不是这么算，所以不这样算了）
    %% case DX * DX + (2*DY) * (2*DY) =< Str2 of
    case DX*DX + DY*DY =< Str2 of
        %% case ActualPx =< StrikingDistance of
        true when StrikingDistance =< 100 -> {TX, TY};
        true -> true; %% 可直接攻击
        %% false when ActualPx =< ?ONE_STEP -> {TX, TY};
        false ->
            OriginalAngle   = math:atan2(DY, DX),
            %% 最后走的距离的角度微调，使怪物不容易扎堆
            %% GrapLen = max(ActualPx-StrikingDistance, 0),
            %% if GrapLen > ?ONE_STEP ->
            %% ?PRINT("StrikingDistance1 ~p~n", [{StrikingDistance, StrikingDistance1, DX, DY}]),
            if
                abs(DX)-?ONE_STEP_X*2 =< StrikingDistance1 andalso abs(DY)-?ONE_STEP_Y*2 =< StrikingDistance1 ->
                    %% 最后一步
                    Angle = OriginalAngle + (math:pi()/40 * urand:rand(1,9)) * urand:rand(-1,1),
                    TanA = math:tan(Angle),
                    TanA2 = TanA*TanA,
                    Strl2 = (StrikingDistance1-10)*(StrikingDistance1-10),
                    X0 = trunc(TX+math:sqrt(Strl2/(1+4*TanA2))*(-DX div max(1, abs(DX)))),
                    Y0 = trunc(TY+math:sqrt(Strl2*TanA2/(1+4*TanA2))*(-DY div max(1, abs(DY)))),
                    %%?PRINT("X0, Y0 ~p~n", [{X0-TX, Y0-TY}]),
                    ok;
                true ->
                    X0 = round(OX+?ONE_STEP*math:cos(OriginalAngle)),
                    Y0 = round(OY+?ONE_STEP*math:sin(OriginalAngle))
            end,
            %% if
            %%    abs(DX)+abs(DY) > 2*?ONE_STEP ->
            %%        Angle  = OriginalAngle,
            %%        MovePx = ?ONE_STEP;
            %%   true ->
            %%        Angle = OriginalAngle + (math:pi()/40 * urand:rand(1,9)) * urand:rand(-1,1),
            %%        MovePx = ?ONE_STEP %(px)
            %% end,
            %% if
            %%    Angle /= 0 ->
            %%        X0 = round(OX+MovePx*math:cos(Angle)),
            %%        Y0 = round(OY+MovePx*math:sin(Angle));
            %%    OY == TY andalso OX /= TX  -> %% Y轴相等
            %%        X0 = round(OX + min(?ONE_STEP_X,MovePx) * (DX div abs(DX))),
            %%        Y0 = OY;
            %%    OX == TX andalso OY /= TY ->  %% X轴相等
            %%        Y0 = round(OY + min(?ONE_STEP_Y,MovePx) * (DY div abs(DY))),
            %%        X0 = OX;
            %%    true -> %% 其他情况
            %%        X0 = OX + min(?ONE_STEP_X,MovePx),
            %%        Y0 = OY
            %% end,
            %% ?PRINT("x0, Y0 ~p~n", [{X0, Y0}]),
            {max(0, X0), max(0, Y0)}
    end.

%% A*算法
a_star_px(NowXpx, NowYpx, SceneId, CopyId, AttArea, TargetXpx, TargetYpx) ->
    %% 转换坐标
    {NowXl, NowYl}       = lib_scene_calc:pixel_to_logic_coordinate(NowXpx, NowYpx),
    {TargetXl, TargetYl} = lib_scene_calc:pixel_to_logic_coordinate(TargetXpx, TargetYpx),
    %% 计算下一个点
    {NextXl, NextYl}     = a_star(NowXl, NowYl, SceneId, CopyId, AttArea, TargetXl, TargetYl),
    lib_scene_calc:logic_to_pixel_coordinate(NextXl, NextYl).

%% A*算法
a_star(X, Y, SceneId, _CopyId, _, TX, TY) ->
    %% 周围8格子
    FirstP = {X+1, Y},
    FirstDis = math:pow(X - TX, 2) + math:pow(Y - TY, 2),
    OtherPList = [
        {X+1, Y},   {X,   Y+1}, {X-1, Y},   {X,   Y-1},
        {X+1, Y+1}, {X-1, Y+1}, {X+1, Y-1}, {X-1, Y-1}
    ],

    F = fun(P, {TmpP0, DisP0}) ->
            {PX, PY} = P,
            case scene_landform:is_block(SceneId, PX, PY) of
                false ->    %% 非障碍点
                    DisP = math:pow(PX - TX, 2) + math:pow(PY - TX, 2),
                    case DisP < DisP0 of
                        true -> {P, DisP};
                        _    -> {TmpP0, DisP0}
                    end;
                _ ->        %% 障碍点过滤
                    {TmpP0, DisP0}
            end
    end,
    {NP, _} = lists:foldl(F, {FirstP, FirstDis}, OtherPList),
    NP.

%% 直线坐标数组
dest_path(_, _, _, _, 0) -> [];
dest_path(OldX, OldY, OldX, OldY, _) -> [];
dest_path(OldX, OldY, DestX, DestY, _Speed) ->
    %% 计算移动路径点时的单位长度(像素)
    LenUnit = 120,
    Fun =
    if
        OldX =:= DestX ->
            DisLogic = util:ceil(abs(DestY - OldY)/LenUnit),
            case DestY > OldY of
                true  ->
                    fun(I, Acc) ->
                            Y = OldY + I * LenUnit,
                            {ok, [{OldX, Y} | Acc]}
                    end;
                false ->
                    fun(I, Acc) ->
                            Y = OldY - I * LenUnit,
                            {ok, [{OldX, Y} | Acc]}
                    end
            end;
        OldY =:= DestY ->
            DisLogic = util:ceil(abs(DestX - OldX)/LenUnit),
            case DestX > OldX of
                true  ->
                    fun(I, Acc) ->
                            X = OldX + I * LenUnit,
                            {ok, [{X, OldY} | Acc]}
                    end;
                false ->
                    fun(I, Acc) ->
                            X = OldX - I * LenUnit,
                            {ok, [{X, OldY} | Acc]}
                    end
            end;
        true ->
            %% 斜率A
            A = (DestY - OldY) / (DestX - OldX),
            %% 常量B
            B = DestY - A * DestX,
            %% 逻辑坐标距离
            DisX = util:ceil(abs(DestX - OldX)/LenUnit),
            DisY = util:ceil(abs(DestY - OldY)/LenUnit),
            %% 以逻辑距离长的一边，按顺序生成路径坐标点（对比使用短边，可避免生成过于稀疏路径坐标点集合）
            if
                DisX >= DisY ->
                    DisLogic = DisX,
                    case DestX > OldX of
                        true  ->
                            fun(I, Acc) ->
                                    X = OldX + I * LenUnit,
                                    Y = round(A * X + B),
                                    {ok, [{X, Y} | Acc]}
                            end;
                        false ->
                            fun(I, Acc) ->
                                    X = OldX - I * LenUnit,
                                    Y = round(A * X + B),
                                    {ok, [{X, Y} | Acc]}
                            end
                    end;
                true ->
                    DisLogic = DisY,
                    case DestY > OldY of
                        true  ->
                            fun(I, Acc) ->
                                    Y = OldY + I * LenUnit,
                                    X = round( (Y - B) / A ),
                                    {ok, [{X, Y} | Acc]}
                            end;
                        false ->
                            fun(I, Acc) ->
                                    Y = OldY - I * LenUnit,
                                    X = round( (Y - B) / A ),
                                    {ok, [{X, Y} | Acc]}
                            end
                    end
            end
    end,
    {ok, Path} = util:for(1, DisLogic, Fun, []),
    case Path of
        [{DestX, DestY} | _Others] ->
            lists:reverse(Path);
        [{_, _} | Others] ->
            %% 由于DisLogic采用ceil向上取整，
            %% 所以按前面公式计算出的最后一个路径点不可能在{DestX, DestY}之前，只能是跑过头了
            %% 可以去除该坐标点，以目标坐标点为终点即可()
            lists:reverse([{DestX, DestY} | Others]);
        _ ->
            %% 由于前面已经排除原点与目标点相同的情况，所以实际上计算出的路径必然不为空列表
            %% 此处实际上是不可能发生的
            []
    end.

mon_speak(Minfo, Args) ->
    [LanId|_] = Args,
    #scene_object{id = Id, scene=Scene, scene_pool_id=ScenePoolId, copy_id=CopyId, x=X, y=Y} = Minfo,
    Msg = utext:get(LanId),
    {ok, BinData} = pt_120:write(12023,[Id, Msg]),
    lib_server_send:send_to_area_scene(Scene, ScenePoolId, CopyId, X, Y, BinData).

mon_speak_tv_msg(Minfo, Args) ->
    [ModId, LanId|_] = Args,
    #scene_object{scene=Scene, scene_pool_id=ScenePoolId, copy_id=CopyId, x=X, y=Y, figure = #figure{name = Name}} = Minfo,
    {ok, BinData} = pt_110:write(11064,[ModId, LanId, Name]),
    lib_server_send:send_to_area_scene(Scene, ScenePoolId, CopyId, X, Y, BinData).

mon_center_msg(Minfo, Args) ->
    [ModId, LanId|_] = Args,
    #scene_object{scene=Scene, scene_pool_id=ScenePoolId, copy_id=CopyId, x=X, y=Y} = Minfo,
    {ok, BinData} = pt_110:write(11067,[ModId, LanId]),
    lib_server_send:send_to_area_scene(Scene, ScenePoolId, CopyId, X, Y, BinData).

%% 选择一个技能
select_a_skill(SkillL, SkillCd, StrikingDistance, SkillLinkInfo, IsSilence, AttType) ->
    Now = utime:longunixtime(),
    case SkillL of
        [] when AttType == 1 ->  %% 远程
            format_a_skill(?MON_DEFAUL_SKILL, 1, StrikingDistance, SkillLinkInfo);
        [] ->                    %% 近程
            format_a_skill(?MON_DEFAUL_SKILL, 1, StrikingDistance, SkillLinkInfo);
        _  ->
            case filter_skills_cd(SkillCd, SkillL, IsSilence, Now, 0) of
                {[], NextSelectTime} ->
                    {false, NextSelectTime};
                {NSkillL, _} ->
                    {SkillId, SkillLv, NSkillLinkInfo} = rand_a_skill(NSkillL, SkillLinkInfo),
                    format_a_skill(SkillId, SkillLv, StrikingDistance, NSkillLinkInfo)
            end
    end.

%% 移除正在cd的技能
filter_skills_cd([], SkillList, IsSilence, _Now, NextSelectTime) ->
    F = fun({SkillId, _SkillLv}) ->
            if
                IsSilence == false -> true;
                %% 沉默只给释放普攻
                true -> lib_skill_api:is_normal(SkillId)
            end
    end,
    NewSkillList = lists:filter(F, SkillList),
    {NewSkillList, NextSelectTime};
filter_skills_cd([{SkillId, EndTime}|T], SkillList, IsSilence, Now, NextSelectTime) ->
    case Now >= EndTime  of
        true -> filter_skills_cd(T, SkillList, IsSilence, Now, NextSelectTime);
        false ->
            GapTime = ?IF(EndTime - Now < NextSelectTime orelse NextSelectTime == 0, EndTime - Now, NextSelectTime),
            filter_skills_cd(T, lists:keydelete(SkillId, 1, SkillList), IsSilence, Now, GapTime)
    end.

%% %% 随机一个技能释放:考虑优先使用近战技能
rand_a_skill([], _SkillLinkInfo) ->
    {false, 5000};
rand_a_skill(SkillL, SkillLinkInfo) ->
    F = fun({SkillId, SkillLv}, {TmpDis, Sk, NormalSk}) ->
            case data_skill:get(SkillId, SkillLv) of
                #skill{type = ?SKILL_TYPE_PASSIVE} ->
                    {TmpDis, Sk, NormalSk};
                #skill{is_normal = 0, range = 1, lv_data = #skill_lv{distance = Dis, area = Area}}
                when Dis+0.8*Area > TmpDis -> %% 圆形技能
                    {trunc(Dis+0.8*Area), {SkillId, SkillLv}, NormalSk };
                #skill{is_normal = 0, lv_data = #skill_lv{distance = Dis}} when Dis > TmpDis -> %% 技能
                    {Dis, {SkillId, SkillLv}, NormalSk };
                #skill{is_normal = 1, skill_link = SkillLink} -> %% 普攻，不会被覆盖
                    {TmpDis, Sk, {SkillId, SkillLv, SkillLink} };
                _ ->
                    {TmpDis, Sk, NormalSk}
            end
    end,
    {_, {SkId, SkLv}, {NormalSkId, NormalSkLv, NormalSkLink}} = lists:foldl(F, {-1, {0, 0}, {0, 0, []}}, SkillL),
    case SkId of
        0 ->
            {SkillId, SkillLinkInfo1} = skill_link_change_skill(NormalSkLink, NormalSkId, SkillLinkInfo),
            {SkillId, NormalSkLv, SkillLinkInfo1};
        _  ->
            {SkId, SkLv, SkillLinkInfo}
    end.

%% 获取技能数据
format_a_skill(SkillId, SkillLv, StrikingDistance, SkillLinkInfo) ->
    case data_skill:get(SkillId, SkillLv) of
        #skill{type = Type, refind_target = ReFindT, is_normal = IsNormal, time = SpellTime} = SkillConfig ->
            LastStrikingDis = case lib_battle:calc_skill_distance(SkillConfig) of
                                  0 -> max(150, StrikingDistance);
                                  SkillDis -> SkillDis
                              end,
            {SkillId, SkillLv, LastStrikingDis, Type, ReFindT, SpellTime, SkillLinkInfo, IsNormal};
        _ ->
            {false, 5000}
    end.

% skill_link_change_skill([], SkillId, _) -> {SkillId, []};
skill_link_change_skill([_|T], SkillId, SkillLinkInfo) ->
    case SkillLinkInfo of
        [] -> {SkillId, T};
        [H1|T1] -> {H1, T1}
    end;
skill_link_change_skill(_, SkillId, _) -> {SkillId, []}.

is_combo(_, []) -> false;
is_combo(MainSkillId, SkillCombo) ->
    case lists:keyfind(MainSkillId, #skill_combo.main_skill_id, SkillCombo) of
        #skill_combo{main_skill_lv=MainSkillLv, next_time=NextTime, combo_list=[{NextSkillId, _, _}|_]} ->
            {NextSkillId, MainSkillLv, NextTime};
        _ -> false
    end.

print(_SceneObject, _D) -> ok.
% ConfigId = SceneObject#scene_object.config_id,
% Sign = SceneObject#scene_object.sign,
% if
%     Sign == ?BATTLE_SIGN_DUMMY ->
%         ?PRINT("####### handle_print:~p~n", [{SceneObject#scene_object.id, ConfigId, D}]);
%     true -> skip
% end.

%% BattleArgs: {target, AtterType, Id} | {xy, X, Y}...
%% return: {State, Args}
%% State: trace|back|error 对象进程要处理这3个返回状态
trace_to_att(_SceneObject, _TX, _TY, _BattleArgs,
    _NextAttTime, NextMoveTime, NowTime,
    _SkillLinkInfo, _CheckBlock) when NextMoveTime > NowTime -> %% 下一次行动
    % old: Ref1 = lib_scene_object:send_event_after(Ref, NextMoveTime-NowTime, repeat),
    NewStateDriven = mon_state_util:conv_delay_driven({NextMoveTime - NowTime, repeat}, NowTime),
    {trace, [{state_driven, NewStateDriven}]};

trace_to_att(SceneObject, TX, TY, BattleArgs, NextAttTime, _NextMoveTime, NowTime, SkillLinkInfo, CheckBlock) ->
    #scene_object{sign = ObjectSign, id = ObjectId, aid = Aid,
        scene = SceneId, scene_pool_id = ScenePoolId, x = X, y = Y, copy_id = CopyId,
        battle_attr = BA, att_time = GapAttTime,
        skill = SkillL, temp_skill = TempSkillL, skill_cd = SkillCd,
        striking_distance = StrikingDistance, att_type = AttType} = SceneObject,
    IsSilence = lib_skill_buff:is_silence(BA#battle_attr.other_buff_list, NowTime),
    case select_a_skill(TempSkillL++SkillL, SkillCd, StrikingDistance, SkillLinkInfo, IsSilence, AttType) of
        {false, NextSelectTime} ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, NextSelectTime+50, repeat),
            NewStateDriven = mon_state_util:conv_delay_driven({NextSelectTime + 50, repeat}, NowTime),
            {trace, [{state_driven, NewStateDriven}]};
        %% 辅助技能
        {SkillId, SkillLv, _, ?SKILL_TYPE_ASSIST, IsReFindT, SpellTime, _, _} ->
            SelectedSkillInfo
            = #selected_skill{
                att_time = NowTime+GapAttTime,
                skill_id = SkillId,
                spell_time = SpellTime,
                is_refind_t = IsReFindT
            },
            % Res = mod_scene_agent:apply_call_with_scene(SceneId, ScenePoolId, lib_battle, object_assist, [ObjectId, SkillId, SkillLv]),
            % handle_assist_res(SelectedSkillInfo, SceneObject, Ref, Res);
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, object_assist_cast, [Aid, ObjectId, SkillId, SkillLv, []]),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 5000, repeat),
            NewStateDriven = mon_state_util:conv_delay_driven({5000, repeat}, NowTime),
            {back, [{state_driven, NewStateDriven}, {selected_skill, {SkillId, SelectedSkillInfo}}]};
        %% 主动技能
        {SkillId, SkillLv, SkillDistance, ?SKILL_TYPE_ACTIVE, IsReFindT, SpellTime, SkillLinkInfo1, _IsNormal} ->
            %% 判断是否可以攻击
            IsNormal = lib_skill_api:is_normal(SkillId),
            case lib_scene_object_ai:get_next_step(X, Y, SkillDistance, SceneId, CopyId, TX, TY, CheckBlock) of
                attack when NowTime > NextAttTime ->
                    case lib_skill_buff:is_can_attack(IsNormal, ObjectSign, BA#battle_attr.other_buff_list, NowTime) of
                        {false, _K, AttWaitMs} ->
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, AttWaitMs, repeat),
                            NewStateDriven = mon_state_util:conv_delay_driven({AttWaitMs, repeat}, NowTime),
                            {trace, [{state_driven, NewStateDriven}]};
                        true ->
                            SelectedSkillInfo
                            = #selected_skill{
                                att_time = NowTime+GapAttTime,
                                skill_id = SkillId,
                                spell_time = SpellTime,
                                is_refind_t = IsReFindT,
                                link_info = SkillLinkInfo1
                            },
                            SelectOption = get_select_option(SceneObject),
                            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, object_battle_cast, [Aid, ObjectSign, ObjectId, BattleArgs, SkillId, SkillLv, [], 0, SelectOption]),
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, 5000, repeat),
                            NewStateDriven = mon_state_util:conv_delay_driven({5000, repeat}, NowTime),
                            {back, [{state_driven, NewStateDriven}, {selected_skill, {SkillId, SelectedSkillInfo}}]}
                    end;

                %% 没有到出手时间
                attack ->
                    % old: Ref1 = lib_scene_object:send_event_after(Ref, max(200, NextAttTime-NowTime), repeat),
                    DelayTime = max(200, NextAttTime - NowTime),
                    NewStateDriven = mon_state_util:conv_delay_driven({DelayTime, repeat}, NowTime),
                    {trace, [{state_driven, NewStateDriven}]};

                %% 移动
                {NextX, NextY} ->
                    #battle_attr{speed = Speed, attr = _Attr} = BA,
                    case lib_skill_buff:is_can_move(BA#battle_attr.other_buff_list, Speed, NowTime) of
                        {false, MoveWaitMs} ->
                            % old: Ref1 = lib_scene_object:send_event_after(Ref, MoveWaitMs, repeat),
                            NewStateDriven = mon_state_util:conv_delay_driven({MoveWaitMs, repeat}, NowTime),
                            {trace, [{state_driven, NewStateDriven}]};
                        false -> %% 本身速度为0：一般为策划填错配置，本身可反击或主动，但是移动速度为0
                            {error, [{att, undefined}] };
                        true ->
                            case lib_scene_object_ai:move(NextX, NextY, SceneObject, Speed, false) of
                                block ->
                                    % old: Ref1 = lib_scene_object:send_event_after(Ref, 500, repeat),
                                    NewStateDriven = mon_state_util:conv_delay_driven({500, repeat}, NowTime),
                                    {back, [{state_driven, NewStateDriven}, {att, undefined}]};
                                {true, NewObj, Time} ->
                                    % old: Ref1 = lib_scene_object:send_event_after(Ref, Time, repeat),
                                    NewStateDriven = mon_state_util:conv_delay_driven({Time, repeat}, NowTime),
                                    UpdateArgs = [
                                        {obj, NewObj},
                                        {state_driven, NewStateDriven},
                                        {move_begin_time, NowTime},
                                        {each_move_time, Time},
                                        {o_point, {X, Y}}
                                    ],
                                    {trace, UpdateArgs}
                            end
                    end
            end;
        _R ->
            % ?PRINT("sign:~w, id=~w, [SceneObject#scene_object.sign, ObjectId, SkillId, SkillLv, SkillType]:~w~n",
            %     [SceneObject#scene_object.sign, ObjectId, _R]),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 500, repeat),
            NewStateDriven = mon_state_util:conv_delay_driven({500, repeat}, NowTime),
            {back, [{state_driven, NewStateDriven}]}
    end.

combo({OX, OY, X, Y, SkillId, SkillLv, _IsReFindT, OldRadian}, State) ->
    #ob_act{att=Att, object=Object, selected_skill = SelectedSkillMap} = State,
    #scene_object{sign=Sign, id=Id, scene=SceneId, scene_pool_id=ScenePoolId, x=Xnow, y=Ynow, aid = Aid} = Object,
    case data_skill:get(SkillId, SkillLv) of
        #skill{type=?SKILL_TYPE_ACTIVE, is_combo=1,
            refind_target=IsReFindT, mod=Mod
        } ->
            if
                Mod == 1 ->
                    Args = case Att of
                        #{sign:=TSign, id:=Tid} -> {target, TSign, Tid};
                        _ -> {xy, OX, OY, X, Y}
                    end,
                    Radian = OldRadian;
                IsReFindT == 1 ->
                    Args = case Att of
                        #{sign:=TSign, id:=Tid} -> {target, TSign, Tid};
                        _ -> {xy, Xnow, Ynow, X, Y}
                    end,
                    Radian = 0;
                true ->
                    Args = {xy, OX, OY, X, Y},
                    Radian = OldRadian
            end,
            SelectedSkillInfo = #selected_skill{skill_id = SkillId, is_refind_t = IsReFindT},
            SelectOption = get_select_option(Object),
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, combo_battle_cast, [Aid, Sign, Id, Args, SkillId, SkillLv, Radian, SelectOption]),
            State#ob_act{selected_skill = SelectedSkillMap#{SkillId => SelectedSkillInfo}};
        #skill{type=?SKILL_TYPE_ASSIST, is_combo=1} ->
            SelectedSkillInfo = #selected_skill{skill_id = SkillId, is_refind_t = _IsReFindT},
            % Res = mod_scene_agent:apply_call_with_scene(SceneId, ScenePoolId, lib_battle, object_assist, [Id, SkillId, SkillLv]),
            % assist_combo_res(SelectedSkillInfo, State, Res);
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, combo_assist_cast, [Aid, Id, SkillId, SkillLv]),
            State#ob_act{selected_skill = SelectedSkillMap#{SkillId => SelectedSkillInfo}};
        _ -> State
    end.

%% 释放特殊副技能
% find_target_combo({BuffAttSkillId, BuffAttSkillIdLv, BuffAttGap, BuffAttCount, _Radain}=OldFindTargetArgs, State) ->
%     #ob_act{object=Object} = State,
%     #scene_object{sign=Sign, id=Id, scene=SceneId, scene_pool_id=ScenePoolId, copy_id=CopyId,
%                   x=X, y=Y, aid=Aid, skill_owner=SkillOwner, battle_attr=BA} = Object,

%     case lib_skill_buff:is_buff_att(BA#battle_attr.other_buff_list, utime:longunixtime()) of
%         false -> State;
%         _ ->
%             {IsConut, State1}
%                 = case data_skill:get(BuffAttSkillId, BuffAttSkillIdLv) of
%                       #skill{lv_data=#skill_lv{area=Area}} ->
%                           {OwnerSign, OwnerId} = get_skill_owner_args(SkillOwner),
%                           OwnerArgs = {BA#battle_attr.group, Sign, Id, OwnerSign, OwnerId},
%                           case mod_scene_agent:apply_call(SceneId, ScenePoolId, lib_scene_calc, get_ellipse_object_for_battle, [CopyId, X, Y, Area, OwnerArgs]) of
%                               [_|_] = List ->
%                                   Len = length(List),
%                                   case lists:nth(urand:rand(1, Len), List) of
%                                       #ets_scene_user{id=TId} -> TSign = ?BATTLE_SIGN_PLAYER;
%                                       #scene_object{sign=TSign, id=TId} -> skip
%                                   end,
%                                   TargetArgs = {target, TSign, TId},
%                                   case mod_scene_agent:apply_call_with_scene(SceneId, ScenePoolId, lib_battle, object_start_battle,
%                                                                              [Sign, Id, TargetArgs, BuffAttSkillId, BuffAttSkillIdLv, 0]) of
%                                       {false, _ErrCode} ->  {false, State};
%                                       {true, _, _OX1, _OY1, _AttX, _AttY, _MainSkillId, _} ->
%                                           case mod_scene_agent:apply_call_with_scene(SceneId, ScenePoolId, lib_battle, object_start_battle,
%                                                                                      [Sign, Id, TargetArgs, 300203, BuffAttSkillIdLv, 0]) of
%                                               {false, _ErrCode} ->  {false, State};
%                                               {true, BackObj, _OX2, _OY2, _AttX2, _AttY2, _MainSkillId2, _} ->
%                                                   {true, State#ob_act{object=BackObj}}
%                                           end
%                                   end;

%                               _ -> {false, State}
%                           end;
%                       _ -> {false, State}
%                   end,
%             case BuffAttCount-1 > 0 of
%                 true when IsConut ->
%                     FindTargetArgs = {BuffAttSkillId, BuffAttSkillIdLv, BuffAttGap, BuffAttCount-1, 0},
%                     erlang:send_after(BuffAttGap, Aid, {'find_target_combo', FindTargetArgs});
%                 true ->
%                     erlang:send_after(BuffAttGap, Aid, {'find_target_combo', OldFindTargetArgs});
%                 _ -> skip
%             end,
%             State1
%     end.

set_ac_state([H|T], ObAct) ->
    NewObAct = case H of
        {obj, Value} -> ObAct#ob_act{object = Value};
        % old: {ref, Value} -> ObAct#ob_act{ref = Value};
        {state_driven, Value} -> ObAct#ob_act{state_driven = Value};
        {general_driven, Value} -> ObAct#ob_act{general_driven = Value};
        {att, Value} -> ObAct#ob_act{att = Value};
        {att_time, Value} -> ObAct#ob_act{next_att_time = Value};
        {move_time, Value} -> ObAct#ob_act{next_move_time = Value};
        {skill_link_info, Value} -> ObAct#ob_act{skill_link_info = Value};
        {each_move_time, Value} -> ObAct#ob_act{each_move_time = Value};
        {o_point, Value} -> ObAct#ob_act{o_point = Value};
        {move_begin_time, Value} -> ObAct#ob_act{move_begin_time = Value};
        {selected_skill, {K, V}} ->
            SelectedSkillMap = ObAct#ob_act.selected_skill,
            ObAct#ob_act{selected_skill = SelectedSkillMap#{K => V}};
        _ -> ObAct
    end,
    set_ac_state(T, NewObAct);
set_ac_state([], NewObAct) -> NewObAct.

%% 获取skill_owern关键参数
get_skill_owner_args(#skill_owner{id=Id, sign=Sign}) -> {Sign, Id};
get_skill_owner_args(_) -> {0, 0}.

handle_assist_res(SelectedSkillInfo, SceneObject, OldGeneralDriven, Res) ->
    % #ob_act{object = SceneObject, ref = Ref, selected_skill = SelectedSkillInfo} = State,
    #scene_object{x = X, y = Y, temp_skill = TempSkillL} = SceneObject,
    #selected_skill{
        att_time = AttTime,
        skill_id = SkillId,
        spell_time = SpellTime,
        is_refind_t = IsReFindT
    } = SelectedSkillInfo,
    NowTimeMS = utime:unixtime(ms),
    case Res of
        #skill_return{
            aer_info = BackData,
            % rx = RX, ry = RY, tx = TX, ty = TY, radian = Radian,
            used_skill = SkillId,
            main_skill = MainSkillId
        } ->
            BackObj = lib_battle_api:update_by_slim_back_data(SceneObject, BackData),
            GeneralDriven =
            case is_combo(MainSkillId, BackObj#scene_object.skill_combo) of
                false -> OldGeneralDriven;
                {NextSkillId, NextSkillLv, NextSkillTime} -> %% combo技能施放
                    ComboArgs = {X, Y, X, Y, NextSkillId, NextSkillLv, IsReFindT, 0},
                    % old: erlang:send_after(NextSkillTime, Aid, {'combo', ComboArgs})
                    DelayDrivenInfo = [{NextSkillTime, {'combo', ComboArgs}}],
                    mon_state_util:update_general_driven(OldGeneralDriven, DelayDrivenInfo, NowTimeMS)
            end,
            TempSkillL1 = lists:keydelete(SkillId, 1, TempSkillL),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, max(200, SpellTime+100), repeat),
            StateDriven = mon_state_util:conv_delay_driven({max(200, SpellTime+100), repeat}, NowTimeMS),
            UpdateArgs = [
                {state_driven, StateDriven},
                {general_driven, GeneralDriven},
                {obj, BackObj#scene_object{temp_skill=TempSkillL1} },
                {att_time, AttTime},
                {selected_skill, {SkillId, []}}
            ],
            {trace, UpdateArgs};

        {false, _Err} ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1000, repeat}, NowTimeMS),
            {trace, [{state_driven, StateDriven}, {selected_skill, {SkillId, []}}]};
        _O ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1000, repeat}, NowTimeMS),
            {back, [{state_driven, StateDriven}, {att, undefined}, {selected_skill, {SkillId, []}}]}
    end.

handle_battle_res(SelectedSkillInfo, SceneObject, OldGeneralDriven, Res) ->
    #selected_skill{
        att_time = AttTime,
        skill_id = SkillId,
        spell_time = SpellTime,
        is_refind_t = IsReFindT,
        link_info = SkillLinkInfo1
    } = SelectedSkillInfo,
    #scene_object{temp_skill = TempSkillL} = SceneObject,
    NowTimeMS = utime:unixtime(ms),
    case Res of
        #skill_return{
            aer_info = BackData,
            rx = OX, ry = OY, tx = AttX, ty = AttY, radian = Radian,
            used_skill = SkillId,
            main_skill = MainSkillId
        } ->
            % {true, BackData, OX, OY, AttX, AttY, MainSkillId, Radian} ->
            BackObj = lib_battle_api:update_by_slim_back_data(SceneObject, BackData),
            GeneralDriven =
            case lib_scene_object_ai:is_combo(MainSkillId, BackObj#scene_object.skill_combo) of
                false -> OldGeneralDriven;
                {NextSkillId, NextSkillLv, NextSkillTime} ->
                    ComboArgs = {OX, OY, AttX, AttY, NextSkillId, NextSkillLv, IsReFindT, Radian},
                    % old: erlang:send_after(NextSkillTime, Aid, {'combo', ComboArgs})
                    DelayDrivenInfo = [{NextSkillTime, {'combo', ComboArgs}}],
                    mon_state_util:update_general_driven(OldGeneralDriven, DelayDrivenInfo, NowTimeMS)
            end,
            %% 技能施放时间
            SpellTime1 = ?IF(SpellTime<1000, SpellTime+500, SpellTime),
            TempSkillL1 = lists:keydelete(SkillId, 1, TempSkillL),
            % old: Ref1 = lib_scene_object:send_event_after(Ref, SpellTime1, repeat),
            StateDriven = mon_state_util:conv_delay_driven({SpellTime1, repeat}, NowTimeMS),
            UpdateArgs = [
                {state_driven, StateDriven},
                {general_driven, GeneralDriven},
                {obj, BackObj#scene_object{temp_skill=TempSkillL1}},
                {skill_link_info, SkillLinkInfo1},
                {att_time, AttTime},
                {selected_skill, {SkillId, []}}
            ],
            {trace, UpdateArgs};
        {false, _ErrCode} ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1000, repeat}, NowTimeMS),
            {trace, [{state_driven, StateDriven}, {att_time, AttTime}, {selected_skill, {SkillId, []}}] };
        _O ->
            % old: Ref1 = lib_scene_object:send_event_after(Ref, 1000, repeat),
            StateDriven = mon_state_util:conv_delay_driven({1000, repeat}, NowTimeMS),
            {back, [{state_driven, StateDriven}, {att, undefined}, {selected_skill, {SkillId, []}}] }
    end.

battle_combo_res(SelectedSkillInfo, State, Res) ->
    #ob_act{object = Object} = State,
    #selected_skill{is_refind_t = IsReFindT} = SelectedSkillInfo,
    case Res of
        #skill_return{
            aer_info = BackData,
            rx = OX1, ry = OY1, tx = AttX, ty = AttY, radian = Radian,
            main_skill = MainSkillId
        } ->
            % {true, BackData, OX1, OY1, AttX, AttY, MainSkillId, Radian} ->
            BackObj = lib_battle_api:update_by_slim_back_data(Object, BackData),
            case lib_scene_object_ai:is_combo(MainSkillId, BackObj#scene_object.skill_combo) of
                false ->
                    State#ob_act{object = BackObj};
                {NextSkillId, NextSkillLv, NextSkillTime} ->
                    ComboArgs = {OX1, OY1, AttX, AttY, NextSkillId, NextSkillLv, IsReFindT, Radian},
                    % old: erlang:send_after(NextSkillTime, Aid, {'combo', ComboArgs}),
                    StateDriven = mon_state_util:conv_delay_driven({NextSkillTime, {'combo', ComboArgs}}),
                    State#ob_act{object = BackObj, state_driven = StateDriven}
            end;
        {false, _ErrCode} -> State;
        _O -> State
    end.

assist_combo_res(SelectedSkillInfo, State, Res) ->
    #ob_act{object = Object} = State,
    #scene_object{x=X, y=Y} = Object,
    #selected_skill{is_refind_t = IsReFindT} = SelectedSkillInfo,
    case Res of
        #skill_return{
            aer_info = BackData,
            % rx = RX, ry = RY, tx = TX, ty = TY, radian = Radian,
            % used_skill = SkillId,
            main_skill = MainSkillId
        } ->
            BackObj = lib_battle_api:update_by_slim_back_data(Object, BackData),
            case lib_scene_object_ai:is_combo(MainSkillId, BackObj#scene_object.skill_combo) of
                false ->
                    State#ob_act{object = BackObj};
                {NextSkillId, NextSkillLv, NextSkillTime} ->
                    ComboArgs = {X, Y, X, Y, NextSkillId, NextSkillLv, IsReFindT, 0},
                    % old: erlang:send_after(NextSkillTime, Aid, {'combo', ComboArgs}),
                    StateDriven = mon_state_util:conv_delay_driven({NextSkillTime, {'combo', ComboArgs}}),
                    State#ob_act{object = BackObj, state_driven = StateDriven}
            end;
        {false, _Err} ->  State;
        _ -> State
    end.

%% 直接释放一个技能
object_ac_skill(State, SkillId, SkillLv) ->
    object_ac_skill(State, SkillId, SkillLv, []).

object_ac_skill(State, SkillId, SkillLv, SkillAdjust) ->
    #ob_act{skill_link_info = SkillLinkInfo, selected_skill = SelectedSkillMap, object = Object} = State,
    #scene_object{sign = ObjectSign, id = ObjectId, aid = Aid, att_time = GapAttTime,
        scene = SceneId, scene_pool_id = ScenePoolId} = Object,
    NowTime = utime:longunixtime(),
    case data_skill:get(SkillId, SkillLv) of
        #skill{type = ?SKILL_TYPE_ASSIST, refind_target = IsReFindT, time = SpellTime} ->
            SelectedSkillInfo
            = #selected_skill{
                att_time = NowTime+GapAttTime,
                skill_id = SkillId,
                spell_time = SpellTime,
                is_refind_t = IsReFindT
            },
            % Res = mod_scene_agent:apply_call_with_scene(SceneId, ScenePoolId, lib_battle, object_assist, [ObjectId, SkillId, SkillLv]),
            % handle_assist_res(SelectedSkillInfo, SceneObject, Ref, Res);
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, object_assist_cast, [Aid, ObjectId, SkillId, SkillLv, SkillAdjust]),
            State#ob_act{selected_skill = SelectedSkillMap#{SkillId => SelectedSkillInfo}};
        #skill{type = ?SKILL_TYPE_ACTIVE, refind_target = IsReFindT, time = SpellTime, is_normal = _IsNormal} ->
            {SkillId1, SkillLv1, SkillLinkInfo1} = rand_a_skill([{SkillId, SkillLv}], SkillLinkInfo),
            SelectedSkillInfo
            = #selected_skill{
                att_time = NowTime+GapAttTime,
                skill_id = SkillId1,
                spell_time = SpellTime,
                is_refind_t = IsReFindT,
                link_info = SkillLinkInfo1
            },
            SelectOption = get_select_option(Object),
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId,
                lib_battle, object_battle_cast,
                [Aid, ObjectSign, ObjectId, find_target, SkillId1, SkillLv1, SkillAdjust, 0, SelectOption]
            ),
            State#ob_act{selected_skill = SelectedSkillMap#{SkillId => SelectedSkillInfo}};
        _ ->
            State
    end.
%% 直接释放一个技能，怪物不会继续追踪
object_ac_skill_donot_trace(State, SkillId, SkillLv) ->
    #ob_act{
        skill_link_info = SkillLinkInfo, object = Object
    } = State,
    #scene_object{
        sign = ObjectSign, id = ObjectId
        ,scene = SceneId, scene_pool_id = ScenePoolId
    } = Object,
    case data_skill:get(SkillId, SkillLv) of
        #skill{type = ?SKILL_TYPE_ASSIST} ->
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, object_assist, [ObjectId, SkillId, SkillLv]),
            State;
        #skill{type = ?SKILL_TYPE_ACTIVE} ->
            {SkillId1, SkillLv1, _SkillLinkInfo1} = rand_a_skill([{SkillId, SkillLv}], SkillLinkInfo),
            mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, lib_battle, object_start_battle, [ObjectSign, ObjectId, find_target, SkillId1, SkillLv1, 0]),
            State;
        _ ->
            State
    end.

%% 获得怪物选取战斗对象的刷选条件
get_select_option(
    #scene_object{
        mon = #scene_mon{} = SceneMon,
        config_id = BossId,
        scene = SceneId,
        battle_attr = BattleAttr,
        x = X, y = Y
    }) ->
    IsRewardBossScene = local_boss_api:is_reward_boss_scene(SceneId),
    IsDesertedBossScene = local_boss_api:is_deserted_boss_scene(SceneId),
    IsDunPetScene = lib_dungeon_pet:is_dun_pet_scene(SceneId),
    if 
        SceneMon#scene_mon.boss =:= ?MON_REWARD_BOSS andalso IsRewardBossScene ->
            {boss_tired_filter, BossId, ?MON_REWARD_BOSS}; %% 是否需要排除boss疲劳度已满的玩家            
        SceneMon#scene_mon.boss =:= ?MON_DESERTED_BOSS andalso IsDesertedBossScene ->
            {boss_tired_filter, BossId, ?MON_DESERTED_BOSS};
        SceneMon#scene_mon.boss =:= ?MON_DESERTED_ELITE andalso IsDesertedBossScene ->
            {boss_tired_filter, BossId, ?MON_DESERTED_ELITE};
        IsDunPetScene ->  %% 宠物副本入侵怪需要跟踪场景守护和妖宠
            lib_dungeon_pet:select_tar_option(BattleAttr, X, Y);
        true ->
            undefined
    end;
get_select_option(_) -> undefined.


%% 朝指定目标前进
walk_to_target(State) ->
    #ob_act{object = Minfo, ai_option = {Type, AttList}} = State,
    #scene_object{scene = SceneId, scene_pool_id = ScenePoolId,battle_attr = BA,
        copy_id = CopyId, x = Mx, y = My, striking_distance = AttArea} = Minfo,
    case lib_mon:get_scene_mon_by_mids(SceneId, ScenePoolId, CopyId, AttList, [#scene_object.mon, #scene_object.x, #scene_object.y]) of
        [_|_] = TargetXy ->
            TargetXyL = [{AttMon#scene_mon.mid, AttX, AttY} || [AttMon, AttX, AttY] <-TargetXy],
            [X, Y] = get_first_target_xy(AttList, TargetXyL, {Mx, My}, []),
            %% 判断是否走到目的地
            case abs(X - Mx) > AttArea orelse abs(Y - My) > AttArea of
                false ->
                    % mod_mon_active:find_target_cast_mon_first(Minfo, Type),
                    mon_state_logic:find_target_cast_mon_first(Minfo, Type),
                    {walk_auto, 1000, State};
                true ->
                    #battle_attr{speed=Speed, other_buff_list = BuffList} = BA,
                    NowTime = utime:longunixtime(),
                    case lib_skill_buff:is_can_move(BuffList, Speed, NowTime) of
                        {false, MoveWaitMs} ->
                            {walk_auto, MoveWaitMs, State};
                        false ->
                            {sleep, 1000, State#ob_act{att=undefined}};
                        true ->
                            case get_next_step(Mx, My, AttArea, SceneId, CopyId, X, Y, true) of
                                {New_x, New_y} ->
                                    case lib_scene_object_ai:move(New_x, New_y, Minfo, Speed, false) of
                                        block ->
                                            {sleep, 500, State#ob_act{path=[]}};
                                        {true, NewMinfo, Time} ->
                                            {walk_auto, Time, State#ob_act{object = NewMinfo, o_point = {X, Y}, w_point = {New_x, New_y}}}
                                    end;
                                _ ->
                                    % mod_mon_active:find_target_cast_mon_first(Minfo, Type),
                                    mon_state_logic:find_target_cast_mon_first(Minfo, Type),
                                    {walk_auto, 1000, State}
                            end
                    end
            end;
        _ ->
            {sleep, 1000, State}
    end.

%% 获取攻击目标(取最近的)
get_first_target_xy([], [{_Mid, FirstX, FirstY}|_], {_X, _Y}, Result) ->
    case Result of
        [_|_] ->
            [{_Mid1, _Distance1, {FinalX, FinalY}}|_] = lists:keysort(2, Result),
            [FinalX, FinalY];
        _ ->
            [FirstX, FirstY]
    end;
get_first_target_xy([Mid|AttList], TargetXy, {Mx, My}, Result)->
    case lists:keyfind(Mid, 1, TargetXy) of
        false ->
            get_first_target_xy(AttList, TargetXy, {Mx, My}, Result);
        {Mid, X, Y} -> 
            Distance = umath:distance({X, Y}, {Mx, My}),
            get_first_target_xy(AttList, TargetXy, {Mx, My}, [{Mid, Distance, {X, Y}}|Result])
    end.

%% 非进攻巡逻怪路径(没碰到障碍点之前会以半径创建的直线路径一直走)
create_auto_patrol_path(X, Y, DX, DY, Range, Speed, IsBlock) ->
    % 遇到障碍点反弹;路径走完/初始路径则随机一个钝角方向走
    ChangeAngel =
    case IsBlock of
        true -> math:pi();
        _ -> (urand:rand(90, 180)/180)*math:pi()
    end,
    % 以配置范围为半径+500范围内随机
    NewRange = urand:rand(Range, Range+500),
    if
        X =/= DX andalso Y =/= DY ->
            X1 = DX - X,
            Y1 = DY - Y,
            Z  = math:sqrt(X1*X1 + Y1*Y1),
            % 上一点的方向角
            OldAngel =  math:acos(X1/Z) * math:pi(),
            case OldAngel > math:pi() of
                true -> NewAngle = OldAngel - ChangeAngel;
                _ ->  NewAngle = ChangeAngel + OldAngel
            end,
            TX = max(0, round(X + NewRange*math:sin(NewAngle))),
            TY = max(0, round(Y + NewRange*math:cos(NewAngle)));
        true ->
            ChangeAngel1 = (urand:rand(0, 360)/180)*math:pi(),
            TX = max(0, round(X + NewRange*math:sin(ChangeAngel1))),
            TY = max(0, round(Y + NewRange*math:cos(ChangeAngel1)))
    end,
    NewPath = lib_scene_object_ai:dest_path(X, Y, TX, TY, Speed),
    NewPath.
