%%%-------------------------------------------------------------------
%%% @author  tyl
%%% @doc
%%%      成就逻辑模块
%%% @end
%%% Created : 2021-03-15
%%%-------------------------------------------------------------------
-module(lib_achievement).

-include("achievement.hrl").
-include("common.hrl").
-include("errcode.hrl").
-include("def_fun.hrl").
-include("server.hrl").
-include("def_module.hrl").
-include("goods.hrl").
-include("def_goods.hrl").
-include("vip.hrl").
-include("def_vip.hrl").
-include("predefine.hrl").
-include("figure.hrl").

-export([
	login/1
	,login_achv/1
	,logout/1
	,trigger/5
	,refresh_attr/1
	,check/1

	,send_type_progress/1               %% 成就大类进度
	,send_achievement_list/2            %% 成就信息
	,receive_reward/4                   %% 领取成就奖励
	,send_achievement_lv_info/1         %% 成就等级信息
	,send_achievement_privilege_info/1  %% 成就特权奖励信息
	,receive_privilege_reward/2         %% 领取特权奖励

	,weekly_reward_info/1               %% 每周固定奖励信息
	,receive_weekly_reward/1            %% 领取周固定奖励
	,achievement_privilege_exchange/2   %% 红钻兑换交易券

	,check_can_free_sweep/1             %% 检查是否可以免费扫荡日常任务
]).

%% gm操作
-export([
	gm_reset/1              %% 重置所有成就进度
	,gm_set_finish/4        %% 设置某个成就进度
	,gm_add_exp/2           %% 增加成就经验
	,gm_finish_all/2        %% 完成所有成就
]).

%% TODO: 成就修改思路：
%% 修改原因：一个玩家的dict中存储的成就进度太大。还没全部触发情况下已经达到19KB。
%% 但很多进度实际上没必要拿到内存中
%% 要求：数据库的数据结构不变，只通过逻辑修改进行优化
%% 修改：1. 内存dict中只拉取当前最新的进度，不影响后面进度的触发(因为大部分成就进度实时存库)
%%       2. 触发时的配置筛选需要根据key={type, sub_type}来，而不是现在的模糊式key(atom)匹配

login(RoleId) ->
	List = db_select_achievement_level(RoleId),
	case List of
		[Lv, Exp, RewardS] ->
			case data_achievement:get_achievement_stage_cfg(Lv) of
				#achievement_stage_cfg{attr_list = AttrList} ->
					skip;
				_ ->
					AttrList = []
			end,
			#role_achievement{
				lv   = Lv,
				exp  = Exp,
				attr = AttrList,
				lv_reward_list = util:bitstring_to_term(RewardS)
			};
		_ ->
			#role_achievement{}
	end.

login_achv(RoleId) ->
	List  = db_select_achievement(RoleId),
	{FinishMap, ProgressMap} = init_achievement_list(List, #{}, #{}),
	% Pid = misc:get_player_process(RoleId),
	{FinCountMap, IdMap} = calc_fin_count_and_now_id(FinishMap),
	IdMap1 = fix_id_map(IdMap),
	ProcAchievement = #proc_achievement{
		finish_map    = FinishMap,
		progress_map  = ProgressMap,
		fin_count_map = FinCountMap,
		now_id_map    = IdMap1
	},
	save_achievement_dict(ProcAchievement),
	ProcAchievement.

