%% ---------------------------------------------------------------------------
%% @doc 多人副本相关
%% @author zengzy
%% @since  2019-03-26
%% ---------------------------------------------------------------------------
-module(lib_dun_multi).

-include("dungeon_multi.hrl").
-include("server.hrl").
-include("dungeon.hrl").
-include("predefine.hrl").
-include("scene.hrl").
-include("figure.hrl").
-include("def_module.hrl").
-include("def_daily.hrl").
-include("errcode.hrl").
-include("common.hrl").
-include("def_fun.hrl").
-include("def_vip.hrl").
-include("attr.hrl").
-include("team.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").

-export([
    login/1                                 %% 登录
    ,re_login/1                             %% 重登(顶号操作)
    ,logout/1                               %% 退出游戏
    ,send_dun_multi_info/1                  %% 发送角色多人副本信息
    % ,get_help_type/2                      %% 获取玩家助战类型
    ,send_team_info/1                       %% 发送玩家队伍信息
    ,invite_role/5                          %% 邀请玩家组队
    ,role_invited_dun_multi/6               %% 玩家进程增加邀请信息
    ,show_inviter_list/1                    %% 打开邀请列表
    ,reject_inviter/3                       %% 拒绝邀请
    ,role_join_multi_team/5                 %% 角色加入多人副本组队
    ,role_quit_multi_team/1                 %% 角色离开或被踢出多人副本组队
    ,role_to_multi_member/2                 %% 角色数据转换为多人副本组队成员
    ,role_mirror_multi_team/2
    ,role_team_rela_change/4                %% 队员被指定为队长
    ,role_change_team_target/4              %% 队长切换队伍目标
    ,multi_member_to_dungeon_role/2         %% 多人副本组队成员转换为副本角色数据
    ,multi_crusade_to_member/1              %% 角色数据转换讨伐副本组队成员
	,check_dungeon_condition/2
    ,check_dungeon_condition/3
    ,check_dungeon_condition/4
	,check_all_member/2                     %% 跨服进程检查所有玩家状态
	,check_dungeon_condition_on_multi_member/3
    ,check_dungeon_condition_on_multi_member/4
    ,check_mirror_help_guard_dun_condition/3
    ,get_max_times/1                        %% 获取副本配置每日上限次数
    ,get_max_help_times/1                   %% 获取副本配置每日助战上限次数
    ,get_role_times/3                       %% 获取玩家相关次数
    ,get_dun_open_lv/1                      %% 获取副本开启等级
    ,get_max_num/1                          %% 获取副本参加人数上限
    ,unpack_error/1                         %% 错误码解析
    ,update_status_dungeon/2                %% 更新ps状态
    ,handle_event/2                         %% 事件处理

    ,get_crusade_count_info/2               %% 讨伐副本-获取副本次数信息
    ,get_crusade_count_reward/2             %% 讨伐副本-获取每次讨伐次数奖励
    ,calc_next_reward_point_do/3
    ,calc_guard_assist_count/1              %% 获取守卫信标邀请辅助信息
    ,calc_last_time/7
    ,change_scene_check/1
    ,dun_multi_vote_broad/3
    ,role_update_req_list/3
    ,not_is_in_dun_multi_match/1                %% 是否处于匹配状态
    ,update_team_match/2                        %% 更新人物身上的匹配状态
    ,check_action_lock_in_dun_multi/3           %% 检查是否更新人物状态
    ,role_approved_join_team/6                  %% 队员被审批加入队伍
    ,delete_role_req_list/2                     %% 副本开始删除尚有申请列表的玩家
    ,role_deleted_req_list/2
    ,calc_crusade_max_num/2
]).


