%% ---------------------------------------------------------------------------
%% @doc 跨服多人副本组队相关
%% @author zengzy
%% @since  2019-03-26
%% ---------------------------------------------------------------------------
-module(lib_dun_multi_cluster).

-include("dungeon_multi.hrl").
-include("server.hrl").
-include("dungeon.hrl").
-include("chat.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("errcode.hrl").
-include("def_module.hrl").
-include("common.hrl").
-include("team.hrl").
-include("def_fun.hrl").
-include("guild.hrl").
-include("role.hrl").

-export([
    cast_dun_multi_mgr_cluster/1      %% cast跨服多人副本大厅进程。游戏服执行
    ,cast_dun_multi_hall_cluster/2    %% cast跨服多人副本进程。需要在跨服执行
    ,call_dun_multi_mgr_cluster/1     %% call跨服多人副本大厅进程。游戏服执行
    ,call_dun_multi_hall_cluster/2    %% call跨服多人副本进程。需要在跨服执行
    ,team_broadcast_notify/5          %% 成员状态变更通知（全队广播）
    ,broadcast_team_info/1            %% 广播队伍详细信息（全队广播）
    ,send_to_team/2                   %% 队伍消息（全队广播）
    ,send_to_team_on_cluster/2        %% 队伍消息（全队广播).在跨服执行
    ,login_cast_team/3                %% 登录cast队伍
    ,get_all_team_num/3               %% 获取总的队伍数量
    ,get_all_team_num_on_cluster/3    %% 获取总的队伍数量.在跨服执行
    ,get_all_team/6                   %% 分页获取总的队伍信息
    ,get_all_team_on_cluster/6        %% 分页获取总的队伍信息.在跨服执行
    ,send_dun_teams/2                 %% 查询对应多人副本的队伍列表
    ,create_team/10                    %% 创建多人副本组队
    ,join_team/5                      %% 加入多人副本组队
    ,join_team_on_cluster/1           %% 加入多人副本组队.在跨服执行
    % ,match_join_team/3				  %% 匹配加入队伍
    ,macth_or_create_team/4           %% 连续讨伐
    ,finish_first/2                   %% 完成首次进入副本
    ,leave_team/1                     %% 离开多人副本组队
    ,change_ready_status/2            %% 切换准备状态
    ,kick_out/2                       %% 踢出成员
    ,recruit_member/2                 %% 招募队员
    ,recruit_member_on_cluster/5      %% 招募队员.在跨服执行
    ,update_set_info/4                %% 修改队伍设置
    ,change_leader/2                  %% 委任队长
    ,prompt_enter/1                   %% 催促队长
    ,start_dun_multi/1                %% 开启多人副本
    ,pack_team_info/3                 %% 查询多人副本队伍信息。在跨服执行
    ,pack_team_info/1                 %% 打包多人副本队伍信息
    ,apply_to_local_role/4
    ,gm_remove_team/1                 %% gm随机移除增加队伍
    ,get_return_dun_id/1
    ,get_real_dun_id/1
    ,get_act_pid_map_id/1
    ,is_need_password/1
    ,get_access_invite_role/3         %% 获取可邀请的玩家列表
    ,dun_multi_mirror_help/3          %% 发起镜像协助
    ,dun_multi_vote/2                 %% 发起投票
    ,dun_multi_vote_on_cluster/1
    ,team_mate_vote/2                 %% 队员投票
    ,team_mate_vote_on_clusters/1
    ,reply_to_req_roles/2              %% 队长回复申请
    ,reply_to_req_roles_on_clusters/1
    ,change_approve_state/2           %% 修改审批状态
    ,change_approve_state_on_clusters/1
    ,change_team_target/3             %% 切换队伍目标
    ,change_team_target_on_clusters/1
    ,update_team_req_action_lock/3
    ,change_help_type/2               %% 切换协助状态
    ,change_help_type_on_clusters/1
    ,change_merge_type/4              %% 切换合并次数状态
    ,change_merge_type_on_clusters/1
]).

%% ---------------------------------------------------------------------------
%% @doc cast跨服多人副本管理进程。游戏服执行
-spec cast_dun_multi_mgr_cluster(Msg) -> ok when
    Msg         :: term().   %% {Fun, Args}
%% ---------------------------------------------------------------------------
cast_dun_multi_mgr_cluster(Msg) ->
    clusters_node:apply_cast(mod_dun_multi_mgr_cluster, cast, [Msg]).

%% ---------------------------------------------------------------------------
%% @doc cast跨服多人副本进程。需要在跨服执行
-spec cast_dun_multi_hall_cluster(DunHallId, Msg) -> ok when
    DunHallId       :: integer(),
    Msg         :: term().