%% 对当前进度+1
fix_id_map(IdMap) ->
	IdList = maps:to_list(IdMap),
	F = fun({{Type, SubType} = Key, Id}, TMap) ->
		case data_achievement:get_max_id(Type, SubType) of
			Id -> maps:put(Key, Id, TMap);
			_  -> maps:put(Key, Id + 1, TMap)
		end end,
	lists:foldl(F, #{}, IdList).

calc_fin_count_and_now_id(FinishMap) ->
	F = fun(Type, FinishList, {TFinMap, TIdMap}) ->
		AllSubType = data_achievement:get_sub_list_by_type(Type),
		do_calc_fin_count_and_now_id(Type, AllSubType, FinishList, TFinMap, TIdMap)
	end,
	maps:fold(F, {#{}, #{}}, FinishMap).

do_calc_fin_count_and_now_id(Type, AllSubType, FinishList, FinMap, IdMap) ->
	ForFun = fun({SubType, Id}, {TFinMap, TIdMap}) ->
		FinCount = maps:get(Type, TFinMap, 0),
		NowId = maps:get({Type, SubType}, TIdMap, 0),
		case lists:keyfind({SubType, Id}, #achievement.key, FinishList) of
			#achievement{} ->
				TFinMap1 = maps:put(Type, FinCount+1, TFinMap),
				case Id > NowId of
					true ->
						TIdMap1 = maps:put({Type, SubType}, Id, TIdMap);
					_ ->
						TIdMap1 = TIdMap
				end,
				{TFinMap1, TIdMap1};
			_ ->
				{TFinMap, TIdMap}
		end end,
	lists:foldl(ForFun, {FinMap, IdMap}, AllSubType).

%% 退出更新成就进度(未完成部分成就有写库CD)
logout(RoleId) ->
	case get_achievement_dict(RoleId) of
		{ok, StatusAchievement} ->
			#proc_achievement{
				progress_map = ProgressMap
			} = StatusAchievement,
			NowTime = utime:unixtime(),
			ForFun = fun(Type, ProgressList) ->
				F = fun(T, SqlTemp) ->
					case T of
						#achievement{
							key = {SubType, Id},
							progress = Progress,
							status   = ?ACHV_UNFINISH,
							wstatus  = ?ACHV_WSTATUS_WAIT
						} ->
							Value = io_lib:format(?batch_insert_achievement_value,[
								RoleId, Type, SubType, Id, Progress, ?ACHV_UNFINISH, NowTime]
							),
							[Value|SqlTemp];
						_ -> SqlTemp
					end end,
				case lists:foldl(F, [], ProgressList) of
					[_|_] = SqlList ->
						NewValues = util:join_bitstring(SqlList, <<", ">>),
						FinalSql  = util:fmt_bin(?batch_insert_achievement, [NewValues]),
						db:execute(FinalSql);
					_ ->
						skip
				end end,
			maps:map(ForFun, ProgressMap);
		_ -> skip
	end.

trigger(RoleId, RoleLv, TypeList, Key, Args) ->
	case get_achievement_dict(RoleId) of
		{ok, StatusAchievement} ->
			trigger(RoleId, RoleLv, StatusAchievement, TypeList, Key, Args);
		_ -> skip
	end.

trigger(RoleId, _RoleLv, StatusAchievement, TypeList, Key, Args) ->
	NowTime = utime:unixtime(),
	case TypeList of
		[Type, SubType] -> ok;
		_ -> Type = 0, SubType = 0
	end,
	AllIds = get_all_achievement_ids(Type, SubType),  %% 筛选出符合条件的 type,subtype 对应id
	% ?INFO_TTY("AllIds is ~w~n",[AllIds]),
	% AllIds  = get_all_achievements(),
	CfgL = filter_achievement_cfg_by_key(AllIds, Key, []),
	F = fun() ->
		put(broadcast_info, []),
		{NewStatusAchievement, UTypeL} = do_trigger(CfgL, RoleId, StatusAchievement, Args, NowTime, []),
		UpdateList = erase(broadcast_info),
		{ok, UpdateList, UTypeL, NewStatusAchievement}
	    end,
	case catch db:transaction(F) of
		{ok, UpdateList, _UTypeL, NewStatusAchievement} ->
			% ?INFO_TTY("UpdateList is ~w~n",[UpdateList]),
			save_achievement_dict(NewStatusAchievement),
			%% 日志 todo
			F1 = fun(UpInfo) ->
				case UpInfo of
					{{_Type, _SubType, _Id}, Status, _Progress}
					when Status == ?ACHV_FINISH ->
						ok; % lib_log_api:log_achv(RoleId, RoleLv, Type, SubType, Id);
					_ -> skip
				end end,
			lists:foreach(F1, UpdateList),
			%% 有成就的状态发生改变(进度完成)通知客户端
			case UpdateList =/= [] of
				true ->
					notify_to_client(?NOTIFY_SUB_TYPE_PROGRESS, RoleId, UpdateList);
				false ->
					skip
			end;
		Error ->
			?ERR("role_id:~p trigger achievement args:~p err:~p~n", [RoleId, Args, Error])
	end.

do_trigger([], _RoleId, ProcAchievement, _Args, _NowTime, UTypeL) ->
	{ProcAchievement, lists:usort(UTypeL)};
do_trigger([#achievement_cfg{type = Type, sub_type = SubType, id = Id} = Cfg|L], RoleId, ProcAchievement, Args, NowTime, UTypeL) ->
	#proc_achievement{
		progress_map = ProgressMap, finish_map = FinishMap
	} = ProcAchievement,
	FinishList   = maps:get(Type, FinishMap, []),
	ProgressList = maps:get(Type, ProgressMap, []),
	case lists:keyfind({SubType, Id}, #achievement.key, FinishList) of
		false ->
			#achievement_cfg{
				type = Type, sub_type = SubType, condition = Condition
			} = Cfg,
			case lists:keyfind({SubType, Id}, #achievement.key, ProgressList) of
				false ->
					NeedInsert = true,
					OldAchievementInfo = #achievement{key = {SubType, Id}};
				OldAchievementInfo ->
					NeedInsert = false
			end,
			#achievement{
				progress = OldProgress, status = Status, wtime = LastWtime
			} = OldAchievementInfo,
			case Status == ?ACHV_UNFINISH of
				true ->
					NewAchievementInfo = lib_achievement_trigger:trigger(Args, Condition, OldAchievementInfo),
					#achievement{progress = NewProgress, status = NewStatus} = NewAchievementInfo,
					case NewProgress =/= OldProgress orelse Status =/= NewStatus of
						true ->
							SaveDBCD = get_save_cd(Args),
							case NewStatus == ?ACHV_FINISH orelse LastWtime == 0 orelse NowTime - LastWtime >= SaveDBCD of
								true ->
									%% 检测是更新还是插入新数据
									case NeedInsert of
										true ->
											db:execute(io_lib:format(?insert_achievement,
												[RoleId, Type, SubType, Id, NewProgress, NewStatus, NowTime])
											);
										false ->
											db:execute(io_lib:format(?update_achievement_progress,
												[NewProgress, NewStatus, NowTime, RoleId, Type, SubType, Id])
											)
									end,
									LastAchievementInfo = NewAchievementInfo#achievement{wtime = NowTime, wstatus = ?ACHV_WSTATUS_SUCCESS};
								_ ->
									LastAchievementInfo = NewAchievementInfo#achievement{wstatus = ?ACHV_WSTATUS_WAIT}
							end,
							%% 缓存更新的成就进度(包括完成/未完成),整理后推送给客户端
							broadcast_info_add(Type, SubType, Id, NewProgress, NewStatus),
							NewUpTypeL = case NewStatus == ?ACHV_FINISH of
								             true ->
									             [{Type, SubType}|UTypeL];
								             false ->
									             UTypeL
							             end,
							NewProgressL = lists:keystore({SubType, Id}, #achievement.key, ProgressList, LastAchievementInfo),
							NewProgressMap = maps:put(Type, NewProgressL, ProgressMap);
						false ->
							NewProgressMap = ProgressMap,
							NewUpTypeL     = UTypeL
					end,
					NewProcAchievement = ProcAchievement#proc_achievement{progress_map = NewProgressMap},
					do_trigger(L, RoleId, NewProcAchievement, Args, NowTime, NewUpTypeL);
				_ ->
					do_trigger(L, RoleId, ProcAchievement, Args, NowTime, UTypeL)
			end;
		_ ->
			do_trigger(L, RoleId, ProcAchievement, Args, NowTime, UTypeL)
	end.

get_save_cd(Args) ->
	%% 杀怪的成就触发太频繁,只有第一次以及完成的时候才实时写入数据库,其他情况根据实际情况调整写入数据库的CD时间
	SaveDBCD = case Args of
		           [{kill_monster, _}]    -> 1800;
		           [{void_fam_score, _}] -> 30;     % 30s保存一次
		           [{point, _}] -> 30;              % 30s保存一次
		           [{cumulative_gold, _}] -> 300;
		           _ -> ?ACHV_SAVE_DB_CD
	           end,
	SaveDBCD.

%% ---------------------------------------------------------------------
%% @doc 成就大类总进度 @end
%% ---------------------------------------------------------------------
send_type_progress(RoleId) ->
	case get_achievement_dict(RoleId) of
		{ok, ProcAchievement} ->
			#proc_achievement{
				fin_count_map = FinCountMap
			} = ProcAchievement,
			TypeList = maps:to_list(FinCountMap),
			{ok, BinData} = pt_409:write(40901, [TypeList]),
			lib_server_send:send_to_uid(RoleId, BinData),
			ok;
		_ ->
			ignore
	end.

%% ---------------------------------------------------------------------
%% @doc 成就信息 @end
%% ---------------------------------------------------------------------
send_achievement_list(RoleId, Type) ->
	case get_achievement_dict(RoleId) of
		{ok, ProcAchievement} ->
			#proc_achievement{
				finish_map = FinishMap, progress_map = ProgressMap
				,now_id_map = NowIdMap
			} = ProcAchievement,
			FinishList   = maps:get(Type, FinishMap, []),
			ProgressList = maps:get(Type, ProgressMap, []),
			PackL = case data_achievement:get_sub_list_by_type(Type) of
				        [_|_] = SubTypeL ->
					        get_sub_type_progress(Type, SubTypeL, ProgressList, FinishList, NowIdMap);
				        _ ->
					        []
			        end,
			{ok, BinData} = pt_409:write(40902, [Type, PackL]),
			lib_server_send:send_to_uid(RoleId, BinData);
		_ ->
			skip
	end.

get_sub_type_progress(Type, SubTypeL, ProgressList, FinishList, NowIdMap) ->
	case get_cur_achievement(Type, SubTypeL, ProgressList, FinishList, NowIdMap, []) of
		[_|_] = ShowList ->
			F = fun(Achievement, Acc) ->
				case Achievement of
					#achievement{
						key = {SubType, Id},
						progress = Progress,
						status = Status
					} ->
						[{SubType, Id, Progress, Status}|Acc];
					_ ->
						Acc
				end end,
			lists:foldl(F, [], ShowList);
		_ ->
			[]
	end.

%% ---------------------------------------------------------------------
%% @doc 领取成就达成奖励 @end
%% ---------------------------------------------------------------------
receive_reward(#player_status{sid = Sid, id = RoleId} = Player, Type, SubType, Id) ->
	case get_achievement_dict(RoleId) of
		{ok, ProcAchievement} ->
			#proc_achievement{
				finish_map = FinishMap, progress_map = ProgressMap,
				fin_count_map = FinMap, now_id_map = NowIdMap
			} = ProcAchievement,
			FinishList   = maps:get(Type, FinishMap, []),
			ProgressList = maps:get(Type, ProgressMap, []),
			CheckList = [{ach_cfg, Type, SubType, Id}, {check_receive, SubType, Id, FinishList}
				,{has_finish, SubType, Id, ProgressList}, {pre_id, Type, SubType, Id, FinishList},
				{now_id, Type, SubType, NowIdMap, Id}, {cell_enough, Player, Type, SubType, Id}
			],
			case check_receive_ach_reward(CheckList) of
				true ->
					#achievement_cfg{reward = RewardL} =
						data_achievement:get_achievement_cfg(Type, SubType, Id),
					Achievement = lists:keyfind({SubType, Id},
						#achievement.key, ProgressList),
					db_update_achievement(RoleId, Type, SubType, Id,
						Achievement#achievement{status = ?ACHV_HAS_RECEIVE}),
					NewProgressL = lists:keydelete(
						{SubType, Id}, #achievement.key, ProgressList
					),
					NewFinishL = [Achievement#achievement{status = ?ACHV_HAS_RECEIVE}|FinishList],
					NewProcAchievement = ProcAchievement#proc_achievement{
						finish_map    = maps:put(Type, NewFinishL, FinishMap),
						progress_map  = maps:put(Type, NewProgressL, ProgressMap),
						fin_count_map = maps:put(Type, erlang:length(NewFinishL), FinMap),
						now_id_map    = maps:put({Type, SubType}, Id+1, NowIdMap)
					},
					save_achievement_dict(NewProcAchievement),
					%% 提升成就等级
					Player1 = upgrade_achievement_level(Player, Type, SubType, Id),
					Produce = #produce{
						type = achievement_reward, subtype = 1, reward = RewardL, show_tips = 1
					},
					{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player1, Produce),
					send_msg(Sid, 40903, [?SUCCESS, Type, SubType, Id]),
					%% 推送下一Id的进度
					notice_next_progress(Type, SubType, Id, NewProgressL, RoleId),
					%% 推送大类进度
					notice_type_count(RoleId, Type, SubType, Id),
					NewPlayer;
				{false, Err} ->
					send_error_code(Sid, Err), Player
			end;
		_ ->
			Player
	end.

notice_next_progress(Type, SubType, Id, ProgressList, RoleId) ->
	MaxId = data_achievement:get_max_id(Type, SubType),
	NewId = min(Id + 1, MaxId),
	case lists:keyfind({SubType, NewId}, #achievement.key, ProgressList) of
		#achievement{progress = Progress, status = Status} ->
			notify_to_client(?NOTIFY_SUB_TYPE_PROGRESS, RoleId,
				{Type, SubType, NewId, Progress, Status});
		_ ->
			%% 如果没有进度，推送默认0的进度
			case NewId =/= MaxId of
				true ->
					notify_to_client(?NOTIFY_SUB_TYPE_PROGRESS, RoleId,
						{Type, SubType, NewId, 0, 0});
				_ ->
					ignore
			end
	end,
	ok.

notice_type_count(RoleId, Type, _SubType, _Id) ->
	notify_to_client(?NOTIFY_TYPE_FIN_COUNT, RoleId, [Type]),
	ok.

check_receive_ach_reward([]) -> true;
check_receive_ach_reward([Check|L]) ->
	case do_check_recieve_ach(Check) of
		true -> check_receive_ach_reward(L);
		{false, Err} -> {false, Err}
	end.

do_check_recieve_ach({ach_cfg, Type, SubType, Id}) ->
	case data_achievement:get_achievement_cfg(Type, SubType, Id) of
		#achievement_cfg{} -> true;
		_ -> {false, ?ERRCODE(missing_config)}
	end;
do_check_recieve_ach({check_receive, SubType, Id, FinishList}) ->
	case lists:keyfind({SubType, Id}, #achievement.key, FinishList) of
		#achievement{} -> {false, ?ERRCODE(err409_has_received)};
		_ -> true
	end;
do_check_recieve_ach({has_finish, SubType, Id, ProgressList}) ->
	case lists:keyfind({SubType, Id}, #achievement.key, ProgressList) of
		#achievement{status = ?ACHV_FINISH} -> true;
		_ -> {false, ?ERRCODE(err409_not_finish)}
	end;
do_check_recieve_ach({now_id, Type, SubType, NowIdMap, ReceiveId}) ->
	case maps:get({Type, SubType}, NowIdMap, 0) of
		Id when Id =:= 0 ->
			MinId = data_achievement:get_min_id(Type, SubType),
			ReceiveId =:= MinId;
		Id when Id =:= ReceiveId -> true;
		_ -> {false, ?ERRCODE(err409_pre_id)}
	end;
do_check_recieve_ach({pre_id, Type, SubType, Id, FinishList}) ->
	PreId = case data_achievement:get_achievement_cfg(Type, SubType, Id - 1) of
				#achievement_cfg{} -> Id - 1;
				_ -> Id
	        end,
	MinId = data_achievement:get_min_id(Type, SubType),
	case Id =:= MinId of
		true -> true;
		_ ->
			case lists:keyfind({SubType, PreId}, #achievement.key, FinishList) of
				#achievement{} -> true;
				_ -> {false, ?ERRCODE(err409_pre_id)}
			end
	end;
do_check_recieve_ach({cell_enough, PlayerStatus, Type, SubType, Id}) ->
	case data_achievement:get_achievement_cfg(Type, SubType, Id) of
		#achievement_cfg{reward = RewardL} ->
			case lib_goods_api:can_give_goods(PlayerStatus, RewardL) of
				true -> true;
				_ -> {false, ?ERRCODE(err150_no_cell)}
			end;
		_ -> {false, ?ERRCODE(missing_config)}
	end;
do_check_recieve_ach(_) ->
	?ERR("check receive achievement reward err"),
	false.

%% ---------------------------------------------------------------------
%% @doc 获取成就等级信息 @end
%% ---------------------------------------------------------------------
send_achievement_lv_info(PlayerStatus) ->
	#player_status{sid = Sid, role_achievement = RoleAchievement} = PlayerStatus,
	#role_achievement{lv = Lv, exp = Exp} = RoleAchievement,
	{ok, BinData} = pt_409:write(40906, [Lv, Exp]),
	lib_server_send:send_to_sid(Sid, BinData).

%% ---------------------------------------------------------------------
%% @doc 成就特权等级信息 @end
%% ---------------------------------------------------------------------
send_achievement_privilege_info(PlayerStatus) ->
	#player_status{sid = Sid, role_achievement = RoleAchievement} = PlayerStatus,
	#role_achievement{lv_reward_list = RewardList} = RoleAchievement,
	{ok, BinData} = pt_409:write(40907, [RewardList]),
	lib_server_send:send_to_sid(Sid, BinData).

%% ---------------------------------------------------------------------
%% @doc 领取特权奖励 @end
%% ---------------------------------------------------------------------
receive_privilege_reward(Player, Lv) ->
	#player_status{sid = Sid, id = RoleId, role_achievement = RoleAchievement} = Player,
	#role_achievement{lv = AchieveLv, lv_reward_list = RewardList} = RoleAchievement,
	CheckList = [{lv, AchieveLv, Lv}, {has_receive, Lv, RewardList}
		,{reward_cfg, Lv}
	],
	case check_privilege_reward(CheckList) of
		true ->
			#stage_privilege_cfg{reward = Reward} =
				data_achievement:get_stage_privilege_cfg(Lv),
			Produce = #produce{
				type = achievement_privilege_reward, show_tips= 1, reward = Reward
			},
			{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(
				Player, Produce, ?MOD_ACHIEVEMENT
			),
			NewRewardList = lists:keystore(Lv, 1, RewardList, {Lv, ?RECEIVE_TYPE_YES}),
			db_update_achievement_lv_reward_status(RoleId, NewRewardList),
			NewPlayer1 = save_role_achievement(
				RoleAchievement#role_achievement{lv_reward_list = NewRewardList}, NewPlayer
			),
			send_msg(RoleId, 40908, [Lv, ?RECEIVE_TYPE_YES]),
			NewPlayer1;
		{false, Err} ->
			send_error(Sid, Err),
			Player
	end.

check_privilege_reward([]) -> true;
check_privilege_reward([Check|L]) ->
	case do_check_privilege_reward(Check) of
		true -> check_privilege_reward(L);
		{false, Err} -> {false, Err}
	end.

do_check_privilege_reward({lv, AchieveLv, Lv}) ->
	case AchieveLv >= Lv of
		true -> true;
		_ -> {false, ?ERRCODE(err409_lv_not_reach)}
	end;
do_check_privilege_reward({has_receive, Lv, RewardList}) ->
	case lists:keyfind(Lv, 1, RewardList) of
		{_, Status} when Status > ?RECEIVE_TYPE_CAN ->
			{false, ?ERRCODE(err409_has_received)};
		_ ->
			true
	end;
do_check_privilege_reward({reward_cfg, Lv}) ->
	case data_achievement:get_stage_privilege_cfg(Lv) of
		#stage_privilege_cfg{reward = Reward} when is_list(Reward) ->
			case Reward of
				[] -> {false, ?ERRCODE(config_err)};
				_ -> true
			end;
		_ -> {false, ?ERRCODE(config_err)}
	end;
do_check_privilege_reward(_Check) ->
	?ERR("Check privilege reward err"),
	false.

%% ---------------------------------------------------------------------
%% @doc 周固定奖励 @end
%% ---------------------------------------------------------------------
%% 每周固定奖励信息
weekly_reward_info(Player) ->
	#player_status{id = RoleId, sid = Sid, role_achievement = RoleAchievement} = Player,
	#role_achievement{lv = AchieveLv} = RoleAchievement,
	ReceiveCount = lib_week:get_count(RoleId, ?MOD_ACHIEVEMENT, 1),
	case AchieveLv >= ?ACHV_WEEKLY_RECEIVE_LV of
		false ->
			Status = ?RECEIVE_TYPE_NOT;
		_ ->
			case ReceiveCount > 0 of
				true ->
					Status = ?RECEIVE_TYPE_YES;
				_ ->
					Status = ?RECEIVE_TYPE_CAN
			end
	end,
	send_msg(Sid, 40912, [Status]),
	{ok, Player}.

%% 领取周固定奖励
receive_weekly_reward(Player) ->
	#player_status{id = RoleId, sid = Sid, role_achievement = RoleAchievement} = Player,
	#role_achievement{lv = AchieveLv} = RoleAchievement,
	case AchieveLv >= ?ACHV_WEEKLY_RECEIVE_LV of
		true ->
			ReceiveCount = lib_week:get_count(RoleId, ?MOD_ACHIEVEMENT, 1),
			case ReceiveCount =< 0 of
				true -> CanReceive = 1;
				_ -> CanReceive = 2
			end;
		_ -> CanReceive = 0
	end,
	case CanReceive of
		0 ->
			send_error(Sid, ?ERRCODE(err409_lv_not_reach)),
			{ok, Player};
		1 ->
			#stage_privilege_cfg{content = Reward} =
				data_achievement:get_stage_privilege_cfg(?ACHV_WEEKLY_RECEIVE_LV),
			Produce = #produce{type = achievement_privilege_reward, show_tips= 1, reward = Reward},
			lib_week:plus_count(RoleId, ?MOD_ACHIEVEMENT, 1, 1),
			{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player, Produce, ?MOD_ACHIEVEMENT),
			Status = ?RECEIVE_TYPE_YES,
			send_msg(RoleId, 40912, [Status]),
			{ok, NewPlayer};
		_ ->
			send_error(Sid, ?ERRCODE(err409_has_received)),
			{ok, Player}
	end.

%% ---------------------------------------------------------------------
%% @doc 红钻兑换交易券 @end
%% ---------------------------------------------------------------------
achievement_privilege_exchange(Player, Count) ->
	#player_status{id = RoleId, sid = Sid, role_achievement = RoleAchievement, bgold = BGold} = Player,
	#role_achievement{lv = AchieveLv, lv_reward_list = _RewardList} = RoleAchievement,
	NowCount = lib_daily:get_count(RoleId, ?MOD_ACHIEVEMENT, 1),
	#stage_privilege_cfg{content = {[{_, _, NeedNum}], [{_, _, SendNum}], NormalCount}} =
		data_achievement:get_stage_privilege_cfg(?ACHV_EXCHANGE_LV),
	AddCount = lib_vip_api:get_vip_privilege(Player, ?VIP_ACHIEVEMENT_EXCHANGE, 1),
	MaxCount = NormalCount + AddCount,
	CheckList = [{lv_enough, AchieveLv}, {count_true, Count},
		{count_enough, NowCount, MaxCount}, {money_enough, BGold, NeedNum}],
	case util:check_lists(?MODULE, CheckList) of
		true ->
			CanChangeCount  = calc_can_change_count(BGold, NeedNum),
			LeftChangeCount = MaxCount - NowCount,
			ChangeCount  = min(LeftChangeCount, Count),
			ChangeCount1 = min(ChangeCount, CanChangeCount),
			case lib_goods_api:cost_object_list_with_check(Player,
				[{?TYPE_BGOLD, ?GOODS_ID_BGOLD, NeedNum * ChangeCount1}], achievement_exchange, "") of
				{true, Player1, _} ->
					Produce = #produce{type = achievement_exchange,
						reward = [{?TYPE_COIN, ?GOODS_ID_COIN, SendNum * ChangeCount1}]
					},
					{ok, NewPlayer} = lib_goods_api:send_reward_with_mail(Player1, Produce),
					%% 增加次数
					lib_daily:plus_count(RoleId, ?MOD_ACHIEVEMENT, 1, ChangeCount1),
					send_msg(RoleId, 40911, [NowCount + ChangeCount1, MaxCount]),
					{ok, NewPlayer};
				{false, ErrCode, _Player1} ->
					send_error(Sid, ErrCode), {ok, Player}
			end;
		{false, Res} ->
			send_error(Sid, Res), {ok, Player}
	end.

check_can_free_sweep(#player_status{role_achievement = RoleAchievement}) ->
	#role_achievement{lv = AchieveLv} = RoleAchievement,
	case AchieveLv >= ?ACHV_FREE_SWEEP_TASK of
		true ->
			true;
		_ ->
			false
	end.

%% ---------------------------------------------------------------------
%% 发送信息
send_msg(RoleId, Cmd, Msg) when is_integer(RoleId) ->
	{ok, BinData} = pt_409:write(Cmd, Msg),
	lib_server_send:send_to_uid(RoleId, BinData);
send_msg(Sid, Cmd, Msg) ->
	{ok, BinData} = pt_409:write(Cmd, Msg),
	lib_server_send:send_to_sid(Sid, BinData).

%% 发送错误信息
send_error(Sid, Err) ->
	{ok, BinData} = pt_409:write(40909, [Err]),
	lib_server_send:send_to_sid(Sid, BinData).

%% 发送错误码
send_error_code(Sid, ErrorCode) ->
	{ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code(ErrorCode),
	{ok, BinData} = pt_102:write(10205, [ErrorCodeInt, ErrorCodeArgs]),
	lib_server_send:send_to_sid(Sid, BinData).


%% ------------------------------ builtin_function -----------------------------
%% 初始化成就相关数据
init_achievement_list([], FinishMap, ProgressMap) ->
	{FinishMap, ProgressMap};
init_achievement_list([T|L], FinishMap, ProgressMap) ->
	case T of
		[Type, SubType, Id, Progress, Status] ->
			case data_achievement:get_achievement_cfg(Type, SubType, Id) of
				#achievement_cfg{} ->
					AchievementInfo = #achievement{key = {SubType, Id}, progress = Progress, status = Status},
					case Status == ?ACHV_HAS_RECEIVE of
						true ->
							FinishL = maps:get(Type, FinishMap, []),
							NewFinishL = [AchievementInfo|FinishL],
							NewFinishMap   = maps:put(Type, NewFinishL, FinishMap),
							NewProgressMap = ProgressMap;
						false ->
							ProgressList = maps:get(Type, ProgressMap, []),
							NewProgressList = [AchievementInfo|ProgressList],
							NewProgressMap  = maps:put(Type, NewProgressList, ProgressMap),
							NewFinishMap    = FinishMap
					end,
					init_achievement_list(L, NewFinishMap, NewProgressMap);
				_ ->
					init_achievement_list(L,FinishMap, ProgressMap)
			end;
		_ -> init_achievement_list(L, FinishMap, ProgressMap)
	end.

%% 获取{Type, SubType, Id}格式的Id列表
%%get_all_achievements() ->
%%	case data_achievement:get_all_type() of
%%		TypeList when is_list(TypeList) ->
%%			F = fun(Type, TList) ->
%%				case data_achievement:get_sub_list_by_type(Type) of
%%					SubTypeL when is_list(SubTypeL) ->
%%						[{Type, SubType, Id} || {SubType, Id} <- SubTypeL] ++ TList;
%%					_ ->
%%						TList
%%				end end,
%%			lists:foldl(F, [], TypeList);
%%		_ ->
%%			[]
%%	end.

%% 获取{Type, SubType, Id}格式的Id列表
get_all_achievement_ids(Type, SubType) ->
	case data_achievement:get_achievement_id_list(Type, SubType) of
		IdList when is_list(IdList) ->
			F = fun(Id, TList) ->
				[{Type, SubType, Id}|TList]
			end,
			lists:foldl(F, [], IdList);
		_ ->
			[]
	end.

%% 获取当前类型成就需要显示的成就信息
get_cur_achievement(_Type, [], _ProgressList, _FinishList, _NowIdMap, ShowList) -> ShowList;
get_cur_achievement(Type, [{SubType, Id}|L], ProgressList, FinishList, NowIdMap, TShowList) ->
	NowId = case maps:get({Type, SubType}, NowIdMap, 0) of
		        Integer when Integer > 0 -> Integer;
		        _ -> data_achievement:get_min_id(Type, SubType)
	        end,
	case Id =:= NowId of
		true ->
			Key = {SubType, NowId},
			case lists:keyfind(Key, #achievement.key, ProgressList) of
				false ->
					case lists:keyfind(Key, #achievement.key, FinishList) of
						AchievementInfo when is_record(AchievementInfo, achievement) ->
							get_cur_achievement(Type, L, ProgressList, FinishList, NowIdMap, [AchievementInfo|TShowList]);
						false ->
							%% 都找不到数据就发送一个默认0进度数据
							AchievementInfo = #achievement{key = Key},
							get_cur_achievement(Type, L, ProgressList, FinishList, NowIdMap, [AchievementInfo|TShowList])
					end;
				AchievementInfo ->
					get_cur_achievement(Type, L, ProgressList, FinishList, NowIdMap, [AchievementInfo|TShowList])
			end;
		_ ->
			get_cur_achievement(Type, L, ProgressList, FinishList, NowIdMap, TShowList)
	end.

%% 通过Key筛选出符合的成就配置
filter_achievement_cfg_by_key([], _, ResultL) ->
	lists:sort(ResultL);
filter_achievement_cfg_by_key([{Type, SubType, Id}|L], Key, ResultL) ->
	case data_achievement:get_achievement_cfg(Type, SubType, Id) of
		#achievement_cfg{condition = Condition} = Cfg ->
			% SureKeys = [1 || Key <- Keys, lists:keyfind(Key, 1, Condition) == true],    % 确保key和条件完全匹配
			case lists:keyfind(Key, 1, Condition) of
				false ->
					filter_achievement_cfg_by_key(L, Key, ResultL);
				_ ->
					filter_achievement_cfg_by_key(L, Key, [Cfg|ResultL])
			end;
		_ ->
			filter_achievement_cfg_by_key(L, Key, ResultL)
	end.

%% 领取成就奖励 -- 增加成就经验
upgrade_achievement_level(Player, Type, SubType, Id) ->
	#player_status{sid = Sid, id = RoleId, role_achievement = RoleAchievement} = Player,
	#role_achievement{lv = Lv, exp = Exp, lv_reward_list = RewardList} = RoleAchievement,
	case data_achievement:get_achievement_cfg(Type, SubType, Id) of
		#achievement_cfg{exp = AddExp} ->
			NewExp = Exp + AddExp,
			{NewLv, NowExp}  = do_upgrade_level(Lv, NewExp) ,
			case Lv == 0 andalso Exp == 0 of
				true ->
					db_save_achievement_level(RoleId, NewLv, NowExp, []);
				_ ->
					db_update_achievement_level(RoleId, NewLv, NowExp)
			end,
			NRoleAchievement  = RoleAchievement#role_achievement{lv = NewLv, exp = NowExp},
			NRoleAchievement1 = calc_achievement_lv_attr(NRoleAchievement),
			send_msg(Sid, 40906, [NewLv, NowExp]),
			%% 推送等级特权奖励
			case NewLv =/= Lv of
				true ->
					case get_new_lv_reward_list(RewardList, NewLv) of
						{true, NewRewardList} ->
							db_update_achievement_lv_reward_status(RoleId, NewRewardList),
							send_msg(Sid, 40907, [NewRewardList]);
						_ -> NewRewardList = RewardList
					end;
				_ ->  NewRewardList = RewardList
			end,
			%% TODO 检查是否激活新的特权
			check_if_active_privilege(NewLv),
			NewPlayer = Player#player_status{role_achievement =
				NRoleAchievement1#role_achievement{lv_reward_list = NewRewardList}
			},
			RefreshPlayer = refresh_attr(NewPlayer),
			mod_scene_agent:update(RefreshPlayer, [{battle_attr, RefreshPlayer#player_status.battle_attr}]),
			RefreshPlayer;
		_ ->
			Player
	end.

do_upgrade_level(Lv, NewExp) ->
	case data_achievement:get_achievement_stage_cfg(Lv+1) of
		#achievement_stage_cfg{need_exp = NeedExp} ->
			case NewExp >= NeedExp of
				true ->
					do_upgrade_level(Lv+1, NewExp - NeedExp);
				_ ->
					{Lv, NewExp}
			end;
		_ -> % 最大等级
			{Lv, NewExp}
	end.

get_new_lv_reward_list(OldRewardList, Lv) ->
	OldLvList = get_old_lv_list(OldRewardList),  %% 加强判断,防止跳级(其实理论上不会发生)
	MatchLvL  = get_reach_lv_list(Lv),
	case (MatchLvL -- OldLvList) =/= [] of
		true ->
			UpL = MatchLvL -- OldLvList,
			F = fun(TLv, TRewardList) ->
				lists:keystore(TLv, 1, TRewardList, {TLv, ?RECEIVE_TYPE_CAN})
			    end,
			NewRewardList = lists:foldl(F, OldRewardList, UpL),
			{true, NewRewardList};
		_ ->
			{false, OldRewardList}
	end.

get_old_lv_list(OldRewardList) ->
	F = fun({Lv, _Status}, TList) ->
		[Lv|TList]
	    end,
	lists:foldl(F, [], OldRewardList).

get_reach_lv_list(Lv) ->
	LvList = data_achievement:get_all_stage_privilege(),
	F = fun(TLv, TList) ->
		case Lv >= TLv of
			true ->
				[TLv|TList];
			_ ->
				TList
		end
	    end,
	lists:foldl(F, [], LvList).

check_if_active_privilege(_NewLv) ->
	ok.

calc_achievement_lv_attr(RoleAchievement) ->
	#role_achievement{lv = Lv} = RoleAchievement,
	case data_achievement:get_achievement_stage_cfg(Lv) of
		#achievement_stage_cfg{attr_list = AttrList} ->
			skip;
		_ ->
			AttrList = []
	end,
	RoleAchievement#role_achievement{attr = AttrList}.

refresh_attr(Player) ->
	RefreshPlayer = lib_player:count_player_attribute(Player, ?MOD_ACHIEVEMENT, achievement_lv_up),
	lib_player:send_attribute_change_notify(RefreshPlayer, ?NOTIFY_ATTR),
	RefreshPlayer.

broadcast_info_add(Type, SubType, Id, Progress, Status) ->
	case get(broadcast_info) of
		Val when is_list(Val) ->
			case lists:keyfind({Type, SubType, Id}, 1, Val) of
				{_, _, _} ->
					skip;
				_ -> put(broadcast_info, [{{Type, SubType, Id}, Progress, Status}|Val])
			end;
		_ ->
			put(broadcast_info, [{{Type, SubType, Id}, Progress, Status}])
	end.

%% 成就进度/大类进度修改,更新客户端
%% UpList :: list()|tuple()  --
%% [{Type, SubType, Id, Status, Progress}...]|{Type, SubType, Id, Status, Progress}
notify_to_client(?NOTIFY_SUB_TYPE_PROGRESS, RoleId, BroadcastList) ->
	case BroadcastList of
		{Type, SubType, Id, Progress, Status} ->
			PackList = [{Type, SubType, Id, Progress, Status}],
			lib_server_send:send_to_uid(RoleId, pt_409, 40904, [PackList]);
		[_|_] ->
			case get_achievement_dict(RoleId) of
				{ok, ProcAchievement} ->
					#proc_achievement{
						now_id_map   = NowIdMap
					} = ProcAchievement,
					F = fun({{Type, SubType, Id}, Progress, Status}, TList) ->
								NowId = case maps:get({Type, SubType}, NowIdMap, 0) of
											Integer when Integer > 0 -> Integer;
											_ -> data_achievement:get_min_id(Type, SubType)
								        end,
								case Id =:= NowId of
									true ->
										[{Type, SubType, Id, Progress, Status}|TList];
									_ ->
										TList
								end;
						(_, TList) -> TList
					end,
					PackList = lists:foldl(F, [], BroadcastList),
					lib_server_send:send_to_uid(RoleId, pt_409, 40904, [PackList]);
				_ ->
					ignore
			end;
		_ ->
			ignore
	end;
%% 更新总进度
notify_to_client(?NOTIFY_TYPE_FIN_COUNT, RoleId, UTypeL) ->
	case get_achievement_dict(RoleId) of
		{ok, ProcAchievement} ->
			#proc_achievement{
				fin_count_map = FinCountMap
			} = ProcAchievement,
			F = fun(Type, TmpUTypeL) ->
				Count  = maps:get(Type, FinCountMap, 0),
				NewUTypeL = [{Type, Count}|TmpUTypeL],
				NewUTypeL
			end,
			STypeL = lists:foldl(F, [], UTypeL),
			case STypeL =/= [] of
				true ->
					{ok, BinData} = pt_409:write(40905, [STypeL]),
					lib_server_send:send_to_uid(RoleId, BinData);
				false ->
					skip
			end;
		_ ->
			skip
	end.

%% 计算可以兑换的次数
calc_can_change_count(HasBGold, NeedNum) ->
	HasBGold div NeedNum.

check({lv_enough, AchieveLv}) ->
	case AchieveLv >= ?ACHV_EXCHANGE_LV of
		true -> true;
		_ -> {false, ?ERRCODE(err409_lv_not_reach)}
	end;
check({count_true, Count}) ->
	case Count > 0 of
		true -> true;
		_ -> {false, ?ERRCODE(err409_param_err)}
	end;
check({count_enough, NowCount, MaxCount}) ->
	case NowCount < MaxCount of
		true -> true;
		_ -> {false, ?ERRCODE(err409_count_not_enough)}
	end;
check({money_enough, BGold, NeedNum}) ->
	case BGold >= NeedNum of
		true -> true;
		_ -> {false, ?ERRCODE(bgold_not_enough)}
	end;
check(_) ->
	{false, ?FAIL}.

%% -------------------------- 内存数据操作 --------------------------
get_achievement_dict(RoleId) ->
	Pid = misc:get_player_process(RoleId),
	case Pid =:= self() of
		true ->
			case get(?P_ACHIEVEMENT) of
				undefined ->
					StatusAchievement = login_achv(RoleId),
					save_achievement_dict(StatusAchievement),
					{ok, StatusAchievement};
				Val -> {ok, Val}
			end;
		false -> skip
	end.

save_achievement_dict(StatusAchievement) ->
	put(?P_ACHIEVEMENT, StatusAchievement).

save_role_achievement(RoleAchievement, Player) ->
	Player#player_status{role_achievement = RoleAchievement}.

%% ------------------------------ 数据库操作 ------------------------------------
%% 成就进度 & 成就奖励
db_select_achievement(RoleId) ->
	db:get_all(io_lib:format(?select_achievement, [RoleId])).

db_update_achievement(RoleId, Type, SubType, Id, Achievement) ->
	#achievement{status = Status} = Achievement,
	NowTime = utime:unixtime(),
	db:execute(io_lib:format(?update_achievement_status, [Status, NowTime, RoleId, Type, SubType, Id])).

%% 成就等级特权
db_save_achievement_level(RoleId, Lv, Exp, RewardStatus) ->
	db:execute(io_lib:format(?insert_achievement_level, [RoleId, Lv, Exp, util:term_to_string(RewardStatus)])).

db_update_achievement_level(RoleId, Lv, Exp) ->
	db:execute(io_lib:format(?update_achievement_level, [Lv, Exp, RoleId])).

db_update_achievement_lv_reward_status(RoleId, RewardStatus) ->
	db:execute(io_lib:format(?update_achievement_lv_reward_status, [util:term_to_string(RewardStatus), RoleId])).

db_select_achievement_level(RoleId) ->
	db:get_row(io_lib:format(?select_achievement_level, [RoleId])).

%% ------------------------------- gm操作 ---------------------------------------
%% 重置所有成就进度
gm_reset(RoleId) ->
	F = fun() ->
		db:execute(io_lib:format(<<"delete from achievement where role_id = ~p">>, [RoleId])),
		db:execute(io_lib:format(<<"delete from achievement_category_reward where role_id = ~p">>, [RoleId]))
	    end,
	db:transaction(F),
	erase(?P_ACHIEVEMENT),
	%% 更新成就进度
	TypeList = data_achievement:get_all_type(),
	F1 = fun(Type, TList) ->
		case data_achievement:get_sub_list_by_type(Type) of
			[_|_] = SubList ->
				F2 = fun({TSubType, TId}, TList1) ->
					data_achievement:get_achievement_cfg(Type, TSubType, TId),
					[{Type, TSubType, TId, 0, 0}|TList1]
				     end,
				lists:foldl(F2, [], SubList) ++ TList;
			_ ->
				TList
		end
	     end,
	PackList = lists:foldl(F1, [], TypeList),
	send_msg(RoleId, 40904, [PackList]).

%% 设置单个成就进度
gm_set_finish(PS, Type, SubType, Id) ->
	#player_status{id = RoleId, figure = #figure{lv = RoleLv}} = PS,
	case get_achievement_dict(RoleId) of
		{ok, ProcAchievement} ->
			#proc_achievement{
				finish_map = FinishMap, progress_map = ProgressMap,
				fin_count_map = CountMap, now_id_map = NowIdMap
			} = ProcAchievement,
			case data_achievement:get_achievement_cfg(Type, SubType, Id) of
				#achievement_cfg{} ->
					F = fun() ->
						put(broadcast_info, []),
						FinishL   = maps:get(Type, FinishMap, []),
						ProgressL = maps:get(Type, ProgressMap, []),
						NewProgressL = set_finish([{Type, SubType, Id}], RoleId, FinishL, ProgressL),
						UpdateList = get(broadcast_info),
						{ok, UpdateList, NewProgressL}
					end,
					case catch db:transaction(F) of
						{ok, UpdateList, NewProgressL} ->
							PreCount    = maps:get(Type, CountMap, 0),
							NewCountMap = maps:put(Type, PreCount + length(UpdateList), CountMap),
							NewProgressMap = maps:put(Type, NewProgressL, ProgressMap),
							NewProcAchievement = ProcAchievement#proc_achievement{
								progress_map = NewProgressMap, fin_count_map = NewCountMap
							},
							save_achievement_dict(NewProcAchievement),
							OpenLv = lib_module:get_open_lv(?MOD_ACHIEVEMENT),
							case RoleLv >= OpenLv of
								true ->
									%% 通知客户端更新数据
									PFun = fun({{TType, TSubType, TId}, Progress, Status}, TList) ->
										NowId = case maps:get({TType, TSubType}, NowIdMap, 0) of
											        Integer when Integer > 0 -> Integer;
											        _ -> data_achievement:get_min_id(TType, TSubType)
										        end,
										case TId =:= NowId of
											true ->
												[{TType, TSubType, TId, Progress, Status}|TList];
											_ ->
												TList
										end;
										(_, TList) -> TList
									    end,
									PackList = lists:foldl(PFun, [], UpdateList),
									{ok, BinData} = pt_409:write(40904, [PackList]),
									lib_server_send:send_to_uid(RoleId, BinData);
								false -> skip
							end;
						Error ->
							?ERR("role_id:~p gm set finish achievement id:~p ~p ~p err:~p~n",
								[RoleId, Type, SubType, Id, Error])
					end;
				_ -> skip
			end;
		_ -> skip
	end.

gm_finish_all([], PS) -> PS;
gm_finish_all([Type|Rest], PS) ->
	SubList = data_achievement:get_sub_list_by_type(Type),
	PS = gm_finish_all_do(PS, Type, SubList),
	gm_finish_all(Rest, PS).

gm_finish_all_do(PS, _Type, []) -> PS;
gm_finish_all_do(PS, Type, [{SubType, Id}|Rest]) ->
	timer:sleep(10),
	gm_set_finish(PS, Type, SubType, Id),
	gm_finish_all_do(PS, Type, Rest).

set_finish([], _, _, ProgressL) -> ProgressL;
set_finish([{Type, SubType, Id}|AcIdL], RoleId, FinishL, ProgressL) ->
	NowTime = utime:unixtime(),
	case data_achievement:get_achievement_cfg(Type, SubType, Id) of
		#achievement_cfg{condition = Condition} ->
			Limit = case Condition of
				        [{_, Num}|_] -> Num;
				        _ -> 1
			        end,
			NeedInsert = lists:keyfind({SubType, Id}, #achievement.key, ProgressL),
			case lists:keyfind({SubType, Id}, #achievement.key, FinishL) of
				false when NeedInsert == false ->
					db:execute(io_lib:format(?insert_achievement, [RoleId, Type, SubType, Id, Limit, ?ACHV_FINISH, NowTime])),
					NewProgressL = lists:keystore({SubType, Id}, #achievement.key, ProgressL,
						#achievement{key = {SubType, Id}, progress = Limit,
						status = ?ACHV_FINISH, wtime = NowTime, wstatus = ?ACHV_WSTATUS_SUCCESS}),
					broadcast_info_add(Type, SubType, Id, Limit, ?ACHV_FINISH);
				false when is_record(NeedInsert, achievement) ->
					db:execute(io_lib:format(?update_achievement_progress, [Limit, ?ACHV_FINISH, NowTime, RoleId, Type, SubType, Id])),
					NewProgressL = lists:keystore({SubType, Id}, #achievement.key, ProgressL,
						#achievement{key = {SubType, Id}, progress = Limit,
						status = ?ACHV_FINISH, wtime = NowTime, wstatus = ?ACHV_WSTATUS_SUCCESS}),
					broadcast_info_add(Type, SubType, Id, Limit, ?ACHV_FINISH);
				#achievement{} ->
					NewProgressL = ProgressL
			end,
			set_finish(AcIdL, RoleId, FinishL, NewProgressL);
		_ ->
			set_finish(AcIdL, RoleId, FinishL, ProgressL)
	end.

%% 增加成就经验
gm_add_exp(Player, AddExp) ->
	#player_status{sid = Sid, id = RoleId, role_achievement = RoleAchievement} = Player,
	#role_achievement{lv = Lv, exp = Exp, lv_reward_list = RewardList} = RoleAchievement,
	NewExp = Exp + AddExp,
	{NewLv, NowExp}  = do_upgrade_level(Lv, NewExp) ,
	case Lv == 0 andalso Exp == 0 of
		true ->
			db_save_achievement_level(RoleId, NewLv, NowExp, []);
		_ ->
			db_update_achievement_level(RoleId, NewLv, NowExp)
	end,
	NRoleAchievement  = RoleAchievement#role_achievement{lv = NewLv, exp = NowExp},
	NRoleAchievement1 = calc_achievement_lv_attr(NRoleAchievement),
	case NewLv =/= Lv of
		true ->
			case get_new_lv_reward_list(RewardList, NewLv) of
				{true, NewRewardList} ->
					db_update_achievement_lv_reward_status(RoleId, NewRewardList),
					send_msg(Sid, 40907, [NewRewardList]);
				_ -> NewRewardList = RewardList
			end;
		_ ->  NewRewardList = RewardList
	end,
	send_msg(Sid, 40906, [NewLv, NowExp]),
	%% TODO 检查是否激活新的特权
	check_if_active_privilege(NewLv),
	NewPlayer = Player#player_status{role_achievement = NRoleAchievement1#role_achievement{lv_reward_list = NewRewardList}},
	RefreshPlayer = refresh_attr(NewPlayer),
	RefreshPlayer.