%% ---------------------------------------------------------------------------
%% 登录
login(PS) ->
    #player_status{id = RoleId} = PS,
    case ets:lookup(?DUN_MULTI_ROLE_ETS, RoleId) of
        [#dun_multi_role{id = RoleId, team_id = TeamId, dun_id = DunId, is_leader = IsLeader}] ->
            Args = [node(), RoleId, TeamId],
            lib_dun_multi_cluster:cast_dun_multi_mgr_cluster({login_cast_team, Args}),
            role_join_multi_team(PS, TeamId, DunId, IsLeader, 0);
        _ ->
            PS#player_status{dun_multi = #role_dun_multi{}}
    end.

%% 重登操作,默认退队处理
re_login(PS) ->
    logout(PS),
    PS#player_status{dun_multi = #role_dun_multi{}}.

%% 退出
logout(PS) ->
    #player_status{id = RoleId, dun_multi = DunMulTi} = PS,
    #role_dun_multi{team_id = TeamId, dun_id = DunId, dun_pid = DunCopyId, req_list = ReqList} = DunMulTi,
    if
        TeamId =< 0 orelse DunId =< 0 -> logout_del_team(0, RoleId);
        true ->
            case misc:is_process_alive(DunCopyId) of
                true -> logout_del_team(TeamId, RoleId);
                false ->
                    logout_handle(PS),
                    logout_del_team(0, RoleId)
            end
    end,
    del_req_from_team(RoleId, ReqList),
    true.

logout_handle(PS) ->
    lib_dun_multi_cluster:leave_team(PS),
    ok.

%%退出操作, 去除所有类型中所在队伍(除当前副本断线队伍TeamId外)
logout_del_team(TeamId, RoleId) ->
    case clusters_api:is_connected_center() of
        false -> ok;
        _ ->
            logout_del_team_core(TeamId, RoleId)
    end.

logout_del_team_core(TeamId, RoleId) ->
    Fun = fun(DunType)->
        case DunType of
            ?DUNGEON_TYPE_CRUSADE ->
                IdList = data_dun_multi_team:get_sub_type(?ACT_TYPE_CRUSADE);
            _ ->
                IdList = data_dungeon:get_ids_by_type(DunType)
        end,
        [
            begin
                Msg = {del_old_team, #dun_multi_member{id = RoleId}, TeamId},
                clusters_node:apply_cast(lib_dun_multi_cluster, cast_dun_multi_hall_cluster, [Id, Msg])
            end
            || Id <- IdList
        ]
    end,
    lists:map(Fun, ?TEAM_DUNGEON).

del_req_from_team(RoleId, ReqList) ->
    lists:map(fun({TeamId, DunId}) ->
        Msg = {del_old_req_from_team, TeamId, RoleId},
        clusters_node:apply_cast(lib_dun_multi_cluster, cast_dun_multi_hall_cluster, [DunId, Msg])
    end, ReqList),
    ok.    

%% 切换场景检测
%% 当进入其他活动且正处于匹配状态未进入副本时,应通知前端取消队伍匹配
change_scene_check(Player) ->
    #player_status{sid = Sid, action_lock = ActionLock, dun_multi = DunMulti, scene = SceneId} = Player,
    #role_dun_multi{team_id = MulTeamId, dun_pid = DunPid} = DunMulti,
    ErrorCode = ?ERRCODE(err215_on_multi_team),
    IsGuildScene = lib_guild:is_guild_scene(SceneId),
    if
        MulTeamId =/= 0 andalso DunPid == 0 andalso ((ActionLock =/= ErrorCode andalso ActionLock =/= free) orelse IsGuildScene) ->
            % ?ERR("check scene change ~p ~p ~p ~p", [MulTeamId, DunPid, ActionLock, IsGuildScene]),
            {ok, BinData} = pt_215:write(21529, [0]),
            lib_server_send:send_to_sid(Sid, BinData);
        true ->
            skip
    end.

%% 是否处于队伍匹配状态
%% true 不处于匹配中 | {false, integer()} 处于匹配状态
not_is_in_dun_multi_match(Player) ->
    #player_status{dun_multi = DunMulti} = Player,
    case DunMulti of
        #role_dun_multi{team_id = TeamId, is_match = IsMatch} ->
            if TeamId =/= 0 andalso IsMatch =/= 0 ->
                    {false, ?ERRCODE(err215_in_dun_multi_match)};
                true ->
                    true
            end;
        _ -> true
    end.

%% 匹配状态变更
%% 只在队伍未进入状态进行更新
update_team_match(Player, MatchType) ->
    #player_status{dun_multi = DunMulti} = Player,
    case DunMulti of
        #role_dun_multi{team_id = TeamId, dun_pid = DunPid} when TeamId =/= 0 andalso DunPid == 0 ->
            {ok, Player#player_status{dun_multi = DunMulti#role_dun_multi{is_match = MatchType}}};
        _ -> {ok, Player}
    end.

check_action_lock_in_dun_multi(RoleId, DunMulti, ActionLock) ->
    case DunMulti of
        #role_dun_multi{team_id = TeamId, dun_id = DunId, dun_pid = DunPid} when
         TeamId =/= 0 andalso DunPid == 0 ->
            Msg = {update_team_mate_action_lock, TeamId, RoleId, ActionLock},
            clusters_node:apply_cast(lib_dun_multi_cluster, cast_dun_multi_hall_cluster, [DunId, Msg]);
        #role_dun_multi{req_list = ReqList} when ReqList =/= [] ->
            Msg = {update_team_req_action_lock, [RoleId, ReqList, ActionLock]},
            lib_dun_multi_cluster:cast_dun_multi_mgr_cluster(Msg);
        _ ->
            skip
    end.

%% 队员被审批加入队伍
role_approved_join_team(Role, TeamId, EnterDunId, DunId, IsMatch, HelpType) ->
    NewRole = role_join_multi_team(Role, TeamId, DunId, 0, IsMatch),
    #player_status{sid = Sid, dun_multi = RoleDunMulti} = NewRole,
    #role_dun_multi{req_list = ReqList} = RoleDunMulti,
    NewReqList = lists:keydelete(TeamId, 1, ReqList),
    {ok, BinData} = pt_215:write(21503, [1, TeamId, EnterDunId]),
    DunType = data_dungeon:get_dun_type(DunId),
    {ok, BinData1} = pt_215:write(21540, [?SUCCESS, DunType, HelpType]),
    lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary>>),
    {ok, NewRole#player_status{dun_multi = RoleDunMulti#role_dun_multi{req_list = NewReqList}}}.

delete_role_req_list(TeamId, ReqList) ->
    lists:map(fun(#dun_multi_member{node = Node, id = RoleId}) -> 
        lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_STATUS, ?NOT_HAND_OFFLINE, ?MODULE, role_deleted_req_list, [TeamId])
    end, ReqList).

role_deleted_req_list(Player, TeamId) ->
    #player_status{dun_multi = RoleDunMulti} = Player,
    % ?ERR("~p ~p", [TeamId, RoleDunMulti]),
    case RoleDunMulti of
        #role_dun_multi{req_list = ReqList} ->
            NewReqList = lists:keydelete(TeamId, 1, ReqList),
            {ok, Player#player_status{dun_multi = RoleDunMulti#role_dun_multi{req_list = NewReqList}}};
        _ -> {ok, Player}
    end.

%% 获取玩家多人副本信息
send_dun_multi_info(Player) ->
    #player_status{sid = Sid, dun_multi = DunMulTi} = Player,
    #role_dun_multi{team_id = TeamId, dun_id = DunId, dun_pid = _DunPid} = DunMulTi,
    DunId1 =
    case data_dungeon:get_dun_type(DunId) of
        ?DUNGEON_TYPE_CRUSADE ->
            SubType = data_dun_multi_team:get_dun_sub_type(DunId),
            case data_dun_multi_team:get_type_duns(?ACT_TYPE_CRUSADE, SubType) of
                [FirstDunId|_] -> FirstDunId;
                _ -> DunId
            end;
        _ -> DunId
    end,
    {ok, BinData} = pt_215:write(21500, [TeamId, DunId1]),
    lib_server_send:send_to_sid(Sid, BinData),
    ok.

%% 获取玩家队伍信息
send_team_info(Player) ->
    #player_status{sid = Sid, dun_multi = DunMulTi} = Player,
    #role_dun_multi{team_id = MulTeamId} = DunMulTi,
    {ok, BinData} = pt_215:write(21519, [0, MulTeamId]),
    lib_server_send:send_to_sid(Sid, BinData),
    ok.

%% 邀请玩家组队
invite_role(Player, InviteTeamId, InviteDunId, ToIdList, InviteType) ->
    #player_status{id = RoleId, dun_multi = DunMulTi} = Player,
    #role_dun_multi{team_id = TeamId, dun_id = DunId, is_leader = _IsLeader} = DunMulTi,
    DunId1 =
    case data_dungeon:get_dun_type(DunId) of
        ?DUNGEON_TYPE_CRUSADE -> 
            SubType = data_dun_multi_team:get_dun_sub_type(DunId),
            case data_dun_multi_team:get_type_duns(?ACT_TYPE_CRUSADE, SubType) of
                [FirstDunId|_] -> FirstDunId;
                _ -> DunId
            end;
        _ -> DunId
    end,
    Node = node(),
    if TeamId > 0 -> %%andalso IsLeader > 0
            if InviteTeamId == TeamId andalso InviteDunId == DunId1 ->
                    lists:foldl(fun(ToId, Acc) ->
                        Pid = misc:get_player_process(ToId),
                        case is_pid(Pid) andalso misc:is_process_alive(Pid) of
                            true -> 
                                lib_player:apply_cast(ToId, ?APPLY_CAST_SAVE, ?MODULE, role_invited_dun_multi, [DunId1, TeamId, RoleId, Node, InviteType]),
                                [ToId|Acc];
                            _ ->
                                Acc
                        end
                    end, [], ToIdList),
                    {ok};
                true ->
                    {false, ?FAIL}
            end;
        TeamId == 0 -> 
            {false, ?ERRCODE(err215_not_in_team)};
        true ->
            {false, ?ERRCODE(err215_not_leader)}
    end.

%% 添加邀请信息
role_invited_dun_multi(Player, DunId, TeamId, FromRoleId, FromNode, InviteType) ->
    #player_status{id = RoleId, dun_multi = DunMulTi, figure = #figure{lv = RoleLv}, dungeon_record = DungeonRecord} = Player,
    #role_dun_multi{team_id = RoleTeamId, inviter_list = InviterList, mirror_help = MirrorHelpList} = DunMulTi,
    IsAlreadyHelp = lists:keymember(TeamId, 1, MirrorHelpList),
    GuardDunRet = check_invite_guard_dun_condition(RoleId, DunId, DungeonRecord),
    Ret = data_dungeon:get_dun(DunId),
    if
        RoleTeamId > 0 ->
            {ok, BinData} = pt_215:write(21510, [?ERRCODE(err240_other_in_team), TeamId, DunId, []]),
            lib_server_send:send_to_uid(FromRoleId, BinData),
            Player;
        IsAlreadyHelp ->
            {ok, BinData} = pt_215:write(21510, [?ERRCODE(err215_mirror_in_this_team), TeamId, DunId, []]),
            lib_server_send:send_to_uid(FromRoleId, BinData),
            Player;
        RoleTeamId > 0 andalso RoleTeamId == TeamId ->
            {ok, BinData} = pt_215:write(21510, [?ERRCODE(err215_have_team), TeamId, DunId, []]),
            lib_server_send:send_to_uid(FromRoleId, BinData),
            Player;
        not is_record(Ret, dun_cfg) ->
            {ok, BinData} = pt_215:write(21510, [?FAIL, TeamId, DunId, []]),
            lib_server_send:send_to_uid(FromRoleId, BinData),
            Player;
        GuardDunRet =/= true ->
            {false, ErrorCode} = GuardDunRet,
            {ok, BinData} = pt_215:write(21510, [ErrorCode, TeamId, DunId, []]),
            lib_server_send:send_to_uid(FromRoleId, BinData),
            Player;
        true ->
            case lists:keyfind(lv, 1, Ret#dun_cfg.condition) of
                {_, CfgLv} -> ok;
                _ -> CfgLv = 0
            end,
            if RoleLv < CfgLv ->
                    {ok, BinData} = pt_215:write(21510, [?ERRCODE(err215_oppo_lv_limit), TeamId, DunId, []]),
                    lib_server_send:send_to_uid(FromRoleId, BinData),
                    Player;
                true ->
                    Info = {DunId, TeamId, FromRoleId, InviteType},
                    NewDunMulTi = DunMulTi#role_dun_multi{inviter_list = [Info|lists:keydelete(FromRoleId, 3, InviterList)]},
                    NewPlayer = Player#player_status{dun_multi = NewDunMulTi},
                    show_inviter_list(NewPlayer),
                    {ok, BinData} = pt_215:write(21510, [?SUCCESS, TeamId, DunId, [RoleId]]),
                    lib_server_send:send_to_uid(FromNode, FromRoleId, BinData),
                    NewPlayer
            end
    end.

%% 打开邀请列表
show_inviter_list(Player) ->
    #player_status{sid = Sid, dun_multi = #role_dun_multi{inviter_list = InviterList}} = Player,
    case InviterList of
        [{DunId, TeamId, FromRoleId, _InviteType}|_] ->
            %% 只发最新的一条
            DunType = data_dungeon:get_dun_type(DunId),
            Figure  = lib_role:get_role_figure(FromRoleId),
            {ok, BinData} = pt_215:write(21511, [DunType, [{DunId, TeamId, FromRoleId, Figure, 10}]]),
            lib_server_send:send_to_sid(Sid, BinData);
        _ ->
            skip
    end,
    ok.

%% 拒绝邀请
reject_inviter(Role, Type, RejectList) when is_list(RejectList)->
    #player_status{sid = Sid, dun_multi = DunMulti} = Role,
    #role_dun_multi{inviter_list = InviterList} = DunMulti,
    NewInviterList = [Info || {_DunId, TeamId, _RoleId, _InviteType} = Info <- InviterList, lists:member(TeamId, RejectList) == false],
    NewDunMulti = DunMulti#role_dun_multi{inviter_list = NewInviterList},
    NewRole = Role#player_status{dun_multi = NewDunMulti},
    {ok, BinData} = pt_215:write(21520, [?SUCCESS, Type]),
    lib_server_send:send_to_sid(Sid, BinData),
    % show_inviter_list(NewRole),
    {ok, NewRole};
reject_inviter(Role, Type, _) ->
    {ok, BinData} = pt_215:write(21520, [?SUCCESS, Type]),
    lib_server_send:send_to_sid(Role#player_status.sid, BinData),
    ok.

%% 角色加入多人副本组队
role_join_multi_team(Role, TeamId, DunId, IsLeader, IsMatch) ->
    #player_status{id = RoleId, dun_multi = DunMulti} = Role,
    case DunMulti of
        #role_dun_multi{merge_type = OldMergeType} -> ok;
        _ -> OldMergeType = 0
    end,
    NewDunMulti = #role_dun_multi{team_id = TeamId, dun_id = DunId, is_leader = IsLeader, is_match = IsMatch, merge_type = OldMergeType},
    MultiRole = #dun_multi_role{id = RoleId, team_id = TeamId, dun_id = DunId, is_leader = IsLeader},
    ets:insert(?DUN_MULTI_ROLE_ETS, MultiRole),
    NewRole = Role#player_status{dun_multi = NewDunMulti},
    mod_scene_agent:update(NewRole, [{dun_multi, {TeamId, DunId}}]),
    NewRole.
    % lib_player:setup_action_lock(NewRole, ?ERRCODE(err215_on_multi_team)).

%% 角色离开或被踢出多人副本组队
role_quit_multi_team(RoleId) when is_integer(RoleId) ->
    case lib_player:is_online_global(RoleId) of
        true ->
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, role_quit_multi_team, []);
        false ->
            ets:delete(?DUN_MULTI_ROLE_ETS, RoleId)
    end,
    ok;
role_quit_multi_team(Role) ->
    #player_status{id = RoleId, dun_multi = DunMulti} = Role,
    case DunMulti of
        #role_dun_multi{merge_type = MergeType} -> ok;
        _ -> MergeType = 0
    end,
    ets:delete(?DUN_MULTI_ROLE_ETS, RoleId),
    NewDunMulti = #role_dun_multi{team_id = 0, dun_id = 0, merge_type = MergeType},
    NewRole = Role#player_status{dun_multi = NewDunMulti},
    mod_scene_agent:update(NewRole, [{dun_multi, {0, 0}}]),
    NewRole.
    % lib_player:break_action_lock(NewRole, ?ERRCODE(err215_on_multi_team)).

role_mirror_multi_team(RoleId, TeamId) when is_integer(RoleId) ->
    case lib_player:is_online_global(RoleId) of
        true ->
            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, role_mirror_multi_team, [TeamId]);
        false ->
            ok
    end,
    ok;
role_mirror_multi_team(Role, TeamId) ->
    #player_status{dun_multi = RoleDunMulti} = Role,
    #role_dun_multi{mirror_help = HelpList} = RoleDunMulti,
    NewDunMulti = RoleDunMulti#role_dun_multi{mirror_help = lists:keydelete(TeamId, 1, HelpList)},
    NewRole = Role#player_status{dun_multi = NewDunMulti},
    NewRole.

role_change_team_target(Role, TeamId, DunId, IsLeader) ->
    #player_status{id = RoleId, dun_multi = RoleDunMulti} = Role,
    % PowerChangeTime = ?IF(is_record(DunMulti, role_dun_multi), DunMulti#role_dun_multi.power_change_time, 0),
    NewDunMulti = RoleDunMulti#role_dun_multi{dun_id = DunId},
    MultiRole = #dun_multi_role{id = RoleId, team_id = TeamId, dun_id = DunId, is_leader = IsLeader},
    ets:insert(?DUN_MULTI_ROLE_ETS, MultiRole),
    NewRole = Role#player_status{dun_multi = NewDunMulti},
    mod_scene_agent:update(NewRole, [{dun_multi, {TeamId, DunId}}]),
    NewRole.    

%% 角色数据转换为多人副本组队成员
role_to_multi_member(DunId, Role) ->
    #player_status{
        id = Id, pid = Pid, sid = Sid, server_id = ServerId, server_num = ServerNum
        , figure=Figure, combat_power = CombatPower, battle_attr = #battle_attr{hp_lim = HpLim, attr = Attr},
        scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId, x = X, y = Y, dungeon_record = DungeonRecs
        , dungeon = #status_dungeon{dun_data = DunDatas}, action_lock = ActionLock
        , help_type_setting = HelpTypeSetting
    } = Role,
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{type = DunType, count_cond = CountCon} -> ok;
        _ -> DunType = 0, CountCon = []
    end,
    {BuyCount, EnterCount, HelpCount} = lib_dun_multi:get_role_times(Id, DunId, DunType),
    AddTimes = BuyCount,
    IsFirst  = lib_dungeon_api:check_ever_finish(Id, DunId),
    HelpType = get_role_team_help_type(EnterCount, HelpCount, CountCon, HelpTypeSetting),
    AttrList = attr_conv:to_attr_tuple_list(Attr),
    DunData  = case lists:keyfind(DunType, 1, DunDatas) of
        {_, DunData0} -> DunData0;
        _ -> []
    end,
    DungeonRec   = maps:get(DunId, DungeonRecs, []),
    TypicalData  = lib_dungeon_distribute:get_role_rec(DunType, DunId, Id, DungeonRec, DunData, #{}),
    GuardDunPass = lib_dungeon_guard:calc_guard_dun_pass(DungeonRecs),
    #dun_multi_member{
        id = Id, pid = Pid, sid = Sid, server_id = ServerId, server_num = ServerNum
        ,node = node(), figure = Figure, hp_lim = HpLim, power = CombatPower,
        help_type = HelpType, add_times = AddTimes, daily_times = EnterCount,
        help_times = HelpCount, is_first = IsFirst, scene = SceneId, scene_pool_id = ScenePoolId,
        copy_id = CopyId, x = X, y = Y , attr_list = AttrList, typical_data = TypicalData,
        action_lock = ?IF(ActionLock == free, 0, ActionLock), guard_dun_pass = GuardDunPass
    }.

%% 多人副本组队成员转换为副本角色数据
multi_member_to_dungeon_role(Member, _DunId) ->
    #dun_multi_member{
        id=RoleId, node = Node, server_id=ServerId, server_num = ServerNum, figure=Figure, pid=Pid, sid=Sid,
        help_type = HelpType, hp_lim = HpLim, help_times = HelpTimes, power = Power, attr_list = AttrList,
        scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId, x = X, y = Y, is_leader = LeaderType,
        typical_data = TypicalData, is_first = IsFirst
    } = Member,
    #dungeon_role{
        id=RoleId, server_id=ServerId, server_num = ServerNum, figure=Figure, node=Node, pid=Pid, sid=Sid,
        help_type = HelpType, hp = HpLim, hp_lim = HpLim, help_num = HelpTimes, combat_power = Power, 
        team_position = LeaderType, scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId, x = X, 
        y = Y, base_attr = AttrList, typical_data = TypicalData, is_first = IsFirst
    }.

%% 讨伐副本角色数据转换为多人副本组队成员
multi_crusade_to_member(Role) ->
    #player_status{
        id = Id, pid = Pid, sid = Sid, server_id = ServerId, server_num = ServerNum, 
        figure=Figure, combat_power = CombatPower, scene = SceneId,
        battle_attr = #battle_attr{hp_lim = HpLim, base_attr_map = AttrMap}, 
        scene_pool_id = ScenePoolId, copy_id = CopyId, x = X, y = Y
    } = Role,
    AttrList = get_base_attr(AttrMap),
    #dun_multi_member{
        id = Id, pid = Pid, sid = Sid, server_id = ServerId, server_num = ServerNum,
        node = node(), figure = Figure, hp_lim = HpLim, power = CombatPower, attr_list = AttrList,
        scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId, x = X, y = Y
    }.

