%%%-----------------------------------
%%% @Module  : mod_kf_1vn_pk
%%% @Email   : ming_up@foxmail.com
%%% @Created : 2018.1.24
%%% @Description: 跨服1vN pk管理进程
%%%-----------------------------------
-module(mod_kf_1vn_pk).

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

%% API
-export([start_link/1, get_watch_info/3]).

-export([dummy_start_battle/2]).

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

start_link(Args) ->
    gen_server:start_link(?MODULE, Args, []).

get_watch_info(ServerId, Id, CopyId) ->
    gen_server:cast(CopyId, {get_watch_info, ServerId, Id}).

init([SideA, SideBList, [Now, LoadingTime, BattleTime], Stage, MatchTurn, BattleId, ScenePoolId, MaxPower]) ->
    Pid = self(),
    Ref = erlang:start_timer(?KF_1VN_WAITING_TIME * 1000, Pid, wait_timeout),
    %% 传入战场
    case Stage of
        ?KF_1VN_RACE_1 -> %% 如果是资格赛
            [{SideAX, SideAY}, BXYs|_] = data_kf_1vn:get_value(?KF_1VN_CFG_RACE_1_XY),
            SideBXYs = [BXYs],
            SceneId = data_kf_1vn_m:get_config(race_1_scene);
        _ ->
            [{SideAX, SideAY}] = data_kf_1vn:get_value(?KF_1VN_CFG_DEF_XY),
            SideBXYs = data_kf_1vn:get_value(?KF_1VN_CFG_CHALLENGER_XY),
            SceneId = data_kf_1vn_m:get_config(race_2_scene)
    end,
    %% A方玩家传入场景
    clusters_center:apply_cast(SideA#kf_1vn_role_pk.server_id, lib_kf_1vn, pull_into_pk_scene, [SideA#kf_1vn_role_pk.id, SceneId, ScenePoolId, Pid, SideAX, SideAY, [{group,1},{forbid_pk_etime, Now + ?KF_1VN_WAITING_TIME}]]),
    InitSideBList = send_side_b(SideBList, SideBXYs, SceneId, ScenePoolId, Pid, []),
    case Stage of
        ?KF_1VN_RACE_1 -> %% 如果是资格赛，发送双方的战斗信息
            [InitSideB|_] = InitSideBList,
            {ok, BinData} = pt_621:write(62105, [
                [
                    to_62105_battle(SideA),
                    to_62105_battle(InitSideB)
                ],
                Now + LoadingTime, Now + LoadingTime + BattleTime
            ]),
            [clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]) || #kf_1vn_role_pk{server_id = SerId, id = Id} <- [SideA, InitSideB]];
        _ -> skip
    end,
    State = #pk_state{
        stage=Stage,
        battle_id=BattleId,
        optime=Now,
        scene_id=SceneId,
        scene_pool_id=ScenePoolId,
        area=SideA#kf_1vn_role_pk.area,
        side_a=SideA,
        side_b_list=InitSideBList,
        battle_time=BattleTime,
        match_turn=MatchTurn,
        max_power = MaxPower,
        ref = Ref,
        loading_time = LoadingTime
    },
    {ok, State}.

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

do_handle_call(_Request, _State) ->
    ?ERR("do_handle_call 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 cast[~p] error:~p~n", [Msg, Err]),
            {noreply, State}
    end.

%% 观战信息
do_handle_cast({get_watch_info, ServerId, Id}, State) ->
    BinData = pack_race_2_info(State),
    clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [Id, BinData]),
    {noreply, State};
do_handle_cast(_Msg, State) ->
    ?ERR("do_handle_cast unkown msg[~p]~n", [_Msg]),
    {noreply, State}.

handle_info(Info, State) ->
    case catch do_handle_info(Info, State) of
        {noreply, NewState} ->
            {noreply, NewState};
        {stop, Reason, NewState} ->
            {stop, Reason, NewState};
        Err ->
            ?ERR("Handle info[~p] error:~p~n", [Info, Err]),
            {noreply, State}
    end.
