%%-----------------------------------------------------------------------------
%% module  :       lib_void_fam
%% @author  :       Czc
%% email   :       389853407@qq.com
%% Created :       2017-11-14
%% description:    无尽领域
%%-----------------------------------------------------------------------------
-module(lib_void_fam).

-include("void_fam.hrl").
-include("def_fun.hrl").
-include("def_module.hrl").
-include("goods.hrl").
-include("scene.hrl").
-include("battle.hrl").
-include("activitycalen.hrl").
-include("predefine.hrl").
-include("errcode.hrl").
-include("server.hrl").
-include("common.hrl").
-include("def_goods.hrl").
-include("figure.hrl").
-include("clusters.hrl").
-include("czone.hrl").

-export([
    get_role_map/0
    , save_role_map/1
    , get_role_rank/0
    , save_role_rank/1
    , get_achieve_floor_map/0
    , save_achieve_floor_map/1
    , get_act_cls_type/0
    , act_close/1
    %, act_real_close/1
    , send_to_uid/3
    , is_in_void_fam/1
    , is_in_kf_void_fam/1
    , update_role_hp/3
    , kill_player/5
    , kill_mon/4
    , is_pass/1
    , give_floor_reward/6
    , give_floor_reward/4
    , give_score_reward/3
    , pass_all_floor/3
    , get_scene_born_xy/1
    , init_act/0
    , act_start/1
    , act_start_by_time/1
    , sync_data/2
    , request_sync_data/2
    , add_log/5
    , get_floor/1
    , get_pre_scene/3
    , send_rank_info/1
    , send_rank_info/2
    , get_rank/2
    , get_rank/1
    , account_kf_void_fam/1
    , account_void_fam/1
    , add_interval_score/0
    , load_data/0
    , load_kf_data/0
    , make_void_rank/2
    , batch_save_rank/1
    , batch_save_rank_log/1
    , format_bin_string/1
    , analyz/2
    , account_award/4
    , account_award/2
    , get_floor_award/3
    , create_drops/2
    , scene_clear_mon/4
    , scene_clear_mon/0
    , activitycalen_start/1
    , trigger_sp_task/3
    , check/1
    , clean_mon/1
    , clean_mon/2
    , calc_click/0
    , drops_reduce_event/2
    , be_collected/2
    , change_pk_state/1
    , brocast_success_collect/2
    , send_to_scene/3
    , ta_void_fam/5
    ]).

get_role_map() ->
    case get(?P_ROLE_MAP) of
        undefined -> #{};
        Val -> Val
    end.

save_role_map(RoleMap) ->
    put(?P_ROLE_MAP, RoleMap).

get_role_rank()->
    case get(?P_ROLE_RANK) of
        undefined->#{};
        Val -> Val
    end.

save_role_rank(RoleRanks)->
    put(?P_ROLE_RANK,RoleRanks).

get_achieve_floor_map() ->
    case get(?P_ACHIEVE_FLOOR_RECORD_MAP) of
        undefined -> #{};
        Val -> Val
    end.

save_achieve_floor_map(AchieveFloorMap) ->
    put(?P_ACHIEVE_FLOOR_RECORD_MAP, AchieveFloorMap).

init_act() ->
    case lib_activitycalen_api:get_first_enabled_ac_id(?MOD_VOID_FAM, 0) of
        {ok, ActId} ->
            act_start(ActId);
        _ ->
            #status_void_fam{}
    end.

activitycalen_start(AcSub)->
    case config:get_cls_type() =:= ?NODE_GAME of
        true->
            mod_void_fam_local:act_start(AcSub);
        _->
            mod_void_fam:act_start(AcSub)
    end.


act_start(ActId) ->
    Act = data_activitycalen:get_ac(?MOD_VOID_FAM, 0, ActId),
    #base_ac{time_region = TimeRegion} = Act,
    {_NowDay, {NowH,NowM,_}} = calendar:local_time(),
    Now = NowH * 60 + NowM,
    ZeroAclock = utime:unixdate(),
    case ulists:find(fun
        ({{SH, SM}, {EH, EM}}) ->
            SH * 60 + SM =< Now andalso Now =< EH * 60 + EM
    end, TimeRegion) of
        {ok, {{_SH, _SM}, {EH, EM}}} ->
            Etime = ZeroAclock + EH * 3600 + EM * 60,
            IsCls = util:is_cls(),
            ActClsType = lib_void_fam:get_act_cls_type(),
            case IsCls of
                true -> %% 在跨服中心检测到开启要通知各个服务器开启活动
                    Args = [{?SYNC_TYPE_ACT_STATUS, ?ACT_STATUS_OPEN, Etime}],
                    clusters_center:apply_to_all_node(mod_void_fam_local, sync_data, [Args]),
                    NowTime = utime:unixtime(),
                    EndDelay = max(Etime - NowTime, 1),
                    IntAdd = data_void_fam:get_cfg(interval_add),
                    ARef = erlang:send_after(IntAdd * 1000, self(), {'interval_add'}),
                    Ref = erlang:send_after(EndDelay * 1000, self(), {'act_end'}),
                    [{1,DropAdd}|_] = data_void_fam:get_cfg(interval_drop),
                    DRef = erlang:send_after( DropAdd * 1000, self(), {'interval_drop'}),
                    lib_void_fam:clean_mon(1),
                    %% 删除进程字典数据
                    erase(?P_ROLE_MAP),
                    erase(?P_ROLE_RANK),
                    erase(?P_ACHIEVE_FLOOR_RECORD_MAP),
                    #status_void_fam{
                        status = ?ACT_STATUS_OPEN,
                        cls_type = ActClsType,
                        etime = Etime,
                        ref = Ref,
                        add_ref = ARef,
                        drop_ref = [1,DRef,NowTime+DropAdd]
                    };
                false ->
                    case ActClsType of
                        ?ACT_TYPE_BF ->
                            act_start_by_time(Etime);
                        _ ->
                            #status_void_fam{}
                    end
            end;
        _ ->
            #status_void_fam{}
    end.

act_start_by_time(Etime) ->
    act_start_by_time(false,Etime).
act_start_by_time(IsDelay,Etime)->
    ActClsType = lib_void_fam:get_act_cls_type(),
    NowTime = utime:unixtime(),
    case ActClsType of
        ?ACT_TYPE_BF ->
            IntAdd = data_void_fam:get_cfg(interval_add),
            EndDelay = max(Etime - NowTime, 1),
            ARef = erlang:send_after(IntAdd * 1000, self(), {'interval_add'}),
            Ref = erlang:send_after(EndDelay * 1000, self(), {'act_end'}),
            [{1,DropAdd}|_] = data_void_fam:get_cfg(interval_drop),
            DRef = erlang:send_after( DropAdd * 1000, self(), {'interval_drop'}),
            {ok,Bin} = pt_600:write(60011,[0,DropAdd]),
            #void_fam_floor_cfg{scene = Scene}=data_void_fam:get_floor_cfg(9),
            lib_void_fam:clean_mon(0),
            scene_mgr:apply_cast(Scene,[?MODULE,send_to_scene,[Scene,Bin]]);
        _ ->
            Ref = [],ARef = [],DRef = 0,DropAdd=0
    end,

    NeedRoleLv = lib_module:get_open_lv(?MOD_VOID_FAM),

    lib_chat:send_TV({all}, ?MOD_VOID_FAM, 1, [NeedRoleLv]),

    broadcast_act_info(NeedRoleLv, ?ACT_STATUS_OPEN, Etime),

    %% 通知活动日历
    lib_activitycalen_api:success_start_activity(?MOD_VOID_FAM),

    %% 删除进程字典数据
    erase(?P_ROLE_MAP),
    erase(?P_ROLE_RANK),
    erase(?P_ACHIEVE_FLOOR_RECORD_MAP),

    %% 判断托管
    VoidFamPid = offline_hosting_void_fam:check_hosting(IsDelay),
    put(check_hosting,VoidFamPid),

    #status_void_fam{
        status = ?ACT_STATUS_OPEN,
        cls_type = ActClsType,
        etime = Etime,
        ref = Ref,
        add_ref = ARef,
        drop_ref = [1,DRef,NowTime+DropAdd]
    }.

