%% ---------------------------------------------------------------------------
%% @doc lib_scene_object_agent
%% @author ming_up@foxmail.com
%% @since  2017-02-05
%% @deprecated  场景对象进程字典接口
%% ---------------------------------------------------------------------------
-module(lib_scene_object_agent).

-export([
        get_scene_object/0
        ,get_scene_object/1
        ,get_scene_object/2
        ,get_scene_object_by_ids/2
        ,get_scene_object_battle_attr_by_ids/2
        ,update/2
        ,clear_scene_object/3
        ,kill_scene_object/2
        ,clear_when_scene_close/0
        ,clear_scene_object_by_ids/2
        ,get_scene_mon_by_mids/3
        ,clear_scene_mon_by_mids/3
        ,mon_ac_skill/4
        ,clean_buff/4
        ,change_attr_by_ids/2
        ,change_attr_by_mids/3
        ,apply_info/4
    ]).
%% 对场景模块内部接口
-export([
        get_area_object/2,               %% 获取格子对象信息
        get_all_area_object/2,           %% 获取九宫格子对象信息
        get_object/1,                    %% 获取怪物（根据怪物id）
        put_object/1,                    %% 保存怪物数据
        del_object/1,                    %% 保存怪物数据
        get_area/2,                      %% 获取格子怪物id
        get_all_area/2,                  %% 获取九宫格子怪物id
        save_to_area/4,                  %% 添加在九宫格
        del_to_area/4,                   %% 删除在九宫格
        del_all_area/1,                  %% 删除9宫格数据
        put_trace/4,
        get_trace/3,
        del_trace/1,
        del_trace/4,
        do_for_trace/6,
        del_room/1,
        save_to_deploy/4,
        get_from_deploy/2,
        del_from_deploy/3,
        save_to_coordinate/5,
        get_from_coodinate/3,
        del_from_coodinate/5,
        get_from_coodinates/7,
        get_new_object_in_warning_range/6,
        get_cl_mons/3                       % 获取采集怪
        ,object_move_send_msg/3             % 场景对象移动广播
    ]).

-include("scene.hrl").
-include("attr.hrl").
-include("battle.hrl").
-include("common.hrl").

-define(OBJECT_ROOM(X), {object_room, X}).
-define(OBJECT_AREA(X, Y), {object_area, {X, Y}}).
-define(DEPLOY(CopyId), {deploy, CopyId}).
-define(COODINATE(CopyId, X, Y), {coodinate, {CopyId, X, Y}}).

-define(OBJECT_KEY(X), {object, X}).

get_scene_object() ->
    [ Object || {{object, _}, Object} <- get()].

get_scene_object(CopyId) ->
    case CopyId of
        [] -> get_scene_object();
        _  ->
            AllObjectIds = get_from_room(CopyId),
            get_scene_object_helper(AllObjectIds, CopyId, [])
    end.

get_scene_object(CopyId, ResultForm) ->
    AllObject = case CopyId of
        [] -> get_scene_object();
        _  -> get_scene_object(CopyId)
    end,
    [util:record_return_form(Object, ResultForm)||Object <- AllObject].

get_scene_object_by_ids(Ids, ResultForm) ->
    % AllObject = [ get_object(Id) || Id <- Ids ],
    % [util:record_return_form(Object, ResultForm) || Object <- AllObject, Object=/=[]].
    lists:foldr(fun(Id, Acc) ->
                case get_object(Id) of
                    #scene_object{} = Object ->
                        [util:record_return_form(Object, ResultForm) | Acc];
                    _ ->
                        Acc
                end
        end, [], Ids).

