%% -----------------------------
%% desc  :副本进程处理逻辑
%% module:dungeon_logic.erl
%% author:hh
%% time  :19/11/12
%% -----------------------------

-module(dungeon_logic).

-export([
    init_dun/4              %% 初始化副本

    ,handle_kill_mon/2      %% 杀怪处理
    ,handle_player_die/3    %% 玩家死亡处理

    ,handle_active_quit/2   %% 主动退出副本
    ,dungeon_timeout/1      %% 副本超时
    ,wave_timeout/4         %% 波数超时

    ,update_time_pass_score/4 %% 副本评分事件
    ,update_dun_rating/4
    ,calc_rating_drop/1     %% 评级事件掉落

    ,disconnect/4           %% 副本内断开处理
    ,reconnect_do/4         %% 副本内重连处理

    ,enter_next_dun/2       %% 副本内直接进入下一关
    ,refresh_init_wave_info/1   %% 波数校正
    ]).

-export([
    send_to_uid/3,
    send_to_uid/5,
    notice_dun_role_info/2,
    update_dungeon_role/3,
    do_update_dungeon_role/2,
    notice_wave_mon_info/5,
    notice_wave_info/1,
    notice_mon_hurt_rank/1,
    get_dun_scene_pool_id/4,
    pull_player_into_dungeon/3,
    handle_dun_drop/5,
    check_role_die_trigger/6,
    role_hp_change_trigger/4,
    get_mon_hurt_info/1,
    get_dun_time_info/1
    ]).

-export([
    nobody_in_dungeon/1
    ]).

-include("dungeon.hrl").
-include("dungeon_pet.hrl").
-include("dungeon_child.hrl").
-include("scene.hrl").
-include("common.hrl").
-include ("def_module.hrl").
-include("def_fun.hrl").
-include("errcode.hrl").
-include("predefine.hrl").
-include("team.hrl").
-include("figure.hrl").
-include("drop.hrl").
-include("server.hrl").
-include("battle.hrl").

-define(EXTRA_TIME, 2). %% 初始额外补偿时间

%% 初始化副本
init_dun(DunId, TeamId, RoleList, Args) ->
    Dun = data_dungeon:get_dun(DunId),
    #dun_cfg{scene_id = SceneId0, type = DunType, time = Time, born_type = BornType} = Dun,
    SceneId1 = get_dun_scene(DunType, DunId, SceneId0, Args),
    Unixtime = utime:unixtime(),
    %% 结束时间
    EndTime = Unixtime + Time,
    % 场景进程分线处理
    {SceneId, ScenePoolId} = get_dun_scene_pool_id(DunType, BornType, SceneId1, RoleList),
    EnterLv = lib_dungeon:calc_enter_lv(RoleList, DunType),
    % 副本数据
    OwnerId = case RoleList of
        [First|_] -> First#dungeon_role.id;
        _         -> 0
    end,
    % 波数
    case lists:keyfind(wave, 1, Args) of
        {_, Wave} -> ok;
        _         -> Wave = 1
    end,
    % 跨服区分
    case lib_dungeon:is_cluster_dungeon_by_dun_id(DunId) of
        true  -> Cluster = ?DUN_CLUSTER_YES;
        false -> Cluster = ?DUN_CLUSTER_NO
    end,
    % 基本数据初始化
    State = #dungeon_state{
        dun_id      = DunId
        , dun_type  = DunType
        , dun_callcack = lib_dungeon_distribute:mod_dungeon_type_map(DunType)
        , scene_id  = SceneId
        , scene_pool_id = ScenePoolId
        , team_id   = TeamId
        , enter_lv  = EnterLv
        , cluster   = Cluster
        , start_time = Unixtime
        , role_list = RoleList
        , wave_num  = 0
        , wave_ref  = [undefined, undefined]
        , end_time  = EndTime
        , owner_id  = OwnerId
    },
    StateAfSet = set_dun_state(Args, State),

    %% 生成友方对象(怪物或者假人)
    StateAfInit = lib_dungeon_distribute:init_friendly_objects(StateAfSet),
    
    %% 初始化波数
    StateAfWave = lib_dungeon_distribute:init_dun_wave(StateAfInit, Wave),
    #dungeon_state{role_list = NewRoleList} = StateAfWave,
    %% 设置副本超时定时器
    CloseRef = erlang:send_after((Time+3)*1000, self(), {'dungeon_timeout'}),
    CopyId = self(),
    %% 拉人进场
    [begin
        pull_player_into_dungeon(CopyId, StateAfWave, DungeonRole)
    end||DungeonRole<-NewRoleList],
    % 组队大厅的处理
    if
        TeamId > 0 ->
            Msg = {change_team_state, TeamId, DunId, 1},
            lib_dun_multi_cluster:cast_dun_multi_hall_cluster(DunId, Msg),
            lists:member(DunType, ?DUNGEON_MON_HURT_TYPE) andalso notice_mon_hurt_rank(StateAfWave);
        true -> ok
    end,
    
    FinalState = StateAfWave#dungeon_state{
        close_ref = CloseRef
    },
    {ok, FinalState}.


%% 击杀怪物处理
handle_kill_mon(Args, State) ->
    #dungeon_state{
        dun_id = DunId, wave_info = WaveInfos, role_list = RoleList, wave_num = WaveNum
    } = State,
    [AutoId, Mid, _DieSign, CreateKey, _OwnId, DieDatas|_] = Args,
    case DieDatas of
        [{killer, AtterId}, {mon_xy, X, Y}|_] ->
            skip;
        [{killer, AtterId}|_] -> X = 0, Y = 0;
        [{collecter, AtterId, _}|_] -> X = 0, Y = 0
    end,
    case CreateKey of
        {?DUN_WAVE_MON_KEY, _, Wave, WaveIndex} ->
            %% 刷新波数状态
            {WaveFinish, NewWaveInfos} = update_wave_info(Wave, WaveIndex, WaveInfos),

            %% 推送波数怪物数量
            notice_wave_mon_info(DunId, Wave, WaveIndex, NewWaveInfos, RoleList),

            NewState0 = State#dungeon_state{wave_info = NewWaveInfos},
            
            %% 检查波数更新(是否进行下一波或结算)
            WaveStatus = calc_dun_wave_state(Wave, WaveFinish, NewWaveInfos, NewState0),
            NewState = lib_dungeon_distribute:dispatch_dungeon_score_event(NewState0, [?DUN_SCORE_DIE, ?DUN_SCORE_DIE_ALL, ?DUN_SCORE_WAVE], AtterId, [Wave, WaveStatus]),
            % ?ERR("~p ~p ~p", [WaveStatus, WaveFinish, NewWaveInfos]),
            %% 其他回调模块自定义处理,先处理怪物,再处理波数
            MonArgs = [AutoId, X, Y],
            NewState1 = lib_dungeon_distribute:handle_kill_mon_do(NewState, [[Mid, AtterId, MonArgs, Wave]]),
            NewState2 = update_per_wave_mon_kill_info(WaveStatus, NewState1),
            %% 推送怪物击杀信息
            notice_mon_kill_info(NewState1),
            %% 通用处理会判断WaveStatus,通关直接走 lib_dungeon_distribute:dungeon_result进行结算
            NewState3  = lib_dungeon_distribute:handle_wave_state(NewState2, [[WaveStatus, Wave]]),
            %% 推送最新波数状态
            (WaveFinish == ?DUN_WAVE_FIN andalso WaveStatus =/= ?WAVE_SAME)
                andalso notice_wave_info(NewState3),
            %% 扣除副本次数
            WaveFinish =:= ?DUN_WAVE_FIN andalso WaveNum =:= 1 andalso
                [begin
                    {MergeEnterCount, MergeBuyCount} = maps:get(?DUN_ROLE_DAILY_COUNTS, RoleData, {0, 0}),
                    if 
                        % MergeCount =< 1 ->
                        %     unode:apply(RoleNode, lib_dungeon, dungeon_count_increment, [DunId, RoleId, HelpType, ?DUN_COUNT_DEDUCT_WAVE]);
                        true ->
                            unode:apply(RoleNode, lib_dungeon, dungeon_merge_count_plus, [DunId, RoleId, HelpType, ?DUN_COUNT_DEDUCT_WAVE, {MergeEnterCount, MergeBuyCount}])
                    end 
                end||  #dungeon_role{node = RoleNode, id = RoleId, help_type = HelpType, typical_data = RoleData} <- RoleList
                ],
            NewState3;
        _ ->
            State
    end.