%% ---------------------------------------------------------------------------
cast_dun_multi_hall_cluster(DunHallId, Msg) ->
    case data_dungeon:get_dun_type(DunHallId) of
        ?DUNGEON_TYPE_CRUSADE ->
            DunHallId1 = get_act_pid_map_id(DunHallId);
        _ ->
            DunHallId1 = DunHallId
    end,
    case ets:lookup(?DUN_MULTI_HALL_PID_ETS, DunHallId1) of
        [#dun_multi_hall_pid{pid = HallPid}] ->
            mod_dun_multi_hall_cluster:cast(HallPid, Msg);
        _ ->
            ok
    end.

%% ---------------------------------------------------------------------------
%% @doc call跨服多人副本管理进程。游戏服执行
-spec call_dun_multi_mgr_cluster(Msg) -> term() when
    Msg         :: term().   %% {Fun, Args}
%% ---------------------------------------------------------------------------
call_dun_multi_mgr_cluster(Msg) ->
    clusters_node:apply_call(mod_dun_multi_mgr_cluster, call, [Msg]).

%% ---------------------------------------------------------------------------
%% @doc call跨服多人副本进程。需要在跨服执行
-spec call_dun_multi_hall_cluster(Key, Msg) -> term() when
    Key         :: integer(), %% 副本id或组队子类id(讨伐副本)
    Msg         :: term().
%% ---------------------------------------------------------------------------
call_dun_multi_hall_cluster(Key, Msg) ->
    case data_dungeon:get_dun_type(Key) of
        ?DUNGEON_TYPE_CRUSADE ->
            DunHallId = get_act_pid_map_id(Key);
        _ ->
            DunHallId = Key
    end,
    case ets:lookup(?DUN_MULTI_HALL_PID_ETS, DunHallId) of
        [#dun_multi_hall_pid{pid = HallPid}] ->
            mod_dun_multi_hall_cluster:call(HallPid, Msg, 4000);
        _ ->
            {false, ?ERRCODE(err215_not_dun_multi)}
    end.

%% ---------------------------------------------------------------------------
%% @doc 成员状态变更通知（全队广播）
-spec team_broadcast_notify(DunId, Members, MemberKey, Type, Val) -> ok when
    DunId       :: integer(),
    Members     :: [#dun_multi_member{}],
    MemberKey   :: tuple(),
    Type        :: integer(),
    Val         :: integer().
%% ---------------------------------------------------------------------------
team_broadcast_notify(_DunId, Members, MemberKey, Type, Val) ->
    {RoleId} = MemberKey,
    ProData = [RoleId, Type, Val],
    {ok, BinData} = pt_215:write(21509, ProData),
    send_to_team(Members, BinData),
    ok.

%% ---------------------------------------------------------------------------
%% @doc 广播队伍详细信息（全队广播）
-spec broadcast_team_info(Team) -> ok when
    Team        :: #dun_multi_team{}.
%% ---------------------------------------------------------------------------
broadcast_team_info(Team = #dun_multi_team{members = Members}) ->
    ProData = lib_dun_multi_cluster:pack_team_info(Team),
    {ok, BinData} = pt_215:write(21504, ProData),
    send_to_team(Members, BinData),
    ok.

%% ---------------------------------------------------------------------------
%% @doc 队伍消息（全队广播）
-spec send_to_team(Data, BinData) -> ok when
    Data        :: list() | integer(),
    BinData     :: binary().
%% ---------------------------------------------------------------------------
send_to_team(TeamId, BinData) when is_integer(TeamId) ->
    Args = [TeamId, BinData],
    cast_dun_multi_mgr_cluster({send_to_team_on_cluster, Args}),
    ok;
send_to_team(MemberList, BinData) ->
    [
        lib_server_send:send_to_uid(Node, RoleId, BinData) ||
        #dun_multi_member{node = Node, id = RoleId, is_dummy = IsDummy} <- MemberList,
        IsDummy == 0
    ],
    ok.

send_to_team_on_cluster(TeamId, BinData) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [#dun_multi_team{members = Members}] ->
            send_to_team(Members, BinData);
        _ -> ok
    end,
    ok.

%% ---------------------------------------------------------------------------
%% @doc 登录cast队伍
-spec login_cast_team(Node, RoleId, TeamId) -> ok when
    Node           :: port(),
    RoleId         :: integer(),
    TeamId         :: integer().    %% 队伍id
%% ---------------------------------------------------------------------------
login_cast_team(Node, RoleId, TeamId) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [#dun_multi_team{}] ->
            pack_team_info(Node, RoleId, TeamId);
        _ ->
            apply_to_local_role(Node, lib_dun_multi, role_quit_multi_team, [RoleId])
    end.

%% ---------------------------------------------------------------------------
%% @doc 获取总的队伍数量
-spec get_all_team_num(RoleId, Node, ServerTeamNum) -> ok when
    RoleId         :: integer(),
    Node           :: port(),
    ServerTeamNum  :: integer().    %% 游戏服队伍数量
%% ---------------------------------------------------------------------------
get_all_team_num(RoleId, Node, ServerTeamNum) ->
    Args = [RoleId, Node, ServerTeamNum],
    cast_dun_multi_mgr_cluster({get_all_team_num_on_cluster, Args}),
    ok.

get_all_team_num_on_cluster(RoleId, Node, ServerTeamNum) ->
    KfTeamL = ets:tab2list(?DUN_MULTI_TEAM_ETS),
    SumTeam = ServerTeamNum + length(KfTeamL),
    {ok, BinData} = pt_240:write(24059, [SumTeam]),
    lib_server_send:send_to_uid(Node, RoleId, BinData).

%% ---------------------------------------------------------------------------
%% @doc 分页获取总的队伍信息
-spec get_all_team(TeamListSub, Sid, Node, TeamLength, PageSize, PageNo) -> ok when
    TeamListSub    :: list(),       %% 游戏服打包队伍信息
    Sid            :: port(),
    Node           :: port(),
    TeamLength     :: integer(),    %% 游戏服队伍总数
    PageSize       :: integer(),    %% 页大小
    PageNo         :: integer().    %% 页号
%% ---------------------------------------------------------------------------
get_all_team(TeamListSub, Sid, Node, TeamLength, PageSize, PageNo) ->
    Agrs = [TeamListSub, Sid, Node, TeamLength, PageSize, PageNo],
    cast_dun_multi_mgr_cluster({get_all_team_on_cluster, Agrs}),
    ok.

get_all_team_on_cluster(TeamListSub, Sid, Node, TeamLength, PageSize, PageNo) ->
    KfTeamL1 = ets:tab2list(?DUN_MULTI_TEAM_ETS),
    Fun = fun(#dun_multi_team{is_start = IsStart, members = Members, max_num = MaxNum}) ->
         IsStart == 0 andalso length(Members) < MaxNum
    end,
    KfTeamL2 = lists:filter(Fun, KfTeamL1),
    KfTeamL = lists:reverse(lists:keysort(#dun_multi_team.id, KfTeamL2)),
    case TeamListSub =/= [] of
        true ->
            LeftNum = max(PageSize - length(TeamListSub), 1),
            KfSubTeamL = lists:sublist(KfTeamL, 1, LeftNum),
            TeamList = pack_server_team_info(KfSubTeamL, lists:reverse(TeamListSub) );
        false ->
            StartPos = max(PageSize * (PageNo - 1) - TeamLength, 0) + 1,
            case length(KfTeamL) >= StartPos of
                true ->
                    KfSubTeamL = lists:sublist(KfTeamL, StartPos, PageSize),
                    TeamList = pack_server_team_info(KfSubTeamL, []);
                false -> TeamList = TeamListSub
            end
    end,
    % ?PRINT("~p~n",[{TeamList}]),
    {ok, BinData} = pt_240:write(24012, [PageSize, PageNo, TeamList]),
    lib_server_send:send_to_sid(Node, Sid, BinData),
    ok.

%%打包成游戏服所需格式
pack_server_team_info([], TeamListSub) ->
    lists:reverse(TeamListSub);
pack_server_team_info([Team | T], TeamListSub) ->
    #dun_multi_team{id = TeamId, dun_id = DunId, power = Power, min_lv = MinLv, members = Members, max_num = MaxNum} = Team,
    case data_team_ui:get_act_key(DunId) of
        [{ActivityId, SubId}|_T] -> ok;
        _ -> ActivityId = SubId = 0
    end,
    case lists:keyfind(1, #dun_multi_member.is_leader, Members) of
        #dun_multi_member{
            id = LeaderId, figure = LeaderFigure, add_times = AddTimes, daily_times = DailyTimes
        } ->
            MaxTimes = lib_dun_multi:get_max_times(DunId),
            LeftTimes = max(MaxTimes + AddTimes - DailyTimes, 0);
        _ ->
            LeaderId = LeftTimes = 0, LeaderFigure = #figure{}
    end,
    Info = {TeamId, LeaderId, LeaderFigure, LeftTimes, ActivityId, SubId, length(Members), MaxNum, MinLv, Power, 0},
    pack_server_team_info(T, [Info | TeamListSub]).

%% ---------------------------------------------------------------------------
%% @doc 查询对应副本的队伍列表
-spec send_dun_teams(Role, DunId) -> ok when
    Role        :: #player_status{},
    DunId       :: integer().    %%副本id
%% ---------------------------------------------------------------------------
send_dun_teams(Role, DunId) ->
    #player_status{sid = Sid} = Role,
    Node = node(),
    Msg = {send_dun_teams, Node, Sid, DunId},
    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
    ok.

%% ---------------------------------------------------------------------------
%% @doc 创建多人副本组队
-spec create_team(Role, DunId0, MinLv, Power, Password, TeamHandle, IsReady, NeedApprove, SingleMode, MergeType) ->
    {false, Reason} | {ok, NewRole, TeamId, ReturnId} when
    Role        :: #player_status{},
    DunId0      :: integer(),           %% 副本Id
    MinLv       :: integer(),
    Power       :: integer(),
    Password    :: integer(),
    TeamHandle  :: tuple(),
    Reason      :: integer(),
    NewRole     :: #player_status{},
    TeamId      :: integer(),
    ReturnId    :: integer(),
    IsReady     :: 0|1,
    NeedApprove :: 1|2,     %% 1需要|2不需要
    SingleMode  :: 0|1,     %% 0默认模式|1单人模式
    MergeType   :: 0|1.      %% 0默认|1合并类型
%% ---------------------------------------------------------------------------
create_team(Role, DunId0, MinLv, Power, Password, TeamHandle, IsReady, NeedApprove, SingleMode, MergeType) ->
    case check_create_team(Role, DunId0, MergeType) of
        {false, Reason} ->
            {false, Reason};
        {true, DunId} ->
            #player_status{id = RoleId, dun_multi = #role_dun_multi{merge_type = MergeType1}} = Role,
            #dun_multi_member{typical_data = TypicalData} = Member = lib_dun_multi:role_to_multi_member(DunId, Role),
            TypicalData1 = lib_dungeon_crusade:get_role_typical_data(RoleId, DunId, MergeType1, TypicalData),
            NewMember = Member#dun_multi_member{is_leader = 1, is_ready = IsReady, typical_data = TypicalData1},
            {IsMatch, MatchTime} = get_create_team_match_state(DunId),
            MultiTeam = #dun_multi_team{dun_id = DunId, sub_type_dun = DunId0, is_auto_start = 1, 
                is_match = IsMatch, match_time = MatchTime, min_lv = MinLv, power = Power, password = Password, 
                need_approve = NeedApprove, single_mode = SingleMode},
            Msg = {create_team, MultiTeam, NewMember, TeamHandle, 0},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
                {false, Rea} ->
                    {false, Rea};
                {ok, TeamId} ->
                    IsLeader = 1,
                    NewRole = lib_dun_multi:role_join_multi_team(Role, TeamId, DunId, IsLeader, IsMatch),
                    % ReturnId = get_return_dun_id(DunId),
                    {ok, NewRole, TeamId, DunId0};
                _Reason ->
                    Msg1 = {team_fail_handle, DunId, node(), Role#player_status.id},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
                    ?ERR("create_team time out~p~n",[_Reason]),
                    {false, ?ERRCODE(err215_time_out_request)}
            end
    end.

get_create_team_match_state(DunId) ->
    Now = utime:unixtime(),
    case data_dun_multi_team:get_dun_act_type(DunId) of
        ?ACT_TYPE_CRUSADE ->
            {1, Now};
        _ ->
            case lists:member(data_dungeon:get_dun_type(DunId), ?MATCH_DUNGEON) of
                true ->
                    {1, Now};
                _ ->
                    {0, 0}
            end
    end.

get_return_dun_id(DunId) ->
    case data_dun_multi_team:get_dun_act_type(DunId) of
        ?ACT_TYPE_CRUSADE -> %% 讨伐副本返回活动子类
            data_dun_multi_team:get_dun_sub_type(DunId);
        _ ->
            DunId
    end.

get_real_dun_id(DunId) ->
    case data_dun_multi_team:get_dun_act_type(DunId) of
        ?ACT_TYPE_CRUSADE ->
            SubType = data_dun_multi_team:get_dun_sub_type(DunId),
            create_rand_dun(SubType);
        _ ->
            DunId
    end.

get_act_pid_map_id(DunId) ->
    case data_dun_multi_team:get_dun_act_type(DunId) of
        ?ACT_TYPE_CRUSADE ->
            case data_dun_multi_team:get_dun_sub_type(DunId) of
                MapId when MapId =/= 0 ->
                    MapId;
                _ ->
                    DunId
            end;
        _ ->
            DunId
    end.

check_create_team(Role, DunId, _MergeType) ->
    #player_status{id = _RoleId, dun_multi = DunMulti, figure = #figure{lv = RoleLv}} = Role,
    #role_dun_multi{team_id = TeamId, merge_type = MergeType1} = DunMulti,
    IsMultiDun = lib_dungeon:is_cluster_dungeon_by_dun_id(DunId),
    CheckCond = lib_dun_multi:check_dungeon_condition(Role, DunId, create_team, MergeType1),
    MergeLvLimit = data_dungeon_special:get(?DUNGEON_TYPE_CRUSADE, merges_lv_2),
    % MergePassLimit = check_multi_dun_ever_finish(MergeType, RoleId, DunId),
    if
        IsMultiDun =:= false ->
            {false, ?ERRCODE(err215_not_dun_multi)};
        TeamId =/= 0 -> %% 已经有多人副本组队
            {false, ?ERRCODE(err215_have_team)};
        CheckCond =/= true ->
            {false, ErrCode} = CheckCond,
            lib_dun_multi:unpack_error(ErrCode);
        MergeType1 > 0 andalso RoleLv < MergeLvLimit -> %% 合并选项等级限制
            {false, ?ERRCODE(err610_mer_lv_limit)}; 
        % not MergePassLimit ->
        %     {false, ?ERRCODE(err610_dun_pet_not_finish)};
        true ->
            RealDunId = get_real_dun_id(DunId),
            {true, RealDunId}
    end.

% check_multi_dun_ever_finish(0, _, _DunId) ->
%     true;
% check_multi_dun_ever_finish(_MergeType, RoleId, DunId) ->
%     #dun_cfg{type = DunType} = data_dungeon:get_dun(DunId),
%     if DunType == ?DUNGEON_TYPE_CRUSADE ->
%             DunIds = data_dungeon:get_ids_by_type(DunType),
%             DunCountList = [{?MOD_DUNGEON, ?MOD_DUNGEON_SUCCESS, DunIdX} || DunIdX <- DunIds],
%             DunCountList1 = lib_daily:get_count(RoleId, DunCountList),
%             DunNotPass = lists:any(fun({_, DunCount}) -> 
%                 DunCount =< 0
%             end, DunCountList1),
%             if DunNotPass -> false;
%                 true -> true
%             end;
%         true ->
%             true
%     end.

% check_create_team_after_match_fail(Role, DunId) ->
%     #player_status{dun_multi = DunMulti, team = #status_team{team_id = OldTeamId}} = Role,
%     #role_dun_multi{team_id = TeamId} = DunMulti,
%     IsMultiDun = lib_dungeon:is_cluster_dungeon_by_dun_id(DunId),
%     CheckCond = lib_dun_multi:check_dungeon_condition(Role, DunId),
%     if
%         IsMultiDun =:= false ->
%             {false, ?ERRCODE(err215_not_dun_multi)};
%         OldTeamId =/= 0 orelse TeamId =/= 0 -> %% 已经有多人副本组队
%             {false, ?ERRCODE(err215_have_team)};
%         CheckCond =/= true ->
%             {false, ErrCode} = CheckCond,
%             lib_dun_multi:unpack_error(ErrCode);
%         true ->
%             true
%     end.

create_rand_dun(SubType) ->
    case data_dungeon_special:get(SubType, crusade_dun_rand) of
        RandList = [_|_] ->
            urand:rand_with_weight(RandList);
        _ ->
            DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_CRUSADE),
            urand:list_rand(DunIds)
    end.


%% ---------------------------------------------------------------------------
%% @doc 加入多人副本组队
-spec join_team(Role, TeamId, ClientDunId, Password, IsReady) ->
    {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    TeamId      :: integer(),
    ClientDunId :: integer(),
    Password    :: integer(),
    IsReady     :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
join_team(Role, TeamId, ClientDunId, Password, IsReady) ->
    case check_join_team(Role, TeamId, ClientDunId, IsReady, true) of
        {false, Reason} ->
            {false, Reason};
        {true, DunId} ->
            #player_status{id = RoleId, dun_multi = RoleDunMulti} = Role,
            case RoleDunMulti of
                #role_dun_multi{merge_type = MergeType} -> ok;
                _ -> MergeType = 0
            end,
            {IsInvited, InviteType} = check_is_invited(Role, TeamId, DunId),
            Member = lib_dun_multi:role_to_multi_member(DunId, Role),
            TypicalData = lib_dungeon_crusade:get_role_typical_data(RoleId, ClientDunId, MergeType, #{}),
            %% 作为大神被邀请默认入队为助战状态
            NewHelpType = ?IF(InviteType == 1, ?DUN_TEAM_HELP_YES, Member#dun_multi_member.help_type),
            NewMember = Member#dun_multi_member{is_ready = IsReady, help_type = NewHelpType, typical_data = TypicalData},
            Args = [NewMember, TeamId, DunId, Password, IsInvited],
            Msg = {apply_call, ?MODULE, join_team_on_cluster, Args},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
            % case catch call_dun_multi_mgr_cluster({join_team_on_cluster, Args}) of
                {false, Rea} ->
                    {false, Rea};
                {leader_approve} -> %% 等待审批
                    {false, ?ERRCODE(err215_join_req_done)};
                {ok, DunId1, IsMatch} ->
                    IsLeader = 0,
                    NewRole = lib_dun_multi:role_join_multi_team(Role, TeamId, DunId1, IsLeader, IsMatch),
                    DunType = data_dungeon:get_dun_type(DunId1),
                    if InviteType == 1 -> {ok, BinData} = pt_215:write(21540, [?SUCCESS, DunType, NewHelpType]);
                        true -> {ok, BinData} = pt_215:write(21540, [?SUCCESS, DunType, Member#dun_multi_member.help_type])
                    end,
                    lib_server_send:send_to_sid(Role#player_status.sid, BinData),
                    {ok, NewRole};
                _Reason ->
                    Msg1 = {leave_team, {Role#player_status.id}, TeamId},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
                    ?ERR("join_team_local time out~p~n",[_Reason]),
                    {false, ?ERRCODE(err215_time_out_request)}
            end
    end.

check_join_team(Role, TeamId, DunId, IsReady) ->
    check_join_team(Role, TeamId, DunId, IsReady, false).

-spec check_join_team(Role, TeamId, DunId, IsReady, IsAllowCrusade) -> ReturnType when
    Role :: #player_status{},
    TeamId :: integer(),
    DunId :: integer(),             %% 若允许加入讨伐副本队伍,则此处副本id需是真实正确的副本id
    IsReady :: 0 | 1,
    IsAllowCrusade :: boolean(),    %% 是否允许主动加入讨伐副本队伍
    ReturnType :: {boolean(), integer()}.
check_join_team(Role, TeamId, DunId, IsReady, IsAllowCrusade) ->
    #player_status{dun_multi = DunMulti} = Role,
    #role_dun_multi{team_id = RoleTeamId, req_list = ReqList} = DunMulti,
    CheckCond = lib_dun_multi:check_dungeon_condition(Role, DunId, join_team),
    IsReq = lists:keymember(TeamId, 1, ReqList),
    if
        RoleTeamId =/= 0 -> %% 已经有多人副本组队
            {false, ?ERRCODE(err215_have_team)};
        IsReady < 0 orelse IsReady > 1 ->
            {false, ?FAIL};
        CheckCond =/= true ->
            {false, ErrCode} = CheckCond,
            lib_dun_multi:unpack_error(ErrCode);
        IsReq ->
            {false, ?ERRCODE(err215_have_req)};
        true ->
            case data_dun_multi_team:get_dun_act_type(DunId) of
                ?ACT_TYPE_CRUSADE ->
                        if IsAllowCrusade =/= true ->
                                {false, ?ERRCODE(err215_role_match_dun_error)};
                            true ->
                                {true, DunId}
                        end;
                _ ->
                    {true, DunId}
            end
    end.

check_join_crusade_team(Role, SubType) ->
    #player_status{dun_multi = DunMulti, scene = SceneId} = Role,
    #role_dun_multi{team_id = RoleTeamId} = DunMulti,
    DunL = data_dun_multi_team:get_type_duns(?ACT_TYPE_CRUSADE, SubType),
    IsGuildScene = lib_guild:is_guild_scene(SceneId),
    if
        DunL == [] ->
            {false, ?ERRCODE(err215_subtype_error)};
        IsGuildScene ->
            {false, ?ERRCODE(err400_pls_exit_guild_scene)};
        RoleTeamId =/= 0 -> %% 已经有多人副本组队
            {false, ?ERRCODE(err215_have_team)};
        true ->
            [DunId|_] = DunL,
            CheckCond = lib_dun_multi:check_dungeon_condition(Role, DunId, join_team),
            if CheckCond =/= true ->
                    {false, ErrCode} = CheckCond,
                    lib_dun_multi:unpack_error(ErrCode);
                true ->
                    true
            end
    end.

%% 跨服对应组队进程检测
join_team_on_cluster([Member, TeamId, ClientDunId, Password, IsInvited|_]) ->
    case check_join_team_on_cluster(Member, TeamId, ClientDunId, Password, IsInvited) of
        {false, Reason} ->
            {false, Reason};
        {leader_approve, LeaderNode, LeaderId} ->
            join_team_request(Member, TeamId),
            #dun_multi_member{figure = Figure, server_num = SvrNum} = Member,
            lib_server_send:send_to_uid(LeaderNode, LeaderId, pt_215, 21536, [Figure, SvrNum, utime:unixtime()]),
            {leader_approve};
        true ->
            cls_join_team(Member, TeamId)
    end.

check_join_team_on_cluster(Member, TeamId, ClientDunId, Password, IsInvited) ->
    #dun_multi_member{id = RoleId, power = CombatPower, figure = #figure{lv = Lv}} = Member,
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [Team = #dun_multi_team{members = Members, is_start = IsStart, handle_mb = {AllowIds, Time}, need_approve = NeedApprove}] -> ok;
        _ -> IsStart = 0, AllowIds = [], Members = [], Time = 0, Team = false, NeedApprove = 2
    end,
    IsAlreadyInTeam = lists:keyfind(Member#dun_multi_member.id, #dun_multi_member.id, Members),
    if
        Team =:= false ->
            {false, ?ERRCODE(err215_no_team)};
        IsAlreadyInTeam =/= false ->
            {false, ?ERRCODE(err215_have_in_team)};
        true ->
            #dun_multi_team{sub_type_dun = DunId, power = Power, password = TeamPassword, min_lv = MinLv, is_gm = IsGm} = Team,
            CheckCond = lib_dun_multi:check_dungeon_condition_on_multi_member(Member, DunId, [Member|Members], join_team),
            Now = utime:unixtime(),
            HandleType = AllowIds =/= [] andalso lists:member(RoleId, AllowIds) == false andalso Now < Time,
            if
            	IsStart =/= 0 ->
            		{false, ?ERRCODE(err215_is_on_dungeon)};
                ClientDunId =/= DunId ->
                    {false, ?FAIL};
                CombatPower < Power ->
                    {false, ?ERRCODE(err215_less_power)};
                IsInvited == 0 andalso TeamPassword =/= 0 andalso Password =/= TeamPassword ->
                    {false, ?ERRCODE(err215_password_error)};
                Lv < MinLv ->
                	{false, ?ERRCODE(err215_less_lv)};
                HandleType ->
                    {false, ?ERRCODE(err215_is_change_now)};
                IsGm == 1 ->
                    {false, ?ERRCODE(err215_is_dummy_team)};
                CheckCond =/= true ->
                    CheckCond;
                NeedApprove =/= 2 -> %% 需要审批
                    case lists:keyfind(1, #dun_multi_member.is_leader, Members) of
                        #dun_multi_member{id = LeaderId, node = LeaderNode} ->
                            if IsInvited == 1 ->
                                    true;
                                true ->
                                    {leader_approve, LeaderNode, LeaderId}
                            end;
                        _ ->
                            true
                    end;
                true ->
                    true
            end
    end.

cls_join_team(Member, TeamId) ->
    [#dun_multi_team{dun_id = DunId}] = ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId),
    % Msg = {join_team, Member, TeamId},
    % case catch call_dun_multi_hall_cluster(DunId, Msg) of
    case mod_dun_multi_hall_cluster:join_team(Member, TeamId) of
        {false, Rea} ->
            {false, Rea};
        {ok, IsMatch} ->
            {ok, DunId, IsMatch};
        {ok, DunId, IsMatch} ->
            {ok, DunId, IsMatch};
        _Reason ->
            Msg1 = {leave_team, {Member#dun_multi_member.id}, TeamId},
            cast_dun_multi_hall_cluster(DunId, Msg1),
            ?ERR("join_team_center time out~p~n",[_Reason]),
            {false, ?ERRCODE(err215_time_out_request)}
    end.

join_team_request(Member, TeamId) ->
    mod_dun_multi_hall_cluster:join_team_request(Member, TeamId),
    ok.

% match_join_team(Role, Type, DunId) ->
%     #player_status{dun_multi = #role_dun_multi{team_id = TeamId}} = Role,
%     if
%         TeamId > 0 -> match_handle_team(Role, Type);
%         true -> match_handle_role(Role, Type, DunId)
%     end.

%% 连续讨伐
macth_or_create_team(Role, DunType, SubType, MatchType) ->
    #player_status{dun_multi = #role_dun_multi{team_id = TeamId}} = Role,
    if
        TeamId > 0 -> match_handle_team(Role, MatchType);
        true ->
            if DunType == ?DUNGEON_TYPE_CRUSADE -> %% SubType为特殊子类
                    match_crusade_role(Role, SubType, MatchType);
                true -> %% SubType为副本id
                    match_handle_role(Role, MatchType, SubType)
            end
    end.
    

%%队伍匹配
match_handle_team(Role, Type) ->
    #player_status{id = RoleId, dun_multi = #role_dun_multi{team_id = TeamId, dun_id = NowDunId}} = Role,
    MatchDunId = NowDunId,
    IsFirst = is_first(Role, MatchDunId),
    Msg = {match_handle_team, TeamId, RoleId, node(), Type, IsFirst},
    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [MatchDunId, Msg]),
    {ok, Role}.

%%个人匹配
match_handle_role(Role, MatchType, DunId0) ->
    case check_join_team(Role, 0, DunId0, 1) of
        {false, Reason} ->
            {false, Reason};
        {true, DunId} ->
            Member = lib_dun_multi:role_to_multi_member(DunId, Role),
            NewMember = Member#dun_multi_member{is_ready = 1},
            IsFirst = is_first(Role, DunId),
            Msg = {match_join_team, DunId, NewMember, IsFirst},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
                {false, _Rea} ->
                    {false, _Rea};
                {ok, TeamId, NewIsLeader, IsMatch} -> %% 成功加入匹配队伍
                    NewRole = lib_dun_multi:role_join_multi_team(Role, TeamId, DunId, NewIsLeader, IsMatch),
                    {ok, BinData} = pt_215:write(21524, [?SUCCESS, MatchType]),
                    lib_server_send:send_to_sid(Role#player_status.sid, BinData),
                    {ok, NewRole};
                _Reason -> %% 超时
                    Msg1 = {team_fail_handle, DunId, node(), Role#player_status.id},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
                    ?ERR("create_team time out~p~n",[_Reason]),
                    {false, ?ERRCODE(err215_time_out_request)}
            end
    end.

%% 连续讨伐匹配
match_crusade_role(Role, SubType, MatchType) ->
    case check_join_crusade_team(Role, SubType) of
        {false, Reason} ->
            {false, Reason};
        true ->
            #player_status{id = RoleId, dun_multi = #role_dun_multi{merge_type = MergeType}} = Role,
            Member = lib_dun_multi:multi_crusade_to_member(Role),
            [RandDunId|_] = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_CRUSADE),
            TypicalData = lib_dungeon_crusade:get_role_typical_data(RoleId, RandDunId, MergeType, #{}),
            NewMember = Member#dun_multi_member{is_ready = MatchType, typical_data = TypicalData},
            Msg = {match_join_crusade_team, SubType, NewMember, _IsFirst = 0},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [SubType, Msg]) of
                {false, _Rea} ->
                    {false, _Rea};
                {ok, TeamId, DunId, IsLeader, IsMatch} ->
                    NewRole = lib_dun_multi:role_join_multi_team(Role, TeamId, DunId, IsLeader, IsMatch),
                    IsFirst = lib_dungeon_api:check_ever_finish(Role, DunId),
                    Msg1 = {update_role_attr, TeamId, Role#player_status.id, [{is_first, IsFirst}]},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [SubType, Msg1]),
                    if IsLeader == 1 ->
                            {ok, BinData} = pt_215:write(21524, [?SUCCESS, MatchType]),
                            lib_server_send:send_to_sid(Role#player_status.sid, BinData);
                        true ->
                            {ok, BinData} = pt_215:write(21524, [?SUCCESS, MatchType]),
                            {ok, BinData1} = pt_215:write(21503, [?SUCCESS, TeamId, DunId]),
                            lib_server_send:send_to_sid(Role#player_status.sid, <<BinData/binary, BinData1/binary>>)
                    end,
                    {ok, NewRole};
                _Reason ->
                    Msg1 = {team_fail_handle, SubType, node(), Role#player_status.id},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [SubType, Msg1]),
                    ?ERR("match crusade team time out ~p ~p", [SubType, _Reason]),
                    {false, ?ERRCODE(err215_time_out_request)}
            end
    end.

is_first(Role, DunId = 3110) ->
    #player_status{id = RoleId} = Role,
    Count = lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId),
    case Count > 0 of
        true -> 0;
        false -> 1
    end;
is_first(_Role, _DunId) -> 0.

finish_first(Role, DunId = 3110) ->
    #player_status{id = RoleId} = Role,
    case lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId) of
        Count when Count =< 0 ->
            lib_counter:increment_offline(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId);
        _ -> ok
    end;
finish_first(_, _) -> ok.

%% ---------------------------------------------------------------------------
%% @doc 离开多人副本组队
-spec leave_team(Role) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
leave_team(Role) ->
    #player_status{id = RoleId, dun_multi = DunMulti} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId, dun_pid = DunCopyId} = DunMulti,
    IsAlive = misc:is_process_alive(DunCopyId),
    if
        TeamId == 0 -> {false, ?ERRCODE(err215_not_in_team)};
        IsAlive -> {false, ?ERRCODE(err215_is_on_dungeon)};
        true ->
            RoleKey = {RoleId},
            Msg = {leave_team, RoleKey, TeamId},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            NewRole = lib_dun_multi:role_quit_multi_team(Role),
            {ok, NewRole}
    end.

%% ---------------------------------------------------------------------------
%% @doc 踢出成员
-spec kick_out(Role, MemberKey) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    MemberKey   :: tuple(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
kick_out(Role, MemberKey) ->
    #player_status{id = RoleId, dun_multi = DunMulti, sid = Sid} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
    RoleKey = {RoleId},
    case TeamId of
        0 -> %% 不在多人副本组队
            {false, ?ERRCODE(err215_not_in_team)};
        _ ->
            Msg = {kick_out, RoleKey, Sid, MemberKey, TeamId},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            {ok, Role}
    end.

%% ---------------------------------------------------------------------------
%% @doc 切换准备状态
-spec change_ready_status(Role, IsReady) -> {false, Reason} | {ok, NewRole} when
    Role        :: #player_status{},
    IsReady     :: integer(),
    Reason      :: integer(),
    NewRole     :: #player_status{}.
%% ---------------------------------------------------------------------------
change_ready_status(Role, IsReady) ->
    #player_status{id = RoleId, dun_multi = DunMulti, sid = Sid} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
    if
        TeamId =:= 0 -> %% 不在多人副本组队
            {false, ?ERRCODE(err215_not_in_team)};
        IsReady < 0 orelse IsReady > 1 ->
            {false, ?FAIL};
        true ->
            RoleKey = {RoleId},
            Msg = {change_ready_status, RoleKey, Sid, TeamId, IsReady},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            {ok, Role}
    end.

%% ---------------------------------------------------------------------------
%% @doc 开启多人副本
-spec start_dun_multi(Role) -> {false, Reason} | {ok} when
    Role        :: #player_status{},
    Reason      :: integer().
%% ---------------------------------------------------------------------------
start_dun_multi(Role) ->
    #player_status{id = RoleId, dun_multi = DunMulti, sid = Sid, scene = SceneId, action_lock = ActionLock} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
    IsGuildScene = lib_guild:is_guild_scene(SceneId),
    % ErrCode = ?ERRCODE(err215_on_multi_team),
    if 
        ActionLock =/= free -> %% 当前处于其他活动中
            {false, ActionLock};
        IsGuildScene ->
            {false, ?ERRCODE(err400_pls_exit_guild_scene)};
        TeamId == 0 -> %% 不在多人副本组队
            {false, ?ERRCODE(err215_not_in_team)};
        true ->
            RoleKey = {RoleId},
            Msg = {start_dun_multi, RoleKey, Sid, TeamId},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            {ok}
    end.

%% ---------------------------------------------------------------------------
%% @doc 查询多人副本队伍信息。在跨服执行
-spec pack_team_info(Node, RoleSid, TeamId) -> ok when
    Node        :: atom(),
    RoleSid     :: pid(),
    TeamId      :: integer().
%% ---------------------------------------------------------------------------
pack_team_info(Node, RoleId, TeamId) ->
    ProData = case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [Team] ->
            pack_team_info(Team);
        _ ->
            [TeamId, 0, 0, 0, 0, 0, 0, 0, []]
    end,
    {ok, BinData} = pt_215:write(21504, ProData),
    % ?PRINT("~p~n",[ProData]),
    lib_server_send:send_to_uid(Node, RoleId, BinData),
    ok.

pack_team_info(TeamId) when is_integer(TeamId) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [Team = #dun_multi_team{}] ->
            pack_team_info(Team);
        _ ->
            [TeamId, 0, 0, 0, 0, 0, 0, 0, []]
    end;

pack_team_info(Team = #dun_multi_team{}) ->
    #dun_multi_team{
        id = TeamId, power = Power, is_auto_start = IsAuto
        ,members = Members, min_lv = MinLv
        ,is_match = IsMatch, password = Password
        ,sub_type_dun = DunId
    } = Team,
    NeedPassword = is_need_password(Password),
    MaxTimes = lib_dun_multi:get_max_times(DunId),
    Fun = fun(Member) ->
            #dun_multi_member{
                id = RoleId, server_id = ServerId, server_num = ServerNum
                ,figure = Figure, power = MPower, is_leader = IsLeader
                ,is_ready = IsReady, help_type = HelpType, add_times = AddTimes
                ,daily_times = DailyTimes, is_dummy = IsDummy
            } = Member,
            {RoleId, ServerId, ServerNum, IsLeader, HelpType, Figure, max(MaxTimes + AddTimes - DailyTimes, 0), MPower, IsReady, IsDummy}
    end,
    MemberList = [Fun(OneMem) || OneMem <- Members],
    [TeamId, DunId, IsAuto, MinLv, Power, IsMatch, NeedPassword, Password, MemberList].

%% ---------------------------------------------------------------------------
%% @doc 招募队员
-spec recruit_member(Role, Type) -> {false, Reason} | {ok} when
    Role        :: #player_status{},
    Type        :: integer(),         %% 1公会;2世界
    Reason      :: integer().
%% ---------------------------------------------------------------------------
recruit_member(Role, Type) ->
    #player_status{
        dun_multi = DunMulti, sid = Sid, figure = #figure{name=Name, guild_id = GuildId}
    } = Role,
    #role_dun_multi{team_id = TeamId} = DunMulti,
    case TeamId of
        0 -> %% 不在多人副本组队
            {false, ?ERRCODE(err215_not_in_team)};
        _ ->
            FigureArgs = [Name, GuildId],
            Agrs = [node(), Sid, Type, TeamId, FigureArgs],
            cast_dun_multi_mgr_cluster({recruit_member_on_cluster, Agrs}),
            {ok}
    end.

recruit_member_on_cluster(Node, RoleSid, Type, TeamId, FigureArgs) ->
    [RoleName, GuildId | _T] = FigureArgs,
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [#dun_multi_team{dun_id = DunId}] ->
            case data_dungeon:get_dun(DunId) of
                #dun_cfg{name = DungeonName} -> ok;
                _ -> DungeonName = ""
            end,
            OpenLv = lib_dun_multi:get_dun_open_lv(DunId),
            %% 只在本服广播
            {TvType, TvId} = ?IF(Type == 1, {{guild, GuildId}, 1}, {{all}, 2}),
            clusters_center:apply_cast(
               Node, lib_chat, send_TV, [TvType, ?MOD_DUN_MULTI, TvId, [RoleName, DungeonName, OpenLv, DunId, TeamId]]
            ),
            ok;
        _ ->
            {ok, BinData} = pt_215:write(21514, {?ERRCODE(err215_not_in_team)}),
            lib_server_send:send_to_sid(Node, RoleSid, BinData),
            ok
    end.
%% ---------------------------------------------------------------------------
%% @doc 修改设置
-spec update_set_info(Role, MinLv, Power, Password) -> {false, Reason} | {ok} when
    Role        :: #player_status{},
    MinLv       :: integer(),
    Power       :: integer(),
    Password    :: integer(),
    Reason      :: integer().
%% ---------------------------------------------------------------------------
%%修改设置
update_set_info(Role, MinLv, Power, Password) ->
    #player_status{dun_multi = DunMulti, id = RoleId} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
    case TeamId of
        0 -> %% 不在多人副本组队
            {false, ?ERRCODE(err215_not_in_team)};
        _ ->
            Msg = {update_set_info, {RoleId}, node(), TeamId, MinLv, Power, Password},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            {ok}
    end.

%% ---------------------------------------------------------------------------
%% @doc 委任队长
-spec change_leader(Role, ToId) -> {false, Reason} | {ok} when
    Role        :: #player_status{},
    ToId        :: integer(),
    Reason      :: integer().
%% ---------------------------------------------------------------------------
change_leader(Role, ToId) ->
    #player_status{dun_multi = DunMulti, id = RoleId} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
    if
        TeamId =< 0 -> {false, ?ERRCODE(err215_not_in_team)};
        RoleId == ToId -> {false, ?ERRCODE(err215_not_change_self)};
        true ->
            Msg = {change_leader, {RoleId}, node(), TeamId, ToId},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            {ok}
    end.

%% ---------------------------------------------------------------------------
%% @doc 催促队长
-spec prompt_enter(Role) -> {false, Reason} | {ok} when
    Role        :: #player_status{},
    Reason      :: integer().
%% ---------------------------------------------------------------------------
prompt_enter(Role) ->
    #player_status{dun_multi = DunMulti, id = RoleId} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
    if
        TeamId =< 0 -> {false, ?ERRCODE(err215_not_in_team)};
        true ->
            Msg = {prompt_enter, {RoleId}, node(), TeamId},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            {ok}
    end.

%% 是否需要密码
%% 0 不需要|1 需要 密码为纯数字6位整型数
-spec is_need_password(Password :: integer()) -> ReturnType :: integer().
is_need_password(Password) ->
    if Password =/= 0 ->
            1;
        true ->
            0
    end.

%% 获取可邀请的玩家列表
-spec get_access_invite_role(Role, DunId, InviteType) -> Ret when
    Role        :: #player_status{},
    DunId       :: integer(),
    InviteType  :: 0|1|2, %% 0好友|1社团|2支援榜
    Ret         :: ok | skip | term().
get_access_invite_role(Role, DunId, InviteType) ->
    #player_status{id = RoleId, sid = Sid, guild = Guild, dun_multi = DunMulti} = Role,
    #role_dun_multi{team_id = TeamId} = DunMulti,
    DunType = data_dungeon:get_dun_type(DunId),
    Node = node(),
    if TeamId =< 0 ->
            {ok, BinData} = pt_215:write(21526, [?ERRCODE(err215_not_in_team), DunId, InviteType, []]),
            lib_server_send:send_to_sid(Sid, BinData),
            ok;
        InviteType =/= 0 andalso InviteType =/= 1 andalso InviteType =/= 2 ->
            {ok, BinData} = pt_215:write(21526, [?FAIL, DunId, InviteType, []]),
            lib_server_send:send_to_sid(Sid, BinData),
            ok;
        InviteType == 1 andalso Guild#status_guild.id == 0 ->
            {ok, BinData} = pt_215:write(21526, [?ERRCODE(err400_not_join_guild), DunId, InviteType, []]),
            lib_server_send:send_to_sid(Sid, BinData),
            ok;
        InviteType == 2 andalso DunType =/= ?DUNGEON_TYPE_GUARD ->
            {ok, BinData} = pt_215:write(21526, [?FAIL, DunId, InviteType, []]),
            lib_server_send:send_to_sid(Sid, BinData),
            ok;
        true ->
            if InviteType == 2 ->
                    guard_dun_assist_local:get_invite_assist_rank_role(Node, RoleId, DunId, TeamId);
                true ->
                    AccessRoleIds =
                    if InviteType == 1 ->
                            mod_guild:get_guild_member_id_list(Guild#status_guild.id) -- [RoleId];
                        true ->
                            lib_relationship:get_friend_id_on_dict(RoleId)
                    end,
                    AccessRoleInfos =
                    lists:foldl(fun(AccessRoleId, Acc) ->
                        case lib_role:get_role_show(AccessRoleId) of
                            #ets_role_show{online_flag = Online} ->
                                if Online == ?ONLINE_ON ->
                                        Figure = lib_role:get_role_figure(AccessRoleId),
                                        [{AccessRoleId, Figure, 0, 0}|Acc];
                                    true ->
                                        Acc
                                end;
                            _ -> Acc
                        end
                    end, [], AccessRoleIds),
                    {ok, BinData} = pt_215:write(21526, [?SUCCESS, DunId, InviteType, AccessRoleInfos]),
                    lib_server_send:send_to_sid(Sid, BinData),
                    ok
            end
    end.

%% 发起镜像协助
-spec dun_multi_mirror_help(Role, DunId, TeamId) -> Ret when
    Role :: #player_status{},
    DunId :: integer(),
    TeamId :: integer(),
    Ret :: {false, Reason} | {ok, NewRole},
    Reason :: integer(),
    NewRole :: #player_status{}.
dun_multi_mirror_help(Role, DunId, TeamId) ->
    #player_status{id = RoleId, passive_skills = SkillsPassive, dun_multi = RoleDunMulti} = Role,
    case mirror_help_check(Role, DunId, TeamId) of
        true ->
            Member      = lib_dun_multi:role_to_multi_member(DunId, Role),
            Skills      = lib_dummy_api:get_skill_list(Role),
            NewMember   = Member#dun_multi_member{
                is_dummy = ?DUN_MIRROR, skills = Skills, skills_passive = SkillsPassive, is_ready = 1, action_lock = 0},
            Password    = 0,
            {IsInvited, _} = check_is_invited(Role, TeamId, DunId),
            Args    = [NewMember, TeamId, DunId, Password, IsInvited],
            Msg     = {apply_call, ?MODULE, join_team_on_cluster, Args},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
                {false, Reason} ->
                    {false, Reason};
                {leader_approve} -> %% 等待审批
                    {ok, Role};
                {ok, _DunId1, _IsMatch} ->
                    NewRoleDunMulti = case RoleDunMulti of
                        #role_dun_multi{mirror_help = HelpList} ->
                            RoleDunMulti#role_dun_multi{mirror_help = [{TeamId, DunId}|HelpList]};
                        _ ->
                            #role_dun_multi{mirror_help = [{TeamId, DunId}]}
                    end,
                    NewRole = Role#player_status{dun_multi = NewRoleDunMulti},
                    DunType = data_dungeon:get_dun_type(DunId),
                    lib_dungeon_guard:local_update_guard_assist_count(RoleId, DunType),
                    {ok, NewRole};
                _Reason ->
                    Msg1 = {leave_team, {RoleId}, TeamId},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
                    % ?ERR("join_team_local time out ~p ~n",[_Reason]),
                    {false, ?ERRCODE(err215_time_out_request)}
            end;
        {false, Reason} ->
            {false, Reason}
    end.

%% 队长发起投票
-spec dun_multi_vote(Role, TeamId) -> Ret when
    Role :: #player_status{},
    TeamId :: integer(),
    Ret :: {false, Reason} | ok,
    Reason :: integer().
dun_multi_vote(Role, TeamId) ->
    case dun_mulit_vote_check(Role, TeamId) of
        true ->
            #player_status{id = RoleId, dun_multi = DunMulti} = Role,
            #role_dun_multi{dun_id = DunId} = DunMulti,
            Node = node(),
            Args = [Node, TeamId, DunId, RoleId],
            Msg = {apply_cast, ?MODULE, dun_multi_vote_on_cluster, [Args]},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            ok;
        {false, ErrCode} ->
            {false, ErrCode}
    end.

%% 队员开始投票
- spec team_mate_vote(Role, VoteRet) -> Ret when
    Role :: #player_status{},
    VoteRet :: 0|1,
    Ret :: term().
team_mate_vote(Role, VoteRet) ->
    case team_mate_vote_check(Role, VoteRet) of
        true ->
            #player_status{id = RoleId, dun_multi = DunMulti} = Role,
            #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
            Node = node(),
            Args = [Node, TeamId, DunId, RoleId, VoteRet],
            Msg = {apply_cast, ?MODULE, team_mate_vote_on_clusters, [Args]},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            ok;
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Role#player_status.sid, pt_215, 21531, [ErrCode, VoteRet])
    end.

%% 队长回应入队申请
reply_to_req_roles(Role, ReqList) ->
    case reply_action_check(Role) of
        true ->
            #player_status{id = RoleId, dun_multi = DunMulti} = Role,
            #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
            Node = node(),
            Args = [Node, TeamId, ReqList, RoleId],
            Msg = {apply_cast, ?MODULE, reply_to_req_roles_on_clusters, [Args]},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            ok;
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Role#player_status.sid, pt_215, 21537, [ErrCode, []])
    end.

%% 队长修改审批状态
change_approve_state(Role, Approve) ->
    case change_approve_check(Role) of
        true ->
            #player_status{id = RoleId, dun_multi = DunMulti} = Role,
            #role_dun_multi{team_id = TeamId, dun_id = DunId} = DunMulti,
            Node = node(),
            Args = [Node, TeamId, RoleId, Approve],
            Msg = {apply_cast, ?MODULE, change_approve_state_on_clusters, [Args]},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg]),
            ok;
        {false, ErrCode} ->
            {false, ErrCode}
    end.

%% 修改组队目标
change_team_target(Role, ChangeDunId, Approve) ->
    case change_team_target_check(Role, ChangeDunId, Approve) of
        true ->
            #player_status{id = RoleId, sid = Sid, dun_multi = RoleDunMulti} = Role,
            #role_dun_multi{team_id = TeamId, dun_id = DunId, is_leader = IsLeader} = RoleDunMulti,
            Node = node(),
            NewDunId = get_real_dun_id(ChangeDunId),
            Args = [TeamId, Node, RoleId, ChangeDunId, NewDunId, Approve],
            Msg = {apply_call, ?MODULE, change_team_target_on_clusters, Args},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
                {ok, NewDunId} -> %% 切换成功之后的处理
                    NewRole = lib_dun_multi:role_change_team_target(Role, TeamId, NewDunId, IsLeader),
                    % lib_server_send:send_to_sid(Sid, pt_215, 21534, [?SUCCESS, NewDunId, Approve]),
                    {ok, NewRole};
                {false, ErrCode} ->
                    lib_server_send:send_to_sid(Sid, pt_215, 21534, [ErrCode, ChangeDunId, Approve]),
                    ok;
                _Reason ->
                    Msg1 = {team_fail_handle, DunId, Node, RoleId},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
                    ?ERR("create_team time out~p~n", [_Reason]),
                    lib_server_send:send_to_sid(Sid, pt_215, 21534, [?ERRCODE(err215_time_out_request), ChangeDunId, Approve]),
                    ok
            end;
        {false, ErrCode} ->
            lib_server_send:send_to_sid(Role#player_status.sid, pt_215, 21534, [ErrCode, ChangeDunId, Approve]),
            ok
    end.

%% 切换协助状态
change_help_type(Role, HelpType) ->
    #player_status{id = RoleId, dun_multi = RoleDunMulti} = Role,
    #role_dun_multi{team_id = TeamId, dun_id = DunId, dun_pid = DunPid} = RoleDunMulti,
    if 
        DunPid =/= 0 ->
            {false, ?ERRCODE(err610_had_on_dungeon)};
        true ->
            Node = node(),
            Args = [TeamId, Node, RoleId, HelpType],
            Msg  = {apply_call, ?MODULE, change_help_type_on_clusters, Args},
            case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
                ok ->
                    ok;
                {false, ErrCode} ->
                    {false, ErrCode};
                _Reason ->
                    Msg1 = {team_fail_handle, DunId, Node, RoleId},
                    clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
                    ?ERR("create_team time out~p~n", [_Reason]),
                    {false, ?ERRCODE(err215_time_out_request)}
            end    
    end.

%% 切换合并次数状态
change_merge_type(Role, DunType, MergeType, RestTime) ->
    #player_status{id = RoleId, dun_multi = RoleDunMulti} = Role,
    #role_dun_multi{dun_id = DunId, team_id = TeamId} = RoleDunMulti,
    Node = node(),
    Args = [TeamId, Node, RoleId, MergeType, RestTime],
    Msg  = {apply_call, ?MODULE, change_merge_type_on_clusters, Args},
    Ret = 
    case catch clusters_node:apply_call(?MODULE, call_dun_multi_hall_cluster, [DunId, Msg]) of
        ok ->
            ok;
        {false, ErrCode} ->
            {false, ErrCode};
        _Reason ->
            Msg1 = {team_fail_handle, DunId, Node, RoleId},
            clusters_node:apply_cast(?MODULE, cast_dun_multi_hall_cluster, [DunId, Msg1]),
            ?ERR("create_team time out~p~n", [_Reason]),
            {false, ?ERRCODE(err215_time_out_request)}
    end,
    case Ret of
        ok ->
            lib_server_send:send_to_uid(RoleId, pt_215, 21541, [?SUCCESS, DunType, MergeType]),
            RoleDunMulti1 = RoleDunMulti#role_dun_multi{merge_type = MergeType},
            {ok, Role#player_status{dun_multi = RoleDunMulti1}};
        {false, Errcode} ->
            lib_server_send:send_to_uid(RoleId, pt_215, 21541, [Errcode, DunType, MergeType]),
            ok
    end.

dun_multi_vote_on_cluster([Node, TeamId, DunId, RoleId|_]) ->
    case check_vote_on_cluster(TeamId, DunId, RoleId) of
        {true, DunMultiTeam} ->
            mod_dun_multi_hall_cluster:dun_multi_vote(DunMultiTeam, Node, RoleId);
        {false, ErrCode} ->
            {ok, BinData} = pt_215:write(21530, [ErrCode, TeamId]),
            lib_server_send:send_to_uid(Node, RoleId, BinData)
    end.

team_mate_vote_on_clusters([Node, TeamId, DunId, RoleId, VoteRet|_]) ->
    case check_team_mate_vote_on_clusters(TeamId, DunId, RoleId) of
        {true, DunMultiTeam} ->
            mod_dun_multi_hall_cluster:team_mate_vote(DunMultiTeam, Node, RoleId, VoteRet);
        {false, ErrCode} ->
            {ok, BinData} = pt_215:write(21531, [ErrCode, VoteRet]),
            lib_server_send:send_to_uid(Node, RoleId, BinData)
    end.

reply_to_req_roles_on_clusters([Node, TeamId, ReqList, RoleId|_]) ->
    case check_reply_req_on_clusters(TeamId, ReqList, RoleId) of
        {true, DunMultiTeam, NewReqList} ->
            mod_dun_multi_hall_cluster:reply_to_req_roles(DunMultiTeam, NewReqList, RoleId);
        {false, ErrCode} ->
            {ok, BinData} = pt_215:write(21537, [ErrCode, []]),
            lib_server_send:send_to_uid(Node, RoleId, BinData)
    end.

change_approve_state_on_clusters([Node, TeamId, RoleId, Approve|_]) ->
    case check_change_approve_state_on_clusters(TeamId, RoleId, Approve) of
        {true, DunMultiTeam} ->
            mod_dun_multi_hall_cluster:change_approve_state(DunMultiTeam, Node, RoleId, Approve);
        {false, ErrCode} ->
            {ok, BinData} = pt_215:write(21538, [ErrCode, Approve]),
            lib_server_send:send_to_uid(Node, RoleId, BinData)
    end.

change_team_target_on_clusters([TeamId, Node, RoleId, EnterDunId, RealDunId, Approve|_]) ->
    case check_change_team_target_on_clusters(TeamId, RoleId, RealDunId, Approve) of
        {true, DunMultiTeam} -> %% 切换队伍操作
            mod_dun_multi_hall_cluster:change_team_target(DunMultiTeam, Node, RoleId, EnterDunId, RealDunId, Approve);
        {false, ErrCode} ->
            {false, ErrCode}
            % {ok, BinData} = pt_215:write(21534, [ErrCode, DunId, Approve]),
            % lib_server_send:send_to_uid(Node, RoleId, BinData),
    end.

change_help_type_on_clusters([TeamId, Node, RoleId, HelpType|_]) ->
    case check_change_help_type_on_clusters(TeamId, RoleId, HelpType) of
        {true, DunMultiTeam} ->
            mod_dun_multi_hall_cluster:change_help_type(DunMultiTeam, Node, RoleId, HelpType);
        {false, ErrCode} ->
            {false, ErrCode}
    end.

change_merge_type_on_clusters([TeamId, Node, RoleId, MergeType, RestTime|_]) ->
    case check_change_merge_type_on_clusters(TeamId, RoleId, MergeType, RestTime) of
        {true, DunMultiTeam} ->
            mod_dun_multi_hall_cluster:change_merge_type(DunMultiTeam, Node, RoleId, MergeType, RestTime);
        {false, Errcode} ->
            {false, Errcode}
    end.

%% 跨服中心执行
%% 改变队伍中的行为锁
update_team_req_action_lock(RoleId, ReqList, ActionLock) ->
    ActionLock1 = ?IF(ActionLock == free, 0, ActionLock),
    lists:map(fun({TeamId, _DunId}) -> 
        case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
            [Team = #dun_multi_team{req_list = RoleReqList}] ->
                case lists:keyfind(RoleId, #dun_multi_member.id, RoleReqList) of
                    Member = #dun_multi_member{} ->
                        NewRoleReqList = lists:keystore(RoleId, #dun_multi_member.id, RoleReqList, Member#dun_multi_member{action_lock = ActionLock1}),
                        ets:insert(?DUN_MULTI_TEAM_ETS, Team#dun_multi_team{req_list = NewRoleReqList});
                    _ ->
                        skip
                end;
            _ -> skip
        end
    end, ReqList),
    ok.

check_vote_on_cluster(TeamId, _DunId, RoleId) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{members = Members, is_start = IsStart, vote_ref = VoteRef}|_] ->
            LastTime =
            if VoteRef == undefined -> 0;
                true -> erlang:read_timer(VoteRef)
            end,
            if IsStart == 1 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                is_integer(LastTime) andalso LastTime > 0 ->
                    {false, ?ERRCODE(err215_in_vote)};
                true ->
                    case lists:keyfind(RoleId, #dun_multi_member.id, Members) of
                        #dun_multi_member{is_leader = IsLeader, help_type = LeaderHelpType} when IsLeader == 1 ->
                            IsAllFree = lists:all(fun(#dun_multi_member{action_lock = ActionLock}) -> 
                                ActionLock == 0
                            end, Members),
                            IsAllHelp = lists:all(fun(#dun_multi_member{help_type = HelpType}) -> 
                                HelpType == ?DUN_TEAM_HELP_YES
                            end, Members),

                            if 
                                LeaderHelpType == 1 ->
                                    {false, ?ERRCODE(err215_leader_help_type)};
                                not IsAllFree ->
                                    {false, ?ERRCODE(err215_in_other_act)};
                                IsAllHelp ->
                                    {false, ?ERRCODE(err215_not_all_help_type)}; 
                                true ->
                                    {true, DunMultiTeam}
                            end;
                        _ ->
                            {false, ?ERRCODE(err215_not_leader)}
                    end
            end;
        _ ->
            {false, ?ERRCODE(err215_no_team)}
    end.

check_team_mate_vote_on_clusters(TeamId, _DunId, RoleId) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{vote_ref = VoteRef, vote_list = {AgreeList, DisAgreeList}}|_] ->
            LastTime =
            if VoteRef == undefined -> 0;
                true -> erlang:read_timer(VoteRef)
            end,
            if is_integer(LastTime) andalso LastTime > 0 ->
                    IsAgree = lists:member(RoleId, AgreeList),
                    IsDisAgree = lists:member(RoleId, DisAgreeList),
                    if 
                        IsAgree orelse IsDisAgree ->
                            {false, ?ERRCODE(err215_have_voted)};
                        true ->
                            {true, DunMultiTeam}
                    end;
                true ->
                    {false, ?ERRCODE(err215_not_in_vote)}
            end;
        _ ->
            {false, ?ERRCODE(err215_no_team)}
    end.

check_reply_req_on_clusters(TeamId, ReqList, RoleId) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{req_list = TeamReqList, members =Members}|_] ->
            case lists:keyfind(RoleId, #dun_multi_member.id, Members) of
                #dun_multi_member{is_leader = 1} ->
                    NewReqList = lists:filter(fun({_ReplyOperation, RoleIdX}) -> 
                        lists:keymember(RoleIdX, #dun_multi_member.id, TeamReqList)
                    end, ReqList),
                    {true, DunMultiTeam, NewReqList};
                _ ->
                    {false, ?ERRCODE(err215_not_leader)}
            end;
        _ ->
            {false, ?ERRCODE(err215_no_team)}
    end.

check_change_approve_state_on_clusters(TeamId, RoleId, Approve) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{members = Members, need_approve = OldApprove}|_] ->
            case lists:keyfind(RoleId, #dun_multi_member.id, Members) of
                #dun_multi_member{is_leader = 1} ->
                    if OldApprove == Approve ->
                            {false, ?ERRCODE(err215_repeat_approve_state)};
                        Approve =/= 1 andalso Approve =/= 2 ->
                            {false, ?ERRCODE(err215_approve_state_error)};
                        true -> {true, DunMultiTeam}
                    end;
                _ -> {false, ?ERRCODE(err215_not_leader)}
            end;
        _ -> {false, ?ERRCODE(err215_no_team)}
    end.

check_change_team_target_on_clusters(TeamId, _RoleId, DunId, Approve) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{dun_id = _OldDunId, members = Members, is_start = IsStart}|_] ->
            IsMemberMatch = check_dun_members_condition(Members, DunId),
            if
                IsMemberMatch =/= true ->
                    IsMemberMatch;
                    % {false, ?ERRCODE(err215_mate_can_match)};
                IsStart == 1 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                Approve =/= 1 andalso Approve =/= 2 ->
                    {false, ?FAIL};
                true ->
                    {true, DunMultiTeam} 
            end;
        _ -> {false, ?ERRCODE(err215_no_team)}
    end.

check_change_help_type_on_clusters(TeamId, RoleId, HelpType) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{members = Members, is_start = IsStart, vote_ref = VoteRef}|_] ->
            if VoteRef =/= undefined ->
                    {false, ?ERRCODE(err215_in_vote)};
                IsStart =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                true ->
                    case lists:keyfind(RoleId, #dun_multi_member.id, Members) of
                        #dun_multi_member{help_type = OldHelpType} when OldHelpType =/= HelpType ->
                            {true, DunMultiTeam};
                        #dun_multi_member{} ->
                            {false, ?ERRCODE(err215_not_repeat_change_help)};
                        _ ->
                            {false, ?ERRCODE(err215_not_in_team)}
                    end
            end;
        _ -> {false, ?ERRCODE(err215_no_team)}
    end.

check_change_merge_type_on_clusters(TeamId, RoleId, MergeType, _RestTime) ->
    case ets:lookup(?DUN_MULTI_TEAM_ETS, TeamId) of
        [DunMultiTeam = #dun_multi_team{members = Members, is_start = IsStart, vote_ref = VoteRef}|_] ->
            if VoteRef =/= undefined ->
                    {false, ?ERRCODE(err215_in_vote)};
                IsStart =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                true ->
                    case lists:keyfind(RoleId, #dun_multi_member.id, Members) of
                        #dun_multi_member{typical_data = TypicalData} ->
                            OldMergeType = maps:get(?DUN_ROLE_COUNT_MERGE, TypicalData, 0),
                            if 
                                MergeType == OldMergeType ->
                                    {false, ?ERRCODE(err215_same_merge_type)};
                                true ->
                                    {true, DunMultiTeam}
                            end;
                        _ -> {false, ?ERRCODE(err215_not_in_team)}
                    end
            end;
        _ -> {false, ?ERRCODE(err215_no_team)}
    end.            

check_dun_members_condition(Members, DunId) ->
    CheckRet = lists:foldl(fun(Member, Acc) ->
        case Member of
            #dun_multi_member{is_leader = 1} ->
                Ret = lib_dun_multi:check_dungeon_condition_on_multi_member(Member, DunId, Members, create_team);
            #dun_multi_member{} ->
                Ret = lib_dun_multi:check_dungeon_condition_on_multi_member(Member, DunId, Members, join_team);
            _ ->
                Ret = {false, ?FAIL}
        end,
        [Ret|Acc]
    end, [], Members),
    case lists:keyfind(false, 1, CheckRet) of
        {_, ErrCode} ->
            {false, ErrCode};
        _ ->
            true
    end.

mirror_help_check(Role, DunId, _TeamId) ->
    #player_status{id = RoleId, figure = #figure{lv = RoleLv}, dungeon_record = DungeonRecord} = Role,
    Ret = data_dungeon:get_dun(DunId),
    GuardDunCheck = lib_dun_multi:check_mirror_help_guard_dun_condition(RoleId, DunId, DungeonRecord),
    if
        not is_record(Ret, dun_cfg) ->
            {false, ?FAIL};
        GuardDunCheck =/= true ->
            GuardDunCheck;
        true ->
            #dun_cfg{condition = Condition} = Ret,
            case lists:keyfind(lv, 1, Condition) of
                {_, CfgLv} -> ok;
                _   -> CfgLv = 0
            end,
            if CfgLv > RoleLv ->
                    {false, ?ERRCODE(err215_lv_limit)};
                true ->
                    true
            end
    end.

dun_mulit_vote_check(Role, TeamId) ->
    #player_status{dun_multi = RoleDunMulti} = Role,
    case RoleDunMulti of
        #role_dun_multi{team_id = TeamId, dun_pid = DunPid} ->
            if DunPid =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                true -> true
            end;
        _ -> {false, ?ERRCODE(err215_not_in_team)}
    end.

team_mate_vote_check(Role, _VoteRet) ->
    #player_status{dun_multi = RoleDunMulti} = Role,
    case RoleDunMulti of
        #role_dun_multi{dun_pid = DunPid} ->
            if DunPid =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                true -> true
            end;
        _ -> {false, ?ERRCODE(err215_not_in_team)}
    end.

reply_action_check(Role) ->
    #player_status{dun_multi = RoleDunMulti} = Role,
    case RoleDunMulti of
        #role_dun_multi{dun_pid = DunPid} ->
            if DunPid =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                true -> true
            end;
        _ -> {false, ?ERRCODE(err215_not_in_team)}
    end.

change_approve_check(Role) ->
    #player_status{dun_multi = RoleDunMulti} = Role,
    case RoleDunMulti of
        #role_dun_multi{dun_pid = DunPid} ->
            if DunPid =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                true -> true
            end;
        _ -> {false, ?ERRCODE(err215_not_in_team)}
    end.

change_team_target_check(Role, DunId, _Approve) ->
    #player_status{dun_multi = RoleDunMulti} = Role,
    case RoleDunMulti of
        #role_dun_multi{dun_id = OldDunId, dun_pid = DunPid, is_leader = IsLeader} ->
            if DunPid =/= 0 ->
                    {false, ?ERRCODE(err215_is_on_dungeon)};
                IsLeader == 0 ->
                    {false, ?ERRCODE(err215_not_leader)};
                true ->
                    OldDunType = data_dungeon:get_dun_type(OldDunId),
                    DunType    = data_dungeon:get_dun_type(DunId),
                    if OldDunType == DunType ->
                            if DunType == ?DUNGEON_TYPE_CRUSADE ->
                                    OldDunSubType   = data_dun_multi_team:get_dun_sub_type(OldDunId),
                                    DunSubType      = data_dun_multi_team:get_dun_sub_type(DunId),
                                    if OldDunSubType == DunSubType ->
                                            {false, ?ERRCODE(err215_have_in_target)};
                                        true ->
                                            true
                                    end;
                                DunType == ?DUNGEON_TYPE_GUARD ->
                                    if OldDunId == DunId -> {false, ?ERRCODE(err215_have_in_target)};
                                        true -> true
                                    end;
                                true ->
                                    {false, ?FAIL}
                            end;
                        true ->
                            {false, ?ERRCODE(err215_cant_change_team_target)}
                    end
            end;
        _ -> {false, ?ERRCODE(err215_not_in_team)}
    end.

%% 检查是否被邀请
%% @return {0未被邀请|1被邀请|2无需检测, 邀请类型}
check_is_invited(Role, TeamId, DunId) ->
    #player_status{dun_multi = DunMulti} = Role,
    #role_dun_multi{inviter_list = InviterList} = DunMulti,
    case lists:keyfind(TeamId, 2, InviterList) of
        {DunId, TeamId, _, InviteType} ->
            {1, InviteType};
        _ ->
            DunType = data_dungeon:get_dun_type(DunId),
            if DunType == ?DUNGEON_TYPE_GUARD ->
                    {0, 0};
                true ->
                    {2, 0}
            end
    end.

%% 本服调M F A
apply_to_local_role(none, M, F, Args) ->
    apply(M, F, Args);
%% 跨服调M F A
apply_to_local_role(Node, M, F, Args) ->
    SelfNode = node(),
    case SelfNode == Node of
        true-> apply(M, F, Args);
        false-> clusters_center:apply_cast(Node, M, F, Args)
    end.

%% gm随机去除假人队伍
gm_remove_team(Num) ->
    TeamL1 = ets:tab2list(?DUN_MULTI_TEAM_ETS),
    TeamL2 = [Team || Team <- TeamL1, Team#dun_multi_team.is_gm == 1],
    Fun = fun(_I, LeftTeam)->
        case LeftTeam of
            [#dun_multi_team{id = TeamId, dun_id = DunId} | T] ->
                HallId = get_act_pid_map_id(DunId),
                case ets:lookup(?DUN_MULTI_HALL_ETS, HallId) of
                    [DunRoom = #dun_multi_room{teams = Teams}] ->
                        NewTeams = lists:keydelete(TeamId, #dun_multi_team_sub.id, Teams),
                        NewDunRoom = DunRoom#dun_multi_room{teams = NewTeams},
                        ets:insert(?DUN_MULTI_HALL_ETS, NewDunRoom),
                        ok;
                    _ ->
                        ok
                end,
                ets:delete(?DUN_MULTI_TEAM_ETS, TeamId),
                T;
            _ -> LeftTeam
        end
    end,
    lists:foldl(Fun, TeamL2, lists:seq(1, Num)).