%% 所有玩家进入，等待开始战斗
do_handle_info({timeout, Ref, wait_timeout}, #pk_state{ready_time = 0, stage = Stage} = State) ->
    #pk_state{side_a = SideA, side_b_list = SideBList, battle_time = _BattleTime,
        scene_id = SceneId, scene_pool_id = ScenePoolId, optime = OpTime, loading_time = LoadingTime
    } = State,
    Now = utime:unixtime(),
    Delay = max(OpTime + LoadingTime - Now, 0),
    Ref2 = util:send_after(Ref, Delay * 1000+50, self(), start),
    [mod_scene_agent:apply_cast(SceneId, ScenePoolId, ?MODULE, dummy_start_battle, [DummyId, Delay]) || #kf_1vn_role_pk{type = ?KF_1VN_C_TYPE_ROBOT, id = DummyId} <- SideBList],
    NewState = State#pk_state{ref = Ref2, ready_time = Now},

    [begin
        clusters_center:apply_cast( %% 设置禁止战斗的时间
            E2#kf_1vn_role_pk.server_id,
            lib_player,
            update_player_info,
            [E2#kf_1vn_role_pk.id, [{forbid_pk_etime, Now + Delay}]]
        ),
        case Stage of
            ?KF_1VN_RACE_2 -> %% 发送守擂赛的数据
                BinData = pack_race_2_info(NewState),
                clusters_center:apply_cast(
                    E2#kf_1vn_role_pk.server_id,
                    lib_server_send,
                    send_to_uid,
                    [E2#kf_1vn_role_pk.id, BinData]
                );
            _ -> skip
        end
     end|| E2 <- [SideA|SideBList], E2#kf_1vn_role_pk.type == ?KF_1VN_C_TYPE_PLAYER],
    {noreply, NewState};

do_handle_info({timeout, Ref, wait_timeout}, State) ->
    ?ERR("wait_timeout ~p~n", [[Ref, State#pk_state.optime, utime:unixtime()]]),
    {noreply, State};

%% 战斗开始
do_handle_info(start, State) ->
    #pk_state{battle_time=BattleTime, side_a = SideA, side_b_list = SideBList, optime = Optime, loading_time = LoadingTime} = State,
    CopyId = self(),
    StartTime = utime:unixtime(),
    [
        case Enter of %% 不在场景中的算退出场景
            0 ->
                Node = clusters_center:find_node(ServerId),
                mod_kf_1vn:quit(Node, Id, CopyId, 0);
            _ ->
                skip
        end
    || #kf_1vn_role_pk{server_id = ServerId, id = Id, enter = Enter} <- [SideA|SideBList]],
    Delay = Optime + LoadingTime + BattleTime - StartTime,
    Ref = erlang:send_after(Delay*1000+50, CopyId, battle_timeout),
    {noreply, State#pk_state{ref=Ref, start_time=StartTime}};

%% 玩家传送进场景处理完毕
do_handle_info({role_enter_finish, RoleId}, State) ->
    #pk_state{side_a = SideA, side_b_list = SideBList, ref = Ref} = State,
    #kf_1vn_role_pk{id = SideAId, enter = SideAEnter} = SideA,
    if
        SideAId == RoleId ->
            NewSideA = ?IF(SideAEnter == 0, SideA#kf_1vn_role_pk{enter = ?KF_1VN_ENTER}, SideA),
            NewSideBList = SideBList;
        true ->
            case lists:keyfind(RoleId, #kf_1vn_role_pk.id, SideBList) of
                #kf_1vn_role_pk{enter = 0} = B ->
                    NewSideBList = lists:keystore(RoleId, #kf_1vn_role_pk.id, SideBList, B#kf_1vn_role_pk{enter = ?KF_1VN_ENTER});
                _ ->
                    NewSideBList = SideBList
            end,
            NewSideA = SideA
    end,
    case lists:all(fun(#kf_1vn_role_pk{enter = E}) -> E =/= 0 end, [NewSideA|NewSideBList]) of
        true -> %% 当所有的玩家都进入比赛时，才开始战斗
            util:cancel_timer(Ref),
            NewState = State#pk_state{side_a = NewSideA, side_b_list = NewSideBList, ref = all_enter},
            self() ! {timeout, all_enter, wait_timeout},
            {noreply, NewState};
        _ ->
            {noreply, State#pk_state{side_a = NewSideA, side_b_list = NewSideBList}}
    end;

%% 玩家退出
do_handle_info({quit, Node, Id, ScenePoolId, QuitType}, #pk_state{is_end=IsEnd, stage = Stage, side_a = #kf_1vn_role_pk{id = IdA}} = State0) ->
    State = set_player_quit(Id, State0),
    if
        QuitType == 1 -> %% 主动退出
            Args = [{group,0}, {change_scene_hp_lim,1}],
            if  %% 判断退出场景,设置act_label
                Stage >= ?KF_1VN_RACE_1_PRE andalso Stage < ?KF_1VN_RACE_2_PRE -> %% 资格赛
                    NewArgs = Args,
                    RacePreScene = data_kf_1vn_m:get_config(race_1_pre_scene),
                    NewScenePoolId = ScenePoolId;
                Stage >= ?KF_1VN_RACE_2_PRE andalso Stage =< ?KF_1VN_END -> %% 挑战赛
                    NewArgs = ?IF(IdA == Id andalso IsEnd =/= 1, [{act_label, [{?ACT_LABEL_KF_1VN_SIDE, 0}]}|Args], Args),%% 擂主在比赛结束前退出,算失败,修改头顶标志
                    RacePreScene = data_kf_1vn_m:get_config(race_2_pre_scene),
                    NewScenePoolId = ScenePoolId;
                true -> NewArgs = Args, RacePreScene = 0, NewScenePoolId = 0
            end,
            mod_kf_1vn:enter_pre_scene(Node, Id, RacePreScene, NewScenePoolId, NewArgs);
        true ->
            lib_kf_1vn:quit_to_main_scene(Node, Id)
    end ,
    case IsEnd of
        1 ->
            {noreply, State};
        _ ->  %% 比赛尚未结束时退出，则立即结算
            case pk_result(State, ?KF_1VN_C_TYPE_PLAYER, Id, 0, 3) of
                false -> {noreply, State};
                {false, SideBList} -> {noreply, State#pk_state{side_b_list=SideBList}};
                {true, WinSide} ->
                    util:cancel_timer(State#pk_state.ref),
                    self() ! {battle_end, 3}, %% 直接退出
                    {noreply, State#pk_state{is_end=1, ref = undefined, result = WinSide}}
            end
    end;

%% 玩家死亡
do_handle_info({player_die, DieId}, State) ->
    #pk_state{ref=Ref, is_end = IsEnd} = State,
    if
        IsEnd == 0 ->
            case pk_result(State, ?KF_1VN_C_TYPE_PLAYER, DieId, 0, 1) of
                false -> {noreply, State};
                {false, SideBList} -> {noreply, State#pk_state{side_b_list=SideBList}};
                {true, WinSide} ->
                    util:send_after(Ref, 5000, self(), {battle_end, 1}),
                    {noreply, State#pk_state{is_end=1, result = WinSide}}
            end;
        true ->
            {noreply, State}
    end;

do_handle_info({dummy_die, DieId}, State) ->
    #pk_state{ref=Ref, is_end = IsEnd} = State,
    if
        IsEnd == 0 ->
%%            ?PRINT("dummy_die ~w~n", [DieId]),
            case pk_result(State, ?KF_1VN_C_TYPE_ROBOT, DieId, 0, 1) of
                false -> {noreply, State};
                {false, SideBList} -> {noreply, State#pk_state{side_b_list=SideBList}};
                {true, WinSide} ->
                    util:send_after(Ref, 5000, self(), {battle_end, 1}),
                    {noreply, State#pk_state{is_end=1, result = WinSide}}
            end;
        true ->
            {noreply, State}
    end;

do_handle_info(battle_timeout, State) ->
    #pk_state{stage=Stage, scene_id=SceneId, scene_pool_id=ScenePoolId, side_a=SideA, side_b_list=SideBList, ref=Ref} = State,
    util:cancel_timer(Ref),
    case Stage of
        ?KF_1VN_RACE_1 ->
            #kf_1vn_role_pk{id=IdA, combat_power=CPA} = SideA,
            HpLeftRA = case mod_scene_agent:apply_call(SceneId, ScenePoolId, lib_scene_agent, get_user, [IdA]) of
                #ets_scene_user{battle_attr=#battle_attr{hp=HpA, hp_lim=HpLimA}} -> HpA/max(1, HpLimA);
                _ -> 0
            end,
            [#kf_1vn_role_pk{id=IdB, combat_power=CPB}|_] = SideBList,
            HpLeftRB = case mod_scene_agent:apply_call(SceneId, ScenePoolId, lib_scene_agent, get_user, [IdB]) of
                #ets_scene_user{battle_attr=#battle_attr{hp=HpB, hp_lim=HpLimB}} -> HpB/max(1, HpLimB);
                _ -> 0
            end,

            DieId = if
                HpLeftRA > HpLeftRB -> IdB;
                HpLeftRA == HpLeftRB andalso CPA > CPB -> IdB;
                true -> IdA
            end,

            case pk_result(State, ?KF_1VN_C_TYPE_PLAYER, DieId, 0, 2) of
                false -> {noreply, State};
                {false, SideBList} -> {noreply, State#pk_state{side_b_list=SideBList}};
                {true, WinSide} ->
                    erlang:send_after(5000, self(), {battle_end, 1}),
                    {noreply, State#pk_state{is_end=1, result = WinSide}}
            end;
         ?KF_1VN_RACE_2 ->
             #kf_1vn_role_pk{id=IdA} = SideA,
             WinType = case mod_scene_agent:apply_call(SceneId, ScenePoolId, lib_scene_agent, get_user, [IdA]) of
                #ets_scene_user{} -> 1; %% 擂主还在场景中，直接胜利
                _ -> 2 %% 擂主不在场景中，直接失败
            end,
             case pk_result(State, 0, 0, WinType, 2) of
                 false -> {noreply, State};
                 {false, SideBList} -> {noreply, State#pk_state{side_b_list=SideBList}};
                 {true, WinSide} ->
                     erlang:send_after(5000, self(), {battle_end, 1}),
                     {noreply, State#pk_state{is_end=1, result = WinSide}}
             end;
         _ ->
             erlang:send_after(5000, self(), {battle_end, 1}),
             {noreply, State#pk_state{is_end=1}}
     end;

%% 战斗结束 EndType：3 玩家退出，1 正常结算
do_handle_info({battle_end, _EndType}, State) ->
    #pk_state{stage = Stage, scene_id=SceneId, scene_pool_id=ScenePoolId, side_a=SideA, side_b_list=SideBList, ref=Ref, result = WinSide} = State,
    IdA = SideA#kf_1vn_role_pk.id,
    util:cancel_timer(Ref),
    Args = [{group,0}, {change_scene_hp_lim,1}],
    if
        Stage >= ?KF_1VN_RACE_1_PRE andalso Stage < ?KF_1VN_RACE_2_PRE ->
            NewArgs = Args,
            ReadyScene = data_kf_1vn_m:get_config(race_1_pre_scene);
        Stage >= ?KF_1VN_RACE_2_PRE andalso Stage =< ?KF_1VN_END ->
            NewArgs = ?IF(WinSide =:= ?KF_1VN_WIN_SIDE_A, [{act_label, [{?ACT_LABEL_KF_1VN_SIDE, 1}]}|Args], [{act_label, [{?ACT_LABEL_KF_1VN_SIDE, 0}]}|Args]),
            ReadyScene = data_kf_1vn_m:get_config(race_2_pre_scene);
        true ->
            NewArgs = Args,
            ReadyScene = 0
    end,
    Now = utime:unixtime(),
    %% 传出战场
    F = fun(#kf_1vn_role_pk{type=Type, area=Area, server_id=SerId, id=Id, scene_pool_id=RolePoolId, enter = Enter}) ->
            case Type of
                ?KF_1VN_C_TYPE_PLAYER -> %% 玩家
                    if
                        Enter =/= ?KF_1VN_QUIT -> %% 未退出
                            LastArgs = ?IF(Id == IdA, NewArgs, Args),
                            mod_kf_1vn:enter_pre_scene(SerId, Id, ReadyScene, RolePoolId, LastArgs);
                            % {PreX, PreY} = lib_kf_1vn:pre_scene_xy(),
                            % clusters_center:apply_cast(SerId, lib_scene, change_player_scene, [Id, ReadyScene, RolePoolId, 0, PreX, PreY, LastArgs]);
                        true ->
                            ok
                    end,
                    mod_kf_1vn:update_role(Area, Id, [{pk, 0}, {pk_time, Now}]),
                    mod_kf_1vn:exit_pk(Area, Id);
                _ ->
                    skip
            end
    end,
    lists:map(F, [SideA|SideBList]),
    %% 清理场景
    lib_scene:clear_scene_room(SceneId, ScenePoolId, self()),
    {stop, normal, State};

%% 获取资格赛的比赛信息
do_handle_info({get_race_1_info, Node, Id}, #pk_state{stage = ?KF_1VN_RACE_1} = State) ->
    #pk_state{optime = Now, battle_time = BattleTime, side_a = SideA, side_b_list = [SideB|_], loading_time = LoadingTime, ready_time = ReadyTime} = State,
    if
        ReadyTime > 0 ->
            {ok, BinData} = pt_621:write(62105, [
                [
                    to_62105_battle(SideA),
                    to_62105_battle(SideB)
                ],
                Now+LoadingTime, Now+LoadingTime+BattleTime
            ]);
        true ->
            {ok, BinData} = pt_621:write(62105, [
                [
                    to_62105_battle(SideA),
                    to_62105_battle(SideB)
                ],
                0, 0
            ])
    end ,
    lib_server_send:send_to_uid(Node, Id, BinData),
    {noreply, State};

%% 获取守擂赛的比赛信息
do_handle_info({get_race_2_info, Node, Id}, #pk_state{stage = ?KF_1VN_RACE_2} = State) ->
    BinData = pack_race_2_info(State),
    lib_server_send:send_to_uid(Node, Id, BinData),
    {noreply, State};


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

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

%% 处理B方的数据
send_side_b([#kf_1vn_role_pk{type=?KF_1VN_C_TYPE_ROBOT, server_num=_SerNum, career=Career, attr=MonAttr, name=Name, sex = Sex, combat_power = CP, lv = Lv}=H|T], [{Bx, By}|Txy], SceneId, ScenePoolId, Room, Result) ->
    Turn = 3,
    Skill =  lib_skill_api:get_career_active_skill_ids(Career, Sex),
    Figure = #figure{name=Name, lv=Lv, career=Career, sex=Sex, lv_model=lib_player:get_model_list(Career, Sex, Turn, Lv)},
    DieHandler = {lib_kf_1vn, dummy_die, []},
    Args = [{skill, Skill}, {group, 2},{die_handler, DieHandler},{warning_range, 30000}, {combat_power, CP}],
    BattleAttr = #battle_attr{hp=MonAttr#attr.hp, hp_lim=MonAttr#attr.hp, speed=?SPEED_VALUE, attr=MonAttr#attr{speed=?SPEED_VALUE}},
    DummyId = lib_scene_object:sync_create_a_dummy(SceneId, ScenePoolId, Bx, By, Room, 1, Figure, BattleAttr, Args),
    send_side_b(T, Txy, SceneId, ScenePoolId, Room, [H#kf_1vn_role_pk{id=DummyId, enter = ?KF_1VN_ENTER} |Result]);
send_side_b([#kf_1vn_role_pk{type=?KF_1VN_C_TYPE_PLAYER, id=Id, server_id=ServerId, is_dead=0}=H|T], [{Bx, By}|Txy], SceneId, ScenePoolId, Room, Result) ->
    clusters_center:apply_cast(ServerId, lib_kf_1vn, pull_into_pk_scene, [Id, SceneId, ScenePoolId, Room, Bx, By, [{group, 2},{forbid_pk_etime, utime:unixtime() + ?KF_1VN_WAITING_TIME}]]),
    send_side_b(T, Txy, SceneId, ScenePoolId, Room, [H |Result]);
send_side_b([H|T], Txy, SceneId, ScenePoolId, Room, Result) ->
    send_side_b(T, Txy, SceneId, ScenePoolId, Room, [H |Result]);
send_side_b([], _, _, _, _, Result) -> Result.

%% 计算胜负积分
calc_score(WinCP, LoseCP, _MaxPower) ->
    WinScore = data_kf_1vn:get_value(?KF_1VN_CFG_WINNER_SCORE) + data_kf_1vn:get_extra_score(WinCP),
    LoseScore = data_kf_1vn:get_value(?KF_1VN_CFG_LOSER_SCORE) + data_kf_1vn:get_extra_score(LoseCP),
    {WinScore, LoseScore}.

%% Type   : 死亡的角色是玩家还是机器人
%% Id     : 死亡的玩家Id
%% WinType: 0需判定 1擂主直接胜利 2擂主直接失败
%% EndType: 胜利方式 1对手死亡 2时间结束 3 对方退出
pk_result(State, Type, Id, WinType, EndType) ->
    #pk_state{battle_id=BattleId, stage=Stage, area=Area, side_a=SideA, side_b_list=SideBList, match_turn=MatchTurn, start_time=StartTime, max_power = MaxPower} = State,
    #kf_1vn_role_pk{server_id=SerIdA, server_num=SerNumA, area=AreaA, id=IdA, name=NameA, win=WinA, win_streak=WinStreakA,
        combat_power=CPA, score=ScoreA, race_1_times=Race1TimesA} = SideA, %% 擂主或者资格赛的A方

    case Stage of
        ?KF_1VN_RACE_1 -> %% 资格赛
            Race1MaxTimes = data_kf_1vn_m:get_config(race_1_max_times),
            [#kf_1vn_role_pk{server_id=SerIdB, server_num=SerNumB, area=AreaB, id=IdB, win=WinB, win_streak=WinStreakB, name=NameB,
                    combat_power=CPB, score=ScoreB, race_1_times=Race1TimesB}|_] = SideBList,

            case Id of
                IdA -> %% 死亡的是A方，则B方玩家胜出
                    IsWinA = 0, IsWinB = 1,
                    {ScoreAddB, ScoreAddA} = calc_score(CPB, CPA, MaxPower),
                    WinAN = WinA, WinBN = WinB+1,
                    WinStreakAN = 0, WinStreakBN = WinStreakB+1;
                _ ->
                    IsWinA = 1, IsWinB = 0,
                    {ScoreAddA, ScoreAddB} = calc_score(CPA, CPB, MaxPower),
                    WinAN = WinA+1, WinBN = WinB,
                    WinStreakAN = WinStreakA+1, WinStreakBN = 0
            end,
            %% 通知双方比赛结果
            {ok, BinDataA} = pt_621:write(62108, [IsWinA, ScoreA, ScoreAddA, max(0, Race1MaxTimes-Race1TimesA)]),
            clusters_center:apply_cast(SerIdA, lib_server_send, send_to_uid, [IdA, BinDataA]),
            {ok, BinDataB} = pt_621:write(62108, [IsWinB, ScoreB, ScoreAddB, max(0, Race1MaxTimes-Race1TimesB)]),
            clusters_center:apply_cast(SerIdB, lib_server_send, send_to_uid, [IdB, BinDataB]),
            %% 更新双方的分数和比赛数据
            mod_kf_1vn:update_role(AreaA, IdA, [{win, WinAN}, {win_streak, WinStreakAN}, {score, ScoreA+ScoreAddA}]),
            mod_kf_1vn:update_role(AreaB, IdB, [{win, WinBN}, {win_streak, WinStreakBN}, {score, ScoreB+ScoreAddB}]),
            %% 判断是否增加活跃度
            ?IF(Race1TimesA =< 1, clusters_center:apply_cast(SerIdA, lib_activitycalen_api, role_success_end_activity, [IdA, ?MOD_KF_1VN, 0]), skip),
            ?IF(Race1TimesB =< 1, clusters_center:apply_cast(SerIdB, lib_activitycalen_api, role_success_end_activity, [IdB, ?MOD_KF_1VN, 0]), skip),

            %% 日志
            case IsWinA of
                1 -> lib_log_api:log_kf_1vn_race_1(Area, IdA, SerIdA, SerNumA, NameA, CPA, ScoreAddA, IdB, SerIdB, SerNumB, NameB, CPB, ScoreAddB, EndType);
                _ -> lib_log_api:log_kf_1vn_race_1(Area, IdB, SerIdB, SerNumB, NameB, CPB, ScoreAddB, IdA, SerIdA, SerNumA, NameA, CPA, ScoreAddA, EndType)
            end,
            {true, ?IF(IsWinA == 1, ?KF_1VN_WIN_SIDE_A, ?KF_1VN_WIN_SIDE_B)};
        ?KF_1VN_RACE_2 -> %% 守擂赛
            ChallengerLiveNum = length([1 || E <- SideBList, E#kf_1vn_role_pk.is_dead==0]), %% 挑战者存活数量
            LiveTime = utime:unixtime() - StartTime, %% 存活时间
            AreaName = lib_kf_1vn:get_area_name(AreaA),
            %% 胜负奖励
            case data_kf_1vn:get_race_2_award(1, MatchTurn) of %% 守擂者
                {DefWinRewards, DefLoseRewards} -> skip;
                _ -> DefWinRewards = [], DefLoseRewards = []
            end,
            case data_kf_1vn:get_race_2_award(2, MatchTurn) of %% 挑战者
                {ChallengerWinAward, ChallengerLoseAward} -> skip;
                _ -> ChallengerWinAward = [], ChallengerLoseAward = []
            end,

            if
                WinType == 2 orelse Id == IdA -> %% 擂主直接失败
                    %% 更新双方玩家活动数据
                    [if %% 挑战者方
                        E#kf_1vn_role_pk.is_dead == 0 -> %% 依旧存活的玩家更新存活时间和结果
                            mod_kf_1vn:update_role(Area, E#kf_1vn_role_pk.id, [{win, max(E#kf_1vn_role_pk.win + 1, MatchTurn)}, {race_2_time, LiveTime}, {result, ?KF_1VN_WIN}]);
                        true -> %% 死掉的玩家只更新结果
                            mod_kf_1vn:update_role(Area, E#kf_1vn_role_pk.id, [{win, max(E#kf_1vn_role_pk.win + 1, MatchTurn)}, {result, ?KF_1VN_WIN}])
                     end
                     || E <- SideBList],
                    mod_kf_1vn:update_role(Area, IdA, [{race_2_lose, 1}, {race_2_time, LiveTime}, {result, ?KF_1VN_LOSE}]),
                    %% 发放擂主胜负奖励
                    lib_kf_1vn:send_mail(SerIdA, [IdA], utext:get(6210005), utext:get(6210007, [AreaName, MatchTurn]),  DefLoseRewards),
                    %% 同步战斗结果
                    mod_kf_1vn:race_2_pk_result(BattleId, 0, Area, IdA, CPA),
                    %% 发送结果和奖励给双方玩家
                    ChallengerProduce = #produce{type=kf_1vn_race_2, show_tips=1, reward=ChallengerWinAward, title = utext:get(6210005), content = utext:get(6210006, [AreaName, MatchTurn])},
                    {ok, BinDataA} = pt_621:write(62113, [0, ChallengerLiveNum, DefLoseRewards]),
                    clusters_center:apply_cast(SerIdA, lib_server_send, send_to_uid, [IdA, BinDataA]),
                    {ok, BinDataB} = pt_621:write(62113, [1, ChallengerLiveNum, ChallengerWinAward]),
                    handle_side_b_result(SideBList, ChallengerProduce, BinDataB, AreaA, BattleId, MatchTurn, 1, EndType),
                    %% 日志
                    lib_log_api:log_kf_1vn_race_2(AreaA, MatchTurn, BattleId, IdA, SerIdA, SerNumA, NameA, CPA, 1, 0, EndType, 1),
                    {true, ?KF_1VN_WIN_SIDE_B};
                true ->
                    %% 更新死亡玩家的存活时间
                    mod_kf_1vn:update_role(Area, Id, [{race_2_time, LiveTime}]),
                    Result = case WinType of
                        1 -> true;
                        _ -> is_side_b_dead(SideBList, Type, Id, ChallengerLiveNum)
                    end,
                    case Result of
                        false -> false;
                        {false, Role} -> {false, lists:keyreplace(Role#kf_1vn_role_pk.id, #kf_1vn_role_pk.id, SideBList, Role)};
                        true ->  %% 擂主胜利
                            [mod_kf_1vn:update_role(Area, E#kf_1vn_role_pk.id, [{result, ?KF_1VN_LOSE}]) || E <- SideBList],
                            {ok, BinDataA} = pt_621:write(62113, [1, max(ChallengerLiveNum - 1, 0), DefWinRewards]),
                            clusters_center:apply_cast(SerIdA, lib_server_send, send_to_uid, [IdA, BinDataA]),
                            lib_kf_1vn:send_mail(SerIdA, [IdA], utext:get(6210005), utext:get(6210006, [AreaName, MatchTurn]),  DefWinRewards),
                            mod_kf_1vn:update_role(Area, IdA, [{race_2_time, LiveTime}]),

                            {ok, BinDataB} = pt_621:write(62113, [0, ChallengerLiveNum, ChallengerLoseAward]),
                            ChallengerProduce = #produce{type=kf_1vn_race_2, show_tips=1, reward=ChallengerLoseAward, title = utext:get(203), content = utext:get(204)},
                            lib_log_api:log_kf_1vn_race_2(AreaA, MatchTurn, BattleId, IdA, SerIdA, SerNumA, NameA, CPA, 1, 1, EndType, 0),
                            handle_side_b_result(SideBList, ChallengerProduce, BinDataB, AreaA, BattleId, MatchTurn, 0, EndType),
                            mod_kf_1vn:race_2_pk_result(BattleId, 1, Area, IdA, CPA),
                            {true, ?KF_1VN_WIN_SIDE_A}
                    end
            end;
        _ -> false
    end.

%% 挑战者比赛结果处理
handle_side_b_result([#kf_1vn_role_pk{type=?KF_1VN_C_TYPE_PLAYER, id=Id, server_id=ServerId, server_num=SerNum, name=Name, combat_power=CP, is_dead=IsDead}|T], Produce, BinData, Area, BattleId, MatchTurn, SidBWin, EndType) ->
    clusters_center:apply_cast(ServerId, lib_server_send, send_to_uid, [Id, BinData]),
    mod_kf_1vn:update_role(Area, Id, [{match_challenger, 0}]),
    #produce{reward=ChallengerLoseAward, title = Title, content = Content} = Produce,
    lib_kf_1vn:send_mail(ServerId, [Id], Title, Content, ChallengerLoseAward),
    lib_log_api:log_kf_1vn_race_2(Area, MatchTurn, BattleId, Id, ServerId, SerNum, Name, CP, 0, SidBWin, EndType, IsDead),
    handle_side_b_result(T, Produce, BinData, Area, BattleId, MatchTurn, SidBWin, EndType);
handle_side_b_result([#kf_1vn_role_pk{name=Name, combat_power=CP, is_dead=IsDead}|T], Produce, BinData, Area, BattleId, MatchTurn, SidBWin, EndType) ->
    lib_log_api:log_kf_1vn_race_2(Area, MatchTurn, BattleId, 0, 0, 0, Name, CP, 2, SidBWin, EndType, IsDead),
    handle_side_b_result(T, Produce, BinData, Area, BattleId, MatchTurn, SidBWin, EndType);
handle_side_b_result([], _, _, _, _, _, _, _) -> ok.


is_side_b_dead([#kf_1vn_role_pk{type=Type, id=Id, is_dead=0}|_], Type, Id, 1) ->
    true;
is_side_b_dead([#kf_1vn_role_pk{type=Type, id=Id, is_dead=0}=H|_], Type, Id, _ChallengerLiveNum) ->
    {false, H#kf_1vn_role_pk{is_dead=1}};
is_side_b_dead([_|T], Type, Id, ChallengerLiveNum) -> is_side_b_dead(T, Type, Id, ChallengerLiveNum);
is_side_b_dead([], _Type, _Id, _ChallengerLiveNum) -> false.

%% 玩家退出数据处理
set_player_quit(Id, #pk_state{side_a = #kf_1vn_role_pk{id = Id} = SideA} = State) ->
    State#pk_state{side_a = SideA#kf_1vn_role_pk{enter = ?KF_1VN_QUIT}};
set_player_quit(Id, #pk_state{side_b_list = SideBList} = State) ->
    case lists:keyfind(Id, #kf_1vn_role_pk.id, SideBList) of
        #kf_1vn_role_pk{enter = Enter} = R when Enter =/= ?KF_1VN_QUIT ->
            SideBList1 = lists:keystore(Id, #kf_1vn_role_pk.id, SideBList, R#kf_1vn_role_pk{enter = ?KF_1VN_QUIT}),
            State#pk_state{side_b_list = SideBList1};
        _ ->
            State
    end.

%% 打包守擂赛的信息
pack_race_2_info(State) ->
    #pk_state{side_a=SideA, side_b_list=SideBList, match_turn = MatchTurn, ready_time=ReadyTime, battle_time=BattleTime, loading_time = LoadingTime, optime = OpTime} = State,
    if
        ReadyTime > 0 ->
            LoadEndTime = OpTime+LoadingTime,
            BattleEndTime = LoadEndTime + BattleTime;
        true ->
            LoadEndTime = 0, BattleEndTime = 0
    end,
    SendL = [{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.combat_power, E#kf_1vn_role_pk.picture, E#kf_1vn_role_pk.picture_ver, E#kf_1vn_role_pk.win, max(0, MatchTurn - E#kf_1vn_role_pk.win - 1)} || E <- SideBList],
    {ok, BinData} = pt_621:write(62112, [
        SideA#kf_1vn_role_pk.id, SideA#kf_1vn_role_pk.server_num, SideA#kf_1vn_role_pk.name, SideA#kf_1vn_role_pk.career, SideA#kf_1vn_role_pk.turn,SideA#kf_1vn_role_pk.sex,
        SideA#kf_1vn_role_pk.combat_power, MatchTurn - 1, 0, SideA#kf_1vn_role_pk.picture, SideA#kf_1vn_role_pk.picture_ver,
        SendL, LoadEndTime, BattleEndTime
    ]),
    BinData.

%% 通知假人开始战斗
dummy_start_battle(Id, Time) ->
    case lib_scene_object_agent:get_object(Id) of
        #scene_object{aid = Aid, id = MonId} ->
            % old: Aid ! {'change_attr', [{find_target, Time*1000 + 500}]};
            mon_mgr:send(Aid, MonId, {'change_attr', [{find_target, Time*1000 + 500}]});
        _ ->
            skip
    end.

to_62105_battle(#kf_1vn_role_pk{id = Id, server_num = SerNum, server_name = _SerName, name = Name, career = Career, combat_power = Power, win = Win, race_1_times = RaceTimes, sex = Sex, turn = Turn, picture = Pic, picture_ver = PicVer}) ->
    {Id, SerNum, Name, Career, Power, Win, RaceTimes - Win - 1, Sex, Turn, Pic, PicVer}.