%% 玩家死亡处理
handle_player_die(State, RoleId, DeadCount) ->
    NewState = lib_dungeon_distribute:handle_player_die(State, [RoleId, DeadCount]),
    lib_dungeon_distribute:dispatch_dungeon_score_event(NewState, [?DUN_SCORE_DIE_COUNT], RoleId, [DeadCount]).


%% 主动退出副本
handle_active_quit(RoleId, State) ->
    #dungeon_state{is_end = IsEnd, result_type = ResultType, result_subtype = ResultSubType} = State,
    %% 若副本未结束主动退出判断,默认失败
    if IsEnd =/= ?DUN_IS_END_YES ->
            {ResultType1, ResultSubType1} = lib_dungeon_distribute:check_quit_state(State, RoleId),
            LogType = ?DUN_LOG_TYPE_HALFWAY_QUIT;
        true ->
            ResultType1    = ResultType,
            ResultSubType1 = ResultSubType,
            LogType = ?DUN_LOG_TYPE_QUIT
    end,
    quit_dungeon(RoleId, LogType, ResultType1, ResultSubType1, State).


%% @doc 
%% 单人副本未结算时第一次请求退出协议,先结算,不走退出流程
%% 第二次请求退出协议才会走退出流程
quit_dungeon(RoleId, LogType, ResultType, ResultSubType, State) ->
    #dungeon_state{role_list = RoleList, is_end = IsEnd, dun_type = DunType} = State,
    case check_dun_role_exist(RoleId, RoleList) of
        {true, _} ->
            %% 检查副本内是否还有人,没有开始结算
            RoleLen = length(RoleList),
            if RoleLen > 1 -> 
                    NewState0 = State;
                true ->
                    NewState0 = lib_dungeon_distribute:dungeon_result(State, ResultType, ResultSubType)
            end,
            IsTeamDun = lists:member(DunType, ?TEAM_DUNGEON),
            %% 单人副本处理人物退出,根据退出时是否结算为依据
            if
                RoleLen =< 1 andalso not IsTeamDun andalso IsEnd =/= ?DUN_IS_END_YES ->
                    {noreply, NewState0};
                true ->
                    NewState = lib_dungeon_distribute:handle_role_out(RoleId, LogType, ResultType, ResultSubType, NewState0),
                    {noreply, NewState}
            end;
        {false, _Errcode} ->
            {noreply, State}
    end.

