%%%-----------------------------------
%%% @Module  : mod_kf_1vn
%%% @Email   : ming_up@foxmail.com
%%% @Created : 2018.1.24
%%% @Description: 跨服1vN数据进程(跨服中心)
%%%-----------------------------------
-module(mod_kf_1vn).

-include("common.hrl").
-include("kf_1vn.hrl").
-include("errcode.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("goods.hrl").
-include("def_module.hrl").
-include("attr.hrl").
-include("def_fun.hrl").
-include("def_act_label.hrl").
-include("def_goods.hrl").
-compile(export_all).

%% API
-export([start_link/0, sign/7, enter/2, stage_change/4, quit/4, get_banner/2,
    update_role/3, exit_pk/2, get_bet_list/2, bet_battle/7,
    race_2_pk_result/5, get_race_2_rank_top/2, watch/4, update_sign_info/2
    , get_sign_list/3, gm_end/0, send_to_area/2, create_race_2_hard_list/3
    , sync_state/1, enter_pre_scene/5
]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%% 同步数据到节点
sync_state(Node) -> gen_server:cast(?MODULE, {sync_state, Node}).
%% 报名
sign(Node, ServerId, Id, Lv, Name, CombatPower, ServerName) ->
    gen_server:cast(?MODULE, {sign, Node, ServerId, Id, Lv, Name, CombatPower, ServerName}).
%% 更新报名玩家的信息
update_sign_info(Id, Args) -> gen_server:cast(?MODULE, {update_sign_info, Id, Args}).
%% 获取活动阶段数据
get_stage_info(Node, Id) -> gen_server:cast(?MODULE, {get_stage_info, Node, Id}).
%% 进入场景
enter(Node, KfRole) -> gen_server:cast(?MODULE, {enter, Node, KfRole}).
%% 阶段改变
stage_change(Stage, OpTime, EdTime, AcId) -> gen_server:cast(?MODULE, {stage_change, Stage, OpTime, EdTime, AcId}).
%% 退出场景
quit(Node, Id, CopyId, QuitType) -> gen_server:cast(?MODULE, {quit, Node, Id, CopyId, QuitType}).
%% 获取侧栏数据
get_banner(Node, Id) -> gen_server:cast(?MODULE, {get_banner, Node, Id}).
%% 更新玩家数据
update_role(Area, Id, KeyList) -> gen_server:cast(?MODULE, {update_role, Area, Id, KeyList}).
%% 玩家退出pk处理
exit_pk(Area, Id) -> gen_server:cast(?MODULE, {exit_pk, Area, Id}).
%% 挑战赛结果同步
race_2_pk_result(BattleId, DefIsWin, Area, Id, CombatPower) ->
    gen_server:cast(?MODULE, {race_2_pk_result, BattleId, DefIsWin, Area, Id, CombatPower}).

get_bet_list(Node, Id) -> gen_server:cast(?MODULE, {get_bet_list, Node, Id}).

get_sign_list(Node, Id, MyPower) -> gen_server:cast(?MODULE, {get_sign_list, Node, Id, MyPower}).

bet_battle(Node, ServerId, Id, RoleName, BattleId, IsWin, BetType) ->
    gen_server:cast(?MODULE, {bet_battle, Node, ServerId, Id, RoleName, BattleId, IsWin, BetType}).

get_race_2_rank_top(Node, Id) -> gen_server:cast(?MODULE, {get_race_2_rank_top, Node, Id}).

watch(Node, ServerId, Id, BattleId) -> gen_server:cast(?MODULE, {watch, Node, ServerId, Id, BattleId}).

gm_end() -> gen_server:cast(?MODULE, {gm_end}).

send_to_area(Area, BinData) -> gen_server:cast(?MODULE, {send_to_area, Area, BinData}).
%% 从战斗场景中退出到备战场景
enter_pre_scene(Node, Id, RacePreScene, NewScenePoolId, Args) -> gen_server:cast(?MODULE, {enter_pre_scene, Node, Id, RacePreScene, NewScenePoolId, Args}).

init([]) ->
    Race2M = lib_kf_1vn:db_get_def_rank(),
    {ok, #act_state{stage = ?KF_1VN_FREE, race_2_m = Race2M}}.

handle_call(Request, _From, State) ->
    case catch do_handle_call(Request, State) of
        {ok, Reply} ->
            {reply, Reply, State};
        Err ->
            ?ERR("Handle request[~p] error:~p~n", [Request, Err]),
            {noreply, State}
    end.

do_handle_call(_Request, _State) ->
    ?ERR("Handle unkown request[~p]~n", [_Request]),
    {ok, ok}.


handle_cast(Msg, State) ->
    case catch do_handle_cast(Msg, State) of
        {noreply, NewState} ->
            {noreply, NewState};
        Err ->
            ?ERR("Handle msg[~p] error:~p~n", [Msg, Err]),
            {noreply, State}
    end.

%% 同步活动数据到本地节点 Node：all 所有节点
do_handle_cast({sync_state, Node}, State) ->
    #act_state{stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sign_num = SignNum, def_num_m = DefNumM, race_2_m = Race2M} = State,
    F = fun(Area, Race2, TmpDefRankM) ->
        TmpDefRankM#{Area => Race2#kf_1vn_race_2.def_rank}
    end,
    DefRankM = maps:fold(F, #{}, Race2M),
    Info = [Stage, SubStage, OpTime, EdTime, SignNum, DefNumM, DefRankM],
    case Node of
        all -> clusters_center:apply_to_all_node(mod_kf_1vn_local, sync_state, [Info]);
        _   -> clusters_center:apply_cast(Node, mod_kf_1vn_local, sync_state, [Info])
    end,
    {noreply, State};

%% 报名
do_handle_cast({sign, Node, ServerId, Id, Lv, Name, CombatPower, ServerName}, State) when State#act_state.stage == ?KF_1VN_SIGN ->
    #act_state{sign_num = SignNum, sign_list = SignList} = State,
    %% 报名人数+1 同时修改或新增报名数据
    Result = case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
                 false ->
                     {true, SignNum + 1, [#kf_1vn_role_sign{server_id = ServerId, id = Id, lv = Lv, combat_power = CombatPower, name = Name, server_name = ServerName} | SignList]};
                 KfRole when is_record(KfRole, kf_1vn_role_sign) ->
                     {true, SignNum, lists:keyreplace(Id, #kf_1vn_role_sign.id, SignList, KfRole#kf_1vn_role_sign{server_id = ServerId, id = Id, lv = Lv, combat_power = CombatPower})};
                 _ -> false
             end,
    case Result of
        false -> %% 报名失败
            {ok, BinData} = pt_621:write(62102, [?ERRCODE(err621_is_signed)]),
            clusters_api:send_to_uid(Node, Id, BinData),
            {noreply, State};
        {true, SignNum1, SignList1} -> %% 报名成功，通知玩家，同步报名信息到各个节点
            {ok, BinData} = pt_621:write(62102, [?SUCCESS]),
            clusters_api:send_to_uid(Node, Id, BinData),
            clusters_center:apply_cast(Node, mod_kf_1vn_local, sign_back, [Id]),
            clusters_center:apply_to_all_node(mod_kf_1vn_local, set_sign_num, [SignNum1]),
            %% 存数据库，写日志
            lib_kf_1vn:db_save_sign_center(ServerId, Id, Lv, CombatPower, Name, ServerName),
            lib_log_api:log_kf_1vn_sign(Id, ServerId, Lv, Name),
            {noreply, State#act_state{sign_num = SignNum1, sign_list = SignList1}}
    end;

%% 更新玩家的报名信息
do_handle_cast({update_sign_info, Id, Kvs}, State) when State#act_state.stage == ?KF_1VN_SIGN ->
    #act_state{sign_list = SignList} = State,
    case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        false ->
            {noreply, State};
        Role ->
            F = fun
                    ({lv, Lv}, R)       -> R#kf_1vn_role_sign{lv = Lv};
                    ({power, Power}, R) -> R#kf_1vn_role_sign{combat_power = Power};
                    ({name, Name}, R)   -> R#kf_1vn_role_sign{name = Name};
                    (_, R)              -> R
                end,
            R2 = lists:foldl(F, Role, Kvs),
            SignList1 = lists:keystore(Id, #kf_1vn_role_sign.id, SignList, R2),
            {noreply, State#act_state{sign_list = SignList1}}
    end;

%% 发送报名列表
do_handle_cast({get_sign_list, _Node, _RoleId, _MyPower}, State) ->
    % #act_state{sorted_sign_list = SortedSignList, sign_list = SignList, sign_num = SignNum} = State,
    % case SortedSignList of
    %     undefined ->
    %         NewSortList = init_sorted_sign_list(SignList),
    %         {ok, BinData} = pt_621:write(62130, [SignNum, NewSortList, MyPower]),
    %         lib_server_send:send_to_uid(Node, RoleId, BinData),
    %         {noreply, State#act_state{sorted_sign_list = NewSortList}};
    %     _ ->
    %         {ok, BinData} = pt_621:write(62130, [SignNum, SortedSignList, MyPower]),
    %         lib_server_send:send_to_uid(Node, RoleId, BinData),
    %         {noreply, State}
    % end;
    {noreply, State};

%% 获取比赛阶段信息
do_handle_cast({get_stage_info, Node, Id}, State) ->
    #act_state{stage = Stage, sub_stage = SubStage, edtime = EdTime, sub_edtime = SubEdTime, match_turn = MatchTurn} = State,
    {ok, BinData} = pt_621:write(62101, [Stage, MatchTurn, EdTime, SubStage, SubEdTime]),
    clusters_api:send_to_uid(Node, Id, BinData),
    {noreply, State};

%% 进入活动
do_handle_cast({enter, Node, KfRole}, State) when State#act_state.stage >= ?KF_1VN_RACE_1_PRE ->
    #act_state{sign_list = SignList, role_m = RoleM, max_scene_pool = MaxScenePool, stage = Stage, sub_stage = SubStage, race_2_m = Race2M} = State,
    #kf_1vn_role{id = Id, figure = Figure, combat_power = CombatPower} = KfRole,
    Result = case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        false -> {false, ?ERRCODE(err621_no_sign)}; %% 未报名
        #kf_1vn_role_sign{area = Area, race_1_seed = Race1Seed} -> %% 已报名
            %% 获取备战场景和出生点
            RacePreScene = ?IF(Stage >= ?KF_1VN_RACE_1_PRE andalso Stage < ?KF_1VN_RACE_2_PRE, data_kf_1vn_m:get_config(race_1_pre_scene), data_kf_1vn_m:get_config(race_2_pre_scene)),
            {PreX, PreY} = lib_kf_1vn:pre_scene_xy(),
            case maps:get(Area, RoleM, []) of
                [] when Stage >= ?KF_1VN_RACE_2_PRE -> {false, ?ERRCODE(err621_not_in_race_2)}; %% 守擂赛准备时没有玩家进入过活动，该赛区关闭
                Roles ->
                    case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
                        false -> %% 第一次进入
                            ScenePoolId = urand:rand(0, MaxScenePool),
                            clusters_center:apply_cast(Node, mod_hi_point, success_end, [Id, ?MOD_KF_1VN, 0]), % 嗨点活动
                            clusters_center:apply_cast(Node, lib_scene, change_player_scene, [Id, RacePreScene, ScenePoolId, 0, PreX, PreY, [{action_lock, ?ERRCODE(err621_in_kf_1vn)}, {change_scene_hp_lim, 1}]]),
                            Roles1 = [KfRole#kf_1vn_role{scene_pool_id = ScenePoolId, area = Area, race_1_seed = Race1Seed, enter = RacePreScene} | Roles],
                            {true, Area, RoleM#{Area => Roles1}, Race2M};
                        #kf_1vn_role{race_2_side = Race2Side, race_2_lose = Race2Lose, race_2_battle_id = Race2BattleId} = RoleO ->
                            RoleN = RoleO#kf_1vn_role{figure = Figure, combat_power = CombatPower, enter = RacePreScene},
                            Args = [{action_lock, ?ERRCODE(err621_in_kf_1vn)}, {change_scene_hp_lim, 1}],
                            Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, RoleN),
                            NewArgs = ?IF(Race2Side == ?SIDE_DEF andalso Race2Lose == 0, [{act_label, [{?ACT_LABEL_KF_1VN_SIDE, 1}]} | Args], Args),
                            clusters_center:apply_cast(Node, lib_scene, change_player_scene, [Id, RacePreScene, RoleO#kf_1vn_role.scene_pool_id, 0, PreX, PreY, NewArgs]),
                            if %% 当擂主进入活动场景时
                                Race2Side == ?SIDE_DEF andalso Race2Lose == 0 andalso Stage >= ?KF_1VN_RACE_2_PRE andalso Race2BattleId > 0 andalso SubStage /= ?KF_1VN_STATE_FIGHT ->
                                    Race2 = maps:get(Area, Race2M),
                                    #kf_1vn_race_2{match_list = MatchList} = Race2,
                                    MatchList1 = case lists:keyfind(Race2BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
                                        #kf_1vn_race_2_match_info{def_role = DefRole} = MatchInfo ->
                                            lists:keyreplace(Race2BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList, MatchInfo#kf_1vn_race_2_match_info{def_role = DefRole#kf_1vn_role{enter = RacePreScene}});
                                        _ -> MatchList
                                    end,
                                    {true, Area, RoleM#{Area => Roles1}, Race2M#{Area:=Race2#kf_1vn_race_2{match_list = MatchList1}}};
                                true ->
                                    {true, Area, RoleM#{Area => Roles1}, Race2M}
                            end
                    end
            end
    end,
    case Result of
        {false, ErrCode} ->
            {ok, BinData} = pt_621:write(62103, [ErrCode]),
            clusters_api:send_to_uid(Node, KfRole#kf_1vn_role.id, BinData),
            {noreply, State};
        {true, Area1, RoleM1, NewRace2M} -> %% 擂主进场
            {ok, BinData} = pt_621:write(62103, [?SUCCESS]),
            clusters_api:send_to_uid(Node, KfRole#kf_1vn_role.id, BinData),
            State1 = State#act_state{role_m = RoleM1, race_2_m = NewRace2M},
            send_banner(Area1, Id, State1),
            get_stage_info(Node, Id),
            {noreply, State1}
    end;

%% 从战斗场景中退出到备战场景
do_handle_cast({enter_pre_scene, Node, Id, RacePreScene, NewScenePoolId, Args}, State) ->
    {PreX, PreY} = lib_kf_1vn:pre_scene_xy(),
    clusters_center:apply_cast(Node, lib_scene, change_player_scene, [Id, RacePreScene, NewScenePoolId, 0, PreX, PreY, Args]),
    case lists:keyfind(act_label, 1, Args) of
        {act_label, ActLabel} -> lib_kf_1vn:update_player_act_label(Node, Id, ActLabel);
        _ -> ok
    end,
    {noreply, State};

%% 阶段状态改变 (mod_kf_1vn_mgr -> mod_kf_1vn -> mod_kf_1vn_local)
do_handle_cast({stage_change, Stage, OpTime, EdTime, AcId}, State) ->
    util:cancel_timer(State#act_state.match_ref),
    State1 = case Stage of
                ?KF_1VN_SIGN -> %% 报名阶段
                    SubStage = ?KF_1VN_STATE_WAIT,
                    SubEdTime = EdTime,
                    lib_kf_1vn:clean_def_rank(), %% 清除之前的排行榜
                    SignList = lib_kf_1vn:get_sign_list_center(), %% 获取报名列表
                    SignNum = length(SignList), %% 报名人数
                    State#act_state{ac_id = AcId, stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sub_edtime = SubEdTime, race_2_m = #{}, sign_list = SignList, sign_num = SignNum};
                ?KF_1VN_RACE_1_PRE ->  %% 资格赛准备
                    #act_state{sign_list = SignList, sign_num = SignNum} = State,
                    case SignNum < data_kf_1vn:get_value(?KF_1VN_CFG_ACT_MIN_NUM) of
                        true ->  %% 报名玩家少于一定数量则不开启, 给报名玩家发不开启通知
                            self() ! ac_stop,
                            Title = utext:get(6210015),
                            Content = utext:get(6210016),
                            [lib_kf_1vn:send_mail(E#kf_1vn_role_sign.server_id, [E#kf_1vn_role_sign.id], Title, Content, []) || E <- SignList],
                            SubStage = ?KF_1VN_STATE_WAIT,
                            SubEdTime = EdTime,
                            State#act_state{stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sub_edtime = SubEdTime};
                        _ ->
                            {SignListNew, AreaList} = cut_area(SignList),  %% 报名玩家按照等级分配赛区
                            {DefNumM, SeedNumM} = calc_def_num(AreaList, SignListNew, #{}, #{}), %% 获取擂主数量和种子选手数量（配置）
                            clusters_center:apply_to_all_node(mod_kf_1vn_local, change_def_num_m, [DefNumM], 10), %% 擂主数量同步到本地节点
                            MaxScenePool = min(10, State#act_state.sign_num div 100), %% 按照报名人数判断备战场景房间数量
                            change_local_area(SignListNew, #{}),  %% 将分赛区数据更新到本地节点
                            SetSeedSignList = set_seed_role_sign(AreaList, SignListNew, SeedNumM), %% 选出一些种子选手（战力排序）
                            ExpRef = erlang:send_after(?KF_1VN_EXP_ADD_TIME, self(), add_exp),  %% 定时加经验
                            SubStage = ?KF_1VN_STATE_WAIT,
                            SubEdTime = EdTime,
                            SortedSignList = init_sorted_sign_list(SignList),  %% 第一次根据战力排序
                            MaxPower = case SortedSignList of %% 获取首位玩家战力
                                [{_, _, _, Power} | _] -> Power;
                                _ -> 10000000
                            end,
                            State#act_state{stage = Stage, sub_stage = SubStage, max_scene_pool = MaxScenePool, optime = OpTime, edtime = EdTime,
                                            sub_edtime = SubEdTime, sign_list = SetSeedSignList, area_list = AreaList, exp_ref = ExpRef,
                                            def_num_m = DefNumM, seed_num_m = SeedNumM, max_power = MaxPower}
                    end;
                ?KF_1VN_RACE_1 ->  %% 资格赛
                    #act_state{def_num_m = DefNumM, role_m = RoleM} = State,
                    NowTime = utime:unixtime(),
                    #kf_1vn_time_cfg{race_1_m_time = MatchTime, race_1_b_time = BattleTime} = data_kf_1vn:get_info(AcId),
                    Race1MaxTimes = data_kf_1vn_m:get_config(race_1_max_times),
                    F2 = fun(#kf_1vn_role{id= Id, server_id = SerId, exp = Exp, enter = Enter}, DefNum) ->
                        if
                            Enter =/= 0 ->
                                {ok, BinData} = pt_621:write(62104, [Race1MaxTimes, 0, NowTime + MatchTime, 0, 0, Exp, DefNum]),
                                clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]),
                                DefNum;
                            true -> DefNum
                        end
                    end,
                    F1 = fun(Area, Roles) ->
                        DefNum = maps:get(Area, DefNumM, 0),
                        lists:foldl(F2, DefNum, Roles)
                    end,
                    maps:map(F1, RoleM),
                    %% 开始匹配
                    MatchRef = erlang:send_after(MatchTime * 1000, self(), match),
                    SubStage = ?KF_1VN_STATE_MATCH,
                    SubEdTime = OpTime + MatchTime,
                    State#act_state{stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sub_edtime = SubEdTime, match_time = MatchTime, battle_time = BattleTime, match_turn = 1, match_ref = MatchRef};
                ?KF_1VN_RACE_2_PRE -> %% 挑战赛准备阶段，筛选擂主和挑战者
                    #act_state{role_m = RoleM, def_num_m = DefNumM, area_list = LeftAreaList} = State,
                    %% 没人玩则直接结束
                    ?IF(RoleM == #{}, self() ! {ac_end, all}, skip),
                    F = fun(Area, ORoles, [TmpRace2M, TmpRolesM]) ->
                        Roles = sort_roles_by_score(ORoles),
                        RolesUp = [E1 || E1 <- Roles, E1#kf_1vn_role.score > 0],
                        RolesUpLen = length(RolesUp),               %% 还在活动中的人数
                        DefNum = maps:get(Area, DefNumM),           %% 擂主人数
                        SelectDefNum = min(DefNum, RolesUpLen),     %% 最终成为擂主的人数
                        send_race_1_reward(Roles, SelectDefNum, 1), %% 发送资格赛奖励
                        RolesOut = [begin
                                        lib_kf_1vn:quit_to_main_scene(E2#kf_1vn_role.server_id, E2#kf_1vn_role.id),
                                        E2#kf_1vn_role{out = 1}
                                    end || E2 <- Roles, E2#kf_1vn_role.score =< 0], %% 分数小于等于0的都送出活动场景
                        F2 = fun(#kf_1vn_role{server_id = SerId, id = TmpId, combat_power = CombatPower, attr = Attr} = Role, [TmpDefNum, DefList, ChallengerRoles, TmpDefIds, TmpChallengerIds]) ->
                            case TmpDefNum > SelectDefNum of
                                true -> %% 其余人为挑战者
                                    lib_kf_1vn:update_player_1vn(SerId, Role#kf_1vn_role.id, 2, 0, Area),
                                    [TmpDefNum, DefList, [Role#kf_1vn_role{race_2_side = ?SIDE_CHALLENGER, win = 0} | ChallengerRoles], TmpDefIds, [{TmpId, CombatPower} | TmpChallengerIds]];
                                false -> %% 前SelectDefNum人为擂主
                                    lib_kf_1vn:update_player_act_label(SerId, TmpId, [{?ACT_LABEL_KF_1VN_SIDE, 1}]),
                                    lib_kf_1vn:update_player_1vn(SerId, TmpId, 1, 0, Area),

                                    [TmpDefNum + 1, [Role#kf_1vn_role{race_2_side = ?SIDE_DEF, win = 0} | DefList], ChallengerRoles, [{TmpId, Attr} | TmpDefIds], TmpChallengerIds]
                            end
                        end,
                        [_TmpDefNum, DefRoles, ChallengerRoles, DefIds, ChallengerIds] = lists:foldl(F2, [1, [], [], [], []], RolesUp),
                        NewRoles = RolesOut ++ DefRoles ++ ChallengerRoles,
                        %% 发送传闻
                        ChatId = ?IF(Area == 1, 3, 4),
                        BinData = lib_chat:make_tv(?MOD_KF_1VN, ChatId, [SelectDefNum]),
                        lib_kf_1vn:send_all_enter(NewRoles, BinData),
                        {DefRank, ChallenageRank} = sort_race_2_rank(NewRoles, [], []),
                        ShortDefRank = lists:sublist(DefRank, 300),
                        ShortChallenageRank = lists:sublist(ChallenageRank, 300),
                        lib_kf_1vn:update_race_2_rank(Area, ShortDefRank, ShortChallenageRank), %% 同步排行榜到本地节点
                        %% 没有擂主则该赛区比赛停止
                        ?IF(DefIds == [], self() ! {ac_end, Area}, skip),
                        %% 初始化玩家类型
                        [TmpRace2M#{Area => #kf_1vn_race_2{def_list = DefIds, challenger_list = ChallengerIds, def_rank = ShortDefRank, challenage_rank = ShortChallenageRank}}, TmpRolesM#{Area=>NewRoles}]
                        end,
                    [Race2MN, RoleMN] = maps:fold(F, [#{}, RoleM], RoleM),
                    %% 赛区没有玩家则该赛区比赛停止
                    LeftArea = maps:keys(RoleMN),
                    NoPlayerArea = lists:subtract(LeftAreaList, LeftArea),
                    [self() ! {ac_end, TmpArea} || TmpArea <- NoPlayerArea],
                    SubStage = ?KF_1VN_STATE_WAIT,
                    SubEdTime = EdTime,
                    NewState = State#act_state{stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sub_edtime = SubEdTime, match_time = 0, battle_time = 0, match_turn = 1, match_ref = undefined, role_m = RoleMN, race_2_m = Race2MN},
                    send_race_2_banner(NewState),
                    NewState;
                ?KF_1VN_RACE_2 ->
                    #kf_1vn_time_cfg{race_2_m_time = MatchTime, race_2_b_time = BattleTime} = data_kf_1vn:get_info(AcId),
                    MatchRef = erlang:send_after(MatchTime * 1000, self(), race_2_match),
                    SubStage = ?KF_1VN_STATE_MATCH,
                    SubEdTime = OpTime + MatchTime,
                    State#act_state{stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sub_edtime = SubEdTime, match_time = MatchTime, battle_time = BattleTime, match_turn = 0, match_ref = MatchRef};
                ?KF_1VN_FREE ->
                    SubStage = ?KF_1VN_STATE_END,
                    SubEdTime = EdTime,
                    State#act_state{stage = Stage, sub_stage = SubStage, optime = OpTime, edtime = EdTime, sub_edtime = SubEdTime, match_time = 0, battle_time = 0, match_turn = 0};
                _ ->
                    SubStage = State#act_state.sub_stage,
                    SubEdTime = EdTime,
                    State#act_state{sub_edtime = SubEdTime}
             end,
    %% 通知本地节点状态改变，同步数据
    clusters_center:apply_to_all_node(mod_kf_1vn_local, stage_change, [Stage, 1, EdTime, SubStage, SubEdTime]),
    gen_server:cast(?MODULE, {sync_state, all}),
    {noreply, State1};

%% 获取等待场景banner
do_handle_cast({get_banner, _Node, Id}, State) ->
    #act_state{sign_list = SignList} = State,
    case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        #kf_1vn_role_sign{area = Area} -> send_banner(Area, Id, State);
        _ -> skip
    end,
    {noreply, State};

%% 离开场景
do_handle_cast({quit, Node, Id, CopyId, QuitType}, State) ->
    #act_state{
        sign_list = SignList,
        stage = Stage,
        sub_stage = SubStage,
        role_m = RoleM,
        race_2_m = Race2M,
        match_turn = MatchTurn
    } = State,
    case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        false ->
            lib_kf_1vn:quit_to_main_scene(Node, Id),
            {noreply, State};
        #kf_1vn_role_sign{area = Area} ->
            case maps:get(Area, RoleM, false) of
                false ->
                    lib_kf_1vn:quit_to_main_scene(Node, Id),
                    {noreply, State};
                Roles ->
                    case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
                        #kf_1vn_role{
                            scene_pool_id    = ScenePoolId,
                            race_2_side      = Race2Side,
                            race_2_battle_id = Race2BattleId,
                            watch_battle_id  = WatchBattleId,
                            race_2_lose      = Race2Lose,
                            enter            = Et
                        } = Role when Et /= 0 ->
                            if
                                Race2Side == ?SIDE_DEF andalso Race2Lose == 0 andalso Stage == ?KF_1VN_RACE_2 andalso Race2BattleId > 0 andalso SubStage /= ?KF_1VN_STATE_FIGHT ->
                                    lib_kf_1vn:quit_to_main_scene(Node, Id), %% 擂主在比赛开始前离开场景，对匹配数据做记录；比赛开始时擂主未回到场景则判断擂主失败
                                    Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, Role#kf_1vn_role{enter = 0}),
                                    Race2 = maps:get(Area, Race2M),
                                    #kf_1vn_race_2{match_list = MatchList} = Race2,
                                    MatchList1
                                        = case lists:keyfind(Race2BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
                                              #kf_1vn_race_2_match_info{def_role = DefRole} = MatchInfo ->
                                                  lists:keyreplace(Race2BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList, MatchInfo#kf_1vn_race_2_match_info{def_role = DefRole#kf_1vn_role{enter = 0}});
                                              _ -> MatchList
                                        end,
                                    NewState = State#act_state{
                                        role_m   = RoleM#{Area:=Roles1},
                                        race_2_m = Race2M#{Area:=Race2#kf_1vn_race_2{match_list = MatchList1}}
                                    },
                                    {noreply, NewState};
                                (Race2Side == ?SIDE_CHALLENGER orelse Race2Lose == 1) andalso Stage == ?KF_1VN_RACE_2 andalso Race2BattleId > 0 andalso SubStage /= ?KF_1VN_STATE_FIGHT ->
                                    %% 挑战者在竞猜时间离开，则判定输
                                    Race2 = maps:get(Area, Race2M),
                                    #kf_1vn_race_2{match_list = MatchList} = Race2,
                                    QuitRole = Role#kf_1vn_role{enter = 0},
                                    Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, QuitRole),
                                    lib_kf_1vn:quit_to_main_scene(Node, Id),
                                    {MatchList1, LastRoles}
                                        = case lists:keyfind(Race2BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
                                              #kf_1vn_race_2_match_info{
                                                  def_role = #kf_1vn_role{
                                                      id           = DefId,
                                                      server_id    = DefSerId,
                                                      server_num   = DefSerNum,
                                                      figure       = #figure{name = DefName},
                                                      combat_power = DefCombatPower
                                                  },
                                                  challengers = Challengers,
                                                  battle_win  = 0
                                              } = MatchInfo ->
                                                  ChallengersLen = length([1 || E <- Challengers, E#kf_1vn_role_pk.is_dead == 0]),
                                                  {Challengers1, TmpRoles, BattleWin}
                                                      = case lists:keyfind(Id, #kf_1vn_role_pk.id, Challengers) of
                                                            #kf_1vn_role_pk{is_dead = 0} = KfRolePk when ChallengersLen == 1 ->
                                                                %% 最后一个挑战者离开，擂主直接胜利
                                                                Roles2 = case lists:keyfind(DefId, #kf_1vn_role.id, Roles1) of
                                                                     false -> Roles1;
                                                                     DefRole ->
                                                                         lists:keyreplace(
                                                                             DefId,
                                                                             #kf_1vn_role.id,
                                                                             Roles1,
                                                                             DefRole#kf_1vn_role{race_2_turn = MatchTurn}
                                                                         )
                                                                 end,
                                                                %% 单场比赛结束
                                                                race_2_pk_result(Race2BattleId, 1, Area, DefId, DefCombatPower),
                                                                case data_kf_1vn:get_race_2_award(1, MatchTurn) of %% 守擂
                                                                    {DefWinRewards, _} -> skip;
                                                                    _ -> DefWinRewards = []
                                                                end,
                                                                Title = utext:get(6210005),
                                                                Content = utext:get(6210006, [MatchTurn]),
                                                                lib_kf_1vn:send_mail(DefSerId, [DefId], Title, Content, DefWinRewards),
                                                                lib_log_api:log_kf_1vn_race_2(Area, MatchTurn, Race2BattleId, DefId,
                                                                    DefSerId, DefSerNum, DefName, DefCombatPower, 1, 1, 4, 0),
                                                                TmpChallengers = lists:keyreplace(Id, #kf_1vn_role_pk.id, Challengers,
                                                                    KfRolePk#kf_1vn_role_pk{is_dead = 2}),
                                                                {TmpChallengers, Roles2, 1};

                                                            #kf_1vn_role_pk{is_dead = 0} = KfRolePk ->
                                                                TmpChallengers = lists:keyreplace(Id, #kf_1vn_role_pk.id, Challengers,
                                                                    KfRolePk#kf_1vn_role_pk{is_dead = 2}),
                                                                {TmpChallengers, Roles1, 0};

                                                            _ ->
                                                                {Challengers, Roles1, 0}
                                                        end,
                                                  TmpMatchList = lists:keyreplace(
                                                      Race2BattleId,
                                                      #kf_1vn_race_2_match_info.battle_id,
                                                      MatchList,
                                                      MatchInfo#kf_1vn_race_2_match_info{
                                                          challengers = Challengers1,
                                                          battle_win = BattleWin
                                                      }
                                                  ),
                                                  {TmpMatchList, TmpRoles};
                                              _ ->
                                                  {MatchList, Roles1}
                                          end,
                                    NewState = State#act_state{
                                        role_m   = RoleM#{Area:=LastRoles},
                                        race_2_m = Race2M#{Area:=Race2#kf_1vn_race_2{match_list = MatchList1}}
                                    },
                                    {noreply, NewState};
                                WatchBattleId > 0 ->
                                    Race2 = maps:get(Area, Race2M),
                                    #kf_1vn_race_2{match_list = MatchList} = Race2,
                                    Enter = ?IF(QuitType == 1, Role#kf_1vn_role.enter, 0),
                                    Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, Role#kf_1vn_role{watch_battle_id = 0, enter = Enter}),
                                    Race2PreScene = data_kf_1vn_m:get_config(race_2_pre_scene),
                                    {PreX, PreY} = lib_kf_1vn:pre_scene_xy(),
                                    clusters_center:apply_cast(Node, lib_scene, change_player_scene,
                                        [Id, Race2PreScene, ScenePoolId, 0, PreX, PreY, [{ghost, 0}, {group, 0}]]),
                                    send_banner(Area, Id, State),
                                    case lists:keyfind(WatchBattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
                                        false -> {noreply, State#act_state{role_m = RoleM#{Area:=Roles1}}};
                                        #kf_1vn_race_2_match_info{watch_num = WatchNum, watch_ids = WatchIds} = MatchInfo ->
                                            MatchInfo1 = MatchInfo#kf_1vn_race_2_match_info{
                                                watch_num = max(0, WatchNum - 1),
                                                watch_ids = lists:keydelete(Id, 2, WatchIds)
                                            },
                                            MatchList1 = lists:keyreplace(WatchBattleId, #kf_1vn_race_2_match_info.battle_id, MatchList, MatchInfo1),
                                            NewState = State#act_state{
                                                role_m   = RoleM#{Area := Roles1},
                                                race_2_m = Race2M#{Area := Race2#kf_1vn_race_2{match_list = MatchList1}}
                                            },
                                            {noreply, NewState}
                                    end;
                                true ->
                                    RoleM1 = case is_pid(CopyId) of
                                         true ->
                                             CopyId ! {quit, Node, Id, Role#kf_1vn_role.scene_pool_id, QuitType},
                                             case QuitType of
                                                 1 -> RoleM;
                                                 _ ->
                                                     Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, Role#kf_1vn_role{enter = 0}),
                                                     RoleM#{Area := Roles1}
                                             end;
                                         false ->
                                             lib_kf_1vn:quit_to_main_scene(Node, Id),
                                             Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, Role#kf_1vn_role{enter = 0}),
                                             RoleM#{Area := Roles1}
                                     end,
                                    {noreply, State#act_state{role_m = RoleM1}}
                            end;
                        _ ->
                            lib_kf_1vn:quit_to_main_scene(Node, Id),
                            {noreply, State}
                    end
            end
    end;

%% 更新玩家数据
do_handle_cast({update_role, Area, Id, KeyList}, State) ->
    #act_state{role_m = RoleM, race_2_m = Race2M, stage = Stage} = State,
    Roles = maps:get(Area, RoleM, []),
    case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
        false ->
            {noreply, State};
        KfRole ->
            KfRoleN = update_role_dt(KeyList, KfRole, Area),
            RolesN = lists:keyreplace(Id, #kf_1vn_role.id, Roles, KfRoleN),
            %% 判断是挑战赛且要修改战力时需要多加匹配列表的处理
            case lists:keyfind(power, 1, KeyList) of
                {power, IsUpdatePower} when Stage > ?KF_1VN_RACE_2_PRE ->
                    NewRace2M = update_race_2_combat(KfRoleN, Race2M, Area, IsUpdatePower);
                _ -> NewRace2M = Race2M
            end,
            {noreply, State#act_state{role_m = RoleM#{Area:=RolesN}, race_2_m = NewRace2M}}
    end;

%% 退出pk场景，发送banner信息
do_handle_cast({exit_pk, Area, Id}, State) ->
    send_banner(Area, Id, State),
    {noreply, State};

do_handle_cast({race_2_pk_result, BattleId, DefIsWin, Area, Id, CombatPower}, State) ->
    #act_state{race_2_m = Race2M, match_turn = MatchTurn, role_m = RoleM} = State,
    #kf_1vn_race_2{def_list = DefList, challenger_list = ChallengerList, match_list = MatchList, bet_m = BetM} = Race2 = maps:get(Area, Race2M),
    case DefIsWin of
        1 -> %% 擂主胜利
            BattleWin = 1,
            DefList1 = DefList,
            ChallengerList1 = ChallengerList;
        _ -> %% 挑战者胜利
            BattleWin = 2,
            DefList1 = lists:keydelete(Id, 1, DefList),
            ChallengerList1 = [{Id, CombatPower} | ChallengerList]
    end,

    %% 更改1vn战斗状态
    MatchList1 = case lists:keyfind(BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
        false -> MatchList;
        #kf_1vn_race_2_match_info{def_role = DefRole, watch_ids = WatchIds} = MatchInfo ->
            %% 发送传闻
            if
                DefIsWin == 1 -> %% 擂主胜利,根据轮数发送传闻
                    TvId = case MatchTurn of
                        5 -> 13;
                        6 -> 14;
                        7 -> 15;
                        _ -> skip
                    end,
                    if
                        is_integer(TvId) ->
                            BinData = lib_chat:make_tv(?MOD_KF_1VN, TvId, [DefRole#kf_1vn_role.figure#figure.name]),
                            case maps:get(Area, RoleM, false) of
                                false -> skip;
                                Roles -> lib_kf_1vn:send_all_enter(Roles, BinData)
                            end;
                        true -> skip
                    end;
                true -> skip %% 擂主失败
            end,
            Race2PreScene = data_kf_1vn_m:get_config(race_2_pre_scene),
            {PreX, PreY} = lib_kf_1vn:pre_scene_xy(),
            lists:map(fun({EServerId, EId}) ->
                          clusters_center:apply_cast(EServerId, lib_scene, change_player_scene, [EId, Race2PreScene, urand:rand(0, State#act_state.max_scene_pool), 0, PreX, PreY, [{ghost, 0}]])
                      end, WatchIds),
            lists:keyreplace(BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList, MatchInfo#kf_1vn_race_2_match_info{status = 2, battle_win = BattleWin, watch_num = 0, watch_ids = []})
    end,

    %% 发放押注奖励
    case maps:get(BattleId, BetM, false) of
        false -> skip;
        BetList ->
            Ratio = data_kf_1vn:get_ratio(MatchTurn),
            handle_bet_list(BetList, Ratio, BattleWin, BattleId, Area, MatchTurn)
    end,
    {noreply, State#act_state{race_2_m = Race2M#{Area=>Race2#kf_1vn_race_2{def_list = DefList1, challenger_list = ChallengerList1, match_list = MatchList1}}}};

%% 获取押注（匹配）列表
do_handle_cast({get_bet_list, Node, Id}, State) ->
    #act_state{sub_stage = SubStage, sign_list = SignList, race_2_m = Race2M} = State,
    case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        false -> skip;
        #kf_1vn_role_sign{area = Area, battle_id = BattleId, bet_side = BetSide} ->
            case maps:get(Area, Race2M, false) of
                false -> skip;
                #kf_1vn_race_2{match_list = MatchList} ->
                    F = fun(#kf_1vn_race_2_match_info{battle_id = TmpBattleId, status = BStatus, battle_win = BattleWin, def_role = DefRole, challengers = Challengers}, [TmpResult, TmpBetR]) ->
                        BetR = case BattleId of
                                   TmpBattleId when BattleWin > 0 andalso BattleWin == BetSide -> 2; %% 猜对
                                   TmpBattleId when BattleWin > 0 -> 1; %% 猜错
                                   _ -> TmpBetR
                               end,
                        BetStatus = case SubStage of
                                        ?KF_1VN_STATE_MATCH -> 0;
                                        ?KF_1VN_STATE_BET when BStatus == 2 -> 2;
                                        ?KF_1VN_STATE_BET -> 0;
                                        ?KF_1VN_STATE_FIGHT when BStatus == 2 -> 2;
                                        ?KF_1VN_STATE_FIGHT -> 1;
                                        _ -> 2
                                    end,
                        BattleResult = case SubStage of
                                           ?KF_1VN_STATE_FIGHT -> BattleWin;
                                           _ -> 0
                                       end,
                        ChallengersSendL = [
                            {E#kf_1vn_role_pk.id, E#kf_1vn_role_pk.server_num, E#kf_1vn_role_pk.name, E#kf_1vn_role_pk.career,
                                E#kf_1vn_role_pk.turn, E#kf_1vn_role_pk.sex,
                                E#kf_1vn_role_pk.picture, E#kf_1vn_role_pk.picture_ver, E#kf_1vn_role_pk.combat_power
                            } || E <- Challengers],
                        [[{TmpBattleId, BetStatus, DefRole#kf_1vn_role.id, DefRole#kf_1vn_role.server_num, DefRole#kf_1vn_role.figure#figure.name,
                            DefRole#kf_1vn_role.figure#figure.career, DefRole#kf_1vn_role.figure#figure.turn, DefRole#kf_1vn_role.figure#figure.sex,
                            DefRole#kf_1vn_role.figure#figure.picture, DefRole#kf_1vn_role.figure#figure.picture_ver,
                            DefRole#kf_1vn_role.combat_power, ChallengersSendL, BattleResult} | TmpResult], BetR]
                        end,
                    [BetList, BetResult] = lists:foldl(F, [[], 0], MatchList),
                    case BetList of
                        [] -> First = [];
                        _ -> First = [lists:nth(1, BetList)]
                    end,
                    {ok, BinData} = pt_621:write(62117, [First, BattleId, BetResult]),
                    clusters_center:apply_cast(Node, lib_server_send, send_to_uid, [Id, BinData])
            end
    end,
    {noreply, State};

%% 押注
do_handle_cast({bet_battle, Node, ServerId, Id, RoleName, BattleId, IsWin, BetType}, State) ->
    #act_state{sub_stage = SubStage, sign_list = SignList, race_2_m = Race2M, role_m = RoleM, match_turn = Turn} = State,
    Result = case SubStage of
         ?KF_1VN_STATE_BET ->
             case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
                 false -> {false, ?ERRCODE(err621_no_sign)};
                 #kf_1vn_role_sign{battle_id = OldBattleId} when OldBattleId > 0 ->
                     {false, ?ERRCODE(err621_has_betted)};
                 #kf_1vn_role_sign{area = Area} = RoleSign ->
                     Roles = maps:get(Area, RoleM, []),
                     case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
                         false -> {false, ?ERRCODE(err621_no_enter)};
                         _ ->
                             case maps:get(Area, Race2M, []) of
                                 #kf_1vn_race_2{match_list = MatchList, bet_m = BetM} = Race2 ->
                                     case lists:keyfind(BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
                                         false -> {false, ?ERRCODE(err621_not_battle)};
                                         #kf_1vn_race_2_match_info{status = 2} ->
                                             {false, ?ERRCODE(err621_battle_is_end)};
                                         _ ->
                                             BetList = maps:get(BattleId, BetM, []),
                                             BetSide = case IsWin of
                                                           1 -> 1;
                                                           _ -> 2
                                                       end,

                                             NewBetM = case lists:keyfind(Id, 2, BetList) of
                                                           false ->
                                                               Cost = data_kf_1vn_m:get_bet_cost(BetType),
                                                               clusters_center:apply_cast(Node, lib_player, update_player_info, [Id, [{cost_money, {Cost, ?TYPE_BGOLD, kf_1vn_bet, ""}}]]),
                                                               lib_log_api:log_kf_1vn_bet(Id, RoleName, Turn, BattleId, BetSide, Cost, utime:unixtime()),
                                                               BetM#{BattleId => [{ServerId, Id, BetSide, BetType} | BetList]};
                                                           _ -> BetM
                                                       end,
                                             {true, Race2M#{Area => Race2#kf_1vn_race_2{bet_m = NewBetM}}, lists:keyreplace(Id, #kf_1vn_role_sign.id, SignList, RoleSign#kf_1vn_role_sign{server_id = ServerId, battle_id = BattleId, bet_side = BetSide})}
                                     end;
                                 _ -> {false, ?ERRCODE(err621_no_battle)}
                             end
                     end
             end;
         _ -> {false, ?ERRCODE(err621_not_bet_stage)}
     end,
    case Result of
        {true, Race2M1, SignList1} ->
            {ok, BinData} = pt_621:write(62118, [?SUCCESS, BattleId]),
            clusters_center:apply_cast(Node, lib_server_send, send_to_uid, [Id, BinData]),
            {noreply, State#act_state{race_2_m = Race2M1, sign_list = SignList1}};
        {false, ErrCode} ->
            {ok, BinData} = pt_621:write(62118, [ErrCode, BattleId]),
            clusters_center:apply_cast(Node, lib_server_send, send_to_uid, [Id, BinData]),
            {noreply, State}
    end;

%% 获取资格赛中第一名的玩家
do_handle_cast({get_race_2_rank_top, Node, Id}, State) ->
    #act_state{race_1_rank_m = Race1RankM, sign_list = SignList} = State,
    case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        false -> skip;
        #kf_1vn_role_sign{area = Area} ->
            case maps:get(Area, Race1RankM, false) of
                [#kf_1vn_score_rank{name = TopScoreName} | _] = ScoreRank ->
                    MyRank = case lists:keyfind(Id, #kf_1vn_score_rank.id, ScoreRank) of
                        #kf_1vn_score_rank{rank = TmpRank} -> TmpRank;
                        _ -> 0
                    end;
                _ -> TopScoreName = "", MyRank = 0
            end,
            {ok, BinData} = pt_621:write(62119, [MyRank, TopScoreName]),
            clusters_center:apply_cast(Node, lib_server_send, send_to_uid, [Id, BinData])
    end,
    {noreply, State};

%% 观战
do_handle_cast({watch, Node, ServerId, Id, BattleId}, State) when State#act_state.stage == ?KF_1VN_RACE_2 ->
    #act_state{sub_stage = SubStage, sign_list = SignList, role_m = RoleM, race_2_m = Race2M} = State,
    case lists:keyfind(Id, #kf_1vn_role_sign.id, SignList) of
        false -> skip;
        #kf_1vn_role_sign{area = Area} ->
            Roles = maps:get(Area, RoleM, []),
            Result = case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
                         false -> {false, ?ERRCODE(err621_no_enter)};
                         #kf_1vn_role{race_2_battle_id = Race2BattleId} when Race2BattleId > 0 -> %% 匹配到战斗的时候不能观战
                             {false, ?ERRCODE(err621_has_battle)};
                         KfRole ->
                             #kf_1vn_race_2{match_list = MatchList} = Race2 = maps:get(Area, Race2M),
                             case lists:keyfind(BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList) of
                                 #kf_1vn_race_2_match_info{status = 2} -> {false, ?ERRCODE(err621_battle_is_end)};
                                 #kf_1vn_race_2_match_info{watch_num = WatchNum} when WatchNum >= ?KF_1VN_WATCH_MAX_NUM ->
                                     {false, ?ERRCODE(err621_watch_max_num)};
                                 #kf_1vn_race_2_match_info{scene_pool_id = ScenePoolId, copy_id = CopyId, watch_num = WatchNum, watch_ids = WatchIds} = MatchInfo when SubStage == ?KF_1VN_STATE_FIGHT ->
                                     %% 传送入场，观战人数+1
                                     SceneId = data_kf_1vn_m:get_config(race_2_scene),
                                     [{DefX, DefY}] = data_kf_1vn:get_value(?KF_1VN_CFG_DEF_XY),
                                     clusters_center:apply_cast(Node, lib_scene, change_player_scene, [Id, SceneId, ScenePoolId, CopyId, DefX, DefY, [{ghost, 1}]]),
                                     NewMatchInfo = MatchInfo#kf_1vn_race_2_match_info{watch_num = WatchNum + 1, watch_ids = [{ServerId, Id} | WatchIds]},
                                     NewMatchList = lists:keyreplace(BattleId, #kf_1vn_race_2_match_info.battle_id, MatchList, NewMatchInfo),
                                     Roles1 = lists:keyreplace(Id, #kf_1vn_role.id, Roles, KfRole#kf_1vn_role{watch_battle_id = BattleId}),
                                     mod_kf_1vn_pk:get_watch_info(ServerId, Id, CopyId),
                                     TmpState = State#act_state{role_m = RoleM#{Area:=Roles1}, race_2_m = Race2M#{Area:=Race2#kf_1vn_race_2{match_list = NewMatchList}}},
                                     {true, TmpState};
                                 _ ->
                                     {false, ?ERRCODE(err621_no_battle)}
                             end
                     end,
            case Result of
                {false, ErrCode} ->
                    {ok, BinData} = pt_621:write(62121, [ErrCode]),
                    clusters_center:apply_cast(Node, lib_server_send, send_to_uid, [Id, BinData]),
                    {noreply, State};
                {true, NewState} ->
                    {noreply, NewState}
            end
    end;

do_handle_cast({send_to_area, Area, BinData}, State) ->
    #act_state{role_m = RoleM} = State,
    case maps:get(Area, RoleM, false) of
        RoleL when is_list(RoleL) ->
            erlang:spawn(fun() ->
                            Fun = fun(Id, SerId) ->
                                      Node = clusters_center:find_node(SerId),
                                      lib_server_send:send_to_uid(Node, Id, BinData)
                                  end,
                            [Fun(Id, SerId)||#kf_1vn_role{id = Id, server_id = SerId, enter = Enter}<-RoleL, Enter =/= 0]
                        end);
        _ -> skip
    end,
    {noreply, State};

do_handle_cast({gm_end}, State) ->
    self() ! {ac_end, all},
    {noreply, State};

do_handle_cast(_Msg, State) ->
    ?ERR("Handle unkown msg[~p]~n", [_Msg]),
    {noreply, State}.

handle_info(Info, State) ->
    case catch do_handle_info(Info, State) of
        {noreply, NewState} ->
            {noreply, NewState};
        Err ->
            ?ERR("Handle info[~p] error:~p~n", [Info, Err]),
            {noreply, State}
    end.

do_handle_info(add_exp, State) ->
    #act_state{role_m = RoleM, exp_ref = OldExpRef} = State,
    util:cancel_timer(OldExpRef),
    F = fun(_Area, Roles) ->
        add_exp(Roles, [])
        end,
    RoleM1 = maps:map(F, RoleM),
    ExpRef = erlang:send_after(?KF_1VN_EXP_ADD_TIME, self(), add_exp),
    {noreply, State#act_state{role_m = RoleM1, exp_ref = ExpRef}};

%% 资格赛匹配
do_handle_info(match, #act_state{stage = Stage} = State) when Stage /= ?KF_1VN_FREE ->
    #act_state{edtime = EdTime, role_m = RoleM, race_1_rank_m = Race1RankM, max_scene_pool = MaxScenePool,
        match_time = MatchTime, battle_time = BattleTime, match_turn = MatchTurn, match_ref = OldMatchRef, max_power = MaxPower} = State,
    % ?DEBUG("Race1RankM~p~n", [Race1RankM]),
    Now = utime:unixtime(),
    util:cancel_timer(OldMatchRef),
    LoadingTime = data_kf_1vn_m:get_config(loading_time),
    case Now + BattleTime + 2 > EdTime of
        true -> %% 当前时间+战斗时间如果超过了资格赛结束时间，则后面不再进行匹配
            F = fun(_Area, Roles) ->
                {ok, BinData} = pt_621:write(62101, [Stage, MatchTurn, EdTime, ?KF_1VN_STATE_END, EdTime]),
                lib_kf_1vn:send_all_enter(Roles, BinData)
            end,
            maps:map(F, RoleM),
            {noreply, State#act_state{sub_stage = ?KF_1VN_STATE_END, sub_edtime = EdTime}};
        false ->%% 当前时间+战斗时间未超过了资格赛结束时间，则战斗结束后继续匹配
            Rac1Max = data_kf_1vn_m:get_config(race_1_max_times),
            SubEndTime = Now + MatchTime,
            F = fun(Area, Roles, [TmpRoleM, TmpRace1RankM, TmpRace1ShortRankM]) ->
                %% 根据战力排序，进行匹配
                SortRoles = lists:reverse(lists:keysort(#kf_1vn_role.combat_power, Roles)),
                {MatchRoles, LeftRoles} = race_1_match(SortRoles, BattleTime, Stage),
                % ?PRINT("MATCH  -------- ~p~n", [{length(MatchRoles), length(Roles)}]),
                send_to_battle(MatchRoles, BattleTime, Stage, Now, LoadingTime, MaxScenePool, MaxPower),
                {ok, BinData} = pt_621:write(62101, [Stage, MatchTurn + 1, EdTime, ?KF_1VN_STATE_MATCH, SubEndTime]),
                lib_kf_1vn:send_all_enter(Roles, BinData),
                {ok, BinData1} = pt_621:write(62132, [?ERRCODE(err621_not_match_yet), []]),
                lib_kf_1vn:send_all_enter([R || #kf_1vn_role{race_1_times = Times, pk = 0} = R <- LeftRoles, Times < Rac1Max], BinData1),
                RealMatchRoles = [R || R <- MatchRoles, is_record(R, kf_1vn_role)],
                %% 取分数排名前300
                Rank = sort_score_rank(Roles),
                ShortRank = lists:sublist(Rank, 300),
                [TmpRoleM#{Area:=LeftRoles ++ RealMatchRoles}, TmpRace1RankM#{Area=>Rank}, TmpRace1ShortRankM#{Area=>ShortRank}]
            end,
            [RoleM1, Race1RankM1, Race1ShortRankM] = maps:fold(F, [RoleM, Race1RankM, #{}], RoleM),
            MatchRef = erlang:send_after(MatchTime * 1000, self(), match), %% 一段时间后继续匹配（并不会等上一轮结束后再匹配）
            clusters_center:apply_to_all_node(mod_kf_1vn_local, set_race_1_rank, [Race1ShortRankM], 50),
            {noreply, State#act_state{role_m = RoleM1, race_1_rank_m = Race1RankM1, last_match_time = Now, match_turn = MatchTurn + 1, match_ref = MatchRef, sub_edtime = SubEndTime}}
    end;

do_handle_info(race_2_match, #act_state{stage = Stage} = State) when Stage /= ?KF_1VN_FREE ->
    #act_state{sign_list = SignList, edtime = EdTime, role_m = RoleM, race_2_m = Race2M, match_time = MatchTime, match_turn = OMatchTurn, max_scene_pool = MaxScenePool, area_list = LeftAreaList} = State,
    Now = utime:unixtime(),
    MatchTurn = OMatchTurn + 1,
    SignList1 = [E#kf_1vn_role_sign{battle_id = 0, bet_side = 0} || E <- SignList],
    case data_kf_1vn:get_stage_args(MatchTurn, 1) of
        #kf_1vn_race_2_match{} -> %% 还有轮次
            F = fun(Area, Race2, [TmpRace2M, TmpRoleM]) ->
                case data_kf_1vn:get_stage_args(MatchTurn, Area) of
                    #kf_1vn_race_2_match{hard_list = Hard, c_num = ChallengerNum, all_hard = AllHard} ->
                        ORoles = maps:get(Area, TmpRoleM, []),
                        Roles = [KfRole#kf_1vn_role{race_2_bet_id = 0, race_2_battle_id = 0, watch_battle_id = 0} || KfRole <- ORoles],
                        case Roles of
                            [] -> [TmpRace2M, TmpRoleM];
                            _ ->
                                #kf_1vn_race_2{def_list = DefList, challenger_list = ChallengerList, battle_id = OBattleId} = Race2,
                                case DefList of
                                    [] -> %% 擂主为空结束该战场比赛
                                        case lists:member(Area, LeftAreaList) of
                                            true -> self() ! {ac_end, Area};
                                            _ -> skip
                                        end,
                                        [TmpRace2M#{Area=>Race2#kf_1vn_race_2{match_list = [], bet_m = #{}}}, TmpRoleM];
                                    _ ->
                                        [NewHard, NewAllHard] = lib_kf_1vn:get_real_hard_list(Area, DefList, Roles, Hard, AllHard),
                                        HardList = create_race_2_hard_list(NewHard, ChallengerNum, NewAllHard),
                                        % DefAveAttr = calc_def_average_attr(DefList, #attr{}, 1), %% 擂主平均属性（用于生成机器人）现在机器人都读配置
                                        DefAveAttr = #attr{},
                                        {DefRank, ChallenageRank} = sort_race_2_rank(Roles, [], []), %% 将玩家分成挑战者和擂主并排序
                                        ShortDefRank = lists:sublist(DefRank, 300),
                                        ShortChallenageRank = lists:sublist(ChallenageRank, 300),
                                        lib_kf_1vn:update_race_2_rank(Area, ShortDefRank, ShortChallenageRank), %% 同步排行榜到本地节点
                                        {ok, BinData} = pt_621:write(62101, [Stage, MatchTurn, EdTime, ?KF_1VN_STATE_MATCH, Now + MatchTime]),
                                        lib_kf_1vn:send_all_enter(Roles, BinData),
                                        SortCL = sort_challenger_list_by_power(ChallengerList), %% 对挑战者用战力排序
                                        HardM = divide_group(SortCL, Area), %% 根据难度评级拆分玩家
                                        {MatchList, NewBattleId, RolesN, DefListN, ChallengerListN}
                                            = race_2_match(DefList, Roles, HardM, HardList, DefAveAttr, Area, 1, OBattleId, MaxScenePool, DefList, ChallengerList, []),
                                        %% 清理一下上一轮的数据
                                        F1 = fun(#kf_1vn_role{race_2_lose = Lose} = E) ->
                                            ?IF(Lose == 1, E, E#kf_1vn_role{race_2_turn = MatchTurn, result = 0, race_2_time = 0})
                                        end,
                                        NewRoles = lists:map(F1, RolesN),
                                        [TmpRace2M#{Area=>Race2#kf_1vn_race_2{match_list = MatchList, battle_id = NewBattleId, bet_m = #{},
                                         def_rank = ShortDefRank, challenage_rank = ShortChallenageRank, def_list = DefListN,
                                         challenger_list = ChallengerListN}}, TmpRoleM#{Area=>NewRoles}]
                                end
                        end;
                    _ -> [TmpRace2M, TmpRoleM]
                end
            end,
            [Race2MN, RoleM1] = maps:fold(F, [Race2M, RoleM], Race2M),
            Ref = case data_kf_1vn:get_stage_args(MatchTurn, 1) of %% 后面没有比赛轮次则结束比赛
                #kf_1vn_race_2_match{} -> erlang:send_after(MatchTime * 1000 + 500, self(), race_2_bet);
                _ -> undefined
            end,
            {noreply, State#act_state{sub_stage = ?KF_1VN_STATE_MATCH, sign_list = SignList1, role_m = RoleM1, race_2_m = Race2MN, last_match_time = Now, match_turn = MatchTurn, match_ref = Ref, sub_edtime = Now + MatchTime}};
        _ -> %% 没有轮次了
            self() ! {ac_end, all},
            {noreply, State#act_state{sub_stage = ?KF_1VN_STATE_MATCH, sign_list = SignList1, match_turn = MatchTurn, sub_edtime = Now + MatchTime}}
    end;

%% 进入竞猜环节
do_handle_info(race_2_bet, #act_state{ac_id = AcId, stage = Stage} = State) when Stage /= ?KF_1VN_FREE ->
    #act_state{stage = Stage, edtime = EdTime, role_m = RoleM, match_turn = MatchTurn, race_2_m = Race2M} = State,
    #kf_1vn_time_cfg{bet_time = BetTime} = data_kf_1vn:get_info(AcId),
    Now = utime:unixtime(),
    SubEndTime = Now + BetTime,
    % ?PRINT("---- race_2_bet --- ~n", []),
    F = fun(Area, #kf_1vn_race_2{match_list = MatchList}) ->
        Roles = maps:get(Area, RoleM, []),
        {ok, BinData1} = pt_621:write(62101, [Stage, MatchTurn, EdTime, ?KF_1VN_STATE_BET, SubEndTime]),
        lib_kf_1vn:send_all_enter(Roles, BinData1),
        lib_kf_1vn:send_to_challenger(Area, MatchList), %% 通知被选中的挑战者
        %% 第一场竞猜发送传闻
        if
            MatchTurn == 1 ->
                TvId = ?IF(Area == 1, 11, 12),
                BinData2 = lib_chat:make_tv(?MOD_KF_1VN, TvId, []),
                lib_kf_1vn:send_all_enter(Roles, BinData2);
            true -> skip
        end
    end,
    maps:map(F, Race2M),
    Ref = erlang:send_after(BetTime * 1000 + 500, self(), race_2_send_battle),
    {noreply, State#act_state{sub_stage = ?KF_1VN_STATE_BET, match_ref = Ref, sub_edtime = SubEndTime}};

%% 比赛开始，将玩家送入比赛场地
do_handle_info(race_2_send_battle, #act_state{stage = Stage} = State) when Stage /= ?KF_1VN_FREE ->
    #act_state{stage = Stage, edtime = EdTime, role_m = RoleM, race_2_m = Race2M, match_turn = MatchTurn, battle_time = BattleTime, max_power = MaxPower} = State,
    Now = utime:unixtime(),
    LoadingTime = data_kf_1vn_m:get_config(loading_time),
    Delay = LoadingTime + BattleTime + 14,
    SubEndTime = Now + Delay,
    F = fun(Area, Race2) ->
        Roles = maps:get(Area, RoleM, []),
        {ok, BinData} = pt_621:write(62101, [Stage, MatchTurn, EdTime, ?KF_1VN_STATE_FIGHT, SubEndTime]),
        lib_kf_1vn:send_all_enter(Roles, BinData),
        #kf_1vn_race_2{match_list = MatchList} = Race2,
        MatchList1 = lists:reverse(send_to_battle_race_2(MatchList, BattleTime, Stage, Now, LoadingTime, MatchTurn, [], MaxPower)),
        Race2#kf_1vn_race_2{match_list = MatchList1}
    end,

    Race2M1 = maps:map(F, Race2M),
    Ref = erlang:send_after(Delay * 1000 + 500, self(), race_2_match),
    {noreply, State#act_state{sub_stage = ?KF_1VN_STATE_FIGHT, match_ref = Ref, race_2_m = Race2M1, sub_edtime = SubEndTime}};

%% 活动结束 Area all：所有赛区都结束
do_handle_info({ac_end, Area}, State) ->

    % ?PRINT("ac_end~n", []),
    #act_state{stage = _Stage, area_list = AreaList, race_2_m = Race2M, role_m = RoleM, match_ref = MatchRef, exp_ref = ExpRef} = State,
    case AreaList of
        [] ->
            NewState = State;
        _ ->
            IsAllEnd = case Area of
                           all -> true;
                           _ -> case lists:delete(Area, AreaList) of
                                    [] -> true;
                                    AreaList1 -> {false, AreaList1}
                                end
                       end,
            case IsAllEnd of
                {false, LeftAreaList} -> %% 单个赛区结束
                   % ?PRINT("---- ac end area ~w --- ~n", [Area]),
                    Roles = maps:get(Area, RoleM, []),
                    {DefRank, ChallenageRank} = sort_race_2_rank(Roles, [], []),
                    ShortDefRank = lists:sublist(DefRank, 300),
                    ShortChallenageRank = lists:sublist(ChallenageRank, 300),
                    lib_kf_1vn:update_race_2_rank(Area, ShortDefRank, ShortChallenageRank), %% 同步排行榜到本地节点
                    send_race_2_def_award(DefRank, Area),
                    %% 传闻
                    WinnerList = lib_kf_1vn:make_winner_list(DefRank),
                    OpenLv = data_kf_1vn:get_value(?KF_1VN_CFG_ENTER_LV),
                    case WinnerList of
                        [] ->
                            case Area of
                                1 -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 2, []]);
                                _ -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 8, []])
                            end;
                        _ ->
                            case Area of
                                1 -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 6, [WinnerList]]);
                                _ -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 7, [WinnerList]])
                            end
                    end,
                    Race2M1 = Race2M#{Area=>#kf_1vn_race_2{def_rank = ShortDefRank, challenage_rank = ShortChallenageRank}},
                    lib_kf_1vn:db_save_def_rank(ShortDefRank, Area),

                    NewState = State#act_state{area_list = LeftAreaList, race_2_m = Race2M1};
                true -> %% 所有赛区结束
                    NextStage = ?KF_1VN_END,
                    NextSubStage = ?KF_1VN_STATE_END,
                    Now = utime:unixtime(),
                    EdTime = Now + 60,
                    util:cancel_timer(MatchRef),
                    util:cancel_timer(ExpRef),
                    clusters_center:apply_to_all_node(mod_kf_1vn_local, stage_change, [NextStage, 1, EdTime, NextSubStage, EdTime]),

                    F = fun(TmpArea, Roles, TmpRace2M) ->
                        case lists:member(TmpArea, AreaList) of
                            true ->
                                {DefRank, ChallenageRank} = sort_race_2_rank(Roles, [], []),
                                ShortDefRank = lists:sublist(DefRank, 300),
                                ShortChallenageRank = lists:sublist(ChallenageRank, 300),
                                lib_kf_1vn:update_race_2_rank(TmpArea, ShortDefRank, ShortChallenageRank), %% 同步排行榜到本地节点
                                send_race_2_def_award(DefRank, TmpArea),
                                %% 传闻
                                WinnerList = lib_kf_1vn:make_winner_list(DefRank),
                                OpenLv = data_kf_1vn:get_value(?KF_1VN_CFG_ENTER_LV),
                                case WinnerList of
                                    [] ->
                                        case TmpArea of
                                            1 -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 2, []]);
                                            _ -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 8, []])
                                        end;
                                    _ ->
                                        case TmpArea of
                                            1 -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 6, [WinnerList]]);
                                            _ -> clusters_center:apply_to_all_node(lib_chat, send_TV, [{all_lv, {OpenLv, 1000}}, ?MOD_KF_1VN, 7, [WinnerList]])
                                        end
                                end,
                                lib_kf_1vn:db_save_def_rank(ShortDefRank, TmpArea),
                                TmpRace2M#{TmpArea => #kf_1vn_race_2{def_rank = ShortDefRank, challenage_rank = ShortChallenageRank}};
                            false ->
                                TmpRace2M
                        end
                        end,
                    Race2M1 = maps:fold(F, #{}, RoleM),

                    erlang:send_after(?KF_1VN_SEND_OUT_TIME * 1000, self(), ac_stop),
                    NewState = State#act_state{stage = NextStage, sub_stage = NextSubStage, race_2_m = Race2M1}
            end
    end,
    send_race_2_banner(NewState),
    {noreply, NewState};

do_handle_info(ac_stop, State) ->
    #act_state{role_m = RoleM, race_2_m = Race2M} = State,
    mod_kf_1vn_mgr:ac_end(), %% 告诉管理进程活动结束
    NextStage = ?KF_1VN_FREE,
    NextSubStage = ?KF_1VN_STATE_WAIT,
    clusters_center:apply_to_all_node(mod_kf_1vn_local, stage_change, [NextStage, 1, 0, NextSubStage, 0]),
    %% 踢出玩家
    F = fun(_Area, Roles) ->
        [lib_kf_1vn:quit_to_main_scene(E#kf_1vn_role.server_id, E#kf_1vn_role.id) || E <- Roles, E#kf_1vn_role.enter =/= 0]
        end,
    maps:map(F, RoleM),

    lib_kf_1vn:db_clean_sign_center(),
    {noreply, #act_state{stage = NextStage, sub_stage = NextSubStage, race_2_m = Race2M}};


do_handle_info(_Info, State) ->
    ?ERR("Handle unkown info[~p]~n", [_Info]),
    {noreply, State}.

%% 按照战力进行难度划分
divide_group(SortChallengerList, Area) ->
    HardList = data_kf_1vn:get_all_hard(),
    divide_group_roles(HardList, SortChallengerList, #{}, Area).

divide_group_roles([Hard | T], SortChallengerList, HardM, Area) ->
    HardM1 = HardM#{Hard => [Id || {Id, Combat} <- SortChallengerList, data_kf_1vn:get_hard(Combat) == Hard]},
    divide_group_roles(T, SortChallengerList, HardM1, Area);
divide_group_roles([], _SortChallengerList, HardM, _Area) -> HardM.

terminate(_Reason, _State) ->
    ok.
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% 按等级划分赛区
cut_area(SignList) ->
    F = fun(#kf_1vn_role_sign{lv = Lv} = H, AreaList) ->
        AreaId = data_kf_1vn:get_area(Lv),
        case lists:keyfind(AreaId, 1, AreaList) of
            {AreaId, Area} when AreaId > 0 ->
                NewArea = [H#kf_1vn_role_sign{area = AreaId} | Area],
                NewAreaList = lists:keystore(AreaId, 1, AreaList, {AreaId, NewArea});
            false when AreaId > 0 ->
                NewArea = [H#kf_1vn_role_sign{area = AreaId}],
                NewAreaList = lists:keystore(AreaId, 1, AreaList, {AreaId, NewArea});
            _ ->
                NewAreaList = AreaList
        end,
        NewAreaList
    end,
    AreaList = lists:foldl(F, [], SignList),
    NewAreaList = [Area || {_AreaId, Area} <- AreaList],
    MinNum = data_kf_1vn_m:get_config(area_min_num),
    merge_area(NewAreaList, MinNum, 1, [], []).
%% 赛区合并
merge_area([H1, H2 | T], MinNum, Index, Result, AreaList) ->
    case length(H1) < MinNum of
        true ->  %% 当某个赛区玩家不够时不开启该赛区，把该赛区剩余玩家加到下一个赛区
            Title = utext:get(6210017),
            F = fun(RoleSign) ->
                Content = utext:get(6210018, [lib_kf_1vn:get_area_name(Index), lib_kf_1vn:get_area_name(Index + 1)]),
                lib_kf_1vn:send_mail(RoleSign#kf_1vn_role_sign.server_id, [RoleSign#kf_1vn_role_sign.id], Title, Content, []),
                clusters_center:apply_cast(RoleSign#kf_1vn_role_sign.server_id, lib_chat, send_TV, [{player, RoleSign#kf_1vn_role_sign.id}, ?MOD_KF_1VN, 10, []]),

                RoleSign#kf_1vn_role_sign{area = Index + 1}
                end,
            H1N = [F(E) || E <- H1],
            merge_area([H1N ++ H2 | T], MinNum, Index + 1, Result, AreaList);
        false -> merge_area([H2 | T], MinNum, Index + 1, H1 ++ Result, [Index | AreaList])
    end;
merge_area([H], _MinNum, Index, Result, AreaList) -> {H ++ Result, [Index | AreaList]};
merge_area(_, _, _Index, Result, AreaList) -> {Result, AreaList}.

%% 更新游戏服的报名数据
change_local_area([#kf_1vn_role_sign{server_id = ServerId, id = Id, area = Area} | T], SignAreaM) ->
    SignInfos = maps:get(ServerId, SignAreaM, []),
    change_local_area(T, SignAreaM#{ServerId => [{Id, Area} | SignInfos]});
change_local_area([], SignAreaM) ->
    F = fun(ServerId, SignInfos) ->
            clusters_center:apply_cast(ServerId, mod_kf_1vn_local, sign_info, [SignInfos])
        end,
    maps:map(F, SignAreaM).

%% 资格赛匹配
race_1_match(Roles, BattleTime, Stage) ->
    Rac1Max = data_kf_1vn_m:get_config(race_1_max_times),
    NowTime = utime:unixtime(),
    F = fun(R) ->
        IsSkipMatch = lib_kf_1vn:is_skip_this_match(R#kf_1vn_role.pk_time, NowTime),
        R#kf_1vn_role.enter =/= 0 andalso R#kf_1vn_role.pk == 0 andalso R#kf_1vn_role.race_1_times < Rac1Max andalso not IsSkipMatch
        end,
    {WaitMatch, Left} = lists:partition(F, Roles), %% 筛选出等待匹配的和剩下的
    race_1_match(WaitMatch, [], Left, BattleTime, Stage). %% 等待匹配的进去匹配，剩下的回炉重造

race_1_match([H1, H2 | T], Result, Left, BattleTime, Stage) ->
    #kf_1vn_role{id = H1Id, race_1_times = T1, race_1_match = R1M1, race_1_seed = Race1Seed1, combat_power = CP1, race_1_match_record = H1Race1MatchRecord} = H1,
    case race_1_match_get_competitor([H2 | T], CP1, R1M1, H1Race1MatchRecord, Race1Seed1, 1, 8, [], []) of %% 往后筛选8个
        false ->
            Robot = lib_kf_1vn:trans_to_pk_robot(H1), %% 给他创建一个机器人做对手
            H1N = H1#kf_1vn_role{pk = 1, race_1_times = T1 + 1, race_1_match = 0},
            race_1_match([H2 | T], [H1N, Robot | Result], Left, BattleTime, Stage);
        {true, Ctor, LeftT} ->
            #kf_1vn_role{id = CtorId, race_1_times = T2, race_1_match_record = CtorRace1MatchRecord} = Ctor,
            case lists:keyfind(CtorId, 1, H1Race1MatchRecord) of
                {CtorId, MatchNum1} -> skip;
                _ -> MatchNum1 = 0
            end,
            case lists:keyfind(H1Id, 1, CtorRace1MatchRecord) of
                {H1Id, MatchNum2} -> skip;
                _ -> MatchNum2 = 0
            end,
            H1N = H1#kf_1vn_role{pk = 1, race_1_times = T1 + 1, race_1_match = 0, race_1_match_record = lists:keystore(CtorId, 1, H1Race1MatchRecord, {CtorId, MatchNum1 + 1})},
            CtorN = Ctor#kf_1vn_role{pk = 1, race_1_times = T2 + 1, race_1_match = 0, race_1_match_record = lists:keystore(H1Id, 1, CtorRace1MatchRecord, {H1Id, MatchNum2 + 1})},
            race_1_match(LeftT, [H1N, CtorN | Result], Left, BattleTime, Stage)
    end;
race_1_match([#kf_1vn_role{race_1_match = _Old, race_1_times = T} = H], Result, Left, _, _) ->
    Robot = lib_kf_1vn:trans_to_pk_robot(H),
    HN = H#kf_1vn_role{pk = 1, race_1_times = T + 1, race_1_match = 0},
    {[HN, Robot | Result], Left};
race_1_match(_, Result, Left, _, _) -> {Result, Left}.

%% 获取对手
race_1_match_get_competitor([], _CP1, _Race1Match1, _Race1MatchRecord,  _Race1Seed1, _Num, _MaxNum, Result, BadResult) ->
    case ulists:list_shuffle(Result) of
        [] -> false;
        [Ctor1 | UnChoose] -> {true, Ctor1, UnChoose ++ BadResult}
    end;
race_1_match_get_competitor(Ctors, CP1, Race1Match1, Race1MatchRecord, Race1Seed1, MaxNum, MaxNum, Result, BadResult) ->
    race_1_match_get_competitor([], CP1, Race1Match1, Race1MatchRecord, Race1Seed1, MaxNum, MaxNum, Result, BadResult ++ Ctors);
race_1_match_get_competitor([Ctor | T], CP1, Race1Match1, Race1MatchRecord, Race1Seed1, Num, MaxNum, Result, BadResult) ->
    #kf_1vn_role{id = CtorId, race_1_seed = Race1Seed2, combat_power = CP2} = Ctor,
    CPGap = abs(CP1 - CP2),
    Race1Ratio1 = data_kf_1vn_m:get_race_1_match_args(Race1Match1),
    case lists:keyfind(CtorId, 1, Race1MatchRecord) of
        {CtorId, MatchNum} -> skip;
        _ -> MatchNum = 0
    end,
    if
        Race1Seed1 == 1 andalso Race1Seed2 == 1 andalso Race1Match1 < 3 ->
            race_1_match_get_competitor(T, CP1, Race1Match1, Race1MatchRecord, Race1Seed1, Num, MaxNum, Result, [Ctor | BadResult]);
        CPGap > CP1 * Race1Ratio1 ->
            race_1_match_get_competitor(T, CP1, Race1Match1, Race1MatchRecord, Race1Seed1, Num, MaxNum, Result, [Ctor | BadResult]);
        MatchNum >= ?RACE_1_MATCH_COUNT -> %% 同一个玩家匹配不能超过两次
            race_1_match_get_competitor(T, CP1, Race1Match1, Race1MatchRecord, Race1Seed1, Num, MaxNum, Result, [Ctor | BadResult]);
        true ->
            race_1_match_get_competitor(T, CP1, Race1Match1, Race1MatchRecord, Race1Seed1, Num + 1, MaxNum, [Ctor | Result], BadResult)
    end.

race_2_match([{DefId, _} | T], Roles, HardM, HardList, Attr, Area, RobotId, BattleId, MaxScenePool, DefList, ChallengerList, MatchL) ->
    case lists:keyfind(DefId, #kf_1vn_role.id, Roles) of
        #kf_1vn_role{server_num = DefServerNum, enter = Enter, race_2_turn = MatchTurn} = DefRole when Enter =/= 0 ->
            {HardM1, Challengers, NewRobotId, RolesC} = race_2_match_challenger(HardList, HardM, Roles, Attr, Area, DefServerNum, RobotId, BattleId, MatchTurn, []),
            PKScenePoolId = urand:rand(0, MaxScenePool),
            MathInfo = #kf_1vn_race_2_match_info{battle_id = BattleId, scene_pool_id = PKScenePoolId, def_role = DefRole, challengers = Challengers},
            Roles1 = lists:keyreplace(DefId, #kf_1vn_role.id, RolesC, DefRole#kf_1vn_role{race_2_battle_id = BattleId}),
            race_2_match(T, Roles1, HardM1, HardList, Attr, Area, NewRobotId, BattleId + 1, MaxScenePool, DefList, ChallengerList, [MathInfo | MatchL]);
        #kf_1vn_role{enter = 0, combat_power = DefCombatPower} = DefRole ->
            Roles1 = lists:keyreplace(DefId, #kf_1vn_role.id, Roles, DefRole#kf_1vn_role{race_2_lose = 1}),
            race_2_match(T, Roles1, HardM, HardList, Attr, Area, RobotId, BattleId, MaxScenePool, lists:keydelete(DefId, 1, DefList), [{DefId, DefCombatPower} | ChallengerList], MatchL);
        _ ->
            race_2_match(T, Roles, HardM, HardList, Attr, Area, RobotId, BattleId, MaxScenePool, lists:keydelete(DefId, 1, DefList), ChallengerList, MatchL)
    end;
race_2_match([], Roles, _HardM, _HardList, _Attr, _Area, _RobotId, BattleId, _MaxScenePool, DefList, ChallengerList, MatchL) ->
    {MatchL, BattleId, Roles, DefList, ChallengerList}.

%% 挑战赛选出挑战者
race_2_match_challenger([Hard | T], HardM, Roles, Attr, Area, DefServerNum, RobotId, BattleId, MatchTurn, Result) ->
    {Result1, HardM1, Roles1} = case maps:get(Hard, HardM, false) of
        false ->
            {[lib_kf_1vn:create_robot_pk(Hard, Attr, Area, RobotId, DefServerNum, MatchTurn) | Result], HardM, Roles};
        [] ->
            {[lib_kf_1vn:create_robot_pk(Hard, Attr, Area, RobotId, DefServerNum, MatchTurn) | Result], HardM, Roles};
        Ids ->
            case race_2_select_enter_challenger(Ids, Roles, Area, BattleId) of
                {ChallengerPk, LeftIds, TmpRoles} ->
                    {[ChallengerPk | Result], HardM#{Hard=>LeftIds}, TmpRoles};
                _ ->
                    {[lib_kf_1vn:create_robot_pk(Hard, Attr, Area, RobotId, DefServerNum, MatchTurn) | Result], HardM, Roles}
            end
    end,
    race_2_match_challenger(T, HardM1, Roles1, Attr, Area, DefServerNum, RobotId + 1, BattleId, MatchTurn, Result1);
race_2_match_challenger([], HardM, Roles, _Attr, _Area, _DefServerNum, RobotId, _BattleId, _MatchTurn, Result) ->
    {HardM, Result, RobotId, Roles}.


race_2_select_enter_challenger([], _, _, _) -> false;
race_2_select_enter_challenger(Ids, Roles, Area, BattleId) ->
    [Id | T] = ulists:list_shuffle(Ids),
    case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
        #kf_1vn_role{enter = Enter} = R when Enter =/= 0 ->
            TmpRoles = lists:keyreplace(Id, #kf_1vn_role.id, Roles, R#kf_1vn_role{race_2_battle_id = BattleId}),
            {lib_kf_1vn:trans_to_pk_role(R, Area), lists:delete(Id, Ids), TmpRoles};
        _ ->
            race_2_select_enter_challenger(T, Roles, Area, BattleId)
    end.

%% 传送到战斗场景
send_to_battle([PlayerA, PlayerB | T], BattleTime, Stage, Now, LoadingTime, MaxScenePool, MaxPower) ->
    SideA = lib_kf_1vn:trans_to_pk_role(PlayerA),
    SideB = lib_kf_1vn:trans_to_pk_role(PlayerB),
    PKScenePoolId = urand:rand(0, MaxScenePool),
    mod_kf_1vn_pk:start_link([SideA, [SideB], [Now, LoadingTime, BattleTime], Stage, 0, 0, PKScenePoolId, MaxPower]),
    send_to_battle(T, BattleTime, Stage, Now, LoadingTime, MaxScenePool, MaxPower);
send_to_battle([], _, _, _, _, _, _) -> skip.

send_to_battle_race_2([#kf_1vn_race_2_match_info{battle_id = BattleId, scene_pool_id = ScenePoolId, def_role = DefRole, challengers = ChallengerList, status = 0} = H | T], BattleTime, Stage, Now, LoadingTime, MatchTurn, Result, MaxPower) ->
    #kf_1vn_role{id = DefId, figure = #figure{name = DefName}, server_id = DefSerId,
                 enter = Enter, area = Area, combat_power = DefCombatPower, server_num = DefSerNum} = DefRole,
    if %% 比赛开始时擂主不在备战区 则直接判定失败
        Enter == 0 ->
            race_2_pk_result(BattleId, 0, Area, DefId, DefCombatPower), %% 单场比赛结束
            update_role(Area, DefId, [{race_2_lose, 1}, {race_2_turn, MatchTurn}]),
            case data_kf_1vn:get_race_2_award(2, MatchTurn) of %% 挑战者
                {ChallengerWinRewards, _} -> skip;
                _ -> ChallengerWinRewards = []
            end,
            Title = utext:get(6210005),
            Content = utext:get(6210006, [lib_kf_1vn:get_area_name(Area), MatchTurn]),
            [
                lib_kf_1vn:send_mail(EC#kf_1vn_role_pk.server_id, [EC#kf_1vn_role_pk.id], Title, Content, ChallengerWinRewards)
                 || EC <- ChallengerList,
                EC#kf_1vn_role_pk.is_dead == 0,
                EC#kf_1vn_role_pk.type == ?KF_1VN_C_TYPE_PLAYER
            ],
            lib_log_api:log_kf_1vn_race_2(Area, MatchTurn, BattleId, DefId, DefSerId, DefSerNum,
                                            DefName, DefCombatPower, 1, 0, 4, 0),
            send_to_battle_race_2(T, BattleTime, Stage, Now, LoadingTime, MatchTurn, [H#kf_1vn_race_2_match_info{battle_win = 2} | Result], MaxPower);
        true ->
            SideA = lib_kf_1vn:trans_to_pk_role(DefRole, DefRole#kf_1vn_role.area),
            {ok, Pid} = mod_kf_1vn_pk:start_link([SideA, ChallengerList, [Now, LoadingTime, BattleTime], Stage, MatchTurn, BattleId, ScenePoolId, MaxPower]),
            send_to_battle_race_2(T, BattleTime, Stage, Now, LoadingTime, MatchTurn, [H#kf_1vn_race_2_match_info{copy_id = Pid} | Result], MaxPower)
    end;
send_to_battle_race_2([H | T], BattleTime, Stage, Now, LoadingTime, MatchTurn, Result, MaxPower) ->
    send_to_battle_race_2(T, BattleTime, Stage, Now, LoadingTime, MatchTurn, [H | Result], MaxPower);
send_to_battle_race_2([], _, _, _, _, _, Result, _) -> Result.


update_role_dt([H | T], KfRole, Area) ->
    KfRoleN = case H of
          {win, Value} -> KfRole#kf_1vn_role{win = Value};
          {win_streak, Value} -> KfRole#kf_1vn_role{win_streak = Value};
          {score, Value} -> KfRole#kf_1vn_role{score = Value};
          {pk, Value} -> KfRole#kf_1vn_role{pk = Value};
          {pk_time, Value} -> KfRole#kf_1vn_role{pk_time = Value};
          {race_2_lose, Value} -> KfRole#kf_1vn_role{race_2_lose = Value};
          {race_2_turn, Value} ->
              lib_kf_1vn:update_player_1vn(KfRole#kf_1vn_role.server_id, KfRole#kf_1vn_role.id, 1, Value, Area),
              KfRole#kf_1vn_role{race_2_turn = Value};
          {race_2_time, Value} -> KfRole#kf_1vn_role{race_2_time = Value};
          {match_challenger, Value} -> KfRole#kf_1vn_role{match_challenger = Value};
          {result, Value} -> KfRole#kf_1vn_role{result = Value};
          {power, Power} -> KfRole#kf_1vn_role{combat_power = Power};
          _ -> KfRole
      end,
    update_role_dt(T, KfRoleN, Area);
update_role_dt(_, KfRole, _) -> KfRole.

update_race_2_combat(Role, Race2M, Area, Combat) ->
    #kf_1vn_role{id = Id, race_2_side = Race2Side, race_2_battle_id = BattleId} = Role,
    Race2 = maps:get(Area, Race2M, []),
    Race2MatchL = Race2#kf_1vn_race_2.match_list,
    case lists:keyfind(BattleId, #kf_1vn_race_2_match_info.battle_id, Race2MatchL) of
        MatchInfo when is_record(MatchInfo, kf_1vn_race_2_match_info) ->
            #kf_1vn_race_2_match_info{def_role = DefRole, challengers = Challengers} = MatchInfo,
            if
                Race2Side =:= ?SIDE_DEF -> %% 擂主，更新擂主数据
                    NewDefRole = DefRole#kf_1vn_role{combat_power = Combat},
                    NewMatchInfo = MatchInfo#kf_1vn_race_2_match_info{def_role = NewDefRole};
                true ->
                    case lists:keyfind(Id, #kf_1vn_role_pk.id, Challengers) of
                        Challenger when is_record(Challenger, kf_1vn_role_pk) ->
                            NewChallenger = Challenger#kf_1vn_role_pk{combat_power = Combat},
                            NewChallengers = lists:keyreplace(Id, #kf_1vn_role_pk.id, Challengers, NewChallenger);
                        _ ->
                            NewChallengers = Challengers
                    end,
                    NewMatchInfo = MatchInfo#kf_1vn_race_2_match_info{challengers = NewChallengers}
            end,
            NewRace2MatchL = lists:keyreplace(BattleId, #kf_1vn_race_2_match_info.battle_id, Race2MatchL, NewMatchInfo);
        _ ->
            NewRace2MatchL = Race2MatchL
    end,
    NewRace2 = Race2#kf_1vn_race_2{match_list = NewRace2MatchL},
    maps:put(Area, NewRace2, Race2M).

%% 发送场景banner
send_race_2_banner(State) ->
    #act_state{role_m = RoleM} = State,
    F = fun(Area, Roles) ->
        do_send_race_2_banner(Area, Roles, State)
    end,
    maps:map(F, RoleM),
    ok.

do_send_race_2_banner(Area, Roles, State) ->
    #act_state{race_2_m = Race2Map} = State,
    Fun = fun(#kf_1vn_role{id = Id, server_id = SerId, score = _Score, enter = Enter, exp = Exp, race_2_side = Race2Side, race_2_lose = Lose}) ->
        case Enter of
            0 -> skip;
            _ ->
                case maps:get(Area, Race2Map, false) of
                    #kf_1vn_race_2{def_rank = DefRankL, challenage_rank = ShortChallenageRankL} ->
                        case Race2Side of
                            ?SIDE_DEF ->
                                case lists:keyfind(Id, #kf_1vn_def_rank.id, DefRankL) of
                                    #kf_1vn_def_rank{rank = Rank} -> skip;
                                    _ -> Rank = 0
                                end,
                                NewRace2Side = ?IF(Lose > 0, ?SIDE_CHALLENGER, ?SIDE_DEF);
                            _ ->
                                case lists:keyfind(Id, #kf_1vn_challenger_rank.id, ShortChallenageRankL) of
                                    #kf_1vn_challenger_rank{rank = Rank} -> skip;
                                    _ -> Rank = 0
                                end,
                                NewRace2Side = Race2Side
                        end;
                    _ -> Rank = 0, NewRace2Side = Race2Side
                end,
                {ok, BinData} = pt_621:write(62111, [Rank, ?IF(Race2Side == NewRace2Side, 0, 1), Exp, NewRace2Side]),
                clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData])
        end
    end,
    lists:map(Fun, Roles).

%% 发送场景banner
send_banner(Area, Id, State) ->
    #act_state{stage = Stage, optime = OpTime, edtime = EdTime, role_m = RoleM, match_time = MatchTime, last_match_time = LastMatchTime, def_num_m = DefNumM, race_2_m = Race2M} = State,
    Race1MaxTimes = data_kf_1vn_m:get_config(race_1_max_times),

    KfRole = case maps:get(Area, RoleM, false) of
                 false -> false;
                 Roles ->
                     case lists:keyfind(Id, #kf_1vn_role.id, Roles) of
                         false -> false;
                         TmpV -> TmpV
                     end
             end,

    DefNum = maps:get(Area, DefNumM, 0),
    #kf_1vn_race_2{def_rank = DefRankL, challenage_rank = ChallenageRankL} = maps:get(Area, Race2M, #kf_1vn_race_2{}),

    case Stage of
        ?KF_1VN_RACE_1_PRE when KfRole /= false ->
            {ok, BinData} = pt_621:write(62104, [Race1MaxTimes, 0, EdTime, 0, 0, KfRole#kf_1vn_role.exp, DefNum]),
            clusters_center:apply_cast(KfRole#kf_1vn_role.server_id, lib_server_send, send_to_uid, [Id, BinData]);
        ?KF_1VN_RACE_1 when KfRole /= false ->
            #kf_1vn_role{server_id = SerId, race_1_times = Race1Times, score = Score, win = Win, exp = Exp, pk_time = PkTime} = KfRole,
            NextMatchTime = max(OpTime, LastMatchTime) + MatchTime,
            IsSkipMatch = lib_kf_1vn:is_skip_this_match(PkTime, NextMatchTime),
            RealNextMatchTime = ?IF(IsSkipMatch, NextMatchTime + MatchTime, NextMatchTime), %% 如果跳过了该轮匹配，则匹配时间延后一轮
            {ok, BinData} = pt_621:write(62104, [max(0, Race1MaxTimes - Race1Times), Score, RealNextMatchTime, Win, Race1Times - Win, Exp, DefNum]),
            clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]);
        ?KF_1VN_RACE_2_PRE when KfRole /= false ->
            #kf_1vn_role{id = Id, server_id = SerId, exp = Exp, race_2_side = Race2Side, race_2_lose = Lose} = KfRole,
            case Race2Side of
                ?SIDE_DEF ->
                    case lists:keyfind(Id, #kf_1vn_def_rank.id, DefRankL) of
                        #kf_1vn_def_rank{rank = Rank} -> skip;
                        _ -> Rank = 0
                    end,
                    NewRace2Side = ?IF(Lose > 0, ?SIDE_CHALLENGER, ?SIDE_DEF);
                _ ->
                    case lists:keyfind(Id, #kf_1vn_challenger_rank.id, ChallenageRankL) of
                        #kf_1vn_challenger_rank{rank = Rank} -> skip;
                        _ -> Rank = 0
                    end,
                    NewRace2Side = Race2Side
            end,
            {ok, BinData} = pt_621:write(62111, [Rank, ?IF(Race2Side == NewRace2Side, 0, 1), Exp, NewRace2Side]),
            clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]);

        ?KF_1VN_RACE_2 when KfRole /= false ->
            #kf_1vn_role{server_id = SerId, score = _Score, race_2_side = Race2Side, exp = Exp, race_2_lose = Lose} = KfRole,
            IsDef = case Race2Side of
                ?SIDE_DEF ->
                    case lists:keyfind(Id, #kf_1vn_def_rank.id, DefRankL) of
                        #kf_1vn_def_rank{rank = Rank} -> skip;
                        _ -> Rank = 0
                    end,
                    if
                        Lose > 0 -> 0;
                        true     -> 1
                    end;
                _ ->
                    case lists:keyfind(Id, #kf_1vn_challenger_rank.id, ChallenageRankL) of
                        #kf_1vn_challenger_rank{rank = Rank} -> skip;
                        _ -> Rank = 0
                    end,
                    0
            end,
            {ok, BinData} = pt_621:write(62111, [Rank, ?IF(Race2Side == IsDef, 0, 1), Exp, IsDef]),
            clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]);
        _ -> skip
    end.

%% 积分排序
sort_score_rank(Roles) ->
    RankRoles = sort_roles_by_score([E || E <- Roles]),
    TopName = case RankRoles of
                  [] -> "";
                  [H | _] -> H#kf_1vn_role.figure#figure.name
              end,
    F1 = fun(Role, [Result, Rank]) ->
        #kf_1vn_role{id  = Id, server_id = SerId, server_num = SerNum, figure = Figure, server_name = SerName,
            combat_power = CombatPower, score = Score, win = Win, race_1_times = Race1Times} = Role,

        {ok, BinData} = pt_621:write(62119, [Rank, TopName]),
        clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]),
        case Rank > 300 of
            true ->
                [Result, Rank + 1];
            false ->
                [[#kf_1vn_score_rank{
                    rank  = Rank, id = Id, server_id = SerId, server_num = SerNum, server_name = SerName,
                    name  = Figure#figure.name, guild_name = Figure#figure.guild_name, vip = Figure#figure.vip,
                    score = Score, win = Win, lose = Race1Times - Win, combat_power = CombatPower, career = Figure#figure.career
                } | Result], Rank + 1]
        end
         end,
    [Ranks, _] = lists:foldl(F1, [[], 1], RankRoles),
    lists:reverse(Ranks).

sort_challenger_list_by_power(ChallengerList) ->
    F = fun({_, CombatPower1}, {_, CombatPower2}) ->
        CombatPower1 >= CombatPower2
        end,
    lists:sort(F, ChallengerList).

sort_roles_by_score(Roles) ->
    F = fun(#kf_1vn_role{score = Score1, win = Win1}, #kf_1vn_role{score = Score2, win = Win2}) ->
        Score1 > Score2 orelse (Score1 == Score2 andalso Win1 >= Win2)
        end,
    lists:sort(F, Roles).

%% 发送成为擂主的奖励
send_race_1_reward([#kf_1vn_role{server_id = SerId, id = Id, score = Score, area = Area} | T], DefNum, Rank) ->
    Awards = data_kf_1vn:get_score_rank_reward(1, Rank),
    IsDef = case Rank > DefNum of
                true -> 0;
                false -> 1
            end,
    {ok, BinData} = pt_621:write(62109, [IsDef, Rank, Score, Awards]),
    clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]),
    case Awards of
        [] -> skip;
        _ ->
            Title = utext:get(6210001),
            Content = utext:get(6210002, [lib_kf_1vn:get_area_name(Area), Rank]),
            lib_kf_1vn:send_mail(SerId, [Id], Title, Content, Awards)
    end,
    send_race_1_reward(T, DefNum, Rank + 1);
send_race_1_reward([_ | T], DefNum, Rank) -> send_race_1_reward(T, DefNum, Rank + 1);
send_race_1_reward([], _, _) -> ok.

%% 创建挑战赛难度列表
create_race_2_hard_list(Hard, ChallengerNum, AllHard) ->
    race_2_list_add_hard(Hard, ChallengerNum, AllHard, 0).

race_2_list_add_hard(Hard, ChallengerNum, [MinHard, MaxHard], Time) ->
    Result = do_race_2_list_add_hard(Hard, ChallengerNum, []),
    Sum = lists:sum(Result),
    if
        (Sum > MaxHard orelse Sum < MinHard) andalso Time < ?RACE_2_MATCH_COUNT -> race_2_list_add_hard(Hard, ChallengerNum, [MinHard, MaxHard], Time + 1);
        true -> Result
    end.

do_race_2_list_add_hard([MinHard, MaxHard] = Hard, ChallengerNum, Result) when ChallengerNum > 0 ->
    Rand = urand:rand(MinHard, MaxHard),
    do_race_2_list_add_hard(Hard, ChallengerNum - 1, [Rand|Result]);
do_race_2_list_add_hard(Hard, ChallengerNum, Result) when ChallengerNum > 0 ->
    do_race_2_list_add_hard(Hard, ChallengerNum - 1, [1|Result]);
do_race_2_list_add_hard(_, _, Result) -> Result.

calc_def_average_attr([{_, Attr} | T], AttrSum, Len) ->
    AttrSum1 = lib_player_attr:add_attr(record, [Attr, AttrSum]),
    calc_def_average_attr(T, AttrSum1, Len + 1);
calc_def_average_attr([], AttrSum, Len) ->
    #attr{hp = Hp, att = Att, def = Def, hit = Hit, dodge = Dodge, crit = Crit, ten = Ten} = AttrSum,
    #attr{hp  = round(Hp / Len), att = round(Att / Len), def = round(Def / Len), hit = round(Hit / Len),
        dodge = round(Dodge / Len), crit = round(Crit / Len), ten = round(Ten / Len)}.

%% 把1vN玩家record分别化成擂主和挑战者的record，并且排序
sort_race_2_rank([H | T], DefRank, ChallengerRank) ->
    #kf_1vn_role{id = Id, server_id = ServerId, server_num = ServerNum, figure = #figure{name = Name, guild_name = GName, vip = VipLv, career = Career}, combat_power = CombatPower, server_name = ServerName,
        race_2_side = Race2Side, race_2_lose = Race2Lose, race_2_turn = Race2Turn, race_2_time = Race2Time, score = Score, enter = Enter, result = Result} = H,
    [DefRank1, ChallengerRank1] = case Race2Side of
        ?SIDE_DEF ->
            [[#kf_1vn_def_rank{id = Id, server_id = ServerId, server_num = ServerNum, name = Name, guild_name = GName, vip = VipLv, combat_power = CombatPower, career = Career, server_name = ServerName,
                               score = Score, race_2_lose = Race2Lose, race_2_turn = Race2Turn, race_2_time = Race2Time, enter = Enter} | DefRank], ChallengerRank];
        ?SIDE_CHALLENGER -> [DefRank,
            [#kf_1vn_challenger_rank{id = Id, server_id = ServerId, server_num = ServerNum, name = Name, guild_name = GName, vip = VipLv, combat_power = CombatPower, career = Career,
                                    score = Score, enter = Enter, race_2_turn = Race2Turn, race_2_time = Race2Time, result = Result} | ChallengerRank]];
        _ -> [DefRank, ChallengerRank]
    end,
    sort_race_2_rank(T, DefRank1, ChallengerRank1);
sort_race_2_rank([], DefRank, ChallengerRank) ->
    {sort_def_rank(DefRank), sort_challenger_rank(ChallengerRank)}.
%% 擂主排序（比赛轮数、胜负、花费时间、战力）
sort_def_rank(DefRank) ->
    F = fun(#kf_1vn_def_rank{race_2_turn = Race2Turn1, race_2_time = Race2Time1, combat_power = CombatPower1, race_2_lose = Race2Lose1},
        #kf_1vn_def_rank{race_2_turn = Race2Turn2, race_2_time = Race2Time2, combat_power = CombatPower2, race_2_lose = Race2Lose2}) ->
        if
            Race2Turn1 > Race2Turn2 -> true;
            Race2Turn1 == Race2Turn2 andalso Race2Lose1 < Race2Lose2 -> true;
            Race2Turn1 == Race2Turn2 andalso Race2Lose1 == Race2Lose2 andalso Race2Lose1 == 1 andalso Race2Time1 > Race2Time2 -> true;
            Race2Turn1 == Race2Turn2 andalso Race2Lose1 == Race2Lose2 andalso Race2Lose1 == 0 andalso Race2Time1 < Race2Time2 -> true;
            Race2Turn1 == Race2Turn2 andalso Race2Lose1 == Race2Lose2 andalso Race2Time1 == Race2Time2 andalso CombatPower1 >= CombatPower2 ->
                true;
            true -> false
        end
        end,
    set_race_2_rank_num(lists:sort(F, DefRank), 1, []).
%% 挑战者排序（分数）
sort_challenger_rank(ChallengerRank) ->
    F = fun(#kf_1vn_challenger_rank{result = Result1, race_2_time = Time1}, #kf_1vn_challenger_rank{result = Result2, race_2_time = Time2}) ->
        if
            Result1 > Result2 -> true;
            Result1 == Result2 -> Time1 > Time2;
            true -> false
        end
    end,
    set_race_2_rank_num(lists:sort(F, ChallengerRank), 1, []).
%% 设置排名序号
set_race_2_rank_num([H | T], Rank, Result) ->
    Result1 = case H of
                  #kf_1vn_def_rank{} -> [H#kf_1vn_def_rank{rank = Rank} | Result];
                  #kf_1vn_challenger_rank{} -> [H#kf_1vn_challenger_rank{rank = Rank} | Result];
                  _ -> Result
              end,
    set_race_2_rank_num(T, Rank + 1, Result1);
set_race_2_rank_num([], _, Result) -> lists:reverse(Result).

handle_bet_list([{ServerId, Id, DefIsWin, BetType} | T], Ratio, DefIsWin, BattleId, Area, MatchTurn) ->
    Title = utext:get(6210008),
    Content = utext:get(6210009, [lib_kf_1vn:get_area_name(Area), MatchTurn]),
    BGold = round(data_kf_1vn_m:get_bet_cost(BetType) * (Ratio / 100)),
    lib_kf_1vn:send_mail(ServerId, [Id], Title, Content, [{?TYPE_BGOLD, 0, BGold}]),
    {ok, BinData} = pt_621:write(62123, [BattleId, 1]),
    clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [Id, BinData]),
    handle_bet_list(T, Ratio, DefIsWin, BattleId, Area, MatchTurn);
handle_bet_list([{ServerId, Id, _, _BetType} | T], Ratio, DefIsWin, BattleId, Area, MatchTurn) ->
    Title = utext:get(6210008),
    Content = utext:get(6210010, [lib_kf_1vn:get_area_name(Area), MatchTurn]),
    lib_kf_1vn:send_mail(ServerId, [Id], Title, Content, []),
    {ok, BinData} = pt_621:write(62123, [BattleId, 2]),
    clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [Id, BinData]),
    handle_bet_list(T, Ratio, DefIsWin, BattleId, Area, MatchTurn);
handle_bet_list([], _, _, _, _, _) -> ok.

%% 发送擂主奖励
send_race_2_def_award(DefRank, Area) ->
    FDef = fun(#kf_1vn_def_rank{server_id = ServerId, id = Id, score = Score, enter = Enter, race_2_lose = Race2Lose}, TmpRank) ->
        AllWinAwards = data_kf_1vn:get_value(?KF_1VN_CFG_ALL_WIN_REWARD),
        case lists:keyfind(Area, 1, AllWinAwards) of
            {Area, AllWinReward} -> skip;
            _ -> AllWinReward = []
        end,
        DefAwards = data_kf_1vn:get_score_rank_reward(2, TmpRank),
        case DefAwards of
            [] -> skip;
            _ ->
                Title1 = utext:get(6210003),
                Content1 = utext:get(6210004, [lib_kf_1vn:get_area_name(Area), TmpRank]),
                lib_kf_1vn:send_mail(ServerId, [Id], Title1, Content1, DefAwards)
        end,
        case Enter of
            0 -> skip;
            _ ->
                {ok, BinData} = pt_621:write(62120, [TmpRank, Score, DefAwards]),
                clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [Id, BinData])
        end,
        case Race2Lose of
            0 when AllWinReward =/= [] -> %% 全胜
                Title2 = utext:get(6210024),
                Content2 = utext:get(6210025, [lib_kf_1vn:get_area_name(Area)]),
                lib_kf_1vn:send_mail(ServerId, [Id], Title2, Content2, AllWinReward);
            _ -> skip
        end,
        TmpRank + 1
    end,
    lists:foldl(FDef, 1, DefRank),
    ok.
%% 定时增加经验
add_exp([#kf_1vn_role{server_id = SerId, id = Id, figure = #figure{lv = Lv}, exp = OldExp, enter = Enter, race_2_turn = Turn, race_2_side = Side} = H | T], Result) when Enter =/= 0 ->
    case data_kf_1vn:get_exp(Lv) of
        0 ->
            NewExp = OldExp;
        ExpAdd ->
            case Side of
                ?SIDE_DEF ->
                    RatioL = data_kf_1vn:get_value(?KF_1VN_CFG_EXP_RATIO),
                    case lists:keyfind(Turn, 1, RatioL) of
                        {Turn, Ratio} -> skip;
                        _ -> Ratio = 0
                    end,
                    NewExpAdd = util:ceil(ExpAdd * (1 + Ratio / 100));
                _ -> NewExpAdd = ExpAdd
            end,
            clusters_center:apply_cast(SerId, lib_player, update_player_info, [Id, [{add_exp, {NewExpAdd, kf_1vn}}] ]),
            NewExp = OldExp + NewExpAdd,
            {ok, BinData} = pt_621:write(62122, [NewExp]),
            clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData])
    end,
    add_exp(T, [H#kf_1vn_role{exp = NewExp} | Result]);
add_exp([H | T], Result) -> add_exp(T, [H | Result]);
add_exp(_, Result) -> Result.

%% 根据赛区报名玩家数量来选择擂主数量和种子选手数量
calc_def_num([AreaId | T], SignList, DefNumM, SeedNumM) ->
    Num = length([1 || E <- SignList, E#kf_1vn_role_sign.area == AreaId]),
    [DefNum, SeedNum] = data_kf_1vn:get_def_num(Num),
    calc_def_num(T, SignList, DefNumM#{AreaId => DefNum}, SeedNumM#{AreaId => SeedNum});
calc_def_num([], _SignList, DefNumM, SeedNumM) -> {DefNumM, SeedNumM}.

%% 设置种子选手
set_seed_role_sign([AreaId | T], SignList, SeedNumM) ->
    F = fun(#kf_1vn_role_sign{area = TmpAreaId}) ->
        AreaId == TmpAreaId
        end,
    {AreaSList, NoAreaL} = lists:splitwith(F, SignList),
    SeedNum = maps:get(AreaId, SeedNumM, 0),
    SortSignList = sort_sign_list(AreaSList),
    SetSeedAreaSList = set_seed(SortSignList, 1, SeedNum, []),
    set_seed_role_sign(T, SetSeedAreaSList ++ NoAreaL, SeedNumM);
set_seed_role_sign([], SignList, _) -> SignList.

%% 按照战力排序
sort_sign_list(SignList) ->
    F = fun(#kf_1vn_role_sign{combat_power = CP1}, #kf_1vn_role_sign{combat_power = CP2}) ->
        CP1 >= CP2
        end,
    lists:sort(F, SignList).

set_seed([H | T] = SignList, Index, SeedNum, Result) ->
    case Index > SeedNum of
        true  -> Result ++ SignList;
        false -> set_seed(T, Index + 1, SeedNum, [H#kf_1vn_role_sign{race_1_seed = 1} | Result])
    end;
set_seed([], _, _, Result) -> Result.

%% 根据战力对报名玩家排序
init_sorted_sign_list(SignList) ->
    L = lists:sublist(lists:reverse(lists:keysort(#kf_1vn_role_sign.combat_power, SignList)), 100),
    [{Id, Name, ServerName, Power} || #kf_1vn_role_sign{id = Id, name = Name, server_name = ServerName, combat_power = Power} <- L].
