%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     狂战领域活动逻辑通用处理：跨服本服都会走这里
%%% @end
%%%-------------------------------------------------------------------
-module(madness_land_act).
-author("liushl").

-include("madness_land.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("def_act_label.hrl").
-include("clusters.hrl").
-include("predefine.hrl").
-include("goods.hrl").
-include("server.hrl").
-include("def_module.hrl").
-include("scene.hrl").
-include("figure.hrl").
-include("mail.hrl").
-include("skill.hrl").
%% API
-export([
    %% 通用逻辑处理
    handle/3
    %% 拉玩家进入第二个活动场景
    , pull_role_into_round2/1
    , scene_pull_role_into_round2/1
    %% 循环给场景内玩家加积分
    , cycle_add_score/2
    , scene_cycle_add_score/1
    %% 加分的时候判断是否给突破奖励
    , check_send_score_reward/4
    , send_score_reward/2
    %% 出小怪
    , born_mon/1
    , when_mon_die/5
    %% 清理场景怪物
    , clean_round_boss/2
    %% 刷新榜单
    , refresh_rank/2
    %% 更新玩家头顶标识
    , role_update_act_label/3
    , scene_update_server_role_act_label/3
    %% 关闭活动
    , close_act/1
    %% 结算
    , settlement_local/6
    , settlement_center/6
    , settlement_round2_in_server/2
    , settlement_online/5
    , send_reward_do/3
    %% 全服邮件/
    , send_server_reward_mail/2
    , send_reward_mail_in_server/1
    %% 结束传闻
    , broadcast_top_guild_in_server/2
    , broadcast_top_role/2
    , broadcast_top_role_in_server/3
    %% 保存榜单
    , save_rank_data/2
]).

%% 请求进入场景-成功后写入数据
handle({enter_scene, RoleInfo, GuildFlag}, State, #madness_land_act{} = ActData) ->
    #madness_land_act{
        role_grade = RoleGrade,           %% 个人成绩
        guild_grade = GuildGrade
    } = ActData,
    #madness_land_role{
        role_id = RoleId,
        node = Node,
        guild_id = GuildId,
        guild_name = GuildName
    } = RoleInfo,
    case dict:find(RoleId, RoleGrade) of
        {ok, Old}  ->
            %% 已经是重复进入了, 进入的时候连杀数清空
            New = Old#madness_land_role{continue_kill = 0, enter_time = utime:unixtime(), die_count = 0, leave = false},
            NewRoleGrade = dict:store(RoleId, New, RoleGrade),
            NewGuildGrade = GuildGrade;
        error ->
            %% 因为玩法有两轮，第一轮结束会清空数据，所以这里也不是唯一的，有可能触发两次
            check_send_score_reward(Node, RoleId, 0, -1),
            %% 嗨点,第一次进入触发
            clusters_center:apply_cast(Node, mod_hi_point, success_end, [RoleId, ?MOD_MADNESS_LAND, 0]),
            lib_player:apply_cast(Node, RoleId, ?APPLY_CAST, 0,
                custom_act_adventure, do_trigger_adventure_task, [RoleId, ?MOD_MADNESS_LAND, 0, 1]),
            NewRoleGrade = dict:store(RoleId, RoleInfo#madness_land_role{enter_time = utime:unixtime()}, RoleGrade),
            case GuildId > 0 of
                true  ->
                    case lists:keymember(GuildId, #madness_land_guild.guild_id, GuildGrade) of
                        true  ->
                            %% 公会已经有数据了
                            NewGuildGrade = GuildGrade;
                        false ->
                            %% 本公会第一次有人进入，写入公会数据
                            GuildInfo = #madness_land_guild{
                                guild_id   = GuildId,
                                guild_name = GuildName,
                                server_num = RoleInfo#madness_land_role.server_num,
                                server_id  = RoleInfo#madness_land_role.server_id,
                                guild_flag = GuildFlag
                            },
                            NewGuildGrade = [GuildInfo|GuildGrade]
                    end;
                false ->
                    NewGuildGrade = GuildGrade
            end
    end,
    NewActData = ActData#madness_land_act{role_grade = NewRoleGrade, guild_grade = NewGuildGrade},
    {State, NewActData};

%% 进入场景后处理-一般前端触发
handle({after_enter, RoleId, OnlineFlag}, State, #madness_land_act{} = ActData) ->
    #madness_land{
        stage    = Stage,
        end_time = EndTime,
        cls_type = ClsType
    } = State,
    #madness_land_act{
        top = Top,
        boss_first_attack_time = AttackTime,
        role_grade = RoleGrade,           %% 个人成绩
        invincible_time     = InvincibleTime
        , relieve_invincible_time = RelieveTime
    } = ActData,
    case dict:find(RoleId, RoleGrade) of
        {ok, RoleInfo}  ->
            #madness_land_role{
                role_id = RoleId,
                node = Node,
                server_id = ServerId
            } = RoleInfo,
            %% 判断我是不是积分第一
            Top > 0 andalso ServerId =:= Top andalso ClsType =:= ?NODE_CENTER andalso
                update_role_act_label(Node, RoleId, ?ACT_LABEL_MADNESS_LAND_TOP, 1),
            %% 发送阶段BOSS血量信息/积分信息
            case OnlineFlag =:= ?ONLINE_ON of
                true  ->
                    {ok, Bin1} = pt_415:write(41500, [Stage, EndTime]),
                    {ok, Bin2} = pt_415:write(41503, [AttackTime, EndTime, InvincibleTime, RelieveTime]),
                    {ok, Bin3} = pt_415:write(41504, [0]),
                    lib_server_send:send_to_uid(Node, RoleId, <<Bin1/binary, Bin2/binary, Bin3/binary>>);
                false ->
                    ok
            end;
        error ->
            %% 没有数据！没有走请求进入的协议写入数据，先不管
            ignore
    end,
    {State, ActData};