%% 本服请求跨服中心同步数据
request_sync_data([], State) -> State;
request_sync_data([{?SYNC_TYPE_ACT_STATUS, Node}|L], State) ->
    #status_void_fam{status = ActStatus, etime = Etime} = State,
    Args = [{?SYNC_TYPE_ACT_STATUS, ActStatus, Etime}],
    clusters_center:apply_cast(Node, mod_void_fam_local, sync_data, [Args]),
    request_sync_data(L, State);
request_sync_data([_|L], State) ->
    request_sync_data(L, State).

%% 跨服中心同步数据到本服
sync_data([], State) -> State;
sync_data([{?SYNC_TYPE_ACT_STATUS, ?ACT_STATUS_OPEN, Etime}|L], _State) ->
    case (Etime-utime:unixtime()) > 300 of
        true-> %%活动剩余时间大于5min 托管才启动
            NewState = act_start_by_time(true,Etime),
            sync_data(L, NewState);
        _->
            NewState = act_start_by_time(Etime),
            sync_data(L, NewState)
    end;
sync_data([{?SYNC_TYPE_ACT_STATUS, ?ACT_STATUS_CLOSE, _Etime}|L], State) ->
    NewState = act_close(State),
    sync_data(L, NewState);
sync_data([_|L], State) ->
    sync_data(L, State).

%% 判断活动是本服的还是跨服的
get_act_cls_type() ->
    Acts = data_activitycalen:get_ac_sub(?MOD_VOID_FAM, 0),
    NeedDay = calc_need_day(Acts,999),
    Opday = util:get_open_day(),
    case Opday >= NeedDay of
        true -> ?ACT_TYPE_KF;
        false -> ?ACT_TYPE_BF
    end.

calc_need_day([],NeedDay)->
    NeedDay;
calc_need_day([H|T],NeedDay)->
    case data_activitycalen:get_ac(?MOD_VOID_FAM, 0, H) of
        #base_ac{node_type = 2,open_day = [{Min,_}]} when Min<NeedDay->
            calc_need_day(T,Min);
        _->
            calc_need_day(T,NeedDay)
    end.


%% 活动结束
act_close(State) ->
    #status_void_fam{cls_type = ActClsType,status = ActStatus, ref = ORef} = State,
    case ActStatus == ?ACT_STATUS_OPEN of
        true ->
            util:cancel_timer(ORef),

            lib_chat:send_TV({all}, ?MOD_VOID_FAM, 2, []),
            lib_activitycalen_api:success_end_activity(?MOD_VOID_FAM),

            RoleMap = get_role_map(),
            RoleList = maps:values(RoleMap),
            F = fun(RoleInfo) ->
                case RoleInfo of
                    #role_info{
                        role_id = RoleId,
                        name  = RName,
                        scene = Scene,
                        ref = Ref,
                        floor = Floor,
                        mfloor = MFloor,
                        awarded = HasF,
                        sort = Sort,
                        score = Score
                    } when Scene > 0 ->
                        util:cancel_timer(Ref),
                        %% 日志
                        QuitType = 4,
                        lib_void_fam:add_log(RoleId,RName,QuitType, Floor, Score),
                        Rank = get_rank(RoleId,0),
                        Grade = lists:max([0|HasF]),
                        {ok,Bin} = pt_600:write(60009,[Rank,MFloor,Score,Grade,Sort]),
                        lib_server_send:send_to_uid(RoleId,Bin),
                        lib_scene:change_player_scene(RoleId, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]);
                    _ -> skip
                end
            end,
            lists:foreach(F, RoleList),

            NeedRoleLv = lib_module:get_open_lv(?MOD_VOID_FAM),
            broadcast_act_info(NeedRoleLv, ?ACT_STATUS_CLOSE, 0),
            AllRoleRanks = maps:values(get_role_rank()),
            [account_void_fam(RoleRanks)||RoleRanks<-AllRoleRanks],

            lib_void_fam:batch_save_rank_log(?ACT_TYPE_BF),

            %% 删除进程字典数据
            erase(?P_ROLE_MAP),
            erase(?P_ACHIEVE_FLOOR_RECORD_MAP),
            offline_hosting_void_fam:notice_logout(),
            #status_void_fam{cls_type = ActClsType,status = ?ACT_STATUS_CLOSE};
        false -> State#status_void_fam{status = ?ACT_STATUS_CLOSE}
    end.

% act_real_close(_State)->
%     RoleMap = get_role_map(),
%     RoleList = maps:values(RoleMap),
%     F = fun(RoleInfo) ->
%         case RoleInfo of
%             #role_info{
%                 role_id = RoleId,
%                 scene = Scene,
%                 ref = Ref,
%                 floor = Floor,
%                 mfloor = MFloor,
%                 awarded = HasF,
%                 sort = Sort,
%                 score = Score
%             } when Scene > 0 ->
%                 util:cancel_timer(Ref),
%                 %% 日志
%                 QuitType = 4,
%                 lib_void_fam:add_log(RoleId, QuitType, Floor, Score),
%                 Rank = get_rank(RoleId,0),
%                 Grade = lists:max([0|HasF]),
%                 {ok,Bin} = pt_600:write(60009,[Rank,MFloor,Score,Grade,Sort]),
%                 lib_server_send:send_to_uid(RoleId,Bin),
%                 lib_scene:change_player_scene(RoleId, 0, 0, 0, [{change_scene_hp_lim, 1}, {action_free, ?ERRCODE(err600_cant_change_scene_in_void_fam)}]);
%             _ -> skip
%         end
%     end,
%     lists:foreach(F, RoleList),

%     NeedRoleLv = lib_module:get_open_lv(?MOD_VOID_FAM),
%     broadcast_act_info(NeedRoleLv, ?ACT_STATUS_CLOSE, 0),
%     AllRoleRanks = maps:values(get_role_rank()),
%     [account_void_fam(RoleRanks)||RoleRanks<-AllRoleRanks],

%     %% 删除进程字典数据
%     erase(?P_ROLE_MAP),
%     erase(?P_ACHIEVE_FLOOR_RECORD_MAP),
%     #status_void_fam{status=?ACT_STATUS_CLOSE}.

%% 跨服中心给玩家发协议
send_to_uid(Node, RoleId, BinData) when BinData =/= [] ->
    case Node =:= none of
        true ->
            lib_server_send:send_to_uid(RoleId, BinData);
        false ->
            clusters_api:send_to_uid(Node, RoleId, BinData)
    end;
send_to_uid(_Node, _RoleId, _Bin) -> skip.