clear_scene_object(0, CopyId, BroadCast) ->
    AllObject = get_scene_object(CopyId),
    % old: [Object#scene_object.aid !  {'stop', BroadCast} || Object <- AllObject, Object/=[]],
    stop_objects(AllObject, BroadCast),
    ok;
clear_scene_object(Sign, CopyId, BroadCast) ->
    AllObject = get_scene_object(CopyId),
    % old:
    % [
    %     begin
    %             Object#scene_object.aid !  {'stop', BroadCast},
    %             del_object(Object#scene_object.id)
    %     end || Object <- AllObject, Object/=[], Object#scene_object.sign==Sign],
    case AllObject of
        [#scene_object{aid = Aid} | _] ->
            ObjIdList = [
                begin
                        del_object(ObjId),
                        ObjId
                end || #scene_object{id = ObjId, sign = SignX} <- AllObject,
                SignX == Sign
            ],
            mon_mgr:stop_mon(Aid, ObjIdList, BroadCast);
        _ ->
            ignore
    end,
    ok.

kill_scene_object(Sign, CopyId) ->
    AllObject = get_scene_object(CopyId),
    % old:
    % [
    %     begin
    %         Object#scene_object.aid !  die,
    %         del_object(Object#scene_object.id)
    %     end || Object <- AllObject, Object/=[], Object#scene_object.sign==Sign],
    case AllObject of
        [#scene_object{aid = Aid} | _] ->
            ObjIdList = [
                begin
                        del_object(ObjId),
                        ObjId
                end || #scene_object{id = ObjId, sign = SignX} <- AllObject,
                SignX == Sign
            ],
            mon_mgr:mon_die(Aid, ObjIdList);
        _ ->
            ignore
    end,
    ok.

clear_when_scene_close() ->
    AllObject = get_scene_object(),
    % old:
    % [
    %     mod_mon_active:stop(ObjectPid, 0) ||
    %     #scene_object{aid = ObjectPid, id = ObjId} <- AllObject
    % ],
    case AllObject of
        [#scene_object{aid = MonPid} | _] ->
            [
                del_object(ObjId) ||
                #scene_object{id = ObjId} <- AllObject
            ],
            mon_mgr:stop_all(MonPid);
        _ ->
            ignore
    end,
    ok.

clear_scene_object_by_ids(BroadCast, Ids) ->
    AllObject = get_scene_object_by_ids(Ids, all),
    % old: [Object#scene_object.aid ! {'stop', BroadCast} || Object <- AllObject, Object/=[]],
    stop_objects(AllObject, BroadCast),
    ok.

get_scene_object_battle_attr_by_ids(Ids, ResultForm) ->
    AllObject = [ get_object(Id) || Id <- Ids ],
    [util:record_return_form(BA, ResultForm) || #scene_object{battle_attr = BA} <- AllObject].

%% 怪物接口
get_scene_mon_by_mids(CopyId, Mids, ResultForm) ->
    AllObject = get_scene_object(CopyId),
    % F = fun(Object) ->
    %             case Object#scene_object.sign of
    %                 ?BATTLE_SIGN_MON -> lists:member(Object#scene_object.mon#scene_mon.mid, Mids);
    %                 _ -> false
    %             end
    %     end,
    % [util:record_return_form(Object, ResultForm) || Object <- lists:filter(F, AllObject)].
    FoldFun =
    fun (Object = #scene_object{sign = ?BATTLE_SIGN_MON, mon = #scene_mon{mid = Mid}}, Acc) ->
            case lists:member(Mid, Mids) of
                true  -> [util:record_return_form(Object, ResultForm) | Acc];
                false -> Acc
            end;
        (_Object, Acc) -> Acc
    end,
    lists:foldr(FoldFun, [], AllObject).

clear_scene_mon_by_mids(CopyId, BroadCast, Mids) ->
    % old: [Aid ! {'stop', BroadCast} || Aid <- AllAid],
    AllObject = get_scene_mon_by_mids(CopyId, Mids, all),
    stop_objects(AllObject, BroadCast),
    ok.

%% 怪物释放技能
mon_ac_skill(CopyId, MonIds, SkillId, SkillLv) ->
    ResultPattern = #scene_object.id,
    MonIdList = get_scene_mon_by_mids(CopyId, MonIds, ResultPattern),
    % old: [lib_mon:ac_skill(Aid, SkillId, SkillLv) || Aid <- AllAid],
    case MonIdList of
        [_|_] ->
            MonMgrPid = lib_scene_agent:get_mon_mgr_pid(),
            lib_mon:ac_skill(MonMgrPid, MonIdList, SkillId, SkillLv);
        _ ->
            ignore
    end,
    ok.

%% 移除BUFF
clean_buff(EtsScene, CopyId, MonIds, SkillId) ->
    AllId = get_scene_mon_by_mids(CopyId, MonIds, #scene_object.id),
    [lib_skill_buff:clean_mon_buff(EtsScene, Id, SkillId) || Id <- AllId],
    ok.

change_attr_by_ids(Ids, KeyValues) ->
    MonIdList = get_scene_object_by_ids(Ids, #scene_object.id),
    % old: [Aid ! {'change_attr', KeyValues} || #scene_object{aid=Aid} <- AllObject],
    case MonIdList of
        [_|_] ->
            MonMgrPid = lib_scene_agent:get_mon_mgr_pid(),
            mon_mgr:send(MonMgrPid, MonIdList, {'change_attr', KeyValues});
        _ ->
            ignore
    end,
    ok.

change_attr_by_mids(CopyId, MonIds, KeyValues) ->
    AllObject = get_scene_mon_by_mids(CopyId, MonIds, #scene_object.aid),
    % old: [Aid ! {'change_attr', KeyValues} || Aid <- AllObject],
    case AllObject of
        [#scene_object{aid = Aid} | _] ->
            MonIdList = [MonId || #scene_object{id = MonId} <- AllObject],
            mon_mgr:send(Aid, MonIdList, {'change_attr', KeyValues});
        _ ->
            ignore
    end,
    ok.

apply_info(Id, Moudle, Method, Args) ->
    case get_scene_object_by_ids([Id], all) of
        [#scene_object{aid = Aid, id = ObjId}|_] ->
            % old: Aid ! {'mod', Moudle, Method, Args};
            mon_mgr:send(Aid, ObjId, {'mod', Moudle, Method, Args});
        _ -> skip
    end,
    ok.

update(Id, KeyValues) ->
    case get_object(Id) of
        Object = #scene_object{} ->
            NewObject = set_kv_args(KeyValues, Object),
            put_object(NewObject);
        _ -> ignore
    end.

set_kv_args([H|T], Object) ->
    Object1 = case H of
        {skill_owner, Value} ->
            Object#scene_object{skill_owner=Value};
        {figure, Value} ->
            Object#scene_object{figure=Value};
        {battle_attr, Value} ->
            Object#scene_object{battle_attr=Value};
        {pos, Value} ->
            Object#scene_object{pos=Value};
        {filter_skill, Value} ->
            Object#scene_object{skill=Value};
        {skill_passive, Value} ->
            Object#scene_object{skill_passive=Value};
        {dummy, Value} ->
            Object#scene_object{dummy = Value};
        {hp, Value} ->
            BA = Object#scene_object.battle_attr,
            Object#scene_object{battle_attr=BA#battle_attr{hp=Value}};
        {is_be_atted, Value} ->
            Object#scene_object{is_be_atted = Value};
        {is_be_clicked, Value} ->
            Object#scene_object{is_be_clicked = Value};
        {xy, X, Y} ->
            Object#scene_object{x = X, y = Y};
        {mon, Value} ->
            Object#scene_object{mon=Value};
        {mod_args, Value} ->
            Object#scene_object{mod_args=Value};
        {angle, Value} ->
            Object#scene_object{angle=Value};
        {type, Value} ->
            Object#scene_object{type=Value};
        {weapon_id, Value} ->
            Object#scene_object{weapon_id=Value};
        {die_info, Value} ->
            Object#scene_object{die_info = Value};
        {hurt_check, Value} ->
            Object#scene_object{hurt_check = Value};
        {is_collecting, Value} ->
            Mon = Object#scene_object.mon,
            Object#scene_object{mon = Mon#scene_mon{is_collecting = Value}};
        {has_cltimes, Value} ->
            Mon = Object#scene_object.mon,
            Object#scene_object{mon = Mon#scene_mon{has_cltimes = Value}};
        {change_speed, Value} ->
            BA = Object#scene_object.battle_attr,
            Object#scene_object{battle_attr=BA#battle_attr{speed = Value}};
        {attr_list, Value} ->
            BA = Object#scene_object.battle_attr,
            NewBA = lib_player_attr:set_battle_attr(BA, Value),
            Object#scene_object{battle_attr=NewBA};
        {combat_power, Value} ->
            Object#scene_object{combat_power = Value};
        {pk_protect_time, Value} ->
            BA = Object#scene_object.battle_attr,
            OldPk = BA#battle_attr.pk,
            NewPk = OldPk#pk{pk_protect_time = Value},
            NewBA = BA#battle_attr{pk = NewPk},
            Object#scene_object{battle_attr=NewBA};
        {can_att_list, Value} ->
            Object#scene_object{can_att_list = Value};
        {cannot_beatt_list, Value} ->
            Object#scene_object{cannot_beatt_list = Value};
        {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,
            BA = Object#scene_object.battle_attr,
            NewBattleAttr = lists:foldl(Fun, BA, Value),
            Object#scene_object{battle_attr = NewBattleAttr};
        {hurt_del_ratio, Value} ->
            BA = Object#scene_object.battle_attr,
            Attr = BA#battle_attr.attr,
            NewBA = BA#battle_attr{attr = Attr#attr{hurt_del_ratio = Value}},
            Object#scene_object{battle_attr = NewBA};
        {att, Value} ->
            BA = Object#scene_object.battle_attr,
            Attr = BA#battle_attr.attr,
            NewBA = BA#battle_attr{attr = Attr#attr{att = Value}},
            Object#scene_object{battle_attr = NewBA};
        {att_del_hp_each_object, Value} ->
            BA = Object#scene_object.battle_attr,
            Object#scene_object{battle_attr=BA#battle_attr{att_del_hp_each_object=Value}};
        {att_del_hp_each_time, Value} ->
            BA = Object#scene_object.battle_attr,
            Object#scene_object{battle_attr=BA#battle_attr{att_del_hp_each_time=Value}};
        {skill_att_xy, Value} ->
            {AttX2, AttY2} = case Value of
                #{id := AttRoleId, sign := AttSign, x := AttX, y := AttY} ->
                    case AttSign of
                        ?BATTLE_SIGN_MON ->
                            case get_object(AttRoleId) of
                                #scene_object{x = X, y = Y} ->
                                    {X, Y};
                                _ -> {AttX, AttY}
                            end;
                        ?BATTLE_SIGN_PLAYER ->
                            case lib_scene_agent:get_user(AttRoleId) of
                                #ets_scene_user{x = X, y = Y} ->
                                    {X, Y};
                                _ -> {AttX, AttY}
                            end;
                        _ ->
                            {AttX, AttY}
                    end;
                _ ->
                    {0, 0}
            end,
            Object#scene_object{skill_att_xy = {AttX2, AttY2}};
        {skill_att_xy_change, Value} ->
            Object#scene_object{skill_att_xy_change = Value};
        {rivive, Hp, X, Y} ->
            BA = Object#scene_object.battle_attr,
            NewObject = Object#scene_object{battle_attr=BA#battle_attr{hp=Hp}, x = X, y = Y},
            lib_scene_object:scene_broadcast_object(NewObject, 0),
            NewObject;
        {rivive, Hp, X, Y, BroadCast} ->
            BA = Object#scene_object.battle_attr,
            NewObject = Object#scene_object{battle_attr=BA#battle_attr{hp=Hp}, x = X, y = Y},
            lib_scene_object:scene_broadcast_object(NewObject, BroadCast),
            NewObject;
        _ ->
            Object
    end,
    set_kv_args(T, Object1);
set_kv_args([], Object) -> Object.


%% ============================ mod_scene_agent 保存伙伴信息 ===============================

%% 保存怪物数据
put_object(Object) ->
    #scene_object{id=Id, copy_id=CopyId, x=X, y=Y, sign=_Sign, pos=_Pos, battle_attr=#battle_attr{group=_Group}} = Object,
    case get(?OBJECT_KEY(Id)) of
        undefined ->
            save_to_room(CopyId, Id),
            save_to_area(CopyId, X, Y, Id),
            % save_to_deploy(CopyId, Pos, Id, Group),
            %% save_to_coordinate(CopyId, X, Y, Sign, Id),
            ok;
        #scene_object{x=OldX, y=OldY, copy_id = OldCopyId, pos = _OldPos} ->
            AreaIdNew = lib_scene_calc:get_xy(X, Y),
            AreaIdOld = lib_scene_calc:get_xy(OldX, OldY),
            if
                CopyId == OldCopyId andalso AreaIdNew == AreaIdOld ->
                    ignore;
                true ->
                    del_to_area(OldCopyId, AreaIdOld, Id),
                    save_to_area(CopyId, AreaIdNew, Id),
                    %% del_from_coodinate(CopyId, OldX, OldY, Sign, Id),
                    %% save_to_coordinate(CopyId, X, Y, Sign, Id),
                    ok
            end
            % end,
            % case OldPos =/= Pos of
            %     true  ->
            %         del_from_deploy(OldCopyId, OldPos, Id),
            %         save_to_deploy(CopyId, Pos, Id, Group);
            %     false -> ignore
    end,
    put(?OBJECT_KEY(Id), Object).


get_object(IdList) when is_list(IdList)->
    [get_object(Id)||Id<-IdList];

%% 获取怪物数据
get_object(Id) ->
    case get(?OBJECT_KEY(Id)) of
        undefined -> [];
        Object -> Object
    end.

get_scene_object_helper([], _, Data) ->
    Data;
get_scene_object_helper([Id | T], CopyId, Data) ->
    case get(?OBJECT_KEY(Id)) of
        undefined ->
            del_from_room(CopyId, Id),
            get_scene_object_helper(T, CopyId, Data);
        Object ->
            get_scene_object_helper(T, CopyId, [Object | Data])
    end.

%% 删除怪物数据
del_object(Id) ->
    case get(?OBJECT_KEY(Id)) of
        undefined -> [];
        Object ->
            #scene_object{copy_id=CopyId, x=X, y=Y, d_x=Dx, d_y=Dy, sign=_Sign, warning_range=WaringRange, pos=_Pos} = Object,
            del_from_room(CopyId, Id),
            del_to_area(CopyId, X, Y, Id),
            do_for_trace(Id, CopyId, WaringRange, Dx, Dy, 2),
            % del_from_deploy(CopyId, Pos, Id),
            %% del_from_coodinate(CopyId, X, Y, Sign, Id),
            erase(?OBJECT_KEY(Id))
    end.

%% ============================ mod_scene_agent 保存怪物警戒信息 ===============================

-define(object_trace(X), {object_trace, X}).
%% 写入怪物ai(设定怪物追踪范围，角色走路进行触发怪物追踪)
put_trace(CopyId, X, Y, MonId) ->
    case get(?object_trace(CopyId)) of
        undefined ->
            put(?object_trace(CopyId), #{{X, Y} => [MonId]});
        D ->
            case maps:find({X, Y}, D) of
                {ok, OL} -> put(?object_trace(CopyId), D#{{X, Y} := [MonId|OL]});
                _ ->        put(?object_trace(CopyId), D#{{X, Y} => [MonId]})
            end
    end,
    ok.

%% 获取怪物ai
get_trace(CopyId, X, Y) ->
    case get(?object_trace(CopyId)) of
        undefined -> [];
        D ->
            case maps:find({X, Y}, D) of
                {ok, MonIdList} -> MonIdList;
                _ -> []
            end
    end.

%% 删除怪物ai
del_trace(CopyId) ->
    erase(?object_trace(CopyId)).

%% 移除某只怪物的ai
del_trace(CopyId, X, Y, MonId) ->
    case get(?object_trace(CopyId)) of
        undefined -> ok;
        D ->
            case maps:find({X, Y}, D) of
                {ok, OL} ->
                    case lists:delete(MonId, OL) of
                        [] ->
                            D1 = maps:remove({X, Y}, D),
                            case maps:size(D1) of
                                0 -> erase(?object_trace(CopyId));
                                _ -> put(?object_trace(CopyId), D1)
                            end;
                        CL ->
                            put(?object_trace(CopyId), D#{{X, Y} := CL})
                    end;
                _ ->
                    ok
            end
    end.

%% 插入/移除警戒区
%% Type :: integer(),  %% 操作类型(1插入警戒区， 2移除警戒区)
do_for_trace(MonId, CopyId, WaringRange, Xpx, Ypx, Type) ->
    TraceXpx = min(?GRID_WIDTH * 3 bsr 1, WaringRange),  %% 控制最大追踪范围(px)
    TraceYpx = min(?GRID_HEIGHT  * 3 bsr 1, WaringRange),  %% 控制最大追踪范围(px)
    Xmin = max(0,  Xpx - TraceXpx),
    Ymin = max(0,  Ypx - TraceYpx),
    Xmax = Xpx + TraceXpx,
    Ymax = Ypx + TraceYpx,
    {Xlmin, Ylmin} = lib_scene_calc:pixel_to_logic_coordinate(Xmin, Ymin),
    {Xlmax, Ylmax} = lib_scene_calc:pixel_to_logic_coordinate(Xmax, Ymax),
    %% 防止怪物坐标配置成负数造成死循环
    XRealMin = max(Xlmin, 0),
    XRealMax = max(Xlmax, 0),
    YRealMin = max(Ylmin, 0),
    YRealMax = max(Ylmax, 0),
    Fun = case Type of
        1 -> fun put_trace/4;
        2 -> fun del_trace/4
    end,
    loop_post_x(XRealMin, XRealMax, YRealMin, YRealMax, MonId, CopyId, Fun),
    ok.

loop_post_x(M, M, Y, Y1, MonId, CopyId, Fun) ->
    loop_post_y(M, Y, Y1, MonId, CopyId, Fun);
loop_post_x(X, M, Y, Y1, MonId, CopyId, Fun) ->
    loop_post_y(X, Y, Y1, MonId, CopyId, Fun),
    loop_post_x(X+1, M, Y, Y1, MonId, CopyId, Fun).

loop_post_y(X, M, M, MonId, CopyId, Fun) ->
    do_post_aid(MonId, CopyId, X, M, Fun);
loop_post_y(X, Y, M, MonId, CopyId, Fun) ->
    do_post_aid(MonId, CopyId, X, Y, Fun),
    loop_post_y(X, Y+1, M, MonId, CopyId, Fun).

do_post_aid(MonId, CopyId, X, Y, Fun) ->
    Fun(CopyId, X, Y, MonId).

%% ============================ mod_scene_agent 保存怪物九宫格信息 ===============================

%% 添加在九宫格
%% 这里用list即可，因为只存不查找，用list最合适，不需要maps
%% 为了免去去重检查，所以调用的上层接口应保证是新增加的才调用
save_to_area(CopyId, AreaId, Id) ->
    PDAreaKey = ?OBJECT_AREA(AreaId, CopyId),
    case get(PDAreaKey) of
        undefined ->
            put(PDAreaKey, [Id]);
        List ->
            put(PDAreaKey, [Id | List])
    end.

save_to_area(CopyId, X, Y, Id) ->
    AreaId = lib_scene_calc:get_xy(X, Y),
    save_to_area(CopyId, AreaId, Id).


%% 获取一个格子怪物id
get_area(AreaId, CopyId) ->
    case get(?OBJECT_AREA(AreaId, CopyId)) of
        undefined -> [];
        IdList -> IdList
    end.

%% 获取多个格子怪物id
get_all_area(Area, CopyId) ->
    lists:foldl(
        fun(OneArea, L) ->
                get_area(OneArea, CopyId) ++ L
        end,
        [], Area).

%% 获取一个格子object信息
get_area_object(AreaId, CopyId) ->
    AllObject = get_area(AreaId, CopyId),
    get_scene_object_helper(AllObject, CopyId, []).

%% 获取多个格子object信息
get_all_area_object(Area, CopyId) ->
    List = get_all_area(Area, CopyId),
    get_scene_object_helper(List, CopyId, []).

del_to_area(CopyId, AreaId, Id) ->
    Key = ?OBJECT_AREA(AreaId, CopyId),
    case get(Key) of
        undefined -> skip;
        IdList ->
            NewList = lists:delete(Id, IdList),
            case NewList of
                [_ | _] -> put(Key, NewList);
                _       -> erase(Key)
            end
    end.

del_to_area(CopyId, X, Y, Id) ->
    AreaId = lib_scene_calc:get_xy(X, Y),
    del_to_area(CopyId, AreaId, Id).

%% 删除9宫格数据
del_all_area(CopyId) ->
    lists:foreach(fun(Key) ->
                delete_object_area(CopyId, Key)
        end, get_keys()).

delete_object_area(CopyId, Key = {object_area, {_, CopyId}}) -> erase(Key);
delete_object_area(_CopyId, _Key) -> ignore.

%% ============================ mod_scene_agent 保存怪物房间信息 ===============================

%% 保存怪物id到房间
%% 这里用list即可，因为只存不查找，用list最合适，不需要maps
%% 为了免去去重检查，所以调用的上层接口应保证是新增加的才调用
save_to_room(CopyId, Id) ->
    Key = ?OBJECT_ROOM(CopyId),
    case get(Key) of
        undefined ->
            put(Key, [Id]);
        List ->
            NewList = [Id | List],
            put(Key, NewList)
    end.

%% 从房间获取所有怪物id
get_from_room(CopyId) ->
    case get(?OBJECT_ROOM(CopyId)) of
        undefined -> [];
        List -> List
    end.

%% 从房间中删除怪物id
del_from_room(CopyId, Id) ->
    Key = ?OBJECT_ROOM(CopyId),
    case get(Key) of
        undefined -> skip;
        List ->
            NewList = lists:delete(Id, List),
            case NewList of
                [_ | _] -> put(Key, NewList);
                _       -> erase(Key)
            end
    end.

del_room(CopyId) ->
    erase(?OBJECT_ROOM(CopyId)).

%% ============================ mod_scene_agent 保存怪物房间信息 ===============================

%% 保存对象到阵法位置
save_to_deploy(_, 0, _, _) -> skip;
save_to_deploy(CopyId, Pos, Id, Group) ->
    case get(?DEPLOY(CopyId)) of
        undefined ->
            put(?DEPLOY(CopyId), #{Pos => [{Id,Group}] });
        D ->
            D1 = case maps:get(Pos, D, false) of
                false -> D#{Pos => [{Id,Group}]};
                L     -> D#{Pos => [{Id,Group}|lists:keydelete(Id, 1, L)]}
            end,
            put(?DEPLOY(CopyId), D1)
    end.

%% 从阵法位置获取所有对象id
get_from_deploy(CopyId, Pos) ->
    case get(?DEPLOY(CopyId)) of
        undefined -> false;
        D -> maps:get(Pos, D, false)
    end.

%% 从房间阵法位置中删除怪物id
del_from_deploy(_, 0, _) -> skip;
del_from_deploy(CopyId, Pos, Id) ->
    case get(?DEPLOY(CopyId)) of
        undefined -> skip;
        D ->
            case maps:get(Pos, D, false) of
                false -> skip;
                L     ->
                    D1 = case lists:keydelete(Id, 1, L) of
                        [] -> maps:remove(Pos, D);
                        L1 -> D#{Pos => L1}
                    end,
                    case maps:size(D1) of
                        0 -> erase(?DEPLOY(CopyId));
                        _ -> put(?DEPLOY(CopyId), D1)
                    end
            end
    end.

%% ============================ mod_scene_agent 保存怪物房间信息 ===============================

%% 保存对象到逻辑坐标
save_to_coordinate(CopyId, Xpx, Ypx, Sign, Id) ->
    {X, Y} = lib_scene_calc:pixel_to_logic_coordinate(Xpx, Ypx),
    case get(?COODINATE(CopyId, X, Y)) of
        undefined ->
            put(?COODINATE(CopyId, X, Y), #{{Sign,Id} => 0});
        D ->
            put(?COODINATE(CopyId, X, Y), D#{{Sign,Id} => 0})
    end.

%% 从逻辑坐标获取所有对象id
get_from_coodinate(CopyId, X, Y) ->
    case get(?COODINATE(CopyId, X, Y)) of
        undefined -> [];
        D -> maps:keys(D)
    end.

%% 从房间中删除怪物id
del_from_coodinate(CopyId, Xpx, Ypx, Sign, Id) ->
    {X, Y} = lib_scene_calc:pixel_to_logic_coordinate(Xpx, Ypx),
    case get(?COODINATE(CopyId, X, Y)) of
        undefined -> skip;
        D ->
            D1 = maps:remove({Sign,Id}, D),
            case maps:size(D1) of
                0 -> erase(?COODINATE(CopyId, X, Y));
                _ -> put(?COODINATE(CopyId, X, Y), D1)
            end
    end.

%% 对逻辑坐标范围内的所有场景对象进行处理
get_from_coodinates(Xop, Xed, Xc, Yop, Yed, Yc, CopyId) ->
    get_from_x_coodinate(Xop, Xed, Xc, Yop, Yed, Yc, CopyId, fun get_from_coodinate/3, []).

get_from_x_coodinate(Xed, Xed, _, Yop, Yed, Yc, CopyId, Fun, Result) ->
    get_from_y_coodinate(Xed, Yop, Yed, Yc, CopyId, Fun, Result);
get_from_x_coodinate(Xtmp, Xed, Xc, Yop, Yed, Yc, CopyId, Fun, Result) ->
    TmpResult = get_from_y_coodinate(Xtmp, Yop, Yed, Yc, CopyId, Fun, Result),
    get_from_x_coodinate(Xtmp+Xc, Xed, Xc, Yop, Yed, Yc, CopyId, Fun, TmpResult).

get_from_y_coodinate(X, Yed, Yed, _, CopyId, Fun, Result) ->
    Fun(CopyId, X, Yed) ++ Result;
get_from_y_coodinate(X, Ytmp, Yed, Yc, CopyId, Fun, Result) ->
    TmpResult = Fun(CopyId, X, Ytmp) ++ Result,
    get_from_y_coodinate(X, Ytmp+Yc, Yed, Yc, CopyId, Fun, TmpResult).

%% 从两个警戒区中获取新的场景对象
get_new_object_in_warning_range(CopyId, X, Y, TX, TY, WarningRange) ->
    {XW, YW} = lib_scene_calc:pixel_to_logic_coordinate(WarningRange, WarningRange),
    {Xlg, Ylg} = lib_scene_calc:pixel_to_logic_coordinate(X, Y),
    {TXlg, TYlg} = lib_scene_calc:pixel_to_logic_coordinate(TX, TY),
    if
        Xlg == TXlg andalso Ylg == TYlg -> [];
        TXlg - XW < Xlg - XW orelse TXlg + XW > Xlg + XW orelse TYlg - YW < Ylg - YW orelse TYlg + YW > Ylg + XW ->  %% 前后两个警戒区不相交
            lib_scene_object_agent:get_from_coodinates(TXlg -XW, TXlg + XW, 1, TYlg - YW, TYlg + YW, 1, CopyId);
        %% 前后两个警戒区相交
        Xlg == TXlg ->
            YGag = TYlg - Ylg,
            YR = YGag div abs(YGag),
            %% ?PRINT("one y ~p~n", [{TXlg - XW, TXlg + XW, 1, Ylg + YR*YW, TYlg + YR*YW, YR}]),
            lib_scene_object_agent:get_from_coodinates(TXlg - XW, TXlg + XW, 1, Ylg + YR*YW+YR, TYlg + YR*YW, YR, CopyId);
        Ylg == TYlg ->
            XGag = TXlg - Xlg,
            XR = XGag div abs(XGag),
            %% ?PRINT("one x ~p~n", [{Xlg + XR*XW+XR, TXlg + XR*XW, XR, Ylg - YW, TYlg + YW, 1}]),
            lib_scene_object_agent:get_from_coodinates(Xlg + XR*XW+XR, TXlg + XR*XW, XR, Ylg - YW, TYlg + YW, 1, CopyId);
        true -> %% 前后两个警戒区相交
            YGag = TYlg - Ylg,
            YR = YGag div abs(YGag),
            XGag = TXlg - Xlg,
            XR = XGag div abs(XGag),
            %% ?PRINT("two ~p, ~p~n", [{Xlg + XR*XW+XR, TXlg + XR*XW, XR, Ylg - YW, TYlg + YW, 1}, {TXlg - XR*XW, Xlg + XR*XW, XR, Ylg + YR*YW, TYlg + YR*YW, YR}]),
            %% 先计算x
            XObjects = lib_scene_object_agent:get_from_coodinates(Xlg + XR*XW+XR, TXlg + XR*XW, XR, Ylg - YW, TYlg + YW, 1, CopyId),
            YObjects = lib_scene_object_agent:get_from_coodinates(TXlg - XR*XW, Xlg + XR*XW, XR, Ylg + YR*YW, TYlg + YR*YW, YR, CopyId),
            XObjects ++ YObjects
    end.

%%--------------------------------------------------
%% 获取采集怪物信息
%% @param  CopyId CopyId
%% @param  X      像素坐标X
%% @param  Y      像素坐标Y
%% @return        description
%%--------------------------------------------------
get_cl_mons(CopyId, X, Y) ->
    AreaId = lib_scene_calc:get_xy(X, Y),
    Objects = get_area_object(AreaId, CopyId),
    %% FIXME lists:member可以待改造collect_api之后，调用该处接口
    [
        ObjId ||
        #scene_object{id = ObjId, mon = #scene_mon{kind = MonKind}} <- Objects,
        lists:member(MonKind,
            [?MON_KIND_COLLECT, ?MON_KIND_TASK_COLLECT]
        )
    ].

%% 场景对象移动广播
%% 从原lib_scene_object_agent:send_object_move_msg/3移植过来
object_move_send_msg(OldX, OldY, Object)->
    #scene_object{id = Id, scene = SceneId, copy_id = CopyId, x = X, y = Y} = Object,
    %% 告诉玩家有怪物移动
    {ok, BinData}  = pt_120:write(12008, [X, Y, Id]),
    % 告诉玩家有怪物被移除
    {ok, BinData1} = pt_120:write(12006, Id),
    %% 告诉玩家有怪物移动进入
    BinData2 = lib_scene_object:pack_scene_object_bin(Object),
    case lib_scene:is_broadcast_scene(SceneId) of
        true ->
            % lib_server_send:send_to_scene(SceneId, ScenePoolId, CopyId, BinData);
            lib_scene_agent:send_to_local_scene(CopyId, BinData);
        false ->
            % mod_scene_agent:apply_cast(SceneId, ScenePoolId,
            %     lib_scene_calc, move_broadcast,
            %     [SceneId, CopyId, X, Y, OldX, OldY, BinData, BinData1, BinData2, []]
            % )
            lib_scene_calc:move_broadcast(
                SceneId, CopyId, X, Y, OldX, OldY, BinData, BinData1, BinData2, []
            )
    end.

%% stop指定的Objects（无过滤条件）
stop_objects(Objects, BroadCast) ->
    case Objects of
        %% 目前单个场景分线的怪物都使用同一个进程实例
        %% 所以这里简化为取第一个数据的Pid即可
        [#scene_object{aid = Aid} | _] ->
            ObjIdList = [
                begin
                        del_object(ObjId),
                        ObjId
                end || #scene_object{id = ObjId} <- Objects
            ],
            mon_mgr:stop_mon(Aid, ObjIdList, BroadCast);
        _ ->
            ignore
    end.