%% 增加积分
handle({add_score, RoleId, Score, _Type, _Online}, State, #madness_land_act{} = ActData) ->
    #madness_land_act{
        role_grade = RoleGrade,
        guild_grade = GuildGrade
    } = ActData,
    case dict:find(RoleId, RoleGrade) of
        {ok, #madness_land_role{
            score = OldScore,
            node = Node,
            guild_id = GuildId
        } = Value} ->
            Now = utime:unixtime(),
            NewScore = OldScore + Score,
            %% 个人加分的时候发积分奖励
            check_send_score_reward(Node, RoleId, NewScore, OldScore),
            NewValue = Value#madness_land_role{score = NewScore, time = Now},
            NewRoleGrade = dict:store(RoleId, NewValue, RoleGrade),
            %% 社团加分
            NewGuildGrade =
                case GuildId > 0 of
                    true  ->
                        case lists:keyfind(GuildId, #madness_land_guild.guild_id, GuildGrade) of
                            #madness_land_guild{score = GuildScore} = One  ->
                                lists:keyreplace(GuildId, #madness_land_guild.guild_id, GuildGrade,
                                    One#madness_land_guild{score = GuildScore + Score, time = Now});
                            _ ->
                                One = #madness_land_guild{
                                    guild_id        = GuildId
                                    , server_id      = Value#madness_land_role.server_id
                                    , server_num     = Value#madness_land_role.server_num
                                    , guild_name     = Value#madness_land_role.guild_name
                                    , score          = Score
                                    , time           = Now
                                },
                                [One|GuildGrade]
                        end;
                    false ->
                        GuildGrade
                end,
            NewActData = ActData#madness_land_act{
                role_grade = NewRoleGrade,
                guild_grade = NewGuildGrade,
                refresh_flag = true
            },
            {State, NewActData};
        _ ->
            {State, ActData}
    end;