%% 检测玩家是否在虚空秘境
is_in_void_fam(Scene) ->
    SceneIds = data_void_fam:get_scene(),
    KFSceneIds = data_void_fam:get_kf_scene(),
    case lists:member(Scene, SceneIds) of
        false ->
            lists:member(Scene, KFSceneIds);
        true -> true
    end.

%% 检测玩家是否在虚空秘境
is_in_kf_void_fam(Scene) ->
    KFSceneIds = data_void_fam:get_kf_scene(),
    lists:member(Scene, KFSceneIds).


get_floor(Scene)->
    SceneIds = data_void_fam:get_scene(),
    case lists:member(Scene, SceneIds) of
        false ->
            data_void_fam:get_kf_floor(Scene);
        true ->
            data_void_fam:get_floor(Scene)
    end.

get_pre_scene(Scene,Floor,SerID)->
    SceneIds = data_void_fam:get_scene(),
    case lists:member(Scene, SceneIds) of
        false ->
            PoolID = clusters_node:apply_call(cross_zone,get_server_zone,[czone,SerID]),
            #void_fam_floor_cfg{kf_scene=PreScene}=data_void_fam:get_floor_cfg(Floor-1),
            {PreScene,PoolID};
        true ->
            #void_fam_floor_cfg{scene=PreScene}=data_void_fam:get_floor_cfg(Floor-1),
            {PreScene,0}
    end.

update_role_hp(RoleId, Scene, Hp) ->
    RoleMap = lib_void_fam:get_role_map(),
    NewRoleMap = case maps:get(RoleId, RoleMap, false) of
        #role_info{scene = Scene} = RoleInfo ->
            maps:put(RoleId, RoleInfo#role_info{hp = Hp}, RoleMap);
        _ -> RoleMap
    end,
    lib_void_fam:save_role_map(NewRoleMap).

kill_player(AttackerId, AttackerName, DefenderId, DefenderName, DefenderScene) ->
    RoleMap = get_role_map(),
    case maps:get(DefenderId, RoleMap, false) of
        #role_info{
            scene = DefenderScene,
            die_num = DefenderDieNum,
            combo = DCombo,
            figure = DFigure,

            node = DNode,
            score = DScore,
            kills = DKills,
            awarded = DHasF

        } = DRoleInfo when DefenderScene > 0 ->
            NewDRoleInfo = DRoleInfo#role_info{combo = 0, die_num = DefenderDieNum + 1, hp = 0},

            DGrade = lists:max([0|DHasF]),
            {ok, BinData} = pt_600:write(60005, [DScore,DKills,0,DGrade]),
            send_to_uid(DNode, DefenderId, BinData),

            NewRoleMapTmp = maps:put(DefenderId, NewDRoleInfo, RoleMap);
        _ ->
            DRoleInfo = false, NewRoleMapTmp = RoleMap,DFigure=#figure{},DCombo=0
    end,
    case maps:get(AttackerId, NewRoleMapTmp, false) of
        #role_info{
            scene = AttackerScene,
            score = Score,
            kills = OKills,
            combo = AttackerCombo,
            mkills = Mkills,
            mcombo = Mcombo,
            figure = AFigure
        } = ARoleInfo when AttackerScene > 0 andalso AttackerScene == DefenderScene ->
            ScorePlus = data_void_fam:get_cfg(kill_player_score),
            NewScore = Score + ScorePlus,
            UpdateScore = true,
            NKills = OKills+1,
            NewARoleInfo0 = ARoleInfo#role_info{combo = AttackerCombo + 1,mkills=Mkills+1,mcombo=max(AttackerCombo+1,Mcombo),kills=NKills, score = NewScore},
            NewARoleInfo = score_stage_award(NewScore,NewARoleInfo0),
            %% 触发成就
            handle_score_achievement(AttackerId, ScorePlus),
            NewRoleMap = maps:put(AttackerId, NewARoleInfo, NewRoleMapTmp);
        _ -> UpdateScore = false, NewARoleInfo = false, NewRoleMap = NewRoleMapTmp,AFigure=#figure{}
    end,
    save_role_map(NewRoleMap),
    case DCombo>= 0 of
        true->
            {ok,Bin} = pt_600:write(60012,[1,AFigure,DFigure]);
        _->
            {ok,Bin} = pt_600:write(60012,[0,AFigure,DFigure])
    end,
    send_battle_tv(NewARoleInfo, AttackerName, DRoleInfo, DefenderName, Bin),
    %% 触发成就
    ClsType = clusters_api:get_cluster_type(),
    case ClsType of
        ?CLUSTER_NODE ->
            lib_achievement_api:async_event(AttackerId, lib_achievement_api, void_fam_kill_player_event, 1);
        _ ->
            Node = clusters_center:get_node_by_role_id(AttackerId),
            clusters_center:apply_cast(Node, lib_achievement_api, void_fam_kill_player_event, [AttackerId, 1])
    end,
    case UpdateScore of
        true ->
            score_plus(AttackerId, AttackerName);
        _ -> skip
    end.

kill_mon(AttackerId, AttackerName, _MonId, BattleSceneId) ->
    RoleMap = get_role_map(),
    case maps:get(AttackerId, RoleMap, false) of
        #role_info{
            scene = Scene,
            score = Score,
            kills = OKills,
            combo = AttackerCombo,
            mkills = Mkills,
            mcombo = Mcombo
        } = ARoleInfo when Scene > 0 andalso Scene == BattleSceneId ->
            ScorePlus = data_void_fam:get_cfg(kill_mon_score),
            NewARoleInfo0 = ARoleInfo#role_info{kills=OKills+1,mkills=Mkills+1,combo = AttackerCombo + 1,mcombo=max(AttackerCombo+1,Mcombo),score = Score + ScorePlus},
            NewARoleInfo = score_stage_award(Score+ScorePlus,NewARoleInfo0),
            NewRoleMap = maps:put(AttackerId, NewARoleInfo, RoleMap),
            save_role_map(NewRoleMap),
            send_battle_tv(NewARoleInfo, AttackerName, NewARoleInfo, AttackerName),
            handle_score_achievement(AttackerId, ScorePlus),
            score_plus(AttackerId, AttackerName);
        _ ->
            skip
    end.

%% 玩家积分 击杀人头增加
score_plus(RoleId, RoleName) ->
    RoleMap = get_role_map(),
    case maps:get(RoleId, RoleMap, false) of
        #role_info{
            node = Node,
            score = Score,
            kills = Kills,
            floor = Floor,
            combo = Combo,
            awarded=HasF
        } = RoleInfo ->
            case data_void_fam:get_floor_cfg(Floor) of
                #void_fam_floor_cfg{nkill = NeedKill} = FloorCfg ->
                    %% 通知客户端更新积分
                    Grade = lists:max([0|HasF]),
                    {ok, BinData} = pt_600:write(60005, [Score,min(Kills, NeedKill),Combo,Grade]),
                    send_to_uid(Node, RoleId, BinData),
                    case Kills >= NeedKill of
                        true ->
                            pass_floor(RoleInfo, RoleName, FloorCfg, RoleMap);
                        false ->
                            update_ranks(RoleInfo)
                    end;
                _ -> skip
            end;
        _ -> skip
    end.

handle_score_achievement(RoleId, PlusScore) ->
    %% 触发成就
    ClsType = clusters_api:get_cluster_type(),
    case ClsType of
        ?CLUSTER_NODE ->
            lib_achievement_api:async_event(RoleId, lib_achievement_api, void_fam_score_event, PlusScore);
        _ ->
            Node = clusters_center:get_node_by_role_id(RoleId),
            clusters_center:apply_cast(Node, lib_achievement_api, void_fam_score_event, [RoleId, PlusScore])
    end.