%% 队员在队伍中关系转变
role_team_rela_change(Role, TeamId, _DunId, NewRela) ->
    #player_status{id = RoleId, dun_multi = DunMulti} = Role,
    #role_dun_multi{team_id = RoleTeamId} = DunMulti,
    if RoleTeamId == TeamId ->
            NewDunMulti = DunMulti#role_dun_multi{is_leader = NewRela},
            NewRole = Role#player_status{dun_multi = NewDunMulti},
            case ets:lookup(?DUN_MULTI_ROLE_ETS, RoleId) of
                [#dun_multi_role{}|_] ->
                    ets:update_element(?DUN_MULTI_ROLE_ETS, RoleId, {#dun_multi_role.is_leader, NewRela});
                _ ->
                    ok
            end,
            {ok, NewRole};
        true -> %% 数据不一致
            ok
    end.

%%------------------------------检查副本条件---------------------------------------------
%% @return ture | {false, ErrorCode}
check_dungeon_condition(Player, DunId) ->
    check_dungeon_condition(Player, DunId, default, 0).

check_dungeon_condition(Player, DunId, CountCheckType) ->
    check_dungeon_condition(Player, DunId, CountCheckType, 0).

-spec check_dungeon_condition(Player, DunId, CountCheckType, MergeType) -> Return when
     Player :: #player_status{},
     DunId  :: integer(),
     CountCheckType :: default| create_team | join_team | atom(), % 次数检查参数 默认检查|创队检查|入队检查|根据助战参数检查|都不检查
     MergeType :: 0|1,
     Return :: boolean() | {boolean(), integer()}.
check_dungeon_condition(Player, DunId, CountCheckType, MergeType) ->
	Dun = data_dungeon:get_dun(DunId),
    case lib_dungeon:is_cluster_dungeon_by_dun_id(DunId) andalso is_record(Dun, dun_cfg) of
        true ->
            lib_dungeon:execute_check([
                {fun lib_dungeon:check_other_conditions/3, [Player, Dun, MergeType]},
                {fun lib_dungeon:check_dungeon_time_open/1, [Dun]},
                {fun lib_dungeon:check_dungeon_condition/3, [Dun#dun_cfg.condition, Dun, Player]},
                {fun lib_dungeon:check_cost/2, [Player, Dun]},
                {fun lib_dungeon:check_dungeon_count_cond/5, [Dun#dun_cfg.count_cond, Dun, ?DUN_TEAM_HELP_NO, CountCheckType, Player]},
                {fun lib_dungeon:check_extra/2, [Player, Dun]},
                {fun check_guard_dun_pass/2, [Player#player_status.dungeon_record, Dun]}
            ]);
        false ->
            {false, ?ERRCODE(err215_not_dun_multi)}
    end.

%% 检查所有成员
check_all_member(MemberList, DunId) ->
    %%检测全员助战
    Fun1 = fun(#dun_multi_member{help_type = HelpType})-> HelpType == ?DUN_TEAM_HELP_YES end,
    AllHelpType = lists:all(Fun1, MemberList),
	Fun = fun(Member, TmpList)->
		case check_dungeon_condition_on_multi_member(Member, DunId, MemberList, use_help_arg) of
			true -> TmpList;
			{false, Err} -> [{false, Err}|TmpList]
		end
	end,
    AllOk = lists:foldl(Fun, [], MemberList),
    case {AllHelpType, AllOk} of
        {false, []} -> true;
        {true, _} -> {false, ?ERRCODE(err215_not_all_help_type)};
        {_, [{false, ErrorCode}|_T]} -> {false, ErrorCode};
        _ -> {false, ?FAIL}
    end.

%% 检查副本条件
%% @return ture | {false, ErrorCode}
check_dungeon_condition_on_multi_member(Member, DunId, MemberList) ->
    check_dungeon_condition_on_multi_member(Member, DunId, MemberList, default).

-spec check_dungeon_condition_on_multi_member(Member, DunId, MemberList, CountCheckType) -> ReturnType when
    Member :: #dun_multi_member{},
    DunId  :: integer(),
    MemberList :: list(),
    CountCheckType :: default | create_team | join_team | use_help_arg | atom(), % 次数检查参数 默认检查|创队检查|入队检查|根据助战参数检查|都不检查
    ReturnType :: boolean() | {boolean(), integer()}.
check_dungeon_condition_on_multi_member(#dun_multi_member{is_dummy = IsDummy}, _DunId, _MemberList, _CheckType)
 when IsDummy == ?DUN_DUMMY orelse IsDummy == ?DUN_MIRROR -> true;
check_dungeon_condition_on_multi_member(Member, DunId, MemberList, CountCheckType) ->
	Dun = data_dungeon:get_dun(DunId),
    case lib_dungeon:is_cluster_dungeon_by_dun_id(DunId) andalso is_record(Dun, dun_cfg) of
        true ->
            #dun_cfg{condition = Condition, count_cond = CountCond} = Dun,
            lib_dungeon:execute_check([
                {fun do_check_dungeon_condition/4,  [Condition, Dun, Member, MemberList]},
                {fun do_check_dungeon_count_cond/4, [CountCond, Dun, Member, CountCheckType]},
                {fun check_guard_dun_pass/2, [Member#dun_multi_member.guard_dun_pass, Dun]}
            ]);
        false ->
            {false, ?ERRCODE(err215_not_dun_multi)}
    end.

%% 检查副本条件
do_check_dungeon_condition([], _Dun, _Member, _MemberList) -> true;
do_check_dungeon_condition([H|T], Dun, Member, MemberList) ->
    #dun_multi_member{figure = #figure{lv = Lv, sex = Sex}} = Member,
    case H of
        {lv, NeedLv} ->
            case Lv >= NeedLv of
                true -> do_check_dungeon_condition(T, Dun, Member, MemberList);
                false -> {false, ?ERRCODE(err215_not_enough_lv_to_enter_dungeon)}
            end;
        {num, _Min, Max} ->
            case length(MemberList) =< Max of
                true -> do_check_dungeon_condition(T, Dun, Member, MemberList);
                false -> {false, ?ERRCODE(err215_is_max_num)}
            end;
        {relationship, _} ->
            Length = length(MemberList),
            Fun = fun(#dun_multi_member{figure = #figure{sex = TmpSex}})->
                TmpSex =/= Sex
            end,
            case lists:any(Fun, MemberList) of
                true -> do_check_dungeon_condition(T, Dun, Member, MemberList);
                false when Length == 1 -> true;
                _ -> {false, ?ERRCODE(err610_only_couple_enter)}
            end;
        _ ->
            do_check_dungeon_condition(T, Dun, Member, MemberList)
    end.

do_check_dungeon_count_cond([], _Dun, _Member, _CountCheckType) -> true;
do_check_dungeon_count_cond([H | T], Dun, Member, CountCheckType) ->
	#dun_multi_member{help_type = HelpType, add_times = AddTimes, daily_times = DailyTimes, help_times = HelpTimes} = Member,
    case H of
        {?DUN_COUNT_COND_DAILY, MaxNum} ->
            if
                CountCheckType == default orelse CountCheckType == create_team
                 orelse (CountCheckType == use_help_arg andalso HelpType == ?DUN_TEAM_HELP_NO) ->
                    case DailyTimes >= (MaxNum + AddTimes) of
                        true -> {false, ?ERRCODE(err215_dungeon_count_daily)};
                        false ->
                            do_check_dungeon_count_cond(T, Dun, Member, CountCheckType)
                    end;
                true ->
                    do_check_dungeon_count_cond(T, Dun, Member, CountCheckType)
            end;
        {?DUN_COUNT_COND_DAILY_HELP, MaxNum} ->
            if
                CountCheckType == default orelse CountCheckType == join_team
                 orelse (CountCheckType == use_help_arg andalso HelpType == ?DUN_TEAM_HELP_YES) ->
                    if
                        HelpTimes >= MaxNum -> {false, ?ERRCODE(err215_dungeon_count_daily_help)};
                        true ->
                            do_check_dungeon_count_cond(T, Dun, Member, CountCheckType)
                    end;
                true ->
                    do_check_dungeon_count_cond(T, Dun, Member, CountCheckType)
            end;
        _ ->
            {false, ?ERRCODE(err610_dungeon_count_cfg_error)}
    end.

%% 被邀请时的检查 
check_invite_guard_dun_condition(_RoleId, DunId, DungeonRecord) ->
    DunCfg = data_dungeon:get_dun(DunId),
    DunType = data_dungeon:get_dun_type(DunId),
    if 
        DunType == ?DUNGEON_TYPE_CRUSADE -> %% 不检查讨伐副本
            true;
        not is_record(DunCfg, dun_cfg) ->
            {false, ?FAIL};
        true ->
            #dun_cfg{condition = Condition} = DunCfg,
            case lists:keyfind(finish_dun_id, 1, Condition) of
                {_, FinDunId} ->
                    DunRec = maps:get(FinDunId, DungeonRecord, []),
                    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
                        {_, MaxWave} -> ok;
                        _   -> MaxWave = 0
                    end,
                    MaxCfgWave = lists:max(data_dungeon_wave:get_dun_waves(FinDunId)),
                    if MaxWave >= MaxCfgWave ->
                            true;
                        true ->
                            {false, ?ERRCODE(err610_need_finish_dun_id)}
                    end;
                _ ->
                    true
            end
    end.

%% 发起镜像协助时候的检查
check_mirror_help_guard_dun_condition(RoleId, DunId, DungeonRecord) ->
    {AssistCount, _RecoverTime} = calc_guard_assist_count(RoleId),
    DunCfg = data_dungeon:get_dun(DunId),
    DunType = data_dungeon:get_dun_type(DunId),
    if 
        DunType == ?DUNGEON_TYPE_CRUSADE -> %% 不检查讨伐副本
            true;
        not is_record(DunCfg, dun_cfg) ->
            {false, ?FAIL};
        AssistCount =< 0 ->
            {false, ?ERRCODE(err215_assist_count_limt)};
        true ->
            #dun_cfg{condition = Condition} = DunCfg,
            case lists:keyfind(finish_dun_id, 1, Condition) of
                {_, FinDunId} ->
                    DunRec = maps:get(FinDunId, DungeonRecord, []),
                    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
                        {_, MaxWave} -> ok;
                        _   -> MaxWave = 0
                    end,
                    MaxCfgWave = lists:max(data_dungeon_wave:get_dun_waves(FinDunId)),
                    if MaxWave >= MaxCfgWave ->
                            true;
                        true ->
                            {false, ?ERRCODE(err610_need_finish_dun_id)}
                    end;
                _ ->
                    true
            end
    end.

%% 查看守卫信标是否通关前一副本
check_guard_dun_pass(DungeonRecs, Dun)
 when is_map(DungeonRecs) orelse DungeonRecs == undefined ->
    case Dun of
        #dun_cfg{id = DunId, type = ?DUNGEON_TYPE_GUARD} ->
            DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_GUARD),
            [MinDunId|_] = lists:sort(DunIds),
            if DunId == MinDunId ->
                    true;
                true ->
                    PreDunId = DunId - 1,
                    DunWaves = data_dungeon_wave:get_dun_waves(PreDunId),
                    CfgMaxWave = lists:max(DunWaves),
                    DunRec = maps:get(PreDunId, DungeonRecs, []),
                    case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
                        {_, MaxWave} ->
                            if MaxWave >= CfgMaxWave -> true;
                                true -> {false, ?ERRCODE(err215_pass_pre_dun)}
                            end;
                        _ -> {false, ?ERRCODE(err215_pass_pre_dun)}
                    end
            end;
        _ -> true
    end;
check_guard_dun_pass(GuardDunPass, Dun) ->
    case Dun of
        #dun_cfg{id = DunId, type = ?DUNGEON_TYPE_GUARD} ->
            DunIds = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_GUARD),
            [MinDunId|_] = lists:sort(DunIds),
            if DunId == MinDunId ->
                    true;
                true ->
                    PreDunId = DunId - 1,
                    case lists:keyfind(PreDunId, 1, GuardDunPass) of
                        {_, true} -> true;
                        _         -> {false, ?ERRCODE(err215_pass_pre_dun)}
                    end
            end;
        _ -> true
    end.

%% --------------------------------辅助函数-------------------------------------------
%% 获取副本最大次数
get_max_times(DunId) ->
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{count_cond = CountCond} ->
            case lists:keyfind(?DUN_COUNT_COND_DAILY, 1, CountCond) of
                {_, MaxNum} -> MaxNum;
                _ -> 0
            end;
        _ -> 0
    end.

%% 获取副本最大助战次数
get_max_help_times(DunId) ->
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{count_cond = CountCond} ->
            case lists:keyfind(?DUN_COUNT_COND_DAILY_HELP, 1, CountCond) of
                false -> 0;
                {_, MaxNum} -> MaxNum
            end;
        _ -> 0
    end.

%% 获取玩家次数相关
get_role_times(RoleId, DunId, DunType) ->
    CountType = lib_dungeon_api:get_daily_count_type(DunType, DunId),
    CountList = [
        {?MOD_DUNGEON, ?MOD_DUNGEON_BUY, CountType},
        {?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, CountType},
        % {?MOD_DUNGEON, ?MOD_DUNGEON_GOOD_TIMES, DunType},
        {?MOD_DUNGEON, ?MOD_DUNGEON_HELP, CountType}
    ],
    case lib_daily:get_count_offline(RoleId, CountList) of
        [{_, BuyCount}, {_, EnterCount}, {_, HelpCount}] -> ok;
        _ -> BuyCount = EnterCount = HelpCount = 0
    end,
    {BuyCount, EnterCount, HelpCount}.



%% 获取副本开启等级
get_dun_open_lv(DunId) ->
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{condition = Condition} ->
            case lists:keyfind(lv, 1, Condition) of
                {_, Lv} -> Lv;
                _ -> 100
            end;
        _ -> 100
    end.

%% 获取副本参加人数上限
get_max_num(DunId) ->
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{condition = Condition} ->
            case lists:keyfind(num, 1, Condition) of
                {_, _, MaxNum} -> MaxNum;
                _ -> ?DUN_MULTI_TEAM_MAX_MEMBER
            end;
        _ -> ?DUN_MULTI_TEAM_MAX_MEMBER
    end.

%% 获取助战类型
%% 若无挑战次数,则默认为助战
get_role_team_help_type(DailyCount, _HelpCount, CountCond, HelpTypeSetting) ->
    case lists:keyfind(?DUN_COUNT_COND_DAILY, 1, CountCond) of
        {_, MaxDailyCount} -> ok;
        _                  -> MaxDailyCount = 0
    end,
    % case lists:keyfind(?DUN_COUNT_COND_DAILY_HELP, 1, CountCond) of
    %     {_, MaxHelpCount} -> ok;
    %     _                 -> MaxHelpCount = 0
    % end,
    if DailyCount < MaxDailyCount -> %% 优先考虑玩家的选项,默认在次数满足条件下为非助战状态
            maps:get(help_type, HelpTypeSetting, ?DUN_TEAM_HELP_NO);
        true ->
            ?DUN_TEAM_HELP_YES
    end.

%% 错误码解析
unpack_error(CheckCond) ->
    {ErrorCodeInt, _ErrorCodeArgs} = util:parse_error_code(CheckCond),
    DailyLimit = ?ERRCODE(err610_dungeon_count_daily),
    HelpLimit = ?ERRCODE(err610_dungeon_count_daily_help),
    LvLimit   = ?ERRCODE(err610_lv_limit),
    if
        ErrorCodeInt == DailyLimit  ->   {false, ?ERRCODE(err215_not_daily_times)};
        ErrorCodeInt == HelpLimit   ->   {false, ?ERRCODE(err215_not_help_times)};
        ErrorCodeInt == LvLimit     ->   {false, ?ERRCODE(err223_lv_not_enough)};
        true -> {false, ErrorCodeInt}
    end.

%% 更新副本状态
update_status_dungeon([], Player) -> Player;
update_status_dungeon([H|T], Player) ->
    #player_status{dun_multi = DunMulTi} = Player,
    case H of
        {dun_multi_pid, DunPid} -> NewDunMulTi = DunMulTi#role_dun_multi{dun_pid = DunPid};
        _ -> NewDunMulTi = DunMulTi
    end,
    NewPlayer = Player#player_status{dun_multi = NewDunMulTi},
    update_status_dungeon(T, NewPlayer).

handle_event(Player, #event_callback{type_id = ?EVENT_LV_UP}) when is_record(Player, player_status) ->
    #player_status{figure = #figure{lv = Lv}} = Player,
    do_update_role_attr(Player, [{lv, Lv}]),
    {ok, Player};
handle_event(Player, #event_callback{type_id = ?EVENT_FOUR_CLOCK}) when is_record(Player, player_status) ->
    #player_status{sid = Sid, dun_multi = DunMulti} = Player,
    case DunMulti of
        #role_dun_multi{} ->
            Player1 = Player#player_status{dun_multi = DunMulti#role_dun_multi{merge_type = 0}},
            lib_server_send:send_to_sid(Sid, pt_215, 21541, [?SUCCESS, ?DUNGEON_TYPE_CRUSADE, 0]),
            {ok, Player1};
        _ -> {ok, Player}
    end;
handle_event(Player, _Event) ->
    {ok, Player}.

do_update_role_attr(Player, KeyList) ->
    #player_status{id = RoleId, dun_multi = DunMulTi} = Player,
    #role_dun_multi{team_id = TeamId, dun_id = DunId, dun_pid = _DunPid} = DunMulTi,
    case TeamId > 0 andalso DunId > 0 of
        true ->
            Msg = {update_role_attr, TeamId, RoleId, KeyList},
            clusters_node:apply_cast(lib_dun_multi_cluster, cast_dun_multi_hall_cluster, [DunId, Msg]);
        false -> ok
    end.

%% 次数奖励废弃
get_crusade_count_info(RoleId, RoleLv) ->
    [DunId|_] = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_CRUSADE),
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{count_cond = CountCond} ->
            case lists:keyfind(?DUN_COUNT_COND_DAILY, 1, CountCond) of
                {_, ConfigMax0} ->
                    ConfigMax = calc_crusade_max_num(ConfigMax0, RoleLv),
                    ActType = data_dun_multi_team:get_dun_act_type(DunId),
                    CountList = [
                        {?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, ActType},
                        {?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, ActType}],
                    [{_, _RewardCountPoint}, {_, EnterCount}|_] = lib_daily:get_count_offline(RoleId, CountList),
                    RestCount = max(0, ConfigMax - EnterCount),
                    % NextRewardPoint = calc_next_reward_point(ActType, RewardCountPoint),
                    {?SUCCESS, EnterCount, RestCount, 0};
                _ ->
                    {?ERRCODE(err610_dungeon_count_cfg_error), 0, 0, 0}
            end;
        _ ->
            {?ERRCODE(err610_dungeon_count_cfg_error), 0, 0, 0}
    end.

get_crusade_count_reward(Role, RewardPoint) ->
    #player_status{id = RoleId} = Role,
    [DunId|_] = data_dungeon:get_ids_by_type(?DUNGEON_TYPE_CRUSADE),
    ActType = data_dun_multi_team:get_dun_act_type(DunId),
    CountList = [
        {?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, ActType},
        {?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, ActType}],
    [{_, OldRewardPoint}, {_, EnterCount}|_] = lib_daily:get_count_offline(RoleId, CountList),
    if RewardPoint =< OldRewardPoint orelse RewardPoint > EnterCount ->
            {false, ?ERRCODE(err610_tower_dun_rewarded)};
        true -> %% RewardPoint > OldRewardPoint andalso RewardPoint =< EnterCount
            case data_dun_multi_team:get_all_count(ActType) of
                RewardPoints = [_|_] ->
                    SortedRewardPoints = lists:sort(RewardPoints),
                    case calc_next_reward_point_do(OldRewardPoint, SortedRewardPoints, SortedRewardPoints) of
                        RewardPoint ->
                            OpenDay = util:get_open_day(),
                            case data_dun_multi_team:get_count_rewards(ActType, RewardPoint, OpenDay) of
                                Rewards = [_|_] ->
                                    NewRole = lib_goods_api:send_reward(Role, Rewards, crusade_count_reward),
                                    lib_daily:set_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_PASS_LV, ActType, RewardPoint),
                                    NextRewardPoint = calc_next_reward_point(ActType, RewardPoint),
                                    {true, NewRole, NextRewardPoint};
                                _ ->
                                    {false, ?ERRCODE(err610_dungeon_count_cfg_error)}
                            end;
                        _A ->
                            {false, ?ERRCODE(data_error)}
                    end;
                _ ->
                    {false, ?FAIL}
            end
    end.

calc_next_reward_point(ActType, RewardCountPoint) ->
    case data_dun_multi_team:get_all_count(ActType) of
        RewardPoints = [_|_] ->
            SortedRewardPoints = lists:sort(RewardPoints),
            calc_next_reward_point_do(RewardCountPoint, SortedRewardPoints, SortedRewardPoints);
        _ ->
            0
    end.

calc_next_reward_point_do(RewardCountPoint, [], SortedRewardPoints) ->
    MaxCountPoint = lists:max(SortedRewardPoints),
    if RewardCountPoint >= MaxCountPoint ->
            0;
        true ->
            RewardCountPoint
    end;
calc_next_reward_point_do(RewardCountPoint, [ConfigRewardPoint|Rest], SortedRewardPoints) ->
    if RewardCountPoint < ConfigRewardPoint ->
            ConfigRewardPoint;
        true ->
            calc_next_reward_point_do(RewardCountPoint, Rest, SortedRewardPoints)
    end.

get_base_attr(AttrMap) ->
    AttrList = maps:to_list(AttrMap),
    lists:foldl(fun({AttrId, Val}, Acc) -> 
        if AttrId =/= ?FINAL_HURT_ADD andalso AttrId =/= ?FINAL_HURT_DEL ->
                [{AttrId, Val}|Acc];
            true ->
                Acc
        end
    end, [], AttrList).

%% 计算守卫信标支援次数
calc_guard_assist_count(RoleId) ->
    AssistCount = lib_counter:get_count_offline(RoleId, ?MOD_DUN_MULTI, ?MOD_DUN_MULTI_ASSIST_REWARD, ?DUNGEON_TYPE_GUARD),
    RecoverTime = lib_counter:get_count_offline(RoleId, ?MOD_DUN_MULTI, ?MOD_DUN_MULTI_ASSIST_RECOVER, ?DUNGEON_TYPE_GUARD),
    AssistLimit = data_dungeon_special:get(9, guard_dun_assist_limit),
    RecoverCfg  = data_dungeon_special:get(9, guard_dun_assist_recover),
    Now = utime:unixtime(),
    {NewAssistCount, NewRecoverTime, NextRecoverTime} = calc_last_time(Now, RecoverTime, RecoverTime, AssistCount, AssistCount, AssistLimit, RecoverCfg),
    if NewAssistCount =/= AssistCount ->
            lib_counter:set_count(RoleId, ?MOD_DUN_MULTI, ?MOD_DUN_MULTI_ASSIST_REWARD, ?DUNGEON_TYPE_GUARD, NewAssistCount),
            lib_counter:set_count(RoleId, ?MOD_DUN_MULTI, ?MOD_DUN_MULTI_ASSIST_RECOVER, ?DUNGEON_TYPE_GUARD, NewRecoverTime);
        true ->
            skip
    end,
    % ?ERR("~p ~p ~p", [NewAssistCount, NewRecoverTime, NextRecoverTime]),
    {NewAssistCount, NextRecoverTime}.


%% 根据恢复时间判断支援次数
calc_last_time(Now, InitRecoverTime, LastRecoverTime, InitAssistCount, AssistCount, AssistLimit, RecoverCfg) ->
    LastTime = round(Now - LastRecoverTime),
    if LastTime > RecoverCfg ->
            NewLastRecoverTime = LastRecoverTime + RecoverCfg,
            if AssistCount < AssistLimit ->
                    calc_last_time(Now, InitRecoverTime, NewLastRecoverTime, InitAssistCount, AssistCount+1, AssistLimit, RecoverCfg);
                true ->
                    {AssistCount, 0, 0}
            end;
        true ->
            if InitAssistCount == AssistCount ->
                    {InitAssistCount, InitRecoverTime, InitRecoverTime + RecoverCfg};
                true ->
                    if AssistCount < AssistLimit ->
                            {AssistCount, LastRecoverTime, LastRecoverTime + RecoverCfg};
                        true ->
                            {AssistCount, 0, 0}
                    end
            end
    end.

dun_multi_vote_broad(MemberId, TeamId, VoteTime) ->
    NowTime = utime:unixtime(),
    EndTime = NowTime + VoteTime,
    {ok, BinData} = pt_215:write(21532, [TeamId, EndTime]),
    {ok, BinData1} = pt_215:write(21509, [MemberId, ?DUNMULTITEAM_NOTIFY_MATCH, 0]),
    lib_server_send:send_to_uid(MemberId, <<BinData/binary, BinData1/binary>>),    
    ok.


%% 更新玩家申请列表
role_update_req_list(Player, DunId, TeamId) ->
    #player_status{dun_multi = DunMulti} = Player,
    case DunMulti of
        #role_dun_multi{req_list = ReqList} ->
            NewDunMulti = DunMulti#role_dun_multi{req_list = [{TeamId, DunId}|lists:keydelete(TeamId, 1, ReqList)]};
        _ ->
            NewDunMulti = #role_dun_multi{req_list = [{TeamId, DunId}]}
    end,
    NewPlayer = Player#player_status{dun_multi = NewDunMulti},
    {ok, NewPlayer}.

calc_crusade_max_num(_MaxNum, RoleLv) ->
    CrusadeCounts = data_dungeon_special:get(?DUNGEON_TYPE_CRUSADE, crusade_times),
    calc_crusade_max_num_1(RoleLv, CrusadeCounts, 0).

calc_crusade_max_num_1(_, [], Count) -> Count;
calc_crusade_max_num_1(RoleLv, [{CfgLv, LimitCount}|Rest], Count) ->
    if RoleLv < CfgLv -> Count;
        true ->
            calc_crusade_max_num_1(RoleLv, Rest, LimitCount)
    end.


