%%%-------------------------------------------------------------------
%%% @doc     宠物副本
%%% Module  : lib_dungeon_pet.erl
%%% Created : 2020-07-06
%%% @Author : tyl
%%%-------------------------------------------------------------------
-module(lib_dungeon_pet).

-include("dungeon.hrl").
-include("pet.hrl").
-include("common.hrl").
-include("goods.hrl").
-include("def_module.hrl").
-include("predefine.hrl").
-include("errcode.hrl").
-include("server.hrl").
-include("scene.hrl").
-include("battle.hrl").
-include("team.hrl").
-include("attr.hrl").
-include("dungeon_pet.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("figure.hrl").

%% API
-export([
	is_dun_pet_scene/1
	,check_follow_pet/1                 %% 跟随战斗单位检查
	,check_use_dun_pet_skill/2          %% 跟随战斗单位出手判断
	,walk_end_handle/2

	%% 副本
	,check_extra/2
	,before_enter_dungeon/3             %% 进入副本前将选择的奖励赋值到副本数据中
	,handle_wave_state/2
	,get_send_reward/2
	,pack_account_kv_data/2
	,handle_dungeon_result/1

	%% 怪物
	,get_create_mon_args/1
	,when_pet_born/3                     %% 妖宠出生监听
	,record_pet_id/2
	,when_hp_change/3                    %% 妖宠血量监测
	,handle_kill_mon_do/2
	,dispatch_dun_event/2
	,timer_check_act_skill/3             %% 每秒检索敌方怪物
	,dungeon_result_on_mon/2
	,dungeon_end_clear_mon/2

	%% 玩家进程
	,login/1
	,get_pet_info/2
	,choose_pet_assist/4                %% 选择跟战宠物
	,get_pet_last_choose/1              %% 之前选择的跟战宠物信息
	,choose_drop_reward/3               %% 选择掉落奖励
	,get_reward_last_choose/1           %% 之前选择的掉落奖励
	,handle_enter_dungeon/2
	,receive_milestone_reward/2         %% 领取里程碑奖励(废弃)
	,update_player_max_score/3          %% 更新玩家最高积分
	,dungeon_result_on_role/2
	,get_sweep_reward/2                 %% 扫荡奖励展示
	,sweep_dungeon_pet/2                %% 扫荡副本
	,update_dun_history_score/3

	%% 玩法相关
	,pick_buff/4                        %% 捡取buff球
	,trigger_buff/2
	,get_all_buff/1                     %% 获取场景内所有buff球信息
	,get_all_dun_buff/2
	,update_dun_data/2                  %% 更新最新通关副本数据

	,update_rank/2
	,handle_event/2
	,select_tar_option/3
	,update_dun_data_on_db/2

]).

-define(DUN_PET_SCENE, 2500).    %% 宠物副本场景Id
-define(DUN_PET_SKILL_LIST,      %% 宠物副本允许使用的技能
	data_dungeon_pet:get_dun_pet_kv_cfg(pet_skill)).

%% vip改变
handle_event(Player = #player_status{}, #event_callback{type_id = ?EVENT_VIP}) ->
	#player_status{id = RoleId, figure = #figure{vip = VipLv}} = Player,
	mod_dun_pet_rank:update_figure(RoleId, [{vip, VipLv}]),
	{ok, Player};

%% 名字改变
handle_event(Player = #player_status{}, #event_callback{type_id = ?EVENT_RENAME}) ->
	#player_status{id = RoleId, figure = #figure{name = RoleName}} = Player,
	mod_dun_pet_rank:update_figure(RoleId, [{name, RoleName}]),
	{ok, Player};

%% 超级贵族
handle_event(Player = #player_status{}, #event_callback{type_id = ?EVENT_SUP_VIP_CHANGE}) ->
	#player_status{id = RoleId, figure = #figure{sup_vip = SupVip}} = Player,
	mod_dun_pet_rank:update_figure(RoleId, [{sup_vip, SupVip}]),
	{ok, Player};

%% 头像改变
handle_event(Player = #player_status{}, #event_callback{type_id = ?EVENT_PICTURE_CHANGE}) ->
	#player_status{id = RoleId, figure = #figure{profile_photo_id = PhotoId}} = Player,
	mod_dun_pet_rank:update_figure(RoleId, [{photo_id, PhotoId}]),
	{ok, Player}.

%% API
is_dun_pet_scene(SceneId) when is_integer(SceneId)  ->
	case SceneId == ?DUN_PET_SCENE  of
		true -> true;
		_ -> false
	end;
is_dun_pet_scene(_SceneId) ->
	false.

walk_end_handle(State, _) ->
	DelayGeneralDriven = [{500, 'find_target'}],
	State1 = mon_state_util:update_general_driven(State, DelayGeneralDriven),
	State1.

%% ----------------------------------------------------------------------------
%% @doc 检查是否可以使用技能 @end
check_use_dun_pet_skill(Player, SkillId) ->
	#player_status{role_dun_pet = RoleDunPet, dungeon = StatusDungeon} = Player,
	#status_dungeon{dun_id = DunId} = StatusDungeon,
	#role_dun_pet{pet_choose = PetChooseMap} = RoleDunPet,
	PetChooseList = maps:get(DunId, PetChooseMap, []),
	SkillList = [TSkillId||{_, _, TSkillId}<-PetChooseList],
	case SkillId > 0 of
		true -> lists:member(SkillId, SkillList++[121000]); % ++[121000]
		_ -> false
	end.

%% 检查跟随宠物参数
check_follow_pet([]) -> true;
check_follow_pet([Check|L]) ->
	case do_check(Check) of
		true -> check_follow_pet(L);
		{false, Err} -> {false, Err}
	end.

do_check({check_dun, DunId}) ->
	AllDunId = data_dungeon_pet:get_all_dun_pet_cfg(),
	case lists:member(DunId, AllDunId) of
		true -> true;
		_ -> {false, ?ERRCODE(err610_dungeon_not_exist)}
	end;
do_check({param, PetList, LimitFollow}) ->
	ParamLen = length(PetList),
	case ParamLen > 0 andalso ParamLen =< LimitFollow of
		true ->
			F = fun({PetId, SlotId}) ->
				case data_pet:get_pet_cfg(PetId, 1) of
					#pet_cfg{} ->
						case SlotId > 0 andalso SlotId =< LimitFollow of
							true -> true;
							_ -> false
						end;
					_ -> false
				end end,
			case lists:all(F, PetList) of
				true -> true;
				_ -> {false, ?ERRCODE(err610_pet_choose_err)}
			end;
		_ ->
			{false, ?ERRCODE(err610_pet_choose_err)}
	end;
do_check({check_same_slot, PetList}) ->
	F = fun(PetInfo, {Ack, TList}) ->
		case PetInfo of
			{_, SlotId} when SlotId > 0 ->
				case lists:member(SlotId, TList) of
					true -> {false, [SlotId|TList]};
					_ -> {Ack, [SlotId|TList]}
				end;
			_ -> {false, TList}
		end end,
	{Res, _} = lists:foldl(F, {true, []}, PetList),
	case Res of
		true -> true;
		_ -> {false, ?ERRCODE(err610_pet_choose_err)}
	end;
do_check({has_pet, PetList, AdoptPests, _FollowPet}) ->
	ChooseList = [TPetId||{TPetId, _}<-PetList],
	F = fun(PetId) ->
		case lists:keyfind(PetId, #pet_info.pet_id, AdoptPests) of
			#pet_info{} -> true;
			_ -> false
		end end,
	case lists:all(F, ChooseList) of
		true -> true;
		_ -> {false, ?ERRCODE(err610_pet_choose_not_adopt)}
	end.

%% ----------------------------------------------------------------------------
%% @doc 助战选择 @end
choose_pet_assist(#player_status{id = RoleId} = Player, DunId, PetList, IsEnter) ->
	% PetInfos  = get_pet_info(Player, PetList),
	NewPlayer = role_update_pet_assist(Player, DunId, PetList),
	lib_server_send:send_to_uid(RoleId, pt_611, 61101, [?SUCCESS, DunId, PetList, IsEnter]),
	{ok, NewPlayer}.

%% 获取跟战宠物属性和形象
get_pet_info(Player, PetList) ->
	PetPowers = lib_pet_api:get_pet_list_with_attr(Player),
	lists:foldl(fun({PetId, PetPos}, Acc) ->
		case lists:keyfind(PetId, 1, PetPowers) of
			{_, AttrList, Figure} -> [{PetId, PetPos, AttrList, Figure}|Acc];
			_ 	-> Acc
		end end, [], PetList).

%% 更新宠物助战数据
role_update_pet_assist(Player, DunId, PetInfos) ->
	#player_status{id = RoleId, role_dun_pet = RoleDunPet
	} = Player,
	#role_dun_pet{pet_choose = PetChooseMap} = RoleDunPet,
	F = fun({PetId, PetPos}, TList) ->
		case lists:keyfind(PetPos, 1, ?DUN_PET_SKILL_LIST) of
			{_, SkillId} -> [{PetId, PetPos, SkillId}|TList];
			_ -> TList
		end end,
	NewPetChoose = lists:foldl(F, [], PetInfos),
	NewPetChooseMap = maps:put(DunId, NewPetChoose, PetChooseMap),
	NewRoleDunPet = RoleDunPet#role_dun_pet{pet_choose = NewPetChooseMap},
	NewPlayer = Player#player_status{role_dun_pet = NewRoleDunPet},
	OldPetChoose = maps:get(DunId, PetChooseMap, []),
	SortChoose1 = lists:keysort(1, OldPetChoose),
	SortChoose2 = lists:keysort(1, NewPetChoose),
	SortChoose1 =/= SortChoose2 andalso db_replace_pet_choose(RoleId, DunId, PetInfos),
	NewPlayer.

%% 上次助战宠物展示
get_pet_last_choose(Player) ->
	#player_status{sid = Sid, role_dun_pet = RoleDunPet
	} = Player,
	#role_dun_pet{pet_choose = PetChooseMap} = RoleDunPet,
	F = fun(DunId, PetChoose, TList) ->
			PetChoose1 = [{PetId, Pos}||{PetId, Pos, _}<-PetChoose],
			[{DunId, PetChoose1}|TList]
		end,
	SendPetChoose = maps:fold(F, [], PetChooseMap),
	lib_server_send:send_to_sid(Sid, pt_611, 61102, [SendPetChoose]),
	ok.

%% ----------------------------------------------------------------------------
%% @doc 奖励选择 @end
choose_drop_reward(Player, DunId, Index) ->
	#player_status{id = RoleId, sid = Sid, role_dun_pet = RoleDunPet
	} = Player,
	#role_dun_pet{reward_choose = RewardChooseMap} = RoleDunPet,
	case data_dungeon_pet:get_dun_pet_reward_cfg(DunId, 1) of
		[_|_] = OptionReward  ->
			case lists:keyfind(Index, 1, OptionReward) of
				{_, [_|_]} ->
					NewRewardChooseMap = maps:put(DunId, Index, RewardChooseMap),
					NewRoleDunPet = RoleDunPet#role_dun_pet{reward_choose = NewRewardChooseMap},
					NewPlayer = Player#player_status{role_dun_pet = NewRoleDunPet},
					db_replace_reward_choose(RoleId, DunId, Index),
					lib_server_send:send_to_sid(Sid, pt_611, 61112,
						[?SUCCESS, DunId, Index]),
					{ok, NewPlayer};
				_ ->
					lib_server_send:send_to_sid(Sid, pt_611, 61112,
						[?ERRCODE(err610_choose_reward_err), 0, 0]),
					ok
			end;
		_ ->
			lib_server_send:send_to_sid(Sid, pt_611, 61112,
				[?ERRCODE(err610_choose_reward_err), 0, 0]),
			ok
	end.

%% 之前选择的奖励掉落
get_reward_last_choose(Player) ->
	#player_status{sid = Sid, role_dun_pet = RoleDunPet
	} = Player,
	#role_dun_pet{reward_choose = RewardChooseMap} = RoleDunPet,
	SendRewardChoose = maps:to_list(RewardChooseMap),
	lib_server_send:send_to_sid(Sid, pt_611, 61113,
		[SendRewardChoose]),
	ok.

%% ----------------------------------------------------------------------------
%% @doc 扫荡奖励展示 @end
get_sweep_reward(Player, DunId) ->
	#player_status{sid = Sid, role_dun_pet = RoleDunPet, dungeon = StatusDun} = Player,
	#role_dun_pet{dun_history_score = HistoryScoreMap, reward_choose = ChooseMap} = RoleDunPet,
	#status_dungeon{dun_data = DunData} = StatusDun,
	HistoryScore = maps:get(DunId, HistoryScoreMap, 0),
	Index = maps:get(DunId, ChooseMap, 1),
	case data_dungeon_pet:get_dun_pet_reward_cfg(DunId, HistoryScore) of
		[_|_] = SelectReward ->
			case lists:keyfind(Index, 1, SelectReward) of
				{_, [_|_] = Reward} -> ok;
				_ ->
					Reward = []
			end;
		_ ->
			Reward = []
	end,
	RewardString = util:term_to_string(Reward),
	PassDunId = case data_dungeon_pet:get_dun_pet_kv_cfg(pet_dun_raid) of
					TNum when is_integer(TNum) -> TNum + 12000;
					_ -> 12001
	            end,
	CanSweep = case lists:keyfind(?DUNGEON_TYPE_PET, 1, DunData) of
		           {_, [FinishDunId]} when FinishDunId > PassDunId ->
			           case Reward =/= [] of
						   true -> 1;
						   _ -> 0
			           end;
		           _ ->
			           0
	           end,
	lib_server_send:send_to_sid(Sid, pt_611, 61123, [DunId, RewardString, CanSweep]),
	ok.

%% @doc 扫荡副本 @end
sweep_dungeon_pet(Player, DunId) ->
	#player_status{sid = Sid, role_dun_pet = RoleDunPet, dungeon = StatusDun} = Player,
	#role_dun_pet{dun_history_score = HistoryScoreMap, reward_choose = ChooseMap} = RoleDunPet,
	HistoryScore = maps:get(DunId, HistoryScoreMap, 0),
	%% 如果还没有选择奖励，默认选项1
	Index = maps:get(DunId, ChooseMap, 1),
	%% 判断次数是否足够
	CountType = lib_dungeon_api:get_daily_buy_type(?DUNGEON_TYPE_PET, 12001),
	VipBuyCount = lib_daily:get_count(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_BUY, CountType),
	case lib_daily:get_count(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, ?DUNGEON_TYPE_PET) of
		Count when is_integer(Count) -> skip;
		_ -> Count = 0
	end,
	LimitCount = data_dungeon_pet:get_dun_pet_kv_cfg(daily_enter_count),
	LeftCount = max(VipBuyCount + LimitCount - Count, 0),
	%% 3星通过第N个副本之后才可以扫荡
	#status_dungeon{dun_data = DunData} = StatusDun,
	PassDunId = case data_dungeon_pet:get_dun_pet_kv_cfg(pet_dun_raid) of
		            TNum when is_integer(TNum) -> TNum + 12000;
		            _ -> 12001
	            end,
	CanSweep = case lists:keyfind(?DUNGEON_TYPE_PET, 1, DunData) of
		           {_, [FinishDunId]} when FinishDunId > PassDunId ->
			           true;
		           _ ->
			           false
	           end,
	case CanSweep of
		true ->
			case LeftCount > 0 of
				true ->
					%% 计算扫荡奖励
					case data_dungeon_pet:get_dun_pet_reward_cfg(DunId, HistoryScore) of
						[_|_] = SelectReward ->
							case lists:keyfind(Index, 1, SelectReward) of
								{_, [_|_] = Reward} ->
									Produce = #produce{type = dun_pet_sweep, reward = Reward},
									{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player, Produce),
									RewardString = util:term_to_string(Reward),
									{ok, Bin0} = pt_611:write(61122,  [?SUCCESS, DunId, RewardString]),
									{ok, Bin1} = pt_610:write(61084,  [max(LeftCount-1, 0)]),
									lib_server_send:send_to_sid(Sid, <<Bin0/binary, Bin1/binary>>),
									lib_daily:increment(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, ?DUNGEON_TYPE_PET);
								_ ->
									RewardString = util:term_to_string([]),
									lib_server_send:send_to_sid(Sid, pt_611, 61122, [?ERRCODE(err610_dun_pet_reward_err), DunId, RewardString]),
									NewPlayer = Player
							end;
						_ ->
							RewardString = util:term_to_string([]),
							lib_server_send:send_to_sid(Sid, pt_611, 61122, [?ERRCODE(err610_dun_pet_not_finish), DunId, RewardString]),
							NewPlayer = Player
					end;
				_ ->
					RewardString = util:term_to_string([]),
					lib_server_send:send_to_sid(Sid, pt_611, 61122, [?ERRCODE(err610_dun_pet_count_not_enough), DunId, RewardString]),
					NewPlayer = Player
			end;
		_ ->
			RewardString = util:term_to_string([]),
			lib_server_send:send_to_sid(Sid, pt_611, 61122, [?ERRCODE(err610_dun_pet_not_finish_three), DunId, RewardString]),
			NewPlayer = Player
	end,
	{ok, NewPlayer}.

%% ----------------------------------------------------------------------------
%% 生成塔防怪
dispatch_dun_event({'init_friendly_objects'}, State) ->
	#dungeon_state{
		dun_id = DunId,
		mon_auto_id = MonAutoId,
		scene_id = NowSceneId,
		scene_pool_id = ScenePoolId
		,role_list = [DungeonRole|_]
	} = State,
	case data_dungeon_pet:get_dun_pet_cfg(DunId) of
		#dun_pet_next_dun_cfg{guard_mons = GuardMons, pet_mon = [{PetMonId, _, _, _, _}] = PetMon} ->
			MonList = PetMon ++ GuardMons;
		_ -> MonList = [], PetMonId = 0
	end,
	MonAutoId1 = lists:foldl(fun({MonId, X, Y, _RandWeight, ConfigAttrs},  MonAutoIdX) ->
		case data_mon:get(MonId) of
			#mon{type = MonType} -> ok;
			_ -> MonType = 1
		end,
		case MonId == PetMonId of
			true ->
				%% 对妖宠(此处对所有怪物,具体逻辑代码实现)进行血量监控
				ExtraArgs = [
					{born_handler, {?MODULE, when_pet_born, []}},
					{hp_change_handler, {?MODULE, when_hp_change, []}},
					{skill_owner, #skill_owner{id = DungeonRole#dungeon_role.id}}, {is_be_clicked, 0}
				];
			_ ->
				ExtraArgs = [{is_be_clicked, 0}, {skill_owner, #skill_owner{id = DungeonRole#dungeon_role.id}}]
		end,
		CycleCheckHandler = {?MODULE, timer_check_act_skill, [1]}, %% 1秒钟检测一次,搜索敌方怪物
		MonAttrs   = [{type, MonType}, {cycle_check_handler, CycleCheckHandler}],
		lib_mon:async_create_mon(MonId, NowSceneId, ScenePoolId, X, Y, 45, self(), 1, ConfigAttrs ++ MonAttrs ++ ExtraArgs),
		MonAutoIdX+1
	end, MonAutoId, MonList),
	State#dungeon_state{mon_auto_id = MonAutoId1};

dispatch_dun_event(Msg, State) ->
	dungeon_default:dispatch_dun_event(Msg, State).

%% 判断是否达到了每天限制次数
check_extra(Player, _DunCfg) ->
	% #dun_cfg{id = DunId} = DunCfg,
	case lib_daily:get_count(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_ENTER, ?DUNGEON_TYPE_PET) of
		Count when is_integer(Count) -> skip;
		_ -> Count = 0
	end,
	LimitCount = data_dungeon_pet:get_dun_pet_kv_cfg(daily_enter_count),
	BuyCount   = lib_daily:get_count(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_BUY, ?DUNGEON_TYPE_PET),
	UniteNum = custom_unite_buff:get_unite_num(Player, ?DUNGEON_TYPE_PET),
	case Count < LimitCount + BuyCount + UniteNum of
		true -> true;
		_ ->
			{false, ?ERRCODE(err610_dun_pet_count_not_enough)}
			%% 判断是否首次进入 modify: 2021-07-08 取消这个功能
			% case lib_counter:get_count(Player#player_status.id, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId) =:= 0 of
			%	true -> true;
			% 	_    -> {false, ?ERRCODE(err610_dun_pet_count_not_enough)}
			% end
	end.

%% 进入副本前将选择的奖励赋值到副本数据中
before_enter_dungeon(Player, DunId, TypicalData) ->
	#player_status{role_dun_pet = RoleDunPet
	} = Player,
	#role_dun_pet{reward_choose = RewardChooseMap} = RoleDunPet,
	%% 如果没有选择，默认给选项1的奖励
	RewardIndex = maps:get(DunId, RewardChooseMap, 1),
	NewTypicalData =
	maps:put(?DUN_ROLE_PET_DUN_CHOOSE_REWARD, [DunId, RewardIndex], TypicalData),
	NewTypicalData.

%% 杀怪后波数处理
handle_wave_state(State, [WaveState|_] = Args) ->
	#dungeon_state{dun_id = DunId, wave_num = WaveNum, role_list = [DungeonRole|_] = RoleList} = State,
	NewRoleList =
	if WaveState == ?WAVE_NEXT orelse WaveState == ?DUN_PASS ->
		%% 更新积分和排行
		NewDungeonRole = update_pet_dun_score_rec(State, DunId, WaveNum, DungeonRole),
		%% 里程碑/首通奖励
		IsFirst = DungeonRole#dungeon_role.is_first,
		case WaveState == ?DUN_PASS of
			true when IsFirst == false ->
				case data_dungeon_pet:get_dun_pet_cfg(DunId) of
					#dun_pet_next_dun_cfg{reward = FirstReward} -> ok;
					_ -> FirstReward = []
				end;
			_ -> FirstReward = []
		end,
		(WaveState == ?DUN_PASS andalso IsFirst =:= false) andalso
			lib_achievement_api:async_event(
				DungeonRole#dungeon_role.id, lib_achievement_api, dungeon_pet_event, 1
			),
		WaveState == ?DUN_PASS andalso
			lib_elite_assess_api:async_event(
				DungeonRole#dungeon_role.id, lib_elite_assess_api, pet_dun_event, DunId
			),
		OldCalcRewardList = DungeonRole#dungeon_role.calc_reward_list,
		NewCalcRewardList = lib_goods_api:make_reward_unique(OldCalcRewardList ++ FirstReward),
		[NewDungeonRole#dungeon_role{calc_reward_list = NewCalcRewardList}];
	true ->
		RoleList
	end,
	%% 需要判断是否断线
	case WaveState == ?DUN_PASS of
		true ->
			RoleId = DungeonRole#dungeon_role.id,
			Pid = misc:get_player_process(RoleId),
			case is_pid(Pid) andalso misc:is_process_alive(Pid) of
				true ->
					lib_player:apply_cast(
						RoleId, ?APPLY_CAST_SAVE, ?MODULE, update_dun_data, [DunId]
					);
				_ -> update_dun_data_on_db(RoleId, DunId)
			end;
		_ -> ignore
	end,
	State0 = State#dungeon_state{role_list = NewRoleList},
	if WaveState == ?DUN_PASS orelse WaveState == ?WAVE_NEXT ->
			dungeon_default:handle_wave_state_normal(State0, Args);
		true ->
			State0
	end.

%% 副本通关后更新数据(必须完成所有波数,即三星通关)
update_dun_data(Player, DunId) ->
	#player_status{dungeon = StatusDun, id = RoleId, role_dun_pet = RoleDunPet} = Player,
	#status_dungeon{dun_data = DunData} = StatusDun,
	#role_dun_pet{reward_status = RewardStatus} = RoleDunPet,
	%% 更新最新通关副本
	case lists:keyfind(?DUNGEON_TYPE_PET, 1, DunData) of
		{_, [OldDunId]} when DunId < OldDunId ->
			NewDunId = OldDunId;
		_ ->
			NewDunId = DunId + 1,
			lib_dungeon:db_replace_dun_data(RoleId, ?DUNGEON_TYPE_PET, [NewDunId])
	end,
	%% 修改里程碑/首通奖励领取状态
	case lists:keyfind(DunId, 1, RewardStatus) of
		{_, Status} when Status > 1 ->
			NewRewardStatus = RewardStatus;
		_ ->
			NewRewardStatus = lists:keystore(DunId, 1, RewardStatus, {DunId, 2}),  %% 状态为已领取
			save_db_dun_pet_reward(RoleId, DunId, 2)
	end,
	NewDunData = lists:keystore(?DUNGEON_TYPE_PET, 1, DunData, {?DUNGEON_TYPE_PET, [NewDunId]}),
	NewRoleDunPet = RoleDunPet#role_dun_pet{reward_status = NewRewardStatus},
	NewStatusDun = StatusDun#status_dungeon{dun_data = NewDunData},
	{ok, Player#player_status{dungeon = NewStatusDun, role_dun_pet = NewRoleDunPet}}.

update_dun_data_on_db(RoleId, DunId) ->
	Sql = io_lib:format(<<"SELECT `dun_type`, `data` FROM `dungeon_data` where role_id = ~p and `dun_type` = ~p">>,
		[RoleId, ?DUNGEON_TYPE_PET]
	),
	case db:get_row(Sql) of
		[_Type, DataStr] ->
			Data = util:bitstring_to_term(DataStr),
			case Data of
				[OldDunId] when DunId < OldDunId ->
					skip;
				_ ->
					NewDunId = DunId + 1,
					lib_dungeon:db_replace_dun_data(RoleId, ?DUNGEON_TYPE_PET, [NewDunId])
			end;
		_ ->
			lib_dungeon:db_replace_dun_data(RoleId, ?DUNGEON_TYPE_PET, [DunId])
	end,
	ok.

%% 副本结束计算奖励(积分奖励+首通奖励(有可能有))
get_send_reward(State, DungeonRole) ->
	#dungeon_state{dun_id = DunId, result_type = _ResultType} = State,
	#dungeon_role{figure = Figure} = DungeonRole,
	if
		%% 不管是否通关15波都会有积分奖励
		DungeonRole#dungeon_role.help_type =:= ?HELP_TYPE_NO ->
			Rewards = calc_wave_rewards(DunId, DungeonRole),
			CustomRewards =
			case lib_dungeon_api:get_custom_act_reward_times(?DUNGEON_TYPE_PET, DunId, Figure#figure.lv) of
				Times when Times > 1 ->
					[{TGoodsType1, TGoodsId1, TGoodsNum1*max(1, Times-1)}||{TGoodsType1, TGoodsId1, TGoodsNum1}<-Rewards];
				_ ->
					[]
			end,
			OldRewards = DungeonRole#dungeon_role.calc_reward_list,
			NewRewards = lib_goods_api:make_reward_unique(OldRewards ++ Rewards),
			DungeonRole#dungeon_role{calc_reward_list = NewRewards, custom_reward_list = CustomRewards};
		true ->
			DungeonRole
	end.

%% 结算界面推送
pack_account_kv_data(DungeonState, _RoleId) ->
	#dungeon_state{role_list = [DungeonRole|_] = _RoleList} = DungeonState,
	#dungeon_role{typical_data = TypicalData} = DungeonRole,
	case maps:get(?DUN_ROLE_PET_DUN_OLD_RANK, TypicalData, 0) of
		OldRank when is_integer(OldRank) ->
			skip;
		_ ->
			OldRank = 0
	end,
	[{?DUN_RESULT_DUN_PET_OLD_RANK, OldRank}].

%% 结算后的处理
handle_dungeon_result(DungeonState) ->
	#dungeon_state{role_list = [DungeonRole|_], result_type = ResultType, result_subtype = ResultSubType
		,dun_id = DunId, wave_num = WaveNum, start_time = StarTime, scene_id = SceneId, scene_pool_id = PoolId
		,wave_info = WaveInfos
	} = DungeonState,
	#dungeon_role{id = RoleId, typical_data = TypicalData, is_first = IsFirst} = DungeonRole,
	TriggerBuffList = maps:get(?DUN_ROLE_PET_TRIGGER_BUFF, TypicalData, []),
	lists:map(fun(BuffId) ->
					case data_dungeon_pet:get_dun_pet_buff_cfg(BuffId) of
						#dun_pet_buff_cfg{effect = [{_, SkillId}]} ->
							lib_skill_api:clean_buff(DungeonRole#dungeon_role.id, SkillId);
						_ -> skip
					end
				end, TriggerBuffList),
	%% TA数据上报
	IsFirst1 =
	case IsFirst of
		false -> 1;
		_ -> 0
	end,
	case lists:foldl(fun(#dun_wave_info{key = {Wave, _}, status = Status}, Acc) ->
		if Status == ?DUN_WAVE_FIN -> [Wave|Acc];
			true -> Acc
		end end, [], WaveInfos) of
		[_Wave|_] = WaveList ->
			NewWaveNum1 = erlang:length(WaveList);
		_ ->
			NewWaveNum1 = 0
	end,
	case data_dungeon_grade:get_dungeon_grade(DunId, 1) of
		#dungeon_grade{condition = Condition} ->
			F = fun(ConInfo, TStar) ->
				case ConInfo of
					{Star, _, Wave} when NewWaveNum1 >= Wave -> Star;
					_ -> TStar
				end end,
			DunStar = lists:foldl(F, 0, Condition);
		_ ->
			DunStar = 0
	end,
	DunScore = maps:get(?DUN_ROLE_PET_DUN_SCORE, TypicalData, 0),
	DunHisScore = maps:get(?DUN_ROLE_PET_DUN_HISTORY_SCORE, TypicalData, 0),
	%% 记录历史最高分数(扫荡结算使用)
	case DunScore > DunHisScore of
		true ->
			lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, update_dun_history_score, [DunId, DunScore]),
			db_replace_dun_max_score(RoleId, DunId, DunScore);
		_ -> ignore
	end,
	UpInfo = [RoleId, DunId, WaveNum, IsFirst1, ResultType, ResultSubType,
		DunStar, DunScore, max(0, utime:unixtime() - StarTime)],
	MonId = maps:get(?DUN_ROLE_PET_DUN_PET_ID, TypicalData, 0),
	%% 宠物死亡找不到怪物，直接发送给玩家进程
	case ResultSubType of
		?DUN_RESULT_SUBTYPE_PET_DIE ->
			NewUpInfo =
				[DunId, WaveNum, IsFirst1, ResultType, ResultSubType, DunStar, DunScore, 0, max(0, utime:unixtime() - StarTime)],
			lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, ?MODULE, dungeon_result_on_role, [NewUpInfo]);
		_ ->
			mon_mgr:send({SceneId, PoolId}, MonId, {mod, ?MODULE, dungeon_result_on_mon, [UpInfo]})
	end,
	DungeonState.

%% 结算的时候先不清理，因为TA数据需要记录宠物血量(等待副本terminate的时候清理场景)
dungeon_end_clear_mon(_State, _ResultType) ->
	false.

%% 记录副本历史积分
update_dun_history_score(Player, DunId, DunScore) ->
	#player_status{role_dun_pet = RoleDunPet} = Player,
	#role_dun_pet{dun_history_score = HistoryScoreMap} = RoleDunPet,
	NewHistoryScoreMap = maps:put(DunId, DunScore, HistoryScoreMap),
	NewRoleDunPet = RoleDunPet#role_dun_pet{dun_history_score = NewHistoryScoreMap},
	{ok, Player#player_status{role_dun_pet = NewRoleDunPet}}.

dungeon_result_on_mon(MonState, UpInfo) ->
	#ob_act{object = #scene_object{battle_attr = #battle_attr{hp = PetHpLeft}}} = MonState,
	[RoleId, DunId, WaveNum, IsFirst, ResultType, ResultSubType,
		DunStar, DunScore, UseTime] = UpInfo,
	NewUpInfo =
	[DunId, WaveNum, IsFirst, ResultType, ResultSubType, DunStar, DunScore, PetHpLeft, UseTime],
	lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, ?MODULE, dungeon_result_on_role, [NewUpInfo]),
	ok.

dungeon_result_on_role(Player, [DunId, DunWave, IsFirst,
	ResultType, ResultSubType, DunStar, DunScore, PetHpLeft, UseTime]
) ->
	#player_status{role_dun_pet = RoleDunPet} = Player,
	#role_dun_pet{reward_choose = ChooseMap} = RoleDunPet,
	DropSelect = maps:get(DunId, ChooseMap, 0),
	ta_agent_fire:pet_dungeon(Player, [DunId, DunWave, IsFirst, DropSelect,
		ResultType, ResultSubType, DunStar, DunScore, PetHpLeft, UseTime]),
	{ok, Player}.

%% ---------------------------------------------------------------------------------
%% 怪物相关处理
%% 怪物生成额外参数
get_create_mon_args(_State) ->
	ExtraArgs = [],
	ExtraArgs.

%% 妖宠出生
%% 记录妖宠唯一Id
when_pet_born(init, Minfo, _) ->
	#scene_object{id = MonId, copy_id = CopyId} = Minfo,
	mod_dungeon:apply(CopyId, ?MODULE, record_pet_id, [MonId]),
	ok.

record_pet_id(DungeonState, [MonId]) ->
	#dungeon_state{role_list = [DungeonRole|_]} = DungeonState,
	#dungeon_role{typical_data = TypicalData} = DungeonRole,
	NewTypicalData = maps:put(?DUN_ROLE_PET_DUN_PET_ID, MonId, TypicalData),
	NewDungeonRole = DungeonRole#dungeon_role{typical_data = NewTypicalData},
	DungeonState#dungeon_state{role_list = [NewDungeonRole]}.

%% 妖宠血量变更
%% 1.实时更新妖宠血量给前端
%% 2.妖宠血量为0时,副本结束
when_hp_change(Object, _MonAtter, _) ->
	#ob_act{
		object = #scene_object{
			config_id = MonId, copy_id = CopyId,
			battle_attr = #battle_attr{hp = Hp, hp_lim = HpLim}
			,skill_owner = #skill_owner{id = OwnerId}
		}
	} = Object,
	case data_mon:get(MonId) of
		#mon{kind = 7} ->
			%% 妖宠血量低于0副本结算
			case Hp =< 0 of
				true ->
					mod_dungeon:apply(CopyId, lib_dungeon_distribute, dungeon_result, [?DUN_RESULT_TYPE_FAIL, ?DUN_RESULT_SUBTYPE_PET_DIE]);
				_ -> skip
			end,
			%% 给玩家发送血量
			lib_server_send:send_to_uid(OwnerId, pt_610, 61079, [Hp, HpLim]);
		_ ->
			skip
	end.

%% 怪物(入侵怪)死亡,生成buff球
handle_kill_mon_do(DungeonState, [_Mid, _Killer, MonXys|_]) ->
	#dungeon_state{role_list = [DungeonRole|_], dun_id = _DunId} = DungeonState,
	#dungeon_role{typical_data = TypicalData} = DungeonRole,
	BuffIdList = data_dungeon_pet:get_dun_pet_buffs_cfg(),
	RandList   = format_rand(BuffIdList, []),
	RandBuffId = urand:rand_with_weight(RandList),
	%% 怪物坐标
	[_AutoId, MonX, MonY] = MonXys,
	case data_dungeon_pet:get_dun_pet_buff_cfg(RandBuffId) of
		#dun_pet_buff_cfg{effect = [{EffectType, _Value}]} ->
			BuffType =
			case EffectType of
				buff_score -> %% 加积分
					EffectType;
				buff_resume -> %% 回复血量
					EffectType;
				buff_defence -> %% 加防御
					EffectType;
				buff_dizzy -> %% 眩晕敌方怪物
					EffectType;
				_ -> none
			end,
			BuffList = maps:get(?DUN_ROLE_PET_MON_BUFF, TypicalData, []),
			case [MonX, MonY] =/= [0, 0] of
				true ->
					case lists:keyfind({RandBuffId, MonX, MonY}, 1, BuffList) of
						{_, Num} ->
							%% 推送新增buff球
							lib_server_send:send_to_uid(DungeonRole#dungeon_role.id, pt_610, 61085, [RandBuffId, MonX, MonY]),
							NewBuffList = lists:keystore({RandBuffId, MonX, MonY}, 1, BuffList, {{RandBuffId, MonX, MonY}, Num + 1});
						_ when BuffType =/= none ->
							lib_server_send:send_to_uid(DungeonRole#dungeon_role.id, pt_610, 61085, [RandBuffId, MonX, MonY]),
							NewBuffList = lists:keystore({RandBuffId, MonX, MonY}, 1, BuffList, {{RandBuffId, MonX, MonY}, 1});
						_ ->
							NewBuffList = BuffList
					end;
				_ -> NewBuffList = BuffList
			end,
			NewTypicalData = maps:put(?DUN_ROLE_PET_MON_BUFF, NewBuffList, TypicalData),
			NewDungeonRole = DungeonRole#dungeon_role{typical_data = NewTypicalData},
			DungeonState#dungeon_state{role_list = [NewDungeonRole]};
		_ ->
			DungeonState
	end.

timer_check_act_skill(_State = #ob_act{object = #scene_object{id = MonId, aid = MonPid}}, _StateName, _Time) ->
	% old: erlang:send_after(0, self(), 'find_target'),
	mon_mgr:send(MonPid, MonId, 'find_target'),
	ok.

%% ---------------------------------------------------------------------------------
%% 玩家进程相关
%% @param:
%% DunId - 副本Id
%% Status - 通关状态 0-未通关 1-可领取 2-已领取
login(RoleId) ->
	RewardStatus =
	case db:get_all(?SQL_SELECT_PET_DUN_REWARD, [RoleId]) of
		DbData = [_|_] ->
			SFun = fun(RewardInfo, TList) ->
				case RewardInfo of
					[TDunId1, Status] ->
						[{TDunId1, Status}|TList];
					_ -> TList
				end end,
			lists:foldl(SFun, [], DbData);
		_ ->
			[]
	end,
	PetChooseMap =
	case db:get_all(?SQL_SELECT_DUN_PET_CHOOSE, [RoleId]) of
		DbData1 = [_|_] ->
			trans_to_choose_map(DbData1, #{});
		_ ->
			#{}
	end,
	RewardChooseMap =
	case db:get_all(?SQL_SELECT_PET_DUN_REWARD_CHOOSE, [RoleId]) of
		DbData2 = [_|_] ->
			RFun = fun(ChooseInfo, TMap) ->
				case ChooseInfo of
					[TDunId2, Index] ->
						maps:put(TDunId2, Index, TMap);
					_ ->
						TMap
				end end,
			lists:foldl(RFun, #{}, DbData2);
		_ ->
			#{}
	end,
	HistoryScoreMap =
	case db:get_all(?SQL_SELECT_PET_DUN_MAX_SCORE, [RoleId]) of
		DbData3 = [_|_] ->
			ScoreFun = fun(ScoreInfo, TMap1) ->
				case ScoreInfo of
					[TDunId3, MaxScore] ->
						maps:put(TDunId3, MaxScore, TMap1);
					_ ->
						TMap1
				end end,
			lists:foldl(ScoreFun, #{}, DbData3);
		_ ->
			#{}
	end,
	#role_dun_pet{
		reward_status  = RewardStatus
		,pet_choose    = PetChooseMap
		,reward_choose = RewardChooseMap
		,dun_history_score = HistoryScoreMap
	}.

trans_to_choose_map([], PetChooseMap) -> PetChooseMap;
trans_to_choose_map([[DunId, PetId, PetPos]|L], TMap) ->
	ChoosePetList = maps:get(DunId, TMap, []),
	NewChoosePetList =
	case lists:keyfind(PetPos, 1, ?DUN_PET_SKILL_LIST) of
		{_, SkillId} -> [{PetId, PetPos, SkillId}|lists:keydelete(PetId, 1, ChoosePetList)];
		_ -> ChoosePetList
	end,
	NewMap = maps:put(DunId, NewChoosePetList, TMap),
	trans_to_choose_map(L, NewMap);
trans_to_choose_map([_|L], TMap) ->
	trans_to_choose_map(L, TMap).

%% 进入副本后更新排行
handle_enter_dungeon(Player, Dun) ->
	#player_status{id = RoleId, copy_id = CopyId} = Player,
	#dun_cfg{id = DunId} = Dun,
	mod_dun_pet_rank:update_dungeon_rank(Player#player_status.id, CopyId),
	%% 更新首次进入单个副本
	case lib_counter:get_count(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId) of
		Count when Count =< 0 ->
			lib_counter:increment_offline(RoleId, ?MOD_DUNGEON, ?MOD_DUNGEON_FIRTS_ENTER, DunId);
		_ -> ok
	end,
	Player.

%% 副本进程处理
update_rank(DungeonState, [MyRank, _MyScore]) ->
	#dungeon_state{role_list = [DungeonRole|_] = _RoleList} = DungeonState,
	#dungeon_role{typical_data = TypicalData} = DungeonRole,
	%% 刚开始打
	TypicalData1 = maps:put(?DUN_ROLE_PET_DUN_OLD_RANK, MyRank, TypicalData),
	NewDungeonRole = DungeonRole#dungeon_role{typical_data = TypicalData1},
	DungeonState#dungeon_state{role_list = [NewDungeonRole]}.

%% 领取里程碑奖励(废弃)
receive_milestone_reward(Player, DunId) ->
	#player_status{sid = Sid, role_dun_pet = RoleDunPet, id = RoleId} = Player,
	#role_dun_pet{reward_status = RewardStatus} = RoleDunPet,
	case lists:keyfind(DunId, 1, RewardStatus) of
		{_, Status} ->
			case Status > 1 of
				true ->
					%% 已经领取
					lib_server_send:send_to_sid(Sid, pt_610, 61074, [?ERRCODE(err610_dun_pet_reward_has_receive), DunId, 2]),
					{ok, Player};
				_ ->
					case data_dungeon_pet:get_dun_pet_cfg(DunId) of
						#dun_pet_next_dun_cfg{reward = Reward} ->
							Produce = #produce{
								title = utext:get(13), content = utext:get(14), reward = Reward, type = dun_pet_milestone, show_tips = 1
							},
							NewRewardStatus = lists:keystore(DunId, 1, RewardStatus, {DunId, 2}),
							NewRoleDunPet = RoleDunPet#role_dun_pet{reward_status = NewRewardStatus},
							save_db_dun_pet_reward(RoleId, DunId, 2),
							{ok, Player1} = lib_goods_api:send_reward_with_mail(Player#player_status{role_dun_pet = NewRoleDunPet}, Produce, ?MOD_DUNGEON),
							lib_server_send:send_to_sid(Sid, pt_610, 61074, [?SUCCESS, DunId, 2]),
							{ok, Player1};
						_ ->
							lib_server_send:send_to_sid(Sid, pt_610, 61074, [?ERRCODE(err610_dun_pet_reward_err), DunId, 0]),
							{ok, Player}
					end
			end;
		_ ->
			%% 未通关
			lib_server_send:send_to_sid(Sid, pt_610, 61074, [?ERRCODE(err610_dun_pet_not_pass), DunId, 0]),
			{ok, Player}
	end.

%% 重新进入副本,查询当前所有的buff球
get_all_buff(Player) ->
	case lib_dungeon:is_on_dungeon(Player) of
		true ->
			#player_status{id = _RoleId, copy_id = CopyId} = Player,
			mod_dungeon:apply(CopyId, ?MODULE, get_all_dun_buff, []);
		_ ->
			skip
	end.

get_all_dun_buff(DungeonState, _Args) ->
	#dungeon_state{dun_type = DunType, role_list = [DungeonRole|_]} = DungeonState,
	#dungeon_role{typical_data = TypicalData, id = RoleId} = DungeonRole,
	case DunType of
		?DUNGEON_TYPE_PET ->
			BuffList = maps:get(?DUN_ROLE_PET_MON_BUFF, TypicalData, []),
			PackBuffList = pack_buff_list(BuffList, []),
			lib_server_send:send_to_uid(RoleId, pt_610, 61077, [PackBuffList]);
		_ ->
			skip
	end,
	DungeonState.

%% 触发buff球
pick_buff(Player, BuffId, X, Y) ->
	case lib_dungeon:is_on_dungeon(Player) of
		true ->
			#player_status{id = _RoleId, copy_id = CopyId, dungeon = StatusDun} = Player,
			#status_dungeon{dun_id = DunId} = StatusDun,
			case data_dungeon:get_dun_type(DunId) of
				DunType when DunType == ?DUNGEON_TYPE_PET ->
					mod_dungeon:apply(CopyId, ?MODULE, trigger_buff, [BuffId, X, Y]);
				_ -> skip
			end;
		_ ->
			skip
	end.

trigger_buff(DungeonState, Args) ->
	#dungeon_state{dun_id = DunId, role_list = [DungeonRole|_]} = DungeonState,
	#dungeon_role{typical_data = TypicalData} = DungeonRole,
	[BuffId, X, Y] = Args,
	case data_dungeon_pet:get_dun_pet_buff_cfg(BuffId) of
		#dun_pet_buff_cfg{effect = [{EffectType, _Value}]} ->
			BuffType =
			case EffectType of
				buff_score -> %% 加积分
					EffectType;
				buff_resume -> %% 回复血量
					EffectType;
				buff_defence -> %% 加防御
					EffectType;
				buff_dizzy -> %% 眩晕敌方怪物
					EffectType;
				_ -> none
			end,
			BuffList = maps:get(?DUN_ROLE_PET_MON_BUFF, TypicalData, []),
			TriggerBuffList = maps:get(?DUN_ROLE_PET_TRIGGER_BUFF, TypicalData, []),
			case lists:keyfind({BuffId, X, Y}, 1, BuffList) of
				{_, _Num} ->
					NewBuffList = lists:keydelete({BuffId, X, Y}, 1,  BuffList),
					NewTypicalData = maps:put(?DUN_ROLE_PET_MON_BUFF, NewBuffList, TypicalData),
					NewTypicalData1 = maps:put(?DUN_ROLE_PET_TRIGGER_BUFF, [BuffId|lists:delete(BuffId, TriggerBuffList)], NewTypicalData),
					NewDungeonRole = DungeonRole#dungeon_role{typical_data = NewTypicalData1},
					%% 加buff
					NewDungeonRole1 = add_buff_core(DungeonState, DunId, BuffType, BuffId, NewDungeonRole),
					{ok, Bin} = pt_610:write(61078, [?SUCCESS, BuffId, X, Y]),
					lib_server_send:send_to_uid(DungeonRole#dungeon_role.id, Bin),
					DungeonState#dungeon_state{role_list = [NewDungeonRole1]};
				_ ->
					{ok, Bin} = pt_610:write(61078, [?FAIL, BuffId, X, Y]),
					lib_server_send:send_to_uid(DungeonRole#dungeon_role.id, Bin),
					DungeonState
			end;
		_ ->
			DungeonState
	end.

%% ---------------------- builtin function -------------------------------------------
add_buff_core(DungeonState, DunId, BuffType, BuffId, DungeonRole) ->
	#dungeon_state{wave_info = WaveInfos} = DungeonState,
	#dungeon_role{id = RoleId, typical_data = TypicalData, figure = Figure} = DungeonRole,
	case BuffType of
		buff_score -> %% 加积分
			case data_dungeon_pet:get_dun_pet_buff_cfg(BuffId) of
				#dun_pet_buff_cfg{effect = [{_, Value}]} ->
					OldScore = maps:get(?DUN_ROLE_PET_DUN_SCORE, TypicalData, 0),
					NewScore = OldScore + Value,
					NewTypicalData = maps:put(?DUN_ROLE_PET_DUN_SCORE, NewScore, TypicalData),
					%% 更新排行榜
					#figure{sex = Sex, profile_photo_id = PhotoId, vip = VipLv, sup_vip = SupVip} = Figure,
					RoleInfo = [Sex, PhotoId, VipLv, SupVip],
					%% 以击杀的波数为统计
					case lists:foldl(fun(#dun_wave_info{key = {Wave, _}, status = Status}, Acc) ->
						if Status == ?DUN_WAVE_FIN -> [Wave|Acc];
							true -> Acc
						end end, [], WaveInfos) of
						[_Wave|_] = WaveList ->
							WaveNum = erlang:length(WaveList);
						_ ->
							WaveNum = 0
					end,
					%% 更新积分
					mod_dun_pet_rank:update_score_record(DungeonRole#dungeon_role.id, RoleInfo, DunId, WaveNum, NewScore),
					%% 更新玩家身上的历史最高积分
					lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, update_player_max_score, [DunId, NewScore]),
					DungeonRole#dungeon_role{typical_data = NewTypicalData};
				_ ->
					DungeonRole
			end;
		_ -> %% 回复血量/加防御/眩晕
			case data_dungeon_pet:get_dun_pet_buff_cfg(BuffId) of
				#dun_pet_buff_cfg{effect = [{_, SkillId}]} ->
					lib_skill_api:add_buff(DungeonRole#dungeon_role.id, SkillId, 1);
				_ ->
					skip
			end,
			DungeonRole
	end.

calc_wave_rewards(DunId, DungeonRole) ->
	#dungeon_role{typical_data = TypicalData} = DungeonRole,
	Score = maps:get(?DUN_ROLE_PET_DUN_SCORE, TypicalData, 0),
	%% 如果玩家没有选择，默认发选项1的奖励
	case maps:get(?DUN_ROLE_PET_DUN_CHOOSE_REWARD, TypicalData, []) of
		[DunId, Index] -> ok;
		_ -> Index = 1
	end,
	case data_dungeon_pet:get_dun_pet_reward_cfg(DunId, Score) of
		[_|_] = OptionReward ->
			case lists:keyfind(Index, 1, OptionReward) of
				{_, Reward} -> Reward;
				_ -> []
			end;
		_ ->
			[]
	end.

pack_buff_list([], PackList) -> PackList;
pack_buff_list([{{BuffId, X, Y}, Num}|L], PackList) ->
	pack_buff_list(L, [{BuffId, X, Y, Num}|PackList]);
pack_buff_list([_|L], PackList) ->
	pack_buff_list(L, PackList).

format_rand([], WeightL) -> WeightL;
format_rand([BuffId|L], TWeightL) ->
	case data_dungeon_pet:get_dun_pet_buff_cfg(BuffId) of
		#dun_pet_buff_cfg{weight = Weight} ->
			format_rand(L,[{Weight, BuffId}|TWeightL]);
		_ ->
			format_rand(L, TWeightL)
	end.

%% 下一波怪,推送新的积分和排名
update_pet_dun_score_rec(DungeonState, DunId, WaveNum, DungeonRole) ->
	#dungeon_state{wave_info = WaveInfos} = DungeonState,
	#dungeon_role{id = RoleId, typical_data = TypicalData, figure = Figure} = DungeonRole,
	case data_dungeon_pet:get_dun_pet_wave_score_cfg(DunId, WaveNum) of
		Score when is_integer(Score) ->
			OldScore = maps:get(?DUN_ROLE_PET_DUN_SCORE, TypicalData, 0),
			NewScore = OldScore + Score,
			NewTypicalData = maps:put(?DUN_ROLE_PET_DUN_SCORE, NewScore, TypicalData),
			%% 更新排行榜
			#figure{sex = Sex, profile_photo_id = PhotoId, vip = VipLv, sup_vip = SupVip} = Figure,
			RoleInfo = [Sex, PhotoId, VipLv, SupVip],
			%% 以击杀的波数为统计
			case lists:foldl(fun(#dun_wave_info{key = {Wave, _}, status = Status}, Acc) ->
				if Status == ?DUN_WAVE_FIN -> [Wave|Acc];
					true -> Acc
				end end, [], WaveInfos) of
				[_Wave|_] = WaveList ->
					WaveNum1 = erlang:length(WaveList);
				_ ->
					WaveNum1 = WaveNum
			end,
			%% 同时更新波数和积分
			mod_dun_pet_rank:update_score_record(DungeonRole#dungeon_role.id, RoleInfo, DunId, WaveNum1, NewScore),
			%% 更新玩家身上的历史最高积分
			lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, update_player_max_score, [DunId, NewScore]),
			DungeonRole#dungeon_role{typical_data = NewTypicalData};
		_ ->
			DungeonRole
	end.

update_player_max_score(Player, DunId, Score) ->
	#player_status{dungeon_record = DungeonRecs, id = RoleId} = Player,
	DunRec = maps:get(DunId, DungeonRecs, []),
	case lists:keyfind(?DUNGEON_REC_MAX_SCORE, 1, DunRec) of
		{_, OldScore} when Score > OldScore ->
			NewDunRec = lists:keystore(?DUNGEON_REC_MAX_SCORE, 1, DunRec, {?DUNGEON_REC_MAX_SCORE, Score}),
			NewDungeonRecs = maps:put(DunId, NewDunRec, DungeonRecs),
			lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec);
		{_, _OldScore} ->
			NewDungeonRecs = DungeonRecs;
		_ ->
			NewDunRec = lists:keystore(?DUNGEON_REC_MAX_SCORE, 1, DunRec, {?DUNGEON_REC_MAX_SCORE, Score}),
			NewDungeonRecs = maps:put(DunId, NewDunRec, DungeonRecs),
			lib_dungeon:save_dungeon_record(RoleId, DunId, NewDunRec)
	end,
	{ok, Player#player_status{dungeon_record = NewDungeonRecs}}.

%% 创建助战宠物怪
%%create_assist_mon(SceneId, ScenePoolId, CopyId, PetInfos, RoleId) ->
%%	MonId = data_dungeon_pet:get_dun_pet_kv_cfg(pet_id),
%%	CycleCheckHandler = {?MODULE, timer_check_act_skill, [1]},
%%	lists:foldl(fun({_PetId, PetPos, AttrList, Figure}, Acc) ->
%%		MonAttrs = [{group, ?DUN_DEF_GROUP},{cycle_check_handler, CycleCheckHandler},{figure, Figure},{attr_list, AttrList},
%%			{is_be_clicked, 0}, {is_be_atted, 0}, {skill_owner, #skill_owner{id = RoleId}}],
%%		case get_assist_mon_pos(PetPos) of
%%			[{X, Y}|_] ->
%%				case data_mon:get(MonId) of
%%					#mon{type = MonType} -> ok;
%%					_ -> MonType = 1
%%				end,
%%				lib_mon:async_create_mon(MonId, SceneId, ScenePoolId, X, Y, MonType, CopyId, 1, MonAttrs),
%%				Acc;
%%			_ ->
%%				Acc
%%		end
%%	end, [], PetInfos).

%%get_assist_mon_pos(1) ->
%%	data_dungeon_pet:get_dun_pet_kv_cfg(pet_place_up);
%%get_assist_mon_pos(2) ->
%%	data_dungeon_pet:get_dun_pet_kv_cfg(pet_place_middle);
%%get_assist_mon_pos(3) ->
%%	data_dungeon_pet:get_dun_pet_kv_cfg(pet_place_down);
%%get_assist_mon_pos(_) ->
%%	[].

%% 攻击目标选择选项
select_tar_option(BattleAttr, X, Y) ->
	#battle_attr{group = Group} = BattleAttr,
	MonId = data_dungeon_pet:get_dun_pet_kv_cfg(pet_id),
	if Group =/= ?DUN_DEF_GROUP -> %% 入侵怪
			{select_all_exclude, MonId};
		true ->
			{closest, X, Y}
	end.

%% ----------------- 数据库操作 -------------------------------
save_db_dun_pet_reward(RoleId, DunId, Status) ->
	Sql = io_lib:format(?SQL_REPLACE_PET_DUN_REWARD, [RoleId, DunId, Status]),
	db:execute(Sql).

db_replace_pet_choose(RoleId, DunId, PetChoose) ->
    SqlList =
        [
            io_lib:format(?SQL_BATCH_DUN_PET_VALUES, [RoleId, DunId, PetId, PetPos])
            || {PetId, PetPos} <- PetChoose
        ],
    case SqlList =/= [] of
        true  ->
        	db:execute(?SQL_DELETE_DUN_PET_CHOOSE, [RoleId, DunId]),
            SQL = io_lib:format(?SQL_REPLACE_DUN_PET_CHOOSE, [string:join(SqlList, ",")]),
            db:execute(SQL);
        false ->
	        db:execute(?SQL_DELETE_DUN_PET_CHOOSE, [RoleId, DunId])
    end.

db_replace_reward_choose(RoleId, DunId, RewardIndex) ->
	SQL = io_lib:format(?SQL_REPLACE_PET_DUN_REWARD_CHOOSE, [RoleId, DunId, RewardIndex]),
	db:execute(SQL),
	ok.

db_replace_dun_max_score(RoleId, DunId, MaxScore) ->
	SQL = io_lib:format(?SQL_REPLACE_PET_DUN_MAX_SCORE, [RoleId, DunId, MaxScore]),
	db:execute(SQL),
	ok.