%% 通关楼层
pass_floor(RoleInfo, RoleName, _FloorCfg, RoleMap) ->
    #role_info{node = Node, sort=OSort, ser_id = SerId,ser_num=SerNum,zone=Zone,role_id = RoleId, floor = Floor,mfloor=OMfloor, scene = Scene} = RoleInfo,
    % #void_fam_floor_cfg{reward = PassFloorReward} = FloorCfg,
    IsClsScene = lib_scene:is_clusters_scene(Scene),
    [MaxFloor|_] = data_void_fam:get_all_floor(),
    case Floor < MaxFloor of
        true ->
            NextFloor = Floor + 1,
            case data_void_fam:get_floor_cfg(NextFloor) of
                #void_fam_floor_cfg{scene = NextFloorScene,reward = PassFloorReward, kf_scene = NextFloorKFScene} ->
                    RealNextScene = ?IF(IsClsScene == true, NextFloorKFScene, NextFloorScene),
                    NewRoleInfo0 = RoleInfo#role_info{floor = NextFloor, kills = 0, mfloor=max(OMfloor,NextFloor),scene = RealNextScene},
                    Sort = achieve_floor(SerId,SerNum,Zone,RoleId, RoleName, NextFloor, MaxFloor),
                    NewRoleInfo = case  NextFloor == MaxFloor of
                        true when OSort=<0->
                            NewRoleInfo0#role_info{sort=Sort};
                        _->
                            NewRoleInfo0
                    end,
                    NewRoleMap = maps:put(RoleId, NewRoleInfo, RoleMap),
                    save_role_map(NewRoleMap),
                    update_ranks(NewRoleInfo),
                    case IsClsScene of
                        true  ->
                            PoolID = cross_zone:get_server_zone(czone,SerId),
                            Args = [RoleId, NextFloor,OMfloor, RealNextScene,PoolID, PassFloorReward],
                            clusters_center:apply_cast(Node, lib_void_fam, give_floor_reward, Args);
                        false  ->
                            lib_void_fam:give_floor_reward(RoleId, NextFloor,OMfloor, RealNextScene,0, PassFloorReward)
                    end;
                _ -> skip
            end;
        false ->
            % 20180723 新版本 通过不踢人
            ignore
            %% 通关了, 延迟10s把玩家传送出去
            % case IsClsScene of
            %     true ->
            %         Args = [RoleId, MaxFloor, PassFloorReward],
            %         clusters_center:apply_cast(Node, lib_void_fam, pass_all_floor, Args);
            %     false ->
            %         lib_void_fam:pass_all_floor(RoleId, MaxFloor, PassFloorReward)
            % end


            % CountDownTime = data_void_fam:get_cfg(pass_exit_count_down),
            % Ref = erlang:send_after(CountDownTime * 1000, self(), {'pass_exit_scene', RoleId}),
            % NewRoleInfo = RoleInfo#role_info{ref = Ref},
            % NewRoleMap = maps:put(RoleId, NewRoleInfo, RoleMap),
            % save_role_map(NewRoleMap)
    end.

achieve_floor(SerId,SerNum,Zone, RoleId, RoleName, AchieveFloor, MaxFloor) ->
    send_floor_tv(SerId,SerNum,Zone, RoleId, RoleName, AchieveFloor, MaxFloor),
    %% 增加到达本层的玩家数量
    AchieveFloorMap = get_achieve_floor_map(),
    HasAchieveNum = maps:get({Zone,AchieveFloor}, AchieveFloorMap, 0),
    NewAchieveFloorMap = maps:put({Zone,AchieveFloor}, HasAchieveNum + 1, AchieveFloorMap),
    save_achieve_floor_map(NewAchieveFloorMap),
    HasAchieveNum + 1.

%%判断是否给首层
get_floor_award(RoleId,Time,Reward)->
    case utime:is_today(Time) of
        true->
            ignore;
        _->
            Produce = #produce{title = utext:get(6000001), content = utext:get(204), reward = Reward, type = void_fam, show_tips = 1, remark = lists:concat(["floor_reward:",1])},
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, give_floor_reward, [Reward,1,Produce])
    end.

%% 通关楼层
give_floor_reward(RoleId, NextFloor,Mfloor, NextFloorScene,PoolID,Reward) ->
    %% 发本层奖励给玩家
    case Reward =/= [] of
        true when NextFloor>Mfloor->
            Produce = #produce{title = utext:get(6000001), content = utext:get(204), reward = Reward, type = void_fam, show_tips = 1, remark = lists:concat(["floor_reward:", NextFloor - 1])},
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, give_floor_reward, [Reward,Mfloor,Produce]);
        _ -> skip
    end,
    [MaxFloor|_] = data_void_fam:get_all_floor(),
    case NextFloor == MaxFloor of
        true when NextFloor>Mfloor -> %% 达到最高楼层，发通关奖励给玩家
            give_pass_reward(PoolID, RoleId, MaxFloor);
        _ -> skip
    end,
    case NextFloorScene > 0 of
        true ->
            %% 推送到达新楼层协议
            lib_server_send:send_to_uid(RoleId, pt_600, 60007, [NextFloor]),
            %% 进入下层场景
            {X, Y} = get_scene_born_xy(NextFloorScene),
            lib_scene:change_player_scene(RoleId, NextFloorScene, PoolID, 0, X, Y, true, []),
            offline_hosting_void_fam:cast_load_scene_behavior(RoleId);
        false -> skip
    end.

give_floor_reward(Status,Reward,_Floor,Produce)->
    #player_status{id=RoleId}=Status,
    NProduce = Produce#produce{reward=Reward},
    lib_goods_api:send_reward_with_mail(RoleId, NProduce),
    {ok,Status}.


give_pass_reward(PoolID, RoleId, MaxFloor) ->
    Zone = scene_mgr:get_pool_zone(PoolID),
    AchieveFloorMap = get_achieve_floor_map(),
    AchieveNum = maps:get({Zone,MaxFloor}, AchieveFloorMap, 0),
    Max = data_void_fam:get_pass_max(),
    case AchieveNum >= 1 andalso AchieveNum =< Max of
        true ->
            PassReward = data_void_fam:get_pass_reward(AchieveNum);
        _ ->
            PassReward = []
    end,
    case PassReward =/= [] of
        true ->
            Produce = #produce{title = utext:get(6000001), content = utext:get(204), reward = PassReward, type = void_fam, show_tips = 1, remark = lists:concat(["pass_ranking:", AchieveNum])},
            lib_goods_api:send_reward_with_mail(RoleId, Produce);
        false -> skip
    end.

%% 发送通关奖励
pass_all_floor(RoleId, MaxFloor, PassFloorReward) ->
    case PassFloorReward =/= [] of
        true ->
            Produce = #produce{title = utext:get(6000001), content = utext:get(204), reward = PassFloorReward, type = void_fam, show_tips = 1, remark = lists:concat(["floor_reward:", MaxFloor])},
            lib_goods_api:send_reward_with_mail(RoleId, Produce);
        false -> skip
    end,
    %% 更新离开场景标示符
    lib_player:update_player_info(RoleId, [{leave_scene_sign, 1}]),
    lib_player:update_player_scene_info(RoleId, [{leave_scene_sign, 1}]),
    NowTime = utime:unixtime(),
    CountDownTime = data_void_fam:get_cfg(pass_exit_count_down),
    {ok, BinData} = pt_600:write(60006, [NowTime + CountDownTime]),
    lib_server_send:send_to_uid(RoleId, BinData).