%% 击杀玩家-击杀玩家需要根据连杀数计算积分
handle({kill_man, Killer, Dead, AttackOnline, DefendOnline}, State, #madness_land_act{} = ActData) ->
    #madness_land_act{role_grade = RoleGrade} = ActData,
    case dict:find(Killer, RoleGrade) of
        {ok, #madness_land_role{continue_kill = KillerContinueKill} = KillerInfo} ->
            case dict:find(Dead, RoleGrade) of
                {ok, #madness_land_role{continue_kill = DeadContinueKill, node = Node} = DeadInfo} ->
                    BaseScore = ?MADNESS_LAND_KILL_BASE_SCORE,
                    NewKillerContinueKill = KillerContinueKill + 1,
                    %% 连杀加成
                    KillerContinueKillAdd = data_madness_land:get_madness_land_continue_kill_score(NewKillerContinueKill),
                    %% 终结加成
                    DeadContinueKillAdd   = data_madness_land:get_madness_land_end_score(DeadContinueKill),
                    Score = BaseScore + KillerContinueKillAdd + DeadContinueKillAdd,
                    %% 凶手连杀+1
                    NewKillerInfo = KillerInfo#madness_land_role{continue_kill = NewKillerContinueKill},
                    %% 死者连杀清空/死亡次数+1
                    NewDeadInfo   = DeadInfo#madness_land_role{
                        continue_kill = 0,
                        die_count = DeadInfo#madness_land_role.die_count + 1
                    },
                    case DefendOnline =:= ?ONLINE_ON of
                        true  ->
                            {ok, Bin} = pt_415:write(41504, [0]),
                            lib_server_send:send_to_uid(Node, Dead, Bin);
                        false ->
                            ignore
                    end,
                    broadcast_continue_kill(State, ActData, NewKillerInfo),
                    broadcast_end_kill(State, ActData#madness_land_act.zone_id, NewKillerInfo, NewDeadInfo, DeadContinueKill),
                    RoleGrade1 = dict:store(Killer, NewKillerInfo, RoleGrade),
                    RoleGrade2 = dict:store(Dead, NewDeadInfo, RoleGrade1),
                    NewActData = ActData#madness_land_act{role_grade = RoleGrade2},
                    handle({add_score, Killer, Score, kill_man, AttackOnline}, State, NewActData);
                _ ->
                    {State, ActData}
            end;
        _ ->
            {State, ActData}
    end;
%% 怪物首刀攻击后，开始自动扣血
handle({attck_mon, RoleId, MonId}, State, #madness_land_act{} = ActData) ->
    #madness_land_act{
        zone_id = ZoneId,
        role_grade = RoleGrade,
        boss_first_attack_time = AttackTime
    } = ActData,
    case AttackTime > 0 of
        true  ->
            {State, ActData};
        false ->
            Now = utime:unixtime(),
            NewActData = ActData#madness_land_act{
                boss_first_attack_time = Now,
                boss_first_attack_role = RoleId
            },
            case dict:find(RoleId, RoleGrade) of
                {ok, #madness_land_role{node = Node} = Role} ->
                    %% 发奖
                    Reward = ?MADNESS_LAND_FIRST_ATTACK_AWARD,
                    clusters_api:rpc_cast_do_mfa(Node, {lib_goods_api, send_reward_by_id, [Reward, madness_land_attck_mon, RoleId]}),
                    ok;
                _ ->
                    %% 讲道理，不会出现这种情况
                    Role = #madness_land_role{}
            end,
            broadcast_first_attack(State, ZoneId, Now, Role, MonId),
            {State, NewActData}
    end;

%% 分身死亡
handle({mon_die, ScenePoolId, Timing}, State, #madness_land_act{} = ActData) ->
    #madness_land_act{
        zone_id = ZoneId,
        boss_first_attack_time = AttackTime,
        alive_mons_info = AliveMonsInfo,
        invincible_time = InvincibleTime
    } = ActData,
    AliveMon = maps:get(ScenePoolId, AliveMonsInfo, 0),
    case AliveMon > 0 of
        true  ->
            #madness_land{
                round = Round,
                cls_type = ClsType,
                end_time = EndTime
            } = State,
            NewAliveMon = AliveMon - 1,
            NewAliveMonsInfo = maps:put(ScenePoolId, NewAliveMon, AliveMonsInfo),
            case NewAliveMon =:= 0 of
                true  ->
                    BossId = ?MADNESS_LAND_BOSS_ID(Round),
                    SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
                    case ActData#madness_land_act.relieve_invincible_time =:= 0 of
                        true  ->
                            %% 无敌还未解除，那么只要有一个分线的分身全部清除，那么就解除BOSS无敌
                            %% 无敌解除对话
                            {ok, Bin1} = pt_415:write(41509, [3]),
                            %% 广播血量开始变化
                            RelieveTime = utime:unixtime(),
                            {ok, Bin2} = pt_415:write(41503, [AttackTime, EndTime, InvincibleTime, RelieveTime]),
                            case ZoneId > 0 of
                                true  ->
                                    AllPool = scene_mgr:get_zone_scene_pool_ids(ZoneId, SceneId);
                                false ->
                                    AllPool = scene_mgr:get_scene_pool_ids(SceneId)
                            end,
                            Bin = <<Bin1/binary, Bin2/binary>>,
                            [
                                begin
                                    %% BOSS清除BUFF
                                    lib_mon:clean_buff(SceneId, PoolId, 0, [BossId], ?SP_SKILL_INVINCIBLE_MADNESS),
                                    %% 怪物可被攻击
                                    %lib_mon:change_attr_by_mids(SceneId, PoolId, 0, [BossId], [{is_be_atted, 1}]),
                                    lib_server_send:send_to_scene(SceneId, PoolId, Bin)
                                end
                                || PoolId <- AllPool
                            ];
                        false ->
                            %% 无敌已经解除了，只是小怪被打死
                            %% 如果是超时停止，则告诉前端
                            case Timing of
                                stop ->
                                    {ok, Bin} = pt_415:write(41509, [2]),
                                    lib_server_send:send_to_scene(SceneId, ScenePoolId, Bin);
                                _ ->
                                    ok
                            end,
                            RelieveTime = ActData#madness_land_act.relieve_invincible_time
                    end,
                    NewActData = ActData#madness_land_act{
                        alive_mons_info = NewAliveMonsInfo,
                        relieve_invincible_time = RelieveTime
                    };
                false ->
                    NewActData = ActData#madness_land_act{alive_mons_info = NewAliveMonsInfo}
            end,
            {State, NewActData};
        false ->
            {State, ActData}
    end;
%%
handle({leave_scene, RoleId}, State, #madness_land_act{} = ActData) ->
    #madness_land_act{
        role_grade = RoleGrade
    } = ActData,
    case dict:find(RoleId, RoleGrade) of
        {ok, #madness_land_role{node = Node, enter_time = EnterTime, die_count = DieCount, score = Score} = Old} ->
            lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE,
                madness_land_api, leave_scene_callback, [{State#madness_land.stage, Score, utime:unixtime() - EnterTime, DieCount}]),
            New = Old#madness_land_role{continue_kill = 0, die_count = 0, leave = true},
            NewRoleGrade = dict:store(RoleId, New, RoleGrade),
            {State, ActData#madness_land_act{role_grade = NewRoleGrade}};
        _ ->
            {State, ActData}
    end;

handle(_, State, ActData) -> {State, ActData}.

%%--------------------------------------------------------------------
%% @doc
%%      拉玩家进入第二个活动场景:本服
%% @end
%%--------------------------------------------------------------------
pull_role_into_round2(SecondType) ->
    TargetScene = case SecondType of
                  ?NODE_GAME      -> ?MADNESS_LAND_SCENE_ROUND_2_NODE;
                  ?NODE_CENTER    -> ?MADNESS_LAND_SCENE_ROUND_2_CENTER
              end,
    NowScene = ?MADNESS_LAND_SCENE_ROUND_1,
    [
        begin
            pull_role_into_round2_do(NowScene, PoolId, TargetScene)
        end
        || PoolId <- scene_mgr:get_scene_pool_ids(NowScene)
    ].
pull_role_into_round2_do(SceneId, PoolId, TargetScene) ->
    mod_scene_agent:apply_cast(SceneId, PoolId, ?MODULE, scene_pull_role_into_round2, [TargetScene]).

%% 在场景进程通知所有玩家
%% 给玩家一个延时，防止一个服务器的玩家同时挤到同一个场景
scene_pull_role_into_round2(TargetScene) ->
    AllUser = [Pid || #ets_scene_user{pid = Pid} <- lib_scene_agent:get_scene_user()],
    spawn(
        fun() ->
            lists:foldl(
                fun(Role, _) ->
                    timer:sleep(150),
                    madness_land_api:enter_send_round(Role, TargetScene)
                end, [], AllUser
            )
        end
    ).

%%--------------------------------------------------------------------
%% @doc
%%      循环给场景内玩家加积分：跨服或者本服
%% @end
%%--------------------------------------------------------------------
cycle_add_score(Round, ClsType) ->
    SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
    [
        cycle_add_score_do(SceneId, PoolId) || PoolId <- scene_mgr:get_scene_pool_ids(SceneId)
    ].
cycle_add_score_do(SceneId, PoolId) ->
    mod_scene_agent:apply_cast_with_scene(SceneId, PoolId, ?MODULE, scene_cycle_add_score, []).

%% 场景进程处理加分
scene_cycle_add_score(#ets_scene{id = SceneId}) ->
    AllUser = lib_scene_agent:get_scene_user(),
    Score = ?MADNESS_LAND_CYCLE_SCORE,
    case madness_land_api:is_madness_land_center(SceneId) of
        true  ->
            case AllUser of
                [#ets_scene_user{server_id = ServerId, online = Online}| _] ->
                    ZoneId = czone_api:server_zone(ServerId),
                    [
                        madness_land:common_cast_center(ZoneId, {add_score, RoleId, Score, cycle, Online})
                        || #ets_scene_user{id = RoleId} <- AllUser
                    ];
                _ ->
                    ok
            end;
        false ->
            [
                madness_land:common_cast_local({add_score, RoleId, Score, cycle, Online})
                || #ets_scene_user{id = RoleId, online = Online} <- AllUser
            ]
    end.

%%--------------------------------------------------------------------
%% @doc
%%      加分的时候判断是否给突破奖励：跨服或者本服
%% @end
%%--------------------------------------------------------------------
check_send_score_reward(_, _, Score, Score) -> ok;
check_send_score_reward(Node, RoleId, NewScore, OldScore) ->
    LevelOld = data_madness_land:get_madness_land_score_id(OldScore),
    LevelNew = data_madness_land:get_madness_land_score_id(NewScore),
    LevelNew > LevelOld andalso
        begin
            Reward = lists:foldl(
                fun(LevelTemp, AwardTemp) ->
                    data_madness_land:get_madness_land_score_reward(LevelTemp) ++ AwardTemp
                end, [], lists:seq(LevelOld+1, LevelNew)
            ),
            Produce = #produce{type = madness_land_score, reward = Reward, remark = lists:concat(["score=", NewScore])},
            lib_server_send:send_to_uid(Node, RoleId, pt_415, 41512, [NewScore]),
            lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_STATUS, ?NOT_HAND_OFFLINE, ?MODULE, send_score_reward, [Produce])
        end.
%% 玩家进程发积分突破奖励
send_score_reward(Player, Produce) ->
    Player1 = lib_goods_api:send_reward(Player, Produce),
    Player1.

%%--------------------------------------------------------------------
%% @doc
%%      出小怪：跨服或者本服/同时让BOSS无敌
%% @end
%%--------------------------------------------------------------------
born_mon(State) ->
    #madness_land{
        cls_type = ClsType,
        round = Round,
        end_time = EndTime,
        local_data = LocalData,
        zone_data = ZoneData
    } = State,
    SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
    Mons =
        case Round of
            1  -> ?MADNESS_LAND_ROUND_1_MONS;
            2  -> ?MADNESS_LAND_ROUND_2_MONS
        end,
    MonsNum = length(Mons),
    BossId = ?MADNESS_LAND_BOSS_ID(Round),
    {ok, Bin1} = pt_415:write(41509, [1]),
    Now = utime:unixtime(),
    AliveTime = max(1, (EndTime - Now - 120) * 1000),
    %AliveTime = 60 * 1000,
    Arg = [{stop, AliveTime},{die_handler, {?MODULE, when_mon_die, []}}],
    AllPoolIds = scene_mgr:get_scene_pool_ids(SceneId),
    InvincibleTime = utime:unixtime(),
    {ok, Bin2} = pt_415:write(41511, [InvincibleTime]),
    Bin = <<Bin1/binary, Bin2/binary>>,
    [
        begin
            %% BOSS加无敌BUFF
           lib_mon:ac_skill(SceneId, PoolId, 0, [BossId], ?SP_SKILL_INVINCIBLE_MADNESS, 1),
           %% 怪物不可被攻击
           %lib_mon:change_attr_by_mids(SceneId, PoolId, 0, [BossId], [{is_be_atted, 0}]),
            %% 出生小怪
            [
                lib_mon:async_create_mon(MonId, SceneId, PoolId, X, Y, 1, 0, 1, Arg)
                || {MonId, X, Y} <- Mons
            ],
            lib_server_send:send_to_scene(SceneId, PoolId, Bin)
        end
        || PoolId <- AllPoolIds
    ],
    %% 将分身数量记录下来
    case ClsType of
        ?NODE_CENTER ->
            NewZoneData =
            maps:map(
                fun(ZoneId, V) ->
                    AliveMonsInfo = [{PoolId, MonsNum} || PoolId <- AllPoolIds, scene_mgr:get_pool_zone(PoolId) =:= ZoneId],
                    V#madness_land_act{alive_mons_info = maps:from_list(AliveMonsInfo), invincible_time = Now}
                end, ZoneData
            ),
            State#madness_land{zone_data = NewZoneData};
        ?NODE_GAME ->
            AliveMonsInfo = [{PoolId, MonsNum} || PoolId <- AllPoolIds],
            State#madness_land{local_data = LocalData#madness_land_act{alive_mons_info = maps:from_list(AliveMonsInfo), invincible_time = Now}}
    end.

%% 小怪死亡通知管理进程
when_mon_die(#ob_act{object = #scene_object{scene_pool_id = ScenePoolId, scene = Scene, id = AutoId}}, _Klist, _Killer, _, _) ->
    madness_land_api:when_mon_die(Scene, ScenePoolId, AutoId, kill).

%%--------------------------------------------------------------------
%% @doc
%%      清理场景怪物：跨服或者本服
%% @end
%%--------------------------------------------------------------------
clean_round_boss(Round, ClsType) ->
    SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
    [
        lib_mon:kill_scene_mon(SceneId, PoolId, 0)
        || PoolId <- scene_mgr:get_scene_pool_ids(SceneId)
    ].

%%--------------------------------------------------------------------
%% @doc
%%      广播boss首刀数据:本服或者分区
%% @end
%%--------------------------------------------------------------------
broadcast_first_attack(State, ZoneId, Now, Role, MonId) ->
    #madness_land{cls_type = ClsType, round = Round, end_time = EndTime} = State,
    #madness_land_role{server_num = ServerNum, picture = #rec_picture{name = Name}, role_id = RoleId} = Role,
    %% 通知血量变化
    {ok, Bin1} = pt_415:write(41503, [Now, EndTime, 0, 0]),
    %% 传闻首刀
    Bin2 = case ClsType of
        ?NODE_CENTER ->
            lib_chat:make_tv(?MOD_MADNESS_LAND, 20, [ServerNum, Name, RoleId, MonId]);
        ?NODE_GAME ->
            lib_chat:make_tv(?MOD_MADNESS_LAND, 19, [Name, RoleId, MonId])
    end,
    Bin = <<Bin1/binary, Bin2/binary>>,
    SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
    broad_cast_zone_scene(ZoneId, SceneId, Bin).

%%--------------------------------------------------------------------
%% @doc
%%      广播击杀数据:本服或者分区
%% @end
%%--------------------------------------------------------------------
broadcast_continue_kill(State, ActData, KillerInfo) ->
    #madness_land_role{continue_kill = ContinueKill} = KillerInfo,
    if
        ContinueKill =:= 2 ->
            send_diff_tv(State, KillerInfo, 5, 12, ActData#madness_land_act.zone_id);
        ContinueKill =:= 5 ->
            send_diff_tv(State, KillerInfo, 6, 13, ActData#madness_land_act.zone_id);
        ContinueKill =:= 10 ->
            send_diff_tv(State, KillerInfo, 7, 14, ActData#madness_land_act.zone_id);
        ContinueKill =:= 20 ->
            send_diff_tv(State, KillerInfo, 8, 15, ActData#madness_land_act.zone_id);
        ContinueKill =:= 50 ->
            send_diff_tv(State, KillerInfo, 9, 16, ActData#madness_land_act.zone_id);
        true ->
            ok
    end.
send_diff_tv(State, KillerInfo, TVLocal, TVCenter, ZoneId) ->
    #madness_land{cls_type = ClsType, round = Round} = State,
    #madness_land_role{picture = #rec_picture{name = RoleName}, server_num = ServerNum, role_id = RoleId} = KillerInfo,
    case ClsType of
        ?NODE_GAME ->
            SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
            Bin = lib_chat:make_tv(?MOD_MADNESS_LAND, TVLocal, [RoleName, RoleId]);
        ?NODE_CENTER ->
            SceneId = ?MADNESS_LAND_SCENE_ROUND_2_CENTER,
            Bin = lib_chat:make_tv(?MOD_MADNESS_LAND, TVCenter, [ServerNum, RoleName, RoleId])
    end,
    broad_cast_zone_scene(ZoneId, SceneId, Bin).

%% 终结连杀
broadcast_end_kill(State, ZoneId, KillerInfo, DeadInfo, DeadContinueKill) when DeadContinueKill >= 5 ->
    #madness_land{cls_type = ClsType, round = Round} = State,
    #madness_land_role{server_num = KillerServerNum, picture = KillerPicture, role_id = KillerId} = KillerInfo,
    #madness_land_role{server_num = DeadServerNum, picture = DeadPicture, role_id = DeadId} = DeadInfo,
    {ok, Bin1} = pt_415:write(41505, [KillerPicture, DeadPicture]),
    case ClsType of
        ?NODE_GAME ->
            SceneId = ?MADNESS_LAND_SCENE(Round, ClsType),
            Bin2 = lib_chat:make_tv(?MOD_MADNESS_LAND, 11, [KillerPicture#rec_picture.name, KillerId, DeadPicture#rec_picture.name, DeadId, DeadContinueKill]);
        ?NODE_CENTER ->
            SceneId = ?MADNESS_LAND_SCENE_ROUND_2_CENTER,
            Bin2 = lib_chat:make_tv(?MOD_MADNESS_LAND, 18, [KillerServerNum, KillerPicture#rec_picture.name, KillerId, DeadServerNum, DeadPicture#rec_picture.name, DeadId, DeadContinueKill])
    end,
    broad_cast_zone_scene(ZoneId, SceneId, <<Bin1/binary, Bin2/binary>>);
broadcast_end_kill(_, _, _, _, _) -> ok.
%%--------------------------------------------------------------------
%% @doc
%%      本服或者分区活动场景广播:首刀/连杀/终结连杀
%% @end
%%--------------------------------------------------------------------
broad_cast_zone_scene(ZoneId, SceneId, Bin) ->
    case ZoneId > 0 of
        true  ->
            [
                lib_server_send:send_to_scene(SceneId, PoolId, Bin)
                || PoolId <- scene_mgr:get_zone_scene_pool_ids(ZoneId, SceneId)
            ];
        false ->
            %% 跨服
            [
                lib_server_send:send_to_scene(SceneId, PoolId, Bin)
                || PoolId <- scene_mgr:get_scene_pool_ids(SceneId)
            ]
    end.
%%--------------------------------------------------------------------
%% @doc
%%      刷新榜单：本服或者分区
%% @end
%%--------------------------------------------------------------------
refresh_rank(
    #madness_land{
        cls_type = ClsType,
        round = Round,
        zone_data = ZoneData,
        role_rank = RoleRankData,
        guild_rank = GuildRankData
    } = State,
    #madness_land_act{
        zone_id = ZoneId,
        refresh_flag = true,
        role_grade = RoleGrade,
        guild_grade = GuildGrade,
        top = Top
    } = ActData
) ->
    RoleRank  = sort_role(RoleGrade),
    GuildRank = sort_guild(GuildGrade),
    NewTop = case Round =:= 2 andalso ClsType =:= ?NODE_CENTER of
        true  ->
            case GuildRank of
                [#madness_land_guild{server_id = ServerId}|_] ->
                    %% 积分第一需要展示
                    case ServerId =/= Top of
                        true  ->
                            Top > 0 andalso update_server_role_act_label(ZoneId, Top, ?ACT_LABEL_MADNESS_LAND_TOP, 0),
                            update_server_role_act_label(ZoneId, ServerId, ?ACT_LABEL_MADNESS_LAND_TOP, 1);
                        false ->
                            ok
                    end,
                    ServerId;
                _ ->
                    Top
            end;
        false ->
            Top
    end,
    NewActData = ActData#madness_land_act{
        top = NewTop,
        guild_grade = GuildRank,
        role_grade = dict:from_list([{One#madness_land_role.role_id, One} || One <- RoleRank]),
        refresh_flag = false
    },
    State1 =
        case ClsType of
            ?NODE_CENTER -> State#madness_land{zone_data = ZoneData#{ZoneId := NewActData}};
            ?NODE_GAME   -> State#madness_land{local_data = NewActData}
        end,
    State2 = State1#madness_land{
        role_rank = RoleRankData#{ZoneId => lists:sublist(RoleRank, ?MADNESS_LAND_RANK_LENGTH)},
        guild_rank = GuildRankData#{ZoneId => lists:sublist(GuildRank, ?MADNESS_LAND_GUILD_RANK_LENGTH)}
    },
    {State2, RoleRank, GuildRank};
refresh_rank(State, _) -> {State, [], []}.

sort_role(RoleGrade) ->
    F = fun(
        #madness_land_role{score = ScoreA, time = TimeA},
        #madness_land_role{score = ScoreB, time = TimeB}
    ) ->
        if
            ScoreA > ScoreB -> true;
            ScoreA < ScoreB -> false;
            true -> TimeA >= TimeB
        end
        end,
    Ranks1 = lists:sort(F, [Value || {_Key, Value} <- dict:to_list(RoleGrade)]),
    Ranks2 = cut_out_top_role(Ranks1, [], 1),
    lists:reverse(Ranks2).
cut_out_top_role([], List, _) -> List;
cut_out_top_role([One|T], List, Rank) ->
    cut_out_top_role(T, [One#madness_land_role{rank = Rank}|List], Rank+1).

sort_guild(GuildGrade) ->
    F = fun(
        #madness_land_guild{score = ScoreA, time = TimeA},
        #madness_land_guild{score = ScoreB, time = TimeB}
    ) ->
        if
            ScoreA > ScoreB -> true;
            ScoreA < ScoreB -> false;
            true -> TimeA >= TimeB
        end
        end,
    Ranks1 = lists:sort(F, GuildGrade),
    Ranks2 = cut_out_top_guild(Ranks1, [], 1),
    lists:reverse(Ranks2).
cut_out_top_guild([], List, _) -> List;
cut_out_top_guild([One|T], List, Rank) ->
    cut_out_top_guild(T, [One#madness_land_guild{rank = Rank}|List], Rank+1).
%%--------------------------------------------------------------------
%% @doc
%%      更新玩家头顶标识:本服或者分区
%% @end
%%--------------------------------------------------------------------
%% 更新某个玩家的头顶标识 lib_player:apply_cast('mrzj_dev_1@192.168.5.246', 4294967298, 2, 0, madness_land_act, role_update_act_label, [5, 1]).
update_role_act_label(Node, RoleId, Label, State) ->
    lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_STATUS, ?NOT_HAND_OFFLINE, ?MODULE, role_update_act_label, [Label, State]).

%% 玩家进程处理
role_update_act_label(Player, Label, State) ->
    case madness_land_api:is_madness_land_scene(Player#player_status.scene) of
        true  ->
            lib_scene:change_scene_act_label(Player, Label, State);
        false ->
            Player
    end.

%% 更新一批人头顶标识
update_server_role_act_label(ZoneId, ServerId, Label, State) ->
    case ZoneId > 0 of
        true  ->
            Scene = ?MADNESS_LAND_SCENE_ROUND_2_CENTER,
            [
                mod_scene_agent:apply_cast(Scene, PoolId, ?MODULE, scene_update_server_role_act_label, [ServerId, Label, State])
                || PoolId <- scene_mgr:get_zone_scene_pool_ids(ZoneId, Scene)
            ];
        false ->
            Scene = ?MADNESS_LAND_SCENE_ROUND_2_NODE,
            [
                mod_scene_agent:apply_cast(Scene, PoolId, ?MODULE, scene_update_server_role_act_label, [ServerId, Label, State])
                || PoolId <- scene_mgr:get_scene_pool_ids(Scene)
            ]
    end.

scene_update_server_role_act_label(ServerId, Label, State) ->
    AllUser = lib_scene_agent:get_scene_user(),
    [
        lib_scene:change_scene_act_label(Pid, Label, State)
        || #ets_scene_user{pid = Pid, server_id = RoleServer} <- AllUser, RoleServer =:= ServerId
    ].

%%--------------------------------------------------------------------
%% @doc
%%      关闭活动
%% @end
%%--------------------------------------------------------------------
close_act(_ClsType) ->
    %% 延迟5秒关闭场景
    erlang:send_after(5000, self(), close_act).
%%--------------------------------------------------------------------
%% @doc
%%      结算：先推送结算/然后延迟发奖
%% @end
%%--------------------------------------------------------------------
%% 本服玩法结算：第一轮/第二轮
settlement_local(
    Round,
    [#madness_land_role{
        rank = Rank, guild_id = GuildId, role_id = RoleId, score = Score, picture = #rec_picture{name = Name},
        guild_name = GuildName
    }|T],
    GuildGrade,
    State,
    ActData,
    Now
) ->
    case lists:keyfind(GuildId, #madness_land_guild.guild_id, GuildGrade) of
        #madness_land_guild{rank = GuildRank, score = GuildScore} -> GuildRank, GuildScore;
        _ -> GuildRank = 0, GuildScore = 0
    end,
    case Round of
        1  -> Bin = <<>>;
        2  ->
            %% 弹结算框
            case ActData#madness_land_act.boss_first_attack_role == RoleId of
                true  -> Round2FirstAttack = 1;
                false -> Round2FirstAttack = 0
            end,
            Round1Info = get_round1_info(State, RoleId, GuildId),
            %% 本服情形没有榜首服务器，写默认值
            Round2Info = [Rank, Score, GuildRank, Round2FirstAttack, GuildScore, 0, 0],
            {ok, Bin} = pt_415:write(41508, Round1Info ++ Round2Info)
    end,
    settlement_local_do(Round, RoleId, Rank, GuildRank, Bin),
    lib_log_api:log_madness_land_role_reward(
        [Round, RoleId, Name, Score, Rank, GuildId, GuildName, GuildRank, Now]
    ),
    settlement_local(Round, T, GuildGrade, State, ActData, Now);
settlement_local(_, [], _, _, _, _) -> ok.

%% 跨服中心玩法结算：第二轮
settlement_center([#madness_land_role{rank = Rank, guild_id = GuildId, role_id = RoleId, score = RoleScore} = One|T],
    GuildGrade, ActData, TopServer, ZoneStage, Now) ->
    case GuildId > 0 of
        true  ->
            case lists:keyfind(GuildId, #madness_land_guild.guild_id, GuildGrade) of
                #madness_land_guild{rank = GuildRank, score = GuildScore} -> GuildRank,GuildScore;
                _ -> GuildRank = 0, GuildScore = 0
            end;
        false ->
            GuildRank = 0, GuildScore = 0
    end,
    %% 弹结算框
    case ActData#madness_land_act.boss_first_attack_role == RoleId of
        true  -> Round2FirstAttack = 1;
        false -> Round2FirstAttack = 0
    end,
    %% 去游戏服读第一轮的成绩
    clusters_center:apply_cast(One#madness_land_role.node, {madness_land, apply_info, [{settlement_round2_in_server, {RoleId, GuildId, Rank, RoleScore, GuildRank, Round2FirstAttack, GuildScore, TopServer}}]}),
    lib_log_api:log_madness_land_role_reward_kf(
        [ActData#madness_land_act.zone_id, One#madness_land_role.server_id, One#madness_land_role.server_num, ZoneStage, RoleId,
        One#madness_land_role.picture#rec_picture.name, RoleScore, Rank, GuildId, One#madness_land_role.guild_name, GuildRank, Now]
    ),
    settlement_center(T, GuildGrade, ActData, TopServer, ZoneStage, Now);
settlement_center([], _, _, _, _ZoneStage, _Now) -> ok.

%% 跨服第二轮结算的时候读取第一轮的成绩
settlement_round2_in_server(State, {RoleId, GuildId, Rank, Score, GuildRank, Round2FirstAttack, GuildScore, TopServer}) ->
    Round1Info = get_round1_info(State, RoleId, GuildId),
    Round2Info = [Rank, Score, GuildRank, Round2FirstAttack, GuildScore, TopServer, ?NODE_CENTER],
    {ok, Bin} = pt_415:write(41508, Round1Info ++ Round2Info),
    settlement_local_do(2, RoleId, Rank, GuildRank, Bin),
    ok.

%% 进行结算：第一轮或者第二轮: 发奖
settlement_local_do(Round, RoleId, Rank, GuildRank, Bin) ->
    case lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, settlement_online, [Round, Rank, GuildRank, Bin]) of
        skip ->
            settlement_offline(RoleId, Rank, GuildRank);
        _ ->
            ok
    end.

settlement_online(Player, Round, Rank, GuildRank, Bin) ->
    case madness_land_api:is_madness_land_scene(Player#player_status.scene) of
        true  ->
            %% 结算的时候，托管玩家停止托管行为
            {ok, Player1} = offline_hosting:stop_behavior(Player),
            case Round == 2 of
                true ->
                    %% 活动结束，离开场景
                    Player2 = madness_land_api:leave_scene(Player1),
                    lib_server_send:send_to_sid(Player#player_status.sid, Bin);
                _ ->
                    Player2 = Player1
            end,
            %% 随机延迟一个时间发奖
            spawn(
                fun() ->
                    util:multiserver_delay(3, ?MODULE, send_reward_do, [Player#player_status.id, Rank, GuildRank])
                end
            ),
            Player2;
        false ->
            settlement_offline(Player#player_status.id, Rank, GuildRank),
            Player
    end.
%% 不在活动场景发邮件
settlement_offline(RoleId, Rank, GuildRank) ->
    case data_madness_land:get_madness_land_role_reward(Rank) of
        [] -> ok;
        RoleAward ->
            lib_mail_api:send_sys_mail([RoleId], ?MOD_MADNESS_LAND, utext:get(4150001), utext:get(4150002, [Rank]), RoleAward)
    end,
    case data_madness_land:get_madness_land_server_reward(GuildRank) of
        [] -> ok;
        GuildAward ->
            lib_mail_api:send_sys_mail([RoleId], ?MOD_MADNESS_LAND, utext:get(4150003), utext:get(4150004, [GuildRank]), GuildAward)
    end.

%% 发送奖励处理
send_reward_do(RoleId, Rank, GuildRank) ->
    RolePid = misc:get_player_process(RoleId),
    case misc:is_process_alive(RolePid) of
        true  ->
            RoleAward  = data_madness_land:get_madness_land_role_reward(Rank),
            GuildAward = data_madness_land:get_madness_land_server_reward(GuildRank),
            case RoleAward ++ GuildAward of
                [] -> ignore;
                Award ->
                    lib_goods_api:send_reward_by_id(Award, madness_land_role_rank, RoleId)
            end;
        false ->
            settlement_offline(RoleId, Rank, GuildRank)
    end.

get_round1_info(State, RoleId, GuildId) ->
    #madness_land{
        round_1_role_grade = RoleGrade,
        round_1_guild_grade = GuildGrade,
        round_1_first_attack = RoundFirstAttack
    } = State,
    case RoundFirstAttack == RoleId of
        true  -> Round1FirstAttack = 1;
        false -> Round1FirstAttack = 0
    end,
    case dict:find(RoleId, RoleGrade) of
        {ok, #madness_land_role{rank = Round1Rank, score = Round1Score}} ->
            Round1Rank, Round1Score;
        _ ->
            Round1Rank = 0, Round1Score = 0
    end,
    case lists:keyfind(GuildId, #madness_land_guild.guild_id, GuildGrade) of
        #madness_land_guild{rank = GuildRank} -> GuildRank;
        _ -> GuildRank = 0
    end,
    [Round1Rank, Round1Score, GuildRank, Round1FirstAttack].
%%--------------------------------------------------------------------
%% @doc
%%      全服邮件
%% @end
%%--------------------------------------------------------------------
send_server_reward_mail(ZoneId, Rank) ->
    case lists:keyfind(1, #madness_land_guild.rank, Rank) of
        #madness_land_guild{server_id = ServerId, guild_name = GuildName, server_num = ServerNum} ->
            czone:zone_apply_cast(ZoneId, {?MODULE, broadcast_top_guild_in_server, [ServerNum, GuildName]}),
            clusters_center:apply_cast(ServerId, {?MODULE, send_reward_mail_in_server, [GuildName]});
        _ ->
            ok
    end.
send_reward_mail_in_server(GuildName) ->
    Title = utext:get(4150005),
    Content = utext:get(4150006, [GuildName]),
    Reward = ?MADNESS_LAND_SERVER_REWARD,
    lib_mail_api:send_sys_mail_to_active(Title, Content, Reward, 50, 3),
    ok.

broadcast_top_guild_in_server(ServerNum, GuidName) ->
    lib_chat:send_TV({all}, ?MOD_MADNESS_LAND, 4, [ServerNum, GuidName]).

broadcast_top_role(ZoneId, Rank) ->
    case lists:keyfind(1, #madness_land_role.rank, Rank) of
        #madness_land_role{server_num = ServerNum, role_id = RoleId, picture = #rec_picture{name = Name}} ->
            czone:zone_apply_cast(ZoneId, {?MODULE, broadcast_top_role_in_server, [ServerNum, Name, RoleId]});
        _ ->
            ok
    end.
broadcast_top_role_in_server(ServerNum, Name, RoleId) ->
    lib_chat:send_TV({all}, ?MOD_MADNESS_LAND, 23, [ServerNum, Name, RoleId]).
%%--------------------------------------------------------------------
%% @doc
%%      保存榜单
%% @end
%%--------------------------------------------------------------------
save_rank_data(_, []) -> ok;
save_rank_data(local_role, RankData) ->
    db:execute("TRUNCATE madness_land_role_rank"),
    SqlList =
        [
            io_lib:format(<<"(~p,~p,~p)">>, [Rank, RoleId, Score]) || {Rank, RoleId, Score} <- RankData
        ],
    SQL = io_lib:format(<<"INSERT INTO madness_land_role_rank(rank,role_id,score) VALUES ~ts">>, [string:join(SqlList, ",")]),
    db:execute(SQL);
save_rank_data(local_guild, RankData) ->
    timer:sleep(3000),
    db:execute("TRUNCATE madness_land_guild_rank"),
    SqlList =
        [
            io_lib:format(<<"(~p,'~ts',~p,~p)">>, [Rank, GuildName, GuildFlag, Score])
            || {Rank, GuildName, GuildFlag, Score} <- RankData
        ],
    SQL = io_lib:format(<<"INSERT INTO madness_land_guild_rank(rank,guild_name,guild_flag,score) VALUES ~ts">>, [string:join(SqlList, ",")]),
    db:execute(SQL);
save_rank_data(center_role, RankData) ->
    db:execute("TRUNCATE madness_land_zone_role_rank"),
    SqlList =
        [
            io_lib:format(<<"(~p,~p,~p,'~ts',~p,~p,~p,~p,~p,~p,~p,~p,~p)">>, [ZoneId, Rank, RoleId, Name, Sex, Vip, SupVip, ServerNum, Score, Photo, PhotoDress, Career, Turn])
            || {ZoneId, Rank, RoleId, Name, Sex, Vip, SupVip, ServerNum, Score, Photo, PhotoDress, Career, Turn} <- RankData
        ],
    SQL = io_lib:format(<<"INSERT INTO madness_land_zone_role_rank(zone_id,rank,role_id,name,sex,vip,sup_vip,server_num,score,photo,photo_dress,career,turn) VALUES ~ts">>, [string:join(SqlList, ",")]),
    db:execute(SQL);
save_rank_data(center_guild, RankData) ->
    timer:sleep(3000),
    db:execute("TRUNCATE madness_land_zone_guild_rank"),
    SqlList =
        [
            io_lib:format(<<"(~p,~p,~p,'~ts',~p,~p)">>, [ZoneId, Rank, ServerNum, GuildName, Score, GuildFlag])
            || {ZoneId, Rank, ServerNum, GuildName, Score, GuildFlag} <- RankData
        ],
    SQL = io_lib:format(<<"INSERT INTO madness_land_zone_guild_rank(zone_id,rank,server_num,guild_name,score,guild_flag) VALUES ~ts">>, [string:join(SqlList, ",")]),
    db:execute(SQL).