%% 副本超时
%% 暂时默认检测波数是否打完,打完即通关
dungeon_timeout(State = #dungeon_state{dun_type = ?DUNGEON_TYPE_VIP}) ->
    {ResultType, ResultSubType} = {?DUN_RESULT_TYPE_SUCCESS, ?DUN_RESULT_SUBTYPE_TIMEOUT},
    lib_dungeon_distribute:dungeon_result(State, ResultType, ResultSubType);
dungeon_timeout(State) ->
    #dungeon_state{is_end = IsEnd, dun_type = DunType, wave_info = WaveInfos, dun_id = DunId} = State,
    if IsEnd =/= ?DUN_IS_END_YES ->
            case DunType of ?DUNGEON_TYPE_PET -> Wave = ?DUN_PET_WAVE(DunId); _ -> Wave = 0 end,
            {ResultType, ResultSubType} = ?IF(is_dun_wave_fin(DunType, WaveInfos, Wave, DunId) == true,
                {?DUN_RESULT_TYPE_SUCCESS, ?DUN_RESULT_SUBTYPE_NO}, {?DUN_RESULT_TYPE_FAIL, ?DUN_RESULT_SUBTYPE_TIMEOUT}),
            lib_dungeon_distribute:dungeon_result(State, ResultType, ResultSubType);
        true ->
            State
    end.


%% 副本波数超时
wave_timeout(_DunId, Wave, WaveIndex, State) ->
    #dungeon_state{dun_id = DunId, dun_type = DunType, is_end = IsEnd, wave_info = WaveInfos} = State,
    if IsEnd =/= ?DUN_IS_END_YES ->
            case wave_timeout_check(State, DunType, DunId, Wave, WaveIndex, WaveInfos) of
                {?WAVE_NEXT, NewState} when is_record(NewState, dungeon_state) ->
                    dungeon_default:handle_wave_state_normal(NewState, [?WAVE_NEXT]);
                {?WAVE_NEXT, _NextWave} ->
                    dungeon_default:handle_wave_state_normal(State, [?WAVE_NEXT]);
                true ->
                    State;
                {true, ResultType, ResultSubType} ->
                    lib_dungeon_distribute:dungeon_result(State, ResultType, ResultSubType);
                _ ->
                    ResultType = ?DUN_RESULT_TYPE_FAIL, 
                    ResultSubType = ?DUN_RESULT_SUBTYPE_WAVE_TIMEOUT,
                    lib_dungeon_distribute:dungeon_result(State, ResultType, ResultSubType)
            end;
        true ->
            State
    end.

%% 刷新单波波数
update_wave_info(Wave, WaveIndex, WaveInfos) ->
    case lists:keyfind({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos) of
        DunWaveInfo = #dun_wave_info{status = ?DUN_WAVE_UNFIN} -> %% 只刷新未完成的波数,防止重复刷新
            #dun_wave_info{progress = Progress, require = ConfigReq, status = Status} = DunWaveInfo,
            NewProgress = Progress+1,
            NewStatus = ?IF(NewProgress >= ConfigReq, ?DUN_WAVE_FIN, Status),
            % ?ERR("wave ~p old progress ~p new progress ~p require ~p status ~p new status ~p", [Wave, Progress, NewProgress, ConfigReq, Status, NewStatus]),
            NewDunWaveInfo = DunWaveInfo#dun_wave_info{progress = NewProgress, status = NewStatus},
            WaveInfos0 = lists:keystore({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos, NewDunWaveInfo),
            {NewStatus, WaveInfos0};
        #dun_wave_info{status = _Status} ->
            % ?ERR("wave ~p status ~p", [Wave, Status]),
            {?DUN_WAVE_UNFIN, WaveInfos};
        _ ->
            % ?ERR("wave false ~p", [Wave]),
            {?DUN_WAVE_UNFIN, WaveInfos}
    end.

%% 检查是否所有波数已经完成
check_all_wave_finish(DunType, DunId, WaveInfos) ->
    AllWave = data_dungeon_wave:get_dun_waves(DunId),
    case lists:member(DunType, [?DUNGEON_TYPE_PET, ?DUNGEON_TYPE_GUARD]) of
        true ->
            WaveIndex = 1,
            F = fun(Wave) ->
                case lists:keyfind({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos) of
                    DunWaveInfo = #dun_wave_info{} ->
                        #dun_wave_info{status = Status} = DunWaveInfo,
                        Status == ?DUN_WAVE_FIN;
                    _ ->
                        false
                end end,
            lists:all(F, AllWave);
        _ -> false
    end.

%% 检查某个波数是否完成
check_one_wave_finish(Wave, WaveInfos) ->
    WaveIndex = 1,
    case lists:keyfind({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos) of
        DunWaveInfo = #dun_wave_info{} ->
            #dun_wave_info{status = Status} = DunWaveInfo,
            Status == ?DUN_WAVE_FIN;
        _ -> false
    end.

%% 获取所有怪物伤害列表
%% @doc 固定格式 [{服务器id,figure,伤害输出值},...]
get_mon_hurt_info(State) ->
    #dungeon_state{role_list = RoleList, typical_data = TypicalData} = State,
    %% 真人伤害
    PackData = lists:foldl(fun(DungeonRole, Acc) ->
        #dungeon_role{server_num = ServerNumX, figure = FigureX, typical_data = TypicalDataX} = DungeonRole,
        MonHurtX = maps:get(?DUN_ROLE_CRUSADE_DUN_MON_HURT, TypicalDataX, 0),
        [{ServerNumX, FigureX, MonHurtX}|Acc]
     end, [], RoleList),
    %% 假人伤害
    DunDummys = maps:get(?DUN_STATE_SPECIAL_KEY_DUMMY, TypicalData, []),
    NewPackData = lists:foldl(fun(DunDummy, TmpAcc) ->
        #dungeon_dummy{server_num = ServerNumX1, figure = FigureX1, hurt_calc = DummyHurtX1} = DunDummy,
        [{ServerNumX1, FigureX1, DummyHurtX1}|TmpAcc]
    end, PackData, DunDummys),
    NewPackData.

%% 刷怪通知
notice_wave_mon_info(DunId, Wave, WaveIndex, WaveInfos, RoleList) ->
    case lists:keyfind({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos) of
        #dun_wave_info{progress = Progress, require = Require} -> ok;
        _ -> Progress = 0, Require = 0
    end,
    {ok, BinData} = pt_610:write(61005, [DunId, Wave, Progress, Require]),
    notice_dun_role_info(RoleList, BinData).


%% 怪物伤害输出通知
notice_mon_hurt_rank(State) ->
    #dungeon_state{role_list = RoleList, dun_type = DunType} = State,
    if DunType == ?DUNGEON_TYPE_GUIDE_BOSS ->
            lib_dungeon_guide_boss:notice_mon_hurt_rank(State);
        true ->
            #dungeon_state{role_list = RoleList} = State,
            NewPackData = get_mon_hurt_info(State),
            {ok, BinData} = pt_610:write(61029, [NewPackData]),
            notice_dun_role_info(RoleList, BinData)
    end.

%% 通知最新波数状态
notice_wave_info(State) ->
    #dungeon_state{
        dun_id = DunId, dun_type = DunType, start_time = StartTime, end_time = EndTime, 
        scene_id = SceneId, wave_num = WaveNum, index_of_wave = WaveIndex, 
        wave_info = WaveInfos, role_list = RoleList} = State,
    case data_dungeon_wave:get_dun_wave(DunId, WaveNum) of
        #base_dun_wave_cfg{limit_time = LimitTime} -> ok;
        _ -> LimitTime = 0
    end,
    WaveStartTime =
     case lists:keyfind({WaveNum, WaveIndex}, #dun_wave_info.key, WaveInfos) of
        #dun_wave_info{typical_data = Data} ->
            if DunType == ?DUNGEON_TYPE_GOD ->
                    StartTime0 = maps:get(start_time_ms, Data, 0),
                    StartTime0 div 1000;
                true ->
                    maps:get(start_time, Data, 0)
            end;
        _ ->
            0
    end,
    WaveEndTime = round(WaveStartTime + LimitTime),
    {ok, BinData} = pt_610:write(61004, [SceneId, DunId, StartTime, EndTime, WaveNum, WaveStartTime, WaveEndTime]),
    notice_dun_role_info(RoleList, BinData),
    ok.

%% 通知怪物击杀信息
notice_mon_kill_info(State) ->
    #dungeon_state{dun_id = DunId, typical_data = TypicalData, role_list = RoleList} = State,
    KillMonList = maps:get(?DUN_STATE_SPECIAL_KEY_MON_KILL_CALC, TypicalData, []),
    % ?ERR("~p", [KillMonList]),
    {ok, BinData} = pt_610:write(61069, [DunId, KillMonList]),
    notice_dun_role_info(RoleList, BinData),
    ok.

%% 波数变更,刷新击杀怪物数量统计
update_per_wave_mon_kill_info(WaveStatus, State) ->
    #dungeon_state{dun_id = DunId, typical_data = TypicalData} = State,
    if (WaveStatus == ?WAVE_NEXT orelse WaveStatus == ?DUN_PASS) andalso DunId =/= ?DUN_CONVOY_ID ->
            NewTypicalData = maps:put(?DUN_STATE_SPECIAL_KEY_MON_KILL_CALC, [], TypicalData),
            State#dungeon_state{typical_data = NewTypicalData};
        true ->
            State
    end.

%% 副本内断开
disconnect(RoleId, Hp, HpLim, State) ->
    #dungeon_state{role_list = RoleList} = State,
    case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
        DungeonRole = #dungeon_role{online = ?DUN_ONLINE_YES} ->
            NewRoleList = lists:keystore(RoleId, #dungeon_role.id, RoleList, DungeonRole#dungeon_role{online = ?DUN_ONLINE_DELAY}),
            NewState = State#dungeon_state{role_list = NewRoleList},
            NewState1 = lib_dungeon_distribute:handle_disconnect(RoleId, Hp, HpLim, NewState),
            NewState1;
        _ ->
            State
    end.

%% 重连
reconnect_do(RoleId, AttrList, {RoleX, RoleY}, State) ->
    #dungeon_state{dun_id = DunId, dun_type = DunType, is_end = IsEnd, role_list = RoleList, scene_id = NowSceneId,
        scene_pool_id = ScenePoolId, wave_info = WaveInfos
    } = State,
    case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
        #dungeon_role{node = Node, typical_data = TypicalData} = DungeonRole when IsEnd =/= ?DUN_IS_END_YES ->
            #dungeon_role{
                scene = OutSceneId, scene_pool_id = OutScenePoolId, copy_id = OutCopyId, x = OutX, y = OutY,
                dead_time = DeadTime, dead_count = DeadCount, revive_count = ReviveCount, hp = Hp, hp_lim = HpLim
                } = DungeonRole,
            
            NewDungeonRole = DungeonRole#dungeon_role{online = ?DUN_ONLINE_YES},
            NewRoleList = lists:keystore(RoleId, #dungeon_role.id, RoleList, NewDungeonRole),
            NewState = State#dungeon_state{role_list = NewRoleList},
            {ok, NewState2} = update_dungeon_role(NewState, RoleId, AttrList),
            
            [#dun_wave_info{key = {Wave, WaveIndex}}|_] = WaveInfos,
            lists:member(DunType, [?DUNGEON_TYPE_GOD, ?DUNGEON_TYPE_CHILD]) andalso
                notice_wave_mon_info(DunId, Wave, WaveIndex, WaveInfos, NewRoleList),

            %% 宝宝副本推送逃跑数量
            case DunType == ?DUNGEON_TYPE_CHILD of
                true ->
                    lib_dungeon_child:reconnect(DungeonRole, Wave),
                    RunAwayNum  = get_run_away_num(TypicalData),
                    {ok, Bin} = pt_610:write(61097, [RunAwayNum]),
                    lib_server_send:send_to_uid(RoleId, Bin);
                _ ->
                    ignore
            end,

            %% 宠物副本推送死亡(复活)次数 %% todo:最新击杀波数
            case data_dungeon:get_dun_type(DunId) of
                ?DUNGEON_TYPE_PET ->
                    lib_server_send:send_to_uid(RoleId, pt_610, 61083, [DeadCount]);
                _ ->
                    ok
            end,

            % 玩家传送 & 更新玩家身上数据(包括死亡次数)
            % #ets_scene{x = RoleX, y = RoleY} = data_scene:get(NowSceneId),
            {ChangeX, ChangeY} = lib_dungeon_distribute:get_role_dun_born_xy(State, RoleId, RoleX, RoleY),
            case Hp == 0 of
                true  -> Ghost = 1;
                false -> Ghost = 0
            end,
            Out = #dungeon_out{scene = OutSceneId, scene_pool_id = OutScenePoolId, copy_id = OutCopyId, x = OutX, y = OutY},
            StatusDungeonAttrList = [{dun_id, DunId}, {is_end, IsEnd}, {dead_time, DeadTime},
                {revive_count, ReviveCount}, {dead_count, DeadCount}, {out, Out}, {dun_score, 0}],
            IsCluster = lib_dungeon:is_cluster_dungeon_by_dun_id(DunId),
            RoleAttrList1 = [{hp, Hp}, {hp_lim, HpLim}, {ghost, Ghost}, {status_dungeon, StatusDungeonAttrList}, {group, ?DUN_DEF_GROUP}],
            RoleAttrList = ?IF(IsCluster, [{dun_multi_pid, self()}] ++ RoleAttrList1, [{action_lock, ?ERRCODE(err610_had_on_dungeon)}] ++ RoleAttrList1),
            Args = lib_dungeon_distribute:get_login_args(NewState2, RoleId),
            lib_player:apply_cast(Node, RoleId, ?APPLY_CAST_SAVE, ?NOT_HAND_OFFLINE, lib_dungeon, login_back_to_scene, [NowSceneId, ScenePoolId, self(), ChangeX, ChangeY, RoleAttrList, DunType, Args]),
            
            {noreply, NewState2};
        _ ->
            % ?PRINT(":login no found ~p~n", [RoleId]),
            lib_scene:player_change_default_scene(RoleId, [{change_scene_hp_lim, 0}]),
            {noreply, State}
    end.

%% 宝宝副本逃跑数量
get_run_away_num(TypicalData) ->
    RunAwayNum = maps:get(?DUN_ROLE_CHILD_RUN_AWAY_NUM, TypicalData, 0),
    RunAwayNum.

%% 副本评分-时间事件
update_time_pass_score(RoleId, ConfigIndex, _PassTime, State) ->
    #dungeon_state{
        is_end = IsEnd, result_type = ResultType, role_list = RoleList,
        end_time = EndTime, start_time = StartTime} = State,
    PassScoreResult =
     if IsEnd == ?DUN_IS_END_YES
      andalso ResultType == ?DUN_RESULT_TYPE_SUCCESS -> 1;
        true -> 0
    end,
    % ?ERR("~p ~p ~p", [ConfigIndex, _PassTime, PassScoreResult]),
    RealCostTime = EndTime - StartTime,
    case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
        #dungeon_role{} ->
            if PassScoreResult =/= 1 ->
                    lib_dungeon_distribute:dispatch_dungeon_score_event(State, [?DUN_SCORE_PASS_TIME], RoleId, [ConfigIndex, RealCostTime]);
                true ->
                    State
            end;
        _ ->
            State
    end.

%% 副本通用评级事件
update_dun_rating(RoleId, Rating, _PassTime, State) ->
    #dungeon_state{is_end = IsEnd, result_type = ResultType, role_list = RoleList,
        end_time = EndTime, start_time = StartTime} = State,
    if IsEnd == ?DUN_IS_END_YES
     andalso ResultType == ?DUN_RESULT_TYPE_SUCCESS ->
            State;
        true ->
            case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
                #dungeon_role{} ->
                    RealCostTime = EndTime - StartTime,
                    lib_dungeon_distribute:dispatch_dungeon_rating(State, RoleId, [Rating, RealCostTime]);
                _ ->
                    State
            end
    end.

calc_rating_drop(State) ->
    lib_dungeon_distribute:calc_rating_drop(State).

enter_next_dun(List, State) ->
    lib_dungeon_distribute:enter_next_dun(List, State).

%% ---------------------------------------------------------

nobody_in_dungeon(State) ->
    case State#dungeon_state.role_list of
        [] -> true;
        _  -> false
    end.

notice_dun_role_info([], _BinData) -> skip;
notice_dun_role_info([#dungeon_role{node = Node, id = RoleId}|Rest], BinData) ->
    send_to_uid(Node, RoleId, BinData),
    notice_dun_role_info(Rest, BinData).

%% 发送消息给玩家
send_to_uid(Node, RoleId, BinData) ->
    case node() of
        Node ->
            lib_server_send:send_to_uid(RoleId, BinData);
        _ ->
            rpc:cast(Node, lib_server_send, send_to_uid, [RoleId, BinData])
    end.

send_to_uid(Node, RoleId, ProtoMod, Cmd, Args) ->
    case node() of
        Node ->
            lib_server_send:send_to_uid(RoleId, ProtoMod, Cmd, Args);
        _ ->
            rpc:cast(Node, lib_server_send, send_to_uid, [RoleId, ProtoMod, Cmd, Args])
    end.

check_dun_role_exist(RoleId, RoleList) ->
    case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
        DungeonRole = #dungeon_role{} ->
            {true, DungeonRole};
        _ ->
            {false, ?ERRCODE(err610_dungeon_role_not_exist)}
    end.

%% 检查副本波数状态
%% ?WAVE_SAME  未通关维持不变
%% ?WAVE_CYCLE 循环波数
%% ?WAVE_NEXT  叠加波数
calc_dun_wave_state(_Wave, ?DUN_WAVE_UNFIN, _NewWaveInfos, _State) ->
    ?WAVE_SAME;

%% 宠物副本有自己的判断逻辑
calc_dun_wave_state(Wave, ?DUN_WAVE_FIN, NewWaveInfos, #dungeon_state{dun_type = DunType} = State)
 when DunType == ?DUNGEON_TYPE_PET ->
    #dungeon_state{wave_num = _WaveNum, dun_id = DunId} = State,
    case check_all_wave_finish(DunType, DunId, NewWaveInfos) of
        true -> ?DUN_PASS;
        _ ->
            case check_one_wave_finish(Wave, NewWaveInfos) of
                true ->
                    ?WAVE_NEXT;
                    %% 防止最后一波先杀完触发刷新
                    % case data_dungeon_wave:get_dun_wave(DunId, WaveNum) of
                    %    #base_dun_wave_cfg{next_wave = NextWave} when NextWave == 0 ->
                    %        ?WAVE_NEXT;
                    %    _ ->
                    %        ?WAVE_NEXT
                    % end;
                _    -> ?WAVE_SAME
            end
    end;
%% 宝宝副本
calc_dun_wave_state(Wave, ?DUN_WAVE_FIN, NewWaveInfos, #dungeon_state{dun_type = DunType} = State)
    when DunType == ?DUNGEON_TYPE_CHILD ->
    #dungeon_state{wave_num = WaveNum, dun_id = DunId} = State,
    case check_all_wave_finish(DunType, DunId, NewWaveInfos) of
        true -> ?DUN_PASS;
        _ ->
            case check_one_wave_finish(Wave, NewWaveInfos) of
                true ->
                    case Wave == WaveNum of
                        true ->
                            %% 防止最后一波先杀完触发刷新
                            case data_dungeon_wave:get_dun_wave(DunId, WaveNum) of
                                #base_dun_wave_cfg{next_wave = NextWave} when NextWave == 0 ->
                                    ?WAVE_SAME;
                                _ ->
                                    ?WAVE_NEXT
                            end;
                        _ -> ?WAVE_SAME
                    end;
                _    -> ?WAVE_SAME
            end
    end;

calc_dun_wave_state(_Wave, ?DUN_WAVE_FIN, NewWaveInfos, #dungeon_state{dun_type = DunType} = State)
 when DunType == ?DUNGEON_TYPE_GUARD ->
    #dungeon_state{dun_id = DunId, wave_num = WaveNum} = State,
    case check_all_wave_finish(DunType, DunId, NewWaveInfos) of
        true ->
            case data_dungeon_wave:get_dun_wave(DunId, WaveNum) of
                #base_dun_wave_cfg{next_wave = NextWave} ->
                    if WaveNum == NextWave -> ?WAVE_CYCLE;
                        true ->
                            if NextWave == 0 -> ?DUN_PASS;
                                true -> ?WAVE_NEXT
                            end
                    end;
                _ -> ?WAVE_SAME
            end;
        _ ->
            ?WAVE_NEXT
    end;

calc_dun_wave_state(Wave, ?DUN_WAVE_FIN, NewWaveInfos, State) ->
    #dungeon_state{dun_type = DunType, dun_id = DunId} = State,
    case is_dun_wave_fin(DunType, NewWaveInfos, Wave, DunId) of
        true ->
            case data_dungeon_wave:get_dun_wave(DunId, Wave) of
                #base_dun_wave_cfg{next_wave = NextWave} ->
                    if Wave == NextWave -> %% 循环波数
                            ?WAVE_CYCLE;
                        true ->
                            if NextWave == 0 -> %% 已通关
                                    ?DUN_PASS;
                                true -> %% 开启下一波
                                    ?WAVE_NEXT
                            end
                    end;
                _ ->
                    ?ERR("dun ~p wave ~p state error", [DunId, Wave]),
                    ?WAVE_SAME
            end;
        _ ->
            ?WAVE_SAME
    end.

%% 所有波数是否完成
is_dun_wave_fin(?DUNGEON_TYPE_EXP, WaveInfos, _Wave, _DunId) ->
    case WaveInfos of
        [_|_] ->
            case lists:any(fun(#dun_wave_info{status = Status}) ->
                Status == ?DUN_WAVE_UNFIN
                           end, WaveInfos) of
                true ->
                    false;
                _ ->
                    true
            end;
        _ ->
            false
    end;
is_dun_wave_fin(?DUNGEON_TYPE_PET, WaveInfos, Wave, DunId) ->
    case Wave =/= ?DUN_PET_WAVE(DunId) of
        %% 前14波,任一波完成都更新进度
        true ->
            case lists:keyfind({Wave, 1}, #dun_wave_info.key, WaveInfos) of
                #dun_wave_info{status = Status} when Status == ?DUN_WAVE_FIN ->
                    true;
                _ ->
                    false
            end;
        _ ->
            case lists:any(fun(#dun_wave_info{status = Status}) ->
                Status == ?DUN_WAVE_UNFIN
                           end, WaveInfos) of
                true ->
                    false;
                false ->
                    true
            end
    end;
is_dun_wave_fin(?DUNGEON_TYPE_CHILD, WaveInfos, Wave, DunId) ->
    case Wave =/= ?DUN_CHILD_WAVE(DunId) of
        %% 最后一波前,任一波完成都更新进度;最后一波则需要等待前面波数全部完成
        true ->
            case lists:keyfind({Wave, 1}, #dun_wave_info.key, WaveInfos) of
                #dun_wave_info{status = Status} when Status == ?DUN_WAVE_FIN ->
                    true;
                _ ->
                    false
            end;
        _ ->
            case lists:any(fun(#dun_wave_info{status = Status}) ->
                Status == ?DUN_WAVE_UNFIN
                           end, WaveInfos) of
                true ->
                    false;
                false ->
                    true
            end
    end;
is_dun_wave_fin(_DunType, WaveInfos, _Wave, _DunId) ->
    case lists:any(fun(#dun_wave_info{status = Status}) ->
        Status == ?DUN_WAVE_UNFIN
     end, WaveInfos) of
        true ->
            false;
        false ->
            true
    end.

%% 波数超时时检查
wave_timeout_check(_State, DunType, _DunId, Wave, WaveIndex, WaveInfos)
 when DunType == ?DUNGEON_TYPE_EXP ->
    case lists:keyfind({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos) of
        #dun_wave_info{status = Status} ->
            if Status == ?DUN_WAVE_UNFIN ->
                    false;
                true ->
                    true
            end;
        _ ->
            true
    end;
wave_timeout_check(State, DunType, _DunId, _Wave, _WaveIndex, _WaveInfos)
 when DunType == ?DUNGEON_TYPE_VISUS ->
    #dungeon_state{role_list = [#dungeon_role{id = RoleId}|_]} = State,
    {Type, SubType} = lib_dungeon_distribute:check_quit_state(State, RoleId),
    {true, Type, SubType};
wave_timeout_check(State, DunType, _DunId, Wave, _WaveIndex, WaveInfos)
 when DunType == ?DUNGEON_TYPE_GUARD ->
    #dungeon_state{wave_num = WaveNum} = State,
    if Wave >= WaveNum ->
            case lists:any(fun(#dun_wave_info{status = Status}) -> Status == ?DUN_WAVE_FIN end, WaveInfos) of
                true ->
                    ResultType = ?DUN_RESULT_TYPE_SUCCESS, 
                    ResultSubType = ?DUN_RESULT_SUBTYPE_WAVE_TIMEOUT,
                    {true, ResultType, ResultSubType};
                _ ->
                    false
            end;
        true ->
            true
    end;
wave_timeout_check(_State, DunType, DunId, Wave, _WaveIndex, _WaveInfos)
    when DunType == ?DUNGEON_TYPE_PET ->
    case data_dungeon_wave:get_dun_wave(DunId, Wave) of
        #base_dun_wave_cfg{next_wave = NextWave} when NextWave =/= 0 ->
            {?WAVE_NEXT, NextWave};
        _ ->
            false
    end;
wave_timeout_check(_State, DunType, _DunId, _Wave0, _WaveIndex0, WaveInfos)
 when DunType == ?DUNGEON_TYPE_GOD ->
    [#dun_wave_info{key = {Wave, _WaveIndex}, status = Status}|_] = WaveInfos,
    if
        Wave =< 1 ->
            if
                Status == ?DUN_WAVE_UNFIN ->
                    false;
                true ->
                    ResultType = ?DUN_RESULT_TYPE_SUCCESS, 
                    ResultSubType = ?DUN_RESULT_SUBTYPE_WAVE_TIMEOUT,
                    {true, ResultType, ResultSubType}
            end;
        true ->
        ResultType = ?DUN_RESULT_TYPE_SUCCESS, 
        ResultSubType = ?DUN_RESULT_SUBTYPE_WAVE_TIMEOUT,
        {true, ResultType, ResultSubType} 
    end;
wave_timeout_check(State, DunType, DunId, Wave, _WaveIndex0, _WaveInfos)
    when DunType == ?DUNGEON_TYPE_CHILD ->
    %% 最后一波保持不变，等怪物消失结算
    case data_dungeon_wave:get_dun_wave(DunId, Wave) of
        #base_dun_wave_cfg{next_wave = NextWave} when NextWave =/= 0 ->
            {ResultType, ResultSubType} = lib_dungeon_child:wave_timeout_check(State),
            {true, ResultType, ResultSubType};
        _ ->
            true
            % ResultType = ?DUN_RESULT_TYPE_SUCCESS,
            % ResultSubType = ?DUN_RESULT_SUBTYPE_WAVE_TIMEOUT,
            % {true, ResultType, ResultSubType}
    end;
wave_timeout_check(_State, DunType, _DunId, _Wave, _WaveIndex0, _WaveInfos)
    when DunType == ?DUNGEON_TYPE_POINT ->
    ResultType = ?DUN_RESULT_TYPE_FAIL,
    ResultSubType = ?DUN_RESULT_SUBTYPE_WAVE_TIMEOUT,
    {true, ResultType, ResultSubType};
wave_timeout_check(_State, _DunType, _DunId, Wave, WaveIndex, WaveInfos) ->
    case lists:keyfind({Wave, WaveIndex}, #dun_wave_info.key, WaveInfos) of
        #dun_wave_info{status = Status} ->
            if Status == ?DUN_WAVE_UNFIN ->
                    false;
                true ->
                    true
            end;
        _ ->
            false
    end.

set_dun_state([], State) -> State;
set_dun_state([T|L], State) ->
    NewState = case T of
        {typical_data, Datas}   -> setup_typical_data(State, Datas);
        {start_time, StartTime} -> State#dungeon_state{start_time = StartTime};
        {end_time, EndTime}     -> State#dungeon_state{end_time = EndTime};
        {enter_lv, EnterLv}     -> State#dungeon_state{enter_lv = EnterLv};
        {scene_pool_id, ScenePoolId} -> State#dungeon_state{scene_pool_id = ScenePoolId};
        _ -> State
    end,
    set_dun_state(L, NewState).


%% 更新玩家信息
update_dungeon_role(State, RoleId, AttrList) ->
    #dungeon_state{role_list = RoleList} = State,
    case lists:keyfind(RoleId, #dungeon_role.id, RoleList) of
        false -> {ok, State};
        DungeonRole ->
            NewDungeonRole = do_update_dungeon_role(AttrList, DungeonRole),
            NewRoleList = lists:keyreplace(RoleId, #dungeon_role.id, RoleList, NewDungeonRole),
            NewState = State#dungeon_state{role_list = NewRoleList},
            {ok, NewState}
    end.

do_update_dungeon_role([], DungeonRole) -> DungeonRole;
do_update_dungeon_role([H|T], DungeonRole) ->
    case H of
        {figure, Figure} -> 
            NewDungeonRole = DungeonRole#dungeon_role{figure = Figure};
        {pid, Pid} -> 
            NewDungeonRole = DungeonRole#dungeon_role{pid = Pid};
        {sid, Sid} -> 
            NewDungeonRole = DungeonRole#dungeon_role{sid = Sid};
        {dead_time, DeadTime} -> 
            NewDungeonRole = DungeonRole#dungeon_role{dead_time = DeadTime};
        {revive_count, ReviveCount} -> 
            NewDungeonRole = DungeonRole#dungeon_role{revive_count = ReviveCount};
        {cost_revive_count, ReviveCount} ->
            NewDungeonRole = DungeonRole#dungeon_role{cost_revive_count = ReviveCount};
        {team_position, TeamPosition} -> 
            NewDungeonRole = DungeonRole#dungeon_role{team_position = TeamPosition};
        {node, Node} -> 
            NewDungeonRole = DungeonRole#dungeon_role{node = Node};
        {lv, Lv} ->
            #dungeon_role{figure = Figure} = DungeonRole,
            NewDungeonRole = DungeonRole#dungeon_role{figure = Figure#figure{lv = Lv}};
        {help_type, HelpType} -> 
            NewDungeonRole = DungeonRole#dungeon_role{help_type = HelpType};
        {help_times, HelpTimes} -> 
            NewDungeonRole = DungeonRole#dungeon_role{help_num = HelpTimes};
        {hp, Val} ->
            NewDungeonRole = DungeonRole#dungeon_role{hp = Val}
    end,
    do_update_dungeon_role(T, NewDungeonRole).


%% 初始化模块自定义数据
setup_typical_data(State, [{?DUN_STATE_SPECIAL_KEY_HANDLE_MFA, {M, F, A}}|Datas]) ->
    case lists:member({F, 2}, M:module_info(exports)) of
        true ->
            NewState = M:F(State, A),
            setup_typical_data(NewState, Datas);
        _ ->
            case lists:member({F, 2}, dungeon_default:module_info(exports)) of
                true ->
                    NewState = dungeon_default:F(State, A),
                    setup_typical_data(NewState, Datas);
                _ ->
                    setup_typical_data(State, Datas)
            end
    end;
%% 记录玩家战力(TA数据上报使用)
setup_typical_data(State, [{rec_role_power, _}|Datas]) ->
    #dungeon_state{role_list = RoleList, typical_data = TypicalData} = State,
    RolePowerL = [{RoleId, Power} || #dungeon_role{id = RoleId, combat_power = Power} <- RoleList],
    NewTypicalData = TypicalData#{rec_role_power => RolePowerL},
    setup_typical_data(State#dungeon_state{typical_data = NewTypicalData}, Datas);
setup_typical_data(State, [{Key, Value}|Datas]) ->
    TypicalData = State#dungeon_state.typical_data,
    NewTypicalData = TypicalData#{Key => Value},
    setup_typical_data(State#dungeon_state{typical_data = NewTypicalData}, Datas);
setup_typical_data(State, []) -> State.

get_dun_scene(?DUNGEON_TYPE_EXP, DunId, SceneId0, Args) ->
    case lists:keyfind(wave, 1, Args) of
        {_, Wave} ->
            case data_dungeon_wave:get_dun_wave(DunId, Wave) of
                #base_dun_wave_cfg{scene = SceneId} -> SceneId;
                _   -> SceneId0
            end;
        _ -> SceneId0
    end;
get_dun_scene(_DunType, _DunId, SceneId0, _Args) ->
    SceneId0.

get_dun_scene_pool_id(?DUNGEON_TYPE_VIP, BornType, SceneId, [#dungeon_role{scene = RoleScene}|_] = RoleList) ->
    if BornType == 1 ->
            NewScene = ?MIRROR_IMAGE_SCENE(RoleScene);
        true ->
            NewScene = SceneId
    end,
    ScenePool = scene_mgr:manual_divide_pool(NewScene, [RoleId || #dungeon_role{id = RoleId} <- RoleList]),
    {NewScene, ScenePool};
get_dun_scene_pool_id(_DunType, _BornType, SceneId, RoleList) ->
    ScenePool = scene_mgr:manual_divide_pool(SceneId, [RoleId || #dungeon_role{id = RoleId} <- RoleList]),
    {SceneId, ScenePool}.

%% 拉玩家进场
pull_player_into_dungeon(DunPid, State, DungeonRole) ->
    #dungeon_state{team_id = TeamId, dun_id = DunId, dun_type = DunType, end_time = EndTime,
     scene_id = NowSceneId, scene_pool_id = ScenePoolId, is_end = IsEnd} = State,
    #dungeon_role{id = RoleId, node = Node,
        scene = OutSceneId, scene_pool_id = OutScenePoolId, copy_id = OutCopyId, x = OutX, y = OutY,
        dead_time = DeadTime, revive_count = ReviveCount, hp = Hp, hp_lim = HpLim,
        help_type = HelpType, typical_data = TypicalData} = DungeonRole,
    EnterNext = maps:get(?DUN_ROLE_DUN_ENTER_NEXT, TypicalData, 0),
    CostType  = maps:get(?DUN_ROLE_COUNT_COST_TYPE, TypicalData, ?MOD_DUNGEON_ENTER),
    CostList  = maps:get(?DUN_ROLE_COST_LIST, TypicalData, []),
    PlusCount = maps:get(?DUN_ROLE_DAILY_ENTER_COUNT, TypicalData, 1),
    
    Out = #dungeon_out{
        scene = OutSceneId, scene_pool_id = OutScenePoolId, copy_id = OutCopyId,
         x = OutX, y = OutY, data = maps:put(?DUN_ROLE_DUN_ENTER_NEXT, EnterNext, #{})},
    CopyId = DunPid,
    % 同步数据到其他进程
    DunRecord = #dungeon_record{
            role_id = RoleId,
            dun_pid = self(),
            dun_id = DunId,
            end_time = EndTime
    },
    mod_dungeon_agent:set_dungeon_record(DunRecord),
    OtherArgs = lib_dungeon_distribute:get_login_args(State, RoleId),
    {X, Y} = get_role_dun_born_xy(State, NowSceneId, RoleId, {OutX, OutY}),
    ReviveMap = lib_dungeon_distribute:get_revive_info(DunType, NowSceneId, X, Y),

    Reply = #{
        dun_id => DunId, team_id => TeamId, scene_info => {NowSceneId, ScenePoolId, CopyId},
        pos => {X, Y}, hp => {Hp, HpLim}, dead_time => DeadTime,
        revive_count => ReviveCount, out_info => Out, revive_map => ReviveMap,
        is_end => IsEnd, help_type => HelpType, other_args => OtherArgs, cost_type => CostType,
        cost_list => CostList, enter_count => PlusCount
    },
    unode:apply(Node, lib_dungeon, pull_into_dungeon, [RoleId, Reply]).

get_role_dun_born_xy(State, NowSceneId, RoleId, {OriginX, OriginY}) ->
    #dungeon_state{dun_id = DunId} = State,
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{born_type = 1} ->
            {OriginX, OriginY};
        _ ->
            #ets_scene{x = ChangeX, y = ChangeY} = data_scene:get(NowSceneId),
            lib_dungeon_distribute:get_role_dun_born_xy(State, RoleId, ChangeX, ChangeY)
    end.

handle_dun_drop(Player, MonArgs, Alloc, DropCount, Klist) ->
    #player_status{scene = RoleScene, scene_pool_id = RoleScenePool, copy_id = RoleCopyId} = Player,
    #mon_args{scene = Scene, scene_pool_id = ScenePool, copy_id = CopyId} = MonArgs,
    if RoleScene == Scene andalso RoleScenePool == ScenePool andalso RoleCopyId == CopyId ->
            %% 是否多次掉落
            drop:handle_drop_list(Player, MonArgs, Alloc, Klist, DropCount);
        true ->
            skip
    end.

%% 死亡触发判断
check_role_die_trigger(_DunId, ?DUNGEON_TYPE_GUARD, _Sign, _DieRoleId, _DeadCount, State) ->
    State;
check_role_die_trigger(DunId, _DunType, Sign, DieRoleId, DeadCount, State) ->
    #dungeon_state{role_list = RoleList, typical_data = TypicalData} = State,
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{revive_count = ConfigReviveCount} -> ok;
        _ -> ConfigReviveCount = 0
    end,
    IsAllRoleDie = fun(#dungeon_role{revive_count = ReviveCount, dead_time = DeadTime}) ->
        if 
            ConfigReviveCount == 0 ->
                if DeadTime == 0 -> false;
                    true         -> true
                end;
            true -> ReviveCount >= ConfigReviveCount
        end
     end,
    Dummys = maps:get(?DUN_STATE_SPECIAL_KEY_DUMMY, TypicalData, []),
    DummysNum = length(Dummys),
    DieDummy = maps:get(?DUN_STATE_SPECIAL_KEY_DUMMY_KILLED, TypicalData, 0),
    IsDummyDie = DieDummy >= DummysNum,
    %% 宠物副本推送死亡次数
    case data_dungeon:get_dun_type(DunId) of
        ?DUNGEON_TYPE_PET ->
            DungeonRole = hd(RoleList),
            lib_server_send:send_to_uid(DungeonRole#dungeon_role.id, pt_610, 61083, [DeadCount]);
        _ ->
            ok
    end,
    case lists:all(IsAllRoleDie, RoleList) andalso IsDummyDie of
        true ->
            {ResultType, ResultSubType} = lib_dungeon_distribute:check_quit_state(State, DieRoleId),
            lib_dungeon_distribute:dungeon_result(State, ResultType, ResultSubType);
        _ ->
            if Sign == ?BATTLE_SIGN_DUMMY ->
                    State;
                true ->
                    dungeon_logic:handle_player_die(State, DieRoleId, DeadCount)
            end
    end.

%% 人物血量变化检查评分事件(剩余血量评分)
role_hp_change_trigger(State, RoleId, Hp, HpLim) ->
    #dungeon_state{role_list = RoleList} = State,
    case RoleList of
        [#dungeon_role{grade_progress = GradeProgress}|_] when is_record(GradeProgress, dungeon_role_grade) ->
            #dungeon_role_grade{map = Maps} = GradeProgress,
            case maps:get(?DUN_SCORE_HP, Maps, []) of
                [_|_] ->
                    LastUpdate = get(last_hp_update),
                    Now = utime:unixtime(),
                    if LastUpdate == undefined orelse Now - LastUpdate >= 1 ->
                            Rest = Hp/HpLim*100,
                            NewState = lib_dungeon_distribute:dispatch_dungeon_score_event(State, [?DUN_SCORE_HP], RoleId, [Rest]),
                            put(last_hp_update, Now);
                        true -> NewState = State
                    end;
                _ -> NewState = State
            end;
        _ -> NewState = State
    end,
    NewState.

%% 完成开场特效或特效超时更新已初始化的初始波数信息
refresh_init_wave_info(State) ->
    #dungeon_state{
        dun_id = DunId, wave_num = WaveNum, wave_info = [WaveInfo|_], close_ref = CloseRef, 
        wave_ref = [OldWaveStartRef, OldWaveEndRef],
        animation_ref = AnimationRef
    } = State,
    #dun_cfg{time = DunTime} = data_dungeon:get_dun(DunId),
    #base_dun_wave_cfg{create_time = WaveCreateTime} = DunWaveCfg = data_dungeon_wave:get_dun_wave(DunId, WaveNum),
    #dun_wave_info{typical_data = Map} = WaveInfo,
    
    %% 取消初始创怪&特效定时器
    util:cancel_timer(AnimationRef),
    util:cancel_timer(OldWaveStartRef),
    NowTime = utime:unixtime(),

    %% 更新副本开始&结束时间
    NewStartTime = NowTime + WaveCreateTime,
    NewEndTime   = NewStartTime + DunTime + WaveCreateTime,
    DunLastTime  = NewEndTime - NewStartTime,

    %% 更新初始波数开始时间
    NewMap = maps:put(start_time, NewStartTime, Map),
    NewWaveInfos = [WaveInfo#dun_wave_info{typical_data = NewMap}],

    %% 更新副本结束定时
    NewCloseRef = util:send_after(CloseRef, DunLastTime*1000, self(), {'dungeon_timeout'}),
    State1 = State#dungeon_state{start_time = NewStartTime, end_time = NewEndTime, close_ref = NewCloseRef, wave_info = NewWaveInfos, wave_ref = [undefined, OldWaveEndRef]},
    
    %% 更新副本初始波数创怪定时
    refresh_init_wave_info_logic(State1, DunWaveCfg).

refresh_init_wave_info_logic(State = #dungeon_state{dun_type = ?DUNGEON_TYPE_GOD}, DunWaveCfg) ->
    #dungeon_state{dun_id = DunId, typical_data = StateMap, wave_info = WaveInfos, wave_ref = [OldWaveStartRef, OldWaveEndRef|_], role_list = RoleList} = State,
    [WaveInfo|_] = WaveInfos,
    #dun_wave_info{typical_data = Map, key = {Wave, WaveIndex}} = WaveInfo,
    #base_dun_wave_cfg{limit_time = LimitTime, mon_list = MonList} = DunWaveCfg,

    {Now, WaveStartTime, WaveEndTime} = lib_dungeon_god:calc_wave_range_time(DunWaveCfg),
    % WaveEnd = Now + LimitTime*1000,
    NewMap  = maps:put(start_time_ms, WaveStartTime, Map),
    NewMap1 = maps:put(end_time_ms, WaveEndTime, NewMap),
    NewWaveInfos = [WaveInfo#dun_wave_info{typical_data = NewMap1}],
    State0  = State#dungeon_state{wave_info = NewWaveInfos},
    DunMonList = maps:get(mon_list, StateMap, []),

    %% 波数开始定时
    if 
        Now == WaveStartTime ->
            NewWaveStartRef = OldWaveStartRef,
            NewState = dungeon_default:create_dun_wave_mon(DunMonList, Wave, State0);
        true ->
            WaveCreateTime = WaveStartTime - Now,
            NewWaveStartRef = util:send_after(OldWaveStartRef, WaveCreateTime, self(), {'create_dun_wave_mon', Wave, MonList}),
            NewState = State
    end,
    %% 波数结束定时
    if
        LimitTime == 0 ->
            NewWaveEndRef = undefined;
        true ->
            NewWaveEndRef = util:send_after(OldWaveEndRef, LimitTime*1000, self(), {'wave_timeout', DunId, Wave, WaveIndex})
    end,
    notice_wave_info(NewState),
    spawn(fun() -> timer:sleep(300), notice_wave_mon_info(DunId, Wave, WaveIndex, NewWaveInfos, RoleList) end),
    NewState#dungeon_state{wave_ref = [NewWaveStartRef, NewWaveEndRef]};

refresh_init_wave_info_logic(State, DunWaveCfg) ->
    #dungeon_state{dun_id = DunId, wave_info = [#dun_wave_info{key = {Wave, WaveIndex}}|_], wave_ref = [WaveStartRef, WaveEndRef|_]} = State,
    #base_dun_wave_cfg{
        create_time = WaveCreateTime, limit_time = LimitTime, mon_list = MonList
    } = DunWaveCfg,
    %% 波数开始定时
    if WaveCreateTime == 0 ->
            NewWaveStartRef = WaveStartRef,
            NewState = lib_dungeon_distribute:create_mon(State, Wave, MonList);
        true ->
            NewWaveStartRef = util:send_after(WaveStartRef, WaveCreateTime*1000, self(), {'create_dun_wave_mon', Wave, MonList}),
            NewState = State
    end,
    %% 波数结束定时
    if
        LimitTime == 0 ->
            NewWaveEndRef = undefined;
        true ->
            NewWaveEndRef = util:send_after(WaveEndRef, (LimitTime+WaveCreateTime)*1000, self(), {'wave_timeout', DunId, Wave, WaveIndex})
    end,
    notice_wave_info(NewState),
    NewState#dungeon_state{wave_ref = [NewWaveStartRef, NewWaveEndRef]}.

%% {副本开始,副本结束,当前波数开始,当前波数结束}
get_dun_time_info(State) ->
    #dungeon_state{dun_id = DunId, wave_num = WaveNum, start_time = DunStart, end_time = DunEnd, wave_info = [WaveInfo|_]} = State,
    #dun_wave_info{typical_data = Data} = WaveInfo,
    WaveStart = maps:get(start_time, Data, 0),
    case data_dungeon_wave:get_dun_wave(DunId, WaveNum) of
        #base_dun_wave_cfg{limit_time = LimitTime} -> ok;
        _ -> LimitTime = 0
    end,
    WaveEnd = WaveStart + LimitTime,
    {DunStart, DunEnd, WaveStart, WaveEnd}.