%% 是否已经通关
is_pass(RoleInfo) ->
    case RoleInfo of
        #role_info{
            score = Score,
            floor = Floor
        } ->
            [MaxFloor|_] = data_void_fam:get_all_floor(),
            #void_fam_floor_cfg{nkill = MaxFloorScore} = data_void_fam:get_floor_cfg(MaxFloor),
            Floor == MaxFloor andalso Score >= MaxFloorScore;
        _ -> false
    end.

%% 发送到达特殊层数的传闻
send_floor_tv(SerId,SerNum,Zone,RoleId, RoleName, AchieveFloor, MaxFloor) ->
    case AchieveFloor == MaxFloor of
        true ->
            AchieveFloorMap = get_achieve_floor_map(),
            HasAchieveNum = maps:get({Zone,AchieveFloor}, AchieveFloorMap, 0),
            CurAchieveNum = HasAchieveNum + 1,
            case CurAchieveNum of
                1 ->
                    BinData = lib_chat:make_tv(?MOD_VOID_FAM, 7, [SerNum,RoleName, RoleId,SerId]);
                % 2 ->
                %     BinData = lib_chat:make_tv(?MOD_VOID_FAM, 8, [SerNum,RoleName, RoleId,SerId]);
                % 3 ->
                %     BinData = lib_chat:make_tv(?MOD_VOID_FAM, 9, [SerNum,RoleName, RoleId,SerId]);
                _ ->
                    BinData = []
            end,
            case BinData =/= [] of
                true ->
                    RoleMap = get_role_map(),
                    AllRoleInfo = maps:values(RoleMap),
                    F = fun(T) ->
                        case T of
                            #role_info{node = TmpNode, role_id = TmpRoleId, scene = TmpScene,zone=TmpZone} when TmpScene > 0 ->
                                TmpZone==Zone andalso send_to_uid(TmpNode, TmpRoleId, BinData);
                            _ ->
                                skip
                        end
                    end,
                    spawn(fun() ->lists:foreach(F, AllRoleInfo) end);
                false -> skip
            end;
        false -> %% 最高层才需要发传闻
            skip
    end.

%% 发送战斗传闻(只有攻击者和防守者都是有效时才发传闻)
send_battle_tv(NewARoleInfo, AttackerName, OldDRoleInfo, DefenderName)->
    send_battle_tv(NewARoleInfo, AttackerName, OldDRoleInfo, DefenderName,0).
send_battle_tv(NewARoleInfo, AttackerName, OldDRoleInfo, DefenderName,ComboBin)
    when is_record(NewARoleInfo, role_info) andalso is_record(OldDRoleInfo, role_info) ->
    #role_info{ser_id = _AttackerSerId, ser_num=AttackerSnum,role_id = AttackerId, combo = AttackerCombo,zone=AZone,scene = AScene} = NewARoleInfo,
    #role_info{ser_id = _DefenderSerId, role_id = DefenderId, combo = DefenderCombo} = OldDRoleInfo,
    if
        DefenderCombo >= 5 andalso AttackerId /= DefenderId ->
            BreakComboBin = lib_chat:make_tv(?MOD_VOID_FAM, 14, [AttackerName, AttackerId, DefenderName, DefenderId, DefenderCombo]);
        true ->
            BreakComboBin = []
    end,
    if
        AttackerCombo == 10 ->
            AttackerComboBin = lib_chat:make_tv(?MOD_VOID_FAM, 5, [AttackerSnum, AttackerName, AttackerId]);
        AttackerCombo == 20 ->
            AttackerComboBin = lib_chat:make_tv(?MOD_VOID_FAM, 6, [AttackerSnum, AttackerName, AttackerId]);
        AttackerCombo == 50 ->
            AttackerComboBin = lib_chat:make_tv(?MOD_VOID_FAM, 11, [AttackerSnum, AttackerName, AttackerId]);
        AttackerCombo == 100 ->
            AttackerComboBin = lib_chat:make_tv(?MOD_VOID_FAM, 12, [AttackerSnum, AttackerName, AttackerId]);
        true ->
            AttackerComboBin = []
    end,
    RoleMap = get_role_map(),
    AllRoleInfo = maps:values(RoleMap),
    case ComboBin /= 0 of
        true->
            F0 = fun(T) ->
                case T of
                    #role_info{node = TmpNode,zone=TmpZone, role_id = TmpRoleId, scene = TmpScene} when TmpScene > 0 ->
                        % send_to_uid(TmpNode, TmpRoleId, BreakComboBin),
                        AZone =:= TmpZone andalso AScene =:= TmpScene andalso send_to_uid(TmpNode, TmpRoleId, ComboBin);
                    _ ->
                        skip
                end
            end,
            spawn(fun() -> lists:foreach(F0, AllRoleInfo) end);
        _->
            skip
    end,
    case AttackerComboBin =/= []  of
        true ->
            F1 = fun(T) ->
                case T of
                    #role_info{node = TmpNode,zone=TmpZone, role_id = TmpRoleId, scene = TmpScene} when TmpScene > 0 ->
                        AZone =:= TmpZone andalso AScene =:= TmpScene andalso send_to_uid(TmpNode, TmpRoleId, AttackerComboBin);
                    _ ->
                        skip
                end
            end,
            spawn(fun() -> lists:foreach(F1, AllRoleInfo) end);
        false ->
            skip
    end,
    case BreakComboBin =/= [] of
        true->
            F2 = fun(T) ->
                case T of
                    #role_info{node = TmpNode,zone=TmpZone, role_id = TmpRoleId, scene = TmpScene} when TmpScene > 0 ->
                        AZone =:= TmpZone andalso AScene =:= TmpScene andalso send_to_uid(TmpNode, TmpRoleId, BreakComboBin);
                    _ ->
                        skip
                end
            end,
            spawn(fun() -> lists:foreach(F2, AllRoleInfo) end);
        _->
            skip
    end;
send_battle_tv(_, _, _, _,_) -> skip.

%% 获取场景出生点
get_scene_born_xy(SceneId) ->
    [X, Y] = lib_scene:get_born_xy(SceneId),
    {X, Y}.

%%发放积分阶段奖励
score_stage_award(Score,ARoleInfo)->
    #role_info{awarded=HasF,combo=Combo,node=Node,kills=Kills,role_id=RoleID,scene=Scene}=ARoleInfo,
    [Aid,Awards] = data_void_fam:get_stage_award(Score),
    IsClsScene = lib_scene:is_clusters_scene(Scene),
    case lists:member(Aid,HasF) of
        false when Aid>0 ->
            {ok,Bin} = pt_600:write(60005,[Score,Kills,Combo,Aid]),
            case IsClsScene of
                true ->
                    Args = [RoleID,Bin, Awards],
                    clusters_center:apply_cast(Node, lib_void_fam, give_score_reward, Args);
                false ->
                    lib_void_fam:give_score_reward(RoleID, Bin,Awards)
            end,
            ARoleInfo#role_info{awarded=[Aid|HasF]};
        _->
            ARoleInfo
    end.

give_score_reward(RoleId,Bin,Awards) ->
    case Awards =/= [] of
        true ->
            lib_server_send:send_to_uid(RoleId,Bin),
            lib_goods_api:send_reward_by_id(Awards, void_fam, RoleId);
        false -> skip
    end.

%%更新榜单
update_ranks(RoleInfo)->
    AllRoleRanks = get_role_rank(),
    #role_info{role_id=RoleID,ser_id=SerID}=RoleInfo,
    Zone = case util:is_cls() of
        true->
            cross_zone:get_server_zone(czone,SerID);
        _->
            0
    end,
    RoleRanks = maps:get(Zone,AllRoleRanks,[]),
    NRoleRanks0 = lists:keystore(RoleID,#role_info.role_id,RoleRanks,RoleInfo),
    NRoleRanks1 = compare_rank_sort(NRoleRanks0),
    Max = data_void_fam:get_cfg(max_rank),
    NRoleRanks  = compare_rank_data(NRoleRanks1,1,[],Max),
    NewAllRoleRanks = maps:put(Zone,NRoleRanks,AllRoleRanks),
    save_role_rank(NewAllRoleRanks).


analyz(N,Max)->
    List0 = lists:seq(1,N),
    List1 = [#role_info{score=urand:rand(1,100),role_id=A,mkills=urand:rand(1,30)}||A<-List0],
    List2 = compare_rank_sort(List1),
    List3 = compare_rank_data(List2,1,[],Max),
    io:format("sd:~p~n,~p~n,~p~n",[List1,List2,List3]).


%%排序 积分 击杀数
compare_rank_sort(RoleRanks)->
    lists:sort(fun(A1, A2) ->
            compare_rank_sort(A1, A2)
    end, RoleRanks).

compare_rank_sort(V1, V2) ->
    case V1#role_info.score > V2#role_info.score of
        true ->
            true;
        false ->
            case V1#role_info.score == V2#role_info.score of
                true->
                    case V1#role_info.mkills >= V1#role_info.mkills of
                        true->
                            true;
                        _->
                            false
                    end;
                _->
                    false
            end
    end.

%%排名赋值
compare_rank_data([],_,RoleRanks,_)->
    RoleRanks;
compare_rank_data(_,Rank,RoleRanks,Max) when Rank>Max->
    RoleRanks;
compare_rank_data([RoleInfo|ORoleRanks],Rank,RoleRanks,Max)->
    compare_rank_data(ORoleRanks,Rank+1,[RoleInfo#role_info{rank=Rank}|RoleRanks],Max).

%%打包榜单信息
send_rank_info(RoleID)->
    send_rank_info(RoleID,0).
send_rank_info(RoleID,SerID)->
    Zone = case util:is_cls() of
        true->
            cross_zone:get_server_zone(czone,SerID);
        _->
            0
    end,
    AllRoleRanks = lib_void_fam:get_role_rank(),
    RoleRanks = maps:get(Zone,AllRoleRanks,[]),
    {_IsAdd,RoleInfo}  = case lists:keyfind(RoleID,#role_info.role_id,RoleRanks) of
        false->
            RoleMap = lib_void_fam:get_role_map(),
            {true,maps:get(RoleID, RoleMap, false)};
        RoleRank->
            {false,RoleRank}
    end,
    % NRoleRanks = case is_record(RoleInfo,role_info) of
    %     true when IsAdd ->
    %         [RoleInfo|RoleRanks];
    %     _->
    %         RoleRanks
    % end,
    F = fun(#role_info{
        role_id = Rid,
        rank = Rank,
        ser_num = Snum,
        mfloor = Floor,
        mkills = MKills,
        mcombo = Combo,
        score = Score,
        vip = Vip,
        sex = Sex,
        figure = Figure
    }) ->
        {Rank, Rid, Figure, Snum,Floor, MKills, Combo, Score,Vip,Sex}
    end,
    SendList = lists:map(F, RoleRanks),
    case is_record(RoleInfo,role_info) of
        true->
            #role_info{
                rank = MyRank,
                mfloor = MyFloor,
                mkills = MyMKills,
                mcombo = MyCombo,
                score = MyScore
            }=RoleInfo;
        _->
            MyRank=0,MyFloor=1,MyMKills=0,MyCombo=0,MyScore=0
    end,
    {ok,Bin} = pt_600:write(60010,[SendList,MyRank,MyFloor,MyMKills,MyCombo,MyScore]),
    Bin.

%%获取排名
get_rank(RoleID)->
    get_rank(RoleID,0).
get_rank(RoleID,Zone)->
    AllRoleRanks = lib_void_fam:get_role_rank(),
    RoleRanks = maps:get(Zone,AllRoleRanks,[]),
    case lists:keyfind(RoleID,#role_info.role_id,RoleRanks) of
        false->
            0;
        RoleRank->
            RoleRank#role_info.rank
    end.

%%榜单结算
account_void_fam([])->
    ignore;
account_void_fam([RoleInfo|RoleRanks])->
    #role_info{
        rank = Rank,
        level = Lv,
        role_id = RoleID
    }=RoleInfo,
    Awards = data_void_fam:get_rank_award(Rank),
    case Awards =/= [] of
        true ->
            account_award(Awards,Lv,Rank,RoleID);
        false -> skip
    end,
    account_void_fam(RoleRanks).

account_kf_void_fam([])->
    ignore;
account_kf_void_fam([RoleInfo|RoleRanks])->
    #role_info{
        rank = Rank,
        node = Node,
        level = Lv,
        role_id = RoleID
    }=RoleInfo,
    Awards = data_void_fam:get_rank_award(Rank),
    case Awards =/= [] of
        true ->
            Args = [Awards,Lv,Rank,RoleID],
            clusters_center:apply_cast(Node,?MODULE,account_award, Args);
        false -> skip
    end,
    account_kf_void_fam(RoleRanks).

%%在线的用在线等级  离线的用最后的等级
account_award(Awards,_Lv,Rank,RoleID)->
    Produce = #produce{title = utext:get(6000001), content = utext:get(6000002,[Rank]), reward = Awards, type = void_fam, show_tips = 1, remark = lists:concat(["rank:", Rank])},
    Pid = misc:get_player_process(RoleID),
    case misc:is_process_alive(Pid) of
        true->
            lib_player:apply_cast(RoleID, ?APPLY_CAST_SAVE, ?MODULE, account_award, [Produce]);
        _->
            #produce{reward=Reward}=Produce,
            NProduce = Produce#produce{reward=Reward},
            lib_goods_api:send_reward_with_mail(RoleID, NProduce)
    end.

account_award(Player,Produce)->
    #produce{reward=Reward}=Produce,
    NProduce = Produce#produce{reward=Reward},
    lib_goods_api:send_reward_with_mail(Player, NProduce).

%%增加积分
add_interval_score()->
    RoleMap = get_role_map(),

    RoleList = maps:to_list(RoleMap),

   {NRoleList0,ZoneRoles} = split_add_score(RoleList),


    NRoleMap = maps:from_list(NRoleList0),
    save_role_map(NRoleMap),

    RoleRanks = make_zone_rank(ZoneRoles,#{}),

    save_role_rank(RoleRanks).


make_zone_rank([],RoleRanks)->
    RoleRanks;
make_zone_rank([{Zid,RoleLists}|T],RoleRanks)->
    NRoleLists1 = compare_rank_sort(RoleLists),
    Max = data_void_fam:get_cfg(max_rank),
    NRoleList  = compare_rank_data(NRoleLists1,1,[],Max),
    NRoleRanks = maps:put(Zid,NRoleList,RoleRanks),
    make_zone_rank(T,NRoleRanks).


split_add_score(RoleList)->
    split_add_score(RoleList,[],[]).
split_add_score([],RoleList,ZoneRoles)->
    {RoleList,ZoneRoles};
split_add_score([{Key,H}|T],RoleList,ZoneRoles)->
    case H#role_info.scene>0 of
        true->
            #role_info{
                role_id = Rid,
                zone = Zid,
                node = Node,
                score = S,
                floor = Floor,
                kills = Kills,
                combo = Combo,
                awarded = HasF
            }=H,
            Pers = data_void_fam:get_cfg(interval_score),
            Per = case lists:keyfind(Floor,1,Pers) of
                {_,V}->
                    V;
                _->
                    0
            end,
            Grade = lists:max([0|HasF]),
            NScore = S+Per,
            {ok,Bin} = pt_600:write(60005,[NScore,Kills,Combo,Grade]),
            send_to_uid(Node,Rid,Bin),
            NRoleInfo = score_stage_award(NScore,H#role_info{score=NScore}),
            %% 触发成就
            handle_score_achievement(Rid, Per),
            NRoleList = [{Key,NRoleInfo}|RoleList],
            case lists:keyfind(Zid,1,ZoneRoles) of
                {_,SubZoneRoles}->
                    ignore;
                _->
                    SubZoneRoles = []
            end,
            NewSubZoneRoles = [NRoleInfo|SubZoneRoles],
            NZoneRoles = lists:keystore(Zid,1,ZoneRoles,{Zid,NewSubZoneRoles});
        _->
            NRoleList = [{Key,H}|RoleList],
            case lists:keyfind(H#role_info.zone,1,ZoneRoles) of
                {_,SubZoneRoles}->
                    ignore;
                _->
                    SubZoneRoles = []
            end,
            NewSubZoneRoles = [H|SubZoneRoles],
            NZoneRoles = lists:keystore(H#role_info.zone,1,ZoneRoles,{H#role_info.zone,NewSubZoneRoles})
    end,
    split_add_score(T,NRoleList,NZoneRoles).


%%启动加载
load_data()->
    Ranks = case db:get_all(?SQL_VOID_FAM_GET0) of
        [] ->
            [];
        RanksTem when is_list(RanksTem)  ->
            make_void_rank(RanksTem,[]);
        _ ->
            []
    end,
    #{0=>Ranks}.

load_kf_data()->
    Ranks = case db:get_all(?SQL_VOID_FAM_GET1) of
        [] ->
            [];
        RanksTem when is_list(RanksTem)  ->
            make_void_rank(RanksTem,[]);
        _ ->
            []
    end,
    RanksList = trans_zone_list(Ranks,[]),
    maps:from_list(RanksList).

trans_zone_list([],RanksList)->
    RanksList;
trans_zone_list([H|T],RanksList)->
    case lists:keyfind(H#role_info.zone,1,RanksList) of
        {Zone,ZRanks}->
            NewZRanks = lists:keystore(H#role_info.role_id,#role_info.role_id,ZRanks,H),
            NewRanksList = lists:keystore(Zone,1,RanksList,{Zone,NewZRanks}),
            trans_zone_list(T,NewRanksList);
        _->
            NewRanksList = lists:keystore(H#role_info.zone,1,RanksList,{H#role_info.zone,[H]}),
            trans_zone_list(T,NewRanksList)
    end.

%%数据拼接
make_void_rank([],NineRanks)->
    NineRanks;
make_void_rank([[Rank,Zone,Rid,SerNum,Name,Floor,Kills,Combo,Score,Vip,Sex,Carrer,Turn,VType,SVip,Lv]|Ranks],NewRanks)->
    Figure = #figure{
        id = Rid,
        name = Name,
        sex = Sex,
        career = Carrer,
        turn = Turn,
        vip_type = VType,
        vip = Vip,
        sup_vip = SVip,
        lv = Lv,
        power = Combo
    },
    Role = #role_info{
        role_id  = Rid,
        rank = Rank,
        zone = Zone,
        ser_num = SerNum,
        name = Name,
        mfloor  = Floor,
        mkills= Kills,
        mcombo = Combo,
        score= Score,
        vip = Vip,
        sex = Sex,
        figure = Figure
    },
    make_void_rank(Ranks,[Role|NewRanks]).


batch_save_rank_log(Type)->
    AllRoleList = maps:values(get_role_rank()),
    Time = utime:unixtime(),
    [batch_save_rank_log(Type,Time,RoleList)||RoleList<-AllRoleList].

batch_save_rank_log(_,_Time,[])->
    ignore;
batch_save_rank_log(Type,Time,RoleList)->
    DataStrList = [format_log_bin_string(Rank,Time) || Rank <- RoleList],
    DataStrAll  = util:join_bitstring(DataStrList, <<",">>),
    SqlReplace  = case Type of
        ?ACT_TYPE_BF->
            util:fmt_bin(?SQL_VOID_FAM_RANK_LOG_BATCH0, [DataStrAll]);
        _->
            util:fmt_bin(?SQL_VOID_FAM_RANK_LOG_BATCH1, [DataStrAll])
    end,
    db:execute(SqlReplace).

format_log_bin_string(RoleInfo,Time)->
    #role_info{
        role_id  = Rid,
        rank = Rank,
        zone = Zone,
        stage = Stage,
        name = Name,
        ser_id  = SerId,
        ser_num = SerNum,
        floor  = Floor,
        score= Score
    }=RoleInfo,
    Awards0 = data_void_fam:get_rank_award(Rank),
    Awards = util:term_to_string(Awards0),
    util:fmt_bin(<<"(~w,~w,'~ts',~w,~w,~w,~w,~w,~w,'~ts',~w)">>, [Zone,Rid,Name,Stage,SerId,SerNum,Floor,Score,Rank,Awards,Time]).

batch_save_rank(Type)->
    AllRoleList = maps:values(get_role_rank()),
    [batch_save_rank(Type,RoleList)||RoleList<-AllRoleList].

batch_save_rank(_,[])->
    ignore;
batch_save_rank(Type,RoleList)->
    DataStrList = [format_bin_string(Rank) || Rank <- RoleList],
    DataStrAll  = util:join_bitstring(DataStrList, <<",">>),
    SqlReplace  = case Type of
        ?ACT_TYPE_BF->
            util:fmt_bin(?SQL_VOID_FAM_BATCH0, [DataStrAll]);
        _->
            util:fmt_bin(?SQL_VOID_FAM_BATCH1, [DataStrAll])
    end,
    db:execute(SqlReplace).

format_bin_string(RoleInfo)->
    #role_info{
        role_id  = Rid,
        rank = Rank,
        zone = Zone,
        name = Name,
        ser_num = SerNum,
        floor  = Floor,
        kills= Kills,
        combo = Combo,
        score= Score,
        vip = Vip,
        sex = Sex,
        figure = Figure
    }=RoleInfo,
    #figure{
        career = Carrer,
        turn = Turn,
        vip_type = VType,
        vip = Vip,
        sup_vip = SVip,
        lv = Lv
    } = Figure,
    util:fmt_bin(<<"(~w,~w,~w,~w,'~ts',~w,~w,~w,~w,~w,~w,~w,~w,~w,~w,~w)">>, [Rank,Zone,Rid,SerNum,Name,Floor,Kills,Combo,Score,Vip,Sex,Carrer,Turn,VType,SVip,Lv]).



%%--------------------------------------------------
%% 添加虚空秘境日志
%% @param  RoleId 玩家id
%% @param  Type   1: 进入场景 2: 主动退出场景 3: 通关退出场景 4: 活动结束被踢出场景
%% @param  Floor  当时的楼层数
%% @param  Score  当时的积分
%% @returns        description
%%--------------------------------------------------
add_log(RoleId,RName,Type, Floor, Score) ->
    case czone_api:env_get_zone() of
        #czone_svr_local{zone_id = Zone,stage = Stage}->
            skip;
        _->
            Zone = 0,Stage=0
    end,
    lib_log_api:log_void_fam(RoleId, RName, Zone, Stage, Type, Floor, Score),
    ok.

broadcast_act_info(OpenLv, ActStatus, Etime) ->
    {ok, BinData} = pt_600:write(60001, [ActStatus, Etime, 0]),
    lib_server_send:send_to_all(all_lv, {OpenLv, 99999999}, BinData).

calc_click()->
    {_,Num0,_}=data_void_fam:get_cfg(low_drop),
    {_,Num2,_}=data_void_fam:get_cfg(high_drop),
    Num0+Num2.

drops_reduce_event(Pool,State)->
    #status_void_fam{
        cls_type = ClsType,
        drop_ref=[_Inter,_,DropT],
        click = Click
    } = State,
    NowTime = utime:unixtime(),
    NClick = max(Click-1,0),
    {ok,Bin} = pt_600:write(60011,[1,max(DropT-NowTime,0)]),
    case ClsType of
        ?ACT_TYPE_BF ->
            #void_fam_floor_cfg{scene = Scene}=data_void_fam:get_floor_cfg(9),
            lib_server_send:send_to_scene(Scene,Pool,Bin);
        ?ACT_TYPE_KF ->
            #void_fam_floor_cfg{kf_scene = Scene}=data_void_fam:get_floor_cfg(9),
            lib_server_send:send_to_scene(Scene,Pool,Bin);
        _ ->
            skip
    end,
    State#status_void_fam{click=NClick}.


send_to_scene(Pool,Scene,Bin)->
    lib_server_send:send_to_scene(Scene,Pool,Bin).


%%生成掉落
create_drops(Scene,Bin)->
    scene_mgr:apply_cast(Scene,[?MODULE,scene_clear_mon,[Scene,self(),Bin]]).

scene_clear_mon(Pool,Scene,MgrPid,Bin)->
    LowMons=data_void_fam:get_cfg(low_drop),
    HighMons=data_void_fam:get_cfg(high_drop),
    mod_scene_agent:apply_cast(Scene, Pool, ?MODULE, scene_clear_mon, []),
    create_drops(Scene,Pool,MgrPid,[LowMons,HighMons]),
    Bin /= false andalso lib_server_send:send_to_scene(Scene, Pool, Bin).

create_drops(_Scene,_,_,[])->
    ignore;
create_drops(Scene,PoolID,MgrPid,[{Mid,Num,Dots}|T])->
    {NewDots,_} = ulists:sublist(Dots,Num),
    Args = [{create_key, {void_fam_drop, Scene,MgrPid}}],
    #mon{type=Type}=data_mon:get(Mid),
    [lib_mon:async_create_mon(Mid, Scene, PoolID, X, Y, Type, 0, 1, Args)||{X,Y}<-NewDots],
    create_drops(Scene,PoolID,MgrPid,T).

scene_clear_mon() ->
    lib_scene_object_agent:clear_scene_object(?BATTLE_SIGN_MON, 0, 1).

%% 本服执行
trigger_sp_task(RoleId, Module, SubModule) ->
    case lib_daily:get_count_offline(RoleId, Module, SubModule) of
        0 ->
            lib_daily:increment_offline(RoleId, Module, SubModule);
        _ ->
            skip
    end.



check({check_already_in,Scene})->
    case lib_void_fam:is_in_void_fam(Scene) of
        true->
            {false,?ERRCODE(err600_already_in_scene)};
        _->
            true
    end;
check({check_act_open})->
    case mod_void_fam_local:is_act_open() of
        true->
            true;
        _->
            {false,?ERRCODE(err600_act_close)}
    end;
check({check_cfg_lv,Lv})->
    NeedLv = lib_module:get_open_lv(?MOD_VOID_FAM),
    case Lv>=NeedLv of
        true->
            true;
        _->
            {false,?ERRCODE(err600_no_enough_lv)}
    end;
check({check_transferable,Status})->
    case lib_scene:is_transferable(Status) of
        {true,_ErrCode}->
            true;
        {false,ErrCode}->
            {false,ErrCode}
    end.

clean_mon(0)->
    #void_fam_floor_cfg{scene=Scene,kf_scene = _KfScene}=data_void_fam:get_floor_cfg(9),
    scene_mgr:apply_cast(Scene,[?MODULE,clean_mon,[Scene]]);
clean_mon(_)->
    #void_fam_floor_cfg{scene=_Scene,kf_scene = KfScene}=data_void_fam:get_floor_cfg(9),
    scene_mgr:apply_cast(KfScene,[?MODULE,clean_mon,[KfScene]]).

clean_mon(Pool,Scene)->
    {Mid0,_,_} = data_void_fam:get_cfg(low_drop),
    {Mid2,_,_} = data_void_fam:get_cfg(high_drop),
    lib_mon:clear_scene_mon_by_mids(Scene, Pool, 0, 1, [Mid0,Mid2]).


be_collected(CollectorId,Minfo)->
    #scene_object{
        scene_pool_id = Pool,
        mon=#scene_mon{mid=Mid,create_key=CreateKey}
    } = Minfo,
    case CreateKey of
        {void_fam_drop, SceneID, FromPid}->
            FromPid ! {handle_event_collected,CollectorId,Mid,SceneID,Pool};
        _->
            skip
    end,
    ok.

%%修改PK
change_pk_state(RoleId)->
    lib_player:apply_cast(RoleId,?APPLY_CAST_STATUS,?NOT_HAND_OFFLINE,lib_player,change_pkstatus,[?PK_PEACE,true]),
    ok.

%%广播采集传闻
brocast_success_collect(CollectorId,Mid)->
    case data_void_fam:get_cfg(high_drop) of
        {Mid,_,_}->
            RoleMap = get_role_map(),
            case maps:get(CollectorId, RoleMap, false) of
                #role_info{
                    name  = Name,
                    scene = Scene,
                    zone = Zone
                } when Scene > 0->
                    Bin = lib_chat:make_tv(?MOD_VOID_FAM, 15, [Name, CollectorId]),
                    AllRoleInfo = maps:values(RoleMap),
                    F = fun(T) ->
                        case T of
                            #role_info{node = TmpNode,zone=TmpZone, role_id = TmpRoleId, scene = TmpScene} when TmpScene > 0 ->
                                Zone =:= TmpZone andalso send_to_uid(TmpNode, TmpRoleId, Bin);
                            _ ->
                                skip
                        end
                    end,
                    spawn(fun() -> lists:foreach(F, AllRoleInfo) end);
                _ ->
                    skip
            end;
        _->
            skip
    end.

ta_void_fam(Player, MaxFloor, Score, DieNum, StayTime) ->
    ta_agent_fire:void_fam(Player, [MaxFloor, Score, DieNum, StayTime]),
    {ok, Player}.